F:/KPlato/koffice/libs/kotext/KoRichText.cpp

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

Généré le Wed Nov 22 23:41:08 2006 pour KPlato par  doxygen 1.5.1-p1