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 #include "KoTextParag.h"
00039
00040 #include <qpaintdevicemetrics.h>
00041 #include "qdrawutil.h"
00042
00043 #include <stdlib.h>
00044 #include "KoParagCounter.h"
00045 #include "KoTextDocument.h"
00046 #include <kdebug.h>
00047 #include <kdeversion.h>
00048 #include <kglobal.h>
00049 #include <klocale.h>
00050 #include <private/qtextengine_p.h>
00051
00052
00053
00054
00055
00056
00057
00058
00059 void KoTextDocCommandHistory::addCommand( KoTextDocCommand *cmd )
00060 {
00061 if ( current < (int)history.count() - 1 ) {
00062 QPtrList<KoTextDocCommand> commands;
00063 commands.setAutoDelete( FALSE );
00064
00065 for( int i = 0; i <= current; ++i ) {
00066 commands.insert( i, history.at( 0 ) );
00067 history.take( 0 );
00068 }
00069
00070 commands.append( cmd );
00071 history.clear();
00072 history = commands;
00073 history.setAutoDelete( TRUE );
00074 } else {
00075 history.append( cmd );
00076 }
00077
00078 if ( (int)history.count() > steps )
00079 history.removeFirst();
00080 else
00081 ++current;
00082 }
00083
00084 KoTextCursor *KoTextDocCommandHistory::undo( KoTextCursor *c )
00085 {
00086 if ( current > -1 ) {
00087 KoTextCursor *c2 = history.at( current )->unexecute( c );
00088 --current;
00089 return c2;
00090 }
00091 return 0;
00092 }
00093
00094 KoTextCursor *KoTextDocCommandHistory::redo( KoTextCursor *c )
00095 {
00096 if ( current > -1 ) {
00097 if ( current < (int)history.count() - 1 ) {
00098 ++current;
00099 return history.at( current )->execute( c );
00100 }
00101 } else {
00102 if ( history.count() > 0 ) {
00103 ++current;
00104 return history.at( current )->execute( c );
00105 }
00106 }
00107 return 0;
00108 }
00109
00110 bool KoTextDocCommandHistory::isUndoAvailable()
00111 {
00112 return current > -1;
00113 }
00114
00115 bool KoTextDocCommandHistory::isRedoAvailable()
00116 {
00117 return current > -1 && current < (int)history.count() - 1 || current == -1 && history.count() > 0;
00118 }
00119
00120
00121
00122 KoTextDocDeleteCommand::KoTextDocDeleteCommand( KoTextDocument *d, int i, int idx, const QMemArray<KoTextStringChar> &str )
00123 : KoTextDocCommand( d ), id( i ), index( idx ), parag( 0 ), text( str )
00124 {
00125 for ( int j = 0; j < (int)text.size(); ++j ) {
00126 if ( text[ j ].format() )
00127 text[ j ].format()->addRef();
00128 }
00129 }
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140 KoTextDocDeleteCommand::~KoTextDocDeleteCommand()
00141 {
00142 for ( int i = 0; i < (int)text.size(); ++i ) {
00143 if ( text[ i ].format() )
00144 text[ i ].format()->removeRef();
00145 }
00146 text.resize( 0 );
00147 }
00148
00149 KoTextCursor *KoTextDocDeleteCommand::execute( KoTextCursor *c )
00150 {
00151 KoTextParag *s = doc ? doc->paragAt( id ) : parag;
00152 if ( !s ) {
00153 if(doc)
00154 kdWarning(32500) << "can't locate parag at " << id << ", last parag: " << doc->lastParag()->paragId() << endl;
00155 return 0;
00156 }
00157
00158 cursor.setParag( s );
00159 cursor.setIndex( index );
00160 int len = text.size();
00161 if ( c )
00162 *c = cursor;
00163 if ( doc ) {
00164 doc->setSelectionStart( KoTextDocument::Temp, &cursor );
00165 for ( int i = 0; i < len; ++i )
00166 cursor.gotoNextLetter();
00167 doc->setSelectionEnd( KoTextDocument::Temp, &cursor );
00168 doc->removeSelectedText( KoTextDocument::Temp, &cursor );
00169 if ( c )
00170 *c = cursor;
00171 } else {
00172 s->remove( index, len );
00173 }
00174
00175 return c;
00176 }
00177
00178 KoTextCursor *KoTextDocDeleteCommand::unexecute( KoTextCursor *c )
00179 {
00180 KoTextParag *s = doc ? doc->paragAt( id ) : parag;
00181 if ( !s ) {
00182 if(doc)
00183 kdWarning(32500) << "can't locate parag at " << id << ", last parag: " << doc->lastParag()->paragId() << endl;
00184 return 0;
00185 }
00186
00187 cursor.setParag( s );
00188 cursor.setIndex( index );
00189 QString str = KoTextString::toString( text );
00190 cursor.insert( str, TRUE, &text );
00191 cursor.setParag( s );
00192 cursor.setIndex( index );
00193 if ( c ) {
00194 c->setParag( s );
00195 c->setIndex( index );
00196 for ( int i = 0; i < (int)text.size(); ++i )
00197 c->gotoNextLetter();
00198 }
00199
00200 s = cursor.parag();
00201 while ( s ) {
00202 s->format();
00203 s->setChanged( TRUE );
00204 if ( c && s == c->parag() )
00205 break;
00206 s = s->next();
00207 }
00208
00209 return &cursor;
00210 }
00211
00212 KoTextDocFormatCommand::KoTextDocFormatCommand( KoTextDocument *d, int sid, int sidx, int eid, int eidx,
00213 const QMemArray<KoTextStringChar> &old, const KoTextFormat *f, int fl )
00214 : KoTextDocCommand( d ), startId( sid ), startIndex( sidx ), endId( eid ), endIndex( eidx ), oldFormats( old ), flags( fl )
00215 {
00216 format = d->formatCollection()->format( f );
00217 for ( int j = 0; j < (int)oldFormats.size(); ++j ) {
00218 if ( oldFormats[ j ].format() )
00219 oldFormats[ j ].format()->addRef();
00220 }
00221 }
00222
00223 KoTextDocFormatCommand::~KoTextDocFormatCommand()
00224 {
00225 format->removeRef();
00226 for ( int j = 0; j < (int)oldFormats.size(); ++j ) {
00227 if ( oldFormats[ j ].format() )
00228 oldFormats[ j ].format()->removeRef();
00229 }
00230 }
00231
00232 KoTextCursor *KoTextDocFormatCommand::execute( KoTextCursor *c )
00233 {
00234 KoTextParag *sp = doc->paragAt( startId );
00235 KoTextParag *ep = doc->paragAt( endId );
00236 if ( !sp || !ep )
00237 return c;
00238
00239 KoTextCursor start( doc );
00240 start.setParag( sp );
00241 start.setIndex( startIndex );
00242 KoTextCursor end( doc );
00243 end.setParag( ep );
00244 end.setIndex( endIndex );
00245
00246 doc->setSelectionStart( KoTextDocument::Temp, &start );
00247 doc->setSelectionEnd( KoTextDocument::Temp, &end );
00248 doc->setFormat( KoTextDocument::Temp, format, flags );
00249 doc->removeSelection( KoTextDocument::Temp );
00250 if ( endIndex == ep->length() )
00251 end.gotoLeft();
00252 *c = end;
00253 return c;
00254 }
00255
00256 KoTextCursor *KoTextDocFormatCommand::unexecute( KoTextCursor *c )
00257 {
00258 KoTextParag *sp = doc->paragAt( startId );
00259 KoTextParag *ep = doc->paragAt( endId );
00260 if ( !sp || !ep )
00261 return 0;
00262
00263 int idx = startIndex;
00264 int fIndex = 0;
00265 if( !oldFormats.isEmpty())
00266 {
00267 for ( ;; ) {
00268 if ( oldFormats.at( fIndex ).c == '\n' ) {
00269 if ( idx > 0 ) {
00270 if ( idx < sp->length() && fIndex > 0 )
00271 sp->setFormat( idx, 1, oldFormats.at( fIndex - 1 ).format() );
00272 if ( sp == ep )
00273 break;
00274 sp = sp->next();
00275 idx = 0;
00276 }
00277 fIndex++;
00278 }
00279 if ( oldFormats.at( fIndex ).format() )
00280 sp->setFormat( idx, 1, oldFormats.at( fIndex ).format() );
00281 idx++;
00282 fIndex++;
00283 if ( fIndex >= (int)oldFormats.size() )
00284 break;
00285 if ( idx >= sp->length() ) {
00286 if ( sp == ep )
00287 break;
00288 sp = sp->next();
00289 idx = 0;
00290 }
00291 }
00292 }
00293 KoTextCursor end( doc );
00294 end.setParag( ep );
00295 end.setIndex( endIndex );
00296 if ( endIndex == ep->length() )
00297 end.gotoLeft();
00298 *c = end;
00299 return c;
00300 }
00301
00302 KoTextAlignmentCommand::KoTextAlignmentCommand( KoTextDocument *d, int fParag, int lParag, int na, const QMemArray<int> &oa )
00303 : KoTextDocCommand( d ), firstParag( fParag ), lastParag( lParag ), newAlign( na ), oldAligns( oa )
00304 {
00305 }
00306
00307 KoTextCursor *KoTextAlignmentCommand::execute( KoTextCursor *c )
00308 {
00309 KoTextParag *p = doc->paragAt( firstParag );
00310 if ( !p )
00311 return c;
00312 while ( p ) {
00313 p->setAlignment( newAlign );
00314 if ( p->paragId() == lastParag )
00315 break;
00316 p = p->next();
00317 }
00318 return c;
00319 }
00320
00321 KoTextCursor *KoTextAlignmentCommand::unexecute( KoTextCursor *c )
00322 {
00323 KoTextParag *p = doc->paragAt( firstParag );
00324 if ( !p )
00325 return c;
00326 int i = 0;
00327 while ( p ) {
00328 if ( i < (int)oldAligns.size() )
00329 p->setAlignment( oldAligns.at( i ) );
00330 if ( p->paragId() == lastParag )
00331 break;
00332 p = p->next();
00333 ++i;
00334 }
00335 return c;
00336 }
00337
00338
00339
00340
00341 KoTextCursor::KoTextCursor( KoTextDocument *d )
00342 : doc( d )
00343 {
00344 idx = 0;
00345 string = doc ? doc->firstParag() : 0;
00346 tmpIndex = -1;
00347 }
00348
00349 KoTextCursor::KoTextCursor()
00350 {
00351 }
00352
00353 KoTextCursor::KoTextCursor( const KoTextCursor &c )
00354 {
00355 doc = c.doc;
00356 idx = c.idx;
00357 string = c.string;
00358 tmpIndex = c.tmpIndex;
00359 }
00360
00361 KoTextCursor &KoTextCursor::operator=( const KoTextCursor &c )
00362 {
00363 doc = c.doc;
00364 idx = c.idx;
00365 string = c.string;
00366 tmpIndex = c.tmpIndex;
00367
00368 return *this;
00369 }
00370
00371 bool KoTextCursor::operator==( const KoTextCursor &c ) const
00372 {
00373 return doc == c.doc && string == c.string && idx == c.idx;
00374 }
00375
00376 void KoTextCursor::insert( const QString &str, bool checkNewLine, QMemArray<KoTextStringChar> *formatting )
00377 {
00378 string->invalidate( idx );
00379 tmpIndex = -1;
00380 bool justInsert = TRUE;
00381 QString s( str );
00382 #if defined(Q_WS_WIN)
00383 if ( checkNewLine )
00384 s = s.replace( QRegExp( "\\r" ), "" );
00385 #endif
00386 if ( checkNewLine )
00387 justInsert = s.find( '\n' ) == -1;
00388 if ( justInsert ) {
00389 string->insert( idx, s );
00390 if ( formatting ) {
00391 for ( int i = 0; i < (int)s.length(); ++i ) {
00392 if ( formatting->at( i ).format() ) {
00393 formatting->at( i ).format()->addRef();
00394 string->string()->setFormat( idx + i, formatting->at( i ).format(), TRUE );
00395 }
00396 }
00397 }
00398 idx += s.length();
00399 } else {
00400 QStringList lst = QStringList::split( '\n', s, TRUE );
00401 QStringList::Iterator it = lst.begin();
00402
00403 int lastIndex = 0;
00404 KoTextFormat *lastFormat = 0;
00405 for ( ; it != lst.end(); ) {
00406 if ( it != lst.begin() ) {
00407 splitAndInsertEmptyParag( FALSE, TRUE );
00408
00409 #if 0 // no!
00410 string->prev()->format( -1, FALSE );
00411 #endif
00412 if ( lastFormat && formatting && string->prev() ) {
00413 lastFormat->addRef();
00414 string->prev()->string()->setFormat( string->prev()->length() - 1, lastFormat, TRUE );
00415 }
00416 }
00417 lastFormat = 0;
00418 QString s = *it;
00419 ++it;
00420 if ( !s.isEmpty() )
00421 string->insert( idx, s );
00422 else
00423 string->invalidate( 0 );
00424
00425 if ( formatting ) {
00426 int len = s.length();
00427 for ( int i = 0; i < len; ++i ) {
00428 if ( formatting->at( i + lastIndex ).format() ) {
00429 formatting->at( i + lastIndex ).format()->addRef();
00430 string->string()->setFormat( i + idx, formatting->at( i + lastIndex ).format(), TRUE );
00431 }
00432 }
00433 if ( it != lst.end() )
00434 lastFormat = formatting->at( len + lastIndex ).format();
00435 ++len;
00436 lastIndex += len;
00437 }
00438
00439 idx += s.length();
00440 }
00441 #if 0
00442 string->format( -1, FALSE );
00443 int dy = string->rect().y() + string->rect().height() - y;
00444 #endif
00445 KoTextParag *p = string;
00446 p->setParagId( p->prev()->paragId() + 1 );
00447 p = p->next();
00448 while ( p ) {
00449 p->setParagId( p->prev()->paragId() + 1 );
00450
00451 p->invalidate( 0 );
00452 p = p->next();
00453 }
00454 }
00455
00456 #if 0
00457 int h = string->rect().height();
00458 string->format( -1, TRUE );
00459 #endif
00460 fixCursorPosition();
00461 }
00462
00463 void KoTextCursor::gotoLeft()
00464 {
00465 if ( string->string()->isRightToLeft() )
00466 gotoNextLetter();
00467 else
00468 gotoPreviousLetter();
00469 }
00470
00471 void KoTextCursor::gotoPreviousLetter()
00472 {
00473 tmpIndex = -1;
00474
00475 if ( idx > 0 ) {
00476 idx = string->string()->previousCursorPosition( idx );
00477 } else if ( string->prev() ) {
00478 string = string->prev();
00479 while ( !string->isVisible() )
00480 string = string->prev();
00481 idx = string->length() - 1;
00482 }
00483 }
00484
00485 bool KoTextCursor::place( const QPoint &p, KoTextParag *s, bool link, int *customItemIndex )
00486 {
00487 if ( customItemIndex )
00488 *customItemIndex = -1;
00489 QPoint pos( p );
00490 QRect r;
00491 if ( pos.y() < s->rect().y() )
00492 pos.setY( s->rect().y() );
00493 while ( s ) {
00494 r = s->rect();
00495 r.setWidth( doc ? doc->width() : QWIDGETSIZE_MAX );
00496 if ( !s->next() || ( pos.y() >= r.y() && pos.y() < s->next()->rect().y() ) )
00497 break;
00498 s = s->next();
00499 }
00500
00501 if ( !s )
00502 return FALSE;
00503
00504 setParag( s, FALSE );
00505 int y = s->rect().y();
00506 int lines = s->lines();
00507 KoTextStringChar *chr = 0;
00508 int index = 0;
00509 int i = 0;
00510 int cy = 0;
00511
00512 for ( ; i < lines; ++i ) {
00513 chr = s->lineStartOfLine( i, &index );
00514 cy = s->lineY( i );
00515
00516 if ( !chr )
00517 return FALSE;
00518 if ( i < lines - 1 && pos.y() >= y + cy && pos.y() <= y + s->lineY( i+1 ) )
00519 break;
00520 }
00521 int nextLine;
00522 if ( i < lines - 1 )
00523 s->lineStartOfLine( i+1, &nextLine );
00524 else
00525 nextLine = s->length();
00526 i = index;
00527 int x = s->rect().x();
00528 if ( pos.x() < x )
00529 pos.setX( x + 1 );
00530 int cw;
00531 int curpos = s->length()-1;
00532 int dist = 10000000;
00533 while ( i < nextLine ) {
00534 chr = s->at(i);
00535 int cpos = x + chr->x;
00536 cw = chr->width;
00537 if ( chr->isCustom() ) {
00538 if ( pos.x() >= cpos && pos.x() <= cpos + cw &&
00539 pos.y() >= y + cy && pos.y() <= y + cy + chr->height() ) {
00540 if ( customItemIndex )
00541 *customItemIndex = i;
00542 }
00543 }
00544 if( chr->rightToLeft )
00545 cpos += cw;
00546 int d = cpos - pos.x();
00547 bool dm = d < 0 ? !chr->rightToLeft : chr->rightToLeft;
00548 if ( (QABS( d ) < dist || (dist == d && dm == TRUE )) && string->string()->validCursorPosition( i ) ) {
00549 dist = QABS( d );
00550 if ( !link || pos.x() >= x + chr->x ) {
00551 curpos = i;
00552 }
00553 }
00554 i++;
00555 }
00556 setIndex( curpos, FALSE );
00557
00558 return TRUE;
00559 }
00560
00561 void KoTextCursor::gotoRight()
00562 {
00563 if ( string->string()->isRightToLeft() )
00564 gotoPreviousLetter();
00565 else
00566 gotoNextLetter();
00567 }
00568
00569 void KoTextCursor::gotoNextLetter()
00570 {
00571 tmpIndex = -1;
00572
00573 int len = string->length() - 1;
00574 if ( idx < len ) {
00575 idx = string->string()->nextCursorPosition( idx );
00576 } else if ( string->next() ) {
00577 string = string->next();
00578 while ( !string->isVisible() )
00579 string = string->next();
00580 idx = 0;
00581 }
00582 }
00583
00584 void KoTextCursor::gotoUp()
00585 {
00586 int indexOfLineStart;
00587 int line;
00588 KoTextStringChar *c = string->lineStartOfChar( idx, &indexOfLineStart, &line );
00589 if ( !c )
00590 return;
00591
00592 tmpIndex = QMAX( tmpIndex, idx - indexOfLineStart );
00593 if ( indexOfLineStart == 0 ) {
00594 if ( !string->prev() ) {
00595 return;
00596 }
00597 string = string->prev();
00598 while ( !string->isVisible() )
00599 string = string->prev();
00600 int lastLine = string->lines() - 1;
00601 if ( !string->lineStartOfLine( lastLine, &indexOfLineStart ) )
00602 return;
00603 if ( indexOfLineStart + tmpIndex < string->length() )
00604 idx = indexOfLineStart + tmpIndex;
00605 else
00606 idx = string->length() - 1;
00607 } else {
00608 --line;
00609 int oldIndexOfLineStart = indexOfLineStart;
00610 if ( !string->lineStartOfLine( line, &indexOfLineStart ) )
00611 return;
00612 if ( indexOfLineStart + tmpIndex < oldIndexOfLineStart )
00613 idx = indexOfLineStart + tmpIndex;
00614 else
00615 idx = oldIndexOfLineStart - 1;
00616 }
00617 fixCursorPosition();
00618 }
00619
00620 void KoTextCursor::gotoDown()
00621 {
00622 int indexOfLineStart;
00623 int line;
00624 KoTextStringChar *c = string->lineStartOfChar( idx, &indexOfLineStart, &line );
00625 if ( !c )
00626 return;
00627
00628 tmpIndex = QMAX( tmpIndex, idx - indexOfLineStart );
00629 if ( line == string->lines() - 1 ) {
00630 if ( !string->next() ) {
00631 return;
00632 }
00633 string = string->next();
00634 while ( !string->isVisible() )
00635 string = string->next();
00636 if ( !string->lineStartOfLine( 0, &indexOfLineStart ) )
00637 return;
00638 int end;
00639 if ( string->lines() == 1 )
00640 end = string->length();
00641 else
00642 string->lineStartOfLine( 1, &end );
00643 if ( indexOfLineStart + tmpIndex < end )
00644 idx = indexOfLineStart + tmpIndex;
00645 else
00646 idx = end - 1;
00647 } else {
00648 ++line;
00649 int end;
00650 if ( line == string->lines() - 1 )
00651 end = string->length();
00652 else
00653 string->lineStartOfLine( line + 1, &end );
00654 if ( !string->lineStartOfLine( line, &indexOfLineStart ) )
00655 return;
00656 if ( indexOfLineStart + tmpIndex < end )
00657 idx = indexOfLineStart + tmpIndex;
00658 else
00659 idx = end - 1;
00660 }
00661 fixCursorPosition();
00662 }
00663
00664 void KoTextCursor::gotoLineEnd()
00665 {
00666 tmpIndex = -1;
00667 int indexOfLineStart;
00668 int line;
00669 KoTextStringChar *c = string->lineStartOfChar( idx, &indexOfLineStart, &line );
00670 if ( !c )
00671 return;
00672
00673 if ( line == string->lines() - 1 ) {
00674 idx = string->length() - 1;
00675 } else {
00676 c = string->lineStartOfLine( ++line, &indexOfLineStart );
00677 indexOfLineStart--;
00678 idx = indexOfLineStart;
00679 }
00680 }
00681
00682 void KoTextCursor::gotoLineStart()
00683 {
00684 tmpIndex = -1;
00685 int indexOfLineStart;
00686 int line;
00687 KoTextStringChar *c = string->lineStartOfChar( idx, &indexOfLineStart, &line );
00688 if ( !c )
00689 return;
00690
00691 idx = indexOfLineStart;
00692 }
00693
00694 void KoTextCursor::gotoHome()
00695 {
00696 tmpIndex = -1;
00697 if ( doc )
00698 string = doc->firstParag();
00699 idx = 0;
00700 }
00701
00702 void KoTextCursor::gotoEnd()
00703 {
00704
00705
00706
00707
00708
00709
00710
00711
00712 tmpIndex = -1;
00713 if ( doc )
00714 string = doc->lastParag();
00715 idx = string->length() - 1;
00716 }
00717
00718 void KoTextCursor::gotoPageUp( int visibleHeight )
00719 {
00720 tmpIndex = -1;
00721 KoTextParag *s = string;
00722 int h = visibleHeight;
00723 int y = s->rect().y();
00724 while ( s ) {
00725 if ( y - s->rect().y() >= h )
00726 break;
00727 s = s->prev();
00728 }
00729
00730 if ( !s && doc )
00731 s = doc->firstParag();
00732
00733 string = s;
00734 idx = 0;
00735 }
00736
00737 void KoTextCursor::gotoPageDown( int visibleHeight )
00738 {
00739 tmpIndex = -1;
00740 KoTextParag *s = string;
00741 int h = visibleHeight;
00742 int y = s->rect().y();
00743 while ( s ) {
00744 if ( s->rect().y() - y >= h )
00745 break;
00746 s = s->next();
00747 }
00748
00749 if ( !s && doc ) {
00750 s = doc->lastParag();
00751 string = s;
00752 idx = string->length() - 1;
00753 return;
00754 }
00755
00756 if ( !s->isValid() )
00757 return;
00758
00759 string = s;
00760 idx = 0;
00761 }
00762
00763 void KoTextCursor::gotoWordRight()
00764 {
00765 if ( string->string()->isRightToLeft() )
00766 gotoPreviousWord();
00767 else
00768 gotoNextWord();
00769 }
00770
00771 void KoTextCursor::gotoWordLeft()
00772 {
00773 if ( string->string()->isRightToLeft() )
00774 gotoNextWord();
00775 else
00776 gotoPreviousWord();
00777 }
00778
00779 void KoTextCursor::gotoPreviousWord()
00780 {
00781 gotoPreviousLetter();
00782 tmpIndex = -1;
00783 KoTextString *s = string->string();
00784 bool allowSame = FALSE;
00785 if ( idx == ( (int)s->length()-1 ) )
00786 return;
00787 for ( int i = idx; i >= 0; --i ) {
00788 if ( s->at( i ).c.isSpace() || s->at( i ).c == '\t' || s->at( i ).c == '.' ||
00789 s->at( i ).c == ',' || s->at( i ).c == ':' || s->at( i ).c == ';' ) {
00790 if ( !allowSame )
00791 continue;
00792 idx = i + 1;
00793 return;
00794 }
00795 if ( !allowSame && !( s->at( i ).c.isSpace() || s->at( i ).c == '\t' || s->at( i ).c == '.' ||
00796 s->at( i ).c == ',' || s->at( i ).c == ':' || s->at( i ).c == ';' ) )
00797 allowSame = TRUE;
00798 }
00799 idx = 0;
00800 }
00801
00802 void KoTextCursor::gotoNextWord()
00803 {
00804 tmpIndex = -1;
00805 KoTextString *s = string->string();
00806 bool allowSame = FALSE;
00807 for ( int i = idx; i < (int)s->length(); ++i ) {
00808 if ( ! ( s->at( i ).c.isSpace() || s->at( i ).c == '\t' || s->at( i ).c == '.' ||
00809 s->at( i ).c == ',' || s->at( i ).c == ':' || s->at( i ).c == ';' ) ) {
00810 if ( !allowSame )
00811 continue;
00812 idx = i;
00813 return;
00814 }
00815 if ( !allowSame && ( s->at( i ).c.isSpace() || s->at( i ).c == '\t' || s->at( i ).c == '.' ||
00816 s->at( i ).c == ',' || s->at( i ).c == ':' || s->at( i ).c == ';' ) )
00817 allowSame = TRUE;
00818 }
00819
00820 if ( idx < ((int)s->length()-1) ) {
00821 gotoLineEnd();
00822 } else if ( string->next() ) {
00823 string = string->next();
00824 while ( !string->isVisible() )
00825 string = string->next();
00826 idx = 0;
00827 } else {
00828 gotoLineEnd();
00829 }
00830 }
00831
00832 bool KoTextCursor::atParagStart() const
00833 {
00834 return idx == 0;
00835 }
00836
00837 bool KoTextCursor::atParagEnd() const
00838 {
00839 return idx == string->length() - 1;
00840 }
00841
00842 void KoTextCursor::splitAndInsertEmptyParag( bool ind, bool updateIds )
00843 {
00844 if ( !doc )
00845 return;
00846 tmpIndex = -1;
00847 KoTextFormat *f = 0;
00848 if ( doc->useFormatCollection() ) {
00849 f = string->at( idx )->format();
00850 if ( idx == string->length() - 1 && idx > 0 )
00851 f = string->at( idx - 1 )->format();
00852 if ( f->isMisspelled() ) {
00853 KoTextFormat fNoMisspelled( *f );
00854 fNoMisspelled.setMisspelled( false );
00855 f = doc->formatCollection()->format( &fNoMisspelled );
00856 }
00857 }
00858
00859 if ( atParagEnd() ) {
00860 KoTextParag *n = string->next();
00861 KoTextParag *s = doc->createParag( doc, string, n, updateIds );
00862 if ( f )
00863 s->setFormat( 0, 1, f, TRUE );
00864 s->copyParagData( string );
00865 #if 0
00866 if ( ind ) {
00867 int oi, ni;
00868 s->indent( &oi, &ni );
00869 string = s;
00870 idx = ni;
00871 } else
00872 #endif
00873 {
00874 string = s;
00875 idx = 0;
00876 }
00877 } else if ( atParagStart() ) {
00878 KoTextParag *p = string->prev();
00879 KoTextParag *s = doc->createParag( doc, p, string, updateIds );
00880 if ( f )
00881 s->setFormat( 0, 1, f, TRUE );
00882 s->copyParagData( string );
00883 if ( ind ) {
00884
00885 s->format();
00886
00887 string->format();
00888 }
00889 } else {
00890 QString str = string->string()->toString().mid( idx, 0xFFFFFF );
00891 KoTextParag *n = string->next();
00892 KoTextParag *s = doc->createParag( doc, string, n, updateIds );
00893 s->copyParagData( string );
00894 s->remove( 0, 1 );
00895 s->append( str, TRUE );
00896 for ( uint i = 0; i < str.length(); ++i ) {
00897 KoTextStringChar* tsc = string->at( idx + i );
00898 s->setFormat( i, 1, tsc->format(), TRUE );
00899 if ( tsc->isCustom() ) {
00900 KoTextCustomItem * item = tsc->customItem();
00901 s->at( i )->setCustomItem( item );
00902 tsc->loseCustomItem();
00903 #if 0
00904 s->addCustomItem();
00905 string->removeCustomItem();
00906 #endif
00907 doc->unregisterCustomItem( item, string );
00908 doc->registerCustomItem( item, s );
00909 }
00910 }
00911 string->truncate( idx );
00912 #if 0
00913 if ( ind ) {
00914 int oi, ni;
00915 s->indent( &oi, &ni );
00916 string = s;
00917 idx = ni;
00918 } else
00919 #endif
00920 {
00921 string = s;
00922 idx = 0;
00923 }
00924 }
00925 }
00926
00927 bool KoTextCursor::removePreviousChar()
00928 {
00929 tmpIndex = -1;
00930 if ( !atParagStart() ) {
00931 string->remove( idx-1, 1 );
00932 idx--;
00933
00934 fixCursorPosition();
00935 string->format( -1, TRUE );
00936
00937
00938 return FALSE;
00939 } else if ( string->prev() ) {
00940 string = string->prev();
00941 string->join( string->next() );
00942 string->invalidateCounters();
00943 return TRUE;
00944 }
00945 return FALSE;
00946 }
00947
00948 bool KoTextCursor::remove()
00949 {
00950 tmpIndex = -1;
00951 if ( !atParagEnd() ) {
00952 int next = string->string()->nextCursorPosition( idx );
00953 string->remove( idx, next-idx );
00954 string->format( -1, TRUE );
00955
00956
00957 return FALSE;
00958 } else if ( string->next() ) {
00959 if ( string->length() == 1 ) {
00960 string->next()->setPrev( string->prev() );
00961 if ( string->prev() )
00962 string->prev()->setNext( string->next() );
00963 KoTextParag *p = string->next();
00964 delete string;
00965 string = p;
00966 string->invalidate( 0 );
00968 string->invalidateCounters();
00970 KoTextParag *s = string;
00971 while ( s ) {
00972 s->id = s->p ? s->p->id + 1 : 0;
00973
00974
00975 s->changed = TRUE;
00976 s = s->n;
00977 }
00978 string->format();
00979 } else {
00980 string->join( string->next() );
00981 }
00982 return TRUE;
00983 }
00984 return FALSE;
00985 }
00986
00987 void KoTextCursor::killLine()
00988 {
00989 if ( atParagEnd() )
00990 return;
00991 string->remove( idx, string->length() - idx - 1 );
00992 string->format( -1, TRUE );
00993
00994
00995 }
00996
00997 #if 0
00998 void KoTextCursor::indent()
00999 {
01000 int oi = 0, ni = 0;
01001 string->indent( &oi, &ni );
01002 if ( oi == ni )
01003 return;
01004
01005 if ( idx >= oi )
01006 idx += ni - oi;
01007 else
01008 idx = ni;
01009 }
01010 #endif
01011
01012 void KoTextCursor::setDocument( KoTextDocument *d )
01013 {
01014 doc = d;
01015 string = d->firstParag();
01016 idx = 0;
01017 tmpIndex = -1;
01018 }
01019
01020
01021 int KoTextCursor::x() const
01022 {
01023 KoTextStringChar *c = string->at( idx );
01024 int curx = c->x;
01025 if ( c->rightToLeft )
01026 curx += c->width;
01027 return curx;
01028 }
01029
01030 int KoTextCursor::y() const
01031 {
01032 int dummy, line;
01033 string->lineStartOfChar( idx, &dummy, &line );
01034 return string->lineY( line );
01035 }
01036
01037
01038 void KoTextCursor::fixCursorPosition()
01039 {
01040
01041 if ( string->string()->validCursorPosition( idx ) )
01042 return;
01043
01044 int lineIdx;
01045 KoTextStringChar *start = string->lineStartOfChar( idx, &lineIdx, 0 );
01046 int x = string->string()->at( idx ).x;
01047 int diff = QABS(start->x - x);
01048 int best = lineIdx;
01049
01050 KoTextStringChar *c = start;
01051 ++c;
01052
01053 KoTextStringChar *end = &string->string()->at( string->length()-1 );
01054 while ( c <= end && !c->lineStart ) {
01055 int xp = c->x;
01056 if ( c->rightToLeft )
01057 xp += c->pixelwidth;
01058 int ndiff = QABS(xp - x);
01059 if ( ndiff < diff && string->string()->validCursorPosition(lineIdx + (c-start)) ) {
01060 diff = ndiff;
01061 best = lineIdx + (c-start);
01062 }
01063 ++c;
01064 }
01065 idx = best;
01066 }
01067
01068
01069
01070 KoTextString::KoTextString()
01071 {
01072 bidiDirty = TRUE;
01073 bNeedsSpellCheck = true;
01074 bidi = FALSE;
01075 rightToLeft = FALSE;
01076 dir = QChar::DirON;
01077 }
01078
01079 KoTextString::KoTextString( const KoTextString &s )
01080 {
01081 bidiDirty = s.bidiDirty;
01082 bNeedsSpellCheck = s.bNeedsSpellCheck;
01083 bidi = s.bidi;
01084 rightToLeft = s.rightToLeft;
01085 dir = s.dir;
01086 data = s.data;
01087 data.detach();
01088 for ( int i = 0; i < (int)data.size(); ++i ) {
01089 KoTextFormat *f = data[i].format();
01090 if ( f )
01091 f->addRef();
01092 }
01093 }
01094
01095 void KoTextString::insert( int index, const QString &s, KoTextFormat *f )
01096 {
01097 int os = data.size();
01098 data.resize( data.size() + s.length() );
01099 if ( index < os ) {
01100 memmove( data.data() + index + s.length(), data.data() + index,
01101 sizeof( KoTextStringChar ) * ( os - index ) );
01102 }
01103 for ( int i = 0; i < (int)s.length(); ++i ) {
01104 KoTextStringChar &ch = data[ (int)index + i ];
01105 ch.x = 0;
01106 ch.pixelxadj = 0;
01107 ch.pixelwidth = 0;
01108 ch.width = 0;
01109 ch.lineStart = 0;
01110 ch.d.format = 0;
01111 ch.type = KoTextStringChar::Regular;
01112 ch.rightToLeft = 0;
01113 ch.startOfRun = 0;
01114 ch.c = s[ i ];
01115 #ifdef DEBUG_COLLECTION
01116 kdDebug(32500) << "KoTextString::insert setting format " << f << " to character " << (int)index+i << endl;
01117 #endif
01118 ch.setFormat( f );
01119 }
01120 bidiDirty = TRUE;
01121 bNeedsSpellCheck = true;
01122 }
01123
01124 KoTextString::~KoTextString()
01125 {
01126 clear();
01127 }
01128
01129 void KoTextString::insert( int index, KoTextStringChar *c )
01130 {
01131 int os = data.size();
01132 data.resize( data.size() + 1 );
01133 if ( index < os ) {
01134 memmove( data.data() + index + 1, data.data() + index,
01135 sizeof( KoTextStringChar ) * ( os - index ) );
01136 }
01137 KoTextStringChar &ch = data[ (int)index ];
01138 ch.c = c->c;
01139 ch.x = 0;
01140 ch.pixelxadj = 0;
01141 ch.pixelwidth = 0;
01142 ch.width = 0;
01143 ch.lineStart = 0;
01144 ch.rightToLeft = 0;
01145 ch.d.format = 0;
01146 ch.type = KoTextStringChar::Regular;
01147 ch.setFormat( c->format() );
01148 bidiDirty = TRUE;
01149 bNeedsSpellCheck = true;
01150 }
01151
01152 void KoTextString::truncate( int index )
01153 {
01154 index = QMAX( index, 0 );
01155 index = QMIN( index, (int)data.size() - 1 );
01156 if ( index < (int)data.size() ) {
01157 for ( int i = index + 1; i < (int)data.size(); ++i ) {
01158 KoTextStringChar &ch = data[ i ];
01159 if ( ch.isCustom() ) {
01160 delete ch.customItem();
01161 if ( ch.d.custom->format )
01162 ch.d.custom->format->removeRef();
01163 delete ch.d.custom;
01164 ch.d.custom = 0;
01165 } else if ( ch.format() ) {
01166 ch.format()->removeRef();
01167 }
01168 }
01169 }
01170 data.truncate( index );
01171 bidiDirty = TRUE;
01172 bNeedsSpellCheck = true;
01173 }
01174
01175 void KoTextString::remove( int index, int len )
01176 {
01177 for ( int i = index; i < (int)data.size() && i - index < len; ++i ) {
01178 KoTextStringChar &ch = data[ i ];
01179 if ( ch.isCustom() ) {
01180 delete ch.customItem();
01181 if ( ch.d.custom->format )
01182 ch.d.custom->format->removeRef();
01183 delete ch.d.custom;
01184 ch.d.custom = 0;
01185 } else if ( ch.format() ) {
01186 ch.format()->removeRef();
01187 }
01188 }
01189 memmove( data.data() + index, data.data() + index + len,
01190 sizeof( KoTextStringChar ) * ( data.size() - index - len ) );
01191 data.resize( data.size() - len, QGArray::SpeedOptim );
01192 bidiDirty = TRUE;
01193 bNeedsSpellCheck = true;
01194 }
01195
01196 void KoTextString::clear()
01197 {
01198 for ( int i = 0; i < (int)data.count(); ++i ) {
01199 KoTextStringChar &ch = data[ i ];
01200 if ( ch.isCustom() ) {
01201
01202
01203
01204
01205
01206
01207 delete ch.customItem();
01208 if ( ch.d.custom->format )
01209 ch.d.custom->format->removeRef();
01210 delete ch.d.custom;
01211 ch.d.custom = 0;
01212
01213 } else if ( ch.format() ) {
01214 ch.format()->removeRef();
01215 }
01216 }
01217 data.resize( 0 );
01218 }
01219
01220 void KoTextString::setFormat( int index, KoTextFormat *f, bool useCollection, bool setFormatAgain )
01221 {
01222 KoTextStringChar &ch = data[ index ];
01223
01224 if ( useCollection && ch.format() )
01225 {
01226
01227 ch.format()->removeRef();
01228 }
01229 ch.setFormat( f, setFormatAgain );
01230 }
01231
01232 void KoTextString::checkBidi() const
01233 {
01234 KoTextString *that = (KoTextString *)this;
01235 that->bidiDirty = FALSE;
01236 int length = data.size();
01237 if ( !length ) {
01238 that->bidi = FALSE;
01239 that->rightToLeft = dir == QChar::DirR;
01240 return;
01241 }
01242 const KoTextStringChar *start = data.data();
01243 const KoTextStringChar *end = start + length;
01244
01245
01246 QTextEngine textEngine( toString(), 0 );
01247 textEngine.direction = (QChar::Direction) dir;
01248 textEngine.itemize(QTextEngine::SingleLine);
01249 const QCharAttributes *ca = textEngine.attributes() + length-1;
01250 KoTextStringChar *ch = (KoTextStringChar *)end - 1;
01251 QScriptItem *item = &textEngine.items[textEngine.items.size()-1];
01252 unsigned char bidiLevel = item->analysis.bidiLevel;
01253 if ( bidiLevel )
01254 that->bidi = TRUE;
01255 int pos = length-1;
01256 while ( ch >= start ) {
01257 if ( item->position > pos ) {
01258 --item;
01259 Q_ASSERT( item >= &textEngine.items[0] );
01260 Q_ASSERT( item < &textEngine.items[textEngine.items.size()] );
01261 bidiLevel = item->analysis.bidiLevel;
01262 if ( bidiLevel )
01263 that->bidi = TRUE;
01264 }
01265 ch->softBreak = ca->softBreak;
01266 ch->whiteSpace = ca->whiteSpace;
01267 ch->charStop = ca->charStop;
01268 ch->wordStop = ca->wordStop;
01269
01270 ch->rightToLeft = (bidiLevel%2);
01271 --ch;
01272 --ca;
01273 --pos;
01274 }
01275
01276 if ( dir == QChar::DirR ) {
01277 that->bidi = TRUE;
01278 that->rightToLeft = TRUE;
01279 } else if ( dir == QChar::DirL ) {
01280 that->rightToLeft = FALSE;
01281 } else {
01282 that->rightToLeft = (textEngine.direction == QChar::DirR);
01283 }
01284 }
01285
01286 QMemArray<KoTextStringChar> KoTextString::subString( int start, int len ) const
01287 {
01288 if ( len == 0xFFFFFF )
01289 len = data.size();
01290 QMemArray<KoTextStringChar> a;
01291 a.resize( len );
01292 for ( int i = 0; i < len; ++i ) {
01293 KoTextStringChar *c = &data[ i + start ];
01294 a[ i ].c = c->c;
01295 a[ i ].x = 0;
01296 a[ i ].pixelxadj = 0;
01297 a[ i ].pixelwidth = 0;
01298 a[ i ].width = 0;
01299 a[ i ].lineStart = 0;
01300 a[ i ].rightToLeft = 0;
01301 a[ i ].d.format = 0;
01302 a[ i ].type = KoTextStringChar::Regular;
01303 a[ i ].setFormat( c->format() );
01304 if ( c->format() )
01305 c->format()->addRef();
01306 }
01307 return a;
01308 }
01309
01310 QString KoTextString::mid( int start, int len ) const
01311 {
01312 if ( len == 0xFFFFFF )
01313 len = data.size();
01314 QString res;
01315 res.setLength( len );
01316 for ( int i = 0; i < len; ++i ) {
01317 KoTextStringChar *c = &data[ i + start ];
01318 res[ i ] = c->c;
01319 }
01320 return res;
01321 }
01322
01323 QString KoTextString::toString( const QMemArray<KoTextStringChar> &data )
01324 {
01325 QString s;
01326 int l = data.size();
01327 s.setUnicode( 0, l );
01328 KoTextStringChar *c = data.data();
01329 QChar *uc = (QChar *)s.unicode();
01330 while ( l-- ) {
01331 *uc = c->c;
01332 uc++;
01333 c++;
01334 }
01335
01336 return s;
01337 }
01338
01339 QString KoTextString::toReverseString() const
01340 {
01341 QString s;
01342 int l = length();
01343 s.setUnicode(0, l);
01344 KoTextStringChar *c = data.data() + (l-1);
01345 QChar *uc = (QChar *)s.unicode();
01346 while ( l-- ) {
01347 *uc = c->c;
01348 uc++;
01349 c--;
01350 }
01351
01352 return s;
01353 }
01354
01355 QString KoTextString::stringToSpellCheck()
01356 {
01357 if ( !bNeedsSpellCheck )
01358 return QString::null;
01359
01360 bNeedsSpellCheck = false;
01361 if ( length() <= 1 )
01362 return QString::null;
01363
01364 QString str = toString();
01365 str.truncate( str.length() - 1 );
01366 return str;
01367 }
01368
01369 int KoTextString::nextCursorPosition( int next )
01370 {
01371 if ( bidiDirty )
01372 checkBidi();
01373
01374 const KoTextStringChar *c = data.data();
01375 int len = length();
01376
01377 if ( next < len - 1 ) {
01378 next++;
01379 while ( next < len - 1 && !c[next].charStop )
01380 next++;
01381 }
01382 return next;
01383 }
01384
01385 int KoTextString::previousCursorPosition( int prev )
01386 {
01387 if ( bidiDirty )
01388 checkBidi();
01389
01390 const KoTextStringChar *c = data.data();
01391
01392 if ( prev ) {
01393 prev--;
01394 while ( prev && !c[prev].charStop )
01395 prev--;
01396 }
01397 return prev;
01398 }
01399
01400 bool KoTextString::validCursorPosition( int idx )
01401 {
01402 if ( bidiDirty )
01403 checkBidi();
01404
01405 return (at( idx ).charStop);
01406 }
01407
01409
01410 void KoTextStringChar::setFormat( KoTextFormat *f, bool setFormatAgain )
01411 {
01412 if ( type == Regular ) {
01413 d.format = f;
01414 } else {
01415 if ( !d.custom ) {
01416 d.custom = new CustomData;
01417 d.custom->custom = 0;
01418 }
01419 d.custom->format = f;
01420 if ( d.custom->custom && setFormatAgain )
01421 d.custom->custom->setFormat( f );
01422 }
01423 }
01424
01425 void KoTextStringChar::setCustomItem( KoTextCustomItem *i )
01426 {
01427 if ( type == Regular ) {
01428 KoTextFormat *f = format();
01429 d.custom = new CustomData;
01430 d.custom->format = f;
01431 type = Custom;
01432 } else {
01433 delete d.custom->custom;
01434 }
01435 d.custom->custom = i;
01436 }
01437
01438 void KoTextStringChar::loseCustomItem()
01439 {
01440 if ( isCustom() ) {
01441 KoTextFormat *f = d.custom->format;
01442 d.custom->custom = 0;
01443 delete d.custom;
01444 type = Regular;
01445 d.format = f;
01446 }
01447 }
01448
01449 KoTextStringChar::~KoTextStringChar()
01450 {
01451 if ( format() )
01452 format()->removeRef();
01453 switch ( type ) {
01454 case Custom:
01455 delete d.custom; break;
01456 default:
01457 break;
01458 }
01459 }
01460
01461 int KoTextStringChar::height() const
01462 {
01463 return !isCustom() ? format()->height() : ( customItem()->placement() == KoTextCustomItem::PlaceInline ? customItem()->height : 0 );
01464 }
01465
01466 int KoTextStringChar::ascent() const
01467 {
01468 return !isCustom() ? format()->ascent() : ( customItem()->placement() == KoTextCustomItem::PlaceInline ? customItem()->ascent() : 0 );
01469 }
01470
01471 int KoTextStringChar::descent() const
01472 {
01473 return !isCustom() ? format()->descent() : 0;
01474 }
01475
01476
01477
01478 KoTextFormatterBase::KoTextFormatterBase()
01479 : wrapColumn( -1 ),
01480 m_bViewFormattingChars( false ),
01481 biw( true )
01482 {
01483 }
01484
01485 #ifdef BIDI_DEBUG
01486 #include <iostream>
01487 #endif
01488
01489
01490 KoTextParagLineStart *KoTextFormatterBase::bidiReorderLine( KoTextParag * , KoTextString *text, KoTextParagLineStart *line,
01491 KoTextStringChar *startChar, KoTextStringChar *lastChar, int align, int space )
01492 {
01493 int start = (startChar - &text->at(0));
01494 int last = (lastChar - &text->at(0) );
01495
01496
01497 KoBidiControl *control = new KoBidiControl( line->context(), line->status );
01498 QString str;
01499 str.setUnicode( 0, last - start + 1 );
01500
01501 KoTextStringChar *ch = startChar;
01502 QChar *qch = (QChar *)str.unicode();
01503 while ( ch <= lastChar ) {
01504 *qch = ch->c;
01505 qch++;
01506 ch++;
01507 }
01508 int x = startChar->x;
01509
01510 QPtrList<KoTextRun> *runs;
01511 runs = KoComplexText::bidiReorderLine(control, str, 0, last - start + 1,
01512 (text->isRightToLeft() ? QChar::DirR : QChar::DirL) );
01513
01514
01515
01516 int numSpaces = 0;
01517
01518 if( align == Qt::AlignAuto ) {
01519
01520 if ( text->isRightToLeft() )
01521 align = Qt::AlignRight;
01522 }
01523
01524 if ( align & Qt::AlignHCenter )
01525 x += space/2;
01526 else if ( align & Qt::AlignRight )
01527 x += space;
01528 else if ( align & Qt::AlignJustify ) {
01529 for ( int j = start; j < last; ++j ) {
01530 if( isBreakable( text, j ) ) {
01531 numSpaces++;
01532 }
01533 }
01534 }
01535 int toAdd = 0;
01536 bool first = TRUE;
01537 KoTextRun *r = runs->first();
01538 int xmax = -0xffffff;
01539 while ( r ) {
01540 if(r->level %2) {
01541
01542 int pos = r->stop + start;
01543 while(pos >= r->start + start) {
01544 KoTextStringChar *c = &text->at(pos);
01545 if( numSpaces && !first && isBreakable( text, pos ) ) {
01546 int s = space / numSpaces;
01547 toAdd += s;
01548 space -= s;
01549 numSpaces--;
01550 } else if ( first ) {
01551 first = FALSE;
01552 if ( c->c == ' ' )
01553 x -= c->format()->width( ' ' );
01554 }
01555 c->x = x + toAdd;
01556 c->rightToLeft = TRUE;
01557 c->startOfRun = FALSE;
01558 int ww = 0;
01559 if ( c->c.unicode() >= 32 || c->c == '\t' || c->c == '\n' || c->isCustom() ) {
01560 ww = c->width;
01561 } else {
01562 ww = c->format()->width( ' ' );
01563 }
01564 if ( xmax < x + toAdd + ww ) xmax = x + toAdd + ww;
01565 x += ww;
01566 pos--;
01567 }
01568 } else {
01569 int pos = r->start + start;
01570 while(pos <= r->stop + start) {
01571 KoTextStringChar* c = &text->at(pos);
01572 if( numSpaces && !first && isBreakable( text, pos ) ) {
01573 int s = space / numSpaces;
01574 toAdd += s;
01575 space -= s;
01576 numSpaces--;
01577 } else if ( first ) {
01578 first = FALSE;
01579 if ( c->c == ' ' )
01580 x -= c->format()->width( ' ' );
01581 }
01582 c->x = x + toAdd;
01583 c->rightToLeft = FALSE;
01584 c->startOfRun = FALSE;
01585 int ww = 0;
01586 if ( c->c.unicode() >= 32 || c->c == '\t' || c->isCustom() ) {
01587 ww = c->width;
01588 } else {
01589 ww = c->format()->width( ' ' );
01590 }
01591
01592 if ( xmax < x + toAdd + ww ) xmax = x + toAdd + ww;
01593 x += ww;
01594 pos++;
01595 }
01596 }
01597 text->at( r->start + start ).startOfRun = TRUE;
01598 r = runs->next();
01599 }
01600
01601 line->w = xmax + 10;
01602 KoTextParagLineStart *ls = new KoTextParagLineStart( control->context, control->status );
01603 delete control;
01604 delete runs;
01605 return ls;
01606 }
01607
01608 bool KoTextFormatterBase::isStretchable( KoTextString *string, int pos ) const
01609 {
01610 if ( string->at( pos ).c == QChar(160) )
01611 return true;
01612 KoTextStringChar& chr = string->at( pos );
01613 return chr.whiteSpace;
01614
01615 }
01616
01617 bool KoTextFormatterBase::isBreakable( KoTextString *string, int pos ) const
01618 {
01619
01620
01621 return (pos < string->length()-1 && string->at(pos+1).softBreak);
01622 }
01623
01624 void KoTextParag::insertLineStart( int index, KoTextParagLineStart *ls )
01625 {
01626
01627
01628
01629
01630 #ifndef NDEBUG
01631 QMap<int, KoTextParagLineStart*>::Iterator it;
01632 if ( ( it = lineStarts.find( index ) ) == lineStarts.end() ) {
01633 lineStarts.insert( index, ls );
01634 } else {
01635 kdWarning(32500) << "insertLineStart: there's already a line for char index=" << index << endl;
01636 delete *it;
01637 lineStarts.remove( it );
01638 lineStarts.insert( index, ls );
01639 }
01640 #else // non-debug code, take the fast route
01641 lineStarts.insert( index, ls );
01642 #endif
01643 }
01644
01645
01646
01647
01648
01649 int KoTextFormatterBase::formatVertically( KoTextDocument* doc, KoTextParag* parag )
01650 {
01651 int oldHeight = parag->rect().height();
01652 QMap<int, KoTextParagLineStart*>& lineStarts = parag->lineStartList();
01653 QMap<int, KoTextParagLineStart*>::Iterator it = lineStarts.begin();
01654 int h = doc->addMargins() ? parag->topMargin() : 0;
01655 for ( ; it != lineStarts.end() ; ++it ) {
01656 KoTextParagLineStart * ls = it.data();
01657 ls->y = h;
01658 KoTextStringChar *c = ¶g->string()->at(it.key());
01659 if ( c && c->customItem() && c->customItem()->ownLine() ) {
01660 int h = c->customItem()->height;
01661 c->customItem()->pageBreak( parag->rect().y() + ls->y + ls->baseLine - h, doc->flow() );
01662 int delta = c->customItem()->height - h;
01663 ls->h += delta;
01664 if ( delta )
01665 parag->setMovedDown( TRUE );
01666 } else {
01667 int shift = doc->flow()->adjustFlow( parag->rect().y() + ls->y, ls->w, ls->h );
01668 ls->y += shift;
01669 if ( shift )
01670 parag->setMovedDown( TRUE );
01671 }
01672 h = ls->y + ls->h;
01673 }
01674 int m = parag->bottomMargin();
01675 if ( parag->next() && doc && !doc->addMargins() )
01676 m = QMAX( m, parag->next()->topMargin() );
01677
01678
01679 h += m;
01680 parag->setHeight( h );
01681 return h - oldHeight;
01682 }
01683
01684
01685
01686 KoTextCustomItem::KoTextCustomItem( KoTextDocument *p )
01687 : width(-1), height(0), parent(p), xpos(0), ypos(-1), parag(0)
01688 {
01689 m_deleted = false;
01690 }
01691
01692 KoTextCustomItem::~KoTextCustomItem()
01693 {
01694 }
01695
01696 KoTextFlow::KoTextFlow()
01697 {
01698 w = 0;
01699 leftItems.setAutoDelete( FALSE );
01700 rightItems.setAutoDelete( FALSE );
01701 }
01702
01703 KoTextFlow::~KoTextFlow()
01704 {
01705 }
01706
01707 void KoTextFlow::clear()
01708 {
01709 leftItems.clear();
01710 rightItems.clear();
01711 }
01712
01713
01714 void KoTextFlow::setWidth( int width )
01715 {
01716 w = width;
01717 }
01718
01719 void KoTextFlow::adjustMargins( int, int, int, int&, int&, int& pageWidth, KoTextParag* )
01720 {
01721 pageWidth = w;
01722 }
01723
01724
01725 int KoTextFlow::adjustFlow( int , int, int )
01726 {
01727 return 0;
01728 }
01729
01730 void KoTextFlow::unregisterFloatingItem( KoTextCustomItem* item )
01731 {
01732 leftItems.removeRef( item );
01733 rightItems.removeRef( item );
01734 }
01735
01736 void KoTextFlow::registerFloatingItem( KoTextCustomItem* item )
01737 {
01738 if ( item->placement() == KoTextCustomItem::PlaceRight ) {
01739 if ( !rightItems.contains( item ) )
01740 rightItems.append( item );
01741 } else if ( item->placement() == KoTextCustomItem::PlaceLeft &&
01742 !leftItems.contains( item ) ) {
01743 leftItems.append( item );
01744 }
01745 }
01746
01747 int KoTextFlow::availableHeight() const
01748 {
01749 return -1;
01750 }
01751
01752 void KoTextFlow::drawFloatingItems( QPainter* p, int cx, int cy, int cw, int ch, const QColorGroup& cg, bool selected )
01753 {
01754 KoTextCustomItem *item;
01755 for ( item = leftItems.first(); item; item = leftItems.next() ) {
01756 if ( item->x() == -1 || item->y() == -1 )
01757 continue;
01758 item->draw( p, item->x(), item->y(), cx, cy, cw, ch, cg, selected );
01759 }
01760
01761 for ( item = rightItems.first(); item; item = rightItems.next() ) {
01762 if ( item->x() == -1 || item->y() == -1 )
01763 continue;
01764 item->draw( p, item->x(), item->y(), cx, cy, cw, ch, cg, selected );
01765 }
01766 }
01767
01768
01769 bool KoTextFlow::isEmpty() { return leftItems.isEmpty() && rightItems.isEmpty(); }