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