lib Library API Documentation

korichtext.cpp

00001 /****************************************************************************
00002 ** Implementation of the internal Qt classes dealing with rich text
00003 **
00004 ** Created : 990101
00005 **
00006 ** Copyright (C) 1992-2000 Trolltech AS.  All rights reserved.
00007 **
00008 ** This file is part of the kernel module of the Qt GUI Toolkit.
00009 **
00010 ** This file may be distributed under the terms of the Q Public License
00011 ** as defined by Trolltech AS of Norway and appearing in the file
00012 ** LICENSE.QPL included in the packaging of this file.
00013 **
00014 ** This file may be distributed and/or modified under the terms of the
00015 ** GNU General Public License version 2 as published by the Free Software
00016 ** Foundation and appearing in the file LICENSE.GPL included in the
00017 ** packaging of this file.
00018 **
00019 ** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
00020 ** licenses may use this file in accordance with the Qt Commercial License
00021 ** Agreement provided with the Software.
00022 **
00023 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00024 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00025 **
00026 ** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
00027 **   information about Qt Commercial License Agreements.
00028 ** See http://www.trolltech.com/qpl/ for QPL licensing information.
00029 ** See http://www.trolltech.com/gpl/ for GPL licensing information.
00030 **
00031 ** Contact info@trolltech.com if any conditions of this licensing are
00032 ** not clear to you.
00033 **
00034 **********************************************************************/
00035 
00036 #include "korichtext.h"
00037 #include "kotextformat.h"
00038 
00039 #include <qpaintdevicemetrics.h>
00040 #include "qdrawutil.h" // for KoTextHorizontalLine
00041 
00042 #include <stdlib.h>
00043 #include "koparagcounter.h"
00044 #include "kotextdocument.h"
00045 #include <kdebug.h>
00046 #include <kdeversion.h>
00047 #if ! KDE_IS_VERSION(3,1,90)
00048 #include <kdebugclasses.h>
00049 #endif
00050 #include <kglobal.h>
00051 #include <klocale.h>
00052 #ifdef INDIC
00053 #include <private/qtextengine_p.h>
00054 #endif
00055 
00056 //#define PARSER_DEBUG
00057 //#define DEBUG_COLLECTION
00058 //#define DEBUG_TABLE_RENDERING
00059 
00060 //static KoTextFormatCollection *qFormatCollection = 0;
00061 
00062 #if defined(PARSER_DEBUG)
00063 static QString debug_indent;
00064 #endif
00065 
00066 static bool is_printer( QPainter *p )
00067 {
00068     return p && p->device() && p->device()->devType() == QInternal::Printer;
00069 }
00070 
00071 static inline int scale( int value, QPainter *painter )
00072 {
00073     if ( is_printer( painter ) ) {
00074     QPaintDeviceMetrics metrics( painter->device() );
00075 #if defined(Q_WS_X11)
00076     value = value * metrics.logicalDpiY() / QPaintDevice::x11AppDpiY();
00077 #elif defined (Q_WS_WIN)
00078     int gdc = GetDeviceCaps( GetDC( 0 ), LOGPIXELSY );
00079     if ( gdc )
00080         value = value * metrics.logicalDpiY() / gdc;
00081 #elif defined (Q_WS_MAC)
00082     value = value * metrics.logicalDpiY() / 75; // ##### FIXME
00083 #elif defined (Q_WS_QWS)
00084     value = value * metrics.logicalDpiY() / 75;
00085 #endif
00086     }
00087     return value;
00088 }
00089 
00090 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
00091 
00092 void KoTextDocCommandHistory::addCommand( KoTextDocCommand *cmd )
00093 {
00094     if ( current < (int)history.count() - 1 ) {
00095     QPtrList<KoTextDocCommand> commands;
00096     commands.setAutoDelete( FALSE );
00097 
00098     for( int i = 0; i <= current; ++i ) {
00099         commands.insert( i, history.at( 0 ) );
00100         history.take( 0 );
00101     }
00102 
00103     commands.append( cmd );
00104     history.clear();
00105     history = commands;
00106     history.setAutoDelete( TRUE );
00107     } else {
00108     history.append( cmd );
00109     }
00110 
00111     if ( (int)history.count() > steps )
00112     history.removeFirst();
00113     else
00114     ++current;
00115 }
00116 
00117 KoTextCursor *KoTextDocCommandHistory::undo( KoTextCursor *c )
00118 {
00119     if ( current > -1 ) {
00120     KoTextCursor *c2 = history.at( current )->unexecute( c );
00121     --current;
00122     return c2;
00123     }
00124     return 0;
00125 }
00126 
00127 KoTextCursor *KoTextDocCommandHistory::redo( KoTextCursor *c )
00128 {
00129     if ( current > -1 ) {
00130     if ( current < (int)history.count() - 1 ) {
00131         ++current;
00132         return history.at( current )->execute( c );
00133     }
00134     } else {
00135     if ( history.count() > 0 ) {
00136         ++current;
00137         return history.at( current )->execute( c );
00138     }
00139     }
00140     return 0;
00141 }
00142 
00143 bool KoTextDocCommandHistory::isUndoAvailable()
00144 {
00145     return current > -1;
00146 }
00147 
00148 bool KoTextDocCommandHistory::isRedoAvailable()
00149 {
00150    return current > -1 && current < (int)history.count() - 1 || current == -1 && history.count() > 0;
00151 }
00152 
00153 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
00154 
00155 KoTextDocDeleteCommand::KoTextDocDeleteCommand( KoTextDocument *d, int i, int idx, const QMemArray<KoTextStringChar> &str )
00156     : KoTextDocCommand( d ), id( i ), index( idx ), parag( 0 ), text( str )
00157 {
00158     for ( int j = 0; j < (int)text.size(); ++j ) {
00159     if ( text[ j ].format() )
00160         text[ j ].format()->addRef();
00161     }
00162 }
00163 
00164 /*KoTextDocDeleteCommand::KoTextDocDeleteCommand( KoTextParag *p, int idx, const QMemArray<KoTextStringChar> &str )
00165     : KoTextDocCommand( 0 ), id( -1 ), index( idx ), parag( p ), text( str )
00166 {
00167     for ( int i = 0; i < (int)text.size(); ++i ) {
00168     if ( text[ i ].format() )
00169         text[ i ].format()->addRef();
00170     }
00171 }*/
00172 
00173 KoTextDocDeleteCommand::~KoTextDocDeleteCommand()
00174 {
00175     for ( int i = 0; i < (int)text.size(); ++i ) {
00176     if ( text[ i ].format() )
00177         text[ i ].format()->removeRef();
00178     }
00179     text.resize( 0 );
00180 }
00181 
00182 KoTextCursor *KoTextDocDeleteCommand::execute( KoTextCursor *c )
00183 {
00184     KoTextParag *s = doc ? doc->paragAt( id ) : parag;
00185     if ( !s ) {
00186     kdWarning(32500) << "can't locate parag at " << id << ", last parag: " << doc->lastParag()->paragId() << endl;
00187     return 0;
00188     }
00189 
00190     cursor.setParag( s );
00191     cursor.setIndex( index );
00192     int len = text.size();
00193     if ( c )
00194     *c = cursor;
00195     if ( doc ) {
00196     doc->setSelectionStart( KoTextDocument::Temp, &cursor );
00197     for ( int i = 0; i < len; ++i )
00198         cursor.gotoNextLetter();
00199     doc->setSelectionEnd( KoTextDocument::Temp, &cursor );
00200     doc->removeSelectedText( KoTextDocument::Temp, &cursor );
00201     if ( c )
00202         *c = cursor;
00203     } else {
00204     s->remove( index, len );
00205     }
00206 
00207     return c;
00208 }
00209 
00210 KoTextCursor *KoTextDocDeleteCommand::unexecute( KoTextCursor *c )
00211 {
00212     KoTextParag *s = doc ? doc->paragAt( id ) : parag;
00213     if ( !s ) {
00214     kdWarning(32500) << "can't locate parag at " << id << ", last parag: " << doc->lastParag()->paragId() << endl;
00215     return 0;
00216     }
00217 
00218     cursor.setParag( s );
00219     cursor.setIndex( index );
00220     QString str = KoTextString::toString( text );
00221     cursor.insert( str, TRUE, &text );
00222     cursor.setParag( s );
00223     cursor.setIndex( index );
00224     if ( c ) {
00225     c->setParag( s );
00226     c->setIndex( index );
00227     for ( int i = 0; i < (int)text.size(); ++i )
00228         c->gotoNextLetter();
00229     }
00230 
00231     s = cursor.parag();
00232     while ( s ) {
00233     s->format();
00234     s->setChanged( TRUE );
00235     if ( s == c->parag() )
00236         break;
00237     s = s->next();
00238     }
00239 
00240     return &cursor;
00241 }
00242 
00243 KoTextDocFormatCommand::KoTextDocFormatCommand( KoTextDocument *d, int sid, int sidx, int eid, int eidx,
00244                     const QMemArray<KoTextStringChar> &old, const KoTextFormat *f, int fl )
00245     : KoTextDocCommand( d ), startId( sid ), startIndex( sidx ), endId( eid ), endIndex( eidx ), oldFormats( old ), flags( fl )
00246 {
00247     format = d->formatCollection()->format( f );
00248     for ( int j = 0; j < (int)oldFormats.size(); ++j ) {
00249     if ( oldFormats[ j ].format() )
00250         oldFormats[ j ].format()->addRef();
00251     }
00252 }
00253 
00254 KoTextDocFormatCommand::~KoTextDocFormatCommand()
00255 {
00256     format->removeRef();
00257     for ( int j = 0; j < (int)oldFormats.size(); ++j ) {
00258     if ( oldFormats[ j ].format() )
00259         oldFormats[ j ].format()->removeRef();
00260     }
00261 }
00262 
00263 KoTextCursor *KoTextDocFormatCommand::execute( KoTextCursor *c )
00264 {
00265     KoTextParag *sp = doc->paragAt( startId );
00266     KoTextParag *ep = doc->paragAt( endId );
00267     if ( !sp || !ep )
00268     return c;
00269 
00270     KoTextCursor start( doc );
00271     start.setParag( sp );
00272     start.setIndex( startIndex );
00273     KoTextCursor end( doc );
00274     end.setParag( ep );
00275     end.setIndex( endIndex );
00276 
00277     doc->setSelectionStart( KoTextDocument::Temp, &start );
00278     doc->setSelectionEnd( KoTextDocument::Temp, &end );
00279     doc->setFormat( KoTextDocument::Temp, format, flags );
00280     doc->removeSelection( KoTextDocument::Temp );
00281     if ( endIndex == ep->length() ) // ### Not in QRT - report sent. Description at http://bugs.kde.org/db/34/34556.html
00282         end.gotoLeft();
00283     *c = end;
00284     return c;
00285 }
00286 
00287 KoTextCursor *KoTextDocFormatCommand::unexecute( KoTextCursor *c )
00288 {
00289     KoTextParag *sp = doc->paragAt( startId );
00290     KoTextParag *ep = doc->paragAt( endId );
00291     if ( !sp || !ep )
00292     return 0;
00293 
00294     int idx = startIndex;
00295     int fIndex = 0;
00296     if( !oldFormats.isEmpty()) // ## not in QRT. Not sure how it can happen.
00297     {
00298     for ( ;; ) {
00299     if ( oldFormats.at( fIndex ).c == '\n' ) {
00300         if ( idx > 0 ) {
00301         if ( idx < sp->length() && fIndex > 0 )
00302             sp->setFormat( idx, 1, oldFormats.at( fIndex - 1 ).format() );
00303         if ( sp == ep )
00304             break;
00305         sp = sp->next();
00306         idx = 0;
00307         }
00308         fIndex++;
00309     }
00310     if ( oldFormats.at( fIndex ).format() )
00311         sp->setFormat( idx, 1, oldFormats.at( fIndex ).format() );
00312     idx++;
00313     fIndex++;
00314     if ( fIndex >= (int)oldFormats.size() )
00315         break;
00316     if ( idx >= sp->length() ) {
00317         if ( sp == ep )
00318         break;
00319         sp = sp->next();
00320         idx = 0;
00321     }
00322     }
00323     }
00324     KoTextCursor end( doc );
00325     end.setParag( ep );
00326     end.setIndex( endIndex );
00327     if ( endIndex == ep->length() )
00328         end.gotoLeft();
00329     *c = end;
00330     return c;
00331 }
00332 
00333 KoTextAlignmentCommand::KoTextAlignmentCommand( KoTextDocument *d, int fParag, int lParag, int na, const QMemArray<int> &oa )
00334     : KoTextDocCommand( d ), firstParag( fParag ), lastParag( lParag ), newAlign( na ), oldAligns( oa )
00335 {
00336 }
00337 
00338 KoTextCursor *KoTextAlignmentCommand::execute( KoTextCursor *c )
00339 {
00340     KoTextParag *p = doc->paragAt( firstParag );
00341     if ( !p )
00342     return c;
00343     while ( p ) {
00344     p->setAlignment( newAlign );
00345     if ( p->paragId() == lastParag )
00346         break;
00347     p = p->next();
00348     }
00349     return c;
00350 }
00351 
00352 KoTextCursor *KoTextAlignmentCommand::unexecute( KoTextCursor *c )
00353 {
00354     KoTextParag *p = doc->paragAt( firstParag );
00355     if ( !p )
00356     return c;
00357     int i = 0;
00358     while ( p ) {
00359     if ( i < (int)oldAligns.size() )
00360         p->setAlignment( oldAligns.at( i ) );
00361     if ( p->paragId() == lastParag )
00362         break;
00363     p = p->next();
00364     ++i;
00365     }
00366     return c;
00367 }
00368 
00369 
00370 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
00371 
00372 KoTextCursor::KoTextCursor( KoTextDocument *d )
00373     : doc( d ), ox( 0 ), oy( 0 )
00374 {
00375     nested = FALSE;
00376     idx = 0;
00377     string = doc ? doc->firstParag() : 0;
00378     tmpIndex = -1;
00379 }
00380 
00381 KoTextCursor::KoTextCursor()
00382 {
00383 }
00384 
00385 KoTextCursor::KoTextCursor( const KoTextCursor &c )
00386 {
00387     doc = c.doc;
00388     ox = c.ox;
00389     oy = c.oy;
00390     nested = c.nested;
00391     idx = c.idx;
00392     string = c.string;
00393     tmpIndex = c.tmpIndex;
00394     indices = c.indices;
00395     parags = c.parags;
00396     xOffsets = c.xOffsets;
00397     yOffsets = c.yOffsets;
00398 }
00399 
00400 KoTextCursor &KoTextCursor::operator=( const KoTextCursor &c )
00401 {
00402     doc = c.doc;
00403     ox = c.ox;
00404     oy = c.oy;
00405     nested = c.nested;
00406     idx = c.idx;
00407     string = c.string;
00408     tmpIndex = c.tmpIndex;
00409     indices = c.indices;
00410     parags = c.parags;
00411     xOffsets = c.xOffsets;
00412     yOffsets = c.yOffsets;
00413 
00414     return *this;
00415 }
00416 
00417 bool KoTextCursor::operator==( const KoTextCursor &c ) const
00418 {
00419     return doc == c.doc && string == c.string && idx == c.idx;
00420 }
00421 
00422 int KoTextCursor::totalOffsetX() const
00423 {
00424     if ( !nested )
00425     return 0;
00426     QValueStack<int>::ConstIterator xit = xOffsets.begin();
00427     int xoff = ox;
00428     for ( ; xit != xOffsets.end(); ++xit )
00429     xoff += *xit;
00430     return xoff;
00431 }
00432 
00433 int KoTextCursor::totalOffsetY() const
00434 {
00435     if ( !nested )
00436     return 0;
00437     QValueStack<int>::ConstIterator yit = yOffsets.begin();
00438     int yoff = oy;
00439     for ( ; yit != yOffsets.end(); ++yit )
00440     yoff += *yit;
00441     return yoff;
00442 }
00443 
00444 void KoTextCursor::gotoIntoNested( const QPoint &globalPos )
00445 {
00446     if ( !doc )
00447     return;
00448     push();
00449     ox = 0;
00450     int bl, y;
00451     string->lineHeightOfChar( idx, &bl, &y );
00452     oy = y + string->rect().y();
00453     nested = TRUE;
00454     QPoint p( globalPos.x() - offsetX(), globalPos.y() - offsetY() );
00455     Q_ASSERT( string->at( idx )->isCustom() );
00456     ox = string->at( idx )->x;
00457     string->at( idx )->customItem()->enterAt( this, doc, string, idx, ox, oy, p );
00458 }
00459 
00460 void KoTextCursor::invalidateNested()
00461 {
00462     if ( nested ) {
00463     QValueStack<KoTextParag*>::Iterator it = parags.begin();
00464     QValueStack<int>::Iterator it2 = indices.begin();
00465     for ( ; it != parags.end(); ++it, ++it2 ) {
00466         if ( *it == string )
00467         continue;
00468         (*it)->invalidate( 0 );
00469         if ( (*it)->at( *it2 )->isCustom() )
00470         (*it)->at( *it2 )->customItem()->invalidate();
00471     }
00472     }
00473 }
00474 
00475 void KoTextCursor::insert( const QString &str, bool checkNewLine, QMemArray<KoTextStringChar> *formatting )
00476 {
00477     string->invalidate( idx );
00478     tmpIndex = -1;
00479     bool justInsert = TRUE;
00480     QString s( str );
00481 #if defined(Q_WS_WIN)
00482     if ( checkNewLine )
00483     s = s.replace( QRegExp( "\\r" ), "" );
00484 #endif
00485     if ( checkNewLine )
00486     justInsert = s.find( '\n' ) == -1;
00487     if ( justInsert ) {
00488     string->insert( idx, s );
00489     if ( formatting ) {
00490         for ( int i = 0; i < (int)s.length(); ++i ) {
00491         if ( formatting->at( i ).format() ) {
00492             formatting->at( i ).format()->addRef();
00493             string->string()->setFormat( idx + i, formatting->at( i ).format(), TRUE );
00494         }
00495         }
00496     }
00497     idx += s.length();
00498     } else {
00499     QStringList lst = QStringList::split( '\n', s, TRUE );
00500     QStringList::Iterator it = lst.begin();
00501     //int y = string->rect().y() + string->rect().height();
00502     int lastIndex = 0;
00503     KoTextFormat *lastFormat = 0;
00504     for ( ; it != lst.end(); ) {
00505         if ( it != lst.begin() ) {
00506         splitAndInsertEmptyParag( FALSE, TRUE );
00507         //string->setEndState( -1 );
00508 #if 0 // no!
00509         string->prev()->format( -1, FALSE );
00510 #endif
00511         if ( lastFormat && formatting && string->prev() ) {
00512             lastFormat->addRef();
00513             string->prev()->string()->setFormat( string->prev()->length() - 1, lastFormat, TRUE );
00514         }
00515         }
00516         lastFormat = 0;
00517         QString s = *it;
00518         ++it;
00519         if ( !s.isEmpty() )
00520         string->insert( idx, s );
00521             else
00522                 string->invalidate( 0 );
00523 
00524         if ( formatting ) {
00525         int len = s.length();
00526         for ( int i = 0; i < len; ++i ) {
00527             if ( formatting->at( i + lastIndex ).format() ) {
00528             formatting->at( i + lastIndex ).format()->addRef();
00529             string->string()->setFormat( i + idx, formatting->at( i + lastIndex ).format(), TRUE );
00530             }
00531         }
00532         if ( it != lst.end() )
00533             lastFormat = formatting->at( len + lastIndex ).format();
00534         ++len;
00535         lastIndex += len;
00536         }
00537 
00538         idx += s.length();
00539     }
00540 #if 0  
00541     string->format( -1, FALSE );
00542     int dy = string->rect().y() + string->rect().height() - y;
00543 #endif
00544     KoTextParag *p = string;
00545     p->setParagId( p->prev()->paragId() + 1 );
00546     p = p->next();
00547     while ( p ) {
00548         p->setParagId( p->prev()->paragId() + 1 );
00549         //p->move( dy );
00550         p->invalidate( 0 );
00551         p = p->next();
00552     }
00553     }
00554 
00555 #if 0  
00556     int h = string->rect().height();
00557     string->format( -1, TRUE );
00558     if ( h != string->rect().height() )
00559     invalidateNested();
00560     else if ( doc && doc->parent() )
00561     doc->nextDoubleBuffered = TRUE;
00562 #endif
00563 #ifdef INDIC
00564     fixCursorPosition();
00565 #endif
00566 }
00567 
00568 void KoTextCursor::gotoLeft()
00569 {
00570     if ( string->string()->isRightToLeft() )
00571     gotoNextLetter();
00572     else
00573     gotoPreviousLetter();
00574 }
00575 
00576 void KoTextCursor::gotoPreviousLetter()
00577 {
00578     tmpIndex = -1;
00579 
00580     if ( idx > 0 ) {
00581 #ifndef INDIC
00582     idx--;
00583 #else
00584     idx = string->string()->previousCursorPosition( idx );
00585 #endif
00586     } else if ( string->prev() ) {
00587     string = string->prev();
00588     while ( !string->isVisible() )
00589         string = string->prev();
00590     idx = string->length() - 1;
00591 #ifndef INDIC
00592     } else {
00593     if ( nested ) {
00594         pop();
00595         processNesting( Prev );
00596         if ( idx == -1 ) {
00597         pop();
00598         if ( idx > 0 ) {
00599             idx--;
00600         } else if ( string->prev() ) {
00601             string = string->prev();
00602             idx = string->length() - 1;
00603         }
00604         }
00605     }
00606 #endif
00607     }
00608 
00609     const KoTextStringChar *tsc = string->at( idx );
00610     if ( tsc && tsc->isCustom() && tsc->customItem()->isNested() ) {
00611     processNesting( EnterEnd );
00612     }
00613 }
00614 
00615 void KoTextCursor::push()
00616 {
00617     indices.push( idx );
00618     parags.push( string );
00619     xOffsets.push( ox );
00620     yOffsets.push( oy );
00621     nestedStack.push( nested );
00622 }
00623 
00624 void KoTextCursor::pop()
00625 {
00626     if ( !doc )
00627     return;
00628     idx = indices.pop();
00629     string = parags.pop();
00630     ox = xOffsets.pop();
00631     oy = yOffsets.pop();
00632     //if ( doc->parent() )
00633     //doc = doc->parent();
00634     nested = nestedStack.pop();
00635 }
00636 
00637 void KoTextCursor::restoreState()
00638 {
00639     while ( !indices.isEmpty() )
00640     pop();
00641 }
00642 
00643 bool KoTextCursor::place( const QPoint &p, KoTextParag *s, bool link, int *customItemIndex )
00644 {
00645     if ( customItemIndex )
00646         *customItemIndex = -1;
00647     QPoint pos( p );
00648     QRect r;
00649     if ( pos.y() < s->rect().y() )
00650     pos.setY( s->rect().y() );
00651     while ( s ) {
00652     r = s->rect();
00653     r.setWidth( doc ? doc->width() : QWIDGETSIZE_MAX );
00654     if ( !s->next() || ( pos.y() >= r.y() && pos.y() < s->next()->rect().y() ) )
00655         break;
00656     s = s->next();
00657     }
00658 
00659     if ( !s )
00660     return FALSE;
00661 
00662     setParag( s, FALSE );
00663     int y = s->rect().y();
00664     int lines = s->lines();
00665     KoTextStringChar *chr = 0;
00666     int index = 0;
00667     int i = 0;
00668     int cy = 0;
00669     //int ch = 0;
00670     for ( ; i < lines; ++i ) {
00671     chr = s->lineStartOfLine( i, &index );
00672     cy = s->lineY( i );
00673     //ch = s->lineHeight( i );
00674     if ( !chr )
00675         return FALSE;
00676     if ( i < lines - 1 && pos.y() >= y + cy && pos.y() <= y + s->lineY( i+1 ) )
00677         break;
00678     }
00679     int nextLine;
00680     if ( i < lines - 1 )
00681     s->lineStartOfLine( i+1, &nextLine );
00682     else
00683     nextLine = s->length();
00684     i = index;
00685     int x = s->rect().x();
00686     if ( pos.x() < x )
00687     pos.setX( x + 1 );
00688     int cw;
00689     int curpos = s->length()-1;
00690     int dist = 10000000;
00691     bool inCustom = FALSE;
00692     while ( i < nextLine ) {
00693     chr = s->at(i);
00694     int cpos = x + chr->x;
00695     cw = chr->width; //s->string()->width( i );
00696     if ( chr->isCustom() ) {
00697              if ( pos.x() >= cpos && pos.x() <= cpos + cw &&
00698                   pos.y() >= y + cy && pos.y() <= y + cy + chr->height() ) {
00699                 if ( customItemIndex )
00700                     *customItemIndex = i;
00701                 if ( chr->customItem()->isNested() )
00702                 {
00703                     curpos = i;
00704                     inCustom = TRUE;
00705                     break;
00706                 }
00707         }
00708     }
00709         if( chr->rightToLeft )
00710             cpos += cw;
00711         int d = cpos - pos.x();
00712         bool dm = d < 0 ? !chr->rightToLeft : chr->rightToLeft;
00713 #ifndef INDIC
00714         if ( QABS( d ) < dist || (dist == d && dm == TRUE ) ) {
00715 #else
00716         if ( (QABS( d ) < dist || (dist == d && dm == TRUE )) && string->string()->validCursorPosition( i ) ) {
00717 #endif
00718             dist = QABS( d );
00719             if ( !link || pos.x() >= x + chr->x ) {
00720                 curpos = i;
00721             }
00722         }
00723     i++;
00724     }
00725     setIndex( curpos, FALSE );
00726 
00727 #ifndef INDIC
00728     if ( inCustom && doc && parag()->at( curpos )->isCustom() && parag()->at( curpos )->customItem()->isNested() ) {
00729     KoTextDocument *oldDoc = doc;
00730     pos.setX( pos.x() - parag()->at( curpos )->x );
00731     gotoIntoNested( pos );
00732     if ( oldDoc == doc )
00733         return TRUE;
00734     QPoint p( pos.x() - offsetX(), pos.y() - offsetY() );
00735     if ( !place( p, document()->firstParag() ) )
00736         pop();
00737     }
00738 #endif
00739     return TRUE;
00740 }
00741 
00742 void KoTextCursor::processNesting( Operation op )
00743 {
00744     if ( !doc )
00745     return;
00746     push();
00747     ox = string->at( idx )->x;
00748     int bl, y;
00749     string->lineHeightOfChar( idx, &bl, &y );
00750     oy = y + string->rect().y();
00751     nested = TRUE;
00752     bool ok = FALSE;
00753 
00754     switch ( op ) {
00755     case EnterBegin:
00756     ok = string->at( idx )->customItem()->enter( this, doc, string, idx, ox, oy );
00757     break;
00758     case EnterEnd:
00759     ok = string->at( idx )->customItem()->enter( this, doc, string, idx, ox, oy, TRUE );
00760     break;
00761     case Next:
00762     ok = string->at( idx )->customItem()->next( this, doc, string, idx, ox, oy );
00763     break;
00764     case Prev:
00765     ok = string->at( idx )->customItem()->prev( this, doc, string, idx, ox, oy );
00766     break;
00767     case Down:
00768     ok = string->at( idx )->customItem()->down( this, doc, string, idx, ox, oy );
00769     break;
00770     case Up:
00771     ok = string->at( idx )->customItem()->up( this, doc, string, idx, ox, oy );
00772     break;
00773     }
00774     if ( !ok )
00775     pop();
00776 }
00777 
00778 void KoTextCursor::gotoRight()
00779 {
00780     if ( string->string()->isRightToLeft() )
00781     gotoPreviousLetter();
00782     else
00783     gotoNextLetter();
00784 }
00785 
00786 void KoTextCursor::gotoNextLetter()
00787 {
00788     tmpIndex = -1;
00789 
00790 #ifdef INDIC
00791     int len = string->length() - 1;
00792 #endif
00793     const KoTextStringChar *tsc = string->at( idx );
00794     if ( tsc && tsc->isCustom() && tsc->customItem()->isNested() ) {
00795     processNesting( EnterBegin );
00796     return;
00797     }
00798 
00799 #ifndef INDIC
00800     if ( idx < string->length() - 1 ) {
00801     idx++;
00802 #else
00803     if ( idx < len ) {
00804         idx = string->string()->nextCursorPosition( idx );
00805 #endif
00806     } else if ( string->next() ) {
00807     string = string->next();
00808     while ( !string->isVisible() )
00809         string = string->next();
00810     idx = 0;
00811 #ifndef INDIC
00812     } else {
00813     if ( nested ) {
00814         pop();
00815         processNesting( Next );
00816         if ( idx == -1 ) {
00817         pop();
00818         if ( idx < string->length() - 1 ) {
00819             idx++;
00820         } else if ( string->next() ) {
00821             string = string->next();
00822             idx = 0;
00823         }
00824         }
00825     }
00826 #endif
00827     }
00828 }
00829 
00830 void KoTextCursor::gotoUp()
00831 {
00832     int indexOfLineStart;
00833     int line;
00834     KoTextStringChar *c = string->lineStartOfChar( idx, &indexOfLineStart, &line );
00835     if ( !c )
00836     return;
00837 
00838     tmpIndex = QMAX( tmpIndex, idx - indexOfLineStart );
00839     if ( indexOfLineStart == 0 ) {
00840     if ( !string->prev() ) {
00841         if ( !nested )
00842         return;
00843         pop();
00844         processNesting( Up );
00845         if ( idx == -1 ) {
00846         pop();
00847         if ( !string->prev() )
00848             return;
00849         idx = tmpIndex = 0;
00850         } else {
00851         tmpIndex = -1;
00852         return;
00853         }
00854     }
00855     string = string->prev();
00856     while ( !string->isVisible() )
00857         string = string->prev();
00858     int lastLine = string->lines() - 1;
00859     if ( !string->lineStartOfLine( lastLine, &indexOfLineStart ) )
00860         return;
00861     if ( indexOfLineStart + tmpIndex < string->length() )
00862         idx = indexOfLineStart + tmpIndex;
00863     else
00864         idx = string->length() - 1;
00865     } else {
00866     --line;
00867     int oldIndexOfLineStart = indexOfLineStart;
00868     if ( !string->lineStartOfLine( line, &indexOfLineStart ) )
00869         return;
00870     if ( indexOfLineStart + tmpIndex < oldIndexOfLineStart )
00871         idx = indexOfLineStart + tmpIndex;
00872     else
00873         idx = oldIndexOfLineStart - 1;
00874     }
00875 #ifdef INDIC
00876     fixCursorPosition();
00877 #endif
00878 }
00879 
00880 void KoTextCursor::gotoDown()
00881 {
00882     int indexOfLineStart;
00883     int line;
00884     KoTextStringChar *c = string->lineStartOfChar( idx, &indexOfLineStart, &line );
00885     if ( !c )
00886     return;
00887 
00888     tmpIndex = QMAX( tmpIndex, idx - indexOfLineStart );
00889     if ( line == string->lines() - 1 ) {
00890     if ( !string->next() ) {
00891         if ( !nested )
00892         return;
00893         pop();
00894         processNesting( Down );
00895         if ( idx == -1 ) {
00896         pop();
00897         if ( !string->next() )
00898             return;
00899         idx = tmpIndex = 0;
00900         } else {
00901         tmpIndex = -1;
00902         return;
00903         }
00904     }
00905     string = string->next();
00906     while ( !string->isVisible() )
00907         string = string->next();
00908     if ( !string->lineStartOfLine( 0, &indexOfLineStart ) )
00909         return;
00910     int end;
00911     if ( string->lines() == 1 )
00912         end = string->length();
00913     else
00914         string->lineStartOfLine( 1, &end );
00915     if ( indexOfLineStart + tmpIndex < end )
00916         idx = indexOfLineStart + tmpIndex;
00917     else
00918         idx = end - 1;
00919     } else {
00920     ++line;
00921     int end;
00922     if ( line == string->lines() - 1 )
00923         end = string->length();
00924     else
00925         string->lineStartOfLine( line + 1, &end );
00926     if ( !string->lineStartOfLine( line, &indexOfLineStart ) )
00927         return;
00928     if ( indexOfLineStart + tmpIndex < end )
00929         idx = indexOfLineStart + tmpIndex;
00930     else
00931         idx = end - 1;
00932     }
00933 #ifdef INDIC
00934     fixCursorPosition();
00935 #endif
00936 }
00937 
00938 void KoTextCursor::gotoLineEnd()
00939 {
00940     tmpIndex = -1;
00941     int indexOfLineStart;
00942     int line;
00943     KoTextStringChar *c = string->lineStartOfChar( idx, &indexOfLineStart, &line );
00944     if ( !c )
00945     return;
00946 
00947     if ( line == string->lines() - 1 ) {
00948     idx = string->length() - 1;
00949     } else {
00950     c = string->lineStartOfLine( ++line, &indexOfLineStart );
00951     indexOfLineStart--;
00952     idx = indexOfLineStart;
00953     }
00954 }
00955 
00956 void KoTextCursor::gotoLineStart()
00957 {
00958     tmpIndex = -1;
00959     int indexOfLineStart;
00960     int line;
00961     KoTextStringChar *c = string->lineStartOfChar( idx, &indexOfLineStart, &line );
00962     if ( !c )
00963     return;
00964 
00965     idx = indexOfLineStart;
00966 }
00967 
00968 void KoTextCursor::gotoHome()
00969 {
00970     tmpIndex = -1;
00971     if ( doc )
00972     string = doc->firstParag();
00973     idx = 0;
00974 }
00975 
00976 void KoTextCursor::gotoEnd()
00977 {
00978     if ( doc && !doc->lastParag()->isValid() )
00979     {
00980     kdDebug(32500) << "Last parag, " << doc->lastParag()->paragId() << ", is invalid - aborting gotoEnd() !" << endl;
00981     return;
00982     }
00983 
00984     tmpIndex = -1;
00985     if ( doc )
00986     string = doc->lastParag();
00987     idx = string->length() - 1;
00988 }
00989 
00990 void KoTextCursor::gotoPageUp( int visibleHeight )
00991 {
00992     tmpIndex = -1;
00993     KoTextParag *s = string;
00994     int h = visibleHeight;
00995     int y = s->rect().y();
00996     while ( s ) {
00997     if ( y - s->rect().y() >= h )
00998         break;
00999     s = s->prev();
01000     }
01001 
01002     if ( !s && doc )
01003     s = doc->firstParag();
01004 
01005     string = s;
01006     idx = 0;
01007 }
01008 
01009 void KoTextCursor::gotoPageDown( int visibleHeight )
01010 {
01011     tmpIndex = -1;
01012     KoTextParag *s = string;
01013     int h = visibleHeight;
01014     int y = s->rect().y();
01015     while ( s ) {
01016     if ( s->rect().y() - y >= h )
01017         break;
01018     s = s->next();
01019     }
01020 
01021     if ( !s && doc ) {
01022     s = doc->lastParag();
01023     string = s;
01024     idx = string->length() - 1;
01025     return;
01026     }
01027 
01028     if ( !s->isValid() )
01029     return;
01030 
01031     string = s;
01032     idx = 0;
01033 }
01034 
01035 void KoTextCursor::gotoWordRight()
01036 {
01037     if ( string->string()->isRightToLeft() )
01038     gotoPreviousWord();
01039     else
01040     gotoNextWord();
01041 }
01042 
01043 void KoTextCursor::gotoWordLeft()
01044 {
01045     if ( string->string()->isRightToLeft() )
01046     gotoNextWord();
01047     else
01048     gotoPreviousWord();
01049 }
01050 
01051 void KoTextCursor::gotoPreviousWord()
01052 {
01053     gotoPreviousLetter();
01054     tmpIndex = -1;
01055     KoTextString *s = string->string();
01056     bool allowSame = FALSE;
01057     if ( idx == ( (int)s->length()-1 ) )
01058         return;
01059     for ( int i = idx; i >= 0; --i ) {
01060     if ( s->at( i ).c.isSpace() || s->at( i ).c == '\t' || s->at( i ).c == '.' ||
01061          s->at( i ).c == ',' || s->at( i ).c == ':' || s->at( i ).c == ';' ) {
01062         if ( !allowSame )
01063         continue;
01064         idx = i + 1;
01065         return;
01066     }
01067     if ( !allowSame && !( s->at( i ).c.isSpace() || s->at( i ).c == '\t' || s->at( i ).c == '.' ||
01068                   s->at( i ).c == ',' || s->at( i ).c == ':' || s->at( i ).c == ';'  ) )
01069         allowSame = TRUE;
01070     }
01071     idx = 0;
01072 }
01073 
01074 void KoTextCursor::gotoNextWord()
01075 {
01076     tmpIndex = -1;
01077     KoTextString *s = string->string();
01078     bool allowSame = FALSE;
01079     for ( int i = idx; i < (int)s->length(); ++i ) {
01080     if ( ! ( s->at( i ).c.isSpace() || s->at( i ).c == '\t' || s->at( i ).c == '.' ||
01081          s->at( i ).c == ',' || s->at( i ).c == ':' || s->at( i ).c == ';' ) ) {
01082         if ( !allowSame )
01083         continue;
01084         idx = i;
01085         return;
01086     }
01087     if ( !allowSame && ( s->at( i ).c.isSpace() || s->at( i ).c == '\t' || s->at( i ).c == '.' ||
01088                   s->at( i ).c == ',' || s->at( i ).c == ':' || s->at( i ).c == ';'  ) )
01089         allowSame = TRUE;
01090     }
01091 
01092     if ( idx < ((int)s->length()-1) ) {
01093         gotoLineEnd();
01094     } else if ( string->next() ) {
01095     string = string->next();
01096     while ( !string->isVisible() )
01097         string = string->next();
01098     idx = 0;
01099     } else {
01100     gotoLineEnd();
01101     }
01102 }
01103 
01104 bool KoTextCursor::atParagStart() const
01105 {
01106     return idx == 0;
01107 }
01108 
01109 bool KoTextCursor::atParagEnd() const
01110 {
01111     return idx == string->length() - 1;
01112 }
01113 
01114 void KoTextCursor::splitAndInsertEmptyParag( bool ind, bool updateIds )
01115 {
01116     if ( !doc )
01117     return;
01118     tmpIndex = -1;
01119     KoTextFormat *f = 0;
01120     if ( doc->useFormatCollection() ) {
01121     f = string->at( idx )->format();
01122     if ( idx == string->length() - 1 && idx > 0 )
01123         f = string->at( idx - 1 )->format();
01124     if ( f->isMisspelled() ) {
01125             KoTextFormat fNoMisspelled( *f );
01126             fNoMisspelled.setMisspelled( false );
01127         f = doc->formatCollection()->format( &fNoMisspelled );
01128     }
01129     }
01130 
01131     if ( atParagEnd() ) {
01132     KoTextParag *n = string->next();
01133     KoTextParag *s = doc->createParag( doc, string, n, updateIds );
01134     if ( f )
01135         s->setFormat( 0, 1, f, TRUE );
01136     s->copyParagData( string );
01137     if ( ind ) {
01138         int oi, ni;
01139         s->indent( &oi, &ni );
01140         string = s;
01141         idx = ni;
01142     } else {
01143         string = s;
01144         idx = 0;
01145     }
01146     } else if ( atParagStart() ) {
01147     KoTextParag *p = string->prev();
01148     KoTextParag *s = doc->createParag( doc, p, string, updateIds );
01149     if ( f )
01150         s->setFormat( 0, 1, f, TRUE );
01151     s->copyParagData( string );
01152     if ( ind ) {
01153         s->indent();
01154         s->format();
01155         indent();
01156         string->format();
01157     }
01158     } else {
01159     QString str = string->string()->toString().mid( idx, 0xFFFFFF );
01160     KoTextParag *n = string->next();
01161     KoTextParag *s = doc->createParag( doc, string, n, updateIds );
01162     s->copyParagData( string );
01163     s->remove( 0, 1 );
01164     s->append( str, TRUE );
01165     for ( uint i = 0; i < str.length(); ++i ) {
01166             KoTextStringChar* tsc = string->at( idx + i );
01167         s->setFormat( i, 1, tsc->format(), TRUE );
01168         if ( tsc->isCustom() ) {
01169         KoTextCustomItem * item = tsc->customItem();
01170         s->at( i )->setCustomItem( item );
01171         tsc->loseCustomItem();
01172 #if 0
01173         s->addCustomItem();
01174         string->removeCustomItem();
01175 #endif
01176         doc->unregisterCustomItem( item, string );
01177         doc->registerCustomItem( item, s );
01178         }
01179     }
01180     string->truncate( idx );
01181     if ( ind ) {
01182         int oi, ni;
01183         s->indent( &oi, &ni );
01184         string = s;
01185         idx = ni;
01186     } else {
01187         string = s;
01188         idx = 0;
01189     }
01190     }
01191 
01192     invalidateNested();
01193 }
01194 
01195 #ifdef INDIC
01196 bool KoTextCursor::removePreviousChar()
01197 {
01198     tmpIndex = -1;
01199     if ( !atParagStart() ) {
01200     string->remove( idx-1, 1 );
01201     int h = string->rect().height();
01202     idx--;
01203     // shouldn't be needed, just to make sure.
01204     fixCursorPosition();
01205     string->format( -1, TRUE );
01206     if ( h != string->rect().height() )
01207         invalidateNested();
01208     //else if ( string->document() && string->document()->parent() )
01209     //    string->document()->nextDoubleBuffered = TRUE;
01210     return FALSE;
01211     } else if ( string->prev() ) {
01212     string = string->prev();
01213     string->join( string->next() );
01214     string->invalidateCounters();
01215     invalidateNested();
01216     return TRUE;
01217     }
01218     return FALSE;
01219 }
01220 
01221 #endif
01222 bool KoTextCursor::remove()
01223 {
01224     tmpIndex = -1;
01225     if ( !atParagEnd() ) {
01226 #ifndef INDIC
01227     string->remove( idx, 1 );
01228 #else
01229     int next = string->string()->nextCursorPosition( idx );
01230     string->remove( idx, next-idx );
01231 #endif
01232     int h = string->rect().height();
01233     string->format( -1, TRUE );
01234     if ( h != string->rect().height() )
01235         invalidateNested();
01236     //else if ( doc && doc->parent() )
01237     //    doc->nextDoubleBuffered = TRUE;
01238     return FALSE;
01239     } else if ( string->next() ) {
01240     if ( string->length() == 1 ) {
01241         string->next()->setPrev( string->prev() );
01242         if ( string->prev() )
01243         string->prev()->setNext( string->next() );
01244         KoTextParag *p = string->next();
01245         delete string;
01246         string = p;
01247         string->invalidate( 0 );
01249             string->invalidateCounters();
01251         KoTextParag *s = string;
01252         while ( s ) {
01253         s->id = s->p ? s->p->id + 1 : 0;
01254         //s->state = -1;
01255         //s->needPreProcess = TRUE;
01256         s->changed = TRUE;
01257         s = s->n;
01258         }
01259         string->format();
01260     } else {
01261         string->join( string->next() );
01262     }
01263     invalidateNested();
01264     return TRUE;
01265     }
01266     return FALSE;
01267 }
01268 
01269 void KoTextCursor::killLine()
01270 {
01271     if ( atParagEnd() )
01272     return;
01273     string->remove( idx, string->length() - idx - 1 );
01274     int h = string->rect().height();
01275     string->format( -1, TRUE );
01276     if ( h != string->rect().height() )
01277     invalidateNested();
01278     //else if ( doc && doc->parent() )
01279     //doc->nextDoubleBuffered = TRUE;
01280 }
01281 
01282 void KoTextCursor::indent()
01283 {
01284     int oi = 0, ni = 0;
01285     string->indent( &oi, &ni );
01286     if ( oi == ni )
01287     return;
01288 
01289     if ( idx >= oi )
01290     idx += ni - oi;
01291     else
01292     idx = ni;
01293 }
01294 
01295 void KoTextCursor::setDocument( KoTextDocument *d )
01296 {
01297     doc = d;
01298     string = d->firstParag();
01299     idx = 0;
01300     nested = FALSE;
01301     restoreState();
01302     tmpIndex = -1;
01303 }
01304 
01305 
01306 int KoTextCursor::x() const
01307 {
01308     KoTextStringChar *c = string->at( idx );
01309     int curx = c->x;
01310     if ( c->rightToLeft )
01311         curx += c->width; //string->string()->width( idx );
01312     return curx;
01313 }
01314 
01315 int KoTextCursor::y() const
01316 {
01317     int dummy, line;
01318     string->lineStartOfChar( idx, &dummy, &line );
01319     return string->lineY( line );
01320 }
01321 
01322 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
01323 
01324 // TODO: move to kotextdocument.cpp
01325 
01326 void KoTextDocument::init()
01327 {
01328 #if defined(PARSER_DEBUG)
01329     kdDebug(32500) << debug_indent + "new KoTextDocument (%p)", this << endl;
01330 #endif
01331     //oTextValid = TRUE;
01332     //if ( par )
01333 //  par->insertChild( this );
01334     //pProcessor = 0;
01335     useFC = TRUE;
01336     pFormatter = 0;
01337     indenter = 0;
01338     fParag = 0;
01339     m_pageBreakEnabled = false;
01340     //minw = 0;
01341     align = Qt::AlignAuto;
01342     nSelections = 1;
01343     addMargs = FALSE;
01344 
01345 #if 0
01346     preferRichText = FALSE;
01347     txtFormat = Qt::AutoText;
01348     focusIndicator.parag = 0;
01349     minwParag = 0;
01350     sheet_ = QStyleSheet::defaultSheet();
01351     factory_ = QMimeSourceFactory::defaultFactory();
01352     contxt = QString::null;
01353     fCollection->setStyleSheet( sheet_ );
01354 #endif
01355 
01356     underlLinks = TRUE;
01357     backBrush = 0;
01358     buf_pixmap = 0;
01359     //nextDoubleBuffered = FALSE;
01360 
01361     //if ( par )
01362 //  withoutDoubleBuffer = par->withoutDoubleBuffer;
01363 //    else
01364     withoutDoubleBuffer = FALSE;
01365 
01366     lParag = fParag = createParag( this, 0, 0 );
01367     tmpCursor = 0;
01368 
01369     //cx = 0;
01370     //cy = 2;
01371     //if ( par )
01372     cx = cy = 0;
01373     //cw = 600; // huh?
01374     //vw = 0;
01375     flow_ = new KoTextFlow;
01376     //flow_->setWidth( cw );
01377 
01378     leftmargin = 0; // 4 in QRT
01379     rightmargin = 0; // 4 in QRT
01380 
01381     selectionColors[ Standard ] = QApplication::palette().color( QPalette::Active, QColorGroup::Highlight );
01382     selectionText[ Standard ] = TRUE;
01383     commandHistory = new KoTextDocCommandHistory( 100 );
01384     tStopWidth = formatCollection()->defaultFormat()->width( 'x' ) * 8;
01385 }
01386 
01387 KoTextDocument::~KoTextDocument()
01388 {
01389     //if ( par )
01390 //  par->removeChild( this );
01392     m_bDestroying = true;
01393     clear( false );
01395     delete commandHistory;
01396     delete flow_;
01397     //if ( !par )
01398     delete pFormatter;
01399     delete fCollection;
01400     //delete pProcessor;
01401     delete buf_pixmap;
01402     delete indenter;
01403     delete backBrush;
01404     if ( tArray )
01405     delete [] tArray;
01406 }
01407 
01408 void KoTextDocument::clear( bool createEmptyParag )
01409 {
01410     if ( flow_ )
01411     flow_->clear();
01412     while ( fParag ) {
01413     KoTextParag *p = fParag->next();
01414     delete fParag;
01415     fParag = p;
01416     }
01417     fParag = lParag = 0;
01418     if ( createEmptyParag )
01419     fParag = lParag = createParag( this );
01420     selections.clear();
01421 }
01422 
01423 /*
01424    // Looks slow!
01425 int KoTextDocument::widthUsed() const
01426 {
01427     KoTextParag *p = fParag;
01428     int w = 0;
01429     while ( p ) {
01430     int a = p->alignment();
01431     p->setAlignment( Qt::AlignLeft );
01432     p->invalidate( 0 );
01433     p->format();
01434     w = QMAX( w, p->rect().width() );
01435     p->setAlignment( a );
01436     p->invalidate( 0 );
01437     p = p->next();
01438     }
01439     return w;
01440 }
01441 */
01442 
01443 int KoTextDocument::height() const
01444 {
01445     int h = 0;
01446     if ( lParag )
01447     h = lParag->rect().top() + lParag->rect().height() + 1;
01448     //int fh = flow_->boundingRect().height();
01449     //return QMAX( h, fh );
01450     return h;
01451 }
01452 
01453 
01454 
01455 KoTextParag *KoTextDocument::createParag( KoTextDocument *d, KoTextParag *pr, KoTextParag *nx, bool updateIds )
01456 {
01457     return new KoTextParag( d, pr, nx, updateIds );
01458 }
01459 
01460 #if 0
01461 bool KoTextDocument::setMinimumWidth( int w, KoTextParag *p )
01462 {
01463     if ( w == -1 ) {
01464     minw = 0;
01465     p = 0;
01466     }
01467     if ( p == minwParag ) {
01468     minw = w;
01469     emit minimumWidthChanged( minw );
01470     } else if ( w > minw ) {
01471     minw = w;
01472     minwParag = p;
01473     emit minimumWidthChanged( minw );
01474     }
01475     cw = QMAX( minw, cw );
01476     return TRUE;
01477 }
01478 #endif
01479 
01480 void KoTextDocument::setPlainText( const QString &text )
01481 {
01482     clear();
01483     //preferRichText = FALSE;
01484     //oTextValid = TRUE;
01485     //oText = text;
01486 
01487     int lastNl = 0;
01488     int nl = text.find( '\n' );
01489     if ( nl == -1 ) {
01490     lParag = createParag( this, lParag, 0 );
01491     if ( !fParag )
01492         fParag = lParag;
01493     QString s = text;
01494     if ( !s.isEmpty() ) {
01495         if ( s[ (int)s.length() - 1 ] == '\r' )
01496         s.remove( s.length() - 1, 1 );
01497         lParag->append( s );
01498     }
01499     } else {
01500     for (;;) {
01501         lParag = createParag( this, lParag, 0 );
01502         if ( !fParag )
01503         fParag = lParag;
01504         QString s = text.mid( lastNl, nl - lastNl );
01505         if ( !s.isEmpty() ) {
01506         if ( s[ (int)s.length() - 1 ] == '\r' )
01507             s.remove( s.length() - 1, 1 );
01508         lParag->append( s );
01509         }
01510         if ( nl == 0xffffff )
01511         break;
01512         lastNl = nl + 1;
01513         nl = text.find( '\n', nl + 1 );
01514         if ( nl == -1 )
01515         nl = 0xffffff;
01516     }
01517     }
01518     if ( !lParag )
01519     lParag = fParag = createParag( this, 0, 0 );
01520 }
01521 
01522 void KoTextDocument::setText( const QString &text, const QString & /*context*/ )
01523 {
01524     //focusIndicator.parag = 0;
01525     selections.clear();
01526 #if 0
01527     if ( txtFormat == Qt::AutoText && QStyleSheet::mightBeRichText( text ) ||
01528      txtFormat == Qt::RichText )
01529     setRichText( text, context );
01530     else
01531 #endif
01532     setPlainText( text );
01533 }
01534 
01535 QString KoTextDocument::plainText( KoTextParag *p ) const
01536 {
01537     if ( !p ) {
01538     QString buffer;
01539     QString s;
01540     KoTextParag *p = fParag;
01541     while ( p ) {
01542         s = p->string()->toString();
01543         s.remove( s.length() - 1, 1 );
01544         if ( p->next() )
01545         s += "\n";
01546         buffer += s;
01547         p = p->next();
01548     }
01549     return buffer;
01550     } else {
01551     return p->string()->toString();
01552     }
01553 }
01554 
01555 QString KoTextDocument::richText( KoTextParag * ) const
01556 {
01557     QString s;
01558     // TODO update from QRT if this code is needed
01559     return s;
01560 }
01561 
01562 QString KoTextDocument::text() const
01563 {
01564     if ( plainText().simplifyWhiteSpace().isEmpty() )
01565     return QString("");
01566     //if ( txtFormat == Qt::AutoText && preferRichText || txtFormat == Qt::RichText )
01567     //    return richText();
01568     return plainText( 0 );
01569 }
01570 
01571 QString KoTextDocument::text( int parag ) const
01572 {
01573     KoTextParag *p = paragAt( parag );
01574     if ( !p )
01575     return QString::null;
01576 
01577     //if ( txtFormat == Qt::AutoText && preferRichText || txtFormat == Qt::RichText )
01578     //    return richText( p );
01579     //else
01580     return plainText( p );
01581 }
01582 
01583 void KoTextDocument::invalidate()
01584 {
01585     KoTextParag *s = fParag;
01586     while ( s ) {
01587     s->invalidate( 0 );
01588     s = s->next();
01589     }
01590 }
01591 
01592 void KoTextDocument::informParagraphDeleted( KoTextParag* parag )
01593 {
01594     QMap<int, KoTextDocumentSelection>::Iterator it = selections.begin();
01595     for ( ; it != selections.end(); ++it )
01596     {
01597         if ( (*it).startCursor.parag() == parag ) {
01598             if ( parag->prev() ) {
01599                 KoTextParag* prevP = parag->prev();
01600                 (*it).startCursor.setParag( prevP );
01601                 (*it).startCursor.setIndex( prevP->length()-1 );
01602             } else
01603                 (*it).startCursor.setParag( parag->next() ); // sets index to 0
01604         }
01605         if ( (*it).endCursor.parag() == parag ) {
01606             if ( parag->prev() ) {
01607                 KoTextParag* prevP = parag->prev();
01608                 (*it).endCursor.setParag( prevP );
01609                 (*it).endCursor.setIndex( prevP->length()-1 );
01610             } else
01611                 (*it).endCursor.setParag( parag->next() ); // sets index to 0
01612         }
01613     }
01614     emit paragraphDeleted( parag );
01615 }
01616 
01617 void KoTextDocument::selectionStart( int id, int &paragId, int &index )
01618 {
01619     QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
01620     if ( it == selections.end() )
01621     return;
01622     KoTextDocumentSelection &sel = *it;
01623     paragId = !sel.swapped ? sel.startCursor.parag()->paragId() : sel.endCursor.parag()->paragId();
01624     index = !sel.swapped ? sel.startCursor.index() : sel.endCursor.index();
01625 }
01626 
01627 KoTextCursor KoTextDocument::selectionStartCursor( int id)
01628 {
01629     QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
01630     if ( it == selections.end() )
01631     return KoTextCursor( this );
01632     KoTextDocumentSelection &sel = *it;
01633     if ( sel.swapped )
01634     return sel.endCursor;
01635     return sel.startCursor;
01636 }
01637 
01638 KoTextCursor KoTextDocument::selectionEndCursor( int id)
01639 {
01640     QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
01641     if ( it == selections.end() )
01642     return KoTextCursor( this );
01643     KoTextDocumentSelection &sel = *it;
01644     if ( !sel.swapped )
01645     return sel.endCursor;
01646     return sel.startCursor;
01647 }
01648 
01649 void KoTextDocument::selectionEnd( int id, int &paragId, int &index )
01650 {
01651     QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
01652     if ( it == selections.end() )
01653     return;
01654     KoTextDocumentSelection &sel = *it;
01655     paragId = sel.swapped ? sel.startCursor.parag()->paragId() : sel.endCursor.parag()->paragId();
01656     index = sel.swapped ? sel.startCursor.index() : sel.endCursor.index();
01657 }
01658 
01659 bool KoTextDocument::isSelectionSwapped( int id )
01660 {
01661     QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
01662     if ( it == selections.end() )
01663     return false;
01664     KoTextDocumentSelection &sel = *it;
01665     return sel.swapped;
01666 }
01667 
01668 KoTextParag *KoTextDocument::selectionStart( int id )
01669 {
01670     QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
01671     if ( it == selections.end() )
01672     return 0;
01673     KoTextDocumentSelection &sel = *it;
01674     if ( sel.startCursor.parag()->paragId() <  sel.endCursor.parag()->paragId() )
01675     return sel.startCursor.parag();
01676     return sel.endCursor.parag();
01677 }
01678 
01679 KoTextParag *KoTextDocument::selectionEnd( int id )
01680 {
01681     QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
01682     if ( it == selections.end() )
01683     return 0;
01684     KoTextDocumentSelection &sel = *it;
01685     if ( sel.startCursor.parag()->paragId() >  sel.endCursor.parag()->paragId() )
01686     return sel.startCursor.parag();
01687     return sel.endCursor.parag();
01688 }
01689 
01690 void KoTextDocument::addSelection( int id )
01691 {
01692     nSelections = QMAX( nSelections, id + 1 );
01693 }
01694 
01695 static void setSelectionEndHelper( int id, KoTextDocumentSelection &sel, KoTextCursor &start, KoTextCursor &end )
01696 {
01697     KoTextCursor c1 = start;
01698     KoTextCursor c2 = end;
01699     if ( sel.swapped ) {
01700     c1 = end;
01701     c2 = start;
01702     }
01703 
01704     c1.parag()->removeSelection( id );
01705     c2.parag()->removeSelection( id );
01706     if ( c1.parag() != c2.parag() ) {
01707     c1.parag()->setSelection( id, c1.index(), c1.parag()->length() - 1 );
01708     c2.parag()->setSelection( id, 0, c2.index() );
01709     } else {
01710     c1.parag()->setSelection( id, QMIN( c1.index(), c2.index() ), QMAX( c1.index(), c2.index() ) );
01711     }
01712 
01713     sel.startCursor = start;
01714     sel.endCursor = end;
01715     if ( sel.startCursor.parag() == sel.endCursor.parag() )
01716     sel.swapped = sel.startCursor.index() > sel.endCursor.index();
01717 }
01718 
01719 bool KoTextDocument::setSelectionEnd( int id, KoTextCursor *cursor )
01720 {
01721     QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
01722     if ( it == selections.end() )
01723     return FALSE;
01724     KoTextDocumentSelection &sel = *it;
01725 
01726     KoTextCursor start = sel.startCursor;
01727     KoTextCursor end = *cursor;
01728 
01729     if ( start == end ) {
01730     removeSelection( id );
01731     setSelectionStart( id, cursor );
01732     return TRUE;
01733     }
01734 
01735     if ( sel.endCursor.parag() == end.parag() ) {
01736     setSelectionEndHelper( id, sel, start, end );
01737     return TRUE;
01738     }
01739 
01740     bool inSelection = FALSE;
01741     KoTextCursor c( this );
01742     KoTextCursor tmp = sel.startCursor;
01743     if ( sel.swapped )
01744     tmp = sel.endCursor;
01745     tmp.restoreState();
01746     KoTextCursor tmp2 = *cursor;
01747     tmp2.restoreState();
01748     c.setParag( tmp.parag()->paragId() < tmp2.parag()->paragId() ? tmp.parag() : tmp2.parag() );
01749     KoTextCursor old;
01750     bool hadStart = FALSE;
01751     bool hadEnd = FALSE;
01752     bool hadStartParag = FALSE;
01753     bool hadEndParag = FALSE;
01754     bool hadOldStart = FALSE;
01755     bool hadOldEnd = FALSE;
01756     bool leftSelection = FALSE;
01757     sel.swapped = FALSE;
01758     for ( ;; ) {
01759     if ( c == start )
01760         hadStart = TRUE;
01761     if ( c == end )
01762         hadEnd = TRUE;
01763     if ( c.parag() == start.parag() )
01764         hadStartParag = TRUE;
01765     if ( c.parag() == end.parag() )
01766         hadEndParag = TRUE;
01767     if ( c == sel.startCursor )
01768         hadOldStart = TRUE;
01769     if ( c == sel.endCursor )
01770         hadOldEnd = TRUE;
01771 
01772     if ( !sel.swapped &&
01773          ( hadEnd && !hadStart ||
01774            hadEnd && hadStart && start.parag() == end.parag() && start.index() > end.index() ) )
01775         sel.swapped = TRUE;
01776 
01777     if ( c == end && hadStartParag ||
01778          c == start && hadEndParag ) {
01779         KoTextCursor tmp = c;
01780         tmp.restoreState();
01781         if ( tmp.parag() != c.parag() ) {
01782         int sstart = tmp.parag()->selectionStart( id );
01783         tmp.parag()->removeSelection( id );
01784         tmp.parag()->setSelection( id, sstart, tmp.index() );
01785         }
01786     }
01787 
01788     if ( inSelection &&
01789          ( c == end && hadStart || c == start && hadEnd ) )
01790          leftSelection = TRUE;
01791     else if ( !leftSelection && !inSelection && ( hadStart || hadEnd ) )
01792         inSelection = TRUE;
01793 
01794     bool noSelectionAnymore = hadOldStart && hadOldEnd && leftSelection && !inSelection && !c.parag()->hasSelection( id ) && c.atParagEnd();
01795     c.parag()->removeSelection( id );
01796     if ( inSelection ) {
01797         if ( c.parag() == start.parag() && start.parag() == end.parag() ) {
01798         c.parag()->setSelection( id, QMIN( start.index(), end.index() ), QMAX( start.index(), end.index() ) );
01799         } else if ( c.parag() == start.parag() && !hadEndParag ) {
01800         c.parag()->setSelection( id, start.index(), c.parag()->length() - 1 );
01801         } else if ( c.parag() == end.parag() && !hadStartParag ) {
01802         c.parag()->setSelection( id, end.index(), c.parag()->length() - 1 );
01803         } else if ( c.parag() == end.parag() && hadEndParag ) {
01804         c.parag()->setSelection( id, 0, end.index() );
01805         } else if ( c.parag() == start.parag() && hadStartParag ) {
01806         c.parag()->setSelection( id, 0, start.index() );
01807         } else {
01808         c.parag()->setSelection( id, 0, c.parag()->length() - 1 );
01809         }
01810     }
01811 
01812     if ( leftSelection )
01813         inSelection = FALSE;
01814 
01815     old = c;
01816     c.gotoNextLetter();
01817     if ( old == c || noSelectionAnymore )
01818         break;
01819     }
01820 
01821     if ( !sel.swapped )
01822     sel.startCursor.parag()->setSelection( id, sel.startCursor.index(), sel.startCursor.parag()->length() - 1 );
01823 
01824     sel.startCursor = start;
01825     sel.endCursor = end;
01826     if ( sel.startCursor.parag() == sel.endCursor.parag() )
01827     sel.swapped = sel.startCursor.index() > sel.endCursor.index();
01828 
01829     setSelectionEndHelper( id, sel, start, end );
01830 
01831     return TRUE;
01832 }
01833 
01834 void KoTextDocument::selectAll( int id )
01835 {
01836     removeSelection( id );
01837 
01838     KoTextDocumentSelection sel;
01839     sel.swapped = FALSE;
01840     KoTextCursor c( this );
01841 
01842     c.setParag( fParag );
01843     c.setIndex( 0 );
01844     sel.startCursor = c;
01845 
01846     c.setParag( lParag );
01847     c.setIndex( lParag->length() - 1 );
01848     sel.endCursor = c;
01849 
01850     KoTextParag *p = fParag;
01851     while ( p ) {
01852     p->setSelection( id, 0, p->length() - 1 );
01853 #ifdef QTEXTTABLE_AVAILABLE
01854     for ( int i = 0; i < (int)p->length(); ++i ) {
01855         if ( p->at( i )->isCustom() && p->at( i )->customItem()->isNested() ) {
01856         KoTextTable *t = (KoTextTable*)p->at( i )->customItem();
01857         QPtrList<KoTextTableCell> tableCells = t->tableCells();
01858         for ( KoTextTableCell *c = tableCells.first(); c; c = tableCells.next() )
01859             c->richText()->selectAll( id );
01860         }
01861     }
01862 #endif
01863     p = p->next();
01864     }
01865 
01866     selections.insert( id, sel );
01867 }
01868 
01869 bool KoTextDocument::removeSelection( int id )
01870 {
01871     QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
01872     if ( it == selections.end() )
01873     return FALSE;
01874 
01875     KoTextDocumentSelection &sel = *it;
01876 
01877     KoTextCursor c( this );
01878     KoTextCursor tmp = sel.startCursor;
01879     if ( sel.swapped )
01880     tmp = sel.endCursor;
01881     tmp.restoreState();
01882     c.setParag( tmp.parag() );
01883     KoTextCursor old;
01884     bool hadStart = FALSE;
01885     bool hadEnd = FALSE;
01886     KoTextParag *lastParag = 0;
01887     bool leftSelection = FALSE;
01888     bool inSelection = FALSE;
01889     sel.swapped = FALSE;
01890     for ( ;; ) {
01891     if ( !hadStart && c.parag() == sel.startCursor.parag() )
01892         hadStart = TRUE;
01893     if ( !hadEnd && c.parag() == sel.endCursor.parag() )
01894         hadEnd = TRUE;
01895 
01896         if ( !leftSelection && !inSelection && ( c.parag() == sel.startCursor.parag() || c.parag() == sel.endCursor.parag() ) )
01897         inSelection = TRUE;
01898 
01899     if ( inSelection &&
01900          ( c == sel.endCursor && hadStart || c == sel.startCursor && hadEnd ) ) {
01901          leftSelection = TRUE;
01902              inSelection = FALSE;
01903         }
01904 
01905     bool noSelectionAnymore = leftSelection && !inSelection && !c.parag()->hasSelection( id ) && c.atParagEnd();
01906 
01907     if ( lastParag != c.parag() )
01908         c.parag()->removeSelection( id );
01909 
01910     old = c;
01911     lastParag = c.parag();
01912     c.gotoNextLetter();
01913     if ( old == c || noSelectionAnymore )
01914         break;
01915     }
01916 
01917     selections.remove( id );
01918     return TRUE;
01919 }
01920 
01921 QString KoTextDocument::selectedText( int id, bool withCustom ) const
01922 {
01923     // ######## TODO: look at textFormat() and return rich text or plain text (like the text() method!)
01924     QMap<int, KoTextDocumentSelection>::ConstIterator it = selections.find( id );
01925     if ( it == selections.end() )
01926     return QString::null;
01927 
01928     KoTextDocumentSelection sel = *it;
01929 
01930 
01931     KoTextCursor c1 = sel.startCursor;
01932     KoTextCursor c2 = sel.endCursor;
01933     if ( sel.swapped ) {
01934     c2 = sel.startCursor;
01935     c1 = sel.endCursor;
01936     }
01937 
01938     c2.restoreState();
01939     c1.restoreState();
01940 
01941     if ( c1.parag() == c2.parag() ) {
01942     QString s;
01943     KoTextParag *p = c1.parag();
01944     int end = c2.index();
01945     if ( p->at( QMAX( 0, end - 1 ) )->isCustom() )
01946         ++end;
01947     if ( !withCustom || !p->customItems() ) {
01948         s += p->string()->toString().mid( c1.index(), end - c1.index() );
01949     } else {
01950         for ( int i = c1.index(); i < end; ++i ) {
01951         if ( p->at( i )->isCustom() ) {
01952 #ifdef QTEXTTABLE_AVAILABLE
01953             if ( p->at( i )->customItem()->isNested() ) {
01954             s += "\n";
01955             KoTextTable *t = (KoTextTable*)p->at( i )->customItem();
01956             QPtrList<KoTextTableCell> cells = t->tableCells();
01957             for ( KoTextTableCell *c = cells.first(); c; c = cells.next() )
01958                 s += c->richText()->plainText() + "\n";
01959             s += "\n";
01960             }
01961 #endif
01962         } else {
01963             s += p->at( i )->c;
01964         }
01965         s += "\n";
01966         }
01967     }
01968     return s;
01969     }
01970 
01971     QString s;
01972     KoTextParag *p = c1.parag();
01973     int start = c1.index();
01974     while ( p ) {
01975     int end = p == c2.parag() ? c2.index() : p->length() - 1;
01976     if ( p == c2.parag() && p->at( QMAX( 0, end - 1 ) )->isCustom() )
01977         ++end;
01978     if ( !withCustom || !p->customItems() ) {
01979         s += p->string()->toString().mid( start, end - start );
01980         if ( p != c2.parag() )
01981         s += "\n";
01982     } else {
01983         for ( int i = start; i < end; ++i ) {
01984         if ( p->at( i )->isCustom() ) {
01985 #ifdef QTEXTTABLE_AVAILABLE
01986             if ( p->at( i )->customItem()->isNested() ) {
01987             s += "\n";
01988             KoTextTable *t = (KoTextTable*)p->at( i )->customItem();
01989             QPtrList<KoTextTableCell> cells = t->tableCells();
01990             for ( KoTextTableCell *c = cells.first(); c; c = cells.next() )
01991                 s += c->richText()->plainText() + "\n";
01992             s += "\n";
01993             }
01994 #endif
01995         } else {
01996             s += p->at( i )->c;
01997         }
01998         s += "\n";
01999         }
02000     }
02001     start = 0;
02002     if ( p == c2.parag() )
02003         break;
02004     p = p->next();
02005     }
02006     return s;
02007 }
02008 
02009 void KoTextDocument::setFormat( int id, const KoTextFormat *f, int flags )
02010 {
02011     QMap<int, KoTextDocumentSelection>::ConstIterator it = selections.find( id );
02012     if ( it == selections.end() )
02013     return;
02014 
02015     KoTextDocumentSelection sel = *it;
02016 
02017     KoTextCursor c1 = sel.startCursor;
02018     KoTextCursor c2 = sel.endCursor;
02019     if ( sel.swapped ) {
02020     c2 = sel.startCursor;
02021     c1 = sel.endCursor;
02022     }
02023 
02024     c2.restoreState();
02025     c1.restoreState();
02026 
02027     if ( c1.parag() == c2.parag() ) {
02028     c1.parag()->setFormat( c1.index(), c2.index() - c1.index(), f, TRUE, flags );
02029     return;
02030     }
02031 
02032     c1.parag()->setFormat( c1.index(), c1.parag()->length() - c1.index(), f, TRUE, flags );
02033     KoTextParag *p = c1.parag()->next();
02034     while ( p && p != c2.parag() ) {
02035     p->setFormat( 0, p->length(), f, TRUE, flags );
02036     p = p->next();
02037     }
02038     c2.parag()->setFormat( 0, c2.index(), f, TRUE, flags );
02039 }
02040 
02041 /*void KoTextDocument::copySelectedText( int id )
02042 {
02043 #ifndef QT_NO_CLIPBOARD
02044     if ( !hasSelection( id ) )
02045     return;
02046 
02047     QApplication::clipboard()->setText( selectedText( id ) );
02048 #endif
02049 }*/
02050 
02051 void KoTextDocument::removeSelectedText( int id, KoTextCursor *cursor )
02052 {
02053     QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
02054     if ( it == selections.end() )
02055     return;
02056 
02057     KoTextDocumentSelection sel = *it;
02058 
02059     KoTextCursor c1 = sel.startCursor;
02060     KoTextCursor c2 = sel.endCursor;
02061     if ( sel.swapped ) {
02062     c2 = sel.startCursor;
02063     c1 = sel.endCursor;
02064     }
02065 
02066     // ### no support for editing tables yet
02067     if ( c1.nestedDepth() || c2.nestedDepth() )
02068     return;
02069 
02070     c2.restoreState();
02071     c1.restoreState();
02072 
02073     *cursor = c1;
02074     removeSelection( id );
02075 
02076     if ( c1.parag() == c2.parag() ) {
02077     c1.parag()->remove( c1.index(), c2.index() - c1.index() );
02078     return;
02079     }
02080 
02081     // ## Qt has a strange setValid/isValid on QTextCursor, only used in the few lines below !?!?
02082     bool valid = true;
02083     if ( c1.parag() == fParag && c1.index() == 0 &&
02084          c2.parag() == lParag && c2.index() == lParag->length() - 1 )
02085         valid = FALSE;
02086 
02087     bool didGoLeft = FALSE;
02088     if (  c1.index() == 0 && c1.parag() != fParag ) {
02089     cursor->gotoPreviousLetter();
02090         if ( valid )
02091             didGoLeft = TRUE;
02092     }
02093 
02094     c1.parag()->remove( c1.index(), c1.parag()->length() - 1 - c1.index() );
02095     KoTextParag *p = c1.parag()->next();
02096     int dy = 0;
02097     KoTextParag *tmp;
02098     while ( p && p != c2.parag() ) {
02099     tmp = p->next();
02100     dy -= p->rect().height();
02101     delete p;
02102     p = tmp;
02103     }
02104     c2.parag()->remove( 0, c2.index() );
02105     while ( p ) {
02106     p->move( dy );
02108         if ( p->paragLayout().counter )
02109             p->paragLayout().counter->invalidate();
02111     p->invalidate( 0 );
02112     //p->setEndState( -1 );
02113     p = p->next();
02114     }
02115 
02116     c1.parag()->join( c2.parag() );
02117 
02118     if ( didGoLeft )
02119     cursor->gotoNextLetter();
02120 }
02121 
02122 void KoTextDocument::indentSelection( int id )
02123 {
02124     QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
02125     if ( it == selections.end() )
02126     return;
02127 
02128     KoTextDocumentSelection sel = *it;
02129     KoTextParag *startParag = sel.startCursor.parag();
02130     KoTextParag *endParag = sel.endCursor.parag();
02131     if ( sel.endCursor.parag()->paragId() < sel.startCursor.parag()->paragId() ) {
02132     endParag = sel.startCursor.parag();
02133     startParag = sel.endCursor.parag();
02134     }
02135 
02136     KoTextParag *p = startParag;
02137     while ( p && p != endParag ) {
02138     p->indent();
02139     p = p->next();
02140     }
02141 }
02142 
02143 void KoTextDocument::addCommand( KoTextDocCommand *cmd )
02144 {
02145     commandHistory->addCommand( cmd );
02146 }
02147 
02148 KoTextCursor *KoTextDocument::undo( KoTextCursor *c )
02149 {
02150     return commandHistory->undo( c );
02151 }
02152 
02153 KoTextCursor *KoTextDocument::redo( KoTextCursor *c )
02154 {
02155     return commandHistory->redo( c );
02156 }
02157 
02158 bool KoTextDocument::find( const QString &expr, bool cs, bool wo, bool forward,
02159               int *parag, int *index, KoTextCursor *cursor )
02160 {
02161     KoTextParag *p = forward ? fParag : lParag;
02162     if ( parag )
02163     p = paragAt( *parag );
02164     else if ( cursor )
02165     p = cursor->parag();
02166     bool first = TRUE;
02167 
02168     while ( p ) {
02169     QString s = p->string()->toString();
02170     s.remove( s.length() - 1, 1 ); // get rid of trailing space
02171     int start = forward ? 0 : s.length() - 1;
02172     if ( first && index )
02173         start = *index;
02174     else if ( first )
02175         start = cursor->index();
02176     if ( !forward && first ) {
02177         start -= expr.length() + 1;
02178         if ( start < 0 ) {
02179         first = FALSE;
02180         p = p->prev();
02181         continue;
02182         }
02183     }
02184     first = FALSE;
02185 
02186     for ( ;; ) {
02187         int res = forward ? s.find( expr, start, cs ) : s.findRev( expr, start, cs );
02188         if ( res == -1 )
02189         break;
02190 
02191         bool ok = TRUE;
02192         if ( wo ) {
02193         int end = res + expr.length();
02194         if ( ( res == 0 || s[ res - 1 ].isSpace() || s[ res - 1 ].isPunct() ) &&
02195              ( end == (int)s.length() || s[ end ].isSpace() || s[ end ].isPunct() ) )
02196             ok = TRUE;
02197         else
02198             ok = FALSE;
02199         }
02200         if ( ok ) {
02201         cursor->setParag( p );
02202         cursor->setIndex( res );
02203         setSelectionStart( Standard, cursor );
02204         cursor->setIndex( res + expr.length() );
02205         setSelectionEnd( Standard, cursor );
02206         if ( parag )
02207             *parag = p->paragId();
02208         if ( index )
02209             *index = res;
02210         return TRUE;
02211         }
02212         if ( forward ) {
02213         start = res + 1;
02214         } else {
02215         if ( res == 0 )
02216             break;
02217         start = res - 1;
02218         }
02219     }
02220     p = forward ? p->next() : p->prev();
02221     }
02222 
02223     return FALSE;
02224 }
02225 
02226 #if 0
02227 void KoTextDocument::setTextFormat( Qt::TextFormat f )
02228 {
02229     txtFormat = f;
02230 }
02231 
02232 Qt::TextFormat KoTextDocument::textFormat() const
02233 {
02234     return txtFormat;
02235 }
02236 #endif
02237 
02238 bool KoTextDocument::inSelection( int selId, const QPoint &pos ) const
02239 {
02240     QMap<int, KoTextDocumentSelection>::ConstIterator it = selections.find( selId );
02241     if ( it == selections.end() )
02242     return FALSE;
02243 
02244     KoTextDocumentSelection sel = *it;
02245     KoTextParag *startParag = sel.startCursor.parag();
02246     KoTextParag *endParag = sel.endCursor.parag();
02247     if ( sel.startCursor.parag() == sel.endCursor.parag() &&
02248      sel.startCursor.parag()->selectionStart( selId ) == sel.endCursor.parag()->selectionEnd( selId ) )
02249     return FALSE;
02250     if ( sel.endCursor.parag()->paragId() < sel.startCursor.parag()->paragId() ) {
02251     endParag = sel.startCursor.parag();
02252     startParag = sel.endCursor.parag();
02253     }
02254 
02255     KoTextParag *p = startParag;
02256     while ( p ) {
02257     if ( p->rect().contains( pos ) ) {
02258         bool inSel = FALSE;
02259         int selStart = p->selectionStart( selId );
02260         int selEnd = p->selectionEnd( selId );
02261         int y = 0;
02262         int h = 0;
02263         for ( int i = 0; i < p->length(); ++i ) {
02264         if ( i == selStart )
02265             inSel = TRUE;
02266         if ( i == selEnd )
02267             break;
02268         if ( p->at( i )->lineStart ) {
02269             y = (*p->lineStarts.find( i ))->y;
02270             h = (*p->lineStarts.find( i ))->h;
02271         }
02272         if ( pos.y() - p->rect().y() >= y && pos.y() - p->rect().y() <= y + h ) {
02273             if ( inSel && pos.x() >= p->at( i )->x &&
02274              pos.x() <= p->at( i )->x + p->at( i )->width /*p->at( i )->format()->width( p->at( i )->c )*/ )
02275             return TRUE;
02276         }
02277         }
02278     }
02279     if ( pos.y() < p->rect().y() )
02280         break;
02281     if ( p == endParag )
02282         break;
02283     p = p->next();
02284     }
02285 
02286     return FALSE;
02287 }
02288 
02289 #if 0
02290 void KoTextDocument::doLayout( QPainter *p, int w )
02291 {
02292     if ( !is_printer( p ) )
02293     p = 0;
02294     withoutDoubleBuffer = ( p != 0 );
02295     flow_->setWidth( w );
02296     cw = w;
02297     vw = w;
02298     fCollection->setPainter( p );
02299     KoTextParag *parag = fParag;
02300     while ( parag ) {
02301     parag->invalidate( 0 );
02302     parag->setPainter( p, TRUE );
02303     parag->format();
02304     parag = parag->next();
02305     }
02306 
02307     fCollection->setPainter( 0 );
02308     parag = fParag;
02309     while ( parag ) {
02310     parag->setPainter( 0, FALSE );
02311     parag = parag->next();
02312     }
02313 }
02314 #endif
02315 
02316 QPixmap *KoTextDocument::bufferPixmap( const QSize &s )
02317 {
02318     if ( !buf_pixmap ) {
02319     int w = QABS( s.width() );
02320     int h = QABS( s.height() );
02321     buf_pixmap = new QPixmap( w, h );
02322     } else {
02323     if ( buf_pixmap->width() < s.width() ||
02324          buf_pixmap->height() < s.height() ) {
02325         buf_pixmap->resize( QMAX( s.width(), buf_pixmap->width() ),
02326                 QMAX( s.height(), buf_pixmap->height() ) );
02327     }
02328     }
02329 
02330     return buf_pixmap;
02331 }
02332 
02333 #if 0
02334 void KoTextDocument::draw( QPainter *p, const QRect &rect, const QColorGroup &cg, const QBrush *paper )
02335 {
02336     if ( !firstParag() )
02337     return;
02338 
02339     QBrush bgBrush = paper ? *paper : cg.brush( QColorGroup::Base ); // ## QRT doesn't use cg.brush(Base)
02340     {
02341     p->setBrushOrigin( -int( p->translationX() ),
02342                -int( p->translationY() ) );
02343     p->fillRect( rect, bgBrush );
02344     }
02345 
02346 #if 0 // strange code found in QRT - I don't want all my colors to go away !
02347     if ( formatCollection()->defaultFormat()->color() != cg.text() ) {
02348     QDict<KoTextFormat> formats = formatCollection()->dict();
02349     QDictIterator<KoTextFormat> it( formats );
02350     while ( it.current() ) {
02351         if ( it.current() == formatCollection()->defaultFormat() ) {
02352         ++it;
02353         continue;
02354         }
02355         it.current()->setColor( cg.text() );
02356         ++it;
02357     }
02358     formatCollection()->defaultFormat()->setColor( cg.text() );
02359     }
02360 #endif
02361 
02362     KoTextParag *parag = firstParag();
02363     while ( parag ) {
02364     if ( !parag->isValid() )
02365         parag->format();
02366     int y = parag->rect().y();
02367     QRect pr( parag->rect() );
02368     pr.setX( 0 );
02369     pr.setWidth( QWIDGETSIZE_MAX );
02370     if ( !rect.isNull() && !rect.intersects( pr ) ) {
02371         parag = parag->next();
02372         continue;
02373     }
02374     p->translate( 0, y );
02375     if ( rect.isValid() )
02376         parag->paint( *p, cg, 0, FALSE, rect.x(), rect.y(), rect.width(), rect.height() );
02377     else
02378         parag->paint( *p, cg, 0, FALSE );
02379     p->translate( 0, -y );
02380     parag = parag->next();
02381     if ( !flow()->isEmpty() )
02382         flow()->drawFloatingItems( p, rect.x(), rect.y(), rect.width(), rect.height(), cg, FALSE );
02383     }
02384 }
02385 
02386 void KoTextDocument::drawParag( QPainter *p, KoTextParag *parag, int cx, int cy, int cw, int ch,
02387                    QPixmap *&doubleBuffer, const QColorGroup &cg,
02388                    bool drawCursor, KoTextCursor *cursor, bool resetChanged )
02389 {
02390     QPainter *painter = 0;
02391     if ( resetChanged )
02392     parag->setChanged( FALSE );
02393     QRect ir( parag->rect() );
02394     bool useDoubleBuffer = true;
02395     //bool useDoubleBuffer = !parag->document()->parent();
02396     //if ( !useDoubleBuffer && parag->document()->nextDoubleBuffered )
02397     //useDoubleBuffer = TRUE;
02398     if ( p->device()->devType() == QInternal::Printer )
02399     useDoubleBuffer = FALSE;
02400 
02401     if ( useDoubleBuffer  ) {
02402     if ( cx >= 0 && cy >= 0 )
02403         {
02404         ir = ir.intersect( QRect( cx, cy, cw, ch ) );
02405             if (ir.isEmpty())
02406                 useDoubleBuffer = FALSE;
02407         }
02408     }
02409 
02410     if ( useDoubleBuffer  ) {
02411     painter = new QPainter;
02412     if ( !doubleBuffer ||
02413          ir.width() > doubleBuffer->width() ||
02414          ir.height() > doubleBuffer->height() ) {
02415         doubleBuffer = bufferPixmap( ir.size() );
02416         painter->begin( doubleBuffer );
02417     } else {
02418         painter->begin( doubleBuffer );
02419     }
02420     } else {
02421     painter = p;
02422     painter->translate( ir.x(), ir.y() );
02423     }
02424 
02425     painter->setBrushOrigin( -ir.x(), -ir.y() );
02426 
02427     if ( useDoubleBuffer || is_printer( painter ) ) {
02428     if ( !parag->backgroundColor() )
02429         painter->fillRect( QRect( 0, 0, ir.width(), ir.height() ),
02430                    cg.brush( QColorGroup::Base ) );
02431     else
02432         painter->fillRect( QRect( 0, 0, ir.width(), ir.height() ),
02433                    *parag->backgroundColor() );
02434     } else {
02435     if ( cursor && cursor->parag() == parag ) {
02436         if ( !parag->backgroundColor() )
02437         painter->fillRect( QRect( parag->at( cursor->index() )->x, 0, 2, ir.height() ),
02438                    cg.brush( QColorGroup::Base ) );
02439         else
02440         painter->fillRect( QRect( parag->at( cursor->index() )->x, 0, 2, ir.height() ),
02441                    *parag->backgroundColor() );
02442     }
02443     }
02444 
02445     painter->translate( -( ir.x() - parag->rect().x() ),
02446                -( ir.y() - parag->rect().y() ) );
02447     parag->paint( *painter, cg, drawCursor ? cursor : 0, TRUE, cx, cy, cw, ch );
02448     if ( !flow()->isEmpty() ) {
02449     painter->translate( 0, -parag->rect().y() );
02450     QRect cr( cx, cy, cw, ch );
02451     cr = cr.intersect( QRect( 0, parag->rect().y(), parag->rect().width(), parag->rect().height() ) );
02452     flow()->drawFloatingItems( painter, cr.x(), cr.y(), cr.width(), cr.height(), cg, FALSE );
02453     painter->translate( 0, +parag->rect().y() );
02454     }
02455 
02456     if ( useDoubleBuffer ) {
02457     delete painter;
02458     painter = 0;
02459     p->drawPixmap( ir.topLeft(), *doubleBuffer, QRect( QPoint( 0, 0 ), ir.size() ) );
02460     } else {
02461     painter->translate( -ir.x(), -ir.y() );
02462     }
02463 
02464     if ( useDoubleBuffer ) {
02465         QRect rect = parag->rect();
02466         if ( rect.x() + rect.width() < parag->document()->x() + parag->document()->width() ) {
02467             p->fillRect( rect.x() + rect.width(), rect.y(),
02468                          ( parag->document()->x() + parag->document()->width() ) -
02469                          ( rect.x() + rect.width() ),
02470                          rect.height(), cg.brush( QColorGroup::Base ) );
02471     }
02472 
02473     //parag->document()->nextDoubleBuffered = FALSE;
02474 }
02475 
02476 KoTextParag *KoTextDocument::draw( QPainter *p, int cx, int cy, int cw, int ch, const QColorGroup &cg,
02477                  bool onlyChanged, bool drawCursor, KoTextCursor *cursor, bool resetChanged )
02478 {
02479     if ( withoutDoubleBuffer || par && par->withoutDoubleBuffer ) {
02480     withoutDoubleBuffer = TRUE;
02481     QRect crect( cx, cy, cw, ch );
02482     draw( p, crect, cg );
02483     return 0;
02484     }
02485     withoutDoubleBuffer = FALSE;
02486 
02487     if ( !firstParag() )
02488     return 0;
02489 
02490     if ( drawCursor && cursor )
02491     tmpCursor = cursor;
02492     if ( cx < 0 && cy < 0 ) {
02493     cx = 0;
02494     cy = 0;
02495     cw = width();
02496     ch = height();
02497     }
02498 
02499     KoTextParag *lastFormatted = 0;
02500     KoTextParag *parag = firstParag();
02501 
02502     QPixmap *doubleBuffer = 0;
02503     QPainter painter;
02504     QRect crect( cx, cy, cw, ch );
02505 
02506     // Space above first parag
02507     if ( isPageBreakEnabled() && parag && cy <= parag->rect().y() && parag->rect().y() > 0 ) {
02508     QRect r( 0, 0,
02509          parag->document()->x() + parag->document()->width(),
02510          parag->rect().y() );
02511     r &= crect;
02512     if ( !r.isEmpty() )
02513         p->fillRect( r, cg.brush( QColorGroup::Base ) );
02514     }
02515 
02516     while ( parag ) {
02517     lastFormatted = parag;
02518     if ( !parag->isValid() )
02519         parag->format();
02520 
02521     QRect ir = parag->rect();
02522     if ( isPageBreakEnabled() && parag->next() )
02523         if ( ir.y() + ir.height() < parag->next()->rect().y() ) {
02524         QRect r( 0, ir.y() + ir.height(),
02525              parag->document()->x() + parag->document()->width(),
02526              parag->next()->rect().y() - ( ir.y() + ir.height() ) );
02527         r &= crect;
02528         if ( !r.isEmpty() )
02529             p->fillRect( r, cg.brush( QColorGroup::Base ) );
02530         }
02531 
02532     if ( !ir.intersects( crect ) ) {
02533         ir.setWidth( parag->document()->width() );
02534         if ( ir.intersects( crect ) )
02535         p->fillRect( ir.intersect( crect ), cg.brush( QColorGroup::Base ) );
02536         if ( ir.y() > cy + ch ) {
02537         tmpCursor = 0;
02538         if ( buf_pixmap && buf_pixmap->height() > 300 ) {
02539             delete buf_pixmap;
02540             buf_pixmap = 0;
02541         }
02542         return lastFormatted;
02543         }
02544         parag = parag->next();
02545         continue;
02546     }
02547 
02548     if ( !parag->hasChanged() && onlyChanged ) {
02549         parag = parag->next();
02550         continue;
02551     }
02552 
02553     drawParag( p, parag, cx, cy, cw, ch, doubleBuffer, cg, drawCursor, cursor, resetChanged );
02554     parag = parag->next();
02555     }
02556 
02557     parag = lastParag();
02558     if ( parag->rect().y() + parag->rect().height() < parag->document()->height() ) {
02559     if ( !parag->document()->parent() ) { // !useDoubleBuffer
02560         p->fillRect( 0, parag->rect().y() + parag->rect().height(), parag->document()->width(),
02561              parag->document()->height() - ( parag->rect().y() + parag->rect().height() ),
02562              cg.brush( QColorGroup::Base ) );
02563     }
02564     if ( !flow()->isEmpty() ) {
02565         QRect cr( cx, cy, cw, ch );
02566         cr = cr.intersect( QRect( 0, parag->rect().y() + parag->rect().height(), parag->document()->width(),
02567                       parag->document()->height() - ( parag->rect().y() + parag->rect().height() ) ) );
02568         flow()->drawFloatingItems( p, cr.x(), cr.y(), cr.width(), cr.height(), cg, FALSE );
02569     }
02570     }
02571 
02572     if ( buf_pixmap && buf_pixmap->height() > 300 ) {
02573     delete buf_pixmap;
02574     buf_pixmap = 0;
02575     }
02576 
02577     tmpCursor = 0;
02578     return lastFormatted;
02579 }
02580 #endif
02581 
02582 #if 0
02583 void KoTextDocument::setDefaultFont( const QFont &f )
02584 {
02585     updateFontSizes( f.pointSize() );
02586 }
02587 #endif
02588 
02589 void KoTextDocument::registerCustomItem( KoTextCustomItem *i, KoTextParag *p )
02590 {
02591     if ( i && i->placement() != KoTextCustomItem::PlaceInline )
02592     flow_->registerFloatingItem( i );
02593     p->registerFloatingItem( i );
02594     i->setParagraph( p );
02595     //kdDebug(32500) << "KoTextDocument::registerCustomItem " << (void*)i << endl;
02596     customItems.append( i );
02597 }
02598 
02599 void KoTextDocument::unregisterCustomItem( KoTextCustomItem *i, KoTextParag *p )
02600 {
02601     flow_->unregisterFloatingItem( i );
02602     p->unregisterFloatingItem( i );
02603     i->setParagraph( 0 );
02604     customItems.removeRef( i );
02605 }
02606 
02607 // unused in kotext, and needs KoTextStringChar::isAnchor
02608 #if 0
02609 bool KoTextDocument::hasFocusParagraph() const
02610 {
02611     return !!focusIndicator.parag;
02612 }
02613 
02614 QString KoTextDocument::focusHref() const
02615 {
02616     return focusIndicator.href;
02617 }
02618 
02619 bool KoTextDocument::focusNextPrevChild( bool next )
02620 {
02621     if ( !focusIndicator.parag ) {
02622     if ( next ) {
02623         focusIndicator.parag = fParag;
02624         focusIndicator.start = 0;
02625         focusIndicator.len = 0;
02626     } else {
02627         focusIndicator.parag = lParag;
02628         focusIndicator.start = lParag->length();
02629         focusIndicator.len = 0;
02630     }
02631     } else {
02632     focusIndicator.parag->setChanged( TRUE );
02633     }
02634     focusIndicator.href = QString::null;
02635 
02636     if ( next ) {
02637     KoTextParag *p = focusIndicator.parag;
02638     int index = focusIndicator.start + focusIndicator.len;
02639     while ( p ) {
02640         for ( int i = index; i < p->length(); ++i ) {
02641         if ( p->at( i )->isAnchor() ) {
02642             p->setChanged( TRUE );
02643             focusIndicator.parag = p;
02644             focusIndicator.start = i;
02645             focusIndicator.len = 0;
02646             focusIndicator.href = p->at( i )->format()->anchorHref();
02647             while ( i < p->length() ) {
02648             if ( !p->at( i )->format()->isAnchor() )
02649                 return TRUE;
02650             focusIndicator.len++;
02651             i++;
02652             }
02653         } else if ( p->at( i )->isCustom() ) {
02654 #ifdef QTEXTTABLE_AVAILABLE
02655             if ( p->at( i )->customItem()->isNested() ) {
02656             KoTextTable *t = (KoTextTable*)p->at( i )->customItem();
02657             QPtrList<KoTextTableCell> cells = t->tableCells();
02658             // first try to continue
02659             KoTextTableCell *c;
02660             bool resetCells = TRUE;
02661             for ( c = cells.first(); c; c = cells.next() ) {
02662                 if ( c->richText()->hasFocusParagraph() ) {
02663                 if ( c->richText()->focusNextPrevChild( next ) ) {
02664                     p->setChanged( TRUE );
02665                     focusIndicator.parag = p;
02666                     focusIndicator.start = i;
02667                     focusIndicator.len = 0;
02668                     focusIndicator.href = c->richText()->focusHref();
02669                     return TRUE;
02670                 } else {
02671                     resetCells = FALSE;
02672                     c = cells.next();
02673                     break;
02674                 }
02675                 }
02676             }
02677             // now really try
02678             if ( resetCells )
02679                 c = cells.first();
02680             for ( ; c; c = cells.next() ) {
02681                 if ( c->richText()->focusNextPrevChild( next ) ) {
02682                 p->setChanged( TRUE );
02683                 focusIndicator.parag = p;
02684                 focusIndicator.start = i;
02685                 focusIndicator.len = 0;
02686                 focusIndicator.href = c->richText()->focusHref();
02687                 return TRUE;
02688                 }
02689             }
02690             }
02691 #endif
02692         }
02693         }
02694         index = 0;
02695         p = p->next();
02696     }
02697     } else {
02698     KoTextParag *p = focusIndicator.parag;
02699     int index = focusIndicator.start - 1;
02700     if ( focusIndicator.len == 0 && index < focusIndicator.parag->length() - 1 )
02701         index++;
02702     while ( p ) {
02703         for ( int i = index; i >= 0; --i ) {
02704         if ( p->at( i )->format()->isAnchor() ) {
02705             p->setChanged( TRUE );
02706             focusIndicator.parag = p;
02707             focusIndicator.start = i;
02708             focusIndicator.len = 0;
02709             focusIndicator.href = p->at( i )->format()->anchorHref();
02710             while ( i >= -1 ) {
02711             if ( i < 0 || !p->at( i )->format()->isAnchor() ) {
02712                 focusIndicator.start++;
02713                 return TRUE;
02714             }
02715             if ( i < 0 )
02716                 break;
02717             focusIndicator.len++;
02718             focusIndicator.start--;
02719             i--;
02720             }
02721         } else if ( p->at( i )->isCustom() ) {
02722 #ifdef QTEXTTABLE_AVAILABLE
02723             if ( p->at( i )->customItem()->isNested() ) {
02724             KoTextTable *t = (KoTextTable*)p->at( i )->customItem();
02725             QPtrList<KoTextTableCell> cells = t->tableCells();
02726             // first try to continue
02727             KoTextTableCell *c;
02728             bool resetCells = TRUE;
02729             for ( c = cells.last(); c; c = cells.prev() ) {
02730                 if ( c->richText()->hasFocusParagraph() ) {
02731                 if ( c->richText()->focusNextPrevChild( next ) ) {
02732                     p->setChanged( TRUE );
02733                     focusIndicator.parag = p;
02734                     focusIndicator.start = i;
02735                     focusIndicator.len = 0;
02736                     focusIndicator.href = c->richText()->focusHref();
02737                     return TRUE;
02738                 } else {
02739                     resetCells = FALSE;
02740                     c = cells.prev();
02741                     break;
02742                 }
02743                 }
02744                 if ( cells.at() == 0 )
02745                 break;
02746             }
02747             // now really try
02748             if ( resetCells )
02749                 c = cells.last();
02750             for ( ; c; c = cells.prev() ) {
02751                 if ( c->richText()->focusNextPrevChild( next ) ) {
02752                 p->setChanged( TRUE );
02753                 focusIndicator.parag = p;
02754                 focusIndicator.start = i;
02755                 focusIndicator.len = 0;
02756                 focusIndicator.href = c->richText()->focusHref();
02757                 return TRUE;
02758                 }
02759                 if ( cells.at() == 0 )
02760                 break;
02761             }
02762             }
02763 #endif
02764         }
02765         }
02766         p = p->prev();
02767         if ( p )
02768         index = p->length() - 1;
02769     }
02770     }
02771 
02772     focusIndicator.parag = 0;
02773 
02774     return FALSE;
02775 }
02776 #endif
02777 
02778 int KoTextDocument::length() const
02779 {
02780     int l = 0;
02781     KoTextParag *p = fParag;
02782     while ( p ) {
02783     l += p->length() - 1; // don't count trailing space
02784     p = p->next();
02785     }
02786     return l;
02787 }
02788 
02789 #ifdef INDIC
02790 
02791 void KoTextCursor::fixCursorPosition()
02792 {
02793     // searches for the closest valid cursor position
02794     if ( string->string()->validCursorPosition( idx ) )
02795     return;
02796 
02797     int lineIdx;
02798     KoTextStringChar *start = string->lineStartOfChar( idx, &lineIdx, 0 );
02799     int x = string->string()->at( idx ).x;
02800     int diff = QABS(start->x - x);
02801     int best = lineIdx;
02802 
02803     KoTextStringChar *c = start;
02804     ++c;
02805 
02806     KoTextStringChar *end = &string->string()->at( string->length()-1 );
02807     while ( c <= end && !c->lineStart ) {
02808     int xp = c->x;
02809     if ( c->rightToLeft )
02810         xp += c->pixelwidth; //string->string()->width( lineIdx + (c-start) );
02811     int ndiff = QABS(xp - x);
02812     if ( ndiff < diff && string->string()->validCursorPosition(lineIdx + (c-start)) ) {
02813         diff = ndiff;
02814         best = lineIdx + (c-start);
02815     }
02816     ++c;
02817     }
02818     idx = best;
02819 }
02820 
02821 #endif
02822 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
02823 
02824 KoTextString::KoTextString()
02825 {
02826     bidiDirty = TRUE;
02827     bNeedsSpellCheck = true;
02828     bidi = FALSE;
02829     rightToLeft = FALSE;
02830     dir = QChar::DirON;
02831 }
02832 
02833 KoTextString::KoTextString( const KoTextString &s )
02834 {
02835     bidiDirty = s.bidiDirty;
02836     bNeedsSpellCheck = s.bNeedsSpellCheck;
02837     bidi = s.bidi;
02838     rightToLeft = s.rightToLeft;
02839     dir = s.dir;
02840     data = s.data;
02841     data.detach();
02842     for ( int i = 0; i < (int)data.size(); ++i ) {
02843         KoTextFormat *f = data[i].format();
02844         if ( f )
02845             f->addRef();
02846     }
02847 }
02848 
02849 void KoTextString::insert( int index, const QString &s, KoTextFormat *f )
02850 {
02851     int os = data.size();
02852     data.resize( data.size() + s.length() );
02853     if ( index < os ) {
02854     memmove( data.data() + index + s.length(), data.data() + index,
02855          sizeof( KoTextStringChar ) * ( os - index ) );
02856     }
02857     for ( int i = 0; i < (int)s.length(); ++i ) {
02858     KoTextStringChar &ch = data[ (int)index + i ];
02859     ch.x = 0;
02860     ch.pixelxadj = 0;
02861     ch.pixelwidth = 0;
02862     ch.width = 0;
02863     ch.lineStart = 0;
02864     ch.d.format = 0;
02865     ch.type = KoTextStringChar::Regular;
02866     ch.rightToLeft = 0;
02867     ch.startOfRun = 0;
02868         ch.c = s[ i ];
02869 #ifdef DEBUG_COLLECTION
02870     kdDebug(32500) << "KoTextString::insert setting format " << f << " to character " << (int)index+i << endl;
02871 #endif
02872     ch.setFormat( f );
02873     }
02874     bidiDirty = TRUE;
02875     bNeedsSpellCheck = true;
02876 }
02877 
02878 KoTextString::~KoTextString()
02879 {
02880     clear();
02881 }
02882 
02883 void KoTextString::insert( int index, KoTextStringChar *c )
02884 {
02885     int os = data.size();
02886     data.resize( data.size() + 1 );
02887     if ( index < os ) {
02888     memmove( data.data() + index + 1, data.data() + index,
02889          sizeof( KoTextStringChar ) * ( os - index ) );
02890     }
02891     KoTextStringChar &ch = data[ (int)index ];
02892     ch.c = c->c;
02893     ch.x = 0;
02894     ch.pixelxadj = 0;
02895     ch.pixelwidth = 0;
02896     ch.width = 0;
02897     ch.lineStart = 0;
02898     ch.rightToLeft = 0;
02899     ch.d.format = 0;
02900     ch.type = KoTextStringChar::Regular;
02901     ch.setFormat( c->format() );
02902     bidiDirty = TRUE;
02903     bNeedsSpellCheck = true;
02904 }
02905 
02906 void KoTextString::truncate( int index )
02907 {
02908     index = QMAX( index, 0 );
02909     index = QMIN( index, (int)data.size() - 1 );
02910     if ( index < (int)data.size() ) {
02911     for ( int i = index + 1; i < (int)data.size(); ++i ) {
02912         KoTextStringChar &ch = data[ i ];
02913         if ( ch.isCustom() ) {
02914         delete ch.customItem();
02915         if ( ch.d.custom->format )
02916             ch.d.custom->format->removeRef();
02917         delete ch.d.custom;
02918         ch.d.custom = 0;
02919         } else if ( ch.format() ) {
02920         ch.format()->removeRef();
02921         }
02922     }
02923     }
02924     data.truncate( index );
02925     bidiDirty = TRUE;
02926     bNeedsSpellCheck = true;
02927 }
02928 
02929 void KoTextString::remove( int index, int len )
02930 {
02931     for ( int i = index; i < (int)data.size() && i - index < len; ++i ) {
02932     KoTextStringChar &ch = data[ i ];
02933     if ( ch.isCustom() ) {
02934         delete ch.customItem();
02935         if ( ch.d.custom->format )
02936         ch.d.custom->format->removeRef();
02937             delete ch.d.custom;
02938         ch.d.custom = 0;
02939     } else if ( ch.format() ) {
02940         ch.format()->removeRef();
02941     }
02942     }
02943     memmove( data.data() + index, data.data() + index + len,
02944          sizeof( KoTextStringChar ) * ( data.size() - index - len ) );
02945     data.resize( data.size() - len, QGArray::SpeedOptim );
02946     bidiDirty = TRUE;
02947     bNeedsSpellCheck = true;
02948 }
02949 
02950 void KoTextString::clear()
02951 {
02952     for ( int i = 0; i < (int)data.count(); ++i ) {
02953     KoTextStringChar &ch = data[ i ];
02954     if ( ch.isCustom() ) {
02955         delete ch.customItem();
02956         if ( ch.d.custom->format )
02957         ch.d.custom->format->removeRef();
02958         delete ch.d.custom;
02959         ch.d.custom = 0;
02960     } else if ( ch.format() ) {
02961         ch.format()->removeRef();
02962     }
02963     }
02964     data.resize( 0 );
02965 }
02966 
02967 void KoTextString::setFormat( int index, KoTextFormat *f, bool useCollection )
02968 {
02969     KoTextStringChar &ch = data[ index ];
02970 //    kdDebug(32500) << "KoTextString::setFormat index=" << index << " f=" << f << endl;
02971     if ( useCollection && ch.format() )
02972     {
02973     //kdDebug(32500) << "KoTextString::setFormat removing ref on old format " << ch.format() << endl;
02974     ch.format()->removeRef();
02975     }
02976     ch.setFormat( f );
02977 }
02978 
02979 void KoTextString::checkBidi() const
02980 {
02981 #ifndef INDIC
02982     bool rtlKnown = FALSE;
02983 #else
02984     KoTextString *that = (KoTextString *)this;
02985     that->bidiDirty = FALSE;
02986     int length = data.size();
02987     if ( !length ) {
02988         that->bidi = FALSE;
02989         that->rightToLeft = dir == QChar::DirR;
02990         return;
02991     }
02992     const KoTextStringChar *start = data.data();
02993     const KoTextStringChar *end = start + length;
02994 
02995     // determines the properties we need for layouting
02996     QTextEngine textEngine( toString(), 0 );
02997     textEngine.direction = (QChar::Direction) dir;
02998     textEngine.itemize(QTextEngine::SingleLine);
02999     const QCharAttributes *ca = textEngine.attributes() + length-1;
03000     KoTextStringChar *ch = (KoTextStringChar *)end - 1;
03001     QScriptItem *item = &textEngine.items[textEngine.items.size()-1];
03002     unsigned char bidiLevel = item->analysis.bidiLevel;
03003     if ( bidiLevel )
03004         that->bidi = TRUE;
03005     int pos = length-1;
03006     while ( ch >= start ) {
03007         if ( item->position > pos ) {
03008             --item;
03009             Q_ASSERT( item >= &textEngine.items[0] );
03010             Q_ASSERT( item < &textEngine.items[textEngine.items.size()] );
03011             bidiLevel = item->analysis.bidiLevel;
03012             if ( bidiLevel )
03013                 that->bidi = TRUE;
03014         }
03015         ch->softBreak = ca->softBreak;
03016         ch->whiteSpace = ca->whiteSpace;
03017         ch->charStop = ca->charStop;
03018         ch->wordStop = ca->wordStop;
03019         //ch->bidiLevel = bidiLevel;
03020         ch->rightToLeft = (bidiLevel%2);
03021         --ch;
03022         --ca;
03023         --pos;
03024     }
03025 
03026 #endif
03027     if ( dir == QChar::DirR ) {
03028 #ifndef INDIC
03029     ((KoTextString *)this)->bidi = TRUE;
03030     ((KoTextString *)this)->rightToLeft = TRUE;
03031     ((KoTextString *)this)->bidiDirty = FALSE;
03032     return;
03033 #else
03034         that->bidi = TRUE;
03035         that->rightToLeft = TRUE;
03036 #endif
03037     } else if ( dir == QChar::DirL ) {
03038 #ifndef INDIC
03039     ((KoTextString *)this)->rightToLeft = FALSE;
03040     rtlKnown = TRUE;
03041 #else
03042         that->rightToLeft = FALSE;
03043 #endif
03044     } else {
03045 #ifndef INDIC
03046     ((KoTextString *)this)->rightToLeft = FALSE;
03047     }
03048 
03049     int len = data.size();
03050     const KoTextStringChar *c = data.data();
03051     ((KoTextString *)this)->bidi = FALSE;
03052     while( len ) {
03053     if ( !rtlKnown ) {
03054         switch( c->c.direction() )
03055         {
03056         case QChar::DirL:
03057         case QChar::DirLRO:
03058         case QChar::DirLRE:
03059             ((KoTextString *)this)->rightToLeft = FALSE;
03060             rtlKnown = TRUE;
03061             break;
03062         case QChar::DirR:
03063         case QChar::DirAL:
03064         case QChar::DirRLO:
03065         case QChar::DirRLE:
03066             ((KoTextString *)this)->rightToLeft = TRUE;
03067             rtlKnown = TRUE;
03068             break;
03069         default:
03070             break;
03071         }
03072     }
03073     uchar row = c->c.row();
03074     if( (row > 0x04 && row < 0x09) || ( row > 0xfa && row < 0xff ) ) {
03075         ((KoTextString *)this)->bidi = TRUE;
03076             if ( rtlKnown )
03077                 break;
03078     }
03079     len--;
03080     ++c;
03081 #else
03082         that->rightToLeft = (textEngine.direction == QChar::DirR);
03083 #endif
03084     }
03085 #ifndef INDIC
03086     ((KoTextString *)this)->bidiDirty = FALSE;
03087 #endif
03088 }
03089 
03090 QMemArray<KoTextStringChar> KoTextString::subString( int start, int len ) const
03091 {
03092     if ( len == 0xFFFFFF )
03093     len = data.size();
03094     QMemArray<KoTextStringChar> a;
03095     a.resize( len );
03096     for ( int i = 0; i < len; ++i ) {
03097     KoTextStringChar *c = &data[ i + start ];
03098     a[ i ].c = c->c;
03099     a[ i ].x = 0;
03100     a[ i ].pixelxadj = 0;
03101     a[ i ].pixelwidth = 0;
03102     a[ i ].width = 0;
03103     a[ i ].lineStart = 0;
03104     a[ i ].rightToLeft = 0;
03105     a[ i ].d.format = 0;
03106     a[ i ].type = KoTextStringChar::Regular;
03107     a[ i ].setFormat( c->format() );
03108     if ( c->format() )
03109         c->format()->addRef();
03110     }
03111     return a;
03112 }
03113 
03114 QString KoTextString::mid( int start, int len ) const
03115 {
03116     if ( len == 0xFFFFFF )
03117     len = data.size();
03118     QString res;
03119     res.setLength( len );
03120     for ( int i = 0; i < len; ++i ) {
03121     KoTextStringChar *c = &data[ i + start ];
03122     res[ i ] = c->c;
03123     }
03124     return res;
03125 }
03126 
03127 QString KoTextString::toString( const QMemArray<KoTextStringChar> &data )
03128 {
03129     QString s;
03130     int l = data.size();
03131     s.setUnicode( 0, l );
03132     KoTextStringChar *c = data.data();
03133     QChar *uc = (QChar *)s.unicode();
03134     while ( l-- ) {
03135     *uc = c->c;
03136     uc++;
03137     c++;
03138     }
03139 
03140     return s;
03141 }
03142 
03143 QString KoTextString::toReverseString() const
03144 {
03145     QString s;
03146     int l = length();
03147     s.setUnicode(0, l);
03148     KoTextStringChar *c = data.data() + (l-1);
03149     QChar *uc = (QChar *)s.unicode();
03150     while ( l-- ) {
03151     *uc = c->c;
03152     uc++;
03153     c--;
03154     }
03155 
03156     return s;
03157 }
03158 
03159 #ifdef INDIC
03160 int KoTextString::nextCursorPosition( int next )
03161 {
03162     if ( bidiDirty )
03163         checkBidi();
03164 
03165     const KoTextStringChar *c = data.data();
03166     int len = length();
03167 
03168     if ( next < len - 1 ) {
03169         next++;
03170         while ( next < len - 1 && !c[next].charStop )
03171             next++;
03172     }
03173     return next;
03174 }
03175 
03176 int KoTextString::previousCursorPosition( int prev )
03177 {
03178     if ( bidiDirty )
03179         checkBidi();
03180 
03181     const KoTextStringChar *c = data.data();
03182 
03183     if ( prev ) {
03184         prev--;
03185         while ( prev && !c[prev].charStop )
03186             prev--;
03187     }
03188     return prev;
03189 }
03190 
03191 bool KoTextString::validCursorPosition( int idx )
03192 {
03193     if ( bidiDirty )
03194         checkBidi();
03195 
03196     return (at( idx ).charStop);
03197 }
03198 
03200 #endif
03201 
03202 void KoTextStringChar::setFormat( KoTextFormat *f )
03203 {
03204     if ( type == Regular ) {
03205     d.format = f;
03206     } else {
03207     if ( !d.custom ) {
03208         d.custom = new CustomData;
03209         d.custom->custom = 0;
03210     }
03211     d.custom->format = f;
03212         if ( d.custom->custom )
03213             d.custom->custom->setFormat( f );
03214     }
03215 }
03216 
03217 void KoTextStringChar::setCustomItem( KoTextCustomItem *i )
03218 {
03219     if ( type == Regular ) {
03220     KoTextFormat *f = format();
03221     d.custom = new CustomData;
03222     d.custom->format = f;
03223     type = Custom;
03224     } else {
03225     delete d.custom->custom;
03226     }
03227     d.custom->custom = i;
03228 }
03229 
03230 void KoTextStringChar::loseCustomItem() // setRegular() might be a better name
03231 {
03232     if ( isCustom() ) {
03233     KoTextFormat *f = d.custom->format;
03234     d.custom->custom = 0;
03235     delete d.custom;
03236     type = Regular;
03237     d.format = f;
03238     }
03239 }
03240 
03241 KoTextStringChar::~KoTextStringChar()
03242 {
03243     if ( format() )
03244     format()->removeRef();
03245     switch ( type ) {
03246     case Custom:
03247         delete d.custom; break;
03248     default:
03249         break;
03250     }
03251 }
03252 
03253 #ifndef INDIC
03254 KoTextStringChar *KoTextStringChar::clone() const
03255 {
03256     KoTextStringChar *chr = new KoTextStringChar;
03257     chr->c = c;
03258     chr->x = 0;
03259     chr->pixelxadj = 0;
03260     chr->pixelwidth = 0;
03261     chr->width = 0;
03262     chr->lineStart = 0;
03263     chr->rightToLeft = 0;
03264     chr->d.format = 0;
03265     chr->type = KoTextStringChar::Regular;
03266     chr->setFormat( format() );
03267     if ( chr->format() )
03268     chr->format()->addRef();
03269     return chr;
03270 }
03271 
03272 #endif
03273 int KoTextStringChar::height() const
03274 {
03275     return !isCustom() ? format()->height() : ( customItem()->placement() == KoTextCustomItem::PlaceInline ? customItem()->height : 0 );
03276 }
03277 
03278 int KoTextStringChar::ascent() const
03279 {
03280     return !isCustom() ? format()->ascent() : ( customItem()->placement() == KoTextCustomItem::PlaceInline ? customItem()->ascent() : 0 );
03281 }
03282 
03283 int KoTextStringChar::descent() const
03284 {
03285     return !isCustom() ? format()->descent() : 0;
03286 }
03287 
03288 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
03289 
03290 KoTextParag::KoTextParag( KoTextDocument *d, KoTextParag *pr, KoTextParag *nx, bool updateIds )
03291     : invalid( 0 ), p( pr ), n( nx ), doc( d ),
03292       changed( FALSE ),
03293       fullWidth( TRUE ),
03294       newLinesAllowed( TRUE ), // default in kotext
03295       visible( TRUE ), breakable( TRUE ), movedDown( FALSE ),
03296       align( 0 ),
03297       m_lineChanged( -1 ),
03298       m_wused( 0 ),
03299       mSelections( 0 ),
03300       mFloatingItems( 0 ),
03301       tArray( 0 )
03302 {
03303     defFormat = formatCollection()->defaultFormat();
03304     /*if ( !doc ) {
03305     tabStopWidth = defFormat->width( 'x' ) * 8;
03306     commandHistory = new KoTextDocCommandHistory( 100 );
03307     }*/
03308 #if defined(PARSER_DEBUG)
03309     kdDebug(32500) << debug_indent + "new KoTextParag" << endl;
03310 #endif
03311 
03312     if ( p ) {
03313     p->n = this;
03314 #ifdef QTEXTTABLE_AVAILABLE
03315     if ( p->tc )
03316         tc = p->tc;
03317 #endif
03318     }
03319     if ( n ) {
03320     n->p = this;
03321 #ifdef QTEXTTABLE_AVAILABLE
03322     if ( n->tc )
03323         tc = n->tc;
03324 #endif
03325     }
03326 
03327 #ifdef QTEXTTABLE_AVAILABLE
03328     if ( !tc && d && d->tableCell() )
03329     tc = d->tableCell();
03330 #endif
03331 
03332     if ( !p && doc )
03333     doc->setFirstParag( this );
03334     if ( !n && doc )
03335     doc->setLastParag( this );
03336 
03337     //firstFormat = TRUE; //// unused
03338     //firstPProcess = TRUE;
03339     //state = -1;
03340     //needPreProcess = FALSE;
03341 
03342     if ( p )
03343     id = p->id + 1;
03344     else
03345     id = 0;
03346     if ( n && updateIds ) {
03347     KoTextParag *s = n;
03348     while ( s ) {
03349         s->id = s->p->id + 1;
03350         //s->lm = s->rm = s->tm = s->bm = -1, s->flm = -1;
03351         s = s->n;
03352     }
03353     }
03354 
03355     str = new KoTextString();
03356     str->insert( 0, " ", formatCollection()->defaultFormat() );
03357 }
03358 
03359 KoTextParag::~KoTextParag()
03360 {
03361     //kdDebug(32500) << "KoTextParag::~KoTextParag " << this << " id=" << paragId() << endl;
03362     delete str;
03363 //    if ( doc && p == doc->minwParag ) {
03364 //  doc->minwParag = 0;
03365 //  doc->minw = 0;
03366 //    }
03367     if ( !doc ) {
03368     //delete pFormatter;
03369     //delete commandHistory;
03370     }
03371     delete [] tArray;
03372     //delete eData;
03373     QMap<int, KoTextParagLineStart*>::Iterator it = lineStarts.begin();
03374     for ( ; it != lineStarts.end(); ++it )
03375     delete *it;
03376     if ( mSelections ) delete mSelections;
03377     if ( mFloatingItems ) delete mFloatingItems;
03378 
03379     if (p)
03380        p->setNext(n);
03381     if (n)
03382        n->setPrev(p);
03383 
03385     if ( doc && !doc->isDestroying() )
03386     {
03387         doc->informParagraphDeleted( this );
03388     }
03389     //kdDebug(32500) << "KoTextParag::~KoTextParag " << this << endl;
03391 }
03392 
03393 void KoTextParag::setNext( KoTextParag *s )
03394 {
03395     n = s;
03396     if ( !n && doc )
03397     doc->setLastParag( this );
03398 }
03399 
03400 void KoTextParag::setPrev( KoTextParag *s )
03401 {
03402     p = s;
03403     if ( !p && doc )
03404     doc->setFirstParag( this );
03405 }
03406 
03407 void KoTextParag::invalidate( int chr )
03408 {
03409     if ( invalid < 0 )
03410     invalid = chr;
03411     else
03412     invalid = QMIN( invalid, chr );
03413 #if 0 
03414     if ( mFloatingItems ) {
03415     for ( KoTextCustomItem *i = mFloatingItems->first(); i; i = mFloatingItems->next() )
03416         i->move( 0, -1 );
03417     }
03418 #endif
03419     //lm = rm = bm = tm = flm = -1;
03420 }
03421 
03422 void KoTextParag::setChanged( bool b, bool /*recursive*/ )
03423 {
03424     changed = b;
03425     m_lineChanged = -1; // all
03426     //if ( recursive ) {
03427 //  if ( doc && doc->parentParag() )
03428 //      doc->parentParag()->setChanged( b, recursive );
03429 //    }
03430 }
03431 
03432 void KoTextParag::setLineChanged( short int line )
03433 {
03434     if ( m_lineChanged == -1 ) {
03435         if ( !changed ) // only if the whole parag wasn't "changed" already
03436             m_lineChanged = line;
03437     }
03438     else
03439         m_lineChanged = QMIN( m_lineChanged, line ); // also works if line=-1
03440     changed = true;
03441     //kdDebug(32500) << "KoTextParag::setLineChanged line=" << line << " -> m_lineChanged=" << m_lineChanged << endl;
03442 }
03443 
03444 void KoTextParag::insert( int index, const QString &s )
03445 {
03446 #if 0
03447     if ( doc && !doc->useFormatCollection() && doc->preProcessor() )
03448     str->insert( index, s,
03449              doc->preProcessor()->format( KoTextPreProcessor::Standard ) );
03450     else
03451 #endif
03452     str->insert( index, s, formatCollection()->defaultFormat() );
03453     invalidate( index );
03454     //needPreProcess = TRUE;
03455 }
03456 
03457 void KoTextParag::truncate( int index )
03458 {
03459     str->truncate( index );
03460     insert( length(), " " );
03461     //needPreProcess = TRUE;
03462 }
03463 
03464 void KoTextParag::remove( int index, int len )
03465 {
03466     if ( index + len - str->length() > 0 )
03467     return;
03468     for ( int i = index; i < index + len; ++i ) {
03469     KoTextStringChar *c = at( i );
03470     if ( doc && c->isCustom() ) {
03471         doc->unregisterCustomItem( c->customItem(), this );
03472         //removeCustomItem();
03473     }
03474     }
03475     str->remove( index, len );
03476     invalidate( 0 );
03477     //needPreProcess = TRUE;
03478 }
03479 
03480 void KoTextParag::join( KoTextParag *s )
03481 {
03482     //kdDebug(32500) << "KoTextParag::join this=" << paragId() << " (length " << length() << ") with " << s->paragId() << " (length " << s->length() << ")" << endl;
03483     int oh = r.height() + s->r.height();
03484     n = s->n;
03485     if ( n )
03486     n->p = this;
03487     else if ( doc )
03488     doc->setLastParag( this );
03489 
03490     int start = str->length();
03491     if ( length() > 0 && at( length() - 1 )->c == ' ' ) {
03492     remove( length() - 1, 1 );
03493     --start;
03494     }
03495     append( s->str->toString(), TRUE );
03496 
03497     for ( int i = 0; i < s->length(); ++i ) {
03498     if ( !doc || doc->useFormatCollection() ) {
03499         s->str->at( i ).format()->addRef();
03500         str->setFormat( i + start, s->str->at( i ).format(), TRUE );
03501     }
03502     if ( s->str->at( i ).isCustom() ) {
03503         KoTextCustomItem * item = s->str->at( i ).customItem();
03504         str->at( i + start ).setCustomItem( item );
03505         s->str->at( i ).loseCustomItem();
03506         doc->unregisterCustomItem( item, s ); // ### missing in QRT
03507         doc->registerCustomItem( item, this );
03508     }
03509     }
03510     Q_ASSERT(str->at(str->length()-1).c == ' ');
03511 
03512     /*if ( !extraData() && s->extraData() ) {
03513     setExtraData( s->extraData() );
03514     s->setExtraData( 0 );
03515     } else if ( extraData() && s->extraData() ) {
03516     extraData()->join( s->extraData() );
03517         }*/
03518     delete s;
03519     invalidate( 0 );
03521     invalidateCounters();
03523     r.setHeight( oh );
03524     //needPreProcess = TRUE;
03525     if ( n ) {
03526     KoTextParag *s = n;
03527     while ( s ) {
03528         s->id = s->p->id + 1;
03529         //s->state = -1;
03530         //s->needPreProcess = TRUE;
03531         s->changed = TRUE;
03532         s = s->n;
03533     }
03534     }
03535     format();
03536     //state = -1;
03537 }
03538 
03539 void KoTextParag::move( int &dy )
03540 {
03541     //kdDebug(32500) << "KoTextParag::move paragId=" << paragId() << " dy=" << dy << endl;
03542     if ( dy == 0 )
03543     return;
03544     changed = TRUE;
03545     r.moveBy( 0, dy );
03546     if ( mFloatingItems ) {
03547     for ( KoTextCustomItem *i = mFloatingItems->first(); i; i = mFloatingItems->next() ) {
03548         i->finalize();
03549     }
03550     }
03551     //if ( p )
03552     //    p->lastInFrame = TRUE; // Qt does this, but the loop at the end of format() calls move a lot!
03553 
03554     movedDown = FALSE;
03555 
03556     // do page breaks if required
03557     if ( doc && doc->isPageBreakEnabled() ) {
03558     int shift;
03559     if ( ( shift = doc->formatter()->formatVertically(  doc, this ) ) ) {
03560         if ( p )
03561         p->setChanged( TRUE );
03562         dy += shift;
03563     }
03564     }
03565 }
03566 
03567 void KoTextParag::format( int start, bool doMove )
03568 {
03569     if ( !str || str->length() == 0 || !formatter() )
03570     return;
03571 
03572 #if 0
03573     if ( doc &&
03574      doc->preProcessor() &&
03575      ( needPreProcess || state == -1 ) )
03576     doc->preProcessor()->process( doc, this, invalid <= 0 ? 0 : invalid );
03577     needPreProcess = FALSE;
03578 #endif
03579 
03580     if ( invalid == -1 )
03581     return;
03582 
03583     //kdDebug(32500) << "KoTextParag::format " << this << " id:" << paragId() << endl;
03584 
03585     r.moveTopLeft( QPoint( documentX(), p ? p->r.y() + p->r.height() : documentY() ) );
03586     //if ( p )
03587     //    p->lastInFrame = FALSE;
03588 
03589     movedDown = FALSE;
03590     bool formattedAgain = FALSE;
03591 
03592  formatAgain:
03593     r.setWidth( documentWidth() );
03594 
03595     // Not really useful....
03596     if ( doc && mFloatingItems ) {
03597     for ( KoTextCustomItem *i = mFloatingItems->first(); i; i = mFloatingItems->next() ) {
03598         if ( i->placement() == KoTextCustomItem::PlaceRight )
03599         i->move( r.x() + r.width() - i->width, r.y() );
03600         else
03601         i->move( i->x(), r.y() );
03602     }
03603     }
03604     QMap<int, KoTextParagLineStart*> oldLineStarts = lineStarts;
03605     lineStarts.clear();
03606     int y;
03607     bool formatterWorked = formatter()->format( doc, this, start, oldLineStarts, y, m_wused );
03608 
03609     // It can't happen that width < minimumWidth -- hopefully.
03610     //r.setWidth( QMAX( r.width(), formatter()->minimumWidth() ) );
03611     //m_minw = formatter()->minimumWidth();
03612 
03613     QMap<int, KoTextParagLineStart*>::Iterator it = oldLineStarts.begin();
03614 
03615     for ( ; it != oldLineStarts.end(); ++it )
03616     delete *it;
03617 
03618 /*    if ( hasBorder() || string()->isRightToLeft() )
03621     {
03622         setWidth( textDocument()->width() - 1 );
03623     }
03624     else*/
03625     {
03626         if ( lineStarts.count() == 1 ) { //&& ( !doc || doc->flow()->isEmpty() ) ) {
03627 // kotext: for proper parag borders, we want all parags to be as wide as linestart->w
03628 /*            if ( !string()->isBidi() ) {
03629                 KoTextStringChar *c = &str->at( str->length() - 1 );
03630                 r.setWidth( c->x + c->width );
03631             } else*/ {
03632                 r.setWidth( lineStarts[0]->w );
03633             }
03634         }
03635         if ( newLinesAllowed ) {
03636             it = lineStarts.begin();
03637             int usedw = 0; int lineid = 0;
03638             for ( ; it != lineStarts.end(); ++it, ++lineid ) {
03639                 usedw = QMAX( usedw, (*it)->w );
03640             }
03641             if ( r.width() <= 0 ) {
03642                 // if the user specifies an invalid rect, this means that the
03643                 // bounding box should grow to the width that the text actually
03644                 // needs
03645                 r.setWidth( usedw );
03646             } else {
03647                 r.setWidth( QMIN( usedw, r.width() ) );
03648             }
03649         }
03650     }
03651 
03652     if ( y != r.height() )
03653     r.setHeight( y );
03654 
03655     if ( !visible )
03656     r.setHeight( 0 );
03657 
03658     // do page breaks if required
03659     if ( doc && doc->isPageBreakEnabled() ) {
03660         int shift = doc->formatter()->formatVertically( doc, this );
03661         //kdDebug(32500) << "formatVertically returned shift=" << shift << endl;
03662         if ( shift && !formattedAgain ) {
03663             formattedAgain = TRUE;
03664             goto formatAgain;
03665         }
03666     }
03667 
03668     if ( doc )
03669         doc->formatter()->postFormat( this );
03670 
03671     if ( n && doMove && n->invalid == -1 && r.y() + r.height() != n->r.y() ) {
03672         //kdDebug(32500) << "r=" << r << " n->r=" << n->r << endl;
03673     int dy = ( r.y() + r.height() ) - n->r.y();
03674     KoTextParag *s = n;
03675     bool makeInvalid = false; //p && p->lastInFrame;
03676     //kdDebug(32500) << "might move of dy=" << dy << ". previous's lastInFrame (=makeInvalid): " << makeInvalid << endl;
03677     while ( s && dy ) {
03678             if ( s->movedDown ) { // (not in QRT) : moved down -> invalidate and stop moving down
03679                 s->invalidate( 0 ); // (there is no point in moving down a parag that has a frame break...)
03680                 break;
03681             }
03682         if ( !s->isFullWidth() )
03683         makeInvalid = TRUE;
03684         if ( makeInvalid )
03685         s->invalidate( 0 );
03686         s->move( dy );
03687         //if ( s->lastInFrame )
03688             //    makeInvalid = TRUE;
03689         s = s->n;
03690     }
03691     }
03692 
03693 //#define DEBUG_CI_PLACEMENT
03694     if ( mFloatingItems ) {
03695 #ifdef DEBUG_CI_PLACEMENT
03696         kdDebug(32500) << lineStarts.count() << " lines" << endl;
03697 #endif
03698         // Place custom items - after the formatting is finished
03699         int len = length();
03700         int line = -1;
03701         int lineY = 0; // the one called "cy" in other algos
03702         int baseLine = 0;
03703         QMap<int, KoTextParagLineStart*>::Iterator it = lineStarts.begin();
03704         for ( int i = 0 ; i < len; ++i ) {
03705             KoTextStringChar *chr = &str->at( i );
03706             if ( chr->lineStart ) {
03707                 ++line;
03708                 if ( line > 0 )
03709                     ++it;
03710                 lineY = (*it)->y;
03711                 baseLine = (*it)->baseLine;
03712 #ifdef DEBUG_CI_PLACEMENT
03713                 kdDebug(32500) << "New line (" << line << "): lineStart=" << (*it) << " lineY=" << lineY << " baseLine=" << baseLine << " height=" << (*it)->h << endl;
03714 #endif
03715             }
03716             if ( chr->isCustom() ) {
03717                 int x = chr->x;
03718                 KoTextCustomItem* item = chr->customItem();
03719                 Q_ASSERT( baseLine >= item->ascent() ); // something went wrong in KoTextFormatter if this isn't the case
03720                 int y = lineY + baseLine - item->ascent();
03721 #ifdef DEBUG_CI_PLACEMENT
03722                 kdDebug(32500) << "Custom item: i=" << i << " x=" << x << " lineY=" << lineY << " baseLine=" << baseLine << " ascent=" << item->ascent() << " -> y=" << y << endl;
03723 #endif
03724                 item->move( x, y );
03725                 item->finalize();
03726             }
03727         }
03728     }
03729 
03730     //firstFormat = FALSE; //// unused
03731     if ( formatterWorked > 0 ) // only if it worked, i.e. we had some width to format it
03732     {
03733         invalid = -1;
03734     }
03735     changed = TRUE;
03736     //####   string()->setTextChanged( FALSE );
03737 }
03738 
03739 int KoTextParag::lineHeightOfChar( int i, int *bl, int *y ) const
03740 {
03741     if ( !isValid() )
03742     ( (KoTextParag*)this )->format();
03743 
03744     QMap<int, KoTextParagLineStart*>::ConstIterator it = lineStarts.end();
03745     --it;
03746     for ( ;; ) {
03747     if ( i >= it.key() ) {
03748         if ( bl )
03749         *bl = ( *it )->baseLine;
03750         if ( y )
03751         *y = ( *it )->y;
03752         return ( *it )->h;
03753     }
03754     if ( it == lineStarts.begin() )
03755         break;
03756     --it;
03757     }
03758 
03759     kdWarning(32500) << "KoTextParag::lineHeightOfChar: couldn't find lh for " << i << endl;
03760     return 15;
03761 }
03762 
03763 KoTextStringChar *KoTextParag::lineStartOfChar( int i, int *index, int *line ) const
03764 {
03765     if ( !isValid() )
03766     ( (KoTextParag*)this )->format();
03767 
03768     int l = (int)lineStarts.count() - 1;
03769     QMap<int, KoTextParagLineStart*>::ConstIterator it = lineStarts.end();
03770     --it;
03771     for ( ;; ) {
03772     if ( i >= it.key() ) {
03773         if ( index )
03774         *index = it.key();
03775         if ( line )
03776         *line = l;
03777         return &str->at( it.key() );
03778     }
03779     if ( it == lineStarts.begin() )
03780         break;
03781     --it;
03782     --l;
03783     }
03784 
03785     kdWarning(32500) << "KoTextParag::lineStartOfChar: couldn't find " << i << endl;
03786     return 0;
03787 }
03788 
03789 int KoTextParag::lines() const
03790 {
03791     if ( !isValid() )
03792     ( (KoTextParag*)this )->format();
03793 
03794     return (int)lineStarts.count();
03795 }
03796 
03797 KoTextStringChar *KoTextParag::lineStartOfLine( int line, int *index ) const
03798 {
03799     if ( !isValid() )
03800     ( (KoTextParag*)this )->format();
03801 
03802     if ( line >= 0 && line < (int)lineStarts.count() ) {
03803     QMap<int, KoTextParagLineStart*>::ConstIterator it = lineStarts.begin();
03804     while ( line-- > 0 )
03805         ++it;
03806     int i = it.key();
03807     if ( index )
03808         *index = i;
03809     return &str->at( i );
03810     }
03811 
03812     kdWarning(32500) << "KoTextParag::lineStartOfLine: couldn't find " << line << endl;
03813     return 0;
03814 }
03815 
03816 int KoTextParag::leftGap() const
03817 {
03818     if ( !isValid() )
03819     ( (KoTextParag*)this )->format();
03820 
03821     int line = 0;
03822     int x = str->at(0).x;  /* set x to x of first char */
03823     if ( str->isBidi() ) {
03824     for ( int i = 1; i < str->length(); ++i )
03825         x = QMIN(x, str->at(i).x);
03826     return x;
03827     }
03828 
03829     QMap<int, KoTextParagLineStart*>::ConstIterator it = lineStarts.begin();
03830     while (line < (int)lineStarts.count()) {
03831     int i = it.key(); /* char index */
03832     x = QMIN(x, str->at(i).x);
03833     ++it;
03834     ++line;
03835     }
03836     return x;
03837 }
03838 
03839 void KoTextParag::setFormat( int index, int len, const KoTextFormat *_f, bool useCollection, int flags )
03840 {
03841     Q_ASSERT( useCollection ); // just for info
03842     if ( index < 0 )
03843     index = 0;
03844     if ( index > str->length() - 1 )
03845     index = str->length() - 1;
03846     if ( index + len >= str->length() )
03847     len = str->length() - index;
03848 
03849     KoTextFormatCollection *fc = 0;
03850     if ( useCollection )
03851     fc = formatCollection();
03852     KoTextFormat *of;
03853     for ( int i = 0; i < len; ++i ) {
03854     of = str->at( i + index ).format();
03855     if ( !changed && _f->key() != of->key() )
03856         changed = TRUE;
03857         // Check things that need the textformatter to run
03858         // (e.g. not color changes)
03859         // ######## Is this test exhaustive?
03860     if ( invalid == -1 &&
03861          ( _f->font().family() != of->font().family() ||
03862            _f->pointSize() != of->pointSize() ||
03863            _f->font().weight() != of->font().weight() ||
03864            _f->font().italic() != of->font().italic() ||
03865            _f->vAlign() != of->vAlign() ||
03866                _f->relativeTextSize() != of->relativeTextSize() ||
03867                _f->offsetFromBaseLine() != of->offsetFromBaseLine() ||
03868                _f->wordByWord() != of->wordByWord()  ||
03869                _f->attributeFont() != of->attributeFont() ||
03870                _f->language() != of->language() ||
03871                _f->hyphenation() != of->hyphenation() ||
03872                _f->shadowDistanceX() != of->shadowDistanceX() ||
03873                _f->shadowDistanceY() != of->shadowDistanceY()
03874                  ) ) {
03875         invalidate( 0 );
03876     }
03877     if ( flags == -1 || flags == KoTextFormat::Format || !fc ) {
03878 #ifdef DEBUG_COLLECTION
03879         kdDebug(32500) << " KoTextParag::setFormat, will use format(f) " << f << " " << _f->key() << endl;
03880 #endif
03881             KoTextFormat* f = fc ? fc->format( _f ) : const_cast<KoTextFormat *>( _f );
03882         str->setFormat( i + index, f, useCollection );
03883     } else {
03884 #ifdef DEBUG_COLLECTION
03885         kdDebug(32500) << " KoTextParag::setFormat, will use format(of,f,flags) of=" << of << " " << of->key() << ", f=" << _f << " " << _f->key() << endl;
03886 #endif
03887         KoTextFormat *fm = fc->format( of, _f, flags );
03888 #ifdef DEBUG_COLLECTION
03889         kdDebug(32500) << " KoTextParag::setFormat, format(of,f,flags) returned " << fm << " " << fm->key() << " " << endl;
03890 #endif
03891         str->setFormat( i + index, fm, useCollection );
03892     }
03893     }
03894 }
03895 
03896 void KoTextParag::indent( int *oldIndent, int *newIndent )
03897 {
03898     if ( !doc || !doc->indent() /*|| isListItem() TODO*/ ) {
03899     if ( oldIndent )
03900         *oldIndent = 0;
03901     if ( newIndent )
03902         *newIndent = 0;
03903     if ( oldIndent && newIndent )
03904         *newIndent = *oldIndent;
03905     return;
03906     }
03907     doc->indent()->indent( doc, this, oldIndent, newIndent );
03908 }
03909 
03910 void KoTextParag::drawCursorDefault( QPainter &painter, KoTextCursor *cursor, int curx, int cury, int curh, const QColorGroup &cg )
03911 {
03912     painter.fillRect( QRect( curx, cury, 1, curh ), cg.color( QColorGroup::Text ) );
03913     painter.save();
03914     if ( string()->isBidi() ) {
03915         const int d = 4;
03916         if ( at( cursor->index() )->rightToLeft ) {
03917             painter.setPen( Qt::black );
03918             painter.drawLine( curx, cury, curx - d / 2, cury + d / 2 );
03919             painter.drawLine( curx, cury + d, curx - d / 2, cury + d / 2 );
03920         } else {
03921             painter.setPen( Qt::black );
03922             painter.drawLine( curx, cury, curx + d / 2, cury + d / 2 );
03923             painter.drawLine( curx, cury + d, curx + d / 2, cury + d / 2 );
03924         }
03925     }
03926     painter.restore();
03927 }
03928 
03929 int *KoTextParag::tabArray() const
03930 {
03931     int *ta = tArray;
03932     if ( !ta && doc )
03933     ta = doc->tabArray();
03934     return ta;
03935 }
03936 
03937 int KoTextParag::nextTabDefault( int, int x )
03938 {
03939     int *ta = tArray;
03940     //if ( doc ) {
03941     if ( !ta )
03942         ta = doc->tabArray();
03943     int tabStopWidth = doc->tabStopWidth();
03944     //}
03945     if ( tabStopWidth != 0 )
03946     return tabStopWidth*(x/tabStopWidth+1);
03947     else
03948         return x;
03949 }
03950 
03951 /*void KoTextParag::setPainter( QPainter *p, bool adjust  )
03952 {
03953     pntr = p;
03954     for ( int i = 0; i < length(); ++i ) {
03955     if ( at( i )->isCustom() )
03956         at( i )->customItem()->setPainter( p, adjust  );
03957     }
03958 }*/
03959 
03960 KoTextFormatCollection *KoTextParag::formatCollection() const
03961 {
03962     if ( doc )
03963     return doc->formatCollection();
03964     //if ( !qFormatCollection )
03965     //    qFormatCollection = new KoTextFormatCollection;
03966     //return qFormatCollection;
03967     return 0L;
03968 }
03969 
03970 QString KoTextParag::richText() const
03971 {
03972     QString s;
03973 #if 0
03974     KoTextStringChar *formatChar = 0;
03975     QString spaces;
03976     for ( int i = 0; i < length()-1; ++i ) {
03977     KoTextStringChar *c = &str->at( i );
03978 #endif
03979 #if 0
03980         if ( c->isAnchor() && !c->anchorName().isEmpty() ) {
03981             if ( c->anchorName().contains( '#' ) ) {
03982                 QStringList l = QStringList::split( '#', c->anchorName() );
03983                 for ( QStringList::ConstIterator it = l.begin(); it != l.end(); ++it )
03984                     s += "<a name=\"" + *it + "\"></a>";
03985             } else {
03986                 s += "<a name=\"" + c->anchorName() + "\"></a>";
03987             }
03988         }
03989 #endif
03990 #if 0
03991         if ( !formatChar ) {
03992             s += c->format()->makeFormatChangeTags( 0, QString::null, QString::null /*c->anchorHref()*/ );
03993             formatChar = c;
03994         } else if ( ( formatChar->format()->key() != c->format()->key() && c->c != ' ' ) /* ||
03995                   (formatChar->isAnchor() != c->isAnchor() &&
03996                   (!c->anchorHref().isEmpty() || !formatChar->anchorHref().isEmpty() ) ) */ )  {// lisp was here
03997             s += c->format()->makeFormatChangeTags( formatChar->format(), QString::null /*formatChar->anchorHref()*/,
03998                                                     QString::null /*c->anchorHref()*/ );
03999             formatChar = c;
04000         }
04001 
04002         if ( c->c == ' ' || c->c == '\t' ) {
04003             spaces += c->c;
04004             continue;
04005         } else if ( !spaces.isEmpty() ) {
04006             if ( spaces.length() > 1 || spaces[0] == '\t' )
04007                 s += "<wsp>" + spaces + "</wsp>";
04008             else
04009                 s += spaces;
04010             spaces = QString::null;
04011         }
04012 
04013         if ( c->c == '<' ) {
04014             s += "&lt;";
04015         } else if ( c->c == '>' ) {
04016             s += "&gt;";
04017         } else if ( c->isCustom() ) {
04018             s += c->customItem()->richText();
04019         } else {
04020             s += c->c;
04021         }
04022     }
04023     if ( !spaces.isEmpty() ) {
04024         if ( spaces.length() > 1 || spaces[0] == '\t' )
04025                 s += "<wsp>" + spaces + "</wsp>";
04026         else
04027             s += spaces;
04028     }
04029 
04030     if ( formatChar )
04031         s += formatChar->format()->makeFormatEndTags( QString::null /*formatChar->anchorHref()*/ );
04032 #endif
04033     return s;
04034 }
04035 
04036 /*void KoTextParag::addCommand( KoTextDocCommand *cmd )
04037 {
04038     if ( !doc )
04039     commandHistory->addCommand( cmd );
04040     else
04041     doc->commands()->addCommand( cmd );
04042 }
04043 
04044 KoTextCursor *KoTextParag::undo( KoTextCursor *c )
04045 {
04046     if ( !doc )
04047     return commandHistory->undo( c );
04048     return doc->commands()->undo( c );
04049 }
04050 
04051 KoTextCursor *KoTextParag::redo( KoTextCursor *c )
04052 {
04053     if ( !doc )
04054     return commandHistory->redo( c );
04055     return doc->commands()->redo( c );
04056 }*/
04057 
04058 void KoTextParag::show()
04059 {
04060     if ( visible || !doc )
04061     return;
04062     visible = TRUE;
04063 }
04064 
04065 void KoTextParag::hide()
04066 {
04067     if ( !visible || !doc )
04068     return;
04069     visible = FALSE;
04070 }
04071 
04072 void KoTextParag::setDirection( QChar::Direction d )
04073 {
04074     if ( str && str->direction() != d ) {
04075     str->setDirection( d );
04076     invalidate( 0 );
04078         m_layout.direction = d;
04079        invalidateCounters(); // #47178
04081     }
04082 }
04083 
04084 QChar::Direction KoTextParag::direction() const
04085 {
04086     return (str ? str->direction() : QChar::DirON );
04087 }
04088 
04089 void KoTextParag::setSelection( int id, int start, int end )
04090 {
04091     QMap<int, KoTextParagSelection>::ConstIterator it = selections().find( id );
04092     if ( it != mSelections->end() ) {
04093     if ( start == ( *it ).start && end == ( *it ).end )
04094         return;
04095     }
04096 
04097     KoTextParagSelection sel;
04098     sel.start = start;
04099     sel.end = end;
04100     (*mSelections)[ id ] = sel;
04101     setChanged( TRUE, TRUE );
04102 }
04103 
04104 void KoTextParag::removeSelection( int id )
04105 {
04106     if ( !hasSelection( id ) )
04107     return;
04108     if ( mSelections )
04109     mSelections->remove( id );
04110     setChanged( TRUE, TRUE );
04111 }
04112 
04113 int KoTextParag::selectionStart( int id ) const
04114 {
04115     if ( !mSelections )
04116     return -1;
04117     QMap<int, KoTextParagSelection>::ConstIterator it = mSelections->find( id );
04118     if ( it == mSelections->end() )
04119     return -1;
04120     return ( *it ).start;
04121 }
04122 
04123 int KoTextParag::selectionEnd( int id ) const
04124 {
04125     if ( !mSelections )
04126     return -1;
04127     QMap<int, KoTextParagSelection>::ConstIterator it = mSelections->find( id );
04128     if ( it == mSelections->end() )
04129     return -1;
04130     return ( *it ).end;
04131 }
04132 
04133 bool KoTextParag::hasSelection( int id ) const
04134 {
04135     if ( !mSelections )
04136     return FALSE;
04137     QMap<int, KoTextParagSelection>::ConstIterator it = mSelections->find( id );
04138     if ( it == mSelections->end() )
04139     return FALSE;
04140     return ( *it ).start != ( *it ).end || length() == 1;
04141 }
04142 
04143 bool KoTextParag::fullSelected( int id ) const
04144 {
04145     if ( !mSelections )
04146     return FALSE;
04147     QMap<int, KoTextParagSelection>::ConstIterator it = mSelections->find( id );
04148     if ( it == mSelections->end() )
04149     return FALSE;
04150     return ( *it ).start == 0 && ( *it ).end == str->length() - 1;
04151 }
04152 
04153 int KoTextParag::lineY( int l ) const
04154 {
04155     if ( l > (int)lineStarts.count() - 1 ) {
04156     kdWarning(32500) << "KoTextParag::lineY: line " << l << " out of range!" << endl;
04157     return 0;
04158     }
04159 
04160     if ( !isValid() )
04161     ( (KoTextParag*)this )->format();
04162 
04163     QMap<int, KoTextParagLineStart*>::ConstIterator it = lineStarts.begin();
04164     while ( l-- > 0 )
04165     ++it;
04166     return ( *it )->y;
04167 }
04168 
04169 int KoTextParag::lineBaseLine( int l ) const
04170 {
04171     if ( l > (int)lineStarts.count() - 1 ) {
04172     kdWarning(32500) << "KoTextParag::lineBaseLine: line " << l << " out of range!" << endl;
04173     return 10;
04174     }
04175 
04176     if ( !isValid() )
04177     ( (KoTextParag*)this )->format();
04178 
04179     QMap<int, KoTextParagLineStart*>::ConstIterator it = lineStarts.begin();
04180     while ( l-- > 0 )
04181     ++it;
04182     return ( *it )->baseLine;
04183 }
04184 
04185 int KoTextParag::lineHeight( int l ) const
04186 {
04187     if ( l > (int)lineStarts.count() - 1 ) {
04188     kdWarning(32500) << "KoTextParag::lineHeight: line " << l << " out of range!" << endl;
04189     return 15;
04190     }
04191 
04192     if ( !isValid() )
04193     ( (KoTextParag*)this )->format();
04194 
04195     QMap<int, KoTextParagLineStart*>::ConstIterator it = lineStarts.begin();
04196     while ( l-- > 0 )
04197     ++it;
04198     return ( *it )->h;
04199 }
04200 
04201 void KoTextParag::lineInfo( int l, int &y, int &h, int &bl ) const
04202 {
04203     if ( l > (int)lineStarts.count() - 1 ) {
04204     kdWarning(32500) << "KoTextParag::lineInfo: line " << l << " out of range!" << endl;
04205     kdDebug(32500) << (int)lineStarts.count() - 1 << " " << l << endl;
04206     y = 0;
04207     h = 15;
04208     bl = 10;
04209     return;
04210     }
04211 
04212     if ( !isValid() )
04213     ( (KoTextParag*)this )->format();
04214 
04215     QMap<int, KoTextParagLineStart*>::ConstIterator it = lineStarts.begin();
04216     while ( l-- > 0 )
04217     ++it;
04218     y = ( *it )->y;
04219     h = ( *it )->h;
04220     bl = ( *it )->baseLine;
04221 }
04222 
04223 uint KoTextParag::alignment() const
04224 {
04225     return align;
04226 }
04227 
04228 void KoTextParag::setFormat( KoTextFormat *fm )
04229 {
04230 #if 0
04231     bool doUpdate = FALSE;
04232     if (defFormat && (defFormat != formatCollection()->defaultFormat()))
04233        doUpdate = TRUE;
04234 #endif
04235     defFormat = formatCollection()->format( fm );
04236 #if 0
04237     if ( !doUpdate )
04238     return;
04239     for ( int i = 0; i < length(); ++i ) {
04240     if ( at( i )->format()->styleName() == defFormat->styleName() )
04241         at( i )->format()->updateStyle();
04242     }
04243 #endif
04244 }
04245 
04246 KoTextFormatterBase *KoTextParag::formatter() const
04247 {
04248     if ( doc )
04249     return doc->formatter();
04250     //if ( pFormatter )
04251     //    return pFormatter;
04252     //return ( ( (KoTextParag*)this )->pFormatter = new KoTextFormatterBaseBreakWords );
04253     return 0L;
04254 }
04255 
04256 /*void KoTextParag::setFormatter( KoTextFormatterBase *f )
04257 {
04258     if ( doc ) return;
04259     if ( pFormatter ) delete pFormatter;
04260     pFormatter = f;
04261 }*/
04262 
04263 /*int KoTextParag::minimumWidth() const
04264 {
04265     //return doc ? doc->minimumWidth() : 0;
04266     return m_minw;
04267 }*/
04268 
04269 int KoTextParag::widthUsed() const
04270 {
04271     return m_wused;
04272 }
04273 
04274 void KoTextParag::setTabArray( int *a )
04275 {
04276     delete [] tArray;
04277     tArray = a;
04278 }
04279 
04280 void KoTextParag::setTabStops( int tw )
04281 {
04282     if ( doc )
04283     doc->setTabStops( tw );
04284     //else
04285     //    tabStopWidth = tw;
04286 }
04287 
04288 QMap<int, KoTextParagSelection> &KoTextParag::selections() const
04289 {
04290     if ( !mSelections )
04291     ((KoTextParag *)this)->mSelections = new QMap<int, KoTextParagSelection>;
04292     return *mSelections;
04293 }
04294 
04295 QPtrList<KoTextCustomItem> &KoTextParag::floatingItems() const
04296 {
04297     if ( !mFloatingItems )
04298     ((KoTextParag *)this)->mFloatingItems = new QPtrList<KoTextCustomItem>;
04299     return *mFloatingItems;
04300 }
04301 
04302 void KoTextCursor::setIndex( int i, bool restore )
04303 {
04304     if ( restore )
04305     restoreState();
04306 // Note: QRT doesn't allow to position the cursor at string->length
04307 // However we need it, when applying a style to a paragraph, so that
04308 // the trailing space gets the style change applied as well.
04309 // Obviously "right of the trailing space" isn't a good place for a real
04310 // cursor, but this needs to be checked somewhere else.
04311     if ( i < 0 || i > string->length() ) {
04312 #if defined(QT_CHECK_RANGE)
04313     kdWarning(32500) << "KoTextCursor::setIndex: " << i << " out of range" << endl;
04314         //abort();
04315 #endif
04316     i = i < 0 ? 0 : string->length() - 1;
04317     }
04318 
04319     tmpIndex = -1;
04320     idx = i;
04321 }
04322 
04323 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
04324 
04325 KoTextFormatterBase::KoTextFormatterBase()
04326     : wrapColumn( -1 ), wrapEnabled( TRUE ),
04327       m_bViewFormattingChars( false ),
04328       biw( true /*default in kotext*/ )
04329 {
04330 }
04331 
04332 // See KoTextFormatter
04333 #if 0
04334 KoTextParagLineStart *KoTextFormatterBase::formatLine( KoTextParag *parag, KoTextString *string, KoTextParagLineStart *line,
04335                            KoTextStringChar *startChar, KoTextStringChar *lastChar, int align, int space )
04336 {
04337 #ifndef QT_NO_COMPLEXTEXT
04338     if( string->isBidi() )
04339     return bidiReorderLine( parag, string, line, startChar, lastChar, align, space );
04340 #endif
04341     space = QMAX( space, 0 ); // #### with nested tables this gets negative because of a bug I didn't find yet, so workaround for now. This also means non-left aligned nested tables do not work at the moment
04342     int start = (startChar - &string->at(0));
04343     int last = (lastChar - &string->at(0) );
04344     // do alignment Auto == Left in this case
04345     if ( align & Qt::AlignHCenter || align & Qt::AlignRight ) {
04346     if ( align & Qt::AlignHCenter )
04347         space /= 2;
04348     for ( int j = start; j <= last; ++j )
04349         string->at( j ).x += space;
04350     } else if ( align & AlignJustify ) {
04351     int numSpaces = 0;
04352     for ( int j = start; j < last; ++j ) {
04353         if( isBreakable( string, j ) ) {
04354         numSpaces++;
04355         }
04356     }
04357     int toAdd = 0;
04358     for ( int k = start + 1; k <= last; ++k ) {
04359         if( isBreakable( string, k ) && numSpaces ) {
04360         int s = space / numSpaces;
04361         toAdd += s;
04362         space -= s;
04363         numSpaces--;
04364         }
04365         string->at( k ).x += toAdd;
04366     }
04367     }
04368 
04369     if ( last >= 0 && last < string->length() )
04370     line->w = string->at( last ).x + string->width( last );
04371     else
04372     line->w = 0;
04373 
04374     return new KoTextParagLineStart();
04375 }
04376 #endif
04377 
04378 #ifdef BIDI_DEBUG
04379 #include <iostream>
04380 #endif
04381 
04382 // collects one line of the paragraph and transforms it to visual order
04383 KoTextParagLineStart *KoTextFormatterBase::bidiReorderLine( KoTextParag * /*parag*/, KoTextString *text, KoTextParagLineStart *line,
04384                             KoTextStringChar *startChar, KoTextStringChar *lastChar, int align, int space )
04385 {
04386     int start = (startChar - &text->at(0));
04387     int last = (lastChar - &text->at(0) );
04388     //kdDebug(32500) << "doing BiDi reordering from " << start << " to " << last << "!" << endl;
04389 
04390     KoBidiControl *control = new KoBidiControl( line->context(), line->status );
04391     QString str;
04392     str.setUnicode( 0, last - start + 1 );
04393     // fill string with logically ordered chars.
04394     KoTextStringChar *ch = startChar;
04395     QChar *qch = (QChar *)str.unicode();
04396     while ( ch <= lastChar ) {
04397     *qch = ch->c;
04398     qch++;
04399     ch++;
04400     }
04401     int x = startChar->x;
04402 
04403     QPtrList<KoTextRun> *runs;
04404     runs = KoComplexText::bidiReorderLine(control, str, 0, last - start + 1,
04405                      (text->isRightToLeft() ? QChar::DirR : QChar::DirL) );
04406 
04407     // now construct the reordered string out of the runs...
04408 
04409     int numSpaces = 0;
04410     // set the correct alignment. This is a bit messy....
04411     if( align == Qt::AlignAuto ) {
04412     // align according to directionality of the paragraph...
04413     if ( text->isRightToLeft() )
04414         align = Qt::AlignRight;
04415     }
04416 
04417     if ( align & Qt::AlignHCenter )
04418     x += space/2;
04419     else if ( align & Qt::AlignRight )
04420     x += space;
04421     else if ( align & Qt::AlignJustify ) {
04422     for ( int j = start; j < last; ++j ) {
04423         if( isBreakable( text, j ) ) {
04424         numSpaces++;
04425         }
04426     }
04427     }
04428     int toAdd = 0;
04429     bool first = TRUE;
04430     KoTextRun *r = runs->first();
04431     int xmax = -0xffffff;
04432     while ( r ) {
04433     if(r->level %2) {
04434         // odd level, need to reverse the string
04435         int pos = r->stop + start;
04436         while(pos >= r->start + start) {
04437         KoTextStringChar *c = &text->at(pos);
04438         if( numSpaces && !first && isBreakable( text, pos ) ) {
04439             int s = space / numSpaces;
04440             toAdd += s;
04441             space -= s;
04442             numSpaces--;
04443         } else if ( first ) {
04444             first = FALSE;
04445             if ( c->c == ' ' )
04446             x -= c->format()->width( ' ' );
04447         }
04448         c->x = x + toAdd;
04449         c->rightToLeft = TRUE;
04450         c->startOfRun = FALSE;
04451         int ww = 0;
04452         if ( c->c.unicode() >= 32 || c->c == '\t' || c->c == '\n' || c->isCustom() ) {
04453             ww = c->width;
04454         } else {
04455             ww = c->format()->width( ' ' );
04456         }
04457         if ( xmax < x + toAdd + ww ) xmax = x + toAdd + ww;
04458         x += ww;
04459         pos--;
04460         }
04461     } else {
04462         int pos = r->start + start;
04463         while(pos <= r->stop + start) {
04464         KoTextStringChar* c = &text->at(pos);
04465         if( numSpaces && !first && isBreakable( text, pos ) ) {
04466             int s = space / numSpaces;
04467             toAdd += s;
04468             space -= s;
04469             numSpaces--;
04470         } else if ( first ) {
04471             first = FALSE;
04472             if ( c->c == ' ' )
04473             x -= c->format()->width( ' ' );
04474         }
04475         c->x = x + toAdd;
04476         c->rightToLeft = FALSE;
04477         c->startOfRun = FALSE;
04478         int ww = 0;
04479         if ( c->c.unicode() >= 32 || c->c == '\t' || c->isCustom() ) {
04480             ww = c->width;
04481         } else {
04482             ww = c->format()->width( ' ' );
04483         }
04484         //kdDebug(32500) << "setting char " << pos << " at pos " << x << endl;
04485         if ( xmax < x + toAdd + ww ) xmax = x + toAdd + ww;
04486         x += ww;
04487         pos++;
04488         }
04489     }
04490     text->at( r->start + start ).startOfRun = TRUE;
04491     r = runs->next();
04492     }
04493 
04494     line->w = xmax + 10;
04495     KoTextParagLineStart *ls = new KoTextParagLineStart( control->context, control->status );
04496     delete control;
04497     delete runs;
04498     return ls;
04499 }
04500 
04501 bool KoTextFormatterBase::isStretchable( KoTextString *string, int pos ) const
04502 {
04503     if ( string->at( pos ).c == QChar(160) ) //non-breaking space
04504     return true;
04505 #ifndef INDIC
04506     return isBreakable( string, pos );
04507 #else
04508     KoTextStringChar& chr = string->at( pos );
04509     return chr.whiteSpace;
04510     //return isBreakable( string, pos );
04511 #endif
04512 }
04513 
04514 bool KoTextFormatterBase::isBreakable( KoTextString *string, int pos ) const
04515 {
04516 #ifdef INDIC
04517     //if (string->at(pos).nobreak)
04518     //    return FALSE;
04519     return (pos < string->length()-1 && string->at(pos+1).softBreak);
04520 #endif
04521 
04522 #ifndef INDIC
04523     const QChar &c = string->at( pos ).c;
04524     char ch = c.latin1();
04525     if ( c.isSpace() && ch != '\n' && c.unicode() != 0x00a0U )
04526     return TRUE;
04527     if ( c == '-' || c.unicode() == 0xad ) // hyphen or soft hyphen
04528     return TRUE;
04529     if ( !ch ) {
04530     // not latin1, need to do more sophisticated checks for other scripts
04531     uchar row = c.row();
04532     if ( row == 0x0e ) {
04533         // 0e00 - 0e7f == Thai
04534         if ( c.cell() < 0x80 ) {
04535 #ifdef HAVE_THAI_BREAKS
04536         // check for thai
04537         if( string != cachedString ) {
04538             // build up string of thai chars
04539             QTextCodec *thaiCodec = QTextCodec::codecForMib(2259);
04540             if ( !thaiCache )
04541             thaiCache = new QCString;
04542             if ( !thaiIt )
04543             thaiIt = ThBreakIterator::createWordInstance();
04544             *thaiCache = thaiCodec->fromUnicode( s->string() );
04545         }
04546         thaiIt->setText(thaiCache->data());
04547         for(int i = thaiIt->first(); i != thaiIt->DONE; i = thaiIt->next() ) {
04548             if( i == pos )
04549             return TRUE;
04550             if( i > pos )
04551             return FALSE;
04552         }
04553         return FALSE;
04554 #else
04555         // if we don't have a thai line breaking lib, allow
04556         // breaks everywhere except directly before punctuation.
04557         return TRUE;
04558 #endif
04559         } else
04560         return FALSE;
04561     }
04562     if ( row < 0x11 ) // no asian font
04563         return FALSE;
04564     if ( row > 0x2d && row < 0xfb || row == 0x11 )
04565         // asian line breaking. Everywhere allowed except directly
04566         // in front of a punctuation character.
04567         return TRUE;
04568     }
04569     return FALSE;
04570 #endif
04571 }
04572 
04573 void KoTextParag::insertLineStart( int index, KoTextParagLineStart *ls )
04574 {
04575     // This tests if we break at the same character in more than one line,
04576     // i.e. there no space even for _one_ char in a given line.
04577     // However this shouldn't happen, KoTextFormatter prevents it, otherwise
04578     // we could loop forever (e.g. if one char is wider than the page...)
04579 #ifndef NDEBUG
04580     QMap<int, KoTextParagLineStart*>::Iterator it;
04581     if ( ( it = lineStarts.find( index ) ) == lineStarts.end() ) {
04582     lineStarts.insert( index, ls );
04583     } else {
04584         kdWarning(32500) << "insertLineStart: there's already a line for char index=" << index << endl;
04585     delete *it;
04586     lineStarts.remove( it );
04587     lineStarts.insert( index, ls );
04588     }
04589 #else // non-debug code, take the fast route
04590     lineStarts.insert( index, ls );
04591 #endif
04592 }
04593 
04594 
04595 /* Standard pagebreak algorithm using KoTextFlow::adjustFlow. Returns
04596  the shift of the paragraphs bottom line.
04597  */
04598 int KoTextFormatterBase::formatVertically( KoTextDocument* doc, KoTextParag* parag )
04599 {
04600     int oldHeight = parag->rect().height();
04601     QMap<int, KoTextParagLineStart*>& lineStarts = parag->lineStartList();
04602     QMap<int, KoTextParagLineStart*>::Iterator it = lineStarts.begin();
04603     int h = doc->addMargins() ? parag->topMargin() : 0;
04604     for ( ; it != lineStarts.end() ; ++it  ) {
04605     KoTextParagLineStart * ls = it.data();
04606     ls->y = h;
04607     KoTextStringChar *c = &parag->string()->at(it.key());
04608     if ( c && c->customItem() && c->customItem()->ownLine() ) {
04609         int h = c->customItem()->height;
04610         c->customItem()->pageBreak( parag->rect().y() + ls->y + ls->baseLine - h, doc->flow() );
04611         int delta = c->customItem()->height - h;
04612         ls->h += delta;
04613         if ( delta )
04614         parag->setMovedDown( TRUE );
04615     } else {
04616         int shift = doc->flow()->adjustFlow( parag->rect().y() + ls->y, ls->w, ls->h );
04617         ls->y += shift;
04618         if ( shift )
04619         parag->setMovedDown( TRUE );
04620     }
04621     h = ls->y + ls->h;
04622     }
04623     int m = parag->bottomMargin();
04624     if ( parag->next() && doc && !doc->addMargins() )
04625     m = QMAX( m, parag->next()->topMargin() );
04626     //if ( parag->next() && parag->next()->isLineBreak() )
04627     //  m = 0;
04628     h += m;
04629     parag->setHeight( h );
04630     return h - oldHeight;
04631 }
04632 
04633 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
04634 
04635 KoTextIndent::KoTextIndent()
04636 {
04637 }
04638 
04639 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
04640 
04641 KoTextCustomItem::KoTextCustomItem( KoTextDocument *p )
04642       :  width(-1), height(0), parent(p), xpos(0), ypos(-1), parag(0)
04643 {
04644     m_deleted = false; // added for kotext
04645 }
04646 
04647 KoTextCustomItem::~KoTextCustomItem()
04648 {
04649 }
04650 
04651 KoTextFlow::KoTextFlow()
04652 {
04653     w = 0;
04654     leftItems.setAutoDelete( FALSE );
04655     rightItems.setAutoDelete( FALSE );
04656 }
04657 
04658 KoTextFlow::~KoTextFlow()
04659 {
04660 }
04661 
04662 void KoTextFlow::clear()
04663 {
04664     leftItems.clear();
04665     rightItems.clear();
04666 }
04667 
04668 // Called by KoTextDocument::setWidth
04669 void KoTextFlow::setWidth( int width )
04670 {
04671     w = width;
04672 }
04673 
04674 void KoTextFlow::adjustMargins( int, int, int, int&, int&, int& pageWidth, KoTextParag* )
04675 {
04676     pageWidth = w;
04677 }
04678 
04679 #if 0
04680 int KoTextFlow::adjustLMargin( int yp, int, int margin, int space, KoTextParag* )
04681 {
04682     for ( KoTextCustomItem* item = leftItems.first(); item; item = leftItems.next() ) {
04683     if ( item->y() == -1 )
04684         continue;
04685     if ( yp >= item->y() && yp < item->y() + item->height )
04686         margin = QMAX( margin, item->x() + item->width + space );
04687     }
04688     return margin;
04689 }
04690 
04691 int KoTextFlow::adjustRMargin( int yp, int, int margin, int space, KoTextParag* )
04692 {
04693     for ( KoTextCustomItem* item = rightItems.first(); item; item = rightItems.next() ) {
04694     if ( item->y() == -1 )
04695         continue;
04696     if ( yp >= item->y() && yp < item->y() + item->height )
04697         margin = QMAX( margin, w - item->x() - space );
04698     }
04699     return margin;
04700 }
04701 #endif
04702 
04703 int KoTextFlow::adjustFlow( int /*y*/, int, int /*h*/ )
04704 {
04705 #if 0
04706     if ( pagesize > 0 ) { // check pages
04707     int yinpage = y % pagesize;
04708     if ( yinpage <= 2 )
04709         return 2 - yinpage;
04710     else
04711         if ( yinpage + h > pagesize - 2 )
04712         return ( pagesize - yinpage ) + 2;
04713     }
04714 #endif
04715     return 0;
04716 }
04717 
04718 void KoTextFlow::unregisterFloatingItem( KoTextCustomItem* item )
04719 {
04720     leftItems.removeRef( item );
04721     rightItems.removeRef( item );
04722 }
04723 
04724 void KoTextFlow::registerFloatingItem( KoTextCustomItem* item )
04725 {
04726     if ( item->placement() == KoTextCustomItem::PlaceRight ) {
04727     if ( !rightItems.contains( item ) )
04728         rightItems.append( item );
04729     } else if ( item->placement() == KoTextCustomItem::PlaceLeft &&
04730         !leftItems.contains( item ) ) {
04731     leftItems.append( item );
04732     }
04733 }
04734 
04735 #if 0
04736 QRect KoTextFlow::boundingRect() const
04737 {
04738     QRect br;
04739     QPtrListIterator<KoTextCustomItem> l( leftItems );
04740     while( l.current() ) {
04741     br = br.unite( l.current()->geometry() );
04742     ++l;
04743     }
04744     QPtrListIterator<KoTextCustomItem> r( rightItems );
04745     while( r.current() ) {
04746     br = br.unite( r.current()->geometry() );
04747     ++r;
04748     }
04749     return br;
04750 }
04751 #endif
04752 
04753 int KoTextFlow::availableHeight() const
04754 {
04755     return -1; // no limit
04756 }
04757 
04758 void KoTextFlow::drawFloatingItems( QPainter* p, int cx, int cy, int cw, int ch, const QColorGroup& cg, bool selected )
04759 {
04760     KoTextCustomItem *item;
04761     for ( item = leftItems.first(); item; item = leftItems.next() ) {
04762     if ( item->x() == -1 || item->y() == -1 )
04763         continue;
04764     item->draw( p, item->x(), item->y(), cx, cy, cw, ch, cg, selected );
04765     }
04766 
04767     for ( item = rightItems.first(); item; item = rightItems.next() ) {
04768     if ( item->x() == -1 || item->y() == -1 )
04769         continue;
04770     item->draw( p, item->x(), item->y(), cx, cy, cw, ch, cg, selected );
04771     }
04772 }
04773 
04774 //void KoTextFlow::setPageSize( int ps ) { pagesize = ps; }
04775 bool KoTextFlow::isEmpty() { return leftItems.isEmpty() && rightItems.isEmpty(); }
04776 
KDE Logo
This file is part of the documentation for lib Library Version 1.3.5.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Nov 17 13:19:25 2004 by doxygen 1.3.5 written by Dimitri van Heesch, © 1997-2003