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 <q3paintdevicemetrics.h>
00041
00042
00043 #include <Q3MemArray>
00044 #include <Q3PtrList>
00045 #include <QWidget>
00046
00047 #include <stdlib.h>
00048 #include "KoParagCounter.h"
00049 #include "KoTextDocument.h"
00050 #include <kdebug.h>
00051 #include <kdeversion.h>
00052 #include <kglobal.h>
00053 #include <klocale.h>
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063 void KoTextDocCommandHistory::addCommand( KoTextDocCommand *cmd )
00064 {
00065 if ( current < (int)history.count() - 1 ) {
00066 Q3PtrList<KoTextDocCommand> commands;
00067 commands.setAutoDelete( false );
00068
00069 for( int i = 0; i <= current; ++i ) {
00070 commands.insert( i, history.at( 0 ) );
00071 history.take( 0 );
00072 }
00073
00074 commands.append( cmd );
00075 history.clear();
00076 history = commands;
00077 history.setAutoDelete( true );
00078 } else {
00079 history.append( cmd );
00080 }
00081
00082 if ( (int)history.count() > steps )
00083 history.removeFirst();
00084 else
00085 ++current;
00086 }
00087
00088 KoTextCursor *KoTextDocCommandHistory::undo( KoTextCursor *c )
00089 {
00090 if ( current > -1 ) {
00091 KoTextCursor *c2 = history.at( current )->unexecute( c );
00092 --current;
00093 return c2;
00094 }
00095 return 0;
00096 }
00097
00098 KoTextCursor *KoTextDocCommandHistory::redo( KoTextCursor *c )
00099 {
00100 if ( current > -1 ) {
00101 if ( current < (int)history.count() - 1 ) {
00102 ++current;
00103 return history.at( current )->execute( c );
00104 }
00105 } else {
00106 if ( history.count() > 0 ) {
00107 ++current;
00108 return history.at( current )->execute( c );
00109 }
00110 }
00111 return 0;
00112 }
00113
00114 bool KoTextDocCommandHistory::isUndoAvailable()
00115 {
00116 return current > -1;
00117 }
00118
00119 bool KoTextDocCommandHistory::isRedoAvailable()
00120 {
00121 return current > -1 && current < (int)history.count() - 1 || current == -1 && history.count() > 0;
00122 }
00123
00124
00125
00126 KoTextDocDeleteCommand::KoTextDocDeleteCommand( KoTextDocument *d, int i, int idx, const Q3MemArray<KoTextStringChar> &str )
00127 : KoTextDocCommand( d ), id( i ), index( idx ), parag( 0 ), text( str )
00128 {
00129 for ( int j = 0; j < (int)text.size(); ++j ) {
00130 if ( text[ j ].format() )
00131 text[ j ].format()->addRef();
00132 }
00133 }
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144 KoTextDocDeleteCommand::~KoTextDocDeleteCommand()
00145 {
00146 for ( int i = 0; i < (int)text.size(); ++i ) {
00147 if ( text[ i ].format() )
00148 text[ i ].format()->removeRef();
00149 }
00150 text.resize( 0 );
00151 }
00152
00153 KoTextCursor *KoTextDocDeleteCommand::execute( KoTextCursor *c )
00154 {
00155 KoTextParag *s = doc->paragAt( id );
00156 if ( !s ) {
00157 kWarning(32500) << "can't locate parag at " << id << ", last parag: " << doc->lastParag()->paragId() << endl;
00158 return 0;
00159 }
00160
00161 cursor.setParag( s );
00162 cursor.setIndex( index );
00163 int len = text.size();
00164 if ( c )
00165 *c = cursor;
00166 doc->setSelectionStart( KoTextDocument::Temp, &cursor );
00167 for ( int i = 0; i < len; ++i )
00168 cursor.gotoNextLetter();
00169 doc->setSelectionEnd( KoTextDocument::Temp, &cursor );
00170 doc->removeSelectedText( KoTextDocument::Temp, &cursor );
00171 if ( c )
00172 *c = cursor;
00173
00174 return c;
00175 }
00176
00177 KoTextCursor *KoTextDocDeleteCommand::unexecute( KoTextCursor *c )
00178 {
00179 KoTextParag *s = doc->paragAt( id );
00180 if ( !s ) {
00181 kWarning(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 Q3MemArray<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 Q3MemArray<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, Q3MemArray<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 || !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 kDebug(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, Q3GArray::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 #if 0 //KoText porting
01244
01245 QTextLayout textLayout( toString() );
01246
01247
01248 QTextLine line = textLayout.createLine();
01249 Q_ASSERT(line.isValid());
01250 KoTextStringChar *ch = (KoTextStringChar *)end - 1;
01251 int pos = length-1;
01252 while ( ch >= start ) {
01253 #if 0 // TODO RTL
01254 if ( item->position > pos ) {
01255 --item;
01256 Q_ASSERT( item >= &textEngine.items[0] );
01257 Q_ASSERT( item < &textEngine.items[textEngine.items.size()] );
01258 bidiLevel = item->analysis.bidiLevel;
01259 if ( bidiLevel )
01260 that->bidi = true;
01261 }
01262 #endif
01263
01264 ch->whiteSpace = QChar( data[pos].c ).isSpace();
01265 ch->charStop = textLayout.isValidCursorPosition( pos );
01266
01267
01268 --ch;
01269
01270 --pos;
01271 }
01272
01273 if ( dir == QChar::DirR ) {
01274 that->bidi = true;
01275 that->rightToLeft = true;
01276 } else if ( dir == QChar::DirL ) {
01277 that->rightToLeft = false;
01278 } else {
01279 that->rightToLeft = false;
01280 }
01281 #endif
01282 }
01283
01284 Q3MemArray<KoTextStringChar> KoTextString::subString( int start, int len ) const
01285 {
01286 if ( len == 0xFFFFFF )
01287 len = data.size();
01288 Q3MemArray<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 Q3MemArray<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 #if 0
01488
01489 KoTextParagLineStart *KoTextFormatterBase::bidiReorderLine( KoTextParag * , KoTextString *text, KoTextParagLineStart *line,
01490 KoTextStringChar *startChar, KoTextStringChar *lastChar, int align, int space )
01491 {
01492 int start = (startChar - &text->at(0));
01493 int last = (lastChar - &text->at(0) );
01494
01495
01496 KoBidiControl *control = new KoBidiControl( line->context(), line->status );
01497 QString str;
01498 str.setUnicode( 0, last - start + 1 );
01499
01500 KoTextStringChar *ch = startChar;
01501 QChar *qch = (QChar *)str.unicode();
01502 while ( ch <= lastChar ) {
01503 *qch = ch->c;
01504 qch++;
01505 ch++;
01506 }
01507 int x = startChar->x;
01508
01509 Q3PtrList<KoTextRun> *runs;
01510 runs = KoComplexText::bidiReorderLine(control, str, 0, last - start + 1,
01511 (text->isRightToLeft() ? QChar::DirR : QChar::DirL) );
01512
01513
01514
01515 int numSpaces = 0;
01516
01517 if( align == Qt::AlignLeft ) {
01518
01519 if ( text->isRightToLeft() )
01520 align = Qt::AlignRight;
01521 }
01522
01523 if ( align & Qt::AlignHCenter )
01524 x += space/2;
01525 else if ( align & Qt::AlignRight )
01526 x += space;
01527 else if ( align & Qt::AlignJustify ) {
01528 for ( int j = start; j < last; ++j ) {
01529 if( isBreakable( text, j ) ) {
01530 numSpaces++;
01531 }
01532 }
01533 }
01534 int toAdd = 0;
01535 bool first = true;
01536 KoTextRun *r = runs->first();
01537 int xmax = -0xffffff;
01538 while ( r ) {
01539 if(r->level %2) {
01540
01541 int pos = r->stop + start;
01542 while(pos >= r->start + start) {
01543 KoTextStringChar *c = &text->at(pos);
01544 if( numSpaces && !first && isBreakable( text, pos ) ) {
01545 int s = space / numSpaces;
01546 toAdd += s;
01547 space -= s;
01548 numSpaces--;
01549 } else if ( first ) {
01550 first = false;
01551 if ( c->c == ' ' )
01552 x -= c->format()->width( ' ' );
01553 }
01554 c->x = x + toAdd;
01555 c->rightToLeft = true;
01556 c->startOfRun = false;
01557 int ww = 0;
01558 if ( c->c.unicode() >= 32 || c->c == '\t' || c->c == '\n' || c->isCustom() ) {
01559 ww = c->width;
01560 } else {
01561 ww = c->format()->width( ' ' );
01562 }
01563 if ( xmax < x + toAdd + ww ) xmax = x + toAdd + ww;
01564 x += ww;
01565 pos--;
01566 }
01567 } else {
01568 int pos = r->start + start;
01569 while(pos <= r->stop + start) {
01570 KoTextStringChar* c = &text->at(pos);
01571 if( numSpaces && !first && isBreakable( text, pos ) ) {
01572 int s = space / numSpaces;
01573 toAdd += s;
01574 space -= s;
01575 numSpaces--;
01576 } else if ( first ) {
01577 first = false;
01578 if ( c->c == ' ' )
01579 x -= c->format()->width( ' ' );
01580 }
01581 c->x = x + toAdd;
01582 c->rightToLeft = false;
01583 c->startOfRun = false;
01584 int ww = 0;
01585 if ( c->c.unicode() >= 32 || c->c == '\t' || c->isCustom() ) {
01586 ww = c->width;
01587 } else {
01588 ww = c->format()->width( ' ' );
01589 }
01590
01591 if ( xmax < x + toAdd + ww ) xmax = x + toAdd + ww;
01592 x += ww;
01593 pos++;
01594 }
01595 }
01596 text->at( r->start + start ).startOfRun = true;
01597 r = runs->next();
01598 }
01599
01600 line->w = xmax + 10;
01601 KoTextParagLineStart *ls = new KoTextParagLineStart( control->context, control->status );
01602 delete control;
01603 delete runs;
01604 return ls;
01605 }
01606 #endif
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 kWarning(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->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(); }