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