lib Library API Documentation

korichtext.cpp

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