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     kdWarning(32500) << "can't locate parag at " << id << ", last parag: " << doc->lastParag()->paragId() << endl;
00154     return 0;
00155     }
00156 
00157     cursor.setParag( s );
00158     cursor.setIndex( index );
00159     int len = text.size();
00160     if ( c )
00161     *c = cursor;
00162     if ( doc ) {
00163     doc->setSelectionStart( KoTextDocument::Temp, &cursor );
00164     for ( int i = 0; i < len; ++i )
00165         cursor.gotoNextLetter();
00166     doc->setSelectionEnd( KoTextDocument::Temp, &cursor );
00167     doc->removeSelectedText( KoTextDocument::Temp, &cursor );
00168     if ( c )
00169         *c = cursor;
00170     } else {
00171     s->remove( index, len );
00172     }
00173 
00174     return c;
00175 }
00176 
00177 KoTextCursor *KoTextDocDeleteCommand::unexecute( KoTextCursor *c )
00178 {
00179     KoTextParag *s = doc ? doc->paragAt( id ) : parag;
00180     if ( !s ) {
00181     kdWarning(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 QMemArray<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 QMemArray<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, QMemArray<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 //  kdDebug(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->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     kdDebug(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, QGArray::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 //    kdDebug(32500) << "KoTextString::setFormat index=" << index << " f=" << f << endl;
01222     if ( useCollection && ch.format() )
01223     {
01224     //kdDebug(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     // determines the properties we need for layouting
01244     QTextEngine textEngine( toString(), 0 );
01245     textEngine.direction = (QChar::Direction) dir;
01246     textEngine.itemize(QTextEngine::SingleLine);
01247     const QCharAttributes *ca = textEngine.attributes() + length-1;
01248     KoTextStringChar *ch = (KoTextStringChar *)end - 1;
01249     QScriptItem *item = &textEngine.items[textEngine.items.size()-1];
01250     unsigned char bidiLevel = item->analysis.bidiLevel;
01251     if ( bidiLevel )
01252         that->bidi = TRUE;
01253     int pos = length-1;
01254     while ( ch >= start ) {
01255         if ( item->position > pos ) {
01256             --item;
01257             Q_ASSERT( item >= &textEngine.items[0] );
01258             Q_ASSERT( item < &textEngine.items[textEngine.items.size()] );
01259             bidiLevel = item->analysis.bidiLevel;
01260             if ( bidiLevel )
01261                 that->bidi = TRUE;
01262         }
01263         ch->softBreak = ca->softBreak;
01264         ch->whiteSpace = ca->whiteSpace;
01265         ch->charStop = ca->charStop;
01266         ch->wordStop = ca->wordStop;
01267         //ch->bidiLevel = bidiLevel;
01268         ch->rightToLeft = (bidiLevel%2);
01269         --ch;
01270         --ca;
01271         --pos;
01272     }
01273 
01274     if ( dir == QChar::DirR ) {
01275         that->bidi = TRUE;
01276         that->rightToLeft = TRUE;
01277     } else if ( dir == QChar::DirL ) {
01278         that->rightToLeft = FALSE;
01279     } else {
01280     that->rightToLeft = (textEngine.direction == QChar::DirR);
01281     }
01282 }
01283 
01284 QMemArray<KoTextStringChar> KoTextString::subString( int start, int len ) const
01285 {
01286     if ( len == 0xFFFFFF )
01287     len = data.size();
01288     QMemArray<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 QMemArray<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 // collects one line of the paragraph and transforms it to visual order
01488 KoTextParagLineStart *KoTextFormatterBase::bidiReorderLine( KoTextParag * /*parag*/, KoTextString *text, KoTextParagLineStart *line,
01489                             KoTextStringChar *startChar, KoTextStringChar *lastChar, int align, int space )
01490 {
01491     int start = (startChar - &text->at(0));
01492     int last = (lastChar - &text->at(0) );
01493     //kdDebug(32500) << "doing BiDi reordering from " << start << " to " << last << "!" << endl;
01494 
01495     KoBidiControl *control = new KoBidiControl( line->context(), line->status );
01496     QString str;
01497     str.setUnicode( 0, last - start + 1 );
01498     // fill string with logically ordered chars.
01499     KoTextStringChar *ch = startChar;
01500     QChar *qch = (QChar *)str.unicode();
01501     while ( ch <= lastChar ) {
01502     *qch = ch->c;
01503     qch++;
01504     ch++;
01505     }
01506     int x = startChar->x;
01507 
01508     QPtrList<KoTextRun> *runs;
01509     runs = KoComplexText::bidiReorderLine(control, str, 0, last - start + 1,
01510                      (text->isRightToLeft() ? QChar::DirR : QChar::DirL) );
01511 
01512     // now construct the reordered string out of the runs...
01513 
01514     int numSpaces = 0;
01515     // set the correct alignment. This is a bit messy....
01516     if( align == Qt::AlignAuto ) {
01517     // align according to directionality of the paragraph...
01518     if ( text->isRightToLeft() )
01519         align = Qt::AlignRight;
01520     }
01521 
01522     if ( align & Qt::AlignHCenter )
01523     x += space/2;
01524     else if ( align & Qt::AlignRight )
01525     x += space;
01526     else if ( align & Qt::AlignJustify ) {
01527     for ( int j = start; j < last; ++j ) {
01528         if( isBreakable( text, j ) ) {
01529         numSpaces++;
01530         }
01531     }
01532     }
01533     int toAdd = 0;
01534     bool first = TRUE;
01535     KoTextRun *r = runs->first();
01536     int xmax = -0xffffff;
01537     while ( r ) {
01538     if(r->level %2) {
01539         // odd level, need to reverse the string
01540         int pos = r->stop + start;
01541         while(pos >= r->start + start) {
01542         KoTextStringChar *c = &text->at(pos);
01543         if( numSpaces && !first && isBreakable( text, pos ) ) {
01544             int s = space / numSpaces;
01545             toAdd += s;
01546             space -= s;
01547             numSpaces--;
01548         } else if ( first ) {
01549             first = FALSE;
01550             if ( c->c == ' ' )
01551             x -= c->format()->width( ' ' );
01552         }
01553         c->x = x + toAdd;
01554         c->rightToLeft = TRUE;
01555         c->startOfRun = FALSE;
01556         int ww = 0;
01557         if ( c->c.unicode() >= 32 || c->c == '\t' || c->c == '\n' || c->isCustom() ) {
01558             ww = c->width;
01559         } else {
01560             ww = c->format()->width( ' ' );
01561         }
01562         if ( xmax < x + toAdd + ww ) xmax = x + toAdd + ww;
01563         x += ww;
01564         pos--;
01565         }
01566     } else {
01567         int pos = r->start + start;
01568         while(pos <= r->stop + start) {
01569         KoTextStringChar* c = &text->at(pos);
01570         if( numSpaces && !first && isBreakable( text, pos ) ) {
01571             int s = space / numSpaces;
01572             toAdd += s;
01573             space -= s;
01574             numSpaces--;
01575         } else if ( first ) {
01576             first = FALSE;
01577             if ( c->c == ' ' )
01578             x -= c->format()->width( ' ' );
01579         }
01580         c->x = x + toAdd;
01581         c->rightToLeft = FALSE;
01582         c->startOfRun = FALSE;
01583         int ww = 0;
01584         if ( c->c.unicode() >= 32 || c->c == '\t' || c->isCustom() ) {
01585             ww = c->width;
01586         } else {
01587             ww = c->format()->width( ' ' );
01588         }
01589         //kdDebug(32500) << "setting char " << pos << " at pos " << x << endl;
01590         if ( xmax < x + toAdd + ww ) xmax = x + toAdd + ww;
01591         x += ww;
01592         pos++;
01593         }
01594     }
01595     text->at( r->start + start ).startOfRun = TRUE;
01596     r = runs->next();
01597     }
01598 
01599     line->w = xmax + 10;
01600     KoTextParagLineStart *ls = new KoTextParagLineStart( control->context, control->status );
01601     delete control;
01602     delete runs;
01603     return ls;
01604 }
01605 
01606 bool KoTextFormatterBase::isStretchable( KoTextString *string, int pos ) const
01607 {
01608     if ( string->at( pos ).c == QChar(160) ) //non-breaking space
01609     return true;
01610     KoTextStringChar& chr = string->at( pos );
01611     return chr.whiteSpace;
01612     //return isBreakable( string, pos );
01613 }
01614 
01615 bool KoTextFormatterBase::isBreakable( KoTextString *string, int pos ) const
01616 {
01617     //if (string->at(pos).nobreak)
01618     //    return FALSE;
01619     return (pos < string->length()-1 && string->at(pos+1).softBreak);
01620 }
01621 
01622 void KoTextParag::insertLineStart( int index, KoTextParagLineStart *ls )
01623 {
01624     // This tests if we break at the same character in more than one line,
01625     // i.e. there no space even for _one_ char in a given line.
01626     // However this shouldn't happen, KoTextFormatter prevents it, otherwise
01627     // we could loop forever (e.g. if one char is wider than the page...)
01628 #ifndef NDEBUG
01629     QMap<int, KoTextParagLineStart*>::Iterator it;
01630     if ( ( it = lineStarts.find( index ) ) == lineStarts.end() ) {
01631     lineStarts.insert( index, ls );
01632     } else {
01633         kdWarning(32500) << "insertLineStart: there's already a line for char index=" << index << endl;
01634     delete *it;
01635     lineStarts.remove( it );
01636     lineStarts.insert( index, ls );
01637     }
01638 #else // non-debug code, take the fast route
01639     lineStarts.insert( index, ls );
01640 #endif
01641 }
01642 
01643 
01644 /* Standard pagebreak algorithm using KoTextFlow::adjustFlow. Returns
01645  the shift of the paragraphs bottom line.
01646  */
01647 int KoTextFormatterBase::formatVertically( KoTextDocument* doc, KoTextParag* parag )
01648 {
01649     int oldHeight = parag->rect().height();
01650     QMap<int, KoTextParagLineStart*>& lineStarts = parag->lineStartList();
01651     QMap<int, KoTextParagLineStart*>::Iterator it = lineStarts.begin();
01652     int h = doc->addMargins() ? parag->topMargin() : 0;
01653     for ( ; it != lineStarts.end() ; ++it  ) {
01654     KoTextParagLineStart * ls = it.data();
01655     ls->y = h;
01656     KoTextStringChar *c = &parag->string()->at(it.key());
01657     if ( c && c->customItem() && c->customItem()->ownLine() ) {
01658         int h = c->customItem()->height;
01659         c->customItem()->pageBreak( parag->rect().y() + ls->y + ls->baseLine - h, doc->flow() );
01660         int delta = c->customItem()->height - h;
01661         ls->h += delta;
01662         if ( delta )
01663         parag->setMovedDown( TRUE );
01664     } else {
01665         int shift = doc->flow()->adjustFlow( parag->rect().y() + ls->y, ls->w, ls->h );
01666         ls->y += shift;
01667         if ( shift )
01668         parag->setMovedDown( TRUE );
01669     }
01670     h = ls->y + ls->h;
01671     }
01672     int m = parag->bottomMargin();
01673     if ( parag->next() && doc && !doc->addMargins() )
01674     m = QMAX( m, parag->next()->topMargin() );
01675     //if ( parag->next() && parag->next()->isLineBreak() )
01676     //  m = 0;
01677     h += m;
01678     parag->setHeight( h );
01679     return h - oldHeight;
01680 }
01681 
01682 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
01683 
01684 KoTextCustomItem::KoTextCustomItem( KoTextDocument *p )
01685       :  width(-1), height(0), parent(p), xpos(0), ypos(-1), parag(0)
01686 {
01687     m_deleted = false; // added for kotext
01688 }
01689 
01690 KoTextCustomItem::~KoTextCustomItem()
01691 {
01692 }
01693 
01694 KoTextFlow::KoTextFlow()
01695 {
01696     w = 0;
01697     leftItems.setAutoDelete( FALSE );
01698     rightItems.setAutoDelete( FALSE );
01699 }
01700 
01701 KoTextFlow::~KoTextFlow()
01702 {
01703 }
01704 
01705 void KoTextFlow::clear()
01706 {
01707     leftItems.clear();
01708     rightItems.clear();
01709 }
01710 
01711 // Called by KoTextDocument::setWidth
01712 void KoTextFlow::setWidth( int width )
01713 {
01714     w = width;
01715 }
01716 
01717 void KoTextFlow::adjustMargins( int, int, int, int&, int&, int& pageWidth, KoTextParag* )
01718 {
01719     pageWidth = w;
01720 }
01721 
01722 
01723 int KoTextFlow::adjustFlow( int /*y*/, int, int /*h*/ )
01724 {
01725     return 0;
01726 }
01727 
01728 void KoTextFlow::unregisterFloatingItem( KoTextCustomItem* item )
01729 {
01730     leftItems.removeRef( item );
01731     rightItems.removeRef( item );
01732 }
01733 
01734 void KoTextFlow::registerFloatingItem( KoTextCustomItem* item )
01735 {
01736     if ( item->placement() == KoTextCustomItem::PlaceRight ) {
01737     if ( !rightItems.contains( item ) )
01738         rightItems.append( item );
01739     } else if ( item->placement() == KoTextCustomItem::PlaceLeft &&
01740         !leftItems.contains( item ) ) {
01741     leftItems.append( item );
01742     }
01743 }
01744 
01745 int KoTextFlow::availableHeight() const
01746 {
01747     return -1; // no limit
01748 }
01749 
01750 void KoTextFlow::drawFloatingItems( QPainter* p, int cx, int cy, int cw, int ch, const QColorGroup& cg, bool selected )
01751 {
01752     KoTextCustomItem *item;
01753     for ( item = leftItems.first(); item; item = leftItems.next() ) {
01754     if ( item->x() == -1 || item->y() == -1 )
01755         continue;
01756     item->draw( p, item->x(), item->y(), cx, cy, cw, ch, cg, selected );
01757     }
01758 
01759     for ( item = rightItems.first(); item; item = rightItems.next() ) {
01760     if ( item->x() == -1 || item->y() == -1 )
01761         continue;
01762     item->draw( p, item->x(), item->y(), cx, cy, cw, ch, cg, selected );
01763     }
01764 }
01765 
01766 //void KoTextFlow::setPageSize( int ps ) { pagesize = ps; }
01767 bool KoTextFlow::isEmpty() { return leftItems.isEmpty() && rightItems.isEmpty(); }
KDE Home | KDE Accessibility Home | Description of Access Keys