00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038 #include "qrichtext_p.h"
00039
00040
00041 #include <opie2/odebug.h>
00042 using namespace Opie::Core;
00043
00044
00045 #include "qdragobject.h"
00046 #include "qpaintdevicemetrics.h"
00047 #include "qdrawutil.h"
00048 #include "qcleanuphandler.h"
00049
00050
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;
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 ) {
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 {
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 );
00519 if ( end == -1 )
00520 end = s.length();
00521 int len = (start == -1 ? end : end - start - 1);
00522 if ( len > 0 )
00523 para->insert( idx, s.unicode() + start + 1, len );
00524 else
00525 para->invalidate( 0 );
00526 if ( formatting ) {
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;
00536 idx += len;
00537 if ( s[end] == '\n' ) {
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
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
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
01469 QMap<QString, QString> attr;
01470 bool emptyTag = FALSE;
01471 QString tagname = parseOpenTag(doc, length, pos, attr, emptyTag);
01472 if ( tagname.isEmpty() )
01473 continue;
01474
01475 const QStyleSheetItem* nstyle = sheet_->item(tagname);
01476
01477 if ( nstyle ) {
01478
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
01490
01491
01492
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
01504 if ( nstyle->name() == "ul" || nstyle->name() == "ol" )
01505 hasNewPar = FALSE;
01506 if ( !hasNewPar ) {
01507
01508
01509 while ( curtag.style->displayMode() == QStyleSheetItem::DisplayListItem ) {
01510 if ( tags.isEmpty() )
01511 break;
01512 curtag = tags.pop();
01513 }
01514 } else if ( canMergeLi ) {
01515
01516
01517 nstyle = curtag.style;
01518 }
01519 canMergeLi = FALSE;
01520 }
01521 }
01522 }
01523
01524 QTextCustomItem* custom = 0;
01525
01526
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;
01600 }
01601
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 }
01617
01618 if ( !custom )
01619 custom = sheet_->tag( tagname, attr, contxt, *factory_ , emptyTag, this );
01620
01621 if ( !nstyle && !custom )
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
01640
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
01658
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
01704
01705
01706
01707
01708
01709
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 )
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;
01763 if ( !sheet_->item( tagname ) )
01764 continue;
01765
01766
01767 bool needNewPar = curtag.style->displayMode() == QStyleSheetItem::DisplayBlock
01768 || curtag.style->displayMode() == QStyleSheetItem::DisplayListItem;
01769
01770
01771
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
01784 if ( !tags.isEmpty() )
01785 curtag = tags.pop();
01786 else
01787 curtag = initag;
01788
01789 if ( needNewPar ) {
01790 if ( textEditMode && tagname == "p" )
01791 hasNewPar = FALSE;
01792 NEWPAR;
01793 }
01794 }
01795 } else {
01796
01797 QString s;
01798 QChar c;
01799 while ( pos < length && !hasPrefix(doc, length, pos, QChar('<') ) ){
01800 if ( textEditMode ) {
01801
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
01810 if ( c == ' ' && curtag.wsm == QStyleSheetItem::WhiteSpacePre )
01811 c = QChar::nbsp;
01812
01813 if ( c == ' ' || c == QChar_linesep ) {
01814
01815
01816
01817
01818
01819
01820 if ( curtag.wsm == QStyleSheetItem::WhiteSpaceNormal && s.length() > 4096 ) do {
01821 if ( doc[l] == '\n' ) {
01822 hasNewPar = FALSE;
01823 NEWPAR;
01824 hasNewPar = FALSE;
01825 c = '\n';
01826 break;
01827 }
01828 } while ( ++l < pos );
01829 }
01830 }
01831
01832 if ( c == '\n' )
01833 break;
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 );
01850 f->ref += s.length() -1;
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
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
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
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
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
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
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
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
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
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
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
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
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
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 ¶gId, 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 ¶gId, 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
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
02527
02528
02529
02530
02531
02532
02533
02534
02535
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
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
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
02627
02628
02629
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
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
02776
02777
02778
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
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
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
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
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
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
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;
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 )
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
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
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
04009
04010
04011
04012
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 ) {
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
04029
04030
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
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
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;
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();
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
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
04255
04256
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
04269 bool flush = i== length()-1;
04270 bool selectionStateChanged = FALSE;
04271 if ( !flush ) {
04272 QTextStringChar *nextchr = at( i+1 );
04273
04274 flush |= nextchr->lineStart;
04275
04276 flush |= ( nextchr->format() != chr->format() );
04277
04278 flush |= ( nextchr->isAnchor() != chr->isAnchor() );
04279
04280 flush |= nextchr->startOfRun;
04281
04282 flush |= ( nextchr->rightToLeft != chr->rightToLeft );
04283
04284 flush |= ( chr->c == '\t' );
04285
04286 flush |= ( chr->c.unicode() == 0xad );
04287
04288 flush |= chr->isCustom();
04289
04290 flush |= nextchr->isCustom();
04291
04292 if ((alignment() & Qt3::AlignJustify) == Qt3::AlignJustify )
04293 flush |= QTextFormatter::isBreakable( str, i );
04294
04295 flush |= ( i - paintStart >= 256 );
04296
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
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 ) {
04316 break;
04317 }
04318
04319
04320 if ( line == 0 && isListItem() )
04321 drawLabel( &painter, chr->x, y, 0, 0, baseLine, cg );
04322 }
04323
04324
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
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
04338 && (it.key() != QTextDocument::Standard || !is_printer( &painter) ) ) {
04339 selection = it.key();
04340 break;
04341 }
04342 }
04343
04344 if ( flush ) {
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
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
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 ) {
04524 int depth = listDepth();
04525 list_val--;
04526
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:
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 += "<";
04708 else if ( c->c == '>' )
04709 s += ">";
04710 else if ( c->isCustom() )
04711 s += c->customItem()->richText();
04712 else if ( c->c == '\n' || c->c == QChar_linesep )
04713 s += "<br />";
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
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
04884 for ( int j = last-1; j >= start; --j ) {
04885
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
04921 QTextLineStart *QTextFormatter::bidiReorderLine( QTextParagraph * , 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
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
04945
04946 int numSpaces = 0;
04947
04948 if( align == Qt3::AlignAuto ) {
04949
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
04960 for ( int j = last-1; j >= start; --j ) {
04961
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
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 )
05053 return TRUE;
05054 if ( !ch ) {
05055
05056 uchar row = c.row();
05057 if ( row == 0x0e ) {
05058
05059 if ( c.cell() < 0x80 ) {
05060 #ifdef HAVE_THAI_BREAKS
05061
05062 if( string != cachedString ) {
05063
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
05081
05082 return TRUE;
05083 #endif
05084 } else
05085 return FALSE;
05086 }
05087 if ( row < 0x11 )
05088 return FALSE;
05089 if ( row > 0x2d && row < 0xfb || row == 0x11 )
05090
05091
05092 return TRUE;
05093 }
05094 return FALSE;
05095 }
05096
05097 void QTextFormatter::insertLineStart( QTextParagraph *parag, int index, QTextLineStart *ls )
05098 {
05099 if ( index > 0 ) {
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
05115
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 = ¶g->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;
05181 if ( start == 0 )
05182 c = ¶g->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 = ¶g->string()->at( i );
05197 c->rightToLeft = FALSE;
05198
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 = ¶g->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
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
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
05430
05431
05432
05433
05434
05435
05436
05437
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
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' ) {
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 {
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' ) {
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
05532
05533
05534
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
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
05570
05571
05572
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
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
05935
05936
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
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
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" ) ) {
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 )
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 {
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
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
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
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 , int h )
06945 {
06946 if ( pagesize > 0 ) {
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 , QTextFlow* )
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 *¶g, 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 *¶g, 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;
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 *¶g, 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 *¶g, 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 *¶g, 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 *¶g, 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* ,
07496 const QTextFormat& , 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
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
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
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 }