lib

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