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