Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

qrichtext.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002 ** $Id: qrichtext.cpp,v 1.4 2004/04/04 13:54:45 mickeyl Exp $
00003 **
00004 ** Implementation of the internal Qt classes dealing with rich text
00005 **
00006 ** Created : 990101
00007 **
00008 ** Copyright (C) 1992-2000 Trolltech AS.  All rights reserved.
00009 **
00010 ** This file is part of the kernel module of the Qt GUI Toolkit.
00011 **
00012 ** This file may be distributed under the terms of the Q Public License
00013 ** as defined by Trolltech AS of Norway and appearing in the file
00014 ** LICENSE.QPL included in the packaging of this file.
00015 **
00016 ** This file may be distributed and/or modified under the terms of the
00017 ** GNU General Public License version 2 as published by the Free Software
00018 ** Foundation and appearing in the file LICENSE.GPL included in the
00019 ** packaging of this file.
00020 **
00021 ** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
00022 ** licenses may use this file in accordance with the Qt Commercial License
00023 ** Agreement provided with the Software.
00024 **
00025 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00026 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00027 **
00028 ** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
00029 **   information about Qt Commercial License Agreements.
00030 ** See http://www.trolltech.com/qpl/ for QPL licensing information.
00031 ** See http://www.trolltech.com/gpl/ for GPL licensing information.
00032 **
00033 ** Contact info@trolltech.com if any conditions of this licensing are
00034 ** not clear to you.
00035 **
00036 **********************************************************************/
00037 
00038 #include "qrichtext_p.h"
00039 
00040 /* OPIE */
00041 #include <opie2/odebug.h>
00042 using namespace Opie::Core;
00043 
00044 /* QT */
00045 #include "qdragobject.h"
00046 #include "qpaintdevicemetrics.h"
00047 #include "qdrawutil.h"
00048 #include "qcleanuphandler.h"
00049 
00050 /* STD */
00051 #include <stdlib.h>
00052 
00053 using namespace Qt3;
00054 
00055 static QTextCursor* richTextExportStart = 0;
00056 static QTextCursor* richTextExportEnd = 0;
00057 
00058 static QTextFormatCollection *qFormatCollection = 0;
00059 
00060 const int border_tolerance = 2;
00061 
00062 #ifdef Q_WS_WIN
00063 #include "qt_windows.h"
00064 #endif
00065 
00066 #define QChar_linesep QChar(0x2028U)
00067 
00068 static inline bool is_printer( QPainter *p )
00069 {
00070     if ( !p || !p->device() )
00071         return FALSE;
00072     return p->device()->devType() == QInternal::Printer;
00073 }
00074 
00075 static inline int scale( int value, QPainter *painter )
00076 {
00077     if ( is_printer( painter ) ) {
00078         QPaintDeviceMetrics metrics( painter->device() );
00079 #if defined(Q_WS_X11)
00080         value = value * metrics.logicalDpiY() / QPaintDevice::x11AppDpiY();
00081 #elif defined (Q_WS_WIN)
00082         HDC hdc = GetDC( 0 );
00083         int gdc = GetDeviceCaps( hdc, LOGPIXELSY );
00084         if ( gdc )
00085             value = value * metrics.logicalDpiY() / gdc;
00086         ReleaseDC( 0, hdc );
00087 #elif defined (Q_WS_MAC)
00088         value = value * metrics.logicalDpiY() / 75; // ##### FIXME
00089 #elif defined (Q_WS_QWS)
00090         value = value * metrics.logicalDpiY() / 75;
00091 #endif
00092     }
00093     return value;
00094 }
00095 
00096 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
00097 
00098 void QTextCommandHistory::addCommand( QTextCommand *cmd )
00099 {
00100     if ( current < (int)history.count() - 1 ) {
00101         QPtrList<QTextCommand> commands;
00102         commands.setAutoDelete( FALSE );
00103 
00104         for( int i = 0; i <= current; ++i ) {
00105             commands.insert( i, history.at( 0 ) );
00106             history.take( 0 );
00107         }
00108 
00109         commands.append( cmd );
00110         history.clear();
00111         history = commands;
00112         history.setAutoDelete( TRUE );
00113     } else {
00114         history.append( cmd );
00115     }
00116 
00117     if ( (int)history.count() > steps )
00118         history.removeFirst();
00119     else
00120         ++current;
00121 }
00122 
00123 QTextCursor *QTextCommandHistory::undo( QTextCursor *c )
00124 {
00125     if ( current > -1 ) {
00126         QTextCursor *c2 = history.at( current )->unexecute( c );
00127         --current;
00128         return c2;
00129     }
00130     return 0;
00131 }
00132 
00133 QTextCursor *QTextCommandHistory::redo( QTextCursor *c )
00134 {
00135     if ( current > -1 ) {
00136         if ( current < (int)history.count() - 1 ) {
00137             ++current;
00138             return history.at( current )->execute( c );
00139         }
00140     } else {
00141         if ( history.count() > 0 ) {
00142             ++current;
00143             return history.at( current )->execute( c );
00144         }
00145     }
00146     return 0;
00147 }
00148 
00149 bool QTextCommandHistory::isUndoAvailable()
00150 {
00151     return current > -1;
00152 }
00153 
00154 bool QTextCommandHistory::isRedoAvailable()
00155 {
00156    return current > -1 && current < (int)history.count() - 1 || current == -1 && history.count() > 0;
00157 }
00158 
00159 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
00160 
00161 QTextDeleteCommand::QTextDeleteCommand( QTextDocument *d, int i, int idx, const QMemArray<QTextStringChar> &str,
00162                                         const QByteArray& oldStyleInfo )
00163     : QTextCommand( d ), id( i ), index( idx ), parag( 0 ), text( str ), styleInformation( oldStyleInfo )
00164 {
00165     for ( int j = 0; j < (int)text.size(); ++j ) {
00166         if ( text[ j ].format() )
00167             text[ j ].format()->addRef();
00168     }
00169 }
00170 
00171 QTextDeleteCommand::QTextDeleteCommand( QTextParagraph *p, int idx, const QMemArray<QTextStringChar> &str )
00172     : QTextCommand( 0 ), id( -1 ), index( idx ), parag( p ), text( str )
00173 {
00174     for ( int i = 0; i < (int)text.size(); ++i ) {
00175         if ( text[ i ].format() )
00176             text[ i ].format()->addRef();
00177     }
00178 }
00179 
00180 QTextDeleteCommand::~QTextDeleteCommand()
00181 {
00182     for ( int i = 0; i < (int)text.size(); ++i ) {
00183         if ( text[ i ].format() )
00184             text[ i ].format()->removeRef();
00185     }
00186     text.resize( 0 );
00187 }
00188 
00189 QTextCursor *QTextDeleteCommand::execute( QTextCursor *c )
00190 {
00191     QTextParagraph *s = doc ? doc->paragAt( id ) : parag;
00192     if ( !s ) {
00193         owarn << "can't locate parag at " << id << ", last parag: " << doc->lastParagraph()->paragId() << "" << oendl; 
00194         return 0;
00195     }
00196 
00197     cursor.setParagraph( s );
00198     cursor.setIndex( index );
00199     int len = text.size();
00200     if ( c )
00201         *c = cursor;
00202     if ( doc ) {
00203         doc->setSelectionStart( QTextDocument::Temp, cursor );
00204         for ( int i = 0; i < len; ++i )
00205             cursor.gotoNextLetter();
00206         doc->setSelectionEnd( QTextDocument::Temp, cursor );
00207         doc->removeSelectedText( QTextDocument::Temp, &cursor );
00208         if ( c )
00209             *c = cursor;
00210     } else {
00211         s->remove( index, len );
00212     }
00213 
00214     return c;
00215 }
00216 
00217 QTextCursor *QTextDeleteCommand::unexecute( QTextCursor *c )
00218 {
00219     QTextParagraph *s = doc ? doc->paragAt( id ) : parag;
00220     if ( !s ) {
00221         owarn << "can't locate parag at " << id << ", last parag: " << doc->lastParagraph()->paragId() << "" << oendl; 
00222         return 0;
00223     }
00224 
00225     cursor.setParagraph( s );
00226     cursor.setIndex( index );
00227     QString str = QTextString::toString( text );
00228     cursor.insert( str, TRUE, &text );
00229     cursor.setParagraph( s );
00230     cursor.setIndex( index );
00231     if ( c ) {
00232         c->setParagraph( s );
00233         c->setIndex( index );
00234         for ( int i = 0; i < (int)text.size(); ++i )
00235             c->gotoNextLetter();
00236     }
00237 
00238     if ( !styleInformation.isEmpty() ) {
00239         QDataStream styleStream( styleInformation, IO_ReadOnly );
00240         int num;
00241         styleStream >> num;
00242         QTextParagraph *p = s;
00243         while ( num-- && p ) {
00244             p->readStyleInformation( styleStream );
00245             p = p->next();
00246         }
00247     }
00248     s = cursor.paragraph();
00249     while ( s ) {
00250         s->format();
00251         s->setChanged( TRUE );
00252         if ( s == c->paragraph() )
00253             break;
00254         s = s->next();
00255     }
00256 
00257     return &cursor;
00258 }
00259 
00260 QTextFormatCommand::QTextFormatCommand( QTextDocument *d, int sid, int sidx, int eid, int eidx,
00261                                         const QMemArray<QTextStringChar> &old, QTextFormat *f, int fl )
00262     : QTextCommand( d ), startId( sid ), startIndex( sidx ), endId( eid ), endIndex( eidx ), format( f ), oldFormats( old ), flags( fl )
00263 {
00264     format = d->formatCollection()->format( f );
00265     for ( int j = 0; j < (int)oldFormats.size(); ++j ) {
00266         if ( oldFormats[ j ].format() )
00267             oldFormats[ j ].format()->addRef();
00268     }
00269 }
00270 
00271 QTextFormatCommand::~QTextFormatCommand()
00272 {
00273     format->removeRef();
00274     for ( int j = 0; j < (int)oldFormats.size(); ++j ) {
00275         if ( oldFormats[ j ].format() )
00276             oldFormats[ j ].format()->removeRef();
00277     }
00278 }
00279 
00280 QTextCursor *QTextFormatCommand::execute( QTextCursor *c )
00281 {
00282     QTextParagraph *sp = doc->paragAt( startId );
00283     QTextParagraph *ep = doc->paragAt( endId );
00284     if ( !sp || !ep )
00285         return c;
00286 
00287     QTextCursor start( doc );
00288     start.setParagraph( sp );
00289     start.setIndex( startIndex );
00290     QTextCursor end( doc );
00291     end.setParagraph( ep );
00292     end.setIndex( endIndex );
00293 
00294     doc->setSelectionStart( QTextDocument::Temp, start );
00295     doc->setSelectionEnd( QTextDocument::Temp, end );
00296     doc->setFormat( QTextDocument::Temp, format, flags );
00297     doc->removeSelection( QTextDocument::Temp );
00298     if ( endIndex == ep->length() )
00299         end.gotoLeft();
00300     *c = end;
00301     return c;
00302 }
00303 
00304 QTextCursor *QTextFormatCommand::unexecute( QTextCursor *c )
00305 {
00306     QTextParagraph *sp = doc->paragAt( startId );
00307     QTextParagraph *ep = doc->paragAt( endId );
00308     if ( !sp || !ep )
00309         return 0;
00310 
00311     int idx = startIndex;
00312     int fIndex = 0;
00313     for ( ;; ) {
00314         if ( oldFormats.at( fIndex ).c == '\n' ) {
00315             if ( idx > 0 ) {
00316                 if ( idx < sp->length() && fIndex > 0 )
00317                     sp->setFormat( idx, 1, oldFormats.at( fIndex - 1 ).format() );
00318                 if ( sp == ep )
00319                     break;
00320                 sp = sp->next();
00321                 idx = 0;
00322             }
00323             fIndex++;
00324         }
00325         if ( oldFormats.at( fIndex ).format() )
00326             sp->setFormat( idx, 1, oldFormats.at( fIndex ).format() );
00327         idx++;
00328         fIndex++;
00329         if ( fIndex >= (int)oldFormats.size() )
00330             break;
00331         if ( idx >= sp->length() ) {
00332             if ( sp == ep )
00333                 break;
00334             sp = sp->next();
00335             idx = 0;
00336         }
00337     }
00338 
00339     QTextCursor end( doc );
00340     end.setParagraph( ep );
00341     end.setIndex( endIndex );
00342     if ( endIndex == ep->length() )
00343         end.gotoLeft();
00344     *c = end;
00345     return c;
00346 }
00347 
00348 QTextStyleCommand::QTextStyleCommand( QTextDocument *d, int fParag, int lParag, const QByteArray& beforeChange )
00349     : QTextCommand( d ), firstParag( fParag ), lastParag( lParag ), before( beforeChange )
00350 {
00351     after = readStyleInformation(  d, fParag, lParag );
00352 }
00353 
00354 
00355 QByteArray QTextStyleCommand::readStyleInformation(  QTextDocument* doc, int fParag, int lParag )
00356 {
00357     QByteArray style;
00358     QTextParagraph *p = doc->paragAt( fParag );
00359     if ( !p )
00360         return style;
00361     QDataStream styleStream( style, IO_WriteOnly );
00362     int num = lParag - fParag + 1;
00363     styleStream << num;
00364     while ( num -- && p ) {
00365         p->writeStyleInformation( styleStream );
00366         p = p->next();
00367     }
00368     return style;
00369 }
00370 
00371 void QTextStyleCommand::writeStyleInformation(  QTextDocument* doc, int fParag, const QByteArray& style )
00372 {
00373     QTextParagraph *p = doc->paragAt( fParag );
00374     if ( !p )
00375         return;
00376     QDataStream styleStream( style, IO_ReadOnly );
00377     int num;
00378     styleStream >> num;
00379     while ( num-- && p ) {
00380         p->readStyleInformation( styleStream );
00381         p = p->next();
00382     }
00383 }
00384 
00385 QTextCursor *QTextStyleCommand::execute( QTextCursor *c )
00386 {
00387     writeStyleInformation( doc, firstParag, after );
00388     return c;
00389 }
00390 
00391 QTextCursor *QTextStyleCommand::unexecute( QTextCursor *c )
00392 {
00393     writeStyleInformation( doc, firstParag, before );
00394     return c;
00395 }
00396 
00397 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
00398 
00399 QTextCursor::QTextCursor( QTextDocument *d )
00400     : idx( 0 ), tmpIndex( -1 ), ox( 0 ), oy( 0 ),
00401       valid( TRUE )
00402 {
00403     para = d ? d->firstParagraph() : 0;
00404 }
00405 
00406 QTextCursor::QTextCursor( const QTextCursor &c )
00407 {
00408     ox = c.ox;
00409     oy = c.oy;
00410     idx = c.idx;
00411     para = c.para;
00412     tmpIndex = c.tmpIndex;
00413     indices = c.indices;
00414     paras = c.paras;
00415     xOffsets = c.xOffsets;
00416     yOffsets = c.yOffsets;
00417     valid = c.valid;
00418 }
00419 
00420 QTextCursor &QTextCursor::operator=( const QTextCursor &c )
00421 {
00422     ox = c.ox;
00423     oy = c.oy;
00424     idx = c.idx;
00425     para = c.para;
00426     tmpIndex = c.tmpIndex;
00427     indices = c.indices;
00428     paras = c.paras;
00429     xOffsets = c.xOffsets;
00430     yOffsets = c.yOffsets;
00431     valid = c.valid;
00432 
00433     return *this;
00434 }
00435 
00436 bool QTextCursor::operator==( const QTextCursor &c ) const
00437 {
00438     return para == c.para && idx == c.idx;
00439 }
00440 
00441 int QTextCursor::totalOffsetX() const
00442 {
00443     int xoff = ox;
00444     for ( QValueStack<int>::ConstIterator xit = xOffsets.begin(); xit != xOffsets.end(); ++xit )
00445         xoff += *xit;
00446     return xoff;
00447 }
00448 
00449 int QTextCursor::totalOffsetY() const
00450 {
00451     int yoff = oy;
00452     for ( QValueStack<int>::ConstIterator yit = yOffsets.begin(); yit != yOffsets.end(); ++yit )
00453         yoff += *yit;
00454     return yoff;
00455 }
00456 
00457 void QTextCursor::gotoIntoNested( const QPoint &globalPos )
00458 {
00459     if ( !para )
00460         return;
00461     push();
00462     ox = 0;
00463     int bl, y;
00464     para->lineHeightOfChar( idx, &bl, &y );
00465     oy = y + para->rect().y();
00466     QPoint p( globalPos.x() - offsetX(), globalPos.y() - offsetY() );
00467     Q_ASSERT( para->at( idx )->isCustom() );
00468     ox = para->at( idx )->x;
00469 
00470     QTextDocument* doc = document();
00471     para->at( idx )->customItem()->enterAt( this, doc, para, idx, ox, oy, p );
00472 }
00473 
00474 void QTextCursor::invalidateNested()
00475 {
00476     QValueStack<QTextParagraph*>::Iterator it = paras.begin();
00477     QValueStack<int>::Iterator it2 = indices.begin();
00478     for ( ; it != paras.end(); ++it, ++it2 ) {
00479         if ( *it == para )
00480             continue;
00481         (*it)->invalidate( 0 );
00482         if ( (*it)->at( *it2 )->isCustom() )
00483             (*it)->at( *it2 )->customItem()->invalidate();
00484     }
00485 }
00486 
00487 void QTextCursor::insert( const QString &str, bool checkNewLine, QMemArray<QTextStringChar> *formatting )
00488 {
00489     tmpIndex = -1;
00490     bool justInsert = TRUE;
00491     QString s( str );
00492 #if defined(Q_WS_WIN)
00493     if ( checkNewLine ) {
00494         int i = 0;
00495         while ( ( i = s.find( '\r', i ) ) != -1 )
00496             s.remove( i ,1 );
00497     }
00498 #endif
00499     if ( checkNewLine )
00500         justInsert = s.find( '\n' ) == -1;
00501     if ( justInsert ) { // we ignore new lines and insert all in the current para at the current index
00502         para->insert( idx, s.unicode(), s.length() );
00503         if ( formatting ) {
00504             for ( int i = 0; i < (int)s.length(); ++i ) {
00505                 if ( formatting->at( i ).format() ) {
00506                     formatting->at( i ).format()->addRef();
00507                     para->string()->setFormat( idx + i, formatting->at( i ).format(), TRUE );
00508                 }
00509             }
00510         }
00511         idx += s.length();
00512     } else { // we split at new lines
00513         int start = -1;
00514         int end;
00515         int y = para->rect().y() + para->rect().height();
00516         int lastIndex = 0;
00517         do {
00518             end = s.find( '\n', start + 1 ); // find line break
00519             if ( end == -1 ) // didn't find one, so end of line is end of string
00520                 end = s.length();
00521             int len = (start == -1 ? end : end - start - 1);
00522             if ( len > 0 ) // insert the line
00523                 para->insert( idx, s.unicode() + start + 1, len );
00524             else
00525                 para->invalidate( 0 );
00526             if ( formatting ) { // set formats to the chars of the line
00527                 for ( int i = 0; i < len; ++i ) {
00528                     if ( formatting->at( i + lastIndex ).format() ) {
00529                         formatting->at( i + lastIndex ).format()->addRef();
00530                         para->string()->setFormat( i + idx, formatting->at( i + lastIndex ).format(), TRUE );
00531                     }
00532                 }
00533                 lastIndex += len;
00534             }
00535             start = end; // next start is at the end of this line
00536             idx += len; // increase the index of the cursor to the end of the inserted text
00537             if ( s[end] == '\n' ) { // if at the end was a line break, break the line
00538                 splitAndInsertEmptyParagraph( FALSE, TRUE );
00539                 para->setEndState( -1 );
00540                 para->prev()->format( -1, FALSE );
00541                 lastIndex++;
00542             }
00543 
00544         } while ( end < (int)s.length() );
00545 
00546         para->format( -1, FALSE );
00547         int dy = para->rect().y() + para->rect().height() - y;
00548         QTextParagraph *p = para;
00549         p->setParagId( p->prev() ? p->prev()->paragId() + 1 : 0 );
00550         p = p->next();
00551         while ( p ) {
00552             p->setParagId( p->prev()->paragId() + 1 );
00553             p->move( dy );
00554             p->invalidate( 0 );
00555             p->setEndState( -1 );
00556             p = p->next();
00557         }
00558     }
00559 
00560     int h = para->rect().height();
00561     para->format( -1, TRUE );
00562     if ( h != para->rect().height() )
00563         invalidateNested();
00564     else if ( para->document() && para->document()->parent() )
00565         para->document()->nextDoubleBuffered = TRUE;
00566 }
00567 
00568 void QTextCursor::gotoLeft()
00569 {
00570     if ( para->string()->isRightToLeft() )
00571         gotoNextLetter();
00572     else
00573         gotoPreviousLetter();
00574 }
00575 
00576 void QTextCursor::gotoPreviousLetter()
00577 {
00578     tmpIndex = -1;
00579 
00580     if ( idx > 0 ) {
00581         idx--;
00582         const QTextStringChar *tsc = para->at( idx );
00583         if ( tsc && tsc->isCustom() && tsc->customItem()->isNested() )
00584             processNesting( EnterEnd );
00585     } else if ( para->prev() ) {
00586         para = para->prev();
00587         while ( !para->isVisible() && para->prev() )
00588             para = para->prev();
00589         idx = para->length() - 1;
00590     } else if ( nestedDepth() ) {
00591         pop();
00592         processNesting( Prev );
00593         if ( idx == -1 ) {
00594             pop();
00595             if ( idx > 0 ) {
00596                 idx--;
00597             } else if ( para->prev() ) {
00598                 para = para->prev();
00599                 idx = para->length() - 1;
00600             }
00601         }
00602     }
00603 }
00604 
00605 void QTextCursor::push()
00606 {
00607     indices.push( idx );
00608     paras.push( para );
00609     xOffsets.push( ox );
00610     yOffsets.push( oy );
00611 }
00612 
00613 void QTextCursor::pop()
00614 {
00615     if ( indices.isEmpty() )
00616         return;
00617     idx = indices.pop();
00618     para = paras.pop();
00619     ox = xOffsets.pop();
00620     oy = yOffsets.pop();
00621 }
00622 
00623 void QTextCursor::restoreState()
00624 {
00625     while ( !indices.isEmpty() )
00626         pop();
00627 }
00628 
00629 bool QTextCursor::place( const QPoint &p, QTextParagraph *s, bool link )
00630 {
00631     QPoint pos( p );
00632     QRect r;
00633     QTextParagraph *str = s;
00634     if ( pos.y() < s->rect().y() )
00635         pos.setY( s->rect().y() );
00636     while ( s ) {
00637         r = s->rect();
00638         r.setWidth( document() ? document()->width() : QWIDGETSIZE_MAX );
00639         if ( s->isVisible() )
00640             str = s;
00641         if ( pos.y() >= r.y() && pos.y() <= r.y() + r.height() || !s->next() )
00642             break;
00643         s = s->next();
00644     }
00645 
00646     if ( !s || !str )
00647         return FALSE;
00648 
00649     s = str;
00650 
00651     setParagraph( s );
00652     int y = s->rect().y();
00653     int lines = s->lines();
00654     QTextStringChar *chr = 0;
00655     int index = 0;
00656     int i = 0;
00657     int cy = 0;
00658     int ch = 0;
00659     for ( ; i < lines; ++i ) {
00660         chr = s->lineStartOfLine( i, &index );
00661         cy = s->lineY( i );
00662         ch = s->lineHeight( i );
00663         if ( !chr )
00664             return FALSE;
00665         if ( pos.y() <= y + cy + ch )
00666             break;
00667     }
00668     int nextLine;
00669     if ( i < lines - 1 )
00670         s->lineStartOfLine( i+1, &nextLine );
00671     else
00672         nextLine = s->length();
00673     i = index;
00674     int x = s->rect().x();
00675     if ( pos.x() < x )
00676         pos.setX( x + 1 );
00677     int cw;
00678     int curpos = s->length()-1;
00679     int dist = 10000000;
00680     bool inCustom = FALSE;
00681     while ( i < nextLine ) {
00682         chr = s->at(i);
00683         int cpos = x + chr->x;
00684         cw = s->string()->width( i );
00685         if ( chr->isCustom() && chr->customItem()->isNested() ) {
00686             if ( pos.x() >= cpos && pos.x() <= cpos + cw &&
00687                  pos.y() >= y + cy && pos.y() <= y + cy + chr->height() ) {
00688                 inCustom = TRUE;
00689                 curpos = i;
00690                 break;
00691             }
00692         } else {
00693             if( chr->rightToLeft )
00694                 cpos += cw;
00695             int d = cpos - pos.x();
00696             bool dm = d < 0 ? !chr->rightToLeft : chr->rightToLeft;
00697             if ( QABS( d ) < dist || (dist == d && dm == TRUE ) ) {
00698                 dist = QABS( d );
00699                 if ( !link || pos.x() >= x + chr->x )
00700                     curpos = i;
00701             }
00702         }
00703         i++;
00704     }
00705     setIndex( curpos );
00706 
00707     if ( inCustom && para->document() && para->at( curpos )->isCustom() && para->at( curpos )->customItem()->isNested() ) {
00708         QTextDocument *oldDoc = para->document();
00709         gotoIntoNested( pos );
00710         if ( oldDoc == para->document() )
00711             return TRUE;
00712         QPoint p( pos.x() - offsetX(), pos.y() - offsetY() );
00713         if ( !place( p, document()->firstParagraph(), link ) )
00714             pop();
00715     }
00716     return TRUE;
00717 }
00718 
00719 void QTextCursor::processNesting( Operation op )
00720 {
00721     if ( !para->document() )
00722         return;
00723     QTextDocument* doc = para->document();
00724     push();
00725     ox = para->at( idx )->x;
00726     int bl, y;
00727     para->lineHeightOfChar( idx, &bl, &y );
00728     oy = y + para->rect().y();
00729     bool ok = FALSE;
00730 
00731     switch ( op ) {
00732     case EnterBegin:
00733         ok = para->at( idx )->customItem()->enter( this, doc, para, idx, ox, oy );
00734         break;
00735     case EnterEnd:
00736         ok = para->at( idx )->customItem()->enter( this, doc, para, idx, ox, oy, TRUE );
00737         break;
00738     case Next:
00739         ok = para->at( idx )->customItem()->next( this, doc, para, idx, ox, oy );
00740         break;
00741     case Prev:
00742         ok = para->at( idx )->customItem()->prev( this, doc, para, idx, ox, oy );
00743         break;
00744     case Down:
00745         ok = para->at( idx )->customItem()->down( this, doc, para, idx, ox, oy );
00746         break;
00747     case Up:
00748         ok = para->at( idx )->customItem()->up( this, doc, para, idx, ox, oy );
00749         break;
00750     }
00751     if ( !ok )
00752         pop();
00753 }
00754 
00755 void QTextCursor::gotoRight()
00756 {
00757     if ( para->string()->isRightToLeft() )
00758         gotoPreviousLetter();
00759     else
00760         gotoNextLetter();
00761 }
00762 
00763 void QTextCursor::gotoNextLetter()
00764 {
00765     tmpIndex = -1;
00766 
00767     const QTextStringChar *tsc = para->at( idx );
00768     if ( tsc && tsc->isCustom() && tsc->customItem()->isNested() ) {
00769         processNesting( EnterBegin );
00770         return;
00771     }
00772 
00773     if ( idx < para->length() - 1 ) {
00774         idx++;
00775     } else if ( para->next() ) {
00776         para = para->next();
00777         while ( !para->isVisible() && para->next() )
00778             para = para->next();
00779         idx = 0;
00780     } else if ( nestedDepth() ) {
00781         pop();
00782         processNesting( Next );
00783         if ( idx == -1 ) {
00784             pop();
00785             if ( idx < para->length() - 1 ) {
00786                 idx++;
00787             } else if ( para->next() ) {
00788                 para = para->next();
00789                 idx = 0;
00790             }
00791         }
00792     }
00793 }
00794 
00795 void QTextCursor::gotoUp()
00796 {
00797     int indexOfLineStart;
00798     int line;
00799     QTextStringChar *c = para->lineStartOfChar( idx, &indexOfLineStart, &line );
00800     if ( !c )
00801         return;
00802 
00803     tmpIndex = QMAX( tmpIndex, idx - indexOfLineStart );
00804     if ( indexOfLineStart == 0 ) {
00805         if ( !para->prev() ) {
00806             if ( !nestedDepth() )
00807                 return;
00808             pop();
00809             processNesting( Up );
00810             if ( idx == -1 ) {
00811                 pop();
00812                 if ( !para->prev() )
00813                     return;
00814                 idx = tmpIndex = 0;
00815             } else {
00816                 tmpIndex = -1;
00817                 return;
00818             }
00819         }
00820         QTextParagraph *p = para->prev();
00821         while ( p && !p->isVisible() )
00822             p = p->prev();
00823         if ( p )
00824             para = p;
00825         int lastLine = para->lines() - 1;
00826         if ( !para->lineStartOfLine( lastLine, &indexOfLineStart ) )
00827             return;
00828         if ( indexOfLineStart + tmpIndex < para->length() )
00829             idx = indexOfLineStart + tmpIndex;
00830         else
00831             idx = para->length() - 1;
00832     } else {
00833         --line;
00834         int oldIndexOfLineStart = indexOfLineStart;
00835         if ( !para->lineStartOfLine( line, &indexOfLineStart ) )
00836             return;
00837         if ( indexOfLineStart + tmpIndex < oldIndexOfLineStart )
00838             idx = indexOfLineStart + tmpIndex;
00839         else
00840             idx = oldIndexOfLineStart - 1;
00841     }
00842 }
00843 
00844 void QTextCursor::gotoDown()
00845 {
00846     int indexOfLineStart;
00847     int line;
00848     QTextStringChar *c = para->lineStartOfChar( idx, &indexOfLineStart, &line );
00849     if ( !c )
00850         return;
00851 
00852     tmpIndex = QMAX( tmpIndex, idx - indexOfLineStart );
00853     if ( line == para->lines() - 1 ) {
00854         if ( !para->next() ) {
00855             if ( !nestedDepth() )
00856                 return;
00857             pop();
00858             processNesting( Down );
00859             if ( idx == -1 ) {
00860                 pop();
00861                 if ( !para->next() )
00862                     return;
00863                 idx = tmpIndex = 0;
00864             } else {
00865                 tmpIndex = -1;
00866                 return;
00867             }
00868         }
00869         QTextParagraph *s = para->next();
00870         while ( s && !s->isVisible() )
00871             s = s->next();
00872         if ( s )
00873             para = s;
00874         if ( !para->lineStartOfLine( 0, &indexOfLineStart ) )
00875             return;
00876         int end;
00877         if ( para->lines() == 1 )
00878             end = para->length();
00879         else
00880             para->lineStartOfLine( 1, &end );
00881         if ( indexOfLineStart + tmpIndex < end )
00882             idx = indexOfLineStart + tmpIndex;
00883         else
00884             idx = end - 1;
00885     } else {
00886         ++line;
00887         int end;
00888         if ( line == para->lines() - 1 )
00889             end = para->length();
00890         else
00891             para->lineStartOfLine( line + 1, &end );
00892         if ( !para->lineStartOfLine( line, &indexOfLineStart ) )
00893             return;
00894         if ( indexOfLineStart + tmpIndex < end )
00895             idx = indexOfLineStart + tmpIndex;
00896         else
00897             idx = end - 1;
00898     }
00899 }
00900 
00901 void QTextCursor::gotoLineEnd()
00902 {
00903     tmpIndex = -1;
00904     int indexOfLineStart;
00905     int line;
00906     QTextStringChar *c = para->lineStartOfChar( idx, &indexOfLineStart, &line );
00907     if ( !c )
00908         return;
00909 
00910     if ( line == para->lines() - 1 ) {
00911         idx = para->length() - 1;
00912     } else {
00913         c = para->lineStartOfLine( ++line, &indexOfLineStart );
00914         indexOfLineStart--;
00915         idx = indexOfLineStart;
00916     }
00917 }
00918 
00919 void QTextCursor::gotoLineStart()
00920 {
00921     tmpIndex = -1;
00922     int indexOfLineStart;
00923     int line;
00924     QTextStringChar *c = para->lineStartOfChar( idx, &indexOfLineStart, &line );
00925     if ( !c )
00926         return;
00927 
00928     idx = indexOfLineStart;
00929 }
00930 
00931 void QTextCursor::gotoHome()
00932 {
00933     if ( topParagraph()->document() )
00934         gotoPosition( topParagraph()->document()->firstParagraph() );
00935     else
00936         gotoLineStart();
00937 }
00938 
00939 void QTextCursor::gotoEnd()
00940 {
00941     if ( topParagraph()->document() && topParagraph()->document()->lastParagraph()->isValid() )
00942         gotoPosition( topParagraph()->document()->lastParagraph(),
00943                       topParagraph()->document()->lastParagraph()->length() - 1);
00944     else
00945         gotoLineEnd();
00946 }
00947 
00948 void QTextCursor::gotoPageUp( int visibleHeight )
00949 {
00950     int targetY  = globalY() - visibleHeight;
00951     QTextParagraph* old; int index;
00952     do {
00953         old = para; index = idx;
00954         gotoUp();
00955     } while ( (old != para || index != idx)  && globalY() > targetY );
00956 }
00957 
00958 void QTextCursor::gotoPageDown( int visibleHeight )
00959 {
00960     int targetY  = globalY() + visibleHeight;
00961     QTextParagraph* old; int index;
00962     do {
00963         old = para; index = idx;
00964         gotoDown();
00965     } while ( (old != para || index != idx) && globalY() < targetY );
00966 }
00967 
00968 void QTextCursor::gotoWordRight()
00969 {
00970     if ( para->string()->isRightToLeft() )
00971         gotoPreviousWord();
00972     else
00973         gotoNextWord();
00974 }
00975 
00976 void QTextCursor::gotoWordLeft()
00977 {
00978     if ( para->string()->isRightToLeft() )
00979         gotoNextWord();
00980     else
00981         gotoPreviousWord();
00982 }
00983 
00984 void QTextCursor::gotoPreviousWord()
00985 {
00986     gotoPreviousLetter();
00987     tmpIndex = -1;
00988     QTextString *s = para->string();
00989     bool allowSame = FALSE;
00990     if ( idx == ((int)s->length()-1) )
00991         return;
00992     for ( int i = idx; i >= 0; --i ) {
00993         if ( s->at( i ).c.isSpace() || s->at( i ).c == '\t' || s->at( i ).c == '.' ||
00994              s->at( i ).c == ',' || s->at( i ).c == ':' || s->at( i ).c == ';' ) {
00995             if ( !allowSame )
00996                 continue;
00997             idx = i + 1;
00998             return;
00999         }
01000         if ( !allowSame && !( s->at( i ).c.isSpace() || s->at( i ).c == '\t' || s->at( i ).c == '.' ||
01001                               s->at( i ).c == ',' || s->at( i ).c == ':' || s->at( i ).c == ';'  ) )
01002             allowSame = TRUE;
01003     }
01004     idx = 0;
01005 }
01006 
01007 void QTextCursor::gotoNextWord()
01008 {
01009     tmpIndex = -1;
01010     QTextString *s = para->string();
01011     bool allowSame = FALSE;
01012     for ( int i = idx; i < (int)s->length(); ++i ) {
01013         if ( ! (s->at( i ).c.isSpace() || s->at( i ).c == '\t' || s->at( i ).c == '.' ||
01014              s->at( i ).c == ',' || s->at( i ).c == ':' || s->at( i ).c == ';') ) {
01015             if ( !allowSame )
01016                 continue;
01017             idx = i;
01018             return;
01019         }
01020         if ( !allowSame && ( s->at( i ).c.isSpace() || s->at( i ).c == '\t' || s->at( i ).c == '.' ||
01021             s->at( i ).c == ',' || s->at( i ).c == ':' || s->at( i ).c == ';'  ) )
01022             allowSame = TRUE;
01023 
01024     }
01025 
01026     if ( idx < ((int)s->length()-1) ) {
01027         gotoLineEnd();
01028     } else if ( para->next() ) {
01029         QTextParagraph *p = para->next();
01030         while ( p  && !p->isVisible() )
01031             p = p->next();
01032         if ( s ) {
01033             para = p;
01034             idx = 0;
01035         }
01036     } else {
01037         gotoLineEnd();
01038     }
01039 }
01040 
01041 bool QTextCursor::atParagStart()
01042 {
01043     return idx == 0;
01044 }
01045 
01046 bool QTextCursor::atParagEnd()
01047 {
01048     return idx == para->length() - 1;
01049 }
01050 
01051 void QTextCursor::splitAndInsertEmptyParagraph( bool ind, bool updateIds )
01052 {
01053     if ( !para->document() )
01054         return;
01055     tmpIndex = -1;
01056     QTextFormat *f = 0;
01057     if ( para->document()->useFormatCollection() ) {
01058         f = para->at( idx )->format();
01059         if ( idx == para->length() - 1 && idx > 0 )
01060             f = para->at( idx - 1 )->format();
01061         if ( f->isMisspelled() ) {
01062             f->removeRef();
01063             f = para->document()->formatCollection()->format( f->font(), f->color() );
01064         }
01065     }
01066 
01067     if ( atParagEnd() ) {
01068         QTextParagraph *n = para->next();
01069         QTextParagraph *s = para->document()->createParagraph( para->document(), para, n, updateIds );
01070         if ( f )
01071             s->setFormat( 0, 1, f, TRUE );
01072         s->copyParagData( para );
01073         if ( ind ) {
01074             int oi, ni;
01075             s->indent( &oi, &ni );
01076             para = s;
01077             idx = ni;
01078         } else {
01079             para = s;
01080             idx = 0;
01081         }
01082     } else if ( atParagStart() ) {
01083         QTextParagraph *p = para->prev();
01084         QTextParagraph *s = para->document()->createParagraph( para->document(), p, para, updateIds );
01085         if ( f )
01086             s->setFormat( 0, 1, f, TRUE );
01087         s->copyParagData( para );
01088         if ( ind ) {
01089             s->indent();
01090             s->format();
01091             indent();
01092             para->format();
01093         }
01094     } else {
01095         QString str = para->string()->toString().mid( idx, 0xFFFFFF );
01096         QTextParagraph *n = para->next();
01097         QTextParagraph *s = para->document()->createParagraph( para->document(), para, n, updateIds );
01098         s->copyParagData( para );
01099         s->remove( 0, 1 );
01100         s->append( str, TRUE );
01101         for ( uint i = 0; i < str.length(); ++i ) {
01102             QTextStringChar* tsc = para->at( idx + i );
01103             s->setFormat( i, 1, tsc->format(), TRUE );
01104             if ( tsc->isCustom() ) {
01105                 QTextCustomItem * item = tsc->customItem();
01106                 s->at( i )->setCustomItem( item );
01107                 tsc->loseCustomItem();
01108             }
01109             if ( tsc->isAnchor() )
01110                 s->at( i )->setAnchor( tsc->anchorName(),
01111                                        tsc->anchorHref() );
01112         }
01113         para->truncate( idx );
01114         if ( ind ) {
01115             int oi, ni;
01116             s->indent( &oi, &ni );
01117             para = s;
01118             idx = ni;
01119         } else {
01120             para = s;
01121             idx = 0;
01122         }
01123     }
01124 
01125     invalidateNested();
01126 }
01127 
01128 bool QTextCursor::remove()
01129 {
01130     tmpIndex = -1;
01131     if ( !atParagEnd() ) {
01132         para->remove( idx, 1 );
01133         int h = para->rect().height();
01134         para->format( -1, TRUE );
01135         if ( h != para->rect().height() )
01136             invalidateNested();
01137         else if ( para->document() && para->document()->parent() )
01138             para->document()->nextDoubleBuffered = TRUE;
01139         return FALSE;
01140     } else if ( para->next() ) {
01141         para->join( para->next() );
01142         invalidateNested();
01143         return TRUE;
01144     }
01145     return FALSE;
01146 }
01147 
01148 void QTextCursor::indent()
01149 {
01150     int oi = 0, ni = 0;
01151     para->indent( &oi, &ni );
01152     if ( oi == ni )
01153         return;
01154 
01155     if ( idx >= oi )
01156         idx += ni - oi;
01157     else
01158         idx = ni;
01159 }
01160 
01161 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
01162 
01163 QTextDocument::QTextDocument( QTextDocument *p )
01164     : par( p ), parentPar( 0 ), tc( 0 ), tArray( 0 ), tStopWidth( 0 )
01165 {
01166     fCollection = new QTextFormatCollection;
01167     init();
01168 }
01169 
01170 QTextDocument::QTextDocument( QTextDocument *p, QTextFormatCollection *f )
01171     : par( p ), parentPar( 0 ), tc( 0 ), tArray( 0 ), tStopWidth( 0 )
01172 {
01173     fCollection = f;
01174     init();
01175 }
01176 
01177 void QTextDocument::init()
01178 {
01179     oTextValid = TRUE;
01180     mightHaveCustomItems = FALSE;
01181     if ( par )
01182         par->insertChild( this );
01183     pProcessor = 0;
01184     useFC = TRUE;
01185     pFormatter = 0;
01186     indenter = 0;
01187     fParag = 0;
01188     txtFormat = Qt::AutoText;
01189     preferRichText = FALSE;
01190     pages = FALSE;
01191     focusIndicator.parag = 0;
01192     minw = 0;
01193     wused = 0;
01194     minwParag = curParag = 0;
01195     align = AlignAuto;
01196     nSelections = 1;
01197 
01198     setStyleSheet( QStyleSheet::defaultSheet() );
01199     factory_ = QMimeSourceFactory::defaultFactory();
01200     contxt = QString::null;
01201 
01202     underlLinks = par ? par->underlLinks : TRUE;
01203     backBrush = 0;
01204     buf_pixmap = 0;
01205     nextDoubleBuffered = FALSE;
01206 
01207     if ( par )
01208         withoutDoubleBuffer = par->withoutDoubleBuffer;
01209     else
01210         withoutDoubleBuffer = FALSE;
01211 
01212     lParag = fParag = createParagraph( this, 0, 0 );
01213 
01214     cx = 0;
01215     cy = 2;
01216     if ( par )
01217         cx = cy = 0;
01218     cw = 600;
01219     vw = 0;
01220     flow_ = new QTextFlow;
01221     flow_->setWidth( cw );
01222 
01223     leftmargin = rightmargin = 4;
01224     scaleFontsFactor = 1;
01225 
01226 
01227     selectionColors[ Standard ] = QApplication::palette().color( QPalette::Active, QColorGroup::Highlight );
01228     selectionText[ Standard ] = TRUE;
01229     commandHistory = new QTextCommandHistory( 100 );
01230     tStopWidth = formatCollection()->defaultFormat()->width( 'x' ) * 8;
01231 }
01232 
01233 QTextDocument::~QTextDocument()
01234 {
01235     if ( par )
01236         par->removeChild( this );
01237     clear();
01238     delete commandHistory;
01239     delete flow_;
01240     if ( !par )
01241         delete pFormatter;
01242     delete fCollection;
01243     delete pProcessor;
01244     delete buf_pixmap;
01245     delete indenter;
01246     delete backBrush;
01247     if ( tArray )
01248         delete [] tArray;
01249 }
01250 
01251 void QTextDocument::clear( bool createEmptyParag )
01252 {
01253     if ( flow_ )
01254         flow_->clear();
01255     while ( fParag ) {
01256         QTextParagraph *p = fParag->next();
01257         delete fParag;
01258         fParag = p;
01259     }
01260     fParag = lParag = 0;
01261     if ( createEmptyParag )
01262         fParag = lParag = createParagraph( this );
01263     selections.clear();
01264     oText = QString::null;
01265     oTextValid = TRUE;
01266 }
01267 
01268 int QTextDocument::widthUsed() const
01269 {
01270     return wused + border_tolerance;
01271 }
01272 
01273 int QTextDocument::height() const
01274 {
01275     int h = 0;
01276     if ( lParag )
01277         h = lParag->rect().top() + lParag->rect().height() + 1;
01278     int fh = flow_->boundingRect().bottom();
01279     return QMAX( h, fh );
01280 }
01281 
01282 
01283 
01284 QTextParagraph *QTextDocument::createParagraph( QTextDocument *d, QTextParagraph *pr, QTextParagraph *nx, bool updateIds )
01285 {
01286     return new QTextParagraph( d, pr, nx, updateIds );
01287 }
01288 
01289 bool QTextDocument::setMinimumWidth( int needed, int used, QTextParagraph *p )
01290 {
01291     if ( needed == -1 ) {
01292         minw = 0;
01293         wused = 0;
01294         p = 0;
01295     }
01296     if ( p == minwParag ) {
01297         minw = needed;
01298         emit minimumWidthChanged( minw );
01299     } else if ( needed > minw ) {
01300         minw = needed;
01301         minwParag = p;
01302         emit minimumWidthChanged( minw );
01303     }
01304     wused = QMAX( wused, used );
01305     wused = QMAX( wused, minw );
01306     cw = QMAX( minw, cw );
01307     return TRUE;
01308 }
01309 
01310 void QTextDocument::setPlainText( const QString &text )
01311 {
01312     clear();
01313     preferRichText = FALSE;
01314     oTextValid = TRUE;
01315     oText = text;
01316 
01317     int lastNl = 0;
01318     int nl = text.find( '\n' );
01319     if ( nl == -1 ) {
01320         lParag = createParagraph( this, lParag, 0 );
01321         if ( !fParag )
01322             fParag = lParag;
01323         QString s = text;
01324         if ( !s.isEmpty() ) {
01325             if ( s[ (int)s.length() - 1 ] == '\r' )
01326                 s.remove( s.length() - 1, 1 );
01327             lParag->append( s );
01328         }
01329     } else {
01330         for (;;) {
01331             lParag = createParagraph( this, lParag, 0 );
01332             if ( !fParag )
01333                 fParag = lParag;
01334             QString s = text.mid( lastNl, nl - lastNl );
01335             if ( !s.isEmpty() ) {
01336                 if ( s[ (int)s.length() - 1 ] == '\r' )
01337                     s.remove( s.length() - 1, 1 );
01338                 lParag->append( s );
01339             }
01340             if ( nl == 0xffffff )
01341                 break;
01342             lastNl = nl + 1;
01343             nl = text.find( '\n', nl + 1 );
01344             if ( nl == -1 )
01345                 nl = 0xffffff;
01346         }
01347     }
01348     if ( !lParag )
01349         lParag = fParag = createParagraph( this, 0, 0 );
01350 }
01351 
01352 struct Q_EXPORT QTextDocumentTag {
01353     QTextDocumentTag(){}
01354     QTextDocumentTag( const QString&n, const QStyleSheetItem* s, const QTextFormat& f )
01355         :name(n),style(s), format(f), alignment(Qt3::AlignAuto), direction(QChar::DirON),liststyle(QStyleSheetItem::ListDisc) {
01356             wsm = QStyleSheetItem::WhiteSpaceNormal;
01357     }
01358     QString name;
01359     const QStyleSheetItem* style;
01360     QString anchorHref;
01361     QStyleSheetItem::WhiteSpaceMode wsm;
01362     QTextFormat format;
01363     int alignment : 16;
01364     int direction : 5;
01365     QStyleSheetItem::ListStyle liststyle;
01366 
01367     QTextDocumentTag(  const QTextDocumentTag& t ) {
01368         name = t.name;
01369         style = t.style;
01370         anchorHref = t.anchorHref;
01371         wsm = t.wsm;
01372         format = t.format;
01373         alignment = t.alignment;
01374         direction = t.direction;
01375         liststyle = t.liststyle;
01376     }
01377     QTextDocumentTag& operator=(const QTextDocumentTag& t) {
01378         name = t.name;
01379         style = t.style;
01380         anchorHref = t.anchorHref;
01381         wsm = t.wsm;
01382         format = t.format;
01383         alignment = t.alignment;
01384         direction = t.direction;
01385         liststyle = t.liststyle;
01386         return *this;
01387     }
01388 
01389 #if defined(Q_FULL_TEMPLATE_INSTANTIATION)
01390     bool operator==( const QTextDocumentTag& ) const { return FALSE; }
01391 #endif
01392 };
01393 
01394 
01395 #define NEWPAR       do{ if ( !hasNewPar) { \
01396                     if ( !textEditMode && curpar && curpar->length()>1 && curpar->at( curpar->length()-2)->c == QChar_linesep ) \
01397                         curpar->remove( curpar->length()-2, 1 ); \
01398                     curpar = createParagraph( this, curpar, curpar->next() ); styles.append( vec ); vec = 0;} \
01399                     hasNewPar = TRUE; \
01400                     curpar->rtext = TRUE;  \
01401                     curpar->align = curtag.alignment; \
01402                     curpar->lstyle = curtag.liststyle; \
01403                     curpar->litem = ( curtag.style->displayMode() == QStyleSheetItem::DisplayListItem ); \
01404                     curpar->str->setDirection( (QChar::Direction)curtag.direction ); \
01405                     space = TRUE; \
01406                     delete vec; vec = new QPtrVector<QStyleSheetItem>( (uint)tags.count() + 1); \
01407                     int i = 0; \
01408                     for ( QValueStack<QTextDocumentTag>::Iterator it = tags.begin(); it != tags.end(); ++it ) \
01409                         vec->insert( i++, (*it).style ); \
01410                     vec->insert( i, curtag.style ); \
01411                     }while(FALSE)
01412 
01413 
01414 void QTextDocument::setRichText( const QString &text, const QString &context )
01415 {
01416     if ( !context.isEmpty() )
01417         setContext( context );
01418     clear();
01419     fParag = lParag = createParagraph( this );
01420     oTextValid = TRUE;
01421     oText = text;
01422     setRichTextInternal( text );
01423     fParag->rtext = TRUE;
01424 }
01425 
01426 void QTextDocument::setRichTextInternal( const QString &text, QTextCursor* cursor )
01427 {
01428     QTextParagraph* curpar = lParag;
01429     int pos = 0;
01430     QValueStack<QTextDocumentTag> tags;
01431     QTextDocumentTag initag( "", sheet_->item(""), *formatCollection()->defaultFormat() );
01432     QTextDocumentTag curtag = initag;
01433     bool space = TRUE;
01434     bool canMergeLi = FALSE;
01435 
01436     bool textEditMode = FALSE;
01437 
01438     const QChar* doc = text.unicode();
01439     int length = text.length();
01440     bool hasNewPar = curpar->length() <= 1;
01441     QString anchorName;
01442 
01443     // style sheet handling for margin and line spacing calculation below
01444     QTextParagraph* stylesPar = curpar;
01445     QPtrVector<QStyleSheetItem>* vec = 0;
01446     QPtrList< QPtrVector<QStyleSheetItem> > styles;
01447     styles.setAutoDelete( TRUE );
01448 
01449     if ( cursor ) {
01450         cursor->splitAndInsertEmptyParagraph();
01451         QTextCursor tmp = *cursor;
01452         tmp.gotoPreviousLetter();
01453         stylesPar = curpar = tmp.paragraph();
01454         hasNewPar = TRUE;
01455         textEditMode = TRUE;
01456     } else {
01457         NEWPAR;
01458     }
01459 
01460     // set rtext spacing to FALSE for the initial paragraph.
01461     curpar->rtext = FALSE;
01462 
01463     QString wellKnownTags = "br hr wsp table qt body meta title";
01464 
01465     while ( pos < length ) {
01466         if ( hasPrefix(doc, length, pos, '<' ) ){
01467             if ( !hasPrefix( doc, length, pos+1, QChar('/') ) ) {
01468                 // open tag
01469                 QMap<QString, QString> attr;
01470                 bool emptyTag = FALSE;
01471                 QString tagname = parseOpenTag(doc, length, pos, attr, emptyTag);
01472                 if ( tagname.isEmpty() )
01473                     continue; // nothing we could do with this, probably parse error
01474 
01475                 const QStyleSheetItem* nstyle = sheet_->item(tagname);
01476 
01477                 if ( nstyle ) {
01478                     // we might have to close some 'forgotten' tags
01479                     while ( !nstyle->allowedInContext( curtag.style ) ) {
01480                         QString msg;
01481                         msg.sprintf( "QText Warning: Document not valid ( '%s' not allowed in '%s' #%d)",
01482                                      tagname.ascii(), curtag.style->name().ascii(), pos);
01483                         sheet_->error( msg );
01484                         if ( tags.isEmpty() )
01485                             break;
01486                         curtag = tags.pop();
01487                     }
01488 
01489                     /* special handling for p and li for HTML
01490                        compatibility. We do not want to embed blocks in
01491                        p, and we do not want new blocks inside non-empty
01492                        lis. Plus we want to merge empty lis sometimes. */
01493                     if( nstyle->displayMode() == QStyleSheetItem::DisplayListItem ) {
01494                         canMergeLi = TRUE;
01495                     } else if ( nstyle->displayMode() == QStyleSheetItem::DisplayBlock ) {
01496                         while ( curtag.style->name() == "p" ) {
01497                             if ( tags.isEmpty() )
01498                                 break;
01499                             curtag = tags.pop();
01500                         }
01501 
01502                         if ( curtag.style->displayMode() == QStyleSheetItem::DisplayListItem ) {
01503                             // we are in a li and a new block comes along
01504                             if ( nstyle->name() == "ul" || nstyle->name() == "ol" )
01505                                 hasNewPar = FALSE; // we want an empty li (like most browsers)
01506                             if ( !hasNewPar ) {
01507                                 /* do not add new blocks inside
01508                                    non-empty lis */
01509                                 while ( curtag.style->displayMode() == QStyleSheetItem::DisplayListItem ) {
01510                                     if ( tags.isEmpty() )
01511                                         break;
01512                                     curtag = tags.pop();
01513                                 }
01514                             } else if ( canMergeLi ) {
01515                                 /* we have an empty li and a block
01516                                    comes along, merge them */
01517                                 nstyle = curtag.style;
01518                             }
01519                             canMergeLi = FALSE;
01520                         }
01521                     }
01522                 }
01523 
01524                 QTextCustomItem* custom =  0;
01525 
01526                 // some well-known tags, some have a nstyle, some not
01527                 if ( wellKnownTags.find( tagname ) != -1 ) {
01528                     if ( tagname == "br" ) {
01529                         emptyTag = space = TRUE;
01530                         int index = QMAX( curpar->length(),1) - 1;
01531                         QTextFormat format = curtag.format.makeTextFormat( nstyle, attr, scaleFontsFactor );
01532                         curpar->append( QChar_linesep );
01533                         curpar->setFormat( index, 1, &format );
01534                     }  else if ( tagname == "hr" ) {
01535                         emptyTag = space = TRUE;
01536                         custom = sheet_->tag( tagname, attr, contxt, *factory_ , emptyTag, this );
01537                         NEWPAR;
01538                     } else if ( tagname == "table" ) {
01539                         emptyTag = space = TRUE;
01540                         QTextFormat format = curtag.format.makeTextFormat(  nstyle, attr, scaleFontsFactor );
01541                         curpar->setAlignment( curtag.alignment );
01542                         custom = parseTable( attr, format, doc, length, pos, curpar );
01543                     } else if ( tagname == "qt" || tagname == "body" ) {
01544                         if ( attr.contains( "bgcolor" ) ) {
01545                             QBrush *b = new QBrush( QColor( attr["bgcolor"] ) );
01546                             setPaper( b );
01547                         }
01548                         if ( attr.contains( "background" ) ) {
01549                             QImage img;
01550                             QString bg = attr["background"];
01551                             const QMimeSource* m = factory_->data( bg, contxt );
01552                             if ( !m ) {
01553                                 owarn << "QRichText: no mimesource for " << bg.latin1() << "" << oendl; 
01554                             } else {
01555                                 if ( !QImageDrag::decode( m, img ) ) {
01556                                     owarn << "QTextImage: cannot decode " << bg.latin1() << "" << oendl; 
01557                                 }
01558                             }
01559                             if ( !img.isNull() ) {
01560                                 QPixmap pm;
01561                                 pm.convertFromImage( img );
01562                                 QBrush *b = new QBrush( QColor(), pm );
01563                                 setPaper( b );
01564                             }
01565                         }
01566                         if ( attr.contains( "text" ) ) {
01567                             QColor c( attr["text"] );
01568                             if ( formatCollection()->defaultFormat()->color() != c ) {
01569                                 QDict<QTextFormat> formats = formatCollection()->dict();
01570                                 QDictIterator<QTextFormat> it( formats );
01571                                 while ( it.current() ) {
01572                                     if ( it.current() == formatCollection()->defaultFormat() ) {
01573                                         ++it;
01574                                         continue;
01575                                     }
01576                                     it.current()->setColor( c );
01577                                     ++it;
01578                                 }
01579                                 formatCollection()->defaultFormat()->setColor( c );
01580                                 curtag.format.setColor( c );
01581                             }
01582                         }
01583                         if ( attr.contains( "link" ) )
01584                             linkColor = QColor( attr["link"] );
01585                         if ( attr.contains( "title" ) )
01586                             attribs.replace( "title", attr["title"] );
01587 
01588                         if ( textEditMode ) {
01589                             if ( attr.contains("style" ) ) {
01590                                 QString a = attr["style"];
01591                                 for ( int s = 0; s < a.contains(';')+1; s++ ) {
01592                                     QString style = QTextDocument::section( a, ";", s, s );
01593                                     if ( style.startsWith("font-size:" ) && QTextDocument::endsWith(style, "pt") ) {
01594                                         scaleFontsFactor = double( formatCollection()->defaultFormat()->fn.pointSize() ) /
01595                                                            style.mid( 10, style.length() - 12 ).toInt();
01596                                     }
01597                                 }
01598                             }
01599                             nstyle = 0; // ignore body in textEditMode
01600                         }
01601                         // end qt- and body-tag handling
01602                     } else if ( tagname == "meta" ) {
01603                         if ( attr["name"] == "qrichtext" && attr["content"] == "1" )
01604                             textEditMode = TRUE;
01605                     } else if ( tagname == "title" ) {
01606                         QString title;
01607                         while ( pos < length ) {
01608                             if ( hasPrefix( doc, length, pos, QChar('<') ) && hasPrefix( doc, length, pos+1, QChar('/') ) &&
01609                                  parseCloseTag( doc, length, pos ) == "title" )
01610                                 break;
01611                             title += doc[ pos ];
01612                             ++pos;
01613                         }
01614                         attribs.replace( "title", title );
01615                     }
01616                 } // end of well-known tag handling
01617 
01618                 if ( !custom ) // try generic custom item
01619                     custom = sheet_->tag( tagname, attr, contxt, *factory_ , emptyTag, this );
01620 
01621                 if ( !nstyle && !custom ) // we have no clue what this tag could be, ignore it
01622                     continue;
01623 
01624                 if ( custom ) {
01625                     int index = QMAX( curpar->length(),1) - 1;
01626                     QTextFormat format = curtag.format.makeTextFormat( nstyle, attr, scaleFontsFactor );
01627                     curpar->append( QChar('*') );
01628                     curpar->setFormat( index, 1, &format );
01629                     curpar->at( index )->setCustomItem( custom );
01630                     if ( !curtag.anchorHref.isEmpty() )
01631                         curpar->at(index)->setAnchor( QString::null, curtag.anchorHref );
01632                     if ( !anchorName.isEmpty()  ) {
01633                         curpar->at(index)->setAnchor( anchorName, curpar->at(index)->anchorHref() );
01634                         anchorName = QString::null;
01635                     }
01636                     registerCustomItem( custom, curpar );
01637                     hasNewPar = FALSE;
01638                 } else if ( !emptyTag ) {
01639                     /* if we do nesting, push curtag on the stack,
01640                        otherwise reinint curag. */
01641                     if ( curtag.style->name() != tagname || nstyle->selfNesting() ) {
01642                         tags.push( curtag );
01643                     } else {
01644                         if ( !tags.isEmpty() )
01645                             curtag = tags.top();
01646                         else
01647                             curtag = initag;
01648                     }
01649 
01650                     curtag.name = tagname;
01651                     curtag.style = nstyle;
01652                     curtag.name = tagname;
01653                     curtag.style = nstyle;
01654                     if ( int(nstyle->whiteSpaceMode())  != QStyleSheetItem::Undefined )
01655                         curtag.wsm = nstyle->whiteSpaceMode();
01656 
01657                     /* ignore whitespace for inline elements if there
01658                        was already one*/
01659                     if ( !textEditMode && curtag.wsm == QStyleSheetItem::WhiteSpaceNormal
01660                          && ( space || nstyle->displayMode() != QStyleSheetItem::DisplayInline ) )
01661                         eatSpace( doc, length, pos );
01662 
01663                     curtag.format = curtag.format.makeTextFormat( nstyle, attr, scaleFontsFactor );
01664                     if ( nstyle->isAnchor() ) {
01665                         if ( !anchorName.isEmpty() )
01666                             anchorName += "#" + attr["name"];
01667                         else
01668                             anchorName = attr["name"];
01669                         curtag.anchorHref = attr["href"];
01670                     }
01671 
01672                     if ( nstyle->alignment() != QStyleSheetItem::Undefined )
01673                         curtag.alignment = nstyle->alignment();
01674 
01675                     if ( (int) nstyle->listStyle() != QStyleSheetItem::Undefined )
01676                         curtag.liststyle = nstyle->listStyle();
01677 
01678                     if ( nstyle->displayMode() == QStyleSheetItem::DisplayBlock
01679                          || nstyle->displayMode() == QStyleSheetItem::DisplayListItem ) {
01680 
01681                         if ( nstyle->name() == "ol" || nstyle->name() == "ul" || nstyle->name() == "li") {
01682                             QString type = attr["type"];
01683                             if ( !type.isEmpty() ) {
01684                                 if ( type == "1" ) {
01685                                     curtag.liststyle = QStyleSheetItem::ListDecimal;
01686                                 } else if ( type == "a" ) {
01687                                     curtag.liststyle = QStyleSheetItem::ListLowerAlpha;
01688                                 } else if ( type == "A" ) {
01689                                     curtag.liststyle = QStyleSheetItem::ListUpperAlpha;
01690                                 } else {
01691                                     type = type.lower();
01692                                     if ( type == "square" )
01693                                         curtag.liststyle = QStyleSheetItem::ListSquare;
01694                                     else if ( type == "disc" )
01695                                         curtag.liststyle = QStyleSheetItem::ListDisc;
01696                                     else if ( type == "circle" )
01697                                         curtag.liststyle = QStyleSheetItem::ListCircle;
01698                                 }
01699                             }
01700                         }
01701 
01702 
01703                         /* Internally we treat ordered and bullet
01704                           lists the same for margin calculations. In
01705                           order to have fast pointer compares in the
01706                           xMargin() functions we restrict ourselves to
01707                           <ol>. Once we calculate the margins in the
01708                           parser rathern than later, the unelegance of
01709                           this approach goes awy
01710                          */
01711                         if ( nstyle->name() == "ul" )
01712                             curtag.style = sheet_->item( "ol" );
01713 
01714                         if ( attr.contains( "align" ) ) {
01715                             QString align = attr["align"];
01716                             if ( align == "center" )
01717                                 curtag.alignment = Qt::AlignCenter;
01718                             else if ( align == "right" )
01719                                 curtag.alignment = Qt::AlignRight;
01720                             else if ( align == "justify" )
01721                                 curtag.alignment = Qt3::AlignJustify;
01722                         }
01723                         if ( attr.contains( "dir" ) ) {
01724                             QString dir = attr["dir"];
01725                             if ( dir == "rtl" )
01726                                 curtag.direction = QChar::DirR;
01727                             else if ( dir == "ltr" )
01728                                 curtag.direction = QChar::DirL;
01729                         }
01730 
01731                         NEWPAR;
01732 
01733                         if ( curtag.style->displayMode() == QStyleSheetItem::DisplayListItem ) {
01734                             if ( attr.contains( "value " ) )
01735                                 curpar->setListValue( attr["value"].toInt() );
01736                         }
01737 
01738                         if ( attr.contains( "style" ) ) {
01739                             QString a = attr["style"];
01740                             bool ok = TRUE;
01741                             for ( int s = 0; ok && s < a.contains(';')+1; s++ ) {
01742                                 QString style = QTextDocument::section( a, ";", s, s );
01743                                 if ( style.startsWith("margin-top:" ) && QTextDocument::endsWith(style, "px") )
01744                                     curpar->utm = 1+style.mid(11, style.length() - 13).toInt(&ok);
01745                                 else if ( style.startsWith("margin-bottom:" ) && QTextDocument::endsWith(style, "px") )
01746                                     curpar->ubm = 1+style.mid(14, style.length() - 16).toInt(&ok);
01747                                 else if ( style.startsWith("margin-left:" ) && QTextDocument::endsWith(style, "px") )
01748                                     curpar->ulm = 1+style.mid(12, style.length() - 14).toInt(&ok);
01749                                 else if ( style.startsWith("margin-right:" ) && QTextDocument::endsWith(style, "px") )
01750                                     curpar->urm = 1+style.mid(13, style.length() - 15).toInt(&ok);
01751                                 else if ( style.startsWith("text-indent:" ) && QTextDocument::endsWith(style, "px") )
01752                                     curpar->uflm = 1+style.mid(12, style.length() - 14).toInt(&ok);
01753                             }
01754                             if ( !ok ) // be pressmistic
01755                                 curpar->utm = curpar->ubm = curpar->urm = curpar->ulm = 0;
01756                         }
01757                     }
01758                 }
01759             } else {
01760                 QString tagname = parseCloseTag( doc, length, pos );
01761                 if ( tagname.isEmpty() )
01762                     continue; // nothing we could do with this, probably parse error
01763                 if ( !sheet_->item( tagname ) ) // ignore unknown tags
01764                     continue;
01765 
01766                 // we close a block item. Since the text may continue, we need to have a new paragraph
01767                 bool needNewPar = curtag.style->displayMode() == QStyleSheetItem::DisplayBlock
01768                                  || curtag.style->displayMode() == QStyleSheetItem::DisplayListItem;
01769 
01770 
01771                 // html slopiness: handle unbalanched tag closing
01772                 while ( curtag.name != tagname ) {
01773                     QString msg;
01774                     msg.sprintf( "QText Warning: Document not valid ( '%s' not closed before '%s' #%d)",
01775                                  curtag.name.ascii(), tagname.ascii(), pos);
01776                     sheet_->error( msg );
01777                     if ( tags.isEmpty() )
01778                         break;
01779                     curtag = tags.pop();
01780                 }
01781 
01782 
01783                 // close the tag
01784                 if ( !tags.isEmpty() )
01785                     curtag = tags.pop();
01786                 else
01787                     curtag = initag;
01788 
01789                 if ( needNewPar ) {
01790                     if ( textEditMode && tagname == "p" ) // preserve empty paragraphs
01791                         hasNewPar = FALSE;
01792                     NEWPAR;
01793                 }
01794             }
01795         } else {
01796             // normal contents
01797             QString s;
01798             QChar c;
01799             while ( pos < length && !hasPrefix(doc, length, pos, QChar('<') ) ){
01800                 if ( textEditMode ) {
01801                     // text edit mode: we handle all white space but ignore newlines
01802                     c = parseChar( doc, length, pos, QStyleSheetItem::WhiteSpacePre );
01803                     if ( c == QChar_linesep )
01804                         break;
01805                 } else {
01806                     int l = pos;
01807                     c = parseChar( doc, length, pos, curtag.wsm );
01808 
01809                     // in white space pre mode: treat any space as non  breakable
01810                     if ( c == ' ' && curtag.wsm == QStyleSheetItem::WhiteSpacePre )
01811                         c = QChar::nbsp;
01812 
01813                     if ( c == ' ' || c == QChar_linesep ) {
01814                         /* avoid overlong paragraphs by forcing a new
01815                                paragraph after 4096 characters. This case can
01816                                occur when loading undiscovered plain text
01817                                documents in rich text mode. Instead of hanging
01818                                forever, we do the trick.
01819                             */
01820                         if ( curtag.wsm == QStyleSheetItem::WhiteSpaceNormal && s.length() > 4096 ) do {
01821                             if ( doc[l] == '\n' ) {
01822                                 hasNewPar = FALSE; // for a new paragraph ...
01823                                 NEWPAR;
01824                                 hasNewPar = FALSE; // ... and make it non-reusable
01825                                 c = '\n';  // make sure we break below
01826                                 break;
01827                             }
01828                         } while ( ++l < pos );
01829                     }
01830                 }
01831 
01832                 if ( c == '\n' )
01833                     break;  // break on  newlines, pre delievers a QChar_linesep
01834 
01835                 bool c_isSpace = c.isSpace() && c.unicode() != 0x00a0U && !textEditMode;
01836 
01837                 if ( curtag.wsm == QStyleSheetItem::WhiteSpaceNormal && c_isSpace && space )
01838                     continue;
01839                 if ( c == '\r' )
01840                     continue;
01841                 space = c_isSpace;
01842                 s += c;
01843             }
01844             if ( !s.isEmpty() && curtag.style->displayMode() != QStyleSheetItem::DisplayNone ) {
01845                 hasNewPar = FALSE;
01846                 int index = QMAX( curpar->length(),1) - 1;
01847                 curpar->append( s );
01848                 QTextFormat* f = formatCollection()->format( &curtag.format );
01849                 curpar->setFormat( index, s.length(), f, FALSE ); // do not use collection because we have done that already
01850                 f->ref += s.length() -1; // that what friends are for...
01851                 if ( !curtag.anchorHref.isEmpty() ) {
01852                     for ( int i = 0; i < int(s.length()); i++ )
01853                         curpar->at(index + i)->setAnchor( QString::null, curtag.anchorHref );
01854                 }
01855                 if ( !anchorName.isEmpty()  ) {
01856                     curpar->at(index)->setAnchor( anchorName, curpar->at(index)->anchorHref() );
01857                     anchorName = QString::null;
01858                 }
01859             }
01860         }
01861     }
01862     if ( hasNewPar && curpar != fParag && !cursor ) {
01863         // cleanup unused last paragraphs
01864         curpar = curpar->p;
01865         delete curpar->n;
01866     }
01867     if ( !anchorName.isEmpty()  ) {
01868         curpar->at(curpar->length() - 1)->setAnchor( anchorName, curpar->at( curpar->length() - 1 )->anchorHref() );
01869         anchorName = QString::null;
01870     }
01871 
01872 
01873     setRichTextMarginsInternal( styles, stylesPar );
01874 
01875     if ( cursor ) {
01876         cursor->gotoPreviousLetter();
01877         cursor->remove();
01878      }
01879 
01880 }
01881 
01882 void QTextDocument::setRichTextMarginsInternal( QPtrList< QPtrVector<QStyleSheetItem> >& styles, QTextParagraph* stylesPar )
01883 {
01884     // margin and line spacing calculation
01885     QPtrVector<QStyleSheetItem>* prevStyle = 0;
01886     QPtrVector<QStyleSheetItem>* curStyle = styles.first();
01887     QPtrVector<QStyleSheetItem>* nextStyle = styles.next();
01888     while ( stylesPar ) {
01889         if ( !curStyle ) {
01890             stylesPar = stylesPar->next();
01891             prevStyle = curStyle;
01892             curStyle = nextStyle;
01893             nextStyle = styles.next();
01894             continue;
01895         }
01896 
01897         int i, mar;
01898         QStyleSheetItem* mainStyle = curStyle->size() ? (*curStyle)[curStyle->size()-1] : 0;
01899         if ( mainStyle && mainStyle->displayMode() == QStyleSheetItem::DisplayListItem )
01900             stylesPar->setListItem( TRUE );
01901         int numLists = 0;
01902         for ( i = 0; i < (int)curStyle->size(); ++i ) {
01903             if ( (*curStyle)[ i ]->displayMode() == QStyleSheetItem::DisplayBlock
01904                  && int((*curStyle)[ i ]->listStyle()) != QStyleSheetItem::Undefined )
01905                 numLists++;
01906         }
01907         stylesPar->ldepth = numLists;
01908         if ( stylesPar->next() && nextStyle ) {
01909             // also set the depth of the next paragraph, required for the margin calculation
01910             numLists = 0;
01911             for ( i = 0; i < (int)nextStyle->size(); ++i ) {
01912                 if ( (*nextStyle)[ i ]->displayMode() == QStyleSheetItem::DisplayBlock
01913                      && int((*nextStyle)[ i ]->listStyle()) != QStyleSheetItem::Undefined )
01914                     numLists++;
01915             }
01916             stylesPar->next()->ldepth = numLists;
01917         }
01918 
01919         // do the top margin
01920         QStyleSheetItem* item = mainStyle;
01921         int m;
01922         if (stylesPar->utm > 0 ) {
01923             m = stylesPar->utm-1;
01924             stylesPar->utm = 0;
01925         } else {
01926             m = QMAX(0, item->margin( QStyleSheetItem::MarginTop ) );
01927             if ( item->displayMode() == QStyleSheetItem::DisplayListItem
01928                  && stylesPar->ldepth )
01929               m /= stylesPar->ldepth;
01930         }
01931         for ( i = (int)curStyle->size() - 2 ; i >= 0; --i ) {
01932             item = (*curStyle)[ i ];
01933             if ( prevStyle && i < (int) prevStyle->size() &&
01934                  (  item->displayMode() == QStyleSheetItem::DisplayBlock &&
01935                     (*prevStyle)[ i ] == item ) )
01936                 break;
01937             // emulate CSS2' standard 0 vertical margin for multiple ul or ol tags
01938             if ( int(item->listStyle()) != QStyleSheetItem::Undefined  &&
01939                  ( (  i> 0 && (*curStyle)[ i-1 ] == item ) || (*curStyle)[i+1] == item ) )
01940                 continue;
01941             mar = QMAX( 0, item->margin( QStyleSheetItem::MarginTop ) );
01942             m = QMAX( m, mar );
01943         }
01944         stylesPar->utm = m - stylesPar->topMargin();
01945 
01946         // do the bottom margin
01947         item = mainStyle;
01948         if (stylesPar->ubm > 0 ) {
01949             m = stylesPar->ubm-1;
01950             stylesPar->ubm = 0;
01951         } else {
01952             m = QMAX(0, item->margin( QStyleSheetItem::MarginBottom ) );
01953             if ( item->displayMode() == QStyleSheetItem::DisplayListItem
01954                  && stylesPar->ldepth )
01955               m /= stylesPar->ldepth;
01956         }
01957         for ( i = (int)curStyle->size() - 2 ; i >= 0; --i ) {
01958             item = (*curStyle)[ i ];
01959             if ( nextStyle && i < (int) nextStyle->size() &&
01960                  (  item->displayMode() == QStyleSheetItem::DisplayBlock &&
01961                     (*nextStyle)[ i ] == item ) )
01962                 break;
01963             // emulate CSS2' standard 0 vertical margin for multiple ul or ol tags
01964             if ( int(item->listStyle()) != QStyleSheetItem::Undefined  &&
01965                  ( (  i> 0 && (*curStyle)[ i-1 ] == item ) || (*curStyle)[i+1] == item ) )
01966                 continue;
01967             mar = QMAX(0, item->margin( QStyleSheetItem::MarginBottom ) );
01968             m = QMAX( m, mar );
01969         }
01970         stylesPar->ubm = m - stylesPar->bottomMargin();
01971 
01972         // do the left margin, simplyfied
01973         item = mainStyle;
01974         if (stylesPar->ulm > 0 ) {
01975             m = stylesPar->ulm-1;
01976             stylesPar->ulm = 0;
01977         } else {
01978             m = QMAX( 0, item->margin( QStyleSheetItem::MarginLeft ) );
01979         }
01980         for ( i = (int)curStyle->size() - 2 ; i >= 0; --i ) {
01981             item = (*curStyle)[ i ];
01982             m += QMAX( 0, item->margin( QStyleSheetItem::MarginLeft ) );
01983         }
01984         stylesPar->ulm = m - stylesPar->leftMargin();
01985 
01986         // do the right margin, simplyfied
01987         item = mainStyle;
01988         if (stylesPar->urm > 0 ) {
01989             m = stylesPar->urm-1;
01990             stylesPar->urm = 0;
01991         } else {
01992             m = QMAX( 0, item->margin( QStyleSheetItem::MarginRight ) );
01993         }
01994         for ( i = (int)curStyle->size() - 2 ; i >= 0; --i ) {
01995             item = (*curStyle)[ i ];
01996             m += QMAX( 0, item->margin( QStyleSheetItem::MarginRight ) );
01997         }
01998         stylesPar->urm = m - stylesPar->rightMargin();
01999 
02000         // do the first line margin, which really should be called text-indent
02001         item = mainStyle;
02002         if (stylesPar->uflm > 0 ) {
02003             m = stylesPar->uflm-1;
02004             stylesPar->uflm = 0;
02005         } else {
02006             m = QMAX( 0, item->margin( QStyleSheetItem::MarginFirstLine ) );
02007         }
02008         for ( i = (int)curStyle->size() - 2 ; i >= 0; --i ) {
02009             item = (*curStyle)[ i ];
02010             mar = QMAX( 0, item->margin( QStyleSheetItem::MarginFirstLine ) );
02011             m = QMAX( m, mar );
02012         }
02013         stylesPar->uflm =m - stylesPar->firstLineMargin();
02014 
02015         // do the bogus line "spacing", which really is just an extra margin
02016         item = mainStyle;
02017         for ( i = (int)curStyle->size() - 1 ; i >= 0; --i ) {
02018             item = (*curStyle)[ i ];
02019             if ( item->lineSpacing() != QStyleSheetItem::Undefined ) {
02020                 stylesPar->ulinespacing = item->lineSpacing();
02021                 if ( formatCollection() &&
02022                      stylesPar->ulinespacing < formatCollection()->defaultFormat()->height() )
02023                     stylesPar->ulinespacing += formatCollection()->defaultFormat()->height();
02024                 break;
02025             }
02026         }
02027 
02028         stylesPar = stylesPar->next();
02029         prevStyle = curStyle;
02030         curStyle = nextStyle;
02031         nextStyle = styles.next();
02032     }
02033 }
02034 
02035 void QTextDocument::setText( const QString &text, const QString &context )
02036 {
02037     focusIndicator.parag = 0;
02038     selections.clear();
02039     if ( txtFormat == Qt::AutoText && QStyleSheet::mightBeRichText( text ) ||
02040          txtFormat == Qt::RichText )
02041         setRichText( text, context );
02042     else
02043         setPlainText( text );
02044 }
02045 
02046 QString QTextDocument::plainText() const
02047 {
02048     QString buffer;
02049     QString s;
02050     QTextParagraph *p = fParag;
02051     while ( p ) {
02052         if ( !p->mightHaveCustomItems ) {
02053             s = p->string()->toString();
02054         } else {
02055             for ( int i = 0; i < p->length() - 1; ++i ) {
02056                 if ( p->at( i )->isCustom() ) {
02057                     if ( p->at( i )->customItem()->isNested() ) {
02058                         s += "\n";
02059                         QTextTable *t = (QTextTable*)p->at( i )->customItem();
02060                         QPtrList<QTextTableCell> cells = t->tableCells();
02061                         for ( QTextTableCell *c = cells.first(); c; c = cells.next() )
02062                             s += c->richText()->plainText() + "\n";
02063                         s += "\n";
02064                     }
02065                 } else {
02066                     s += p->at( i )->c;
02067                 }
02068             }
02069         }
02070         s.remove( s.length() - 1, 1 );
02071         if ( p->next() )
02072             s += "\n";
02073         buffer += s;
02074         p = p->next();
02075     }
02076     return buffer;
02077 }
02078 
02079 static QString align_to_string( int a )
02080 {
02081     if ( a & Qt::AlignRight )
02082         return " align=\"right\"";
02083     if ( a & Qt::AlignHCenter )
02084         return " align=\"center\"";
02085     if ( a & Qt3::AlignJustify )
02086         return " align=\"justify\"";
02087     return QString::null;
02088 }
02089 
02090 static QString direction_to_string( int d )
02091 {
02092     if ( d != QChar::DirON )
02093         return ( d == QChar::DirL? " dir=\"ltr\"" : " dir=\"rtl\"" );
02094     return QString::null;
02095 }
02096 
02097 static QString list_value_to_string( int v )
02098 {
02099     if ( v != -1 )
02100         return " listvalue=\"" + QString::number( v ) + "\"";
02101     return QString::null;
02102 }
02103 
02104 static QString list_style_to_string( int v )
02105 {
02106     switch( v ) {
02107     case QStyleSheetItem::ListDecimal: return "\"1\"";
02108     case QStyleSheetItem::ListLowerAlpha: return "\"a\"";
02109     case QStyleSheetItem::ListUpperAlpha: return "\"A\"";
02110     case QStyleSheetItem::ListDisc: return "\"disc\"";
02111     case QStyleSheetItem::ListSquare: return "\"square\"";
02112     case QStyleSheetItem::ListCircle: return "\"circle\"";
02113     default:
02114         return QString::null;
02115     }
02116 }
02117 
02118 static inline bool list_is_ordered( int v )
02119 {
02120     return v == QStyleSheetItem::ListDecimal ||
02121            v == QStyleSheetItem::ListLowerAlpha ||
02122            v == QStyleSheetItem::ListUpperAlpha;
02123 }
02124 
02125 
02126 static QString margin_to_string( QStyleSheetItem* style, int t, int b, int l, int r, int fl )
02127 {
02128     QString s;
02129     if ( l > 0 )
02130         s += QString(!!s?";":"") + "margin-left:" + QString::number(l+QMAX(0,style->margin(QStyleSheetItem::MarginLeft))) + "px";
02131     if ( r > 0 )
02132         s += QString(!!s?";":"") + "margin-right:" + QString::number(r+QMAX(0,style->margin(QStyleSheetItem::MarginRight))) + "px";
02133     if ( t > 0 )
02134         s += QString(!!s?";":"") + "margin-top:" + QString::number(t+QMAX(0,style->margin(QStyleSheetItem::MarginTop))) + "px";
02135     if ( b > 0 )
02136         s += QString(!!s?";":"") + "margin-bottom:" + QString::number(b+QMAX(0,style->margin(QStyleSheetItem::MarginBottom))) + "px";
02137     if ( fl > 0 )
02138         s += QString(!!s?";":"") + "text-indent:" + QString::number(fl+QMAX(0,style->margin(QStyleSheetItem::MarginFirstLine))) + "px";
02139     if ( !!s )
02140         return " style=\"" + s + "\"";
02141     return QString::null;
02142 }
02143 
02144 QString QTextDocument::richText() const
02145 {
02146     QString s = "";
02147     if ( !par ) {
02148         s += "<html><head><meta name=\"qrichtext\" content=\"1\" /></head><body style=\"font-size:" ;
02149         s += QString::number( formatCollection()->defaultFormat()->font().pointSize() );
02150         s += "pt;font-family:";
02151         s += formatCollection()->defaultFormat()->font().family();
02152         s +="\">";
02153     }
02154     QTextParagraph* p = fParag;
02155 
02156     QStyleSheetItem* item_p = styleSheet()->item("p");
02157     QStyleSheetItem* item_ul = styleSheet()->item("ul");
02158     QStyleSheetItem* item_ol = styleSheet()->item("ol");
02159     QStyleSheetItem* item_li = styleSheet()->item("li");
02160     if ( !item_p || !item_ul || !item_ol || !item_li ) {
02161         owarn << "QTextEdit: cannot export HTML due to insufficient stylesheet (lack of p, ul, ol, or li)" << oendl; 
02162         return QString::null;
02163     }
02164     int pastListDepth = 0;
02165     int listDepth = 0;
02166     int futureListDepth = 0;
02167     QMemArray<int> listStyles(10);
02168 
02169     while ( p ) {
02170         listDepth = p->listDepth();
02171         if ( listDepth < pastListDepth )  {
02172             for ( int i = listDepth+1; i <= pastListDepth; i++ )
02173                 s += list_is_ordered( listStyles[i] ) ? "</ol>" : "</ul>";
02174             s += '\n';
02175         } else if ( listDepth > pastListDepth ) {
02176             s += '\n';
02177             listStyles.resize( QMAX( (int)listStyles.size(), listDepth+1 ) );
02178             QString list_type;
02179             listStyles[listDepth] = p->listStyle();
02180             if ( !list_is_ordered( p->listStyle() ) || item_ol->listStyle() != p->listStyle() )
02181                 list_type = " type=" + list_style_to_string( p->listStyle() );
02182             for ( int i = pastListDepth; i < listDepth; i++ ) {
02183                 s += list_is_ordered( p->listStyle() ) ? "<ol" : "<ul" ;
02184                 s += list_type + ">";
02185             }
02186         } else {
02187             s += '\n';
02188         }
02189 
02190         QString ps = p->richText();
02191 
02192           // for the bottom margin we need to know whether we are at the end of a list
02193         futureListDepth = 0;
02194         if ( listDepth > 0 && p->next() )
02195             futureListDepth = p->next()->listDepth();
02196 
02197         if ( richTextExportStart && richTextExportStart->paragraph() ==p &&
02198              richTextExportStart->index() == 0 )
02199             s += "<selstart/>";
02200 
02201         if ( p->isListItem() ) {
02202             s += "<li";
02203             if ( p->listStyle() != listStyles[listDepth] )
02204                 s += " type=" + list_style_to_string( p->listStyle() );
02205             s +=align_to_string( p->alignment() );
02206             s += margin_to_string( item_li, p->utm, p->ubm, p->ulm, p->urm, p->uflm );
02207             s +=  list_value_to_string( p->listValue() );
02208             s += direction_to_string( p->direction() );
02209             s +=">";
02210             s += ps;
02211             s += "</li>";
02212         } else {
02213             // normal paragraph item
02214             s += "<p";
02215             s += align_to_string( p->alignment() );
02216             s += margin_to_string( item_p, p->utm, p->ubm, p->ulm, p->urm, p->uflm );
02217             s +=direction_to_string( p->direction() );
02218             s += ">";
02219             s += ps;
02220             s += "</p>";
02221         }
02222         pastListDepth = listDepth;
02223         p = p->next();
02224     }
02225     while ( listDepth > 0 ) {
02226         s += list_is_ordered( listStyles[listDepth] ) ? "</ol>" : "</ul>";
02227         listDepth--;
02228     }
02229 
02230     if ( !par )
02231         s += "\n</body></html>\n";
02232 
02233     return s;
02234 }
02235 
02236 QString QTextDocument::text() const
02237 {
02238     if ( txtFormat == Qt::AutoText && preferRichText || txtFormat == Qt::RichText )
02239         return richText();
02240     return plainText();
02241 }
02242 
02243 QString QTextDocument::text( int parag ) const
02244 {
02245     QTextParagraph *p = paragAt( parag );
02246     if ( !p )
02247         return QString::null;
02248 
02249     if ( txtFormat == Qt::AutoText && preferRichText || txtFormat == Qt::RichText )
02250         return p->richText();
02251     else
02252         return p->string()->toString();
02253 }
02254 
02255 void QTextDocument::invalidate()
02256 {
02257     QTextParagraph *s = fParag;
02258     while ( s ) {
02259         s->invalidate( 0 );
02260         s = s->next();
02261     }
02262 }
02263 
02264 void QTextDocument::selectionStart( int id, int &paragId, int &index )
02265 {
02266     QMap<int, QTextDocumentSelection>::Iterator it = selections.find( id );
02267     if ( it == selections.end() )
02268         return;
02269     QTextDocumentSelection &sel = *it;
02270     paragId = !sel.swapped ? sel.startCursor.paragraph()->paragId() : sel.endCursor.paragraph()->paragId();
02271     index = !sel.swapped ? sel.startCursor.index() : sel.endCursor.index();
02272 }
02273 
02274 QTextCursor QTextDocument::selectionStartCursor( int id)
02275 {
02276     QMap<int, QTextDocumentSelection>::Iterator it = selections.find( id );
02277     if ( it == selections.end() )
02278         return QTextCursor( this );
02279     QTextDocumentSelection &sel = *it;
02280     if ( sel.swapped )
02281         return sel.endCursor;
02282     return sel.startCursor;
02283 }
02284 
02285 QTextCursor QTextDocument::selectionEndCursor( int id)
02286 {
02287     QMap<int, QTextDocumentSelection>::Iterator it = selections.find( id );
02288     if ( it == selections.end() )
02289         return QTextCursor( this );
02290     QTextDocumentSelection &sel = *it;
02291     if ( !sel.swapped )
02292         return sel.endCursor;
02293     return sel.startCursor;
02294 }
02295 
02296 void QTextDocument::selectionEnd( int id, int &paragId, int &index )
02297 {
02298     QMap<int, QTextDocumentSelection>::Iterator it = selections.find( id );
02299     if ( it == selections.end() )
02300         return;
02301     QTextDocumentSelection &sel = *it;
02302     paragId = sel.swapped ? sel.startCursor.paragraph()->paragId() : sel.endCursor.paragraph()->paragId();
02303     index = sel.swapped ? sel.startCursor.index() : sel.endCursor.index();
02304 }
02305 
02306 void QTextDocument::addSelection( int id )
02307 {
02308     nSelections = QMAX( nSelections, id + 1 );
02309 }
02310 
02311 static void setSelectionEndHelper( int id, QTextDocumentSelection &sel, QTextCursor &start, QTextCursor &end )
02312 {
02313     QTextCursor c1 = start;
02314     QTextCursor c2 = end;
02315     if ( sel.swapped ) {
02316         c1 = end;
02317         c2 = start;
02318     }
02319 
02320     c1.paragraph()->removeSelection( id );
02321     c2.paragraph()->removeSelection( id );
02322     if ( c1.paragraph() != c2.paragraph() ) {
02323         c1.paragraph()->setSelection( id, c1.index(), c1.paragraph()->length() - 1 );
02324         c2.paragraph()->setSelection( id, 0, c2.index() );
02325     } else {
02326         c1.paragraph()->setSelection( id, QMIN( c1.index(), c2.index() ), QMAX( c1.index(), c2.index() ) );
02327     }
02328 
02329     sel.startCursor = start;
02330     sel.endCursor = end;
02331     if ( sel.startCursor.paragraph() == sel.endCursor.paragraph() )
02332         sel.swapped = sel.startCursor.index() > sel.endCursor.index();
02333 }
02334 
02335 bool QTextDocument::setSelectionEnd( int id, const QTextCursor &cursor )
02336 {
02337     QMap<int, QTextDocumentSelection>::Iterator it = selections.find( id );
02338     if ( it == selections.end() )
02339         return FALSE;
02340     QTextDocumentSelection &sel = *it;
02341 
02342     QTextCursor start = sel.startCursor;
02343     QTextCursor end = cursor;
02344 
02345     if ( start == end ) {
02346         removeSelection( id );
02347         setSelectionStart( id, cursor );
02348         return TRUE;
02349     }
02350 
02351     if ( sel.endCursor.paragraph() == end.paragraph() ) {
02352         setSelectionEndHelper( id, sel, start, end );
02353         return TRUE;
02354     }
02355 
02356     bool inSelection = FALSE;
02357     QTextCursor c( this );
02358     QTextCursor tmp = sel.startCursor;
02359     if ( sel.swapped )
02360         tmp = sel.endCursor;
02361     tmp.restoreState();
02362     QTextCursor tmp2 = cursor;
02363     tmp2.restoreState();
02364     c.setParagraph( tmp.paragraph()->paragId() < tmp2.paragraph()->paragId() ? tmp.paragraph() : tmp2.paragraph() );
02365     bool hadStart = FALSE;
02366     bool hadEnd = FALSE;
02367     bool hadStartParag = FALSE;
02368     bool hadEndParag = FALSE;
02369     bool hadOldStart = FALSE;
02370     bool hadOldEnd = FALSE;
02371     bool leftSelection = FALSE;
02372     sel.swapped = FALSE;
02373     for ( ;; ) {
02374         if ( c == start )
02375             hadStart = TRUE;
02376         if ( c == end )
02377             hadEnd = TRUE;
02378         if ( c.paragraph() == start.paragraph() )
02379             hadStartParag = TRUE;
02380         if ( c.paragraph() == end.paragraph() )
02381             hadEndParag = TRUE;
02382         if ( c == sel.startCursor )
02383             hadOldStart = TRUE;
02384         if ( c == sel.endCursor )
02385             hadOldEnd = TRUE;
02386 
02387         if ( !sel.swapped &&
02388              ( hadEnd && !hadStart ||
02389                hadEnd && hadStart && start.paragraph() == end.paragraph() && start.index() > end.index() ) )
02390             sel.swapped = TRUE;
02391 
02392         if ( c == end && hadStartParag ||
02393              c == start && hadEndParag ) {
02394             QTextCursor tmp = c;
02395             tmp.restoreState();
02396             if ( tmp.paragraph() != c.paragraph() ) {
02397                 int sstart = tmp.paragraph()->selectionStart( id );
02398                 tmp.paragraph()->removeSelection( id );
02399                 tmp.paragraph()->setSelection( id, sstart, tmp.index() );
02400             }
02401         }
02402 
02403         if ( inSelection &&
02404              ( c == end && hadStart || c == start && hadEnd ) )
02405              leftSelection = TRUE;
02406         else if ( !leftSelection && !inSelection && ( hadStart || hadEnd ) )
02407             inSelection = TRUE;
02408 
02409         bool noSelectionAnymore = hadOldStart && hadOldEnd && leftSelection && !inSelection && !c.paragraph()->hasSelection( id ) && c.atParagEnd();
02410         c.paragraph()->removeSelection( id );
02411         if ( inSelection ) {
02412             if ( c.paragraph() == start.paragraph() && start.paragraph() == end.paragraph() ) {
02413                 c.paragraph()->setSelection( id, QMIN( start.index(), end.index() ), QMAX( start.index(), end.index() ) );
02414             } else if ( c.paragraph() == start.paragraph() && !hadEndParag ) {
02415                 c.paragraph()->setSelection( id, start.index(), c.paragraph()->length() - 1 );
02416             } else if ( c.paragraph() == end.paragraph() && !hadStartParag ) {
02417                 c.paragraph()->setSelection( id, end.index(), c.paragraph()->length() - 1 );
02418             } else if ( c.paragraph() == end.paragraph() && hadEndParag ) {
02419                 c.paragraph()->setSelection( id, 0, end.index() );
02420             } else if ( c.paragraph() == start.paragraph() && hadStartParag ) {
02421                 c.paragraph()->setSelection( id, 0, start.index() );
02422             } else {
02423                 c.paragraph()->setSelection( id, 0, c.paragraph()->length() - 1 );
02424             }
02425         }
02426 
02427         if ( leftSelection )
02428             inSelection = FALSE;
02429 
02430         if ( noSelectionAnymore )
02431             break;
02432         // *ugle*hack optimization
02433         QTextParagraph *p = c.paragraph();
02434         if (  p->mightHaveCustomItems || p == start.paragraph() || p == end.paragraph() || p == lastParagraph() ) {
02435             c.gotoNextLetter();
02436             if ( p == lastParagraph() && c.atParagEnd() )
02437                 break;
02438         } else {
02439             if ( p->document()->parent() )
02440                 do {
02441                     c.gotoNextLetter();
02442                 } while ( c.paragraph() == p );
02443             else
02444                 c.setParagraph( p->next() );
02445         }
02446     }
02447 
02448     if ( !sel.swapped )
02449         sel.startCursor.paragraph()->setSelection( id, sel.startCursor.index(), sel.startCursor.paragraph()->length() - 1 );
02450 
02451     sel.startCursor = start;
02452     sel.endCursor = end;
02453     if ( sel.startCursor.paragraph() == sel.endCursor.paragraph() )
02454         sel.swapped = sel.startCursor.index() > sel.endCursor.index();
02455 
02456     setSelectionEndHelper( id, sel, start, end );
02457 
02458     return TRUE;
02459 }
02460 
02461 void QTextDocument::selectAll( int id )
02462 {
02463     removeSelection( id );
02464 
02465     QTextDocumentSelection sel;
02466     sel.swapped = FALSE;
02467     QTextCursor c( this );
02468 
02469     c.setParagraph( fParag );
02470     c.setIndex( 0 );
02471     sel.startCursor = c;
02472 
02473     c.setParagraph( lParag );
02474     c.setIndex( lParag->length() - 1 );
02475     sel.endCursor = c;
02476 
02477     selections.insert( id, sel );
02478 
02479     QTextParagraph *p = fParag;
02480     while ( p ) {
02481         p->setSelection( id, 0, p->length() - 1 );
02482         p = p->next();
02483     }
02484 
02485     for ( QTextDocument *d = childList.first(); d; d = childList.next() )
02486         d->selectAll( id );
02487 }
02488 
02489 bool QTextDocument::removeSelection( int id )
02490 {
02491     if ( !selections.contains( id ) )
02492         return FALSE;
02493 
02494     QTextDocumentSelection &sel = selections[ id ];
02495 
02496     QTextCursor start = sel.swapped ? sel.endCursor : sel.startCursor;
02497     QTextCursor end = sel.swapped ? sel.startCursor : sel.endCursor;
02498     QTextParagraph* p = 0;
02499     while ( start != end ) {
02500         if ( p != start.paragraph() ) {
02501             p = start.paragraph();
02502             p->removeSelection( id );
02503         }
02504         start.gotoNextLetter();
02505     }
02506     selections.remove( id );
02507     return TRUE;
02508 }
02509 
02510 QString QTextDocument::selectedText( int id, bool asRichText ) const
02511 {
02512     QMap<int, QTextDocumentSelection>::ConstIterator it = selections.find( id );
02513     if ( it == selections.end() )
02514         return QString::null;
02515 
02516     QTextDocumentSelection sel = *it;
02517 
02518 
02519     QTextCursor c1 = sel.startCursor;
02520     QTextCursor c2 = sel.endCursor;
02521     if ( sel.swapped ) {
02522         c2 = sel.startCursor;
02523         c1 = sel.endCursor;
02524     }
02525 
02526     /* 3.0.3 improvement: Make it possible to get a reasonable
02527        selection inside a table.  This approach is very conservative:
02528        make sure that both cursors have the same depth level and point
02529        to paragraphs within the same text document.
02530 
02531        Meaning if you select text in two table cells, you will get the
02532        entire table. This is still far better than the 3.0.2, where
02533        you always got the entire table.
02534 
02535        ### Fix this properly when refactoring
02536      */
02537     while ( c2.nestedDepth() > c1.nestedDepth() )
02538         c2.oneUp();
02539     while ( c1.nestedDepth() > c2.nestedDepth() )
02540         c1.oneUp();
02541     while ( c1.nestedDepth() && c2.nestedDepth() &&
02542             c1.paragraph()->document() != c2.paragraph()->document() ) {
02543         c1.oneUp();
02544         c2.oneUp();
02545     }
02546     // do not trust sel_swapped with tables. Fix this properly when refactoring as well
02547     if ( c1.paragraph()->paragId() > c2.paragraph()->paragId() ||
02548          (c1.paragraph() == c2.paragraph() && c1.index() > c2.index() ) ) {
02549         QTextCursor tmp = c1;
02550         c2 = c1;
02551         c1 = tmp;
02552     }
02553 
02554     // end selection 3.0.3 improvement
02555 
02556     if ( asRichText && !parent() ) {
02557         richTextExportStart = &c1;
02558         richTextExportEnd = &c2;
02559 
02560         QString sel = richText();
02561         int from = sel.find( "<selstart/>" );
02562         int to = sel.findRev( "<selend/>" );
02563         if ( from >= 0 && from <= to )
02564             sel = sel.mid( from, to - from );
02565         richTextExportStart = richTextExportEnd = 0;
02566         return sel;
02567     }
02568 
02569     QString s;
02570     if ( c1.paragraph() == c2.paragraph() ) {
02571         QTextParagraph *p = c1.paragraph();
02572         int end = c2.index();
02573         if ( p->at( QMAX( 0, end - 1 ) )->isCustom() )
02574             ++end;
02575         if ( !p->mightHaveCustomItems ) {
02576             s += p->string()->toString().mid( c1.index(), end - c1.index() );
02577         } else {
02578             for ( int i = c1.index(); i < end; ++i ) {
02579                 if ( p->at( i )->isCustom() ) {
02580                     if ( p->at( i )->customItem()->isNested() ) {
02581                         s += "\n";
02582                         QTextTable *t = (QTextTable*)p->at( i )->customItem();
02583                         QPtrList<QTextTableCell> cells = t->tableCells();
02584                         for ( QTextTableCell *c = cells.first(); c; c = cells.next() )
02585                             s += c->richText()->plainText() + "\n";
02586                         s += "\n";
02587                     }
02588                 } else {
02589                     s += p->at( i )->c;
02590                 }
02591             }
02592         }
02593     } else {
02594         QTextParagraph *p = c1.paragraph();
02595         int start = c1.index();
02596         while ( p ) {
02597             int end = p == c2.paragraph() ? c2.index() : p->length() - 1;
02598             if ( p == c2.paragraph() && p->at( QMAX( 0, end - 1 ) )->isCustom() )
02599                 ++end;
02600             if ( !p->mightHaveCustomItems ) {
02601                 s += p->string()->toString().mid( start, end - start );
02602                 if ( p != c2.paragraph() )
02603                     s += "\n";
02604             } else {
02605                 for ( int i = start; i < end; ++i ) {
02606                     if ( p->at( i )->isCustom() ) {
02607                         if ( p->at( i )->customItem()->isNested() ) {
02608                             s += "\n";
02609                             QTextTable *t = (QTextTable*)p->at( i )->customItem();
02610                             QPtrList<QTextTableCell> cells = t->tableCells();
02611                             for ( QTextTableCell *c = cells.first(); c; c = cells.next() )
02612                                 s += c->richText()->plainText() + "\n";
02613                             s += "\n";
02614                         }
02615                     } else {
02616                         s += p->at( i )->c;
02617                     }
02618                 }
02619             }
02620             start = 0;
02621             if ( p == c2.paragraph() )
02622                 break;
02623             p = p->next();
02624         }
02625     }
02626     // ### workaround for plain text export until we get proper
02627     // mime types: turn unicode line seperators into the more
02628     // widely understood \n. Makes copy and pasting code snipplets
02629     // from within Assistent possible
02630     QChar* uc = (QChar*) s.unicode();
02631     for ( uint ii = 0; ii < s.length(); ii++ )
02632         if ( uc[(int)ii] == QChar_linesep )
02633             uc[(int)ii] = QChar('\n');
02634     return s;
02635 }
02636 
02637 void QTextDocument::setFormat( int id, QTextFormat *f, int flags )
02638 {
02639     QMap<int, QTextDocumentSelection>::ConstIterator it = selections.find( id );
02640     if ( it == selections.end() )
02641         return;
02642 
02643     QTextDocumentSelection sel = *it;
02644 
02645     QTextCursor c1 = sel.startCursor;
02646     QTextCursor c2 = sel.endCursor;
02647     if ( sel.swapped ) {
02648         c2 = sel.startCursor;
02649         c1 = sel.endCursor;
02650     }
02651 
02652     c2.restoreState();
02653     c1.restoreState();
02654 
02655     if ( c1.paragraph() == c2.paragraph() ) {
02656         c1.paragraph()->setFormat( c1.index(), c2.index() - c1.index(), f, TRUE, flags );
02657         return;
02658     }
02659 
02660     c1.paragraph()->setFormat( c1.index(), c1.paragraph()->length() - c1.index(), f, TRUE, flags );
02661     QTextParagraph *p = c1.paragraph()->next();
02662     while ( p && p != c2.paragraph() ) {
02663         p->setFormat( 0, p->length(), f, TRUE, flags );
02664         p = p->next();
02665     }
02666     c2.paragraph()->setFormat( 0, c2.index(), f, TRUE, flags );
02667 }
02668 
02669 void QTextDocument::removeSelectedText( int id, QTextCursor *cursor )
02670 {
02671     QMap<int, QTextDocumentSelection>::Iterator it = selections.find( id );
02672     if ( it == selections.end() )
02673         return;
02674 
02675     QTextDocumentSelection sel = *it;
02676 
02677     QTextCursor c1 = sel.startCursor;
02678     QTextCursor c2 = sel.endCursor;
02679     if ( sel.swapped ) {
02680         c2 = sel.startCursor;
02681         c1 = sel.endCursor;
02682     }
02683 
02684     // ### no support for editing tables yet
02685     if ( c1.nestedDepth() || c2.nestedDepth() )
02686         return;
02687 
02688     c2.restoreState();
02689     c1.restoreState();
02690 
02691     *cursor = c1;
02692     removeSelection( id );
02693 
02694     if ( c1.paragraph() == c2.paragraph() ) {
02695         c1.paragraph()->remove( c1.index(), c2.index() - c1.index() );
02696         return;
02697     }
02698 
02699     if ( c1.paragraph() == fParag && c1.index() == 0 &&
02700          c2.paragraph() == lParag && c2.index() == lParag->length() - 1 )
02701         cursor->setValid( FALSE );
02702 
02703     bool didGoLeft = FALSE;
02704     if (  c1.index() == 0 && c1.paragraph() != fParag ) {
02705         cursor->gotoPreviousLetter();
02706         if ( cursor->isValid() )
02707             didGoLeft = TRUE;
02708     }
02709 
02710     c1.paragraph()->remove( c1.index(), c1.paragraph()->length() - 1 - c1.index() );
02711     QTextParagraph *p = c1.paragraph()->next();
02712     int dy = 0;
02713     QTextParagraph *tmp;
02714     while ( p && p != c2.paragraph() ) {
02715         tmp = p->next();
02716         dy -= p->rect().height();
02717         delete p;
02718         p = tmp;
02719     }
02720     c2.paragraph()->remove( 0, c2.index() );
02721     while ( p ) {
02722         p->move( dy );
02723         p->invalidate( 0 );
02724         p->setEndState( -1 );
02725         p = p->next();
02726     }
02727 
02728     c1.paragraph()->join( c2.paragraph() );
02729 
02730     if ( didGoLeft )
02731         cursor->gotoNextLetter();
02732 }
02733 
02734 void QTextDocument::indentSelection( int id )
02735 {
02736     QMap<int, QTextDocumentSelection>::Iterator it = selections.find( id );
02737     if ( it == selections.end() )
02738         return;
02739 
02740     QTextDocumentSelection sel = *it;
02741     QTextParagraph *startParag = sel.startCursor.paragraph();
02742     QTextParagraph *endParag = sel.endCursor.paragraph();
02743     if ( sel.endCursor.paragraph()->paragId() < sel.startCursor.paragraph()->paragId() ) {
02744         endParag = sel.startCursor.paragraph();
02745         startParag = sel.endCursor.paragraph();
02746     }
02747 
02748     QTextParagraph *p = startParag;
02749     while ( p && p != endParag ) {
02750         p->indent();
02751         p = p->next();
02752     }
02753 }
02754 
02755 void QTextDocument::addCommand( QTextCommand *cmd )
02756 {
02757     commandHistory->addCommand( cmd );
02758 }
02759 
02760 QTextCursor *QTextDocument::undo( QTextCursor *c )
02761 {
02762     return commandHistory->undo( c );
02763 }
02764 
02765 QTextCursor *QTextDocument::redo( QTextCursor *c )
02766 {
02767     return commandHistory->redo( c );
02768 }
02769 
02770 bool QTextDocument::find( QTextCursor& cursor, const QString &e, bool cs, bool wo, bool forward )
02771 {
02772     removeSelection( Standard );
02773     QTextParagraph *p = 0;
02774     QString expr = e;
02775     // if we search for 'word only' than we have to be sure that
02776     // the expression contains no space or punct character at the
02777     // beginning or in the end. Otherwise we would run into a
02778     // endlessloop.
02779     if ( wo ) {
02780         for ( ;; ) {
02781             if ( expr[ 0 ].isSpace() || expr[ 0 ].isPunct() )
02782                 expr = expr.right( expr.length() - 1 );
02783             else
02784                 break;
02785         }
02786         for ( ;; ) {
02787             if ( expr.at( expr.length() - 1 ).isSpace() || expr.at( expr.length() - 1 ).isPunct() )
02788                 expr = expr.left( expr.length() - 1 );
02789             else
02790                 break;
02791         }
02792     }
02793     for (;;) {
02794         if ( p != cursor.paragraph() ) {
02795             p = cursor.paragraph();
02796             QString s = cursor.paragraph()->string()->toString();
02797             int start = cursor.index();
02798             for ( ;; ) {
02799                 int res = forward ? s.find( expr, start, cs ) : s.findRev( expr, start, cs );
02800                 int end = res + expr.length();
02801                 if ( res == -1 || ( !forward && start < end ) )
02802                     break;
02803                 if ( !wo || ( ( res == 0 || s[ res - 1 ].isSpace() || s[ res - 1 ].isPunct() ) &&
02804                               ( end == (int)s.length() || s[ end ].isSpace() || s[ end ].isPunct() ) ) ) {
02805                     removeSelection( Standard );
02806                     cursor.setIndex( forward ? end : res );
02807                     setSelectionStart( Standard, cursor );
02808                     cursor.setIndex( forward ? res : end );
02809                     setSelectionEnd( Standard, cursor );
02810                     return TRUE;
02811                 }
02812                 start = res +  (forward ? 1 : -1);
02813             }
02814         }
02815         if ( forward ) {
02816             if ( cursor.paragraph() == lastParagraph() && cursor.atParagEnd () )
02817                  break;
02818             cursor.gotoNextLetter();
02819         } else {
02820             if ( cursor.paragraph() == firstParagraph() && cursor.atParagStart() )
02821                  break;
02822             cursor.gotoPreviousLetter();
02823         }
02824     }
02825     return FALSE;
02826 }
02827 
02828 void QTextDocument::setTextFormat( Qt::TextFormat f )
02829 {
02830     txtFormat = f;
02831     if ( fParag == lParag && fParag->length() <= 1 )
02832         fParag->rtext = ( f == Qt::RichText );
02833 }
02834 
02835 Qt::TextFormat QTextDocument::textFormat() const
02836 {
02837     return txtFormat;
02838 }
02839 
02840 bool QTextDocument::inSelection( int selId, const QPoint &pos ) const
02841 {
02842     QMap<int, QTextDocumentSelection>::ConstIterator it = selections.find( selId );
02843     if ( it == selections.end() )
02844         return FALSE;
02845 
02846     QTextDocumentSelection sel = *it;
02847     QTextParagraph *startParag = sel.startCursor.paragraph();
02848     QTextParagraph *endParag = sel.endCursor.paragraph();
02849     if ( sel.startCursor.paragraph() == sel.endCursor.paragraph() &&
02850          sel.startCursor.paragraph()->selectionStart( selId ) == sel.endCursor.paragraph()->selectionEnd( selId ) )
02851         return FALSE;
02852     if ( sel.endCursor.paragraph()->paragId() < sel.startCursor.paragraph()->paragId() ) {
02853         endParag = sel.startCursor.paragraph();
02854         startParag = sel.endCursor.paragraph();
02855     }
02856 
02857     QTextParagraph *p = startParag;
02858     while ( p ) {
02859         if ( p->rect().contains( pos ) ) {
02860             bool inSel = FALSE;
02861             int selStart = p->selectionStart( selId );
02862             int selEnd = p->selectionEnd( selId );
02863             int y = 0;
02864             int h = 0;
02865             for ( int i = 0; i < p->length(); ++i ) {
02866                 if ( i == selStart )
02867                     inSel = TRUE;
02868                 if ( i == selEnd )
02869                     break;
02870                 if ( p->at( i )->lineStart ) {
02871                     y = (*p->lineStarts.find( i ))->y;
02872                     h = (*p->lineStarts.find( i ))->h;
02873                 }
02874                 if ( pos.y() - p->rect().y() >= y && pos.y() - p->rect().y() <= y + h ) {
02875                     if ( inSel && pos.x() >= p->at( i )->x &&
02876                          pos.x() <= p->at( i )->x + p->at( i )->format()->width( p->at( i )->c ) )
02877                         return TRUE;
02878                 }
02879             }
02880         }
02881         if ( pos.y() < p->rect().y() )
02882             break;
02883         if ( p == endParag )
02884             break;
02885         p = p->next();
02886     }
02887 
02888     return FALSE;
02889 }
02890 
02891 void QTextDocument::doLayout( QPainter *p, int w )
02892 {
02893     minw = wused = 0;
02894     if ( !is_printer( p ) )
02895         p = 0;
02896     withoutDoubleBuffer = ( p != 0 );
02897     QPainter * oldPainter = QTextFormat::painter();
02898     QTextFormat::setPainter( p );
02899     flow_->setWidth( w );
02900     cw = w;
02901     vw = w;
02902     QTextParagraph *parag = fParag;
02903     while ( parag ) {
02904         parag->invalidate( 0 );
02905         if ( p )
02906             parag->adjustToPainter( p );
02907         parag->format();
02908         parag = parag->next();
02909     }
02910     QTextFormat::setPainter( oldPainter );
02911 }
02912 
02913 QPixmap *QTextDocument::bufferPixmap( const QSize &s )
02914 {
02915     if ( !buf_pixmap )
02916         buf_pixmap = new QPixmap( s.expandedTo( QSize(1,1) ) );
02917     else if ( buf_pixmap->size() != s )
02918         buf_pixmap->resize( s.expandedTo( buf_pixmap->size() ) );
02919     return buf_pixmap;
02920 }
02921 
02922 void QTextDocument::draw( QPainter *p, const QRect &rect, const QColorGroup &cg, const QBrush *paper )
02923 {
02924     if ( !firstParagraph() )
02925         return;
02926 
02927     if ( paper ) {
02928         p->setBrushOrigin( 0, 0 );
02929 
02930         p->fillRect( rect, *paper );
02931     }
02932 
02933     if ( formatCollection()->defaultFormat()->color() != cg.text() )
02934         setDefaultFormat( formatCollection()->defaultFormat()->font(), cg.text() );
02935 
02936     QTextParagraph *parag = firstParagraph();
02937     while ( parag ) {
02938         if ( !parag->isValid() )
02939             parag->format();
02940         int y = parag->rect().y();
02941         QRect pr( parag->rect() );
02942         pr.setX( 0 );
02943         pr.setWidth( QWIDGETSIZE_MAX );
02944         if ( !rect.isNull() && !rect.intersects( pr ) ) {
02945             parag = parag->next();
02946             continue;
02947         }
02948         p->translate( 0, y );
02949         if ( rect.isValid() )
02950             parag->paint( *p, cg, 0, FALSE, rect.x(), rect.y(), rect.width(), rect.height() );
02951         else
02952             parag->paint( *p, cg, 0, FALSE );
02953         p->translate( 0, -y );
02954         parag = parag->next();
02955         if ( !flow()->isEmpty() )
02956             flow()->drawFloatingItems( p, rect.x(), rect.y(), rect.width(), rect.height(), cg, FALSE );
02957     }
02958 }
02959 
02960 void QTextDocument::drawParagraph( QPainter *p, QTextParagraph *parag, int cx, int cy, int cw, int ch,
02961                                QPixmap *&doubleBuffer, const QColorGroup &cg,
02962                                bool drawCursor, QTextCursor *cursor, bool resetChanged )
02963 {
02964     QPainter *painter = 0;
02965     if ( resetChanged )
02966         parag->setChanged( FALSE );
02967     QRect ir( parag->rect() );
02968     bool useDoubleBuffer = !parag->document()->parent();
02969     if ( !useDoubleBuffer && parag->document()->nextDoubleBuffered )
02970         useDoubleBuffer = TRUE;
02971     if ( is_printer( p ) )
02972         useDoubleBuffer = FALSE;
02973 
02974     if ( useDoubleBuffer  ) {
02975         painter = new QPainter;
02976         if ( cx >= 0 && cy >= 0 )
02977             ir = ir.intersect( QRect( cx, cy, cw, ch ) );
02978         if ( !doubleBuffer ||
02979              ir.width() > doubleBuffer->width() ||
02980              ir.height() > doubleBuffer->height() ) {
02981             doubleBuffer = bufferPixmap( ir.size() );
02982             painter->begin( doubleBuffer );
02983         } else {
02984             painter->begin( doubleBuffer );
02985         }
02986     } else {
02987         painter = p;
02988         painter->translate( ir.x(), ir.y() );
02989     }
02990 
02991     painter->setBrushOrigin( -ir.x(), -ir.y() );
02992 
02993     if ( useDoubleBuffer || is_printer( painter ) )
02994         painter->fillRect( QRect( 0, 0, ir.width(), ir.height() ), parag->backgroundBrush( cg ) );
02995     else if ( cursor && cursor->paragraph() == parag )
02996         painter->fillRect( QRect( parag->at( cursor->index() )->x, 0, 2, ir.height() ),
02997                            parag->backgroundBrush( cg ) );
02998 
02999     painter->translate( -( ir.x() - parag->rect().x() ),
03000                         -( ir.y() - parag->rect().y() ) );
03001     parag->paint( *painter, cg, drawCursor ? cursor : 0, TRUE, cx, cy, cw, ch );
03002 
03003     if ( useDoubleBuffer ) {
03004         delete painter;
03005         painter = 0;
03006         p->drawPixmap( ir.topLeft(), *doubleBuffer, QRect( QPoint( 0, 0 ), ir.size() ) );
03007     } else {
03008         painter->translate( -ir.x(), -ir.y() );
03009     }
03010 
03011     if ( useDoubleBuffer ) {
03012         if ( parag->rect().x() + parag->rect().width() < parag->document()->x() + parag->document()->width() ) {
03013             p->fillRect( parag->rect().x() + parag->rect().width(), parag->rect().y(),
03014                          ( parag->document()->x() + parag->document()->width() ) -
03015                          ( parag->rect().x() + parag->rect().width() ),
03016                          parag->rect().height(), cg.brush( QColorGroup::Base ) );
03017         }
03018     }
03019 
03020     parag->document()->nextDoubleBuffered = FALSE;
03021 }
03022 
03023 QTextParagraph *QTextDocument::draw( QPainter *p, int cx, int cy, int cw, int ch, const QColorGroup &cg,
03024                                  bool onlyChanged, bool drawCursor, QTextCursor *cursor, bool resetChanged )
03025 {
03026     if ( withoutDoubleBuffer || par && par->withoutDoubleBuffer ) {
03027         withoutDoubleBuffer = TRUE;
03028         QRect r;
03029         draw( p, r, cg );
03030         return 0;
03031     }
03032     withoutDoubleBuffer = FALSE;
03033 
03034     if ( !firstParagraph() )
03035         return 0;
03036 
03037     if ( cx < 0 && cy < 0 ) {
03038         cx = 0;
03039         cy = 0;
03040         cw = width();
03041         ch = height();
03042     }
03043 
03044     QTextParagraph *lastFormatted = 0;
03045     QTextParagraph *parag = firstParagraph();
03046 
03047     QPixmap *doubleBuffer = 0;
03048     QPainter painter;
03049 
03050     bool fullWidthSelection = FALSE;
03051     while ( parag ) {
03052         lastFormatted = parag;
03053         if ( !parag->isValid() )
03054             parag->format();
03055 
03056         QRect pr = parag->rect();
03057         if ( fullWidthSelection )
03058             pr.setWidth( parag->document()->width() );
03059         if ( pr.y() > cy + ch )
03060             goto floating;
03061         if ( !pr.intersects( QRect( cx, cy, cw, ch ) ) || ( onlyChanged && !parag->hasChanged() ) ) {
03062             parag = parag->next();
03063             continue;
03064         }
03065 
03066         drawParagraph( p, parag, cx, cy, cw, ch, doubleBuffer, cg, drawCursor, cursor, resetChanged );
03067         parag = parag->next();
03068     }
03069 
03070     parag = lastParagraph();
03071 
03072  floating:
03073     if ( parag->rect().y() + parag->rect().height() < parag->document()->height() ) {
03074         if ( !parag->document()->parent() ) {
03075             p->fillRect( 0, parag->rect().y() + parag->rect().height(), parag->document()->width(),
03076                          parag->document()->height() - ( parag->rect().y() + parag->rect().height() ),
03077                          cg.brush( QColorGroup::Base ) );
03078         }
03079         if ( !flow()->isEmpty() ) {
03080             QRect cr( cx, cy, cw, ch );
03081             flow()->drawFloatingItems( p, cr.x(), cr.y(), cr.width(), cr.height(), cg, FALSE );
03082         }
03083     }
03084 
03085     if ( buf_pixmap && buf_pixmap->height() > 300 ) {
03086         delete buf_pixmap;
03087         buf_pixmap = 0;
03088     }
03089 
03090     return lastFormatted;
03091 }
03092 
03093 /*
03094   #### this function only sets the default font size in the format collection
03095  */
03096 void QTextDocument::setDefaultFormat( const QFont &font, const QColor &color )
03097 {
03098     bool reformat = font != fCollection->defaultFormat()->font();
03099     for ( QTextDocument *d = childList.first(); d; d = childList.next() )
03100         d->setDefaultFormat( font, color );
03101     fCollection->updateDefaultFormat( font, color, sheet_ );
03102 
03103     if ( !reformat )
03104         return;
03105     tStopWidth = formatCollection()->defaultFormat()->width( 'x' ) * 8;
03106 
03107     // invalidate paragraphs and custom items
03108     QTextParagraph *p = fParag;
03109     while ( p ) {
03110         p->invalidate( 0 );
03111         for ( int i = 0; i < p->length() - 1; ++i )
03112             if ( p->at( i )->isCustom() )
03113                 p->at( i )->customItem()->invalidate();
03114         p = p->next();
03115     }
03116 }
03117 
03118 void QTextDocument::registerCustomItem( QTextCustomItem *i, QTextParagraph *p )
03119 {
03120     if ( i && i->placement() != QTextCustomItem::PlaceInline ) {
03121         flow_->registerFloatingItem( i );
03122         p->registerFloatingItem( i );
03123         i->setParagraph( p );
03124     }
03125     p->mightHaveCustomItems = mightHaveCustomItems = TRUE;
03126 }
03127 
03128 void QTextDocument::unregisterCustomItem( QTextCustomItem *i, QTextParagraph *p )
03129 {
03130     flow_->unregisterFloatingItem( i );
03131     p->unregisterFloatingItem( i );
03132     i->setParagraph( 0 );
03133 }
03134 
03135 bool QTextDocument::hasFocusParagraph() const
03136 {
03137     return !!focusIndicator.parag;
03138 }
03139 
03140 QString QTextDocument::focusHref() const
03141 {
03142     return focusIndicator.href;
03143 }
03144 
03145 bool QTextDocument::focusNextPrevChild( bool next )
03146 {
03147     if ( !focusIndicator.parag ) {
03148         if ( next ) {
03149             focusIndicator.parag = fParag;
03150             focusIndicator.start = 0;
03151             focusIndicator.len = 0;
03152         } else {
03153             focusIndicator.parag = lParag;
03154             focusIndicator.start = lParag->length();
03155             focusIndicator.len = 0;
03156         }
03157     } else {
03158         focusIndicator.parag->setChanged( TRUE );
03159     }
03160     focusIndicator.href = QString::null;
03161 
03162     if ( next ) {
03163         QTextParagraph *p = focusIndicator.parag;
03164         int index = focusIndicator.start + focusIndicator.len;
03165         while ( p ) {
03166             for ( int i = index; i < p->length(); ++i ) {
03167                 if ( p->at( i )->isAnchor() ) {
03168                     p->setChanged( TRUE );
03169                     focusIndicator.parag = p;
03170                     focusIndicator.start = i;
03171                     focusIndicator.len = 0;
03172                     focusIndicator.href = p->at( i )->anchorHref();
03173                     while ( i < p->length() ) {
03174                         if ( !p->at( i )->isAnchor() )
03175                             return TRUE;
03176                         focusIndicator.len++;
03177                         i++;
03178                     }
03179                 } else if ( p->at( i )->isCustom() ) {
03180                     if ( p->at( i )->customItem()->isNested() ) {
03181                         QTextTable *t = (QTextTable*)p->at( i )->customItem();
03182                         QPtrList<QTextTableCell> cells = t->tableCells();
03183                         // first try to continue
03184                         QTextTableCell *c;
03185                         bool resetCells = TRUE;
03186                         for ( c = cells.first(); c; c = cells.next() ) {
03187                             if ( c->richText()->hasFocusParagraph() ) {
03188                                 if ( c->richText()->focusNextPrevChild( next ) ) {
03189                                     p->setChanged( TRUE );
03190                                     focusIndicator.parag = p;
03191                                     focusIndicator.start = i;
03192                                     focusIndicator.len = 0;
03193                                     focusIndicator.href = c->richText()->focusHref();
03194                                     return TRUE;
03195                                 } else {
03196                                     resetCells = FALSE;
03197                                     c = cells.next();
03198                                     break;
03199                                 }
03200                             }
03201                         }
03202                         // now really try
03203                         if ( resetCells )
03204                             c = cells.first();
03205                         for ( ; c; c = cells.next() ) {
03206                             if ( c->richText()->focusNextPrevChild( next ) ) {
03207                                 p->setChanged( TRUE );
03208                                 focusIndicator.parag = p;
03209                                 focusIndicator.start = i;
03210                                 focusIndicator.len = 0;
03211                                 focusIndicator.href = c->richText()->focusHref();
03212                                 return TRUE;
03213                             }
03214                         }
03215                     }
03216                 }
03217             }
03218             index = 0;
03219             p = p->next();
03220         }
03221     } else {
03222         QTextParagraph *p = focusIndicator.parag;
03223         int index = focusIndicator.start - 1;
03224         if ( focusIndicator.len == 0 && index < focusIndicator.parag->length() - 1 )
03225             index++;
03226         while ( p ) {
03227             for ( int i = index; i >= 0; --i ) {
03228                 if ( p->at( i )->isAnchor() ) {
03229                     p->setChanged( TRUE );
03230                     focusIndicator.parag = p;
03231                     focusIndicator.start = i;
03232                     focusIndicator.len = 0;
03233                     focusIndicator.href = p->at( i )->anchorHref();
03234                     while ( i >= -1 ) {
03235                         if ( i < 0 || !p->at( i )->isAnchor() ) {
03236                             focusIndicator.start++;
03237                             return TRUE;
03238                         }
03239                         if ( i < 0 )
03240                             break;
03241                         focusIndicator.len++;
03242                         focusIndicator.start--;
03243                         i--;
03244                     }
03245                 } else if ( p->at( i )->isCustom() ) {
03246                     if ( p->at( i )->customItem()->isNested() ) {
03247                         QTextTable *t = (QTextTable*)p->at( i )->customItem();
03248                         QPtrList<QTextTableCell> cells = t->tableCells();
03249                         // first try to continue
03250                         QTextTableCell *c;
03251                         bool resetCells = TRUE;
03252                         for ( c = cells.last(); c; c = cells.prev() ) {
03253                             if ( c->richText()->hasFocusParagraph() ) {
03254                                 if ( c->richText()->focusNextPrevChild( next ) ) {
03255                                     p->setChanged( TRUE );
03256                                     focusIndicator.parag = p;
03257                                     focusIndicator.start = i;
03258                                     focusIndicator.len = 0;
03259                                     focusIndicator.href = c->richText()->focusHref();
03260                                     return TRUE;
03261                                 } else {
03262                                     resetCells = FALSE;
03263                                     c = cells.prev();
03264                                     break;
03265                                 }
03266                             }
03267                             if ( cells.at() == 0 )
03268                                 break;
03269                         }
03270                         // now really try
03271                         if ( resetCells )
03272                             c = cells.last();
03273                         for ( ; c; c = cells.prev() ) {
03274                             if ( c->richText()->focusNextPrevChild( next ) ) {
03275                                 p->setChanged( TRUE );
03276                                 focusIndicator.parag = p;
03277                                 focusIndicator.start = i;
03278                                 focusIndicator.len = 0;
03279                                 focusIndicator.href = c->richText()->focusHref();
03280                                 return TRUE;
03281                             }
03282                             if ( cells.at() == 0 )
03283                                 break;
03284                         }
03285                     }
03286                 }
03287             }
03288             p = p->prev();
03289             if ( p )
03290                 index = p->length() - 1;
03291         }
03292     }
03293 
03294     focusIndicator.parag = 0;
03295 
03296     return FALSE;
03297 }
03298 
03299 int QTextDocument::length() const
03300 {
03301     int l = 0;
03302     QTextParagraph *p = fParag;
03303     while ( p ) {
03304         l += p->length() - 1; // don't count trailing space
03305         p = p->next();
03306     }
03307     return l;
03308 }
03309 
03310 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
03311 
03312 int QTextFormat::width( const QChar &c ) const
03313 {
03314     if ( c.unicode() == 0xad ) // soft hyphen
03315         return 0;
03316     if ( !pntr || !pntr->isActive() ) {
03317         if ( c == '\t' )
03318             return fm.width( 'x' ) * 8;
03319         if ( ha == AlignNormal ) {
03320             int w;
03321             if ( c.row() )
03322                 w = fm.width( c );
03323             else
03324                 w = widths[ c.unicode() ];
03325             if ( w == 0 && !c.row() ) {
03326                 w = fm.width( c );
03327                 ( (QTextFormat*)this )->widths[ c.unicode() ] = w;
03328             }
03329             return w;
03330         } else {
03331             QFont f( fn );
03332             if ( usePixelSizes )
03333                 f.setPixelSize( ( f.pixelSize() * 2 ) / 3 );
03334             else
03335                 f.setPointSize( ( f.pointSize() * 2 ) / 3 );
03336             QFontMetrics fm_( f );
03337             return fm_.width( c );
03338         }
03339     }
03340 
03341     QFont f( fn );
03342     if ( ha != AlignNormal ) {
03343         if ( usePixelSizes )
03344             f.setPixelSize( ( f.pixelSize() * 2 ) / 3 );
03345         else
03346             f.setPointSize( ( f.pointSize() * 2 ) / 3 );
03347     }
03348     pntr->setFont( f );
03349 
03350     return pntr->fontMetrics().width( c );
03351 }
03352 
03353 int QTextFormat::width( const QString &str, int pos ) const
03354 {
03355     int w = 0;
03356     if ( str[ pos ].unicode() == 0xad )
03357         return w;
03358     if ( !pntr || !pntr->isActive() ) {
03359         if ( ha == AlignNormal ) {
03360             w = fm.width( str[ pos ] );
03361         } else {
03362             QFont f( fn );
03363             if ( usePixelSizes )
03364                 f.setPixelSize( ( f.pixelSize() * 2 ) / 3 );
03365             else
03366                 f.setPointSize( ( f.pointSize() * 2 ) / 3 );
03367             QFontMetrics fm_( f );
03368             w = fm_.width( str[ pos ] );
03369         }
03370     } else {
03371         QFont f( fn );
03372         if ( ha != AlignNormal ) {
03373             if ( usePixelSizes )
03374                 f.setPixelSize( ( f.pixelSize() * 2 ) / 3 );
03375             else
03376                 f.setPointSize( ( f.pointSize() * 2 ) / 3 );
03377         }
03378         pntr->setFont( f );
03379         w = pntr->fontMetrics().width( str[ pos ] );
03380     }
03381     return w;
03382 }
03383 
03384 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
03385 
03386 QTextString::QTextString()
03387 {
03388     bidiDirty = FALSE;
03389     bidi = FALSE;
03390     rightToLeft = FALSE;
03391     dir = QChar::DirON;
03392 }
03393 
03394 QTextString::QTextString( const QTextString &s )
03395 {
03396     bidiDirty = s.bidiDirty;
03397     bidi = s.bidi;
03398     rightToLeft = s.rightToLeft;
03399     dir = s.dir;
03400     data = s.subString();
03401 }
03402 
03403 void QTextString::insert( int index, const QString &s, QTextFormat *f )
03404 {
03405     insert( index, s.unicode(), s.length(), f );
03406 }
03407 
03408 void QTextString::insert( int index, const QChar *unicode, int len, QTextFormat *f )
03409 {
03410     int os = data.size();
03411     data.resize( data.size() + len );
03412     if ( index < os ) {
03413         memmove( data.data() + index + len, data.data() + index,
03414                  sizeof( QTextStringChar ) * ( os - index ) );
03415     }
03416     for ( int i = 0; i < len; ++i ) {
03417         data[ (int)index + i ].x = 0;
03418         data[ (int)index + i ].lineStart = 0;
03419         data[ (int)index + i ].d.format = 0;
03420         data[ (int)index + i ].type = QTextStringChar::Regular;
03421         data[ (int)index + i ].rightToLeft = 0;
03422         data[ (int)index + i ].startOfRun = 0;
03423         data[ (int)index + i ].c = unicode[i];
03424         data[ (int)index + i ].setFormat( f );
03425     }
03426     bidiDirty = TRUE;
03427 }
03428 
03429 QTextString::~QTextString()
03430 {
03431     clear();
03432 }
03433 
03434 void QTextString::insert( int index, QTextStringChar *c, bool doAddRefFormat  )
03435 {
03436     int os = data.size();
03437     data.resize( data.size() + 1 );
03438     if ( index < os ) {
03439         memmove( data.data() + index + 1, data.data() + index,
03440                  sizeof( QTextStringChar ) * ( os - index ) );
03441     }
03442     data[ (int)index ].c = c->c;
03443     data[ (int)index ].x = 0;
03444     data[ (int)index ].lineStart = 0;
03445     data[ (int)index ].rightToLeft = 0;
03446     data[ (int)index ].d.format = 0;
03447     data[ (int)index ].type = QTextStringChar::Regular;
03448     if ( doAddRefFormat && c->format() )
03449         c->format()->addRef();
03450     data[ (int)index ].setFormat( c->format() );
03451     bidiDirty = TRUE;
03452 }
03453 
03454 void QTextString::truncate( int index )
03455 {
03456     index = QMAX( index, 0 );
03457     index = QMIN( index, (int)data.size() - 1 );
03458     if ( index < (int)data.size() ) {
03459         for ( int i = index + 1; i < (int)data.size(); ++i ) {
03460             if ( !(data[ i ].type == QTextStringChar::Regular) ) {
03461                 delete data[ i ].customItem();
03462                 if ( data[ i ].d.custom->format )
03463                     data[ i ].d.custom->format->removeRef();
03464                 delete data[ i ].d.custom;
03465                 data[ i ].d.custom = 0;
03466             } else if ( data[ i ].format() ) {
03467                 data[ i ].format()->removeRef();
03468             }
03469         }
03470     }
03471     data.truncate( index );
03472     bidiDirty = TRUE;
03473 }
03474 
03475 void QTextString::remove( int index, int len )
03476 {
03477     for ( int i = index; i < (int)data.size() && i - index < len; ++i ) {
03478         if ( !(data[ i ].type == QTextStringChar::Regular) ) {
03479             delete data[ i ].customItem();
03480             if ( data[ i ].d.custom->format )
03481                 data[ i ].d.custom->format->removeRef();
03482             delete data[ i ].d.custom;
03483             data[ i ].d.custom = 0;
03484         } else if ( data[ i ].format() ) {
03485             data[ i ].format()->removeRef();
03486         }
03487     }
03488     memmove( data.data() + index, data.data() + index + len,
03489              sizeof( QTextStringChar ) * ( data.size() - index - len ) );
03490     data.resize( data.size() - len );
03491     bidiDirty = TRUE;
03492 }
03493 
03494 void QTextString::clear()
03495 {
03496     for ( int i = 0; i < (int)data.count(); ++i ) {
03497         if ( !(data[ i ].type == QTextStringChar::Regular) ) {
03498             delete data[ i ].customItem();
03499             if ( data[ i ].d.custom->format )
03500                 data[ i ].d.custom->format->removeRef();
03501             delete data[ i ].d.custom;
03502             data[ i ].d.custom = 0;
03503         } else if ( data[ i ].format() ) {
03504             data[ i ].format()->removeRef();
03505         }
03506     }
03507     data.resize( 0 );
03508 }
03509 
03510 void QTextString::setFormat( int index, QTextFormat *f, bool useCollection )
03511 {
03512     if ( useCollection && data[ index ].format() )
03513         data[ index ].format()->removeRef();
03514     data[ index ].setFormat( f );
03515 }
03516 
03517 void QTextString::checkBidi() const
03518 {
03519     bool rtlKnown = FALSE;
03520     if ( dir == QChar::DirR ) {
03521         ((QTextString *)this)->bidi = TRUE;
03522         ((QTextString *)this)->rightToLeft = TRUE;
03523         rtlKnown = TRUE;
03524         return;
03525     } else if ( dir == QChar::DirL ) {
03526         ((QTextString *)this)->rightToLeft = FALSE;
03527         rtlKnown = TRUE;
03528     } else {
03529         ((QTextString *)this)->rightToLeft = FALSE;
03530     }
03531 
03532     int len = data.size();
03533     const QTextStringChar *c = data.data();
03534     ((QTextString *)this)->bidi = FALSE;
03535     while( len ) {
03536         if ( !rtlKnown ) {
03537             switch( c->c.direction() )
03538             {
03539                 case QChar::DirL:
03540                 case QChar::DirLRO:
03541                 case QChar::DirLRE:
03542                     ((QTextString *)this)->rightToLeft = FALSE;
03543                     rtlKnown = TRUE;
03544                     break;
03545                 case QChar::DirR:
03546                 case QChar::DirAL:
03547                 case QChar::DirRLO:
03548                 case QChar::DirRLE:
03549                     ((QTextString *)this)->rightToLeft = TRUE;
03550                     rtlKnown = TRUE;
03551                     break;
03552                 default:
03553                     break;
03554             }
03555         }
03556         uchar row = c->c.row();
03557         if( (row > 0x04 && row < 0x09) || (row > 0xfa && row < 0xff) ) {
03558             ((QTextString *)this)->bidi = TRUE;
03559             if ( rtlKnown )
03560                 return;
03561         }
03562         len--;
03563         ++c;
03564     }
03565 }
03566 
03567 void QTextDocument::setStyleSheet( QStyleSheet *s )
03568 {
03569     if ( !s )
03570         return;
03571     sheet_ = s;
03572     list_tm = list_bm = par_tm = par_bm = 12;
03573     list_lm = 40;
03574     li_tm = li_bm = 0;
03575     QStyleSheetItem* item = s->item( "ol" );
03576     if ( item ) {
03577         list_tm = QMAX(0,item->margin( QStyleSheetItem::MarginTop ));
03578         list_bm = QMAX(0,item->margin( QStyleSheetItem::MarginBottom ));
03579         list_lm = QMAX(0,item->margin( QStyleSheetItem::MarginLeft ));
03580     }
03581     if ( (item = s->item( "li" ) ) ) {
03582         li_tm = QMAX(0,item->margin( QStyleSheetItem::MarginTop ));
03583         li_bm = QMAX(0,item->margin( QStyleSheetItem::MarginBottom ));
03584     }
03585     if ( (item = s->item( "p" ) ) ) {
03586         par_tm = QMAX(0,item->margin( QStyleSheetItem::MarginTop ));
03587         par_bm = QMAX(0,item->margin( QStyleSheetItem::MarginBottom ));
03588     }
03589 }
03590 
03591 void QTextDocument::setUnderlineLinks( bool b ) {
03592     underlLinks = b;
03593     for ( QTextDocument *d = childList.first(); d; d = childList.next() )
03594         d->setUnderlineLinks( b );
03595 }
03596 
03597 void QTextStringChar::setFormat( QTextFormat *f )
03598 {
03599     if ( type == Regular ) {
03600         d.format = f;
03601     } else {
03602         if ( !d.custom ) {
03603             d.custom = new CustomData;
03604             d.custom->custom = 0;
03605         }
03606         d.custom->format = f;
03607     }
03608 }
03609 
03610 void QTextStringChar::setCustomItem( QTextCustomItem *i )
03611 {
03612     if ( type == Regular ) {
03613         QTextFormat *f = format();
03614         d.custom = new CustomData;
03615         d.custom->format = f;
03616     } else {
03617         delete d.custom->custom;
03618     }
03619     d.custom->custom = i;
03620     type = (type == Anchor ? CustomAnchor : Custom);
03621 }
03622 
03623 void QTextStringChar::loseCustomItem()
03624 {
03625     if ( type == Custom ) {
03626         QTextFormat *f = d.custom->format;
03627         d.custom->custom = 0;
03628         delete d.custom;
03629         type = Regular;
03630         d.format = f;
03631     } else if ( type == CustomAnchor ) {
03632         d.custom->custom = 0;
03633         type = Anchor;
03634     }
03635 }
03636 
03637 QString QTextStringChar::anchorName() const
03638 {
03639     if ( type == Regular )
03640         return QString::null;
03641     else
03642         return d.custom->anchorName;
03643 }
03644 
03645 QString QTextStringChar::anchorHref() const
03646 {
03647     if ( type == Regular )
03648         return QString::null;
03649     else
03650         return d.custom->anchorHref;
03651 }
03652 
03653 void QTextStringChar::setAnchor( const QString& name, const QString& href )
03654 {
03655     if ( type == Regular ) {
03656         QTextFormat *f = format();
03657         d.custom = new CustomData;
03658         d.custom->custom = 0;
03659         d.custom->format = f;
03660         type = Anchor;
03661     } else if ( type == Custom ) {
03662         type = CustomAnchor;
03663     }
03664     d.custom->anchorName = name;
03665     d.custom->anchorHref = href;
03666 }
03667 
03668 
03669 int QTextString::width( int idx ) const
03670 {
03671      int w = 0;
03672      QTextStringChar *c = &at( idx );
03673      if ( c->c.unicode() == 0xad || c->c.unicode() == 0x2028 )
03674          return 0;
03675      if( c->isCustom() ) {
03676          if( c->customItem()->placement() == QTextCustomItem::PlaceInline )
03677              w = c->customItem()->width;
03678      } else {
03679          int r = c->c.row();
03680          if( r < 0x06 || r > 0x1f )
03681              w = c->format()->width( c->c );
03682          else {
03683              // complex text. We need some hacks to get the right metric here
03684              QString str;
03685              int pos = 0;
03686              if( idx > 4 )
03687                  pos = idx - 4;
03688              int off = idx - pos;
03689              int end = QMIN( length(), idx + 4 );
03690              while ( pos < end ) {
03691                  str += at(pos).c;
03692                  pos++;
03693              }
03694              w = c->format()->width( str, off );
03695          }
03696      }
03697      return w;
03698 }
03699 
03700 QMemArray<QTextStringChar> QTextString::subString( int start, int len ) const
03701 {
03702     if ( len == 0xFFFFFF )
03703         len = data.size();
03704     QMemArray<QTextStringChar> a;
03705     a.resize( len );
03706     for ( int i = 0; i < len; ++i ) {
03707         QTextStringChar *c = &data[ i + start ];
03708         a[ i ].c = c->c;
03709         a[ i ].x = 0;
03710         a[ i ].lineStart = 0;
03711         a[ i ].rightToLeft = 0;
03712         a[ i ].d.format = 0;
03713         a[ i ].type = QTextStringChar::Regular;
03714         a[ i ].setFormat( c->format() );
03715         if ( c->format() )
03716             c->format()->addRef();
03717     }
03718     return a;
03719 }
03720 
03721 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
03722 
03723 QTextParagraph::QTextParagraph( QTextDocument *d, QTextParagraph *pr, QTextParagraph *nx, bool updateIds )
03724     : invalid( 0 ), p( pr ), n( nx ), docOrPseudo( d ),
03725       changed(FALSE), firstFormat(TRUE), firstPProcess(TRUE), needPreProcess(FALSE), fullWidth(TRUE),
03726       lastInFrame(FALSE), visible(TRUE), breakable(TRUE), movedDown(FALSE),
03727       mightHaveCustomItems(FALSE), hasdoc( d != 0 ), litem(FALSE), rtext(FALSE),
03728       align( 0 ),mSelections( 0 ),
03729       mFloatingItems( 0 ), lstyle( QStyleSheetItem::ListDisc ),
03730       utm( 0 ), ubm( 0 ), ulm( 0 ), urm( 0 ), uflm( 0 ), ulinespacing( 0 ),
03731       tArray(0), tabStopWidth(0), eData( 0 ), ldepth( 0 )
03732 {
03733     lstyle = QStyleSheetItem::ListDisc;
03734     if ( !hasdoc )
03735         docOrPseudo = new QTextParagraphPseudoDocument;
03736     bgcol = 0;
03737     list_val = -1;
03738     QTextFormat* defFormat = formatCollection()->defaultFormat();
03739     if ( !hasdoc ) {
03740         tabStopWidth = defFormat->width( 'x' ) * 8;
03741         pseudoDocument()->commandHistory = new QTextCommandHistory( 100 );
03742     }
03743 
03744     if ( p )
03745         p->n = this;
03746     if ( n )
03747         n->p = this;
03748 
03749 
03750     if ( !p && hasdoc )
03751         document()->setFirstParagraph( this );
03752     if ( !n && hasdoc )
03753         document()->setLastParagraph( this );
03754 
03755     state = -1;
03756 
03757     if ( p )
03758         id = p->id + 1;
03759     else
03760         id = 0;
03761     if ( n && updateIds ) {
03762         QTextParagraph *s = n;
03763         while ( s ) {
03764             s->id = s->p->id + 1;
03765             s->invalidateStyleCache();
03766             s = s->n;
03767         }
03768     }
03769 
03770     str = new QTextString();
03771     str->insert( 0, " ", formatCollection()->defaultFormat() );
03772 }
03773 
03774 QTextParagraph::~QTextParagraph()
03775 {
03776     delete str;
03777     if ( hasdoc ) {
03778         register QTextDocument *doc = document();
03779         if ( this == doc->minwParag ) {
03780             doc->minwParag = 0;
03781             doc->minw = 0;
03782         }
03783         if ( this == doc->curParag )
03784             doc->curParag = 0;
03785     } else {
03786         delete pseudoDocument();
03787     }
03788     if ( tArray )
03789         delete [] tArray;
03790     delete eData;
03791     QMap<int, QTextLineStart*>::Iterator it = lineStarts.begin();
03792     for ( ; it != lineStarts.end(); ++it )
03793         delete *it;
03794     if ( mSelections )
03795         delete mSelections;
03796     if ( mFloatingItems )
03797         delete mFloatingItems;
03798     if ( p )
03799         p->setNext( n );
03800     if ( n )
03801         n->setPrev( p );
03802 }
03803 
03804 void QTextParagraph::setNext( QTextParagraph *s )
03805 {
03806     n = s;
03807     if ( !n && hasdoc )
03808         document()->setLastParagraph( this );
03809 }
03810 
03811 void QTextParagraph::setPrev( QTextParagraph *s )
03812 {
03813     p = s;
03814     if ( !p && hasdoc )
03815         document()->setFirstParagraph( this );
03816 }
03817 
03818 void QTextParagraph::invalidate( int chr )
03819 {
03820     if ( invalid < 0 )
03821         invalid = chr;
03822     else
03823         invalid = QMIN( invalid, chr );
03824     if ( mFloatingItems ) {
03825         for ( QTextCustomItem *i = mFloatingItems->first(); i; i = mFloatingItems->next() )
03826             i->ypos = -1;
03827     }
03828     invalidateStyleCache();
03829 }
03830 
03831 void QTextParagraph::invalidateStyleCache()
03832 {
03833     if ( list_val < 0 )
03834         list_val = -1;
03835 }
03836 
03837 
03838 void QTextParagraph::insert( int index, const QString &s )
03839 {
03840     insert( index, s.unicode(), s.length() );
03841 }
03842 
03843 void QTextParagraph::insert( int index, const QChar *unicode, int len )
03844 {
03845     if ( hasdoc && !document()->useFormatCollection() && document()->preProcessor() )
03846         str->insert( index, unicode, len,
03847                      document()->preProcessor()->format( QTextPreProcessor::Standard ) );
03848     else
03849         str->insert( index, unicode, len, formatCollection()->defaultFormat() );
03850     invalidate( index );
03851     needPreProcess = TRUE;
03852 }
03853 
03854 void QTextParagraph::truncate( int index )
03855 {
03856     str->truncate( index );
03857     insert( length(), " " );
03858     needPreProcess = TRUE;
03859 }
03860 
03861 void QTextParagraph::remove( int index, int len )
03862 {
03863     if ( index + len - str->length() > 0 )
03864         return;
03865     for ( int i = index; i < index + len; ++i ) {
03866         QTextStringChar *c = at( i );
03867         if ( hasdoc && c->isCustom() ) {
03868             document()->unregisterCustomItem( c->customItem(), this );
03869         }
03870     }
03871     str->remove( index, len );
03872     invalidate( 0 );
03873     needPreProcess = TRUE;
03874 }
03875 
03876 void QTextParagraph::join( QTextParagraph *s )
03877 {
03878     int oh = r.height() + s->r.height();
03879     n = s->n;
03880     if ( n )
03881         n->p = this;
03882     else if ( hasdoc )
03883         document()->setLastParagraph( this );
03884 
03885     int start = str->length();
03886     if ( length() > 0 && at( length() - 1 )->c == ' ' ) {
03887         remove( length() - 1, 1 );
03888         --start;
03889     }
03890     append( s->str->toString(), TRUE );
03891 
03892     for ( int i = 0; i < s->length(); ++i ) {
03893         if ( !hasdoc || document()->useFormatCollection() ) {
03894             s->str->at( i ).format()->addRef();
03895             str->setFormat( i + start, s->str->at( i ).format(), TRUE );
03896         }
03897         if ( s->str->at( i ).isCustom() ) {
03898             QTextCustomItem * item = s->str->at( i ).customItem();
03899             str->at( i + start ).setCustomItem( item );
03900             s->str->at( i ).loseCustomItem();
03901             if ( hasdoc ) {
03902                 document()->unregisterCustomItem( item, s );
03903                 document()->registerCustomItem( item, this );
03904             }
03905         }
03906         if ( s->str->at( i ).isAnchor() ) {
03907             str->at( i + start ).setAnchor( s->str->at( i ).anchorName(),
03908                             s->str->at( i ).anchorHref() );
03909         }
03910     }
03911 
03912     if ( !extraData() && s->extraData() ) {
03913         setExtraData( s->extraData() );
03914         s->setExtraData( 0 );
03915     } else if ( extraData() && s->extraData() ) {
03916         extraData()->join( s->extraData() );
03917     }
03918     delete s;
03919     invalidate( 0 );
03920     r.setHeight( oh );
03921     needPreProcess = TRUE;
03922     if ( n ) {
03923         QTextParagraph *s = n;
03924         s->invalidate( 0 );
03925         while ( s ) {
03926             s->id = s->p->id + 1;
03927             s->state = -1;
03928             s->needPreProcess = TRUE;
03929             s->changed = TRUE;
03930             s->invalidateStyleCache();
03931             s = s->n;
03932         }
03933     }
03934     format();
03935     state = -1;
03936 }
03937 
03938 void QTextParagraph::move( int &dy )
03939 {
03940     if ( dy == 0 )
03941         return;
03942     changed = TRUE;
03943     r.moveBy( 0, dy );
03944     if ( mFloatingItems ) {
03945         for ( QTextCustomItem *i = mFloatingItems->first(); i; i = mFloatingItems->next() )
03946             i->ypos += dy;
03947     }
03948     if ( p )
03949         p->lastInFrame = TRUE;
03950 
03951     // do page breaks if required
03952     if ( hasdoc && document()->isPageBreakEnabled() ) {
03953         int shift;
03954         if ( ( shift = document()->formatter()->formatVertically(  document(), this ) ) ) {
03955             if ( p )
03956                 p->setChanged( TRUE );
03957             dy += shift;
03958         }
03959     }
03960 }
03961 
03962 void QTextParagraph::format( int start, bool doMove )
03963 {
03964     if ( !str || str->length() == 0 || !formatter() )
03965         return;
03966 
03967     if ( hasdoc &&
03968          document()->preProcessor() &&
03969          ( needPreProcess || state == -1 ) )
03970         document()->preProcessor()->process( document(), this, invalid <= 0 ? 0 : invalid );
03971     needPreProcess = FALSE;
03972 
03973     if ( invalid == -1 )
03974         return;
03975 
03976     r.moveTopLeft( QPoint( documentX(), p ? p->r.y() + p->r.height() : documentY() ) );
03977     if ( p )
03978         p->lastInFrame = FALSE;
03979 
03980     movedDown = FALSE;
03981     bool formattedAgain = FALSE;
03982 
03983  formatAgain:
03984 
03985     r.setWidth( documentWidth() );
03986     if ( hasdoc && mFloatingItems ) {
03987         for ( QTextCustomItem *i = mFloatingItems->first(); i; i = mFloatingItems->next() ) {
03988             i->ypos = r.y();
03989             if ( i->placement() == QTextCustomItem::PlaceRight ) {
03990                 i->xpos = r.x() + r.width() - i->width;
03991             }
03992         }
03993     }
03994     QMap<int, QTextLineStart*> oldLineStarts = lineStarts;
03995     lineStarts.clear();
03996     int y = formatter()->format( document(), this, start, oldLineStarts );
03997 
03998 
03999     r.setWidth( QMAX( r.width(), formatter()->minimumWidth() ) );
04000 
04001 
04002     QMap<int, QTextLineStart*>::Iterator it = oldLineStarts.begin();
04003 
04004     for ( ; it != oldLineStarts.end(); ++it )
04005         delete *it;
04006 
04007     QTextStringChar *c = 0;
04008     // do not do this on mac, as the paragraph
04009     // with has to be the full document width on mac as the selections
04010     // always extend completely to the right. This is a bit unefficient,
04011     // as this results in a bigger double buffer than needed but ok for
04012     // now.
04013     if ( lineStarts.count() == 1 ) {
04014         if ( !string()->isBidi() ) {
04015             c = &str->at( str->length() - 1 );
04016             r.setWidth( c->x + str->width( str->length() - 1 ) );
04017         } else {
04018             r.setWidth( lineStarts[0]->w );
04019         }
04020     }
04021 
04022     if ( !hasdoc ) { // qt_format_text bounding rect handling
04023         it = lineStarts.begin();
04024         int usedw = 0;
04025         for ( ; it != lineStarts.end(); ++it )
04026             usedw = QMAX( usedw, (*it)->w );
04027         if ( r.width() <= 0 ) {
04028             // if the user specifies an invalid rect, this means that the
04029             // bounding box should grow to the width that the text actually
04030             // needs
04031             r.setWidth( usedw );
04032         } else {
04033             r.setWidth( QMIN( usedw, r.width() ) );
04034         }
04035     }
04036 
04037     if ( y != r.height() )
04038         r.setHeight( y );
04039 
04040     if ( !visible ) {
04041         r.setHeight( 0 );
04042     } else {
04043         int minw = formatter()->minimumWidth();
04044         int wused = formatter()->widthUsed();
04045         wused = QMAX( minw, wused );
04046         if ( hasdoc ) {
04047             document()->setMinimumWidth( minw, wused, this );
04048         }  else {
04049             pseudoDocument()->minw = QMAX( pseudoDocument()->minw, minw );
04050             pseudoDocument()->wused = QMAX( pseudoDocument()->wused, wused );
04051         }
04052     }
04053 
04054     // do page breaks if required
04055     if ( hasdoc && document()->isPageBreakEnabled() ) {
04056         int shift = document()->formatter()->formatVertically( document(), this );
04057         if ( shift && !formattedAgain ) {
04058             formattedAgain = TRUE;
04059             goto formatAgain;
04060         }
04061     }
04062 
04063     if ( n && doMove && n->invalid == -1 && r.y() + r.height() != n->r.y() ) {
04064         int dy = ( r.y() + r.height() ) - n->r.y();
04065         QTextParagraph *s = n;
04066         bool makeInvalid = p && p->lastInFrame;
04067         while ( s && dy ) {
04068             if ( !s->isFullWidth() )
04069                 makeInvalid = TRUE;
04070             if ( makeInvalid )
04071                 s->invalidate( 0 );
04072             s->move( dy );
04073             if ( s->lastInFrame )
04074                 makeInvalid = TRUE;
04075             s = s->n;
04076         }
04077     }
04078 
04079     firstFormat = FALSE;
04080     changed = TRUE;
04081     invalid = -1;
04082     //#####   string()->setTextChanged( FALSE );
04083 }
04084 
04085 int QTextParagraph::lineHeightOfChar( int i, int *bl, int *y ) const
04086 {
04087     if ( !isValid() )
04088         ( (QTextParagraph*)this )->format();
04089 
04090     QMap<int, QTextLineStart*>::ConstIterator it = lineStarts.end();
04091     --it;
04092     for ( ;; ) {
04093         if ( i >= it.key() ) {
04094             if ( bl )
04095                 *bl = ( *it )->baseLine;
04096             if ( y )
04097                 *y = ( *it )->y;
04098             return ( *it )->h;
04099         }
04100         if ( it == lineStarts.begin() )
04101             break;
04102         --it;
04103     }
04104 
04105     owarn << "QTextParagraph::lineHeightOfChar: couldn't find lh for " << i << "" << oendl; 
04106     return 15;
04107 }
04108 
04109 QTextStringChar *QTextParagraph::lineStartOfChar( int i, int *index, int *line ) const
04110 {
04111     if ( !isValid() )
04112         ( (QTextParagraph*)this )->format();
04113 
04114     int l = (int)lineStarts.count() - 1;
04115     QMap<int, QTextLineStart*>::ConstIterator it = lineStarts.end();
04116     --it;
04117     for ( ;; ) {
04118         if ( i >= it.key() ) {
04119             if ( index )
04120                 *index = it.key();
04121             if ( line )
04122                 *line = l;
04123             return &str->at( it.key() );
04124         }
04125         if ( it == lineStarts.begin() )
04126             break;
04127         --it;
04128         --l;
04129     }
04130 
04131     owarn << "QTextParagraph::lineStartOfChar: couldn't find " << i << "" << oendl; 
04132     return 0;
04133 }
04134 
04135 int QTextParagraph::lines() const
04136 {
04137     if ( !isValid() )
04138         ( (QTextParagraph*)this )->format();
04139 
04140     return (int)lineStarts.count();
04141 }
04142 
04143 QTextStringChar *QTextParagraph::lineStartOfLine( int line, int *index ) const
04144 {
04145     if ( !isValid() )
04146         ( (QTextParagraph*)this )->format();
04147 
04148     if ( line >= 0 && line < (int)lineStarts.count() ) {
04149         QMap<int, QTextLineStart*>::ConstIterator it = lineStarts.begin();
04150         while ( line-- > 0 )
04151             ++it;
04152         int i = it.key();
04153         if ( index )
04154             *index = i;
04155         return &str->at( i );
04156     }
04157 
04158     owarn << "QTextParagraph::lineStartOfLine: couldn't find " << line << "" << oendl; 
04159     return 0;
04160 }
04161 
04162 int QTextParagraph::leftGap() const
04163 {
04164     if ( !isValid() )
04165         ( (QTextParagraph*)this )->format();
04166 
04167     int line = 0;
04168     int x = str->at(0).x;  /* set x to x of first char */
04169     if ( str->isBidi() ) {
04170         for ( int i = 1; i < str->length()-1; ++i )
04171             x = QMIN(x, str->at(i).x);
04172         return x;
04173     }
04174 
04175     QMap<int, QTextLineStart*>::ConstIterator it = lineStarts.begin();
04176     while (line < (int)lineStarts.count()) {
04177         int i = it.key(); /* char index */
04178         x = QMIN(x, str->at(i).x);
04179         ++it;
04180         ++line;
04181     }
04182     return x;
04183 }
04184 
04185 void QTextParagraph::setFormat( int index, int len, QTextFormat *f, bool useCollection, int flags )
04186 {
04187     if ( !f )
04188         return;
04189     if ( index < 0 )
04190         index = 0;
04191     if ( index > str->length() - 1 )
04192         index = str->length() - 1;
04193     if ( index + len >= str->length() )
04194         len = str->length() - index;
04195 
04196     QTextFormatCollection *fc = 0;
04197     if ( useCollection )
04198         fc = formatCollection();
04199     QTextFormat *of;
04200     for ( int i = 0; i < len; ++i ) {
04201         of = str->at( i + index ).format();
04202         if ( !changed && f->key() != of->key() )
04203             changed = TRUE;
04204         if ( invalid == -1 &&
04205              ( f->font().family() != of->font().family() ||
04206                f->font().pointSize() != of->font().pointSize() ||
04207                f->font().weight() != of->font().weight() ||
04208                f->font().italic() != of->font().italic() ||
04209                f->vAlign() != of->vAlign() ) ) {
04210             invalidate( 0 );
04211         }
04212         if ( flags == -1 || flags == QTextFormat::Format || !fc ) {
04213             if ( fc )
04214                 f = fc->format( f );
04215             str->setFormat( i + index, f, useCollection );
04216         } else {
04217             QTextFormat *fm = fc->format( of, f, flags );
04218             str->setFormat( i + index, fm, useCollection );
04219         }
04220     }
04221 }
04222 
04223 void QTextParagraph::indent( int *oldIndent, int *newIndent )
04224 {
04225     if ( !hasdoc || !document()->indent() || isListItem() ) {
04226         if ( oldIndent )
04227             *oldIndent = 0;
04228         if ( newIndent )
04229             *newIndent = 0;
04230         if ( oldIndent && newIndent )
04231             *newIndent = *oldIndent;
04232         return;
04233     }
04234     document()->indent()->indent( document(), this, oldIndent, newIndent );
04235 }
04236 
04237 void QTextParagraph::paint( QPainter &painter, const QColorGroup &cg, QTextCursor *cursor, bool drawSelections,
04238                         int clipx, int clipy, int clipw, int cliph )
04239 {
04240     if ( !visible )
04241         return;
04242     QTextStringChar *chr = 0;
04243     int i, y, h, baseLine, xstart, xend;
04244     i = y =h = baseLine = 0;
04245     QRect cursorRect;
04246     drawSelections &= ( mSelections != 0 );
04247     // macintosh full-width selection style
04248     bool fullWidthStyle = FALSE;
04249     int fullSelectionWidth = 0;
04250     if ( drawSelections && fullWidthStyle )
04251         fullSelectionWidth = (hasdoc ? document()->width() : r.width());
04252         
04253     QString qstr = str->toString();
04254     // ### workaround so that \n are not drawn, actually this should
04255     // be fixed in QFont somewhere (under Windows you get ugly boxes
04256     // otherwise)
04257     QChar* uc = (QChar*) qstr.unicode();
04258     for ( uint ii = 0; ii < qstr.length(); ii++ )
04259         if ( uc[(int)ii]== '\n' || uc[(int)ii] == QChar_linesep || uc[(int)ii] == '\t' )
04260             uc[(int)ii] = 0x20;
04261 
04262     int line = -1;
04263     int paintStart = 0;
04264     int selection = -1;
04265     for ( i = 0; i < length(); i++ ) {
04266         chr = at( i );
04267 
04268         // we flush at end of document
04269         bool flush = i== length()-1;
04270         bool selectionStateChanged = FALSE;
04271         if ( !flush ) {
04272             QTextStringChar *nextchr = at( i+1 );
04273             // we flush at end of line
04274             flush |= nextchr->lineStart;
04275             // we flush on format changes
04276             flush |= ( nextchr->format() != chr->format() );
04277             // we flush on anchor changes
04278             flush |= ( nextchr->isAnchor() != chr->isAnchor() );
04279             // we flush on start of run
04280             flush |= nextchr->startOfRun;
04281             // we flush on bidi changes
04282             flush |= ( nextchr->rightToLeft != chr->rightToLeft );
04283             // we flush on tab
04284             flush |= ( chr->c == '\t' );
04285             // we flush on soft hypens
04286             flush |= ( chr->c.unicode() == 0xad );
04287             // we flush on custom items
04288             flush |= chr->isCustom();
04289             // we flush before custom items
04290             flush |= nextchr->isCustom();
04291             // when painting justified, we flush on spaces
04292             if ((alignment() & Qt3::AlignJustify) == Qt3::AlignJustify )
04293                 flush |= QTextFormatter::isBreakable( str, i );
04294             // we flush when the string is getting too long
04295             flush |= ( i - paintStart >= 256 );
04296             // we flush when the selection state changes
04297             if ( drawSelections ) {
04298                 for ( QMap<int, QTextParagraphSelection>::ConstIterator it = mSelections->begin();
04299                       it != mSelections->end(); ++it )
04300                     selectionStateChanged |=( (*it).start == i || (*it).start == i+1 || (*it).end == i+1 );
04301                 flush |= selectionStateChanged;
04302             }
04303         }
04304 
04305         // init a new line
04306         if ( chr->lineStart ) {
04307             if (fullWidthStyle && drawSelections && selection >= 0)
04308                 painter.fillRect( xend, y, fullSelectionWidth - xend, h,
04309                                   (selection == QTextDocument::Standard || !hasdoc) ?
04310                                   cg.color( QColorGroup::Highlight ) :
04311                                   document()->selectionColor( selection ) );
04312             ++line;
04313             paintStart = i;
04314             lineInfo( line, y, h, baseLine );
04315             if ( clipy != -1 && cliph != 0 && y + r.y() - h > clipy + cliph ) { // outside clip area, leave
04316                 break;
04317             }
04318 
04319             // if this is the first line and we are a list item, draw the the bullet label
04320             if ( line == 0 && isListItem() )
04321                 drawLabel( &painter, chr->x, y, 0, 0, baseLine, cg );
04322         }
04323 
04324         // check for cursor mark
04325         if ( cursor && this == cursor->paragraph() && i == cursor->index() ) {
04326             QTextStringChar *c = i == 0 ? chr : chr - 1;
04327             cursorRect.setRect( cursor->x() , y + baseLine - c->format()->ascent(),
04328                                 1, c->format()->height() );
04329         }
04330 
04331         // check if we are in a selection and store which one it is
04332         selection = -1;
04333         if ( drawSelections ) {
04334             for ( QMap<int, QTextParagraphSelection>::ConstIterator it = mSelections->begin();
04335                   it != mSelections->end(); ++it )
04336                 if ( (*it).start <= i && i < (*it).end + ( (*it).end == length()-1 && n && n->hasSelection(it.key()) ) ? 1:0
04337                      // exclude the standard selection from printing
04338                      && (it.key() != QTextDocument::Standard || !is_printer( &painter) ) ) {
04339                     selection = it.key();
04340                     break;
04341                 }
04342         }
04343 
04344         if ( flush ) {  // something changed, draw what we have so far
04345             if ( chr->rightToLeft ) {
04346                 xstart = chr->x;
04347                 xend = at( paintStart )->x + str->width( paintStart );
04348             } else {
04349                 xstart = at( paintStart )->x;
04350                 if ( !selectionStateChanged && i < length() - 1 && !str->at( i + 1 ).lineStart )
04351                     xend = str->at( i + 1 ).x;
04352                 else
04353                     xend = chr->x + str->width( i );
04354             }
04355 
04356             if ( (clipx == -1 || clipw == -1) || (xend >= clipx && xstart <= clipx + clipw) ) {
04357                 if ( !chr->isCustom() )
04358                     drawString( painter, qstr, paintStart, i - paintStart + 1, xstart, y,
04359                                 baseLine, xend-xstart, h, selection,
04360                                 chr, cg, chr->rightToLeft );
04361                 else if ( chr->customItem()->placement() == QTextCustomItem::PlaceInline )
04362                     chr->customItem()->draw( &painter, chr->x, y,
04363                                              clipx == -1 ? clipx : (clipx - r.x()),
04364                                              clipy == -1 ? clipy : (clipy - r.y()),
04365                                              clipw, cliph, cg, selection >= 0 );
04366             }
04367             paintStart = i+1;
04368         }
04369         
04370     }
04371 
04372     if (fullWidthStyle && drawSelections && selection >= 0 && next() && next()->mSelections)
04373         for ( QMap<int, QTextParagraphSelection>::ConstIterator it = next()->mSelections->begin();
04374               it != next()->mSelections->end(); ++it )
04375             if (((*it).start) == 0) {
04376                 painter.fillRect( xend, y, fullSelectionWidth - xend, h,
04377                                   (selection == QTextDocument::Standard || !hasdoc) ?
04378                                   cg.color( QColorGroup::Highlight ) :
04379                                   document()->selectionColor( selection ) );
04380                 break;
04381             }
04382 
04383     // time to draw the cursor
04384     const int cursor_extent = 4;
04385     if ( !cursorRect.isNull() && cursor &&
04386          ((clipx == -1 || clipw == -1) || (cursorRect.right()+cursor_extent >= clipx && cursorRect.left()-cursor_extent <= clipx + clipw)) ) {
04387         painter.fillRect( cursorRect, cg.color( QColorGroup::Text ) );
04388         painter.save();
04389         if ( string()->isBidi() ) {
04390             if ( at( cursor->index() )->rightToLeft ) {
04391                 painter.setPen( Qt::black );
04392                 painter.drawLine( cursorRect.x(), cursorRect.y(), cursorRect.x() - cursor_extent / 2, cursorRect.y() + cursor_extent / 2 );
04393                 painter.drawLine( cursorRect.x(), cursorRect.y() + cursor_extent, cursorRect.x() - cursor_extent / 2, cursorRect.y() + cursor_extent / 2 );
04394             } else {
04395                 painter.setPen( Qt::black );
04396                 painter.drawLine( cursorRect.x(), cursorRect.y(), cursorRect.x() + cursor_extent / 2, cursorRect.y() + cursor_extent / 2 );
04397                 painter.drawLine( cursorRect.x(), cursorRect.y() + cursor_extent, cursorRect.x() + cursor_extent / 2, cursorRect.y() + cursor_extent / 2 );
04398             }
04399         }
04400         painter.restore();
04401     }
04402 }
04403 
04404 //#define BIDI_DEBUG
04405 
04406 void QTextParagraph::drawString( QPainter &painter, const QString &s, int start, int len, int xstart,
04407                              int y, int baseLine, int w, int h, int selection,
04408                              QTextStringChar *formatChar, const QColorGroup& cg,
04409                              bool rightToLeft )
04410 {
04411     int i = start + len - 1;
04412     bool plainText = hasdoc ? document()->textFormat() == Qt::PlainText : FALSE;
04413     QTextFormat* format = formatChar->format();
04414     QString str( s );
04415     if ( str[ (int)str.length() - 1 ].unicode() == 0xad )
04416         str.remove( str.length() - 1, 1 );
04417     if ( !plainText || hasdoc && format->color() != document()->formatCollection()->defaultFormat()->color() )
04418         painter.setPen( QPen( format->color() ) );
04419     else
04420         painter.setPen( cg.text() );
04421     painter.setFont( format->font() );
04422 
04423     if ( hasdoc && formatChar->isAnchor() && !formatChar->anchorHref().isEmpty() ) {
04424         if ( format->useLinkColor() ) {
04425             if ( document()->linkColor.isValid() )
04426                 painter.setPen( document()->linkColor );
04427             else
04428                 painter.setPen( QPen( Qt::blue ) );
04429         }
04430         if ( document()->underlineLinks() ) {
04431             QFont fn = format->font();
04432             fn.setUnderline( TRUE );
04433             painter.setFont( fn );
04434         }
04435     }
04436 
04437     if ( selection >= 0 )  {
04438         if ( !hasdoc || document()->invertSelectionText( selection ) )
04439             painter.setPen( cg.color( QColorGroup::HighlightedText ) );
04440         painter.fillRect( xstart, y, w, h,
04441                           (selection == QTextDocument::Standard || !hasdoc) ?
04442                           cg.color( QColorGroup::Highlight ) : document()->selectionColor( selection ) );
04443     }
04444 
04445     if ( str[ start ] != '\t' && str[ start ].unicode() != 0xad ) {
04446         if ( format->vAlign() == QTextFormat::AlignNormal ) {
04447             painter.drawText( xstart, y + baseLine, str.mid( start ), len );
04448 #ifdef BIDI_DEBUG
04449             painter.save();
04450             painter.setPen ( Qt::red );
04451             painter.drawLine( xstart, y, xstart, y + baseLine );
04452             painter.drawLine( xstart, y + baseLine/2, xstart + 10, y + baseLine/2 );
04453             int w = 0;
04454             int i = 0;
04455             while( i < len )
04456                 w += painter.fontMetrics().charWidth( str, start + i++ );
04457             painter.setPen ( Qt::blue );
04458             painter.drawLine( xstart + w - 1, y, xstart + w - 1, y + baseLine );
04459             painter.drawLine( xstart + w - 1, y + baseLine/2, xstart + w - 1 - 10, y + baseLine/2 );
04460             painter.restore();
04461 #endif
04462         } else if ( format->vAlign() == QTextFormat::AlignSuperScript ) {
04463             QFont f( painter.font() );
04464             if ( format->fontSizesInPixels() )
04465                 f.setPixelSize( ( f.pixelSize() * 2 ) / 3 );
04466             else
04467                 f.setPointSize( ( f.pointSize() * 2 ) / 3 );
04468             painter.setFont( f );
04469             painter.drawText( xstart, y + baseLine - ( painter.fontMetrics().height() / 2 ),
04470                               str.mid( start ), len );
04471         } else if ( format->vAlign() == QTextFormat::AlignSubScript ) {
04472             QFont f( painter.font() );
04473             if ( format->fontSizesInPixels() )
04474                 f.setPixelSize( ( f.pixelSize() * 2 ) / 3 );
04475             else
04476                 f.setPointSize( ( f.pointSize() * 2 ) / 3 );
04477             painter.setFont( f );
04478             painter.drawText( xstart, y + baseLine + painter.fontMetrics().height() / 6, str.mid( start ), len );
04479         }
04480     }
04481     if ( i + 1 < length() && at( i + 1 )->lineStart && at( i )->c.unicode() == 0xad ) {
04482         painter.drawText( xstart + w, y + baseLine, "\xad" );
04483     }
04484     if ( format->isMisspelled() ) {
04485         painter.save();
04486         painter.setPen( QPen( Qt::red, 1, Qt::DotLine ) );
04487         painter.drawLine( xstart, y + baseLine + 1, xstart + w, y + baseLine + 1 );
04488         painter.restore();
04489     }
04490 
04491     i -= len;
04492 
04493     if ( hasdoc && formatChar->isAnchor() && !formatChar->anchorHref().isEmpty() &&
04494          document()->focusIndicator.parag == this &&
04495          ( document()->focusIndicator.start >= i  &&
04496            document()->focusIndicator.start + document()->focusIndicator.len <= i + len ||
04497            document()->focusIndicator.start <= i &&
04498            document()->focusIndicator.start + document()->focusIndicator.len >= i + len ) ) {
04499         painter.drawWinFocusRect( QRect( xstart, y, w, h ) );
04500     }
04501 
04502 }
04503 
04504 void QTextParagraph::drawLabel( QPainter* p, int x, int y, int w, int h, int base, const QColorGroup& cg )
04505 {
04506     QRect r ( x, y, w, h );
04507     QStyleSheetItem::ListStyle s = listStyle();
04508 
04509     p->save();
04510     QTextFormat *format = at( 0 )->format();
04511     if ( format ) {
04512         p->setPen( format->color() );
04513         p->setFont( format->font() );
04514     }
04515     QFontMetrics fm( p->fontMetrics() );
04516     int size = fm.lineSpacing() / 3;
04517 
04518     switch ( s ) {
04519     case QStyleSheetItem::ListDecimal:
04520     case QStyleSheetItem::ListLowerAlpha:
04521     case QStyleSheetItem::ListUpperAlpha:
04522         {
04523             if ( list_val == -1 ) { // uninitialised list value, calcluate the right one
04524                 int depth = listDepth();
04525                 list_val--;
04526                 // ### evil, square and expensive. This needs to be done when formatting, not when painting
04527                 QTextParagraph* s = prev();
04528                 int depth_s;
04529                 while ( s && (depth_s = s->listDepth()) >= depth ) {
04530                     if ( depth_s == depth && s->isListItem() )
04531                         list_val--;
04532                     s = s->prev();
04533                 }
04534             }
04535 
04536             int n = list_val;
04537             if ( n < -1 )
04538                 n = -n - 1;
04539             QString l;
04540             switch ( s ) {
04541             case QStyleSheetItem::ListLowerAlpha:
04542                 if ( n < 27 ) {
04543                     l = QChar( ('a' + (char) (n-1)));
04544                     break;
04545                 }
04546             case QStyleSheetItem::ListUpperAlpha:
04547                 if ( n < 27 ) {
04548                     l = QChar( ('A' + (char) (n-1)));
04549                     break;
04550                 }
04551                 break;
04552             default:  //QStyleSheetItem::ListDecimal:
04553                 l.setNum( n );
04554                 break;
04555             }
04556             l += QString::fromLatin1(". ");
04557             p->drawText( r.right() - fm.width( l ), r.top() + base, l );
04558         }
04559         break;
04560     case QStyleSheetItem::ListSquare:
04561         {
04562             QRect er( r.right() - size * 2, r.top() + fm.height() / 2 - size / 2, size, size );
04563             p->fillRect( er , cg.brush( QColorGroup::Text ) );
04564         }
04565         break;
04566     case QStyleSheetItem::ListCircle:
04567         {
04568             QRect er( r.right()-size*2, r.top() + fm.height() / 2 - size / 2, size, size);
04569             p->drawEllipse( er );
04570         }
04571         break;
04572     case QStyleSheetItem::ListDisc:
04573     default:
04574         {
04575             p->setBrush( cg.brush( QColorGroup::Text ));
04576             QRect er( r.right()-size*2, r.top() + fm.height() / 2 - size / 2, size, size);
04577             p->drawEllipse( er );
04578             p->setBrush( Qt::NoBrush );
04579         }
04580         break;
04581     }
04582 
04583     p->restore();
04584 }
04585 
04586 void QTextParagraph::readStyleInformation( QDataStream& stream )
04587 {
04588     int int_align, int_lstyle;
04589     uchar uchar_litem, uchar_rtext, uchar_dir;
04590     stream >> int_align >> int_lstyle >> utm >> ubm >> ulm >> urm >> uflm
04591            >> ulinespacing >> ldepth >> uchar_litem >> uchar_rtext >> uchar_dir;
04592     align = int_align; lstyle = (QStyleSheetItem::ListStyle) int_lstyle;
04593     litem = uchar_litem; rtext = uchar_rtext; str->setDirection( (QChar::Direction)uchar_dir );
04594     QTextParagraph* s = prev() ? prev() : this;
04595     while ( s ) {
04596         s->invalidate( 0 );
04597         s = s->next();
04598     }
04599 }
04600 
04601 void QTextParagraph::writeStyleInformation( QDataStream& stream ) const
04602 {
04603     stream << (int) align << (int) lstyle << utm << ubm << ulm << urm << uflm << ulinespacing << ldepth << (uchar)litem << (uchar)rtext << (uchar)str->direction();
04604 }
04605 
04606 
04607 
04608 void QTextParagraph::setListDepth( int depth ) {
04609     if ( !hasdoc || depth == ldepth )
04610         return;
04611     ldepth = depth;
04612     QTextParagraph* s = prev() ? prev() : this;
04613     while ( s ) {
04614         s->invalidate( 0 );
04615         s = s->next();
04616     }
04617 }
04618 
04619 int *QTextParagraph::tabArray() const
04620 {
04621     int *ta = tArray;
04622     if ( !ta && hasdoc )
04623         ta = document()->tabArray();
04624     return ta;
04625 }
04626 
04627 int QTextParagraph::nextTab( int, int x )
04628 {
04629     int *ta = tArray;
04630     if ( hasdoc ) {
04631         if ( !ta )
04632             ta = document()->tabArray();
04633         tabStopWidth = document()->tabStopWidth();
04634     }
04635     if ( ta ) {
04636         int i = 0;
04637         while ( ta[ i ] ) {
04638             if ( ta[ i ] >= x )
04639                 return tArray[ i ];
04640             ++i;
04641         }
04642         return tArray[ 0 ];
04643     } else {
04644         int d;
04645         if ( tabStopWidth != 0 )
04646             d = x / tabStopWidth;
04647         else
04648             return x;
04649         return tabStopWidth * ( d + 1 );
04650     }
04651 }
04652 
04653 void QTextParagraph::adjustToPainter( QPainter *p )
04654 {
04655     for ( int i = 0; i < length(); ++i ) {
04656         if ( at( i )->isCustom() )
04657             at( i )->customItem()->adjustToPainter( p );
04658     }
04659 }
04660 
04661 QTextFormatCollection *QTextParagraph::formatCollection() const
04662 {
04663     if ( hasdoc )
04664         return document()->formatCollection();
04665     if ( !qFormatCollection ) {
04666         qFormatCollection = new QTextFormatCollection;
04667         static QSingleCleanupHandler<QTextFormatCollection> qtfCleanup;
04668         qtfCleanup.set( &qFormatCollection );
04669     }
04670     return qFormatCollection;
04671 }
04672 
04673 QString QTextParagraph::richText() const
04674 {
04675     QString s;
04676     QTextStringChar *formatChar = 0;
04677     QString spaces;
04678     bool doStart = richTextExportStart && richTextExportStart->paragraph() == this;
04679     bool doEnd = richTextExportEnd && richTextExportEnd->paragraph() == this;
04680     int i;
04681     for ( i = 0; i < length()-1; ++i ) {
04682         if ( doStart && i && richTextExportStart->index() == i )
04683             s += "<selstart/>";
04684         if ( doEnd && richTextExportEnd->index() == i )
04685             s += "<selend/>";
04686         QTextStringChar *c = &str->at( i );
04687         if ( c->isAnchor() && !c->anchorName().isEmpty() ) {
04688             if ( c->anchorName().contains( '#' ) ) {
04689                 QStringList l = QStringList::split( '#', c->anchorName() );
04690                 for ( QStringList::ConstIterator it = l.begin(); it != l.end(); ++it )
04691                     s += "<a name=\"" + *it + "\"></a>";
04692             } else {
04693                 s += "<a name=\"" + c->anchorName() + "\"></a>";
04694             }
04695         }
04696         if ( !formatChar ) {
04697             s += c->format()->makeFormatChangeTags( formatCollection()->defaultFormat(),
04698                                                     0, QString::null, c->anchorHref() );
04699             formatChar = c;
04700         } else if ( ( formatChar->format()->key() != c->format()->key() ) ||
04701                   (c->anchorHref() != formatChar->anchorHref() ) )  {
04702             s += c->format()->makeFormatChangeTags( formatCollection()->defaultFormat(),
04703                                                     formatChar->format() , formatChar->anchorHref(), c->anchorHref() );
04704             formatChar = c;
04705         }
04706         if ( c->c == '<' )
04707             s += "&lt;";
04708         else if ( c->c == '>' )
04709             s += "&gt;";
04710         else if ( c->isCustom() )
04711             s += c->customItem()->richText();
04712         else if ( c->c == '\n' || c->c == QChar_linesep )
04713             s += "<br />"; // space on purpose for compatibility with Netscape, Lynx & Co.
04714         else
04715             s += c->c;
04716     }
04717     if ( doEnd && richTextExportEnd->index() == i )
04718         s += "<selend/>";
04719     if ( formatChar )
04720         s += formatChar->format()->makeFormatEndTags( formatCollection()->defaultFormat(), formatChar->anchorHref() );
04721     return s;
04722 }
04723 
04724 void QTextParagraph::addCommand( QTextCommand *cmd )
04725 {
04726     if ( !hasdoc )
04727         pseudoDocument()->commandHistory->addCommand( cmd );
04728     else
04729         document()->commands()->addCommand( cmd );
04730 }
04731 
04732 QTextCursor *QTextParagraph::undo( QTextCursor *c )
04733 {
04734     if ( !hasdoc )
04735         return pseudoDocument()->commandHistory->undo( c );
04736     return document()->commands()->undo( c );
04737 }
04738 
04739 QTextCursor *QTextParagraph::redo( QTextCursor *c )
04740 {
04741     if ( !hasdoc )
04742         return pseudoDocument()->commandHistory->redo( c );
04743     return document()->commands()->redo( c );
04744 }
04745 
04746 int QTextParagraph::topMargin() const
04747 {
04748     int m = 0;
04749     if ( rtext ) {
04750         m = isListItem() ? (document()->li_tm/QMAX(1,listDepth())) : document()->par_tm;
04751         if ( listDepth() == 1 &&(  !prev() || prev()->listDepth() < listDepth() ) )
04752             m = QMAX( m, document()->list_tm );
04753     }
04754     m += utm;
04755     return scale( m, QTextFormat::painter() );
04756 }
04757 
04758 int QTextParagraph::bottomMargin() const
04759 {
04760     int m = 0;
04761     if ( rtext ) {
04762         m = isListItem() ? (document()->li_bm/QMAX(1,listDepth())) : document()->par_bm;
04763         if ( listDepth() == 1 &&(  !next() || next()->listDepth() < listDepth() ) )
04764             m = QMAX( m, document()->list_bm );
04765     }
04766     m += ubm;
04767     return scale( m, QTextFormat::painter() );
04768 }
04769 
04770 int QTextParagraph::leftMargin() const
04771 {
04772     int m = ulm;
04773     if ( listDepth() )
04774         m += listDepth() * document()->list_lm;
04775     return scale( m, QTextFormat::painter() );
04776 }
04777 
04778 int QTextParagraph::firstLineMargin() const
04779 {
04780     int m = uflm;
04781     return scale( m, QTextFormat::painter() );
04782 }
04783 
04784 int QTextParagraph::rightMargin() const
04785 {
04786     int m = urm;
04787     return scale( m, QTextFormat::painter() );
04788 }
04789 
04790 int QTextParagraph::lineSpacing() const
04791 {
04792     int l = ulinespacing;
04793     l = scale( l, QTextFormat::painter() );
04794     return l;
04795 }
04796 
04797 void QTextParagraph::copyParagData( QTextParagraph *parag )
04798 {
04799     rtext = parag->rtext;
04800     lstyle = parag->lstyle;
04801     ldepth = parag->ldepth;
04802     litem = parag->litem;
04803     align = parag->align;
04804     utm = parag->utm;
04805     ubm = parag->ubm;
04806     urm = parag->urm;
04807     ulm = parag->ulm;
04808     uflm = parag->uflm;
04809     ulinespacing = parag->ulinespacing;
04810     QColor *c = parag->backgroundColor();
04811     if ( c )
04812         setBackgroundColor( *c );
04813     str->setDirection( parag->str->direction() );
04814 }
04815 
04816 void QTextParagraph::show()
04817 {
04818     if ( visible || !hasdoc )
04819         return;
04820     visible = TRUE;
04821 }
04822 
04823 void QTextParagraph::hide()
04824 {
04825     if ( !visible || !hasdoc )
04826         return;
04827     visible = FALSE;
04828 }
04829 
04830 void QTextParagraph::setDirection( QChar::Direction d )
04831 {
04832     if ( str && str->direction() != d ) {
04833         str->setDirection( d );
04834         invalidate( 0 );
04835     }
04836 }
04837 
04838 QChar::Direction QTextParagraph::direction() const
04839 {
04840     return (str ? str->direction() : QChar::DirON );
04841 }
04842 
04843 void QTextParagraph::setChanged( bool b, bool recursive )
04844 {
04845     changed = b;
04846     if ( recursive ) {
04847         if ( document() && document()->parentParagraph() )
04848             document()->parentParagraph()->setChanged( b, recursive );
04849     }
04850 }
04851 
04852 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
04853 
04854 
04855 QTextPreProcessor::QTextPreProcessor()
04856 {
04857 }
04858 
04859 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
04860 
04861 QTextFormatter::QTextFormatter()
04862     : thisminw(0), thiswused(0), wrapEnabled( TRUE ), wrapColumn( -1 ), biw( FALSE )
04863 {
04864 }
04865 
04866 QTextLineStart *QTextFormatter::formatLine( QTextParagraph *parag, QTextString *string, QTextLineStart *line,
04867                                                    QTextStringChar *startChar, QTextStringChar *lastChar, int align, int space )
04868 {
04869 #ifndef QT_NO_COMPLEXTEXT
04870     if( string->isBidi() )
04871         return bidiReorderLine( parag, string, line, startChar, lastChar, align, space );
04872 #endif
04873     int start = (startChar - &string->at(0));
04874     int last = (lastChar - &string->at(0) );
04875     // do alignment Auto == Left in this case
04876     if ( align & Qt::AlignHCenter || align & Qt::AlignRight ) {
04877         if ( align & Qt::AlignHCenter )
04878             space /= 2;
04879         for ( int j = start; j <= last; ++j )
04880             string->at( j ).x += space;
04881     } else if ( align & Qt3::AlignJustify ) {
04882         int numSpaces = 0;
04883         // End at "last-1", the last space ends up with a width of 0
04884         for ( int j = last-1; j >= start; --j ) {
04885             // Start at last tab, if any.
04886             if ( string->at( j ).c == '\t' ) {
04887                 start = j+1;
04888                 break;
04889             }
04890             if( isBreakable( string, j ) ) {
04891                 numSpaces++;
04892             }
04893         }
04894         int toAdd = 0;
04895         for ( int k = start + 1; k <= last; ++k ) {
04896             if( isBreakable( string, k ) && numSpaces ) {
04897                 int s = space / numSpaces;
04898                 toAdd += s;
04899                 space -= s;
04900                 numSpaces--;
04901             }
04902             string->at( k ).x += toAdd;
04903         }
04904     }
04905 
04906     if ( last >= 0 && last < string->length() )
04907         line->w = string->at( last ).x + string->width( last );
04908     else
04909         line->w = 0;
04910 
04911     return new QTextLineStart();
04912 }
04913 
04914 #ifndef QT_NO_COMPLEXTEXT
04915 
04916 #ifdef BIDI_DEBUG
04917 #include <iostream>
04918 #endif
04919 
04920 // collects one line of the paragraph and transforms it to visual order
04921 QTextLineStart *QTextFormatter::bidiReorderLine( QTextParagraph * /*parag*/, QTextString *text, QTextLineStart *line,
04922                                                         QTextStringChar *startChar, QTextStringChar *lastChar, int align, int space )
04923 {
04924     int start = (startChar - &text->at(0));
04925     int last = (lastChar - &text->at(0) );
04926 
04927     QBidiControl *control = new QBidiControl( line->context(), line->status );
04928     QString str;
04929     str.setUnicode( 0, last - start + 1 );
04930     // fill string with logically ordered chars.
04931     QTextStringChar *ch = startChar;
04932     QChar *qch = (QChar *)str.unicode();
04933     while ( ch <= lastChar ) {
04934         *qch = ch->c;
04935         qch++;
04936         ch++;
04937     }
04938     int x = startChar->x;
04939 
04940     QPtrList<QTextRun> *runs;
04941     runs = QComplexText::bidiReorderLine(control, str, 0, last - start + 1,
04942                                          (text->isRightToLeft() ? QChar::DirR : QChar::DirL) );
04943 
04944     // now construct the reordered string out of the runs...
04945 
04946     int numSpaces = 0;
04947     // set the correct alignment. This is a bit messy....
04948     if( align == Qt3::AlignAuto ) {
04949         // align according to directionality of the paragraph...
04950         if ( text->isRightToLeft() )
04951             align = Qt::AlignRight;
04952     }
04953 
04954     if ( align & Qt::AlignHCenter )
04955         x += space/2;
04956     else if ( align & Qt::AlignRight )
04957         x += space;
04958     else if ( align & Qt3::AlignJustify ) {
04959         // End at "last-1", the last space ends up with a width of 0
04960         for ( int j = last-1; j >= start; --j ) {
04961             // Start at last tab, if any.
04962             if ( text->at( j ).c == '\t' ) {
04963                 start = j+1;
04964                 break;
04965             }
04966             if( isBreakable( text, j ) ) {
04967                 numSpaces++;
04968             }
04969         }
04970     }
04971     int toAdd = 0;
04972     bool first = TRUE;
04973     QTextRun *r = runs->first();
04974     int xmax = -0xffffff;
04975     while ( r ) {
04976         if(r->level %2) {
04977             // odd level, need to reverse the string
04978             int pos = r->stop + start;
04979             while(pos >= r->start + start) {
04980                 QTextStringChar *c = &text->at(pos);
04981                 if( numSpaces && !first && isBreakable( text, pos ) ) {
04982                     int s = space / numSpaces;
04983                     toAdd += s;
04984                     space -= s;
04985                     numSpaces--;
04986                 } else if ( first ) {
04987                     first = FALSE;
04988                     if ( c->c == ' ' )
04989                         x -= c->format()->width( ' ' );
04990                 }
04991                 c->x = x + toAdd;
04992                 c->rightToLeft = TRUE;
04993                 c->startOfRun = FALSE;
04994                 int ww = 0;
04995                 if ( c->c.unicode() >= 32 || c->c == '\t' || c->c == '\n' || c->isCustom() ) {
04996                     ww = text->width( pos );
04997                 } else {
04998                     ww = c->format()->width( ' ' );
04999                 }
05000                 if ( xmax < x + toAdd + ww ) xmax = x + toAdd + ww;
05001                 x += ww;
05002                 pos--;
05003             }
05004         } else {
05005             int pos = r->start + start;
05006             while(pos <= r->stop + start) {
05007                 QTextStringChar* c = &text->at(pos);
05008                 if( numSpaces && !first && isBreakable( text, pos ) ) {
05009                     int s = space / numSpaces;
05010                     toAdd += s;
05011                     space -= s;
05012                     numSpaces--;
05013                 } else if ( first ) {
05014                     first = FALSE;
05015                     if ( c->c == ' ' )
05016                         x -= c->format()->width( ' ' );
05017                 }
05018                 c->x = x + toAdd;
05019                 c->rightToLeft = FALSE;
05020                 c->startOfRun = FALSE;
05021                 int ww = 0;
05022                 if ( c->c.unicode() >= 32 || c->c == '\t' || c->isCustom() ) {
05023                     ww = text->width( pos );
05024                 } else {
05025                     ww = c->format()->width( ' ' );
05026                 }
05027                 if ( xmax < x + toAdd + ww ) xmax = x + toAdd + ww;
05028                 x += ww;
05029                 pos++;
05030             }
05031         }
05032         text->at( r->start + start ).startOfRun = TRUE;
05033         r = runs->next();
05034     }
05035 
05036     line->w = xmax + 10;
05037     QTextLineStart *ls = new QTextLineStart( control->context, control->status );
05038     delete control;
05039     delete runs;
05040     return ls;
05041 }
05042 #endif
05043 
05044 bool QTextFormatter::isBreakable( QTextString *string, int pos )
05045 {
05046     const QChar &c = string->at( pos ).c;
05047     char ch = c.latin1();
05048     if ( c == QChar_linesep )
05049         return TRUE;
05050     if ( c.isSpace() && ch != '\n' && c.unicode() != 0x00a0U )
05051         return TRUE;
05052     if ( c.unicode() == 0xad ) // soft hyphen
05053         return TRUE;
05054     if ( !ch ) {
05055         // not latin1, need to do more sophisticated checks for other scripts
05056         uchar row = c.row();
05057         if ( row == 0x0e ) {
05058             // 0e00 - 0e7f == Thai
05059             if ( c.cell() < 0x80 ) {
05060 #ifdef HAVE_THAI_BREAKS
05061                 // check for thai
05062                 if( string != cachedString ) {
05063                     // build up string of thai chars
05064                     QTextCodec *thaiCodec = QTextCodec::codecForMib(2259);
05065                     if ( !thaiCache )
05066                         thaiCache = new QCString;
05067                     if ( !thaiIt )
05068                         thaiIt = ThBreakIterator::createWordInstance();
05069                     *thaiCache = thaiCodec->fromUnicode( s->string() );
05070                 }
05071                 thaiIt->setText(thaiCache->data());
05072                 for(int i = thaiIt->first(); i != thaiIt->DONE; i = thaiIt->next() ) {
05073                     if( i == pos )
05074                         return TRUE;
05075                     if( i > pos )
05076                         return FALSE;
05077                 }
05078                 return FALSE;
05079 #else
05080                 // if we don't have a thai line breaking lib, allow
05081                 // breaks everywhere except directly before punctuation.
05082                 return TRUE;
05083 #endif
05084             } else
05085                 return FALSE;
05086         }
05087         if ( row < 0x11 ) // no asian font
05088             return FALSE;
05089         if ( row > 0x2d && row < 0xfb || row == 0x11 )
05090             // asian line breaking. Everywhere allowed except directly
05091             // in front of a punctuation character.
05092             return TRUE;
05093     }
05094     return FALSE;
05095 }
05096 
05097 void QTextFormatter::insertLineStart( QTextParagraph *parag, int index, QTextLineStart *ls )
05098 {
05099     if ( index > 0 ) { // we can assume that only first line starts are insrted multiple times
05100         parag->lineStartList().insert( index, ls );
05101         return;
05102     }
05103     QMap<int, QTextLineStart*>::Iterator it;
05104     if ( ( it = parag->lineStartList().find( index ) ) == parag->lineStartList().end() ) {
05105         parag->lineStartList().insert( index, ls );
05106     } else {
05107         delete *it;
05108         parag->lineStartList().remove( it );
05109         parag->lineStartList().insert( index, ls );
05110     }
05111 }
05112 
05113 
05114 /* Standard pagebreak algorithm using QTextFlow::adjustFlow. Returns
05115  the shift of the paragraphs bottom line.
05116  */
05117 int QTextFormatter::formatVertically( QTextDocument* doc, QTextParagraph* parag )
05118 {
05119     int oldHeight = parag->rect().height();
05120     QMap<int, QTextLineStart*>& lineStarts = parag->lineStartList();
05121     QMap<int, QTextLineStart*>::Iterator it = lineStarts.begin();
05122     int h = parag->prev() ? QMAX(parag->prev()->bottomMargin(),parag->topMargin() ) / 2: 0;
05123     for ( ; it != lineStarts.end() ; ++it  ) {
05124         QTextLineStart * ls = it.data();
05125         ls->y = h;
05126         QTextStringChar *c = &parag->string()->at(it.key());
05127         if ( c && c->customItem() && c->customItem()->ownLine() ) {
05128             int h = c->customItem()->height;
05129             c->customItem()->pageBreak( parag->rect().y() + ls->y + ls->baseLine - h, doc->flow() );
05130             int delta = c->customItem()->height - h;
05131             ls->h += delta;
05132             if ( delta )
05133                 parag->setMovedDown( TRUE );
05134         } else {
05135             int shift = doc->flow()->adjustFlow( parag->rect().y() + ls->y, ls->w, ls->h );
05136             ls->y += shift;
05137             if ( shift )
05138                 parag->setMovedDown( TRUE );
05139         }
05140         h = ls->y + ls->h;
05141     }
05142     int m = parag->bottomMargin();
05143     if ( !parag->next() )
05144         m = 0;
05145     else
05146         m = QMAX(m, parag->next()->topMargin() ) / 2;
05147     h += m;
05148     parag->setHeight( h );
05149     return h - oldHeight;
05150 }
05151 
05152 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
05153 
05154 QTextFormatterBreakInWords::QTextFormatterBreakInWords()
05155 {
05156 }
05157 
05158 #define SPACE(s) doc?(s>0?s:0):s
05159 
05160 int QTextFormatterBreakInWords::format( QTextDocument *doc,QTextParagraph *parag,
05161                                         int start, const QMap<int, QTextLineStart*> & )
05162 {
05163     QTextStringChar *c = 0;
05164     QTextStringChar *firstChar = 0;
05165     int left = doc ? parag->leftMargin() + doc->leftMargin() : 0;
05166     int x = left + ( doc ? parag->firstLineMargin() : 0 );
05167     int dw = parag->documentVisibleWidth() - ( doc ? doc->rightMargin() : 0 );
05168     int y = parag->prev() ? QMAX(parag->prev()->bottomMargin(),parag->topMargin()) / 2: 0;
05169     int h = y;
05170     int len = parag->length();
05171     if ( doc )
05172         x = doc->flow()->adjustLMargin( y + parag->rect().y(), parag->rect().height(), x, 4 );
05173     int rm = parag->rightMargin();
05174     int w = dw - ( doc ? doc->flow()->adjustRMargin( y + parag->rect().y(), parag->rect().height(), rm, 4 ) : 0 );
05175     bool fullWidth = TRUE;
05176     int minw = 0;
05177     int wused = 0;
05178     bool wrapEnabled = isWrapEnabled( parag );
05179 
05180     start = 0;    //######### what is the point with start?! (Matthias)
05181     if ( start == 0 )
05182         c = &parag->string()->at( 0 );
05183 
05184     int i = start;
05185     QTextLineStart *lineStart = new QTextLineStart( y, y, 0 );
05186     insertLineStart( parag, 0, lineStart );
05187 
05188     QPainter *painter = QTextFormat::painter();
05189 
05190     int col = 0;
05191     int ww = 0;
05192     QChar lastChr;
05193     for ( ; i < len; ++i, ++col ) {
05194         if ( c )
05195             lastChr = c->c;
05196         c = &parag->string()->at( i );
05197         c->rightToLeft = FALSE;
05198         // ### the lines below should not be needed
05199         if ( painter )
05200             c->format()->setPainter( painter );
05201         if ( i > 0 ) {
05202             c->lineStart = 0;
05203         } else {
05204             c->lineStart = 1;
05205             firstChar = c;
05206         }
05207         if ( c->c.unicode() >= 32 || c->isCustom() ) {
05208             ww = parag->string()->width( i );
05209         } else if ( c->c == '\t' ) {
05210             int nx = parag->nextTab( i, x - left ) + left;
05211             if ( nx < x )
05212                 ww = w - x;
05213             else
05214                 ww = nx - x;
05215         } else {
05216             ww = c->format()->width( ' ' );
05217         }
05218 
05219         if ( c->isCustom() && c->customItem()->ownLine() ) {
05220             x = doc ? doc->flow()->adjustLMargin( y + parag->rect().y(), parag->rect().height(), left, 4 ) : left;
05221             w = dw - ( doc ? doc->flow()->adjustRMargin( y + parag->rect().y(), parag->rect().height(), rm, 4 ) : 0 );
05222             c->customItem()->resize( w - x );
05223             w = dw;
05224             y += h;
05225             h = c->height();
05226             lineStart = new QTextLineStart( y, h, h );
05227             insertLineStart( parag, i, lineStart );
05228             c->lineStart = 1;
05229             firstChar = c;
05230             x = 0xffffff;
05231             continue;
05232         }
05233 
05234         if ( wrapEnabled &&
05235              ( wrapAtColumn() == -1 && x + ww > w ||
05236                wrapAtColumn() != -1 && col >= wrapAtColumn() ) ) {
05237             x = doc ? parag->document()->flow()->adjustLMargin( y + parag->rect().y(), parag->rect().height(), left, 4 ) : left;
05238             w = dw;
05239             y += h;
05240             h = c->height();
05241             lineStart = formatLine( parag, parag->string(), lineStart, firstChar, SPACE(c-1) );
05242             lineStart->y = y;
05243             insertLineStart( parag, i, lineStart );
05244             lineStart->baseLine = c->ascent();
05245             lineStart->h = c->height();
05246             c->lineStart = 1;
05247             firstChar = c;
05248             col = 0;
05249             if ( wrapAtColumn() != -1 )
05250                 minw = QMAX( minw, w );
05251         } else if ( lineStart ) {
05252             lineStart->baseLine = QMAX( lineStart->baseLine, c->ascent() );
05253             h = QMAX( h, c->height() );
05254             lineStart->h = h;
05255         }
05256 
05257         c->x = x;
05258         x += ww;
05259         wused = QMAX( wused, x );
05260     }
05261 
05262     int m = parag->bottomMargin();
05263     if ( !parag->next() )
05264         m = 0;
05265     else
05266         m = QMAX(m, parag->next()->topMargin() ) / 2;
05267     parag->setFullWidth( fullWidth );
05268     y += h + m;
05269     if ( doc )
05270         minw += doc->rightMargin();
05271     if ( !wrapEnabled )
05272         minw = QMAX(minw, wused);
05273 
05274     thisminw = minw;
05275     thiswused = wused;
05276     return y;
05277 }
05278 
05279 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
05280 
05281 QTextFormatterBreakWords::QTextFormatterBreakWords()
05282 {
05283 }
05284 
05285 #define DO_FLOW( lineStart ) do{ if ( doc && doc->isPageBreakEnabled() ) { \
05286                     int yflow = lineStart->y + parag->rect().y();\
05287                     int shift = doc->flow()->adjustFlow( yflow, dw, lineStart->h ); \
05288                     lineStart->y += shift;\
05289                     y += shift;\
05290                 }}while(FALSE)
05291 
05292 int QTextFormatterBreakWords::format( QTextDocument *doc, QTextParagraph *parag,
05293                                       int start, const QMap<int, QTextLineStart*> & )
05294 {
05295     QTextStringChar *c = 0;
05296     QTextStringChar *firstChar = 0;
05297     QTextString *string = parag->string();
05298     int left = doc ? parag->leftMargin() + doc->leftMargin() : 0;
05299     int x = left + ( doc ? parag->firstLineMargin() : 0 );
05300     int y = parag->prev() ? QMAX(parag->prev()->bottomMargin(),parag->topMargin()) / 2: 0;
05301     int h = y;
05302     int len = parag->length();
05303     if ( doc )
05304         x = doc->flow()->adjustLMargin( y + parag->rect().y(), parag->rect().height(), x, 0 );
05305     int dw = parag->documentVisibleWidth() - ( doc ? ( left != x ? 0 : doc->rightMargin() ) : 0 );
05306 
05307     int curLeft = x;
05308     int rm = parag->rightMargin();
05309     int rdiff = doc ? doc->flow()->adjustRMargin( y + parag->rect().y(), parag->rect().height(), rm, 0 ) : 0;
05310     int w = dw - rdiff;
05311     bool fullWidth = TRUE;
05312     int marg = left + rdiff;
05313     int minw = 0;
05314     int wused = 0;
05315     int tminw = marg;
05316     int linespacing = doc ? parag->lineSpacing() : 0;
05317     bool wrapEnabled = isWrapEnabled( parag );
05318 
05319     start = 0;
05320     if ( start == 0 )
05321         c = &parag->string()->at( 0 );
05322 
05323     int i = start;
05324     QTextLineStart *lineStart = new QTextLineStart( y, y, 0 );
05325     insertLineStart( parag, 0, lineStart );
05326     int lastBreak = -1;
05327     int tmpBaseLine = 0, tmph = 0;
05328     bool lastWasNonInlineCustom = FALSE;
05329 
05330     int align = parag->alignment();
05331     if ( align == Qt3::AlignAuto && doc && doc->alignment() != Qt3::AlignAuto )
05332         align = doc->alignment();
05333 
05334     align &= Qt3::AlignHorizontal_Mask;
05335 
05336     QPainter *painter = QTextFormat::painter();
05337     int col = 0;
05338     int ww = 0;
05339     QChar lastChr;
05340     for ( ; i < len; ++i, ++col ) {
05341         if ( c )
05342             lastChr = c->c;
05343         // ### next line should not be needed
05344         if ( painter )
05345             c->format()->setPainter( painter );
05346         c = &string->at( i );
05347         c->rightToLeft = FALSE;
05348         if ( i > 0 && (x > curLeft || ww == 0) || lastWasNonInlineCustom ) {
05349             c->lineStart = 0;
05350         } else {
05351             c->lineStart = 1;
05352             firstChar = c;
05353         }
05354 
05355         if ( c->isCustom() && c->customItem()->placement() != QTextCustomItem::PlaceInline )
05356             lastWasNonInlineCustom = TRUE;
05357         else
05358             lastWasNonInlineCustom = FALSE;
05359 
05360         if ( c->c.unicode() >= 32 || c->isCustom() ) {
05361             ww = string->width( i );
05362         } else if ( c->c == '\t' ) {
05363             int nx = parag->nextTab( i, x - left ) + left;
05364             if ( nx < x )
05365                 ww = w - x;
05366             else
05367                 ww = nx - x;
05368         } else {
05369             ww = c->format()->width( ' ' );
05370         }
05371 
05372         // last character ("invisible" space) has no width
05373         if ( i == len - 1 )
05374             ww = 0;
05375 
05376         QTextCustomItem* ci = c->customItem();
05377         if ( c->isCustom() && ci->ownLine() ) {
05378             x = doc ? doc->flow()->adjustLMargin( y + parag->rect().y(), parag->rect().height(), left, 4 ) : left;
05379             w = dw - ( doc ? doc->flow()->adjustRMargin( y + parag->rect().y(), parag->rect().height(), rm, 4 ) : 0 );
05380             QTextLineStart *lineStart2 = formatLine( parag, string, lineStart, firstChar, c-1, align, SPACE(w - x) );
05381             ci->resize( w - x);
05382             if ( ci->width < w - x ) {
05383                 if ( align & Qt::AlignHCenter )
05384                     x = ( w - ci->width ) / 2;
05385                 else if ( align & Qt::AlignRight ) {
05386                     x = w - ci->width;
05387                 }
05388             }
05389             c->x = x;
05390             curLeft = x;
05391             if ( i == 0 || !isBreakable( string, i - 1 ) || string->at( i - 1 ).lineStart == 0 ) {
05392                 y += QMAX( h, QMAX( tmph, linespacing ) );
05393                 tmph = c->height();
05394                 h = tmph;
05395                 lineStart = lineStart2;
05396                 lineStart->y = y;
05397                 insertLineStart( parag, i, lineStart );
05398                 c->lineStart = 1;
05399                 firstChar = c;
05400             } else {
05401                 tmph = c->height();
05402                 h = tmph;
05403                 delete lineStart2;
05404             }
05405             lineStart->h = h;
05406             lineStart->baseLine = h;
05407             tmpBaseLine = lineStart->baseLine;
05408             lastBreak = -2;
05409             x = 0xffffff;
05410             minw = QMAX( minw, tminw );
05411 
05412             int tw = ci->minimumWidth() + ( doc ? doc->leftMargin() : 0 );
05413             if ( tw < QWIDGETSIZE_MAX )
05414                 tminw = tw;
05415             else
05416                 tminw = marg;
05417             wused = QMAX( wused, ci->width );
05418             continue;
05419         } else if ( c->isCustom() && ci->placement() != QTextCustomItem::PlaceInline ) {
05420             int tw = ci->minimumWidth();
05421             if ( tw < QWIDGETSIZE_MAX )
05422                 minw = QMAX( minw, tw );
05423         }
05424 
05425         bool lastWasOwnLineCustomItem = lastBreak == -2;
05426         bool hadBreakableChar = lastBreak != -1;
05427         bool lastWasHardBreak = lastChr == QChar_linesep;
05428 
05429         // we break if
05430         // 1. the last character was a hard break (QChar_linesep) or
05431         // 2. the last charater was a own-line custom item (eg. table or ruler) or
05432         // 3. wrapping was enabled, it was not a space and following
05433         // condition is true: We either had a breakable character
05434         // previously or we ar allowed to break in words and - either
05435         // we break at w pixels and the current char would exceed that
05436         // or - we break at a column and the current character would
05437         // exceed that.
05438         if ( lastWasHardBreak || lastWasOwnLineCustomItem ||
05439              ( wrapEnabled &&
05440                ( (!c->c.isSpace() && (hadBreakableChar || allowBreakInWords()) &&
05441                   ( (wrapAtColumn() == -1 && x + ww > w) ||
05442                     (wrapAtColumn() != -1 && col >= wrapAtColumn()) ) ) )
05443                )
05444              ) {
05445             if ( wrapAtColumn() != -1 )
05446                 minw = QMAX( minw, x + ww );
05447             // if a break was forced (no breakable char, hard break or own line custom item), break immediately....
05448             if ( !hadBreakableChar || lastWasHardBreak || lastWasOwnLineCustomItem ) {
05449                 if ( lineStart ) {
05450                     lineStart->baseLine = QMAX( lineStart->baseLine, tmpBaseLine );
05451                     h = QMAX( h, tmph );
05452                     lineStart->h = h;
05453                     DO_FLOW( lineStart );
05454                 }
05455                 lineStart = formatLine( parag, string, lineStart, firstChar, c-1, align, SPACE(w - x) );
05456                 x = doc ? doc->flow()->adjustLMargin( y + parag->rect().y(), parag->rect().height(), left, 4 ) : left;
05457                 w = dw - ( doc ? doc->flow()->adjustRMargin( y + parag->rect().y(), parag->rect().height(), rm, 4 ) : 0 );
05458                 if ( !doc && c->c == '\t' ) { // qt_format_text tab handling
05459                     int nx = parag->nextTab( i, x - left ) + left;
05460                     if ( nx < x )
05461                         ww = w - x;
05462                     else
05463                         ww = nx - x;
05464                 }
05465                 curLeft = x;
05466                 y += QMAX( h, linespacing );
05467                 tmph = c->height();
05468                 h = 0;
05469                 lineStart->y = y;
05470                 insertLineStart( parag, i, lineStart );
05471                 lineStart->baseLine = c->ascent();
05472                 lineStart->h = c->height();
05473                 c->lineStart = 1;
05474                 firstChar = c;
05475                 tmpBaseLine = lineStart->baseLine;
05476                 lastBreak = -1;
05477                 col = 0;
05478             } else { // ... otherwise if we had a breakable char, break there
05479                 DO_FLOW( lineStart );
05480                 i = lastBreak;
05481                 lineStart = formatLine( parag, string, lineStart, firstChar, parag->at( lastBreak ),align, SPACE(w - string->at( i ).x) );
05482                 x = doc ? doc->flow()->adjustLMargin( y + parag->rect().y(), parag->rect().height(), left, 4 ) : left;
05483                 w = dw - ( doc ? doc->flow()->adjustRMargin( y + parag->rect().y(), parag->rect().height(), rm, 4 ) : 0 );
05484                 if ( !doc && c->c == '\t' ) { // qt_format_text tab handling
05485                     int nx = parag->nextTab( i, x - left ) + left;
05486                     if ( nx < x )
05487                         ww = w - x;
05488                     else
05489                         ww = nx - x;
05490                 }
05491                 curLeft = x;
05492                 y += QMAX( h, linespacing );
05493                 tmph = c->height();
05494                 h = tmph;
05495                 lineStart->y = y;
05496                 insertLineStart( parag, i + 1, lineStart );
05497                 lineStart->baseLine = c->ascent();
05498                 lineStart->h = c->height();
05499                 c->lineStart = 1;
05500                 firstChar = c;
05501                 tmpBaseLine = lineStart->baseLine;
05502                 lastBreak = -1;
05503                 col = 0;
05504                 tminw = marg;
05505                 continue;
05506             }
05507         } else if ( lineStart && isBreakable( string, i ) ) {
05508             if ( len <= 2 || i < len - 1 ) {
05509                 tmpBaseLine = QMAX( tmpBaseLine, c->ascent() );
05510                 tmph = QMAX( tmph, c->height() );
05511             }
05512             minw = QMAX( minw, tminw );
05513             tminw = marg + ww;
05514             lineStart->baseLine = QMAX( lineStart->baseLine, tmpBaseLine );
05515             h = QMAX( h, tmph );
05516             lineStart->h = h;
05517             if ( i < len - 2 || c->c != ' ' )
05518                 lastBreak = i;
05519         } else {
05520             tminw += ww;
05521             int belowBaseLine = QMAX( tmph - tmpBaseLine, c->height()- c->ascent() );
05522             tmpBaseLine = QMAX( tmpBaseLine, c->ascent() );
05523             tmph = tmpBaseLine + belowBaseLine;
05524         }
05525 
05526         c->x = x;
05527         x += ww;
05528         wused = QMAX( wused, x );
05529     }
05530 
05531     // ### hack. The last char in the paragraph is always invisible,
05532     // ### and somehow sometimes has a wrong format. It changes
05533     // ### between // layouting and printing. This corrects some
05534     // ### layouting errors in BiDi mode due to this.
05535     if ( len > 1 && !c->isAnchor() ) {
05536         c->format()->removeRef();
05537         c->setFormat( string->at( len - 2 ).format() );
05538         c->format()->addRef();
05539     }
05540 
05541     if ( lineStart ) {
05542         lineStart->baseLine = QMAX( lineStart->baseLine, tmpBaseLine );
05543         h = QMAX( h, tmph );
05544         lineStart->h = h;
05545         // last line in a paragraph is not justified
05546         if ( align == Qt3::AlignJustify || lastChr == QChar_linesep )
05547             align = Qt3::AlignAuto;
05548         DO_FLOW( lineStart );
05549         lineStart = formatLine( parag, string, lineStart, firstChar, c, align, SPACE(w - x) );
05550         delete lineStart;
05551     }
05552 
05553     minw = QMAX( minw, tminw );
05554     if ( doc )
05555         minw += doc->rightMargin();
05556 
05557     int m = parag->bottomMargin();
05558     if ( !parag->next() )
05559         m = 0;
05560     else
05561         m = QMAX(m, parag->next()->topMargin() ) / 2;
05562     parag->setFullWidth( fullWidth );
05563     y += QMAX( h, linespacing ) + m;
05564 
05565     wused += rm;
05566     if ( !wrapEnabled || wrapAtColumn() != -1 )
05567         minw = QMAX(minw, wused);
05568 
05569     // This is the case where we are breaking wherever we darn well please
05570     // in cases like that, the minw should not be the length of the entire
05571     // word, because we necessarily want to show the word on the whole line.
05572     // example: word wrap in iconview
05573     if ( allowBreakInWords() && minw > wused )
05574         minw = wused;
05575 
05576     thisminw = minw;
05577     thiswused = wused;
05578     return y;
05579 }
05580 
05581 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
05582 
05583 QTextIndent::QTextIndent()
05584 {
05585 }
05586 
05587 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
05588 
05589 QTextFormatCollection::QTextFormatCollection()
05590     : cKey( 307 )
05591 {
05592     defFormat = new QTextFormat( QApplication::font(),
05593                                  QApplication::palette().color( QPalette::Active, QColorGroup::Text ) );
05594     lastFormat = cres = 0;
05595     cflags = -1;
05596     cKey.setAutoDelete( TRUE );
05597     cachedFormat = 0;
05598 }
05599 
05600 QTextFormatCollection::~QTextFormatCollection()
05601 {
05602     delete defFormat;
05603 }
05604 
05605 QTextFormat *QTextFormatCollection::format( QTextFormat *f )
05606 {
05607     if ( f->parent() == this || f == defFormat ) {
05608         lastFormat = f;
05609         lastFormat->addRef();
05610         return lastFormat;
05611     }
05612 
05613     if ( f == lastFormat || ( lastFormat && f->key() == lastFormat->key() ) ) {
05614         lastFormat->addRef();
05615         return lastFormat;
05616     }
05617 
05618     QTextFormat *fm = cKey.find( f->key() );
05619     if ( fm ) {
05620         lastFormat = fm;
05621         lastFormat->addRef();
05622         return lastFormat;
05623     }
05624 
05625     if ( f->key() == defFormat->key() )
05626         return defFormat;
05627 
05628     lastFormat = createFormat( *f );
05629     lastFormat->collection = this;
05630     cKey.insert( lastFormat->key(), lastFormat );
05631     return lastFormat;
05632 }
05633 
05634 QTextFormat *QTextFormatCollection::format( QTextFormat *of, QTextFormat *nf, int flags )
05635 {
05636     if ( cres && kof == of->key() && knf == nf->key() && cflags == flags ) {
05637         cres->addRef();
05638         return cres;
05639     }
05640 
05641     cres = createFormat( *of );
05642     kof = of->key();
05643     knf = nf->key();
05644     cflags = flags;
05645     if ( flags & QTextFormat::Bold )
05646         cres->fn.setBold( nf->fn.bold() );
05647     if ( flags & QTextFormat::Italic )
05648         cres->fn.setItalic( nf->fn.italic() );
05649     if ( flags & QTextFormat::Underline )
05650         cres->fn.setUnderline( nf->fn.underline() );
05651     if ( flags & QTextFormat::StrikeOut )
05652         cres->fn.setStrikeOut( nf->fn.strikeOut() );
05653     if ( flags & QTextFormat::Family )
05654         cres->fn.setFamily( nf->fn.family() );
05655     if ( flags & QTextFormat::Size ) {
05656         if ( of->usePixelSizes )
05657             cres->fn.setPixelSize( nf->fn.pixelSize() );
05658         else
05659             cres->fn.setPointSize( nf->fn.pointSize() );
05660     }
05661     if ( flags & QTextFormat::Color )
05662         cres->col = nf->col;
05663     if ( flags & QTextFormat::Misspelled )
05664         cres->missp = nf->missp;
05665     if ( flags & QTextFormat::VAlign )
05666         cres->ha = nf->ha;
05667     cres->update();
05668 
05669     QTextFormat *fm = cKey.find( cres->key() );
05670     if ( !fm ) {
05671         cres->collection = this;
05672         cKey.insert( cres->key(), cres );
05673     } else {
05674         delete cres;
05675         cres = fm;
05676         cres->addRef();
05677     }
05678 
05679     return cres;
05680 }
05681 
05682 QTextFormat *QTextFormatCollection::format( const QFont &f, const QColor &c )
05683 {
05684     if ( cachedFormat && cfont == f && ccol == c ) {
05685         cachedFormat->addRef();
05686         return cachedFormat;
05687     }
05688 
05689     QString key = QTextFormat::getKey( f, c, FALSE,  QTextFormat::AlignNormal );
05690     cachedFormat = cKey.find( key );
05691     cfont = f;
05692     ccol = c;
05693 
05694     if ( cachedFormat ) {
05695         cachedFormat->addRef();
05696         return cachedFormat;
05697     }
05698 
05699     if ( key == defFormat->key() )
05700         return defFormat;
05701 
05702     cachedFormat = createFormat( f, c );
05703     cachedFormat->collection = this;
05704     cKey.insert( cachedFormat->key(), cachedFormat );
05705     if ( cachedFormat->key() != key )
05706         owarn << "ASSERT: keys for format not identical: '" << cachedFormat->key().latin1() << " '" << key.latin1() << "'" << oendl; 
05707     return cachedFormat;
05708 }
05709 
05710 void QTextFormatCollection::remove( QTextFormat *f )
05711 {
05712     if ( lastFormat == f )
05713         lastFormat = 0;
05714     if ( cres == f )
05715         cres = 0;
05716     if ( cachedFormat == f )
05717         cachedFormat = 0;
05718     cKey.remove( f->key() );
05719 }
05720 
05721 #define UPDATE( up, lo, rest ) \
05722         if ( font.lo##rest() != defFormat->fn.lo##rest() && fm->fn.lo##rest() == defFormat->fn.lo##rest() ) \
05723             fm->fn.set##up##rest( font.lo##rest() )
05724 
05725 void QTextFormatCollection::updateDefaultFormat( const QFont &font, const QColor &color, QStyleSheet *sheet )
05726 {
05727     QDictIterator<QTextFormat> it( cKey );
05728     QTextFormat *fm;
05729     bool usePixels = font.pointSize() == -1;
05730     bool changeSize = usePixels ? font.pixelSize() != defFormat->fn.pixelSize() :
05731         font.pointSize() != defFormat->fn.pointSize();
05732     int base = usePixels ? font.pixelSize() : font.pointSize();
05733     while ( ( fm = it.current() ) ) {
05734         ++it;
05735         UPDATE( F, f, amily );
05736         UPDATE( W, w, eight );
05737         UPDATE( B, b, old );
05738         UPDATE( I, i, talic );
05739         UPDATE( U, u, nderline );
05740         if ( changeSize ) {
05741             fm->stdSize = base;
05742             fm->usePixelSizes = usePixels;
05743             if ( usePixels )
05744                 fm->fn.setPixelSize( fm->stdSize );
05745             else
05746                 fm->fn.setPointSize( fm->stdSize );
05747             sheet->scaleFont( fm->fn, fm->logicalFontSize );
05748         }
05749         if ( color.isValid() && color != defFormat->col && fm->col == defFormat->col )
05750             fm->col = color;
05751         fm->update();
05752     }
05753 
05754     defFormat->fn = font;
05755     defFormat->col = color;
05756     defFormat->update();
05757     defFormat->stdSize = base;
05758     defFormat->usePixelSizes = usePixels;
05759 
05760     updateKeys();
05761 }
05762 
05763 // the keys in cKey have changed, rebuild the hashtable
05764 void QTextFormatCollection::updateKeys()
05765 {
05766     if ( cKey.isEmpty() )
05767         return;
05768     cKey.setAutoDelete( FALSE );
05769     QTextFormat** formats = new QTextFormat*[ cKey.count() + 1 ];
05770     QTextFormat **f = formats;
05771     QDictIterator<QTextFormat> it( cKey );
05772     while ( ( *f = it.current() ) ) {
05773        ++it;
05774        ++f;
05775     }
05776     cKey.clear();
05777     for ( f = formats; *f; f++ )
05778        cKey.insert( (*f)->key(), *f );
05779     cKey.setAutoDelete( TRUE );
05780     delete [] formats;
05781 }
05782 
05783 
05784 
05785 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
05786 
05787 void QTextFormat::setBold( bool b )
05788 {
05789     if ( b == fn.bold() )
05790         return;
05791     fn.setBold( b );
05792     update();
05793 }
05794 
05795 void QTextFormat::setMisspelled( bool b )
05796 {
05797     if ( b == (bool)missp )
05798         return;
05799     missp = b;
05800     update();
05801 }
05802 
05803 void QTextFormat::setVAlign( VerticalAlignment a )
05804 {
05805     if ( a == ha )
05806         return;
05807     ha = a;
05808     update();
05809 }
05810 
05811 void QTextFormat::setItalic( bool b )
05812 {
05813     if ( b == fn.italic() )
05814         return;
05815     fn.setItalic( b );
05816     update();
05817 }
05818 
05819 void QTextFormat::setUnderline( bool b )
05820 {
05821     if ( b == fn.underline() )
05822         return;
05823     fn.setUnderline( b );
05824     update();
05825 }
05826 
05827 void QTextFormat::setStrikeOut( bool b )
05828 {
05829     if ( b == fn.strikeOut() )
05830         return;
05831     fn.setStrikeOut( b );
05832     update();
05833 }
05834 
05835 void QTextFormat::setFamily( const QString &f )
05836 {
05837     if ( f == fn.family() )
05838         return;
05839     fn.setFamily( f );
05840     update();
05841 }
05842 
05843 void QTextFormat::setPointSize( int s )
05844 {
05845     if ( s == fn.pointSize() )
05846         return;
05847     fn.setPointSize( s );
05848     usePixelSizes = FALSE;
05849     update();
05850 }
05851 
05852 void QTextFormat::setFont( const QFont &f )
05853 {
05854     if ( f == fn && !k.isEmpty() )
05855         return;
05856     fn = f;
05857     update();
05858 }
05859 
05860 void QTextFormat::setColor( const QColor &c )
05861 {
05862     if ( c == col )
05863         return;
05864     col = c;
05865     update();
05866 }
05867 
05868 QString QTextFormat::makeFormatChangeTags( QTextFormat* defaultFormat, QTextFormat *f,
05869                            const QString& oldAnchorHref, const QString& anchorHref  ) const
05870 {
05871     QString tag;
05872     if ( f )
05873         tag += f->makeFormatEndTags( defaultFormat, oldAnchorHref );
05874 
05875     if ( !anchorHref.isEmpty() )
05876         tag += "<a href=\"" + anchorHref + "\">";
05877 
05878     if ( font() != defaultFormat->font()
05879          || vAlign() != defaultFormat->vAlign()
05880          || color().rgb() != defaultFormat->color().rgb() ) {
05881         QString s;
05882         if ( font().family() != defaultFormat->font().family() )
05883             s += QString(!!s?";":"") + "font-family:" + fn.family();
05884         if ( font().italic() && font().italic() != defaultFormat->font().italic() )
05885             s += QString(!!s?";":"") + "font-style:" + (font().italic() ? "italic" : "normal");
05886         if ( font().pointSize() != defaultFormat->font().pointSize() )
05887             s += QString(!!s?";":"") + "font-size:" + QString::number( fn.pointSize() ) + "pt";
05888         if ( font().weight() != defaultFormat->font().weight() )
05889             s += QString(!!s?";":"") + "font-weight:" + QString::number( fn.weight() * 8 );
05890         if ( font().underline() != defaultFormat->font().underline() )
05891             s += QString(!!s?";":"") + "text-decoration:" + ( font().underline() ? "underline" : "none");
05892         if ( vAlign() != defaultFormat->vAlign() ) {
05893             s += QString(!!s?";":"") + "vertical-align:";
05894             if ( vAlign() == QTextFormat::AlignSuperScript )
05895                 s += "super";
05896             else if ( vAlign() == QTextFormat::AlignSubScript )
05897                 s += "sub";
05898             else
05899                 s += "normal";
05900         }
05901         if ( color().rgb() != defaultFormat->color().rgb() )
05902             s += QString(!!s?";":"") + "color:" + col.name();
05903         if ( !s.isEmpty() )
05904             tag += "<span style=\"" + s + "\">";
05905     }
05906 
05907     return tag;
05908 }
05909 
05910 QString QTextFormat::makeFormatEndTags( QTextFormat* defaultFormat, const QString& anchorHref ) const
05911 {
05912     QString tag;
05913     if ( font().family() != defaultFormat->font().family()
05914          || font().pointSize() != defaultFormat->font().pointSize()
05915          || font().weight() != defaultFormat->font().weight()
05916          || font().italic() != defaultFormat->font().italic()
05917          || font().underline() != defaultFormat->font().underline()
05918          || font().strikeOut() != defaultFormat->font().strikeOut()
05919          || vAlign() != defaultFormat->vAlign()
05920          || color().rgb() != defaultFormat->color().rgb() )
05921         tag += "</span>";
05922     if ( !anchorHref.isEmpty() )
05923         tag += "</a>";
05924     return tag;
05925 }
05926 
05927 QTextFormat QTextFormat::makeTextFormat( const QStyleSheetItem *style, const QMap<QString,QString>& attr, double scaleFontsFactor ) const
05928 {
05929     QTextFormat format(*this);
05930     if (!style )
05931         return format;
05932 
05933     if ( !style->isAnchor() && style->color().isValid() ) {
05934         // the style is not an anchor and defines a color.
05935         // It might be used inside an anchor and it should
05936         // override the link color.
05937         format.linkColor = FALSE;
05938     }
05939     switch ( style->verticalAlignment() ) {
05940     case QStyleSheetItem::VAlignBaseline:
05941         format.setVAlign( QTextFormat::AlignNormal );
05942         break;
05943     case QStyleSheetItem::VAlignSuper:
05944         format.setVAlign( QTextFormat::AlignSuperScript );
05945         break;
05946     case QStyleSheetItem::VAlignSub:
05947         format.setVAlign( QTextFormat::AlignSubScript );
05948         break;
05949     }
05950 
05951     if ( style->fontWeight() != QStyleSheetItem::Undefined )
05952         format.fn.setWeight( style->fontWeight() );
05953     if ( style->fontSize() != QStyleSheetItem::Undefined ) {
05954         format.fn.setPointSize( style->fontSize() );
05955     } else if ( style->logicalFontSize() != QStyleSheetItem::Undefined ) {
05956         format.logicalFontSize = style->logicalFontSize();
05957         if ( format.usePixelSizes )
05958             format.fn.setPixelSize( format.stdSize );
05959         else
05960             format.fn.setPointSize( format.stdSize );
05961         style->styleSheet()->scaleFont( format.fn, format.logicalFontSize );
05962     } else if ( style->logicalFontSizeStep() ) {
05963         format.logicalFontSize += style->logicalFontSizeStep();
05964         if ( format.usePixelSizes )
05965             format.fn.setPixelSize( format.stdSize );
05966         else
05967             format.fn.setPointSize( format.stdSize );
05968         style->styleSheet()->scaleFont( format.fn, format.logicalFontSize );
05969     }
05970     if ( !style->fontFamily().isEmpty() )
05971         format.fn.setFamily( style->fontFamily() );
05972     if ( style->color().isValid() )
05973         format.col = style->color();
05974     if ( style->definesFontItalic() )
05975         format.fn.setItalic( style->fontItalic() );
05976     if ( style->definesFontUnderline() )
05977         format.fn.setUnderline( style->fontUnderline() );
05978     if ( style->definesFontStrikeOut() )
05979         format.fn.setStrikeOut( style->fontStrikeOut() );
05980 
05981 
05982     if ( style->name() == "font") {
05983         if ( attr.contains("color") ) {
05984             QString s = attr["color"];
05985             if ( !s.isEmpty() ) {
05986                 format.col.setNamedColor( s );
05987                 format.linkColor = FALSE;
05988             }
05989         }
05990         if ( attr.contains("face") ) {
05991             QString a = attr["face"];
05992             QString family = QTextDocument::section( a, ",", 0, 0 );
05993             if ( !!family )
05994                 format.fn.setFamily( family );
05995         }
05996         if ( attr.contains("size") ) {
05997             QString a = attr["size"];
05998             int n = a.toInt();
05999             if ( a[0] == '+' || a[0] == '-' )
06000                 n += format.logicalFontSize;
06001             format.logicalFontSize = n;
06002             if ( format.usePixelSizes )
06003                 format.fn.setPixelSize( format.stdSize );
06004             else
06005                 format.fn.setPointSize( format.stdSize );
06006             style->styleSheet()->scaleFont( format.fn, format.logicalFontSize );
06007         }
06008     }
06009     if ( attr.contains("style" ) ) {
06010         QString a = attr["style"];
06011         for ( int s = 0; s < a.contains(';')+1; s++ ) {
06012             QString style = QTextDocument::section( a, ";", s, s );
06013             if ( style.startsWith("font-size:" ) && QTextDocument::endsWith(style, "pt") ) {
06014                 format.logicalFontSize = 0;
06015                 format.setPointSize( int( scaleFontsFactor * style.mid( 10, style.length() - 12 ).toInt() ) );
06016             } if ( style.startsWith("font-style:" ) ) {
06017                 QString s = style.mid( 11 ).stripWhiteSpace();
06018                 if ( s == "normal" )
06019                     format.fn.setItalic( FALSE );
06020                 else if ( s == "italic" || s == "oblique" )
06021                     format.fn.setItalic( TRUE );
06022             } else if ( style.startsWith("font-weight:" ) ) {
06023                 QString s = style.mid( 12 );
06024                 bool ok = TRUE;
06025                 int n = s.toInt( &ok );
06026                 if ( ok )
06027                     format.fn.setWeight( n/8 );
06028             } else if ( style.startsWith("font-family:" ) ) {
06029                 format.fn.setFamily( QTextDocument::section(style.mid(12),",",0,0).stripWhiteSpace() );
06030             } else if ( style.startsWith("text-decoration:" ) ) {
06031                 QString s = style.mid( 16 ).stripWhiteSpace();
06032                 format.fn.setUnderline( s == "underline" );
06033             } else if ( style.startsWith("vertical-align:" ) ) {
06034                 QString s = style.mid( 15 ).stripWhiteSpace();
06035                 if ( s == "sub" )
06036                     format.setVAlign( QTextFormat::AlignSubScript );
06037                 else if ( s == "super" )
06038                     format.setVAlign( QTextFormat::AlignSuperScript );
06039                 else
06040                     format.setVAlign( QTextFormat::AlignNormal );
06041             } else if ( style.startsWith("color:" ) ) {
06042                 format.col.setNamedColor( style.mid(6) );
06043                 format.linkColor = FALSE;
06044             }
06045         }
06046     }
06047 
06048     format.update();
06049     return format;
06050 }
06051 
06052 struct QPixmapInt
06053 {
06054     QPixmapInt() : ref( 0 ) {}
06055     QPixmap pm;
06056     int     ref;
06057 };
06058 
06059 static QMap<QString, QPixmapInt> *pixmap_map = 0;
06060 
06061 QTextImage::QTextImage( QTextDocument *p, const QMap<QString, QString> &attr, const QString& context,
06062                         QMimeSourceFactory &factory )
06063     : QTextCustomItem( p )
06064 {
06065     width = height = 0;
06066     if ( attr.contains("width") )
06067         width = attr["width"].toInt();
06068     if ( attr.contains("height") )
06069         height = attr["height"].toInt();
06070 
06071     reg = 0;
06072     QString imageName = attr["src"];
06073 
06074     if (!imageName)
06075         imageName = attr["source"];
06076 
06077     if ( !imageName.isEmpty() ) {
06078         imgId = QString( "%1,%2,%3,%4" ).arg( imageName ).arg( width ).arg( height ).arg( (ulong)&factory );
06079         if ( !pixmap_map )
06080             pixmap_map = new QMap<QString, QPixmapInt>;
06081         if ( pixmap_map->contains( imgId ) ) {
06082             QPixmapInt& pmi = pixmap_map->operator[](imgId);
06083             pm = pmi.pm;
06084             pmi.ref++;
06085             width = pm.width();
06086             height = pm.height();
06087         } else {
06088             QImage img;
06089             const QMimeSource* m =
06090                 factory.data( imageName, context );
06091             if ( !m ) {
06092                 owarn << "QTextImage: no mimesource for " << imageName.latin1() << "" << oendl; 
06093             }
06094             else {
06095                 if ( !QImageDrag::decode( m, img ) ) {
06096                     owarn << "QTextImage: cannot decode " << imageName.latin1() << "" << oendl; 
06097                 }
06098             }
06099 
06100             if ( !img.isNull() ) {
06101                 if ( width == 0 ) {
06102                     width = img.width();
06103                     if ( height != 0 ) {
06104                         width = img.width() * height / img.height();
06105                     }
06106                 }
06107                 if ( height == 0 ) {
06108                     height = img.height();
06109                     if ( width != img.width() ) {
06110                         height = img.height() * width / img.width();
06111                     }
06112                 }
06113                 if ( img.width() != width || img.height() != height ){
06114 #ifndef QT_NO_IMAGE_SMOOTHSCALE
06115                     img = img.smoothScale(width, height);
06116 #endif
06117                     width = img.width();
06118                     height = img.height();
06119                 }
06120                 pm.convertFromImage( img );
06121             }
06122             if ( !pm.isNull() ) {
06123                 QPixmapInt& pmi = pixmap_map->operator[](imgId);
06124                 pmi.pm = pm;
06125                 pmi.ref++;
06126             }
06127         }
06128         if ( pm.mask() ) {
06129             QRegion mask( *pm.mask() );
06130             QRegion all( 0, 0, pm.width(), pm.height() );
06131             reg = new QRegion( all.subtract( mask ) );
06132         }
06133     }
06134 
06135     if ( pm.isNull() && (width*height)==0 )
06136         width = height = 50;
06137 
06138     place = PlaceInline;
06139     if ( attr["align"] == "left" )
06140         place = PlaceLeft;
06141     else if ( attr["align"] == "right" )
06142         place = PlaceRight;
06143 
06144     tmpwidth = width;
06145     tmpheight = height;
06146 
06147     attributes = attr;
06148 }
06149 
06150 QTextImage::~QTextImage()
06151 {
06152     if ( pixmap_map && pixmap_map->contains( imgId ) ) {
06153         QPixmapInt& pmi = pixmap_map->operator[](imgId);
06154         pmi.ref--;
06155         if ( !pmi.ref ) {
06156             pixmap_map->remove( imgId );
06157             if ( pixmap_map->isEmpty() ) {
06158                 delete pixmap_map;
06159                 pixmap_map = 0;
06160             }
06161         }
06162     }
06163     delete reg;
06164 }
06165 
06166 QString QTextImage::richText() const
06167 {
06168     QString s;
06169     s += "<img ";
06170     QMap<QString, QString>::ConstIterator it = attributes.begin();
06171     for ( ; it != attributes.end(); ++it )
06172         s += it.key() + "=" + *it + " ";
06173     s += ">";
06174     return s;
06175 }
06176 
06177 void QTextImage::adjustToPainter( QPainter* p )
06178 {
06179     width = scale( tmpwidth, p );
06180     height = scale( tmpheight, p );
06181 }
06182 
06183 #if !defined(Q_WS_X11)
06184 #include <qbitmap.h>
06185 #include "qcleanuphandler.h"
06186 static QPixmap *qrt_selection = 0;
06187 static QSingleCleanupHandler<QPixmap> qrt_cleanup_pixmap;
06188 static void qrt_createSelectionPixmap( const QColorGroup &cg )
06189 {
06190     qrt_selection = new QPixmap( 2, 2 );
06191     qrt_cleanup_pixmap.set( &qrt_selection );
06192     qrt_selection->fill( Qt::color0 );
06193     QBitmap m( 2, 2 );
06194     m.fill( Qt::color1 );
06195     QPainter p( &m );
06196     p.setPen( Qt::color0 );
06197     for ( int j = 0; j < 2; ++j ) {
06198         p.drawPoint( j % 2, j );
06199     }
06200     p.end();
06201     qrt_selection->setMask( m );
06202     qrt_selection->fill( cg.highlight() );
06203 }
06204 #endif
06205 
06206 void QTextImage::draw( QPainter* p, int x, int y, int cx, int cy, int cw, int ch, const QColorGroup& cg, bool selected )
06207 {
06208     if ( placement() != PlaceInline ) {
06209         x = xpos;
06210         y = ypos;
06211     }
06212 
06213     if ( pm.isNull() ) {
06214         p->fillRect( x , y, width, height,  cg.dark() );
06215         return;
06216     }
06217 
06218     if ( is_printer( p ) ) {
06219         p->drawPixmap( x, y, pm );
06220         return;
06221     }
06222 
06223     if ( placement() != PlaceInline && !QRect( xpos, ypos, width, height ).intersects( QRect( cx, cy, cw, ch ) ) )
06224         return;
06225 
06226     if ( placement() == PlaceInline )
06227         p->drawPixmap( x , y, pm );
06228     else
06229         p->drawPixmap( cx , cy, pm, cx - x, cy - y, cw, ch );
06230 
06231     if ( selected && placement() == PlaceInline && is_printer( p ) ) {
06232 #if defined(Q_WS_X11)
06233         p->fillRect( QRect( QPoint( x, y ), pm.size() ), QBrush( cg.highlight(), QBrush::Dense4Pattern) );
06234 #else // in WIN32 Dense4Pattern doesn't work correctly (transparency problem), so work around it
06235         if ( !qrt_selection )
06236             qrt_createSelectionPixmap( cg );
06237         p->drawTiledPixmap( x, y, pm.width(), pm.height(), *qrt_selection );
06238 #endif
06239     }
06240 }
06241 
06242 void QTextHorizontalLine::adjustToPainter( QPainter* p )
06243 {
06244     height = scale( tmpheight, p );
06245 }
06246 
06247 
06248 QTextHorizontalLine::QTextHorizontalLine( QTextDocument *p, const QMap<QString, QString> &attr,
06249                                           const QString &,
06250                                           QMimeSourceFactory & )
06251     : QTextCustomItem( p )
06252 {
06253     height = tmpheight = 8;
06254     if ( attr.find( "color" ) != attr.end() )
06255         color = QColor( *attr.find( "color" ) );
06256 }
06257 
06258 QTextHorizontalLine::~QTextHorizontalLine()
06259 {
06260 }
06261 
06262 QString QTextHorizontalLine::richText() const
06263 {
06264     return "<hr>";
06265 }
06266 
06267 void QTextHorizontalLine::draw( QPainter* p, int x, int y, int , int , int , int , const QColorGroup& cg, bool selected )
06268 {
06269     QRect r( x, y, width, height);
06270     if ( is_printer( p ) ) {
06271         QPen oldPen = p->pen();
06272         if ( !color.isValid() )
06273             p->setPen( QPen( cg.text(), height/8 ) );
06274         else
06275             p->setPen( QPen( color, height/8 ) );
06276         p->drawLine( r.left()-1, y + height / 2, r.right() + 1, y + height / 2 );
06277         p->setPen( oldPen );
06278     } else {
06279         QColorGroup g( cg );
06280         if ( color.isValid() )
06281             g.setColor( QColorGroup::Dark, color );
06282         if ( selected )
06283             p->fillRect( r.left(), y, r.right(), y + height, g.highlight() );
06284         qDrawShadeLine( p, r.left() - 1, y + height / 2, r.right() + 1, y + height / 2, g, TRUE, height / 8 );
06285     }
06286 }
06287 
06288 
06289 /*****************************************************************/
06290 // Small set of utility functions to make the parser a bit simpler
06291 //
06292 
06293 bool QTextDocument::hasPrefix(const QChar* doc, int length, int pos, QChar c)
06294 {
06295     if ( pos >= length )
06296         return FALSE;
06297     return doc[ pos ].lower() == c.lower();
06298 }
06299 
06300 bool QTextDocument::hasPrefix( const QChar* doc, int length, int pos, const QString& s )
06301 {
06302     if ( pos + (int) s.length() >= length )
06303         return FALSE;
06304     for ( int i = 0; i < (int)s.length(); i++ ) {
06305         if ( doc[ pos + i ].lower() != s[ i ].lower() )
06306             return FALSE;
06307     }
06308     return TRUE;
06309 }
06310 
06311 static bool qt_is_cell_in_use( QPtrList<QTextTableCell>& cells, int row, int col )
06312 {
06313     for ( QTextTableCell* c = cells.first(); c; c = cells.next() ) {
06314         if ( row >= c->row() && row < c->row() + c->rowspan()
06315              && col >= c->column() && col < c->column() + c->colspan() )
06316             return TRUE;
06317     }
06318     return FALSE;
06319 }
06320 
06321 QTextCustomItem* QTextDocument::parseTable( const QMap<QString, QString> &attr, const QTextFormat &fmt,
06322                                             const QChar* doc, int length, int& pos, QTextParagraph *curpar )
06323 {
06324 
06325     QTextTable* table = new QTextTable( this, attr );
06326     int row = -1;
06327     int col = -1;
06328 
06329     QString rowbgcolor;
06330     QString rowalign;
06331     QString tablebgcolor = attr["bgcolor"];
06332 
06333     QPtrList<QTextTableCell> multicells;
06334 
06335     QString tagname;
06336     (void) eatSpace(doc, length, pos);
06337     while ( pos < length) {
06338         if (hasPrefix(doc, length, pos, QChar('<')) ){
06339             if (hasPrefix(doc, length, pos+1, QChar('/'))) {
06340                 tagname = parseCloseTag( doc, length, pos );
06341                 if ( tagname == "table" ) {
06342                     return table;
06343                 }
06344             } else {
06345                 QMap<QString, QString> attr2;
06346                 bool emptyTag = FALSE;
06347                 tagname = parseOpenTag( doc, length, pos, attr2, emptyTag );
06348                 if ( tagname == "tr" ) {
06349                     rowbgcolor = attr2["bgcolor"];
06350                     rowalign = attr2["align"];
06351                     row++;
06352                     col = -1;
06353                 }
06354                 else if ( tagname == "td" || tagname == "th" ) {
06355                     col++;
06356                     while ( qt_is_cell_in_use( multicells, row, col ) ) {
06357                         col++;
06358                     }
06359 
06360                     if ( row >= 0 && col >= 0 ) {
06361                         const QStyleSheetItem* s = sheet_->item(tagname);
06362                         if ( !attr2.contains("bgcolor") ) {
06363                             if (!rowbgcolor.isEmpty() )
06364                                 attr2["bgcolor"] = rowbgcolor;
06365                             else if (!tablebgcolor.isEmpty() )
06366                                 attr2["bgcolor"] = tablebgcolor;
06367                         }
06368                         if ( !attr2.contains("align") ) {
06369                             if (!rowalign.isEmpty() )
06370                                 attr2["align"] = rowalign;
06371                         }
06372 
06373                         // extract the cell contents
06374                         int end = pos;
06375                         while ( end < length
06376                                 && !hasPrefix( doc, length, end, "</td")
06377                                 && !hasPrefix( doc, length, end, "<td")
06378                                 && !hasPrefix( doc, length, end, "</th")
06379                                 && !hasPrefix( doc, length, end, "<th")
06380                                 && !hasPrefix( doc, length, end, "<td")
06381                                 && !hasPrefix( doc, length, end, "</tr")
06382                                 && !hasPrefix( doc, length, end, "<tr")
06383                                 && !hasPrefix( doc, length, end, "</table") ) {
06384                             if ( hasPrefix( doc, length, end, "<table" ) ) { // nested table
06385                                 int nested = 1;
06386                                 ++end;
06387                                 while ( end < length && nested != 0 ) {
06388                                     if ( hasPrefix( doc, length, end, "</table" ) )
06389                                         nested--;
06390                                     if ( hasPrefix( doc, length, end, "<table" ) )
06391                                         nested++;
06392                                     end++;
06393                                 }
06394                             }
06395                             end++;
06396                         }
06397                         QTextTableCell* cell  = new QTextTableCell( table, row, col,
06398                                             attr2, s, fmt.makeTextFormat( s, attr2, scaleFontsFactor ),
06399                                             contxt, *factory_, sheet_,
06400                                             QString( doc, length).mid( pos, end - pos ) );
06401                         cell->richText()->parentPar = curpar;
06402                         if ( cell->colspan() > 1 || cell->rowspan() > 1 )
06403                             multicells.append( cell );
06404                         col += cell->colspan()-1;
06405                         pos = end;
06406                     }
06407                 }
06408             }
06409 
06410         } else {
06411             ++pos;
06412         }
06413     }
06414     return table;
06415 }
06416 
06417 bool QTextDocument::eatSpace(const QChar* doc, int length, int& pos, bool includeNbsp )
06418 {
06419     int old_pos = pos;
06420     while (pos < length && doc[pos].isSpace() && ( includeNbsp || (doc[pos] != QChar::nbsp ) ) )
06421         pos++;
06422     return old_pos < pos;
06423 }
06424 
06425 bool QTextDocument::eat(const QChar* doc, int length, int& pos, QChar c)
06426 {
06427     bool ok = pos < length && doc[pos] == c;
06428     if ( ok )
06429         pos++;
06430     return ok;
06431 }
06432 /*****************************************************************/
06433 
06434 struct Entity {
06435     const char * name;
06436     Q_UINT16 code;
06437 };
06438 
06439 static const Entity entitylist [] = {
06440     { "AElig", 0x00c6 },
06441     { "Aacute", 0x00c1 },
06442     { "Acirc", 0x00c2 },
06443     { "Agrave", 0x00c0 },
06444     { "Alpha", 0x0391 },
06445     { "AMP", 38 },
06446     { "Aring", 0x00c5 },
06447     { "Atilde", 0x00c3 },
06448     { "Auml", 0x00c4 },
06449     { "Beta", 0x0392 },
06450     { "Ccedil", 0x00c7 },
06451     { "Chi", 0x03a7 },
06452     { "Dagger", 0x2021 },
06453     { "Delta", 0x0394 },
06454     { "ETH", 0x00d0 },
06455     { "Eacute", 0x00c9 },
06456     { "Ecirc", 0x00ca },
06457     { "Egrave", 0x00c8 },
06458     { "Epsilon", 0x0395 },
06459     { "Eta", 0x0397 },
06460     { "Euml", 0x00cb },
06461     { "Gamma", 0x0393 },
06462     { "GT", 62 },
06463     { "Iacute", 0x00cd },
06464     { "Icirc", 0x00ce },
06465     { "Igrave", 0x00cc },
06466     { "Iota", 0x0399 },
06467     { "Iuml", 0x00cf },
06468     { "Kappa", 0x039a },
06469     { "Lambda", 0x039b },
06470     { "LT", 60 },
06471     { "Mu", 0x039c },
06472     { "Ntilde", 0x00d1 },
06473     { "Nu", 0x039d },
06474     { "OElig", 0x0152 },
06475     { "Oacute", 0x00d3 },
06476     { "Ocirc", 0x00d4 },
06477     { "Ograve", 0x00d2 },
06478     { "Omega", 0x03a9 },
06479     { "Omicron", 0x039f },
06480     { "Oslash", 0x00d8 },
06481     { "Otilde", 0x00d5 },
06482     { "Ouml", 0x00d6 },
06483     { "Phi", 0x03a6 },
06484     { "Pi", 0x03a0 },
06485     { "Prime", 0x2033 },
06486     { "Psi", 0x03a8 },
06487     { "QUOT", 34 },
06488     { "Rho", 0x03a1 },
06489     { "Scaron", 0x0160 },
06490     { "Sigma", 0x03a3 },
06491     { "THORN", 0x00de },
06492     { "Tau", 0x03a4 },
06493     { "Theta", 0x0398 },
06494     { "Uacute", 0x00da },
06495     { "Ucirc", 0x00db },
06496     { "Ugrave", 0x00d9 },
06497     { "Upsilon", 0x03a5 },
06498     { "Uuml", 0x00dc },
06499     { "Xi", 0x039e },
06500     { "Yacute", 0x00dd },
06501     { "Yuml", 0x0178 },
06502     { "Zeta", 0x0396 },
06503     { "aacute", 0x00e1 },
06504     { "acirc", 0x00e2 },
06505     { "acute", 0x00b4 },
06506     { "aelig", 0x00e6 },
06507     { "agrave", 0x00e0 },
06508     { "alefsym", 0x2135 },
06509     { "alpha", 0x03b1 },
06510     { "amp", 38 },
06511     { "and", 0x22a5 },
06512     { "ang", 0x2220 },
06513     { "apos", 0x0027 },
06514     { "aring", 0x00e5 },
06515     { "asymp", 0x2248 },
06516     { "atilde", 0x00e3 },
06517     { "auml", 0x00e4 },
06518     { "bdquo", 0x201e },
06519     { "beta", 0x03b2 },
06520     { "brvbar", 0x00a6 },
06521     { "bull", 0x2022 },
06522     { "cap", 0x2229 },
06523     { "ccedil", 0x00e7 },
06524     { "cedil", 0x00b8 },
06525     { "cent", 0x00a2 },
06526     { "chi", 0x03c7 },
06527     { "circ", 0x02c6 },
06528     { "clubs", 0x2663 },
06529     { "cong", 0x2245 },
06530     { "copy", 0x00a9 },
06531     { "crarr", 0x21b5 },
06532     { "cup", 0x222a },
06533     { "curren", 0x00a4 },
06534     { "dArr", 0x21d3 },
06535     { "dagger", 0x2020 },
06536     { "darr", 0x2193 },
06537     { "deg", 0x00b0 },
06538     { "delta", 0x03b4 },
06539     { "diams", 0x2666 },
06540     { "divide", 0x00f7 },
06541     { "eacute", 0x00e9 },
06542     { "ecirc", 0x00ea },
06543     { "egrave", 0x00e8 },
06544     { "empty", 0x2205 },
06545     { "emsp", 0x2003 },
06546     { "ensp", 0x2002 },
06547     { "epsilon", 0x03b5 },
06548     { "equiv", 0x2261 },
06549     { "eta", 0x03b7 },
06550     { "eth", 0x00f0 },
06551     { "euml", 0x00eb },
06552     { "euro", 0x20ac },
06553     { "exist", 0x2203 },
06554     { "fnof", 0x0192 },
06555     { "forall", 0x2200 },
06556     { "frac12", 0x00bd },
06557     { "frac14", 0x00bc },
06558     { "frac34", 0x00be },
06559     { "frasl", 0x2044 },
06560     { "gamma", 0x03b3 },
06561     { "ge", 0x2265 },
06562     { "gt", 62 },
06563     { "hArr", 0x21d4 },
06564     { "harr", 0x2194 },
06565     { "hearts", 0x2665 },
06566     { "hellip", 0x2026 },
06567     { "iacute", 0x00ed },
06568     { "icirc", 0x00ee },
06569     { "iexcl", 0x00a1 },
06570     { "igrave", 0x00ec },
06571     { "image", 0x2111 },
06572     { "infin", 0x221e },
06573     { "int", 0x222b },
06574     { "iota", 0x03b9 },
06575     { "iquest", 0x00bf },
06576     { "isin", 0x2208 },
06577     { "iuml", 0x00ef },
06578     { "kappa", 0x03ba },
06579     { "lArr", 0x21d0 },
06580     { "lambda", 0x03bb },
06581     { "lang", 0x2329 },
06582     { "laquo", 0x00ab },
06583     { "larr", 0x2190 },
06584     { "lceil", 0x2308 },
06585     { "ldquo", 0x201c },
06586     { "le", 0x2264 },
06587     { "lfloor", 0x230a },
06588     { "lowast", 0x2217 },
06589     { "loz", 0x25ca },
06590     { "lrm", 0x200e },
06591     { "lsaquo", 0x2039 },
06592     { "lsquo", 0x2018 },
06593     { "lt", 60 },
06594     { "macr", 0x00af },
06595     { "mdash", 0x2014 },
06596     { "micro", 0x00b5 },
06597     { "middot", 0x00b7 },
06598     { "minus", 0x2212 },
06599     { "mu", 0x03bc },
06600     { "nabla", 0x2207 },
06601     { "nbsp", 0x00a0 },
06602     { "ndash", 0x2013 },
06603     { "ne", 0x2260 },
06604     { "ni", 0x220b },
06605     { "not", 0x00ac },
06606     { "notin", 0x2209 },
06607     { "nsub", 0x2284 },
06608     { "ntilde", 0x00f1 },
06609     { "nu", 0x03bd },
06610     { "oacute", 0x00f3 },
06611     { "ocirc", 0x00f4 },
06612     { "oelig", 0x0153 },
06613     { "ograve", 0x00f2 },
06614     { "oline", 0x203e },
06615     { "omega", 0x03c9 },
06616     { "omicron", 0x03bf },
06617     { "oplus", 0x2295 },
06618     { "or", 0x22a6 },
06619     { "ordf", 0x00aa },
06620     { "ordm", 0x00ba },
06621     { "oslash", 0x00f8 },
06622     { "otilde", 0x00f5 },
06623     { "otimes", 0x2297 },
06624     { "ouml", 0x00f6 },
06625     { "para", 0x00b6 },
06626     { "part", 0x2202 },
06627     { "percnt", 0x0025 },
06628     { "permil", 0x2030 },
06629     { "perp", 0x22a5 },
06630     { "phi", 0x03c6 },
06631     { "pi", 0x03c0 },
06632     { "piv", 0x03d6 },
06633     { "plusmn", 0x00b1 },
06634     { "pound", 0x00a3 },
06635     { "prime", 0x2032 },
06636     { "prod", 0x220f },
06637     { "prop", 0x221d },
06638     { "psi", 0x03c8 },
06639     { "quot", 34 },
06640     { "rArr", 0x21d2 },
06641     { "radic", 0x221a },
06642     { "rang", 0x232a },
06643     { "raquo", 0x00bb },
06644     { "rarr", 0x2192 },
06645     { "rceil", 0x2309 },
06646     { "rdquo", 0x201d },
06647     { "real", 0x211c },
06648     { "reg", 0x00ae },
06649     { "rfloor", 0x230b },
06650     { "rho", 0x03c1 },
06651     { "rlm", 0x200f },
06652     { "rsaquo", 0x203a },
06653     { "rsquo", 0x2019 },
06654     { "sbquo", 0x201a },
06655     { "scaron", 0x0161 },
06656     { "sdot", 0x22c5 },
06657     { "sect", 0x00a7 },
06658     { "shy", 0x00ad },
06659     { "sigma", 0x03c3 },
06660     { "sigmaf", 0x03c2 },
06661     { "sim", 0x223c },
06662     { "spades", 0x2660 },
06663     { "sub", 0x2282 },
06664     { "sube", 0x2286 },
06665     { "sum", 0x2211 },
06666     { "sup1", 0x00b9 },
06667     { "sup2", 0x00b2 },
06668     { "sup3", 0x00b3 },
06669     { "sup", 0x2283 },
06670     { "supe", 0x2287 },
06671     { "szlig", 0x00df },
06672     { "tau", 0x03c4 },
06673     { "there4", 0x2234 },
06674     { "theta", 0x03b8 },
06675     { "thetasym", 0x03d1 },
06676     { "thinsp", 0x2009 },
06677     { "thorn", 0x00fe },
06678     { "tilde", 0x02dc },
06679     { "times", 0x00d7 },
06680     { "trade", 0x2122 },
06681     { "uArr", 0x21d1 },
06682     { "uacute", 0x00fa },
06683     { "uarr", 0x2191 },
06684     { "ucirc", 0x00fb },
06685     { "ugrave", 0x00f9 },
06686     { "uml", 0x00a8 },
06687     { "upsih", 0x03d2 },
06688     { "upsilon", 0x03c5 },
06689     { "uuml", 0x00fc },
06690     { "weierp", 0x2118 },
06691     { "xi", 0x03be },
06692     { "yacute", 0x00fd },
06693     { "yen", 0x00a5 },
06694     { "yuml", 0x00ff },
06695     { "zeta", 0x03b6 },
06696     { "zwj", 0x200d },
06697     { "zwnj", 0x200c },
06698     { "", 0x0000 }
06699 };
06700 
06701 
06702 
06703 
06704 
06705 static QMap<QCString, QChar> *html_map = 0;
06706 static void qt_cleanup_html_map()
06707 {
06708     delete html_map;
06709     html_map = 0;
06710 }
06711 
06712 static QMap<QCString, QChar> *htmlMap()
06713 {
06714     if ( !html_map ) {
06715         html_map = new QMap<QCString, QChar>;
06716         qAddPostRoutine( qt_cleanup_html_map );
06717 
06718         const Entity *ent = entitylist;
06719         while( ent->code ) {
06720             html_map->insert( ent->name, QChar(ent->code) );
06721             ent++;
06722         }
06723     }
06724     return html_map;
06725 }
06726 
06727 QChar QTextDocument::parseHTMLSpecialChar(const QChar* doc, int length, int& pos)
06728 {
06729     QCString s;
06730     pos++;
06731     int recoverpos = pos;
06732     while ( pos < length && doc[pos] != ';' && !doc[pos].isSpace() && pos < recoverpos + 6) {
06733         s += doc[pos];
06734         pos++;
06735     }
06736     if (doc[pos] != ';' && !doc[pos].isSpace() ) {
06737         pos = recoverpos;
06738         return '&';
06739     }
06740     pos++;
06741 
06742     if ( s.length() > 1 && s[0] == '#') {
06743         int num = s.mid(1).toInt();
06744         if ( num == 151 ) // ### hack for designer manual
06745             return '-';
06746         return num;
06747     }
06748 
06749     QMap<QCString, QChar>::Iterator it = htmlMap()->find(s);
06750     if ( it != htmlMap()->end() ) {
06751         return *it;
06752     }
06753 
06754     pos = recoverpos;
06755     return '&';
06756 }
06757 
06758 QString QTextDocument::parseWord(const QChar* doc, int length, int& pos, bool lower)
06759 {
06760     QString s;
06761 
06762     if (doc[pos] == '"') {
06763         pos++;
06764         while ( pos < length  && doc[pos] != '"' ) {
06765             s += doc[pos];
06766             pos++;
06767         }
06768         eat(doc, length, pos, '"');
06769     } else {
06770         static QString term = QString::fromLatin1("/>");
06771         while( pos < length &&
06772                (doc[pos] != '>' && !hasPrefix( doc, length, pos, term))
06773                && doc[pos] != '<'
06774                && doc[pos] != '='
06775                && !doc[pos].isSpace())
06776         {
06777             if ( doc[pos] == '&')
06778                 s += parseHTMLSpecialChar( doc, length, pos );
06779             else {
06780                 s += doc[pos];
06781                 pos++;
06782             }
06783         }
06784         if (lower)
06785             s = s.lower();
06786     }
06787     return s;
06788 }
06789 
06790 QChar QTextDocument::parseChar(const QChar* doc, int length, int& pos, QStyleSheetItem::WhiteSpaceMode wsm )
06791 {
06792     if ( pos >=  length )
06793         return QChar::null;
06794 
06795     QChar c = doc[pos++];
06796 
06797     if (c == '<' )
06798         return QChar::null;
06799 
06800     if ( c.isSpace() && c != QChar::nbsp ) {
06801         if ( wsm == QStyleSheetItem::WhiteSpacePre ) {
06802             if ( c == '\n' )
06803                 return QChar_linesep;
06804             else
06805                 return c;
06806         } else { // non-pre mode: collapse whitespace except nbsp
06807             while ( pos< length &&
06808                     doc[pos].isSpace()  && doc[pos] != QChar::nbsp )
06809                 pos++;
06810             if ( wsm == QStyleSheetItem::WhiteSpaceNoWrap )
06811                 return QChar::nbsp;
06812             else
06813                 return ' ';
06814         }
06815     }
06816     else if ( c == '&' )
06817         return parseHTMLSpecialChar( doc, length, --pos );
06818     else
06819         return c;
06820 }
06821 
06822 QString QTextDocument::parseOpenTag(const QChar* doc, int length, int& pos,
06823                                   QMap<QString, QString> &attr, bool& emptyTag)
06824 {
06825     emptyTag = FALSE;
06826     pos++;
06827     if ( hasPrefix(doc, length, pos, '!') ) {
06828         if ( hasPrefix( doc, length, pos+1, "--")) {
06829             pos += 3;
06830             // eat comments
06831             QString pref = QString::fromLatin1("-->");
06832             while ( !hasPrefix(doc, length, pos, pref ) && pos < length )
06833                 pos++;
06834             if ( hasPrefix(doc, length, pos, pref ) ) {
06835                 pos += 3;
06836                 eatSpace(doc, length, pos, TRUE);
06837             }
06838             emptyTag = TRUE;
06839             return QString::null;
06840         }
06841         else {
06842             // eat strange internal tags
06843             while ( !hasPrefix(doc, length, pos, '>') && pos < length )
06844                 pos++;
06845             if ( hasPrefix(doc, length, pos, '>') ) {
06846                 pos++;
06847                 eatSpace(doc, length, pos, TRUE);
06848             }
06849             return QString::null;
06850         }
06851     }
06852 
06853     QString tag = parseWord(doc, length, pos );
06854     eatSpace(doc, length, pos, TRUE);
06855     static QString term = QString::fromLatin1("/>");
06856     static QString s_TRUE = QString::fromLatin1("TRUE");
06857 
06858     while (doc[pos] != '>' && ! (emptyTag = hasPrefix(doc, length, pos, term) )) {
06859         QString key = parseWord(doc, length, pos );
06860         eatSpace(doc, length, pos, TRUE);
06861         if ( key.isEmpty()) {
06862             // error recovery
06863             while ( pos < length && doc[pos] != '>' )
06864                 pos++;
06865             break;
06866         }
06867         QString value;
06868         if (hasPrefix(doc, length, pos, '=') ){
06869             pos++;
06870             eatSpace(doc, length, pos);
06871             value = parseWord(doc, length, pos, FALSE);
06872         }
06873         else
06874             value = s_TRUE;
06875         attr.insert(key.lower(), value );
06876         eatSpace(doc, length, pos, TRUE);
06877     }
06878 
06879     if (emptyTag) {
06880         eat(doc, length, pos, '/');
06881         eat(doc, length, pos, '>');
06882     }
06883     else
06884         eat(doc, length, pos, '>');
06885 
06886     return tag;
06887 }
06888 
06889 QString QTextDocument::parseCloseTag( const QChar* doc, int length, int& pos )
06890 {
06891     pos++;
06892     pos++;
06893     QString tag = parseWord(doc, length, pos );
06894     eatSpace(doc, length, pos, TRUE);
06895     eat(doc, length, pos, '>');
06896     return tag;
06897 }
06898 
06899 QTextFlow::QTextFlow()
06900 {
06901     w = pagesize = 0;
06902     leftItems.setAutoDelete( FALSE );
06903     rightItems.setAutoDelete( FALSE );
06904 }
06905 
06906 QTextFlow::~QTextFlow()
06907 {
06908 }
06909 
06910 void QTextFlow::clear()
06911 {
06912     leftItems.clear();
06913     rightItems.clear();
06914 }
06915 
06916 void QTextFlow::setWidth( int width )
06917 {
06918     w = width;
06919 }
06920 
06921 int QTextFlow::adjustLMargin( int yp, int, int margin, int space )
06922 {
06923     for ( QTextCustomItem* item = leftItems.first(); item; item = leftItems.next() ) {
06924         if ( item->ypos == -1 )
06925             continue;
06926         if ( yp >= item->ypos && yp < item->ypos + item->height )
06927             margin = QMAX( margin, item->xpos + item->width + space );
06928     }
06929     return margin;
06930 }
06931 
06932 int QTextFlow::adjustRMargin( int yp, int, int margin, int space )
06933 {
06934     for ( QTextCustomItem* item = rightItems.first(); item; item = rightItems.next() ) {
06935         if ( item->ypos == -1 )
06936             continue;
06937         if ( yp >= item->ypos && yp < item->ypos + item->height )
06938             margin = QMAX( margin, w - item->xpos - space );
06939     }
06940     return margin;
06941 }
06942 
06943 
06944 int QTextFlow::adjustFlow( int y, int /*w*/, int h )
06945 {
06946     if ( pagesize > 0 ) { // check pages
06947         int yinpage = y % pagesize;
06948         if ( yinpage <= border_tolerance )
06949             return border_tolerance - yinpage;
06950         else
06951             if ( yinpage + h > pagesize - border_tolerance )
06952                 return ( pagesize - yinpage ) + border_tolerance;
06953     }
06954     return 0;
06955 }
06956 
06957 void QTextFlow::unregisterFloatingItem( QTextCustomItem* item )
06958 {
06959     leftItems.removeRef( item );
06960     rightItems.removeRef( item );
06961 }
06962 
06963 void QTextFlow::registerFloatingItem( QTextCustomItem* item )
06964 {
06965     if ( item->placement() == QTextCustomItem::PlaceRight ) {
06966         if ( !rightItems.contains( item ) )
06967             rightItems.append( item );
06968     } else if ( item->placement() == QTextCustomItem::PlaceLeft &&
06969                 !leftItems.contains( item ) ) {
06970         leftItems.append( item );
06971     }
06972 }
06973 
06974 QRect QTextFlow::boundingRect() const
06975 {
06976     QRect br;
06977     QPtrListIterator<QTextCustomItem> l( leftItems );
06978     while( l.current() ) {
06979         br = br.unite( l.current()->geometry() );
06980         ++l;
06981     }
06982     QPtrListIterator<QTextCustomItem> r( rightItems );
06983     while( r.current() ) {
06984         br = br.unite( r.current()->geometry() );
06985         ++r;
06986     }
06987     return br;
06988 }
06989 
06990 
06991 void QTextFlow::drawFloatingItems( QPainter* p, int cx, int cy, int cw, int ch, const QColorGroup& cg, bool selected )
06992 {
06993     QTextCustomItem *item;
06994     for ( item = leftItems.first(); item; item = leftItems.next() ) {
06995         if ( item->xpos == -1 || item->ypos == -1 )
06996             continue;
06997         item->draw( p, item->xpos, item->ypos, cx, cy, cw, ch, cg, selected );
06998     }
06999 
07000     for ( item = rightItems.first(); item; item = rightItems.next() ) {
07001         if ( item->xpos == -1 || item->ypos == -1 )
07002             continue;
07003         item->draw( p, item->xpos, item->ypos, cx, cy, cw, ch, cg, selected );
07004     }
07005 }
07006 
07007 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
07008 
07009 void QTextCustomItem::pageBreak( int /*y*/ , QTextFlow* /*flow*/ )
07010 {
07011 }
07012 
07013 QTextTable::QTextTable( QTextDocument *p, const QMap<QString, QString> & attr  )
07014     : QTextCustomItem( p )
07015 {
07016     cells.setAutoDelete( FALSE );
07017     cellspacing = 2;
07018     if ( attr.contains("cellspacing") )
07019         cellspacing = attr["cellspacing"].toInt();
07020     cellpadding = 1;
07021     if ( attr.contains("cellpadding") )
07022         cellpadding = attr["cellpadding"].toInt();
07023     border = innerborder = 0;
07024     if ( attr.contains("border" ) ) {
07025         QString s( attr["border"] );
07026         if ( s == "TRUE" )
07027             border = 1;
07028         else
07029             border = attr["border"].toInt();
07030     }
07031     us_b = border;
07032 
07033     innerborder = us_ib = border ? 1 : 0;
07034 
07035     if ( border )
07036         cellspacing += 2;
07037 
07038     us_ib = innerborder;
07039     us_cs = cellspacing;
07040     us_cp = cellpadding;
07041     outerborder = cellspacing + border;
07042     us_ob = outerborder;
07043     layout = new QGridLayout( 1, 1, cellspacing );
07044 
07045     fixwidth = 0;
07046     stretch = 0;
07047     if ( attr.contains("width") ) {
07048         bool b;
07049         QString s( attr["width"] );
07050         int w = s.toInt( &b );
07051         if ( b ) {
07052             fixwidth = w;
07053         } else {
07054             s = s.stripWhiteSpace();
07055             if ( s.length() > 1 && s[ (int)s.length()-1 ] == '%' )
07056                 stretch = s.left( s.length()-1).toInt();
07057         }
07058     }
07059 
07060     place = PlaceInline;
07061     if ( attr["align"] == "left" )
07062         place = PlaceLeft;
07063     else if ( attr["align"] == "right" )
07064         place = PlaceRight;
07065     cachewidth = 0;
07066     attributes = attr;
07067     pageBreakFor = -1;
07068 }
07069 
07070 QTextTable::~QTextTable()
07071 {
07072     delete layout;
07073 }
07074 
07075 QString QTextTable::richText() const
07076 {
07077     QString s;
07078     s = "<table ";
07079     QMap<QString, QString>::ConstIterator it = attributes.begin();
07080     for ( ; it != attributes.end(); ++it )
07081         s += it.key() + "=" + *it + " ";
07082     s += ">\n";
07083 
07084     int lastRow = -1;
07085     bool needEnd = FALSE;
07086     QPtrListIterator<QTextTableCell> it2( cells );
07087     while ( it2.current() ) {
07088         QTextTableCell *cell = it2.current();
07089         ++it2;
07090         if ( lastRow != cell->row() ) {
07091             if ( lastRow != -1 )
07092                 s += "</tr>\n";
07093             s += "<tr>";
07094             lastRow = cell->row();
07095             needEnd = TRUE;
07096         }
07097         s += "<td";
07098         it = cell->attributes.begin();
07099         for ( ; it != cell->attributes.end(); ++it )
07100             s += " " + it.key() + "=" + *it;
07101         s += ">";
07102         s += cell->richText()->richText();
07103         s += "</td>";
07104     }
07105     if ( needEnd )
07106         s += "</tr>\n";
07107     s += "</table>\n";
07108     return s;
07109 }
07110 
07111 void QTextTable::adjustToPainter( QPainter* p )
07112 {
07113     cellspacing = scale( us_cs, p );
07114     cellpadding = scale( us_cp, p );
07115     border = scale( us_b , p );
07116     innerborder = scale( us_ib, p );
07117     outerborder = scale( us_ob ,p );
07118     width = 0;
07119     cachewidth = 0;
07120     for ( QTextTableCell* cell = cells.first(); cell; cell = cells.next() )
07121         cell->adjustToPainter( p );
07122 }
07123 
07124 void QTextTable::adjustCells( int y , int shift )
07125 {
07126     QPtrListIterator<QTextTableCell> it( cells );
07127     QTextTableCell* cell;
07128     bool enlarge = FALSE;
07129     while ( ( cell = it.current() ) ) {
07130         ++it;
07131         QRect r = cell->geometry();
07132         if ( y <= r.top() ) {
07133             r.moveBy(0, shift );
07134             cell->setGeometry( r );
07135             enlarge = TRUE;
07136         } else if ( y <= r.bottom() ) {
07137             r.rBottom() += shift;
07138             cell->setGeometry( r );
07139             enlarge = TRUE;
07140         }
07141     }
07142     if ( enlarge )
07143         height += shift;
07144 }
07145 
07146 void QTextTable::pageBreak( int  yt, QTextFlow* flow )
07147 {
07148     if ( flow->pageSize() <= 0 )
07149         return;
07150     if ( layout && pageBreakFor > 0 && pageBreakFor != yt ) {
07151         layout->invalidate();
07152         int h = layout->heightForWidth( width-2*outerborder );
07153         layout->setGeometry( QRect(0, 0, width-2*outerborder, h)  );
07154         height = layout->geometry().height()+2*outerborder;
07155     }
07156     pageBreakFor = yt;
07157     QPtrListIterator<QTextTableCell> it( cells );
07158     QTextTableCell* cell;
07159     while ( ( cell = it.current() ) ) {
07160         ++it;
07161         int y = yt + outerborder + cell->geometry().y();
07162         int shift = flow->adjustFlow( y - cellspacing, width, cell->richText()->height() + 2*cellspacing );
07163         adjustCells( y - outerborder - yt, shift );
07164     }
07165 }
07166 
07167 
07168 void QTextTable::draw(QPainter* p, int x, int y, int cx, int cy, int cw, int ch, const QColorGroup& cg, bool selected )
07169 {
07170     if ( placement() != PlaceInline ) {
07171         x = xpos;
07172         y = ypos;
07173     }
07174 
07175     for (QTextTableCell* cell = cells.first(); cell; cell = cells.next() ) {
07176         if ( cx < 0 && cy < 0 ||
07177              QRect( cx, cy, cw, ch ).intersects( QRect( x + outerborder + cell->geometry().x(),
07178                                                         y + outerborder + cell->geometry().y(),
07179                                                         cell->geometry().width(), cell->geometry().height() ) ) ) {
07180             cell->draw( p, x+outerborder, y+outerborder, cx, cy, cw, ch, cg, selected );
07181             if ( border ) {
07182                 QRect r( x+outerborder+cell->geometry().x() - innerborder,
07183                          y+outerborder+cell->geometry().y() - innerborder,
07184                          cell->geometry().width() + 2 * innerborder,
07185                          cell->geometry().height() + 2 * innerborder );
07186                 if ( is_printer( p ) ) {
07187                     QPen oldPen = p->pen();
07188                     QRect r2 = r;
07189                     r2.setLeft( r2.left() + innerborder/2 );
07190                     r2.setTop( r2.top() + innerborder/2 );
07191                     r2.setRight( r2.right() - innerborder/2 );
07192                     r2.setBottom( r2.bottom() - innerborder/2 );
07193                     p->setPen( QPen( cg.text(), innerborder ) );
07194                     p->drawRect( r2 );
07195                     p->setPen( oldPen );
07196                 } else {
07197                     int s =  QMAX( cellspacing-2*innerborder, 0);
07198                     if ( s ) {
07199                         p->fillRect( r.left()-s, r.top(), s+1, r.height(), cg.button() );
07200                         p->fillRect( r.right(), r.top(), s+1, r.height(), cg.button() );
07201                         p->fillRect( r.left()-s, r.top()-s, r.width()+2*s, s, cg.button() );
07202                         p->fillRect( r.left()-s, r.bottom(), r.width()+2*s, s, cg.button() );
07203                     }
07204                     qDrawShadePanel( p, r, cg, TRUE, innerborder );
07205                 }
07206             }
07207         }
07208     }
07209     if ( border ) {
07210         QRect r ( x, y, width, height );
07211         if ( is_printer( p ) ) {
07212             QRect r2 = r;
07213             r2.setLeft( r2.left() + border/2 );
07214             r2.setTop( r2.top() + border/2 );
07215             r2.setRight( r2.right() - border/2 );
07216             r2.setBottom( r2.bottom() - border/2 );
07217             QPen oldPen = p->pen();
07218             p->setPen( QPen( cg.text(), border ) );
07219             p->drawRect( r2 );
07220             p->setPen( oldPen );
07221         } else {
07222             int s = border+QMAX( cellspacing-2*innerborder, 0);
07223             if ( s ) {
07224                 p->fillRect( r.left(), r.top(), s, r.height(), cg.button() );
07225                 p->fillRect( r.right()-s, r.top(), s, r.height(), cg.button() );
07226                 p->fillRect( r.left(), r.top(), r.width(), s, cg.button() );
07227                 p->fillRect( r.left(), r.bottom()-s, r.width(), s, cg.button() );
07228             }
07229             qDrawShadePanel( p, r, cg, FALSE, border );
07230         }
07231     }
07232 
07233 }
07234 
07235 int QTextTable::minimumWidth() const
07236 {
07237     return (layout ? layout->minimumSize().width() : 0) + 2 * outerborder;
07238 }
07239 
07240 void QTextTable::resize( int nwidth )
07241 {
07242     if ( fixwidth && cachewidth != 0 )
07243         return;
07244     if ( nwidth == cachewidth )
07245         return;
07246 
07247 
07248     cachewidth = nwidth;
07249     int w = nwidth;
07250 
07251     format( w );
07252 
07253     if ( stretch )
07254         nwidth = nwidth * stretch / 100;
07255 
07256     width = nwidth;
07257     layout->invalidate();
07258     int shw = layout->sizeHint().width() + 2*outerborder;
07259     int mw = layout->minimumSize().width() + 2*outerborder;
07260     if ( stretch )
07261         width = QMAX( mw, nwidth );
07262     else
07263         width = QMAX( mw, QMIN( nwidth, shw ) );
07264 
07265     if ( fixwidth )
07266         width = fixwidth;
07267 
07268     layout->invalidate();
07269     mw = layout->minimumSize().width() + 2*outerborder;
07270     width = QMAX( width, mw );
07271 
07272     int h = layout->heightForWidth( width-2*outerborder );
07273     layout->setGeometry( QRect(0, 0, width-2*outerborder, h)  );
07274     height = layout->geometry().height()+2*outerborder;
07275 }
07276 
07277 void QTextTable::format( int w )
07278 {
07279     for ( int i = 0; i < (int)cells.count(); ++i ) {
07280         QTextTableCell *cell = cells.at( i );
07281         QRect r = cell->geometry();
07282         r.setWidth( w - 2*outerborder );
07283         cell->setGeometry( r );
07284     }
07285 }
07286 
07287 void QTextTable::addCell( QTextTableCell* cell )
07288 {
07289     cells.append( cell );
07290     layout->addMultiCell( cell, cell->row(), cell->row() + cell->rowspan()-1,
07291                           cell->column(), cell->column() + cell->colspan()-1 );
07292 }
07293 
07294 bool QTextTable::enter( QTextCursor *c, QTextDocument *&doc, QTextParagraph *&parag, int &idx, int &ox, int &oy, bool atEnd )
07295 {
07296     currCell.remove( c );
07297     if ( !atEnd )
07298         return next( c, doc, parag, idx, ox, oy );
07299     currCell.insert( c, cells.count() );
07300     return prev( c, doc, parag, idx, ox, oy );
07301 }
07302 
07303 bool QTextTable::enterAt( QTextCursor *c, QTextDocument *&doc, QTextParagraph *&parag, int &idx, int &ox, int &oy, const QPoint &pos )
07304 {
07305     currCell.remove( c );
07306     int lastCell = -1;
07307     int lastY = -1;
07308     int i;
07309     for ( i = 0; i < (int)cells.count(); ++i ) {
07310         QTextTableCell *cell = cells.at( i );
07311         if ( !cell )
07312             continue;
07313         QRect r( cell->geometry().x(),
07314                  cell->geometry().y(),
07315                  cell->geometry().width() + 2 * innerborder + 2 * outerborder,
07316                  cell->geometry().height() + 2 * innerborder + 2 * outerborder );
07317 
07318         if ( r.left() <= pos.x() && r.right() >= pos.x() ) {
07319             if ( cell->geometry().y() > lastY ) {
07320                 lastCell = i;
07321                 lastY = cell->geometry().y();
07322             }
07323             if ( r.top() <= pos.y() && r.bottom() >= pos.y() ) {
07324                 currCell.insert( c, i );
07325                 break;
07326             }
07327         }
07328     }
07329     if ( i == (int) cells.count() )
07330         return FALSE; // no cell found
07331 
07332     if ( currCell.find( c ) == currCell.end() ) {
07333         if ( lastY != -1 )
07334             currCell.insert( c, lastCell );
07335         else
07336             return FALSE;
07337     }
07338 
07339     QTextTableCell *cell = cells.at( *currCell.find( c ) );
07340     if ( !cell )
07341         return FALSE;
07342     doc = cell->richText();
07343     parag = doc->firstParagraph();
07344     idx = 0;
07345     ox += cell->geometry().x() + cell->horizontalAlignmentOffset() + outerborder + parent->x();
07346     oy += cell->geometry().y() + cell->verticalAlignmentOffset() + outerborder;
07347     return TRUE;
07348 }
07349 
07350 bool QTextTable::next( QTextCursor *c, QTextDocument *&doc, QTextParagraph *&parag, int &idx, int &ox, int &oy )
07351 {
07352     int cc = -1;
07353     if ( currCell.find( c ) != currCell.end() )
07354         cc = *currCell.find( c );
07355     if ( cc > (int)cells.count() - 1 || cc < 0 )
07356         cc = -1;
07357     currCell.remove( c );
07358     currCell.insert( c, ++cc );
07359     if ( cc >= (int)cells.count() ) {
07360         currCell.insert( c, 0 );
07361         QTextCustomItem::next( c, doc, parag, idx, ox, oy );
07362         QTextTableCell *cell = cells.first();
07363         if ( !cell )
07364             return FALSE;
07365         doc = cell->richText();
07366         idx = -1;
07367         return TRUE;
07368     }
07369 
07370     if ( currCell.find( c ) == currCell.end() )
07371         return FALSE;
07372     QTextTableCell *cell = cells.at( *currCell.find( c ) );
07373     if ( !cell )
07374         return FALSE;
07375     doc = cell->richText();
07376     parag = doc->firstParagraph();
07377     idx = 0;
07378     ox += cell->geometry().x() + cell->horizontalAlignmentOffset() + outerborder + parent->x();
07379     oy += cell->geometry().y() + cell->verticalAlignmentOffset() + outerborder;
07380     return TRUE;
07381 }
07382 
07383 bool QTextTable::prev( QTextCursor *c, QTextDocument *&doc, QTextParagraph *&parag, int &idx, int &ox, int &oy )
07384 {
07385     int cc = -1;
07386     if ( currCell.find( c ) != currCell.end() )
07387         cc = *currCell.find( c );
07388     if ( cc > (int)cells.count() - 1 || cc < 0 )
07389         cc = cells.count();
07390     currCell.remove( c );
07391     currCell.insert( c, --cc );
07392     if ( cc < 0 ) {
07393         currCell.insert( c, 0 );
07394         QTextCustomItem::prev( c, doc, parag, idx, ox, oy );
07395         QTextTableCell *cell = cells.first();
07396         if ( !cell )
07397             return FALSE;
07398         doc = cell->richText();
07399         idx = -1;
07400         return TRUE;
07401     }
07402 
07403     if ( currCell.find( c ) == currCell.end() )
07404         return FALSE;
07405     QTextTableCell *cell = cells.at( *currCell.find( c ) );
07406     if ( !cell )
07407         return FALSE;
07408     doc = cell->richText();
07409     parag = doc->lastParagraph();
07410     idx = parag->length() - 1;
07411     ox += cell->geometry().x() + cell->horizontalAlignmentOffset() + outerborder + parent->x();
07412     oy += cell->geometry().y()  + cell->verticalAlignmentOffset() + outerborder;
07413     return TRUE;
07414 }
07415 
07416 bool QTextTable::down( QTextCursor *c, QTextDocument *&doc, QTextParagraph *&parag, int &idx, int &ox, int &oy )
07417 {
07418     if ( currCell.find( c ) == currCell.end() )
07419         return FALSE;
07420     QTextTableCell *cell = cells.at( *currCell.find( c ) );
07421     if ( cell->row_ == layout->numRows() - 1 ) {
07422         currCell.insert( c, 0 );
07423         QTextCustomItem::down( c, doc, parag, idx, ox, oy );
07424         QTextTableCell *cell = cells.first();
07425         if ( !cell )
07426             return FALSE;
07427         doc = cell->richText();
07428         idx = -1;
07429         return TRUE;
07430     }
07431 
07432     int oldRow = cell->row_;
07433     int oldCol = cell->col_;
07434     if ( currCell.find( c ) == currCell.end() )
07435         return FALSE;
07436     int cc = *currCell.find( c );
07437     for ( int i = cc; i < (int)cells.count(); ++i ) {
07438         cell = cells.at( i );
07439         if ( cell->row_ > oldRow && cell->col_ == oldCol ) {
07440             currCell.insert( c, i );
07441             break;
07442         }
07443     }
07444     doc = cell->richText();
07445     if ( !cell )
07446         return FALSE;
07447     parag = doc->firstParagraph();
07448     idx = 0;
07449     ox += cell->geometry().x() + cell->horizontalAlignmentOffset() + outerborder + parent->x();
07450     oy += cell->geometry().y()  + cell->verticalAlignmentOffset() + outerborder;
07451     return TRUE;
07452 }
07453 
07454 bool QTextTable::up( QTextCursor *c, QTextDocument *&doc, QTextParagraph *&parag, int &idx, int &ox, int &oy )
07455 {
07456     if ( currCell.find( c ) == currCell.end() )
07457         return FALSE;
07458     QTextTableCell *cell = cells.at( *currCell.find( c ) );
07459     if ( cell->row_ == 0 ) {
07460         currCell.insert( c, 0 );
07461         QTextCustomItem::up( c, doc, parag, idx, ox, oy );
07462         QTextTableCell *cell = cells.first();
07463         if ( !cell )
07464             return FALSE;
07465         doc = cell->richText();
07466         idx = -1;
07467         return TRUE;
07468     }
07469 
07470     int oldRow = cell->row_;
07471     int oldCol = cell->col_;
07472     if ( currCell.find( c ) == currCell.end() )
07473         return FALSE;
07474     int cc = *currCell.find( c );
07475     for ( int i = cc; i >= 0; --i ) {
07476         cell = cells.at( i );
07477         if ( cell->row_ < oldRow && cell->col_ == oldCol ) {
07478             currCell.insert( c, i );
07479             break;
07480         }
07481     }
07482     doc = cell->richText();
07483     if ( !cell )
07484         return FALSE;
07485     parag = doc->lastParagraph();
07486     idx = parag->length() - 1;
07487     ox += cell->geometry().x() + cell->horizontalAlignmentOffset() + outerborder + parent->x();
07488     oy += cell->geometry().y()  + cell->verticalAlignmentOffset() + outerborder;
07489     return TRUE;
07490 }
07491 
07492 QTextTableCell::QTextTableCell( QTextTable* table,
07493                                 int row, int column,
07494                                 const QMap<QString, QString> &attr,
07495                                 const QStyleSheetItem* /*style*/, // ### use them
07496                                 const QTextFormat& /*fmt*/, const QString& context,
07497                                 QMimeSourceFactory &factory, QStyleSheet *sheet,
07498                                 const QString& doc)
07499 {
07500     cached_width = -1;
07501     cached_sizehint = -1;
07502 
07503     maxw = QWIDGETSIZE_MAX;
07504     minw = 0;
07505 
07506     parent = table;
07507     row_ = row;
07508     col_ = column;
07509     stretch_ = 0;
07510     richtext = new QTextDocument( table->parent );
07511     richtext->setTableCell( this );
07512     QString a = *attr.find( "align" );
07513     if ( !a.isEmpty() ) {
07514         a = a.lower();
07515         if ( a == "left" )
07516             richtext->setAlignment( Qt::AlignLeft );
07517         else if ( a == "center" )
07518             richtext->setAlignment( Qt::AlignHCenter );
07519         else if ( a == "right" )
07520             richtext->setAlignment( Qt::AlignRight );
07521     }
07522     align = 0;
07523     QString va = *attr.find( "valign" );
07524     if ( !va.isEmpty() ) {
07525         va = va.lower();
07526         if ( va == "center" )
07527             align |= Qt::AlignVCenter;
07528         else if ( va == "bottom" )
07529             align |= Qt::AlignBottom;
07530     }
07531     richtext->setFormatter( table->parent->formatter() );
07532     richtext->setUseFormatCollection( table->parent->useFormatCollection() );
07533     richtext->setMimeSourceFactory( &factory );
07534     richtext->setStyleSheet( sheet );
07535     richtext->setDefaultFormat( table->parent->formatCollection()->defaultFormat()->font(),
07536                                 table->parent->formatCollection()->defaultFormat()->color() );
07537     richtext->setRichText( doc, context );
07538     rowspan_ = 1;
07539     colspan_ = 1;
07540     if ( attr.contains("colspan") )
07541         colspan_ = attr["colspan"].toInt();
07542     if ( attr.contains("rowspan") )
07543         rowspan_ = attr["rowspan"].toInt();
07544 
07545     background = 0;
07546     if ( attr.contains("bgcolor") ) {
07547         background = new QBrush(QColor( attr["bgcolor"] ));
07548     }
07549 
07550 
07551     hasFixedWidth = FALSE;
07552     if ( attr.contains("width") ) {
07553         bool b;
07554         QString s( attr["width"] );
07555         int w = s.toInt( &b );
07556         if ( b ) {
07557             maxw = w;
07558             minw = maxw;
07559             hasFixedWidth = TRUE;
07560         } else {
07561             s = s.stripWhiteSpace();
07562             if ( s.length() > 1 && s[ (int)s.length()-1 ] == '%' )
07563                 stretch_ = s.left( s.length()-1).toInt();
07564         }
07565     }
07566 
07567     attributes = attr;
07568 
07569     parent->addCell( this );
07570 }
07571 
07572 QTextTableCell::~QTextTableCell()
07573 {
07574     delete background;
07575     background = 0;
07576     delete richtext;
07577     richtext = 0;
07578 }
07579 
07580 QSize QTextTableCell::sizeHint() const
07581 {
07582     int extra = 2 * ( parent->innerborder + parent->cellpadding + border_tolerance);
07583     int used = richtext->widthUsed() + extra;
07584 
07585     if  (stretch_ ) {
07586         int w = parent->width * stretch_ / 100 - 2*parent->cellspacing - 2*parent->cellpadding;
07587         return QSize( QMIN( w, maxw ), 0 ).expandedTo( minimumSize() );
07588     }
07589 
07590     return QSize( used, 0 ).expandedTo( minimumSize() );
07591 }
07592 
07593 QSize QTextTableCell::minimumSize() const
07594 {
07595     int extra = 2 * ( parent->innerborder + parent->cellpadding + border_tolerance);
07596     return QSize( QMAX( richtext->minimumWidth() + extra, minw), 0 );
07597 }
07598 
07599 QSize QTextTableCell::maximumSize() const
07600 {
07601     return QSize( QWIDGETSIZE_MAX, QWIDGETSIZE_MAX );
07602 }
07603 
07604 QSizePolicy::ExpandData QTextTableCell::expanding() const
07605 {
07606     return QSizePolicy::BothDirections;
07607 }
07608 
07609 bool QTextTableCell::isEmpty() const
07610 {
07611     return FALSE;
07612 }
07613 void QTextTableCell::setGeometry( const QRect& r )
07614 {
07615     int extra = 2 * ( parent->innerborder + parent->cellpadding );
07616     if ( r.width() != cached_width )
07617         richtext->doLayout( QTextFormat::painter(), r.width() - extra );
07618     cached_width = r.width();
07619     geom = r;
07620 }
07621 
07622 QRect QTextTableCell::geometry() const
07623 {
07624     return geom;
07625 }
07626 
07627 bool QTextTableCell::hasHeightForWidth() const
07628 {
07629     return TRUE;
07630 }
07631 
07632 int QTextTableCell::heightForWidth( int w ) const
07633 {
07634     int extra = 2 * ( parent->innerborder + parent->cellpadding );
07635     w = QMAX( minw, w );
07636 
07637     if ( cached_width != w ) {
07638         QTextTableCell* that = (QTextTableCell*) this;
07639         that->richtext->doLayout( QTextFormat::painter(), w - extra );
07640         that->cached_width = w;
07641     }
07642     return richtext->height() + extra;
07643 }
07644 
07645 void QTextTableCell::adjustToPainter( QPainter* p )
07646 {
07647     QTextParagraph *parag = richtext->firstParagraph();
07648     while ( parag ) {
07649         parag->adjustToPainter( p );
07650         parag = parag->next();
07651     }
07652 }
07653 
07654 int QTextTableCell::horizontalAlignmentOffset() const
07655 {
07656     return parent->cellpadding;
07657 }
07658 
07659 int QTextTableCell::verticalAlignmentOffset() const
07660 {
07661     if ( (align & Qt::AlignVCenter ) == Qt::AlignVCenter )
07662         return ( geom.height() - richtext->height() ) / 2;
07663     else if ( ( align & Qt::AlignBottom ) == Qt::AlignBottom )
07664         return geom.height() - parent->cellpadding - richtext->height()  ;
07665     return parent->cellpadding;
07666 }
07667 
07668 void QTextTableCell::draw( QPainter* p, int x, int y, int cx, int cy, int cw, int ch, const QColorGroup& cg, bool )
07669 {
07670     if ( cached_width != geom.width() ) {
07671         int extra = 2 * ( parent->innerborder + parent->cellpadding );
07672         richtext->doLayout( p, geom.width() - extra );
07673         cached_width = geom.width();
07674     }
07675     QColorGroup g( cg );
07676     if ( background )
07677         g.setBrush( QColorGroup::Base, *background );
07678     else if ( richtext->paper() )
07679         g.setBrush( QColorGroup::Base, *richtext->paper() );
07680 
07681     p->save();
07682     p->translate( x + geom.x(), y + geom.y() );
07683     if ( background )
07684         p->fillRect( 0, 0, geom.width(), geom.height(), *background );
07685     else if ( richtext->paper() )
07686         p->fillRect( 0, 0, geom.width(), geom.height(), *richtext->paper() );
07687 
07688     p->translate( horizontalAlignmentOffset(), verticalAlignmentOffset() );
07689 
07690     QRegion r;
07691     if ( cx >= 0 && cy >= 0 )
07692         richtext->draw( p, cx - ( x + horizontalAlignmentOffset() + geom.x() ),
07693                         cy - ( y + geom.y() + verticalAlignmentOffset() ),
07694                         cw, ch, g, FALSE, FALSE, 0 );
07695     else
07696         richtext->draw( p, -1, -1, -1, -1, g, FALSE, FALSE, 0 );
07697 
07698     p->restore();
07699 }
07700 
07701 QString QTextDocument::section( QString str, const QString &sep, int start, int end )
07702 {
07703     const QChar *uc = str.unicode();
07704     if ( !uc )
07705         return QString();
07706     QString _sep = sep;
07707     const QChar *uc_sep = _sep.unicode();
07708     if(!uc_sep)
07709         return QString();
07710     bool match = FALSE, last_match = TRUE;
07711 
07712     //find start
07713     int n = str.length(), sep_len = _sep.length();
07714     const QChar *begin = start < 0 ? uc + n : uc;
07715     while(start) {
07716         match = FALSE;
07717         int c = 0;
07718         for(const QChar *tmp = start < 0 ? begin - sep_len : begin;
07719             c < sep_len && tmp < uc + n && tmp >= uc; tmp++, c++) {
07720             if( *tmp != *(uc_sep + c) )
07721                 break;
07722             if(c == sep_len - 1) {
07723                 match = TRUE;
07724                 break;
07725             }
07726         }
07727         last_match = match;
07728 
07729         if(start < 0) {
07730             if(match) {
07731                 begin -= sep_len;
07732                 if(!++start)
07733                     break;
07734             } else {
07735                 if(start == -1 && begin == uc)
07736                     break;
07737                 begin--;
07738             }
07739         } else {
07740             if(match) {
07741                 if(!--start)
07742                     break;
07743                 begin += sep_len;
07744             } else {
07745                 if(start == 1 && begin == uc + n)
07746                     break;
07747                 begin++;
07748             }
07749         }
07750         if(begin > uc + n || begin < uc)
07751             return QString();
07752     }
07753     if(match)
07754         begin+=sep_len;
07755     if(begin > uc + n || begin < uc)
07756         return QString();
07757 
07758     //now find last
07759     match = FALSE;
07760     const QChar *last = end < 0 ? uc + n : uc;
07761     if(end == -1) {
07762         int c = 0;
07763         for(const QChar *tmp = end < 0 ? last - sep_len : last;
07764             c < sep_len && tmp < uc + n && tmp >= uc; tmp++, c++) {
07765             if( *tmp != *(uc_sep + c) )
07766                 break;
07767             if(c == sep_len - 1) {
07768                 match = TRUE;
07769                 break;
07770             }
07771         }
07772     } else {
07773         end++;
07774         last_match = TRUE;
07775         while(end) {
07776             match = FALSE;
07777             int c = 0;
07778             for(const QChar *tmp = end < 0 ? last - sep_len : last;
07779                 c < sep_len && tmp < uc + n && tmp >= uc; tmp++, c++) {
07780                 if( *tmp != *(uc_sep + c) )
07781                     break;
07782                 if(c == sep_len - 1) {
07783                     match = TRUE;
07784                     break;
07785                 }
07786             }
07787             last_match = match;
07788 
07789             if(end < 0) {
07790                 if(match) {
07791                     if(!++end)
07792                         break;
07793                     last -= sep_len;
07794                 } else {
07795                     last--;
07796                 }
07797             } else {
07798                 if(match) {
07799                     last += sep_len;
07800                     if(!--end)
07801                         break;
07802                 } else {
07803                     last++;
07804                 }
07805             }
07806             if(last >= uc + n) {
07807                 last = uc + n;
07808                 break;
07809             } else if(last < uc) {
07810                 return QString();
07811             }
07812         }
07813     }
07814     if(match)
07815         last -= sep_len;
07816     if(last < uc || last > uc + n || begin >= last)
07817         return QString();
07818 
07819     //done
07820     return QString(begin, last - begin);
07821 }
07822 
07823 bool QTextDocument::endsWith( QString str, const QString &s)
07824 {
07825     if ( str.isNull() )
07826         return s.isNull();
07827     int pos = str.length() - s.length();
07828     if ( pos < 0 )
07829         return FALSE;
07830     for ( uint i = 0; i < s.length(); i++ ) {
07831         if ( str.unicode()[pos+i] != s[(int)i] )
07832             return FALSE;
07833     }
07834     return TRUE;
07835 }

Generated on Sat Nov 5 16:17:04 2005 for OPIE by  doxygen 1.4.2