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