lib Library API Documentation

korichtext.cpp

00001 /****************************************************************************
00002 ** Implementation of the internal Qt classes dealing with rich text
00003 **
00004 ** Created : 990101
00005 **
00006 ** Copyright (C) 1992-2000 Trolltech AS.  All rights reserved.
00007 **
00008 ** This file is part of the kernel module of the Qt GUI Toolkit.
00009 **
00010 ** This file may be distributed under the terms of the Q Public License
00011 ** as defined by Trolltech AS of Norway and appearing in the file
00012 ** LICENSE.QPL included in the packaging of this file.
00013 **
00014 ** This file may be distributed and/or modified under the terms of the
00015 ** GNU General Public License version 2 as published by the Free Software
00016 ** Foundation and appearing in the file LICENSE.GPL included in the
00017 ** packaging of this file.
00018 **
00019 ** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
00020 ** licenses may use this file in accordance with the Qt Commercial License
00021 ** Agreement provided with the Software.
00022 **
00023 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00024 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00025 **
00026 ** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
00027 **   information about Qt Commercial License Agreements.
00028 ** See http://www.trolltech.com/qpl/ for QPL licensing information.
00029 ** See http://www.trolltech.com/gpl/ for GPL licensing information.
00030 **
00031 ** Contact info@trolltech.com if any conditions of this licensing are
00032 ** not clear to you.
00033 **
00034 **********************************************************************/
00035 
00036 #include "korichtext.h"
00037 #include "kotextformat.h"
00038 #include "kotextparag.h"
00039 
00040 #include <qpaintdevicemetrics.h>
00041 #include "qdrawutil.h" // for KoTextHorizontalLine
00042 
00043 #include <stdlib.h>
00044 #include "koparagcounter.h"
00045 #include "kotextdocument.h"
00046 #include <kdebug.h>
00047 #include <kdeversion.h>
00048 #include <kglobal.h>
00049 #include <klocale.h>
00050 #include <private/qtextengine_p.h>
00051 
00052 //#define PARSER_DEBUG
00053 //#define DEBUG_COLLECTION
00054 //#define DEBUG_TABLE_RENDERING
00055 
00056 //static KoTextFormatCollection *qFormatCollection = 0;
00057 
00058 #if defined(PARSER_DEBUG)
00059 static QString debug_indent;
00060 #endif
00061 
00062 static bool is_printer( QPainter *p )
00063 {
00064     return p && p->device() && p->device()->devType() == QInternal::Printer;
00065 }
00066 
00067 static inline int scale( int value, QPainter *painter )
00068 {
00069     if ( is_printer( painter ) ) {
00070     QPaintDeviceMetrics metrics( painter->device() );
00071 #if defined(Q_WS_X11)
00072     value = value * metrics.logicalDpiY() / QPaintDevice::x11AppDpiY();
00073 #elif defined (Q_WS_WIN)
00074     int gdc = GetDeviceCaps( GetDC( 0 ), LOGPIXELSY );
00075     if ( gdc )
00076         value = value * metrics.logicalDpiY() / gdc;
00077 #elif defined (Q_WS_MAC)
00078     value = value * metrics.logicalDpiY() / 75; // ##### FIXME
00079 #elif defined (Q_WS_QWS)
00080     value = value * metrics.logicalDpiY() / 75;
00081 #endif
00082     }
00083     return value;
00084 }
00085 
00086 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
00087 
00088 void KoTextDocCommandHistory::addCommand( KoTextDocCommand *cmd )
00089 {
00090     if ( current < (int)history.count() - 1 ) {
00091     QPtrList<KoTextDocCommand> commands;
00092     commands.setAutoDelete( FALSE );
00093 
00094     for( int i = 0; i <= current; ++i ) {
00095         commands.insert( i, history.at( 0 ) );
00096         history.take( 0 );
00097     }
00098 
00099     commands.append( cmd );
00100     history.clear();
00101     history = commands;
00102     history.setAutoDelete( TRUE );
00103     } else {
00104     history.append( cmd );
00105     }
00106 
00107     if ( (int)history.count() > steps )
00108     history.removeFirst();
00109     else
00110     ++current;
00111 }
00112 
00113 KoTextCursor *KoTextDocCommandHistory::undo( KoTextCursor *c )
00114 {
00115     if ( current > -1 ) {
00116     KoTextCursor *c2 = history.at( current )->unexecute( c );
00117     --current;
00118     return c2;
00119     }
00120     return 0;
00121 }
00122 
00123 KoTextCursor *KoTextDocCommandHistory::redo( KoTextCursor *c )
00124 {
00125     if ( current > -1 ) {
00126     if ( current < (int)history.count() - 1 ) {
00127         ++current;
00128         return history.at( current )->execute( c );
00129     }
00130     } else {
00131     if ( history.count() > 0 ) {
00132         ++current;
00133         return history.at( current )->execute( c );
00134     }
00135     }
00136     return 0;
00137 }
00138 
00139 bool KoTextDocCommandHistory::isUndoAvailable()
00140 {
00141     return current > -1;
00142 }
00143 
00144 bool KoTextDocCommandHistory::isRedoAvailable()
00145 {
00146    return current > -1 && current < (int)history.count() - 1 || current == -1 && history.count() > 0;
00147 }
00148 
00149 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
00150 
00151 KoTextDocDeleteCommand::KoTextDocDeleteCommand( KoTextDocument *d, int i, int idx, const QMemArray<KoTextStringChar> &str )
00152     : KoTextDocCommand( d ), id( i ), index( idx ), parag( 0 ), text( str )
00153 {
00154     for ( int j = 0; j < (int)text.size(); ++j ) {
00155     if ( text[ j ].format() )
00156         text[ j ].format()->addRef();
00157     }
00158 }
00159 
00160 /*KoTextDocDeleteCommand::KoTextDocDeleteCommand( KoTextParag *p, int idx, const QMemArray<KoTextStringChar> &str )
00161     : KoTextDocCommand( 0 ), id( -1 ), index( idx ), parag( p ), text( str )
00162 {
00163     for ( int i = 0; i < (int)text.size(); ++i ) {
00164     if ( text[ i ].format() )
00165         text[ i ].format()->addRef();
00166     }
00167 }*/
00168 
00169 KoTextDocDeleteCommand::~KoTextDocDeleteCommand()
00170 {
00171     for ( int i = 0; i < (int)text.size(); ++i ) {
00172     if ( text[ i ].format() )
00173         text[ i ].format()->removeRef();
00174     }
00175     text.resize( 0 );
00176 }
00177 
00178 KoTextCursor *KoTextDocDeleteCommand::execute( KoTextCursor *c )
00179 {
00180     KoTextParag *s = doc ? doc->paragAt( id ) : parag;
00181     if ( !s ) {
00182     kdWarning(32500) << "can't locate parag at " << id << ", last parag: " << doc->lastParag()->paragId() << endl;
00183     return 0;
00184     }
00185 
00186     cursor.setParag( s );
00187     cursor.setIndex( index );
00188     int len = text.size();
00189     if ( c )
00190     *c = cursor;
00191     if ( doc ) {
00192     doc->setSelectionStart( KoTextDocument::Temp, &cursor );
00193     for ( int i = 0; i < len; ++i )
00194         cursor.gotoNextLetter();
00195     doc->setSelectionEnd( KoTextDocument::Temp, &cursor );
00196     doc->removeSelectedText( KoTextDocument::Temp, &cursor );
00197     if ( c )
00198         *c = cursor;
00199     } else {
00200     s->remove( index, len );
00201     }
00202 
00203     return c;
00204 }
00205 
00206 KoTextCursor *KoTextDocDeleteCommand::unexecute( KoTextCursor *c )
00207 {
00208     KoTextParag *s = doc ? doc->paragAt( id ) : parag;
00209     if ( !s ) {
00210     kdWarning(32500) << "can't locate parag at " << id << ", last parag: " << doc->lastParag()->paragId() << endl;
00211     return 0;
00212     }
00213 
00214     cursor.setParag( s );
00215     cursor.setIndex( index );
00216     QString str = KoTextString::toString( text );
00217     cursor.insert( str, TRUE, &text );
00218     cursor.setParag( s );
00219     cursor.setIndex( index );
00220     if ( c ) {
00221     c->setParag( s );
00222     c->setIndex( index );
00223     for ( int i = 0; i < (int)text.size(); ++i )
00224         c->gotoNextLetter();
00225     }
00226 
00227     s = cursor.parag();
00228     while ( s ) {
00229     s->format();
00230     s->setChanged( TRUE );
00231     if ( s == c->parag() )
00232         break;
00233     s = s->next();
00234     }
00235 
00236     return &cursor;
00237 }
00238 
00239 KoTextDocFormatCommand::KoTextDocFormatCommand( KoTextDocument *d, int sid, int sidx, int eid, int eidx,
00240                     const QMemArray<KoTextStringChar> &old, const KoTextFormat *f, int fl )
00241     : KoTextDocCommand( d ), startId( sid ), startIndex( sidx ), endId( eid ), endIndex( eidx ), oldFormats( old ), flags( fl )
00242 {
00243     format = d->formatCollection()->format( f );
00244     for ( int j = 0; j < (int)oldFormats.size(); ++j ) {
00245     if ( oldFormats[ j ].format() )
00246         oldFormats[ j ].format()->addRef();
00247     }
00248 }
00249 
00250 KoTextDocFormatCommand::~KoTextDocFormatCommand()
00251 {
00252     format->removeRef();
00253     for ( int j = 0; j < (int)oldFormats.size(); ++j ) {
00254     if ( oldFormats[ j ].format() )
00255         oldFormats[ j ].format()->removeRef();
00256     }
00257 }
00258 
00259 KoTextCursor *KoTextDocFormatCommand::execute( KoTextCursor *c )
00260 {
00261     KoTextParag *sp = doc->paragAt( startId );
00262     KoTextParag *ep = doc->paragAt( endId );
00263     if ( !sp || !ep )
00264     return c;
00265 
00266     KoTextCursor start( doc );
00267     start.setParag( sp );
00268     start.setIndex( startIndex );
00269     KoTextCursor end( doc );
00270     end.setParag( ep );
00271     end.setIndex( endIndex );
00272 
00273     doc->setSelectionStart( KoTextDocument::Temp, &start );
00274     doc->setSelectionEnd( KoTextDocument::Temp, &end );
00275     doc->setFormat( KoTextDocument::Temp, format, flags );
00276     doc->removeSelection( KoTextDocument::Temp );
00277     if ( endIndex == ep->length() ) // ### Not in QRT - report sent. Description at http://bugs.kde.org/db/34/34556.html
00278         end.gotoLeft();
00279     *c = end;
00280     return c;
00281 }
00282 
00283 KoTextCursor *KoTextDocFormatCommand::unexecute( KoTextCursor *c )
00284 {
00285     KoTextParag *sp = doc->paragAt( startId );
00286     KoTextParag *ep = doc->paragAt( endId );
00287     if ( !sp || !ep )
00288     return 0;
00289 
00290     int idx = startIndex;
00291     int fIndex = 0;
00292     if( !oldFormats.isEmpty()) // ## not in QRT. Not sure how it can happen.
00293     {
00294     for ( ;; ) {
00295     if ( oldFormats.at( fIndex ).c == '\n' ) {
00296         if ( idx > 0 ) {
00297         if ( idx < sp->length() && fIndex > 0 )
00298             sp->setFormat( idx, 1, oldFormats.at( fIndex - 1 ).format() );
00299         if ( sp == ep )
00300             break;
00301         sp = sp->next();
00302         idx = 0;
00303         }
00304         fIndex++;
00305     }
00306     if ( oldFormats.at( fIndex ).format() )
00307         sp->setFormat( idx, 1, oldFormats.at( fIndex ).format() );
00308     idx++;
00309     fIndex++;
00310     if ( fIndex >= (int)oldFormats.size() )
00311         break;
00312     if ( idx >= sp->length() ) {
00313         if ( sp == ep )
00314         break;
00315         sp = sp->next();
00316         idx = 0;
00317     }
00318     }
00319     }
00320     KoTextCursor end( doc );
00321     end.setParag( ep );
00322     end.setIndex( endIndex );
00323     if ( endIndex == ep->length() )
00324         end.gotoLeft();
00325     *c = end;
00326     return c;
00327 }
00328 
00329 KoTextAlignmentCommand::KoTextAlignmentCommand( KoTextDocument *d, int fParag, int lParag, int na, const QMemArray<int> &oa )
00330     : KoTextDocCommand( d ), firstParag( fParag ), lastParag( lParag ), newAlign( na ), oldAligns( oa )
00331 {
00332 }
00333 
00334 KoTextCursor *KoTextAlignmentCommand::execute( KoTextCursor *c )
00335 {
00336     KoTextParag *p = doc->paragAt( firstParag );
00337     if ( !p )
00338     return c;
00339     while ( p ) {
00340     p->setAlignment( newAlign );
00341     if ( p->paragId() == lastParag )
00342         break;
00343     p = p->next();
00344     }
00345     return c;
00346 }
00347 
00348 KoTextCursor *KoTextAlignmentCommand::unexecute( KoTextCursor *c )
00349 {
00350     KoTextParag *p = doc->paragAt( firstParag );
00351     if ( !p )
00352     return c;
00353     int i = 0;
00354     while ( p ) {
00355     if ( i < (int)oldAligns.size() )
00356         p->setAlignment( oldAligns.at( i ) );
00357     if ( p->paragId() == lastParag )
00358         break;
00359     p = p->next();
00360     ++i;
00361     }
00362     return c;
00363 }
00364 
00365 
00366 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
00367 
00368 KoTextCursor::KoTextCursor( KoTextDocument *d )
00369     : doc( d )
00370 {
00371     idx = 0;
00372     string = doc ? doc->firstParag() : 0;
00373     tmpIndex = -1;
00374 }
00375 
00376 KoTextCursor::KoTextCursor()
00377 {
00378 }
00379 
00380 KoTextCursor::KoTextCursor( const KoTextCursor &c )
00381 {
00382     doc = c.doc;
00383     idx = c.idx;
00384     string = c.string;
00385     tmpIndex = c.tmpIndex;
00386 }
00387 
00388 KoTextCursor &KoTextCursor::operator=( const KoTextCursor &c )
00389 {
00390     doc = c.doc;
00391     idx = c.idx;
00392     string = c.string;
00393     tmpIndex = c.tmpIndex;
00394 
00395     return *this;
00396 }
00397 
00398 bool KoTextCursor::operator==( const KoTextCursor &c ) const
00399 {
00400     return doc == c.doc && string == c.string && idx == c.idx;
00401 }
00402 
00403 void KoTextCursor::insert( const QString &str, bool checkNewLine, QMemArray<KoTextStringChar> *formatting )
00404 {
00405     string->invalidate( idx );
00406     tmpIndex = -1;
00407     bool justInsert = TRUE;
00408     QString s( str );
00409 #if defined(Q_WS_WIN)
00410     if ( checkNewLine )
00411     s = s.replace( QRegExp( "\\r" ), "" );
00412 #endif
00413     if ( checkNewLine )
00414     justInsert = s.find( '\n' ) == -1;
00415     if ( justInsert ) {
00416     string->insert( idx, s );
00417     if ( formatting ) {
00418         for ( int i = 0; i < (int)s.length(); ++i ) {
00419         if ( formatting->at( i ).format() ) {
00420             formatting->at( i ).format()->addRef();
00421             string->string()->setFormat( idx + i, formatting->at( i ).format(), TRUE );
00422         }
00423         }
00424     }
00425     idx += s.length();
00426     } else {
00427     QStringList lst = QStringList::split( '\n', s, TRUE );
00428     QStringList::Iterator it = lst.begin();
00429     //int y = string->rect().y() + string->rect().height();
00430     int lastIndex = 0;
00431     KoTextFormat *lastFormat = 0;
00432     for ( ; it != lst.end(); ) {
00433         if ( it != lst.begin() ) {
00434         splitAndInsertEmptyParag( FALSE, TRUE );
00435         //string->setEndState( -1 );
00436 #if 0 // no!
00437         string->prev()->format( -1, FALSE );
00438 #endif
00439         if ( lastFormat && formatting && string->prev() ) {
00440             lastFormat->addRef();
00441             string->prev()->string()->setFormat( string->prev()->length() - 1, lastFormat, TRUE );
00442         }
00443         }
00444         lastFormat = 0;
00445         QString s = *it;
00446         ++it;
00447         if ( !s.isEmpty() )
00448         string->insert( idx, s );
00449             else
00450                 string->invalidate( 0 );
00451 
00452         if ( formatting ) {
00453         int len = s.length();
00454         for ( int i = 0; i < len; ++i ) {
00455             if ( formatting->at( i + lastIndex ).format() ) {
00456             formatting->at( i + lastIndex ).format()->addRef();
00457             string->string()->setFormat( i + idx, formatting->at( i + lastIndex ).format(), TRUE );
00458             }
00459         }
00460         if ( it != lst.end() )
00461             lastFormat = formatting->at( len + lastIndex ).format();
00462         ++len;
00463         lastIndex += len;
00464         }
00465 
00466         idx += s.length();
00467     }
00468 #if 0  
00469     string->format( -1, FALSE );
00470     int dy = string->rect().y() + string->rect().height() - y;
00471 #endif
00472     KoTextParag *p = string;
00473     p->setParagId( p->prev()->paragId() + 1 );
00474     p = p->next();
00475     while ( p ) {
00476         p->setParagId( p->prev()->paragId() + 1 );
00477         //p->move( dy );
00478         p->invalidate( 0 );
00479         p = p->next();
00480     }
00481     }
00482 
00483 #if 0  
00484     int h = string->rect().height();
00485     string->format( -1, TRUE );
00486 #endif
00487     fixCursorPosition();
00488 }
00489 
00490 void KoTextCursor::gotoLeft()
00491 {
00492     if ( string->string()->isRightToLeft() )
00493     gotoNextLetter();
00494     else
00495     gotoPreviousLetter();
00496 }
00497 
00498 void KoTextCursor::gotoPreviousLetter()
00499 {
00500     tmpIndex = -1;
00501 
00502     if ( idx > 0 ) {
00503     idx = string->string()->previousCursorPosition( idx );
00504     } else if ( string->prev() ) {
00505     string = string->prev();
00506     while ( !string->isVisible() )
00507         string = string->prev();
00508     idx = string->length() - 1;
00509     }
00510 }
00511 
00512 bool KoTextCursor::place( const QPoint &p, KoTextParag *s, bool link, int *customItemIndex )
00513 {
00514     if ( customItemIndex )
00515         *customItemIndex = -1;
00516     QPoint pos( p );
00517     QRect r;
00518     if ( pos.y() < s->rect().y() )
00519     pos.setY( s->rect().y() );
00520     while ( s ) {
00521     r = s->rect();
00522     r.setWidth( doc ? doc->width() : QWIDGETSIZE_MAX );
00523     if ( !s->next() || ( pos.y() >= r.y() && pos.y() < s->next()->rect().y() ) )
00524         break;
00525     s = s->next();
00526     }
00527 
00528     if ( !s )
00529     return FALSE;
00530 
00531     setParag( s, FALSE );
00532     int y = s->rect().y();
00533     int lines = s->lines();
00534     KoTextStringChar *chr = 0;
00535     int index = 0;
00536     int i = 0;
00537     int cy = 0;
00538     //int ch = 0;
00539     for ( ; i < lines; ++i ) {
00540     chr = s->lineStartOfLine( i, &index );
00541     cy = s->lineY( i );
00542     //ch = s->lineHeight( i );
00543     if ( !chr )
00544         return FALSE;
00545     if ( i < lines - 1 && pos.y() >= y + cy && pos.y() <= y + s->lineY( i+1 ) )
00546         break;
00547     }
00548     int nextLine;
00549     if ( i < lines - 1 )
00550     s->lineStartOfLine( i+1, &nextLine );
00551     else
00552     nextLine = s->length();
00553     i = index;
00554     int x = s->rect().x();
00555     if ( pos.x() < x )
00556     pos.setX( x + 1 );
00557     int cw;
00558     int curpos = s->length()-1;
00559     int dist = 10000000;
00560     while ( i < nextLine ) {
00561     chr = s->at(i);
00562     int cpos = x + chr->x;
00563     cw = chr->width; //s->string()->width( i );
00564     if ( chr->isCustom() ) {
00565              if ( pos.x() >= cpos && pos.x() <= cpos + cw &&
00566                   pos.y() >= y + cy && pos.y() <= y + cy + chr->height() ) {
00567                 if ( customItemIndex )
00568                     *customItemIndex = i;
00569         }
00570     }
00571         if( chr->rightToLeft )
00572             cpos += cw;
00573         int d = cpos - pos.x();
00574         bool dm = d < 0 ? !chr->rightToLeft : chr->rightToLeft;
00575         if ( (QABS( d ) < dist || (dist == d && dm == TRUE )) && string->string()->validCursorPosition( i ) ) {
00576             dist = QABS( d );
00577             if ( !link || pos.x() >= x + chr->x ) {
00578                 curpos = i;
00579             }
00580         }
00581     i++;
00582     }
00583     setIndex( curpos, FALSE );
00584 
00585     return TRUE;
00586 }
00587 
00588 void KoTextCursor::gotoRight()
00589 {
00590     if ( string->string()->isRightToLeft() )
00591     gotoPreviousLetter();
00592     else
00593     gotoNextLetter();
00594 }
00595 
00596 void KoTextCursor::gotoNextLetter()
00597 {
00598     tmpIndex = -1;
00599 
00600     int len = string->length() - 1;
00601     if ( idx < len ) {
00602         idx = string->string()->nextCursorPosition( idx );
00603     } else if ( string->next() ) {
00604     string = string->next();
00605     while ( !string->isVisible() )
00606         string = string->next();
00607     idx = 0;
00608     }
00609 }
00610 
00611 void KoTextCursor::gotoUp()
00612 {
00613     int indexOfLineStart;
00614     int line;
00615     KoTextStringChar *c = string->lineStartOfChar( idx, &indexOfLineStart, &line );
00616     if ( !c )
00617     return;
00618 
00619     tmpIndex = QMAX( tmpIndex, idx - indexOfLineStart );
00620     if ( indexOfLineStart == 0 ) {
00621     if ( !string->prev() ) {
00622             return;
00623     }
00624     string = string->prev();
00625     while ( !string->isVisible() )
00626         string = string->prev();
00627     int lastLine = string->lines() - 1;
00628     if ( !string->lineStartOfLine( lastLine, &indexOfLineStart ) )
00629         return;
00630     if ( indexOfLineStart + tmpIndex < string->length() )
00631         idx = indexOfLineStart + tmpIndex;
00632     else
00633         idx = string->length() - 1;
00634     } else {
00635     --line;
00636     int oldIndexOfLineStart = indexOfLineStart;
00637     if ( !string->lineStartOfLine( line, &indexOfLineStart ) )
00638         return;
00639     if ( indexOfLineStart + tmpIndex < oldIndexOfLineStart )
00640         idx = indexOfLineStart + tmpIndex;
00641     else
00642         idx = oldIndexOfLineStart - 1;
00643     }
00644     fixCursorPosition();
00645 }
00646 
00647 void KoTextCursor::gotoDown()
00648 {
00649     int indexOfLineStart;
00650     int line;
00651     KoTextStringChar *c = string->lineStartOfChar( idx, &indexOfLineStart, &line );
00652     if ( !c )
00653     return;
00654 
00655     tmpIndex = QMAX( tmpIndex, idx - indexOfLineStart );
00656     if ( line == string->lines() - 1 ) {
00657     if ( !string->next() ) {
00658             return;
00659     }
00660     string = string->next();
00661     while ( !string->isVisible() )
00662         string = string->next();
00663     if ( !string->lineStartOfLine( 0, &indexOfLineStart ) )
00664         return;
00665     int end;
00666     if ( string->lines() == 1 )
00667         end = string->length();
00668     else
00669         string->lineStartOfLine( 1, &end );
00670     if ( indexOfLineStart + tmpIndex < end )
00671         idx = indexOfLineStart + tmpIndex;
00672     else
00673         idx = end - 1;
00674     } else {
00675     ++line;
00676     int end;
00677     if ( line == string->lines() - 1 )
00678         end = string->length();
00679     else
00680         string->lineStartOfLine( line + 1, &end );
00681     if ( !string->lineStartOfLine( line, &indexOfLineStart ) )
00682         return;
00683     if ( indexOfLineStart + tmpIndex < end )
00684         idx = indexOfLineStart + tmpIndex;
00685     else
00686         idx = end - 1;
00687     }
00688     fixCursorPosition();
00689 }
00690 
00691 void KoTextCursor::gotoLineEnd()
00692 {
00693     tmpIndex = -1;
00694     int indexOfLineStart;
00695     int line;
00696     KoTextStringChar *c = string->lineStartOfChar( idx, &indexOfLineStart, &line );
00697     if ( !c )
00698     return;
00699 
00700     if ( line == string->lines() - 1 ) {
00701     idx = string->length() - 1;
00702     } else {
00703     c = string->lineStartOfLine( ++line, &indexOfLineStart );
00704     indexOfLineStart--;
00705     idx = indexOfLineStart;
00706     }
00707 }
00708 
00709 void KoTextCursor::gotoLineStart()
00710 {
00711     tmpIndex = -1;
00712     int indexOfLineStart;
00713     int line;
00714     KoTextStringChar *c = string->lineStartOfChar( idx, &indexOfLineStart, &line );
00715     if ( !c )
00716     return;
00717 
00718     idx = indexOfLineStart;
00719 }
00720 
00721 void KoTextCursor::gotoHome()
00722 {
00723     tmpIndex = -1;
00724     if ( doc )
00725     string = doc->firstParag();
00726     idx = 0;
00727 }
00728 
00729 void KoTextCursor::gotoEnd()
00730 {
00731     // This can happen in a no-auto-resize frame with overflowing contents.
00732     // Don't prevent going to the end of the text, even if it's not visible.
00733     //if ( doc && !doc->lastParag()->isValid() )
00734     //{
00735 //  kdDebug(32500) << "Last parag, " << doc->lastParag()->paragId() << ", is invalid - aborting gotoEnd() !" << endl;
00736 //  return;
00737 //    }
00738 
00739     tmpIndex = -1;
00740     if ( doc )
00741     string = doc->lastParag();
00742     idx = string->length() - 1;
00743 }
00744 
00745 void KoTextCursor::gotoPageUp( int visibleHeight )
00746 {
00747     tmpIndex = -1;
00748     KoTextParag *s = string;
00749     int h = visibleHeight;
00750     int y = s->rect().y();
00751     while ( s ) {
00752     if ( y - s->rect().y() >= h )
00753         break;
00754     s = s->prev();
00755     }
00756 
00757     if ( !s && doc )
00758     s = doc->firstParag();
00759 
00760     string = s;
00761     idx = 0;
00762 }
00763 
00764 void KoTextCursor::gotoPageDown( int visibleHeight )
00765 {
00766     tmpIndex = -1;
00767     KoTextParag *s = string;
00768     int h = visibleHeight;
00769     int y = s->rect().y();
00770     while ( s ) {
00771     if ( s->rect().y() - y >= h )
00772         break;
00773     s = s->next();
00774     }
00775 
00776     if ( !s && doc ) {
00777     s = doc->lastParag();
00778     string = s;
00779     idx = string->length() - 1;
00780     return;
00781     }
00782 
00783     if ( !s->isValid() )
00784     return;
00785 
00786     string = s;
00787     idx = 0;
00788 }
00789 
00790 void KoTextCursor::gotoWordRight()
00791 {
00792     if ( string->string()->isRightToLeft() )
00793     gotoPreviousWord();
00794     else
00795     gotoNextWord();
00796 }
00797 
00798 void KoTextCursor::gotoWordLeft()
00799 {
00800     if ( string->string()->isRightToLeft() )
00801     gotoNextWord();
00802     else
00803     gotoPreviousWord();
00804 }
00805 
00806 void KoTextCursor::gotoPreviousWord()
00807 {
00808     gotoPreviousLetter();
00809     tmpIndex = -1;
00810     KoTextString *s = string->string();
00811     bool allowSame = FALSE;
00812     if ( idx == ( (int)s->length()-1 ) )
00813         return;
00814     for ( int i = idx; i >= 0; --i ) {
00815     if ( s->at( i ).c.isSpace() || s->at( i ).c == '\t' || s->at( i ).c == '.' ||
00816          s->at( i ).c == ',' || s->at( i ).c == ':' || s->at( i ).c == ';' ) {
00817         if ( !allowSame )
00818         continue;
00819         idx = i + 1;
00820         return;
00821     }
00822     if ( !allowSame && !( s->at( i ).c.isSpace() || s->at( i ).c == '\t' || s->at( i ).c == '.' ||
00823                   s->at( i ).c == ',' || s->at( i ).c == ':' || s->at( i ).c == ';'  ) )
00824         allowSame = TRUE;
00825     }
00826     idx = 0;
00827 }
00828 
00829 void KoTextCursor::gotoNextWord()
00830 {
00831     tmpIndex = -1;
00832     KoTextString *s = string->string();
00833     bool allowSame = FALSE;
00834     for ( int i = idx; i < (int)s->length(); ++i ) {
00835     if ( ! ( s->at( i ).c.isSpace() || s->at( i ).c == '\t' || s->at( i ).c == '.' ||
00836          s->at( i ).c == ',' || s->at( i ).c == ':' || s->at( i ).c == ';' ) ) {
00837         if ( !allowSame )
00838         continue;
00839         idx = i;
00840         return;
00841     }
00842     if ( !allowSame && ( s->at( i ).c.isSpace() || s->at( i ).c == '\t' || s->at( i ).c == '.' ||
00843                   s->at( i ).c == ',' || s->at( i ).c == ':' || s->at( i ).c == ';'  ) )
00844         allowSame = TRUE;
00845     }
00846 
00847     if ( idx < ((int)s->length()-1) ) {
00848         gotoLineEnd();
00849     } else if ( string->next() ) {
00850     string = string->next();
00851     while ( !string->isVisible() )
00852         string = string->next();
00853     idx = 0;
00854     } else {
00855     gotoLineEnd();
00856     }
00857 }
00858 
00859 bool KoTextCursor::atParagStart() const
00860 {
00861     return idx == 0;
00862 }
00863 
00864 bool KoTextCursor::atParagEnd() const
00865 {
00866     return idx == string->length() - 1;
00867 }
00868 
00869 void KoTextCursor::splitAndInsertEmptyParag( bool ind, bool updateIds )
00870 {
00871     if ( !doc )
00872     return;
00873     tmpIndex = -1;
00874     KoTextFormat *f = 0;
00875     if ( doc->useFormatCollection() ) {
00876     f = string->at( idx )->format();
00877     if ( idx == string->length() - 1 && idx > 0 )
00878         f = string->at( idx - 1 )->format();
00879     if ( f->isMisspelled() ) {
00880             KoTextFormat fNoMisspelled( *f );
00881             fNoMisspelled.setMisspelled( false );
00882         f = doc->formatCollection()->format( &fNoMisspelled );
00883     }
00884     }
00885 
00886     if ( atParagEnd() ) {
00887     KoTextParag *n = string->next();
00888     KoTextParag *s = doc->createParag( doc, string, n, updateIds );
00889     if ( f )
00890         s->setFormat( 0, 1, f, TRUE );
00891     s->copyParagData( string );
00892 #if 0
00893     if ( ind ) {
00894         int oi, ni;
00895         s->indent( &oi, &ni );
00896         string = s;
00897         idx = ni;
00898     } else
00899 #endif
00900         {
00901         string = s;
00902         idx = 0;
00903     }
00904     } else if ( atParagStart() ) {
00905     KoTextParag *p = string->prev();
00906     KoTextParag *s = doc->createParag( doc, p, string, updateIds );
00907     if ( f )
00908         s->setFormat( 0, 1, f, TRUE );
00909     s->copyParagData( string );
00910     if ( ind ) {
00911         //s->indent();
00912         s->format();
00913         //indent();
00914         string->format();
00915     }
00916     } else {
00917     QString str = string->string()->toString().mid( idx, 0xFFFFFF );
00918     KoTextParag *n = string->next();
00919     KoTextParag *s = doc->createParag( doc, string, n, updateIds );
00920     s->copyParagData( string );
00921     s->remove( 0, 1 );
00922     s->append( str, TRUE );
00923     for ( uint i = 0; i < str.length(); ++i ) {
00924             KoTextStringChar* tsc = string->at( idx + i );
00925         s->setFormat( i, 1, tsc->format(), TRUE );
00926         if ( tsc->isCustom() ) {
00927         KoTextCustomItem * item = tsc->customItem();
00928         s->at( i )->setCustomItem( item );
00929         tsc->loseCustomItem();
00930 #if 0
00931         s->addCustomItem();
00932         string->removeCustomItem();
00933 #endif
00934         doc->unregisterCustomItem( item, string );
00935         doc->registerCustomItem( item, s );
00936         }
00937     }
00938     string->truncate( idx );
00939 #if 0
00940     if ( ind ) {
00941         int oi, ni;
00942         s->indent( &oi, &ni );
00943         string = s;
00944         idx = ni;
00945     } else
00946 #endif
00947         {
00948         string = s;
00949         idx = 0;
00950     }
00951     }
00952 }
00953 
00954 bool KoTextCursor::removePreviousChar()
00955 {
00956     tmpIndex = -1;
00957     if ( !atParagStart() ) {
00958     string->remove( idx-1, 1 );
00959     idx--;
00960     // shouldn't be needed, just to make sure.
00961     fixCursorPosition();
00962     string->format( -1, TRUE );
00963     //else if ( string->document() && string->document()->parent() )
00964     //    string->document()->nextDoubleBuffered = TRUE;
00965     return FALSE;
00966     } else if ( string->prev() ) {
00967     string = string->prev();
00968     string->join( string->next() );
00969     string->invalidateCounters();
00970     return TRUE;
00971     }
00972     return FALSE;
00973 }
00974 
00975 bool KoTextCursor::remove()
00976 {
00977     tmpIndex = -1;
00978     if ( !atParagEnd() ) {
00979     int next = string->string()->nextCursorPosition( idx );
00980     string->remove( idx, next-idx );
00981     string->format( -1, TRUE );
00982     //else if ( doc && doc->parent() )
00983     //    doc->nextDoubleBuffered = TRUE;
00984     return FALSE;
00985     } else if ( string->next() ) {
00986     if ( string->length() == 1 ) {
00987         string->next()->setPrev( string->prev() );
00988         if ( string->prev() )
00989         string->prev()->setNext( string->next() );
00990         KoTextParag *p = string->next();
00991         delete string;
00992         string = p;
00993         string->invalidate( 0 );
00995             string->invalidateCounters();
00997         KoTextParag *s = string;
00998         while ( s ) {
00999         s->id = s->p ? s->p->id + 1 : 0;
01000         //s->state = -1;
01001         //s->needPreProcess = TRUE;
01002         s->changed = TRUE;
01003         s = s->n;
01004         }
01005         string->format();
01006     } else {
01007         string->join( string->next() );
01008     }
01009     return TRUE;
01010     }
01011     return FALSE;
01012 }
01013 
01014 void KoTextCursor::killLine()
01015 {
01016     if ( atParagEnd() )
01017     return;
01018     string->remove( idx, string->length() - idx - 1 );
01019     string->format( -1, TRUE );
01020     //else if ( doc && doc->parent() )
01021     //doc->nextDoubleBuffered = TRUE;
01022 }
01023 
01024 #if 0
01025 void KoTextCursor::indent()
01026 {
01027     int oi = 0, ni = 0;
01028     string->indent( &oi, &ni );
01029     if ( oi == ni )
01030     return;
01031 
01032     if ( idx >= oi )
01033     idx += ni - oi;
01034     else
01035     idx = ni;
01036 }
01037 #endif
01038 
01039 void KoTextCursor::setDocument( KoTextDocument *d )
01040 {
01041     doc = d;
01042     string = d->firstParag();
01043     idx = 0;
01044     tmpIndex = -1;
01045 }
01046 
01047 
01048 int KoTextCursor::x() const
01049 {
01050     KoTextStringChar *c = string->at( idx );
01051     int curx = c->x;
01052     if ( c->rightToLeft )
01053         curx += c->width; //string->string()->width( idx );
01054     return curx;
01055 }
01056 
01057 int KoTextCursor::y() const
01058 {
01059     int dummy, line;
01060     string->lineStartOfChar( idx, &dummy, &line );
01061     return string->lineY( line );
01062 }
01063 
01064 
01065 void KoTextCursor::fixCursorPosition()
01066 {
01067     // searches for the closest valid cursor position
01068     if ( string->string()->validCursorPosition( idx ) )
01069     return;
01070 
01071     int lineIdx;
01072     KoTextStringChar *start = string->lineStartOfChar( idx, &lineIdx, 0 );
01073     int x = string->string()->at( idx ).x;
01074     int diff = QABS(start->x - x);
01075     int best = lineIdx;
01076 
01077     KoTextStringChar *c = start;
01078     ++c;
01079 
01080     KoTextStringChar *end = &string->string()->at( string->length()-1 );
01081     while ( c <= end && !c->lineStart ) {
01082     int xp = c->x;
01083     if ( c->rightToLeft )
01084         xp += c->pixelwidth; //string->string()->width( lineIdx + (c-start) );
01085     int ndiff = QABS(xp - x);
01086     if ( ndiff < diff && string->string()->validCursorPosition(lineIdx + (c-start)) ) {
01087         diff = ndiff;
01088         best = lineIdx + (c-start);
01089     }
01090     ++c;
01091     }
01092     idx = best;
01093 }
01094 
01095 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
01096 
01097 KoTextString::KoTextString()
01098 {
01099     bidiDirty = TRUE;
01100     bNeedsSpellCheck = true;
01101     bidi = FALSE;
01102     rightToLeft = FALSE;
01103     dir = QChar::DirON;
01104 }
01105 
01106 KoTextString::KoTextString( const KoTextString &s )
01107 {
01108     bidiDirty = s.bidiDirty;
01109     bNeedsSpellCheck = s.bNeedsSpellCheck;
01110     bidi = s.bidi;
01111     rightToLeft = s.rightToLeft;
01112     dir = s.dir;
01113     data = s.data;
01114     data.detach();
01115     for ( int i = 0; i < (int)data.size(); ++i ) {
01116         KoTextFormat *f = data[i].format();
01117         if ( f )
01118             f->addRef();
01119     }
01120 }
01121 
01122 void KoTextString::insert( int index, const QString &s, KoTextFormat *f )
01123 {
01124     int os = data.size();
01125     data.resize( data.size() + s.length() );
01126     if ( index < os ) {
01127     memmove( data.data() + index + s.length(), data.data() + index,
01128          sizeof( KoTextStringChar ) * ( os - index ) );
01129     }
01130     for ( int i = 0; i < (int)s.length(); ++i ) {
01131     KoTextStringChar &ch = data[ (int)index + i ];
01132     ch.x = 0;
01133     ch.pixelxadj = 0;
01134     ch.pixelwidth = 0;
01135     ch.width = 0;
01136     ch.lineStart = 0;
01137     ch.d.format = 0;
01138     ch.type = KoTextStringChar::Regular;
01139     ch.rightToLeft = 0;
01140     ch.startOfRun = 0;
01141         ch.c = s[ i ];
01142 #ifdef DEBUG_COLLECTION
01143     kdDebug(32500) << "KoTextString::insert setting format " << f << " to character " << (int)index+i << endl;
01144 #endif
01145     ch.setFormat( f );
01146     }
01147     bidiDirty = TRUE;
01148     bNeedsSpellCheck = true;
01149 }
01150 
01151 KoTextString::~KoTextString()
01152 {
01153     clear();
01154 }
01155 
01156 void KoTextString::insert( int index, KoTextStringChar *c )
01157 {
01158     int os = data.size();
01159     data.resize( data.size() + 1 );
01160     if ( index < os ) {
01161     memmove( data.data() + index + 1, data.data() + index,
01162          sizeof( KoTextStringChar ) * ( os - index ) );
01163     }
01164     KoTextStringChar &ch = data[ (int)index ];
01165     ch.c = c->c;
01166     ch.x = 0;
01167     ch.pixelxadj = 0;
01168     ch.pixelwidth = 0;
01169     ch.width = 0;
01170     ch.lineStart = 0;
01171     ch.rightToLeft = 0;
01172     ch.d.format = 0;
01173     ch.type = KoTextStringChar::Regular;
01174     ch.setFormat( c->format() );
01175     bidiDirty = TRUE;
01176     bNeedsSpellCheck = true;
01177 }
01178 
01179 void KoTextString::truncate( int index )
01180 {
01181     index = QMAX( index, 0 );
01182     index = QMIN( index, (int)data.size() - 1 );
01183     if ( index < (int)data.size() ) {
01184     for ( int i = index + 1; i < (int)data.size(); ++i ) {
01185         KoTextStringChar &ch = data[ i ];
01186         if ( ch.isCustom() ) {
01187         delete ch.customItem();
01188         if ( ch.d.custom->format )
01189             ch.d.custom->format->removeRef();
01190         delete ch.d.custom;
01191         ch.d.custom = 0;
01192         } else if ( ch.format() ) {
01193         ch.format()->removeRef();
01194         }
01195     }
01196     }
01197     data.truncate( index );
01198     bidiDirty = TRUE;
01199     bNeedsSpellCheck = true;
01200 }
01201 
01202 void KoTextString::remove( int index, int len )
01203 {
01204     for ( int i = index; i < (int)data.size() && i - index < len; ++i ) {
01205     KoTextStringChar &ch = data[ i ];
01206     if ( ch.isCustom() ) {
01207         delete ch.customItem();
01208         if ( ch.d.custom->format )
01209         ch.d.custom->format->removeRef();
01210             delete ch.d.custom;
01211         ch.d.custom = 0;
01212     } else if ( ch.format() ) {
01213         ch.format()->removeRef();
01214     }
01215     }
01216     memmove( data.data() + index, data.data() + index + len,
01217          sizeof( KoTextStringChar ) * ( data.size() - index - len ) );
01218     data.resize( data.size() - len, QGArray::SpeedOptim );
01219     bidiDirty = TRUE;
01220     bNeedsSpellCheck = true;
01221 }
01222 
01223 void KoTextString::clear()
01224 {
01225     for ( int i = 0; i < (int)data.count(); ++i ) {
01226     KoTextStringChar &ch = data[ i ];
01227     if ( ch.isCustom() ) {
01228         delete ch.customItem();
01229         if ( ch.d.custom->format )
01230         ch.d.custom->format->removeRef();
01231         delete ch.d.custom;
01232         ch.d.custom = 0;
01233     } else if ( ch.format() ) {
01234         ch.format()->removeRef();
01235     }
01236     }
01237     data.resize( 0 );
01238 }
01239 
01240 void KoTextString::setFormat( int index, KoTextFormat *f, bool useCollection, bool setFormatAgain )
01241 {
01242     KoTextStringChar &ch = data[ index ];
01243 //    kdDebug(32500) << "KoTextString::setFormat index=" << index << " f=" << f << endl;
01244     if ( useCollection && ch.format() )
01245     {
01246     //kdDebug(32500) << "KoTextString::setFormat removing ref on old format " << ch.format() << endl;
01247     ch.format()->removeRef();
01248     }
01249     ch.setFormat( f, setFormatAgain );
01250 }
01251 
01252 void KoTextString::checkBidi() const
01253 {
01254     KoTextString *that = (KoTextString *)this;
01255     that->bidiDirty = FALSE;
01256     int length = data.size();
01257     if ( !length ) {
01258         that->bidi = FALSE;
01259         that->rightToLeft = dir == QChar::DirR;
01260         return;
01261     }
01262     const KoTextStringChar *start = data.data();
01263     const KoTextStringChar *end = start + length;
01264 
01265     // determines the properties we need for layouting
01266     QTextEngine textEngine( toString(), 0 );
01267     textEngine.direction = (QChar::Direction) dir;
01268     textEngine.itemize(QTextEngine::SingleLine);
01269     const QCharAttributes *ca = textEngine.attributes() + length-1;
01270     KoTextStringChar *ch = (KoTextStringChar *)end - 1;
01271     QScriptItem *item = &textEngine.items[textEngine.items.size()-1];
01272     unsigned char bidiLevel = item->analysis.bidiLevel;
01273     if ( bidiLevel )
01274         that->bidi = TRUE;
01275     int pos = length-1;
01276     while ( ch >= start ) {
01277         if ( item->position > pos ) {
01278             --item;
01279             Q_ASSERT( item >= &textEngine.items[0] );
01280             Q_ASSERT( item < &textEngine.items[textEngine.items.size()] );
01281             bidiLevel = item->analysis.bidiLevel;
01282             if ( bidiLevel )
01283                 that->bidi = TRUE;
01284         }
01285         ch->softBreak = ca->softBreak;
01286         ch->whiteSpace = ca->whiteSpace;
01287         ch->charStop = ca->charStop;
01288         ch->wordStop = ca->wordStop;
01289         //ch->bidiLevel = bidiLevel;
01290         ch->rightToLeft = (bidiLevel%2);
01291         --ch;
01292         --ca;
01293         --pos;
01294     }
01295 
01296     if ( dir == QChar::DirR ) {
01297         that->bidi = TRUE;
01298         that->rightToLeft = TRUE;
01299     } else if ( dir == QChar::DirL ) {
01300         that->rightToLeft = FALSE;
01301     } else {
01302     that->rightToLeft = (textEngine.direction == QChar::DirR);
01303     }
01304 }
01305 
01306 QMemArray<KoTextStringChar> KoTextString::subString( int start, int len ) const
01307 {
01308     if ( len == 0xFFFFFF )
01309     len = data.size();
01310     QMemArray<KoTextStringChar> a;
01311     a.resize( len );
01312     for ( int i = 0; i < len; ++i ) {
01313     KoTextStringChar *c = &data[ i + start ];
01314     a[ i ].c = c->c;
01315     a[ i ].x = 0;
01316     a[ i ].pixelxadj = 0;
01317     a[ i ].pixelwidth = 0;
01318     a[ i ].width = 0;
01319     a[ i ].lineStart = 0;
01320     a[ i ].rightToLeft = 0;
01321     a[ i ].d.format = 0;
01322     a[ i ].type = KoTextStringChar::Regular;
01323     a[ i ].setFormat( c->format() );
01324     if ( c->format() )
01325         c->format()->addRef();
01326     }
01327     return a;
01328 }
01329 
01330 QString KoTextString::mid( int start, int len ) const
01331 {
01332     if ( len == 0xFFFFFF )
01333     len = data.size();
01334     QString res;
01335     res.setLength( len );
01336     for ( int i = 0; i < len; ++i ) {
01337     KoTextStringChar *c = &data[ i + start ];
01338     res[ i ] = c->c;
01339     }
01340     return res;
01341 }
01342 
01343 QString KoTextString::toString( const QMemArray<KoTextStringChar> &data )
01344 {
01345     QString s;
01346     int l = data.size();
01347     s.setUnicode( 0, l );
01348     KoTextStringChar *c = data.data();
01349     QChar *uc = (QChar *)s.unicode();
01350     while ( l-- ) {
01351     *uc = c->c;
01352     uc++;
01353     c++;
01354     }
01355 
01356     return s;
01357 }
01358 
01359 QString KoTextString::toReverseString() const
01360 {
01361     QString s;
01362     int l = length();
01363     s.setUnicode(0, l);
01364     KoTextStringChar *c = data.data() + (l-1);
01365     QChar *uc = (QChar *)s.unicode();
01366     while ( l-- ) {
01367     *uc = c->c;
01368     uc++;
01369     c--;
01370     }
01371 
01372     return s;
01373 }
01374 
01375 QString KoTextString::stringToSpellCheck()
01376 {
01377     if ( !bNeedsSpellCheck )
01378         return QString::null;
01379 
01380     bNeedsSpellCheck = false;
01381     if ( length() <= 1 )
01382         return QString::null;
01383 
01384     QString str = toString();
01385     str.truncate( str.length() - 1 ); // remove trailing space
01386     return str;
01387 }
01388 
01389 int KoTextString::nextCursorPosition( int next )
01390 {
01391     if ( bidiDirty )
01392         checkBidi();
01393 
01394     const KoTextStringChar *c = data.data();
01395     int len = length();
01396 
01397     if ( next < len - 1 ) {
01398         next++;
01399         while ( next < len - 1 && !c[next].charStop )
01400             next++;
01401     }
01402     return next;
01403 }
01404 
01405 int KoTextString::previousCursorPosition( int prev )
01406 {
01407     if ( bidiDirty )
01408         checkBidi();
01409 
01410     const KoTextStringChar *c = data.data();
01411 
01412     if ( prev ) {
01413         prev--;
01414         while ( prev && !c[prev].charStop )
01415             prev--;
01416     }
01417     return prev;
01418 }
01419 
01420 bool KoTextString::validCursorPosition( int idx )
01421 {
01422     if ( bidiDirty )
01423         checkBidi();
01424 
01425     return (at( idx ).charStop);
01426 }
01427 
01429 
01430 void KoTextStringChar::setFormat( KoTextFormat *f, bool setFormatAgain )
01431 {
01432     if ( type == Regular ) {
01433     d.format = f;
01434     } else {
01435     if ( !d.custom ) {
01436         d.custom = new CustomData;
01437         d.custom->custom = 0;
01438     }
01439     d.custom->format = f;
01440         if ( d.custom->custom && setFormatAgain )
01441             d.custom->custom->setFormat( f );
01442     }
01443 }
01444 
01445 void KoTextStringChar::setCustomItem( KoTextCustomItem *i )
01446 {
01447     if ( type == Regular ) {
01448     KoTextFormat *f = format();
01449     d.custom = new CustomData;
01450     d.custom->format = f;
01451     type = Custom;
01452     } else {
01453     delete d.custom->custom;
01454     }
01455     d.custom->custom = i;
01456 }
01457 
01458 void KoTextStringChar::loseCustomItem() // setRegular() might be a better name
01459 {
01460     if ( isCustom() ) {
01461     KoTextFormat *f = d.custom->format;
01462     d.custom->custom = 0;
01463     delete d.custom;
01464     type = Regular;
01465     d.format = f;
01466     }
01467 }
01468 
01469 KoTextStringChar::~KoTextStringChar()
01470 {
01471     if ( format() )
01472     format()->removeRef();
01473     switch ( type ) {
01474     case Custom:
01475         delete d.custom; break;
01476     default:
01477         break;
01478     }
01479 }
01480 
01481 int KoTextStringChar::height() const
01482 {
01483     return !isCustom() ? format()->height() : ( customItem()->placement() == KoTextCustomItem::PlaceInline ? customItem()->height : 0 );
01484 }
01485 
01486 int KoTextStringChar::ascent() const
01487 {
01488     return !isCustom() ? format()->ascent() : ( customItem()->placement() == KoTextCustomItem::PlaceInline ? customItem()->ascent() : 0 );
01489 }
01490 
01491 int KoTextStringChar::descent() const
01492 {
01493     return !isCustom() ? format()->descent() : 0;
01494 }
01495 
01496 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
01497 
01498 KoTextFormatterBase::KoTextFormatterBase()
01499     : wrapColumn( -1 ), //wrapEnabled( TRUE ),
01500       m_bViewFormattingChars( false ),
01501       biw( true /*default in kotext*/ )
01502 {
01503 }
01504 
01505 #ifdef BIDI_DEBUG
01506 #include <iostream>
01507 #endif
01508 
01509 // collects one line of the paragraph and transforms it to visual order
01510 KoTextParagLineStart *KoTextFormatterBase::bidiReorderLine( KoTextParag * /*parag*/, KoTextString *text, KoTextParagLineStart *line,
01511                             KoTextStringChar *startChar, KoTextStringChar *lastChar, int align, int space )
01512 {
01513     int start = (startChar - &text->at(0));
01514     int last = (lastChar - &text->at(0) );
01515     //kdDebug(32500) << "doing BiDi reordering from " << start << " to " << last << "!" << endl;
01516 
01517     KoBidiControl *control = new KoBidiControl( line->context(), line->status );
01518     QString str;
01519     str.setUnicode( 0, last - start + 1 );
01520     // fill string with logically ordered chars.
01521     KoTextStringChar *ch = startChar;
01522     QChar *qch = (QChar *)str.unicode();
01523     while ( ch <= lastChar ) {
01524     *qch = ch->c;
01525     qch++;
01526     ch++;
01527     }
01528     int x = startChar->x;
01529 
01530     QPtrList<KoTextRun> *runs;
01531     runs = KoComplexText::bidiReorderLine(control, str, 0, last - start + 1,
01532                      (text->isRightToLeft() ? QChar::DirR : QChar::DirL) );
01533 
01534     // now construct the reordered string out of the runs...
01535 
01536     int numSpaces = 0;
01537     // set the correct alignment. This is a bit messy....
01538     if( align == Qt::AlignAuto ) {
01539     // align according to directionality of the paragraph...
01540     if ( text->isRightToLeft() )
01541         align = Qt::AlignRight;
01542     }
01543 
01544     if ( align & Qt::AlignHCenter )
01545     x += space/2;
01546     else if ( align & Qt::AlignRight )
01547     x += space;
01548     else if ( align & Qt::AlignJustify ) {
01549     for ( int j = start; j < last; ++j ) {
01550         if( isBreakable( text, j ) ) {
01551         numSpaces++;
01552         }
01553     }
01554     }
01555     int toAdd = 0;
01556     bool first = TRUE;
01557     KoTextRun *r = runs->first();
01558     int xmax = -0xffffff;
01559     while ( r ) {
01560     if(r->level %2) {
01561         // odd level, need to reverse the string
01562         int pos = r->stop + start;
01563         while(pos >= r->start + start) {
01564         KoTextStringChar *c = &text->at(pos);
01565         if( numSpaces && !first && isBreakable( text, pos ) ) {
01566             int s = space / numSpaces;
01567             toAdd += s;
01568             space -= s;
01569             numSpaces--;
01570         } else if ( first ) {
01571             first = FALSE;
01572             if ( c->c == ' ' )
01573             x -= c->format()->width( ' ' );
01574         }
01575         c->x = x + toAdd;
01576         c->rightToLeft = TRUE;
01577         c->startOfRun = FALSE;
01578         int ww = 0;
01579         if ( c->c.unicode() >= 32 || c->c == '\t' || c->c == '\n' || c->isCustom() ) {
01580             ww = c->width;
01581         } else {
01582             ww = c->format()->width( ' ' );
01583         }
01584         if ( xmax < x + toAdd + ww ) xmax = x + toAdd + ww;
01585         x += ww;
01586         pos--;
01587         }
01588     } else {
01589         int pos = r->start + start;
01590         while(pos <= r->stop + start) {
01591         KoTextStringChar* c = &text->at(pos);
01592         if( numSpaces && !first && isBreakable( text, pos ) ) {
01593             int s = space / numSpaces;
01594             toAdd += s;
01595             space -= s;
01596             numSpaces--;
01597         } else if ( first ) {
01598             first = FALSE;
01599             if ( c->c == ' ' )
01600             x -= c->format()->width( ' ' );
01601         }
01602         c->x = x + toAdd;
01603         c->rightToLeft = FALSE;
01604         c->startOfRun = FALSE;
01605         int ww = 0;
01606         if ( c->c.unicode() >= 32 || c->c == '\t' || c->isCustom() ) {
01607             ww = c->width;
01608         } else {
01609             ww = c->format()->width( ' ' );
01610         }
01611         //kdDebug(32500) << "setting char " << pos << " at pos " << x << endl;
01612         if ( xmax < x + toAdd + ww ) xmax = x + toAdd + ww;
01613         x += ww;
01614         pos++;
01615         }
01616     }
01617     text->at( r->start + start ).startOfRun = TRUE;
01618     r = runs->next();
01619     }
01620 
01621     line->w = xmax + 10;
01622     KoTextParagLineStart *ls = new KoTextParagLineStart( control->context, control->status );
01623     delete control;
01624     delete runs;
01625     return ls;
01626 }
01627 
01628 bool KoTextFormatterBase::isStretchable( KoTextString *string, int pos ) const
01629 {
01630     if ( string->at( pos ).c == QChar(160) ) //non-breaking space
01631     return true;
01632     KoTextStringChar& chr = string->at( pos );
01633     return chr.whiteSpace;
01634     //return isBreakable( string, pos );
01635 }
01636 
01637 bool KoTextFormatterBase::isBreakable( KoTextString *string, int pos ) const
01638 {
01639     //if (string->at(pos).nobreak)
01640     //    return FALSE;
01641     return (pos < string->length()-1 && string->at(pos+1).softBreak);
01642 }
01643 
01644 void KoTextParag::insertLineStart( int index, KoTextParagLineStart *ls )
01645 {
01646     // This tests if we break at the same character in more than one line,
01647     // i.e. there no space even for _one_ char in a given line.
01648     // However this shouldn't happen, KoTextFormatter prevents it, otherwise
01649     // we could loop forever (e.g. if one char is wider than the page...)
01650 #ifndef NDEBUG
01651     QMap<int, KoTextParagLineStart*>::Iterator it;
01652     if ( ( it = lineStarts.find( index ) ) == lineStarts.end() ) {
01653     lineStarts.insert( index, ls );
01654     } else {
01655         kdWarning(32500) << "insertLineStart: there's already a line for char index=" << index << endl;
01656     delete *it;
01657     lineStarts.remove( it );
01658     lineStarts.insert( index, ls );
01659     }
01660 #else // non-debug code, take the fast route
01661     lineStarts.insert( index, ls );
01662 #endif
01663 }
01664 
01665 
01666 /* Standard pagebreak algorithm using KoTextFlow::adjustFlow. Returns
01667  the shift of the paragraphs bottom line.
01668  */
01669 int KoTextFormatterBase::formatVertically( KoTextDocument* doc, KoTextParag* parag )
01670 {
01671     int oldHeight = parag->rect().height();
01672     QMap<int, KoTextParagLineStart*>& lineStarts = parag->lineStartList();
01673     QMap<int, KoTextParagLineStart*>::Iterator it = lineStarts.begin();
01674     int h = doc->addMargins() ? parag->topMargin() : 0;
01675     for ( ; it != lineStarts.end() ; ++it  ) {
01676     KoTextParagLineStart * ls = it.data();
01677     ls->y = h;
01678     KoTextStringChar *c = &parag->string()->at(it.key());
01679     if ( c && c->customItem() && c->customItem()->ownLine() ) {
01680         int h = c->customItem()->height;
01681         c->customItem()->pageBreak( parag->rect().y() + ls->y + ls->baseLine - h, doc->flow() );
01682         int delta = c->customItem()->height - h;
01683         ls->h += delta;
01684         if ( delta )
01685         parag->setMovedDown( TRUE );
01686     } else {
01687         int shift = doc->flow()->adjustFlow( parag->rect().y() + ls->y, ls->w, ls->h );
01688         ls->y += shift;
01689         if ( shift )
01690         parag->setMovedDown( TRUE );
01691     }
01692     h = ls->y + ls->h;
01693     }
01694     int m = parag->bottomMargin();
01695     if ( parag->next() && doc && !doc->addMargins() )
01696     m = QMAX( m, parag->next()->topMargin() );
01697     //if ( parag->next() && parag->next()->isLineBreak() )
01698     //  m = 0;
01699     h += m;
01700     parag->setHeight( h );
01701     return h - oldHeight;
01702 }
01703 
01704 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
01705 
01706 KoTextCustomItem::KoTextCustomItem( KoTextDocument *p )
01707       :  width(-1), height(0), parent(p), xpos(0), ypos(-1), parag(0)
01708 {
01709     m_deleted = false; // added for kotext
01710 }
01711 
01712 KoTextCustomItem::~KoTextCustomItem()
01713 {
01714 }
01715 
01716 KoTextFlow::KoTextFlow()
01717 {
01718     w = 0;
01719     leftItems.setAutoDelete( FALSE );
01720     rightItems.setAutoDelete( FALSE );
01721 }
01722 
01723 KoTextFlow::~KoTextFlow()
01724 {
01725 }
01726 
01727 void KoTextFlow::clear()
01728 {
01729     leftItems.clear();
01730     rightItems.clear();
01731 }
01732 
01733 // Called by KoTextDocument::setWidth
01734 void KoTextFlow::setWidth( int width )
01735 {
01736     w = width;
01737 }
01738 
01739 void KoTextFlow::adjustMargins( int, int, int, int&, int&, int& pageWidth, KoTextParag* )
01740 {
01741     pageWidth = w;
01742 }
01743 
01744 
01745 int KoTextFlow::adjustFlow( int /*y*/, int, int /*h*/ )
01746 {
01747     return 0;
01748 }
01749 
01750 void KoTextFlow::unregisterFloatingItem( KoTextCustomItem* item )
01751 {
01752     leftItems.removeRef( item );
01753     rightItems.removeRef( item );
01754 }
01755 
01756 void KoTextFlow::registerFloatingItem( KoTextCustomItem* item )
01757 {
01758     if ( item->placement() == KoTextCustomItem::PlaceRight ) {
01759     if ( !rightItems.contains( item ) )
01760         rightItems.append( item );
01761     } else if ( item->placement() == KoTextCustomItem::PlaceLeft &&
01762         !leftItems.contains( item ) ) {
01763     leftItems.append( item );
01764     }
01765 }
01766 
01767 int KoTextFlow::availableHeight() const
01768 {
01769     return -1; // no limit
01770 }
01771 
01772 void KoTextFlow::drawFloatingItems( QPainter* p, int cx, int cy, int cw, int ch, const QColorGroup& cg, bool selected )
01773 {
01774     KoTextCustomItem *item;
01775     for ( item = leftItems.first(); item; item = leftItems.next() ) {
01776     if ( item->x() == -1 || item->y() == -1 )
01777         continue;
01778     item->draw( p, item->x(), item->y(), cx, cy, cw, ch, cg, selected );
01779     }
01780 
01781     for ( item = rightItems.first(); item; item = rightItems.next() ) {
01782     if ( item->x() == -1 || item->y() == -1 )
01783         continue;
01784     item->draw( p, item->x(), item->y(), cx, cy, cw, ch, cg, selected );
01785     }
01786 }
01787 
01788 //void KoTextFlow::setPageSize( int ps ) { pagesize = ps; }
01789 bool KoTextFlow::isEmpty() { return leftItems.isEmpty() && rightItems.isEmpty(); }
KDE Logo
This file is part of the documentation for lib Library Version 1.4.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Mon Feb 13 09:40:05 2006 by doxygen 1.4.2 written by Dimitri van Heesch, © 1997-2003