00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "KoTextParag.h"
00022 #include "KoTextDocument.h"
00023 #include "KoParagCounter.h"
00024 #include "KoTextZoomHandler.h"
00025 #include "KoStyleCollection.h"
00026 #include "KoVariable.h"
00027 #include <KoOasisContext.h>
00028 #include <KoXmlWriter.h>
00029 #include <KoGenStyles.h>
00030 #include <KoDom.h>
00031 #include <KoXmlNS.h>
00032 #include <KoUnit.h>
00033 #include <kglobal.h>
00034 #include <klocale.h>
00035 #include <kdebug.h>
00036 #include <kglobalsettings.h>
00037 #include <assert.h>
00038
00039 #include <Q3MemArray>
00040 #include <Q3PtrList>
00041
00042
00043
00044 KoTextParag::KoTextParag( KoTextDocument *d, KoTextParag *pr, KoTextParag *nx, bool updateIds )
00045 : p( pr ), n( nx ), doc( d ),
00046 m_invalid( true ),
00047 changed( false ),
00048 fullWidth( true ),
00049 newLinesAllowed( true ),
00050 visible( true ),
00051 movedDown( false ),
00052 m_toc( false ),
00053 align( 0 ),
00054 m_lineChanged( -1 ),
00055 m_wused( 0 ),
00056 mSelections( 0 ),
00057 mFloatingItems( 0 ),
00058 tArray( 0 )
00059 {
00060 defFormat = formatCollection()->defaultFormat();
00061
00062 if ( p ) {
00063 p->n = this;
00064 }
00065 if ( n ) {
00066 n->p = this;
00067 }
00068
00069 if ( !p && doc )
00070 doc->setFirstParag( this );
00071 if ( !n && doc )
00072 doc->setLastParag( this );
00073
00074
00075
00076
00077
00078
00079 if ( p )
00080 id = p->id + 1;
00081 else
00082 id = 0;
00083 if ( n && updateIds ) {
00084 KoTextParag *s = n;
00085 while ( s ) {
00086 s->id = s->p->id + 1;
00087
00088 s = s->n;
00089 }
00090 }
00091
00092 str = new KoTextString();
00093 str->insert( 0, " ", formatCollection()->defaultFormat() );
00094 setJoinBorder( true );
00095 }
00096
00097 KoTextParag::~KoTextParag()
00098 {
00099
00100
00101
00102 const int len = str->length();
00103 for ( int i = 0; i < len; ++i ) {
00104 KoTextStringChar *c = at( i );
00105 if ( doc && c->isCustom() ) {
00106 doc->unregisterCustomItem( c->customItem(), this );
00107
00108 }
00109 }
00110
00111 delete str;
00112 str = 0;
00113
00114
00115
00116
00117 delete [] tArray;
00118
00119 QMap<int, KoTextParagLineStart*>::Iterator it = lineStarts.begin();
00120 for ( ; it != lineStarts.end(); ++it )
00121 delete *it;
00122 if ( mSelections ) delete mSelections;
00123 if ( mFloatingItems ) delete mFloatingItems;
00124
00125 if (p)
00126 p->setNext(n);
00127 if (n)
00128 n->setPrev(p);
00129
00131 if ( !doc->isDestroying() )
00132 {
00133 doc->informParagraphDeleted( this );
00134 }
00135
00137 }
00138
00139 void KoTextParag::setNext( KoTextParag *s )
00140 {
00141 n = s;
00142 if ( !n && doc )
00143 doc->setLastParag( this );
00144 }
00145
00146 void KoTextParag::setPrev( KoTextParag *s )
00147 {
00148 p = s;
00149 if ( !p && doc )
00150 doc->setFirstParag( this );
00151 }
00152
00153 void KoTextParag::invalidate( int )
00154 {
00155 m_invalid = true;
00156 #if 0
00157 if ( invalid < 0 )
00158 invalid = chr;
00159 else
00160 invalid = qMin( invalid, chr );
00161 #endif
00162 }
00163
00164 void KoTextParag::setChanged( bool b, bool )
00165 {
00166 changed = b;
00167 m_lineChanged = -1;
00168 }
00169
00170 void KoTextParag::setLineChanged( short int line )
00171 {
00172 if ( m_lineChanged == -1 ) {
00173 if ( !changed )
00174 m_lineChanged = line;
00175 }
00176 else
00177 m_lineChanged = qMin( m_lineChanged, line );
00178 changed = true;
00179
00180 }
00181
00182 void KoTextParag::insert( int index, const QString &s )
00183 {
00184 str->insert( index, s, formatCollection()->defaultFormat() );
00185 invalidate( index );
00186
00187 }
00188
00189 void KoTextParag::truncate( int index )
00190 {
00191 str->truncate( index );
00192 insert( length(), " " );
00193
00194 }
00195
00196 void KoTextParag::remove( int index, int len )
00197 {
00198 if ( index + len - str->length() > 0 )
00199 return;
00200 for ( int i = index; i < index + len; ++i ) {
00201 KoTextStringChar *c = at( i );
00202 if ( doc && c->isCustom() ) {
00203 doc->unregisterCustomItem( c->customItem(), this );
00204
00205 }
00206 }
00207 str->remove( index, len );
00208 invalidate( 0 );
00209
00210 }
00211
00212 void KoTextParag::join( KoTextParag *s )
00213 {
00214
00215 int oh = r.height() + s->r.height();
00216 n = s->n;
00217 if ( n )
00218 n->p = this;
00219 else
00220 doc->setLastParag( this );
00221
00222 int start = str->length();
00223 if ( length() > 0 && at( length() - 1 )->c == ' ' ) {
00224 remove( length() - 1, 1 );
00225 --start;
00226 }
00227 append( s->str->toString(), true );
00228
00229 for ( int i = 0; i < s->length(); ++i ) {
00230 if ( doc->useFormatCollection() ) {
00231 s->str->at( i ).format()->addRef();
00232 str->setFormat( i + start, s->str->at( i ).format(), true );
00233 }
00234 if ( s->str->at( i ).isCustom() ) {
00235 KoTextCustomItem * item = s->str->at( i ).customItem();
00236 str->at( i + start ).setCustomItem( item );
00237 s->str->at( i ).loseCustomItem();
00238 doc->unregisterCustomItem( item, s );
00239 doc->registerCustomItem( item, this );
00240 }
00241 }
00242 Q_ASSERT(str->at(str->length()-1).c == ' ');
00243
00244
00245
00246
00247
00248
00249
00250 delete s;
00251 invalidate( 0 );
00253 invalidateCounters();
00255 r.setHeight( oh );
00256
00257 if ( n ) {
00258 KoTextParag *s = n;
00259 while ( s ) {
00260 s->id = s->p->id + 1;
00261
00262
00263 s->changed = true;
00264 s = s->n;
00265 }
00266 }
00267 format();
00268
00269 }
00270
00271 void KoTextParag::move( int &dy )
00272 {
00273
00274 if ( dy == 0 )
00275 return;
00276 changed = true;
00277 r.moveBy( 0, dy );
00278 if ( mFloatingItems ) {
00279 for ( KoTextCustomItem *i = mFloatingItems->first(); i; i = mFloatingItems->next() ) {
00280 i->finalize();
00281 }
00282 }
00283
00284
00285
00286 movedDown = false;
00287
00288
00289 if ( doc && doc->isPageBreakEnabled() ) {
00290 int shift;
00291 if ( ( shift = doc->formatter()->formatVertically( doc, this ) ) ) {
00292 if ( p )
00293 p->setChanged( true );
00294 dy += shift;
00295 }
00296 }
00297 }
00298
00299 void KoTextParag::format( int start, bool doMove )
00300 {
00301 if ( !str || str->length() == 0 || !formatter() )
00302 return;
00303
00304 if ( isValid() )
00305 return;
00306
00307
00308
00309 r.moveTopLeft( QPoint( documentX(), p ? p->r.y() + p->r.height() : documentY() ) );
00310
00311
00312
00313 movedDown = false;
00314 bool formattedAgain = false;
00315
00316 formatAgain:
00317 r.setWidth( documentWidth() );
00318
00319
00320 if ( doc && mFloatingItems ) {
00321 for ( KoTextCustomItem *i = mFloatingItems->first(); i; i = mFloatingItems->next() ) {
00322 if ( i->placement() == KoTextCustomItem::PlaceRight )
00323 i->move( r.x() + r.width() - i->width, r.y() );
00324 else
00325 i->move( i->x(), r.y() );
00326 }
00327 }
00328 QMap<int, KoTextParagLineStart*> oldLineStarts = lineStarts;
00329 lineStarts.clear();
00330 int y;
00331 bool formatterWorked = formatter()->format( doc, this, start, oldLineStarts, y, m_wused );
00332
00333
00334
00335
00336
00337 QMap<int, KoTextParagLineStart*>::Iterator it = oldLineStarts.begin();
00338
00339 for ( ; it != oldLineStarts.end(); ++it )
00340 delete *it;
00341
00342
00345
00346
00347
00348
00349 {
00350 if ( lineStarts.count() == 1 ) {
00351
00352
00353
00354
00355 {
00356 r.setWidth( lineStarts[0]->w );
00357 }
00358 }
00359 if ( newLinesAllowed ) {
00360 it = lineStarts.begin();
00361 int usedw = 0; int lineid = 0;
00362 for ( ; it != lineStarts.end(); ++it, ++lineid ) {
00363 usedw = qMax( usedw, (*it)->w );
00364 }
00365 if ( r.width() <= 0 ) {
00366
00367
00368
00369 r.setWidth( usedw );
00370 } else {
00371 r.setWidth( qMin( usedw, r.width() ) );
00372 }
00373 }
00374 }
00375
00376 if ( y != r.height() )
00377 r.setHeight( y );
00378
00379 if ( !visible )
00380 r.setHeight( 0 );
00381
00382
00383 if ( doc && doc->isPageBreakEnabled() ) {
00384 int shift = doc->formatter()->formatVertically( doc, this );
00385
00386 if ( shift && !formattedAgain ) {
00387 formattedAgain = true;
00388 goto formatAgain;
00389 }
00390 }
00391
00392 if ( doc )
00393 doc->formatter()->postFormat( this );
00394
00395 if ( n && doMove && n->isValid() && r.y() + r.height() != n->r.y() ) {
00396
00397 int dy = ( r.y() + r.height() ) - n->r.y();
00398 KoTextParag *s = n;
00399 bool makeInvalid = false;
00400
00401 while ( s && dy ) {
00402 if ( s->movedDown ) {
00403 s->invalidate( 0 );
00404 break;
00405 }
00406 if ( !s->isFullWidth() )
00407 makeInvalid = true;
00408 if ( makeInvalid )
00409 s->invalidate( 0 );
00410 s->move( dy );
00411
00412
00413 s = s->n;
00414 }
00415 }
00416
00417
00418 if ( mFloatingItems ) {
00419 #ifdef DEBUG_CI_PLACEMENT
00420 kDebug(32500) << lineStarts.count() << " lines" << endl;
00421 #endif
00422
00423 int len = length();
00424 int line = -1;
00425 int lineY = 0;
00426 int baseLine = 0;
00427 QMap<int, KoTextParagLineStart*>::Iterator it = lineStarts.begin();
00428 for ( int i = 0 ; i < len; ++i ) {
00429 KoTextStringChar *chr = &str->at( i );
00430 if ( chr->lineStart ) {
00431 ++line;
00432 if ( line > 0 )
00433 ++it;
00434 lineY = (*it)->y;
00435 baseLine = (*it)->baseLine;
00436 #ifdef DEBUG_CI_PLACEMENT
00437 kDebug(32500) << "New line (" << line << "): lineStart=" << (*it) << " lineY=" << lineY << " baseLine=" << baseLine << " height=" << (*it)->h << endl;
00438 #endif
00439 }
00440 if ( chr->isCustom() ) {
00441 int x = chr->x;
00442 KoTextCustomItem* item = chr->customItem();
00443 Q_ASSERT( baseLine >= item->ascent() );
00444 int y = lineY + baseLine - item->ascent();
00445 #ifdef DEBUG_CI_PLACEMENT
00446 kDebug(32500) << "Custom item: i=" << i << " x=" << x << " lineY=" << lineY << " baseLine=" << baseLine << " ascent=" << item->ascent() << " -> y=" << y << endl;
00447 #endif
00448 item->move( x, y );
00449 item->finalize();
00450 }
00451 }
00452 }
00453
00454
00455 if ( formatterWorked )
00456 {
00457 m_invalid = false;
00458 }
00459 changed = true;
00460
00461 }
00462
00463 int KoTextParag::lineHeightOfChar( int i, int *bl, int *y ) const
00464 {
00465 if ( !isValid() )
00466 ( (KoTextParag*)this )->format();
00467
00468 QMap<int, KoTextParagLineStart*>::ConstIterator it = lineStarts.end();
00469 --it;
00470 for ( ;; ) {
00471 if ( i >= it.key() ) {
00472 if ( bl )
00473 *bl = ( *it )->baseLine;
00474 if ( y )
00475 *y = ( *it )->y;
00476 return ( *it )->h;
00477 }
00478 if ( it == lineStarts.begin() )
00479 break;
00480 --it;
00481 }
00482
00483 kWarning(32500) << "KoTextParag::lineHeightOfChar: couldn't find lh for " << i << endl;
00484 return 15;
00485 }
00486
00487 KoTextStringChar *KoTextParag::lineStartOfChar( int i, int *index, int *line ) const
00488 {
00489 if ( !isValid() )
00490 ( (KoTextParag*)this )->format();
00491
00492 int l = (int)lineStarts.count() - 1;
00493 QMap<int, KoTextParagLineStart*>::ConstIterator it = lineStarts.end();
00494 --it;
00495 for ( ;; ) {
00496 if ( i >= it.key() ) {
00497 if ( index )
00498 *index = it.key();
00499 if ( line )
00500 *line = l;
00501 return &str->at( it.key() );
00502 }
00503 if ( it == lineStarts.begin() )
00504 break;
00505 --it;
00506 --l;
00507 }
00508
00509 kWarning(32500) << "KoTextParag::lineStartOfChar: couldn't find " << i << endl;
00510 return 0;
00511 }
00512
00513 int KoTextParag::lines() const
00514 {
00515 if ( !isValid() )
00516 ( (KoTextParag*)this )->format();
00517
00518 return (int)lineStarts.count();
00519 }
00520
00521 KoTextStringChar *KoTextParag::lineStartOfLine( int line, int *index ) const
00522 {
00523 if ( !isValid() )
00524 ( (KoTextParag*)this )->format();
00525
00526 if ( line >= 0 && line < (int)lineStarts.count() ) {
00527 QMap<int, KoTextParagLineStart*>::ConstIterator it = lineStarts.begin();
00528 while ( line-- > 0 )
00529 ++it;
00530 int i = it.key();
00531 if ( index )
00532 *index = i;
00533 return &str->at( i );
00534 }
00535
00536 kWarning(32500) << "KoTextParag::lineStartOfLine: couldn't find " << line << endl;
00537 return 0;
00538 }
00539
00540 int KoTextParag::leftGap() const
00541 {
00542 if ( !isValid() )
00543 ( (KoTextParag*)this )->format();
00544
00545 int line = 0;
00546 int x = str->at(0).x;
00547 if ( str->isBidi() ) {
00548 for ( int i = 1; i < str->length(); ++i )
00549 x = qMin(x, str->at(i).x);
00550 return x;
00551 }
00552
00553 QMap<int, KoTextParagLineStart*>::ConstIterator it = lineStarts.begin();
00554 while (line < (int)lineStarts.count()) {
00555 int i = it.key();
00556 x = qMin(x, str->at(i).x);
00557 ++it;
00558 ++line;
00559 }
00560 return x;
00561 }
00562
00563 void KoTextParag::setFormat( int index, int len, const KoTextFormat *_f, bool useCollection, int flags )
00564 {
00565 Q_ASSERT( useCollection );
00566 if ( index < 0 )
00567 index = 0;
00568 if ( index > str->length() - 1 )
00569 index = str->length() - 1;
00570 if ( index + len >= str->length() )
00571 len = str->length() - index;
00572
00573 KoTextFormatCollection *fc = 0;
00574 if ( useCollection )
00575 fc = formatCollection();
00576 KoTextFormat *of;
00577 for ( int i = 0; i < len; ++i ) {
00578 of = str->at( i + index ).format();
00579 if ( !changed && _f->key() != of->key() )
00580 changed = true;
00581
00582
00583
00584 if ( m_invalid == false &&
00585 ( _f->font().family() != of->font().family() ||
00586 _f->pointSize() != of->pointSize() ||
00587 _f->font().weight() != of->font().weight() ||
00588 _f->font().italic() != of->font().italic() ||
00589 _f->vAlign() != of->vAlign() ||
00590 _f->relativeTextSize() != of->relativeTextSize() ||
00591 _f->offsetFromBaseLine() != of->offsetFromBaseLine() ||
00592 _f->wordByWord() != of->wordByWord() ||
00593 _f->attributeFont() != of->attributeFont() ||
00594 _f->language() != of->language() ||
00595 _f->hyphenation() != of->hyphenation() ||
00596 _f->shadowDistanceX() != of->shadowDistanceX() ||
00597 _f->shadowDistanceY() != of->shadowDistanceY()
00598 ) ) {
00599 invalidate( 0 );
00600 }
00601 if ( flags == -1 || flags == KoTextFormat::Format || !fc ) {
00602 #ifdef DEBUG_COLLECTION
00603 kDebug(32500) << " KoTextParag::setFormat, will use format(f) " << f << " " << _f->key() << endl;
00604 #endif
00605 KoTextFormat* f = fc ? fc->format( _f ) : const_cast<KoTextFormat *>( _f );
00606 str->setFormat( i + index, f, useCollection, true );
00607 } else {
00608 #ifdef DEBUG_COLLECTION
00609 kDebug(32500) << " KoTextParag::setFormat, will use format(of,f,flags) of=" << of << " " << of->key() << ", f=" << _f << " " << _f->key() << endl;
00610 #endif
00611 KoTextFormat *fm = fc->format( of, _f, flags );
00612 #ifdef DEBUG_COLLECTION
00613 kDebug(32500) << " KoTextParag::setFormat, format(of,f,flags) returned " << fm << " " << fm->key() << " " << endl;
00614 #endif
00615 str->setFormat( i + index, fm, useCollection );
00616 }
00617 }
00618 }
00619
00620 void KoTextParag::drawCursorDefault( QPainter &painter, KoTextCursor *cursor, int curx, int cury, int curh, const QColorGroup &cg )
00621 {
00622 painter.fillRect( QRect( curx, cury, 1, curh ), cg.color( QColorGroup::Text ) );
00623 painter.save();
00624 if ( str->isBidi() ) {
00625 const int d = 4;
00626 if ( at( cursor->index() )->rightToLeft ) {
00627 painter.setPen( Qt::black );
00628 painter.drawLine( curx, cury, curx - d / 2, cury + d / 2 );
00629 painter.drawLine( curx, cury + d, curx - d / 2, cury + d / 2 );
00630 } else {
00631 painter.setPen( Qt::black );
00632 painter.drawLine( curx, cury, curx + d / 2, cury + d / 2 );
00633 painter.drawLine( curx, cury + d, curx + d / 2, cury + d / 2 );
00634 }
00635 }
00636 painter.restore();
00637 }
00638
00639 int *KoTextParag::tabArray() const
00640 {
00641 int *ta = tArray;
00642 if ( !ta && doc )
00643 ta = doc->tabArray();
00644 return ta;
00645 }
00646
00647 int KoTextParag::nextTabDefault( int, int x )
00648 {
00649 int *ta = tArray;
00650
00651 if ( !ta )
00652 ta = doc->tabArray();
00653 int tabStopWidth = doc->tabStopWidth();
00654
00655 if ( tabStopWidth != 0 )
00656 return tabStopWidth*(x/tabStopWidth+1);
00657 else
00658 return x;
00659 }
00660
00661 KoTextFormatCollection *KoTextParag::formatCollection() const
00662 {
00663 if ( doc )
00664 return doc->formatCollection();
00665
00666
00667
00668 return 0L;
00669 }
00670
00671 void KoTextParag::show()
00672 {
00673 if ( visible )
00674 return;
00675 visible = true;
00676 }
00677
00678 void KoTextParag::hide()
00679 {
00680 if ( !visible )
00681 return;
00682 visible = false;
00683 }
00684
00685 void KoTextParag::setDirection( QChar::Direction d )
00686 {
00687 if ( str && str->direction() != d ) {
00688 str->setDirection( d );
00689 invalidate( 0 );
00691 m_layout.direction = d;
00692 invalidateCounters();
00694 }
00695 }
00696
00697 QChar::Direction KoTextParag::direction() const
00698 {
00699 return (str ? str->direction() : QChar::DirON );
00700 }
00701
00702 void KoTextParag::setSelection( int id, int start, int end )
00703 {
00704 QMap<int, KoTextParagSelection>::ConstIterator it = selections().find( id );
00705 if ( it != mSelections->end() ) {
00706 if ( start == ( *it ).start && end == ( *it ).end )
00707 return;
00708 }
00709
00710 KoTextParagSelection sel;
00711 sel.start = start;
00712 sel.end = end;
00713 (*mSelections)[ id ] = sel;
00714 setChanged( true, true );
00715 }
00716
00717 void KoTextParag::removeSelection( int id )
00718 {
00719 if ( !hasSelection( id ) )
00720 return;
00721 if ( mSelections )
00722 mSelections->remove( id );
00723 setChanged( true, true );
00724 }
00725
00726 int KoTextParag::selectionStart( int id ) const
00727 {
00728 if ( !mSelections )
00729 return -1;
00730 QMap<int, KoTextParagSelection>::ConstIterator it = mSelections->find( id );
00731 if ( it == mSelections->end() )
00732 return -1;
00733 return ( *it ).start;
00734 }
00735
00736 int KoTextParag::selectionEnd( int id ) const
00737 {
00738 if ( !mSelections )
00739 return -1;
00740 QMap<int, KoTextParagSelection>::ConstIterator it = mSelections->find( id );
00741 if ( it == mSelections->end() )
00742 return -1;
00743 return ( *it ).end;
00744 }
00745
00746 bool KoTextParag::hasSelection( int id ) const
00747 {
00748 if ( !mSelections )
00749 return false;
00750 QMap<int, KoTextParagSelection>::ConstIterator it = mSelections->find( id );
00751 if ( it == mSelections->end() )
00752 return false;
00753 return ( *it ).start != ( *it ).end || length() == 1;
00754 }
00755
00756 bool KoTextParag::fullSelected( int id ) const
00757 {
00758 if ( !mSelections )
00759 return false;
00760 QMap<int, KoTextParagSelection>::ConstIterator it = mSelections->find( id );
00761 if ( it == mSelections->end() )
00762 return false;
00763 return ( *it ).start == 0 && ( *it ).end == str->length() - 1;
00764 }
00765
00766 int KoTextParag::lineY( int l ) const
00767 {
00768 if ( l > (int)lineStarts.count() - 1 ) {
00769 kWarning(32500) << "KoTextParag::lineY: line " << l << " out of range!" << endl;
00770 return 0;
00771 }
00772
00773 if ( !isValid() )
00774 ( (KoTextParag*)this )->format();
00775
00776 QMap<int, KoTextParagLineStart*>::ConstIterator it = lineStarts.begin();
00777 while ( l-- > 0 )
00778 ++it;
00779 return ( *it )->y;
00780 }
00781
00782 int KoTextParag::lineBaseLine( int l ) const
00783 {
00784 if ( l > (int)lineStarts.count() - 1 ) {
00785 kWarning(32500) << "KoTextParag::lineBaseLine: line " << l << " out of range!" << endl;
00786 return 10;
00787 }
00788
00789 if ( !isValid() )
00790 ( (KoTextParag*)this )->format();
00791
00792 QMap<int, KoTextParagLineStart*>::ConstIterator it = lineStarts.begin();
00793 while ( l-- > 0 )
00794 ++it;
00795 return ( *it )->baseLine;
00796 }
00797
00798 int KoTextParag::lineHeight( int l ) const
00799 {
00800 if ( l > (int)lineStarts.count() - 1 ) {
00801 kWarning(32500) << "KoTextParag::lineHeight: line " << l << " out of range!" << endl;
00802 return 15;
00803 }
00804
00805 if ( !isValid() )
00806 ( (KoTextParag*)this )->format();
00807
00808 QMap<int, KoTextParagLineStart*>::ConstIterator it = lineStarts.begin();
00809 while ( l-- > 0 )
00810 ++it;
00811 return ( *it )->h;
00812 }
00813
00814 void KoTextParag::lineInfo( int l, int &y, int &h, int &bl ) const
00815 {
00816 if ( l > (int)lineStarts.count() - 1 ) {
00817 kWarning(32500) << "KoTextParag::lineInfo: line " << l << " out of range!" << endl;
00818 kDebug(32500) << (int)lineStarts.count() - 1 << " " << l << endl;
00819 y = 0;
00820 h = 15;
00821 bl = 10;
00822 return;
00823 }
00824
00825 if ( !isValid() )
00826 ( (KoTextParag*)this )->format();
00827
00828 QMap<int, KoTextParagLineStart*>::ConstIterator it = lineStarts.begin();
00829 while ( l-- > 0 )
00830 ++it;
00831 y = ( *it )->y;
00832 h = ( *it )->h;
00833 bl = ( *it )->baseLine;
00834 }
00835
00836 uint KoTextParag::alignment() const
00837 {
00838 return align;
00839 }
00840
00841 void KoTextParag::setFormat( KoTextFormat *fm )
00842 {
00843 #if 0
00844 bool doUpdate = false;
00845 if (defFormat && (defFormat != formatCollection()->defaultFormat()))
00846 doUpdate = true;
00847 #endif
00848 defFormat = formatCollection()->format( fm );
00849 #if 0
00850 if ( !doUpdate )
00851 return;
00852 for ( int i = 0; i < length(); ++i ) {
00853 if ( at( i )->format()->styleName() == defFormat->styleName() )
00854 at( i )->format()->updateStyle();
00855 }
00856 #endif
00857 }
00858
00859 KoTextFormatterBase *KoTextParag::formatter() const
00860 {
00861 if ( doc )
00862 return doc->formatter();
00863 return 0;
00864 }
00865
00866
00867
00868
00869
00870
00871
00872 int KoTextParag::widthUsed() const
00873 {
00874 return m_wused;
00875 }
00876
00877 void KoTextParag::setTabArray( int *a )
00878 {
00879 delete [] tArray;
00880 tArray = a;
00881 }
00882
00883 void KoTextParag::setTabStops( int tw )
00884 {
00885 if ( doc )
00886 doc->setTabStops( tw );
00887
00888
00889 }
00890
00891 QMap<int, KoTextParagSelection> &KoTextParag::selections() const
00892 {
00893 if ( !mSelections )
00894 ((KoTextParag *)this)->mSelections = new QMap<int, KoTextParagSelection>;
00895 return *mSelections;
00896 }
00897
00898 Q3PtrList<KoTextCustomItem> &KoTextParag::floatingItems() const
00899 {
00900 if ( !mFloatingItems )
00901 ((KoTextParag *)this)->mFloatingItems = new Q3PtrList<KoTextCustomItem>;
00902 return *mFloatingItems;
00903 }
00904
00905 void KoTextCursor::setIndex( int i, bool )
00906 {
00907
00908
00909
00910
00911
00912 if ( i < 0 || i > string->length() ) {
00913 #if defined(QT_CHECK_RANGE)
00914 kWarning(32500) << "KoTextCursor::setIndex: " << i << " out of range" << endl;
00915
00916 #endif
00917 i = i < 0 ? 0 : string->length() - 1;
00918 }
00919
00920 tmpIndex = -1;
00921 idx = i;
00922 }
00923
00925
00926
00927 KoParagCounter *KoTextParag::counter()
00928 {
00929 if ( !m_layout.counter )
00930 return 0L;
00931
00932
00933 if ( m_layout.counter->numbering() == KoParagCounter::NUM_NONE
00934
00935 && ( !m_layout.style || !m_layout.style->isOutline() ) )
00936 setNoCounter();
00937 return m_layout.counter;
00938 }
00939
00940 void KoTextParag::setMargin( Q3StyleSheetItem::Margin m, double _i )
00941 {
00942
00943 m_layout.margins[m] = _i;
00944 if ( m == Q3StyleSheetItem::MarginTop && prev() )
00945 prev()->invalidate(0);
00946 invalidate(0);
00947 }
00948
00949 void KoTextParag::setMargins( const double * margins )
00950 {
00951 for ( int i = 0 ; i < 5 ; ++i )
00952 m_layout.margins[i] = margins[i];
00953 invalidate(0);
00954 }
00955
00956 void KoTextParag::setAlign( int align )
00957 {
00958 Q_ASSERT( align <= Qt::AlignJustify );
00959 align &= Qt::AlignHorizontal_Mask;
00960 setAlignment( align );
00961 m_layout.alignment = align;
00962 }
00963
00964 int KoTextParag::resolveAlignment() const
00965 {
00966 if ( (int)m_layout.alignment == Qt::AlignLeft )
00967 return str->isRightToLeft() ? Qt::AlignRight : Qt::AlignLeft;
00968 return m_layout.alignment;
00969 }
00970
00971 void KoTextParag::setLineSpacing( double _i )
00972 {
00973 m_layout.setLineSpacingValue(_i);
00974 invalidate(0);
00975 }
00976
00977 void KoTextParag::setLineSpacingType( KoParagLayout::SpacingType _type )
00978 {
00979 m_layout.lineSpacingType = _type;
00980 invalidate(0);
00981 }
00982
00983 void KoTextParag::setTopBorder( const KoBorder & _brd )
00984 {
00985 m_layout.topBorder = _brd;
00986 invalidate(0);
00987 }
00988
00989 void KoTextParag::setBottomBorder( const KoBorder & _brd )
00990 {
00991 m_layout.bottomBorder = _brd;
00992 invalidate(0);
00993 }
00994
00995 void KoTextParag::setJoinBorder( bool join )
00996 {
00997 m_layout.joinBorder = join;
00998 invalidate(0);
00999 }
01000
01001 void KoTextParag::setBackgroundColor ( const QColor& color )
01002 {
01003 m_layout.backgroundColor = color;
01004 invalidate(0);
01005 }
01006
01007 void KoTextParag::setNoCounter()
01008 {
01009 delete m_layout.counter;
01010 m_layout.counter = 0L;
01011 invalidateCounters();
01012 }
01013
01014 void KoTextParag::setCounter( const KoParagCounter * pCounter )
01015 {
01016
01017 const bool isFootNote = m_layout.counter &&
01018 m_layout.counter->numbering() == KoParagCounter::NUM_FOOTNOTE;
01019 if ( isFootNote ) {
01020 const QString footNotePrefix = m_layout.counter->prefix();
01021 delete m_layout.counter;
01022 m_layout.counter = pCounter ? new KoParagCounter( *pCounter ) : new KoParagCounter();
01023 m_layout.counter->setNumbering( KoParagCounter::NUM_FOOTNOTE );
01024 m_layout.counter->setStyle( KoParagCounter::STYLE_NONE );
01025 m_layout.counter->setPrefix( footNotePrefix );
01026 m_layout.counter->setSuffix( QString::null );
01027 invalidateCounters();
01028 } else {
01029 if ( pCounter )
01030 setCounter( *pCounter );
01031 else
01032 setNoCounter();
01033 }
01034 }
01035
01036 void KoTextParag::setCounter( const KoParagCounter & counter )
01037 {
01038
01039 if ( counter.numbering() == KoParagCounter::NUM_NONE
01040
01041 && ( !m_layout.style || !m_layout.style->isOutline() ) )
01042 {
01043 setNoCounter();
01044 }
01045 else
01046 {
01047 delete m_layout.counter;
01048 m_layout.counter = new KoParagCounter( counter );
01049
01050
01051 invalidateCounters();
01052 }
01053 }
01054
01055 void KoTextParag::invalidateCounters()
01056 {
01057
01058
01059 invalidate( 0 );
01060 if ( m_layout.counter )
01061 m_layout.counter->invalidate();
01062 KoTextParag *s = next();
01063
01064
01065
01066 while ( s ) {
01067 if ( s->m_layout.counter )
01068 s->m_layout.counter->invalidate();
01069 s->invalidate( 0 );
01070 s = s->next();
01071 }
01072 }
01073
01074 int KoTextParag::counterWidth() const
01075 {
01076 if ( !m_layout.counter )
01077 return 0;
01078
01079 return m_layout.counter->width( this );
01080 }
01081
01082
01083
01084 void KoTextParag::drawLabel( QPainter* p, int xLU, int yLU, int , int , int baseLU, const QColorGroup& )
01085 {
01086 if ( !m_layout.counter )
01087 return;
01088
01089 if ( m_layout.counter->numbering() == KoParagCounter::NUM_NONE )
01090 return;
01091
01092 int counterWidthLU = m_layout.counter->width( this );
01093
01094
01095 KoTextFormat counterFormat( *KoParagCounter::counterFormat( this ) );
01096 if ( !m_layout.style || !m_layout.style->isOutline() )
01097 {
01098
01099
01100 counterFormat.setBold( false );
01101 counterFormat.setItalic( false );
01102 }
01103 KoTextFormat* format = &counterFormat;
01104 p->save();
01105
01106 QColor textColor( format->color() );
01107 if ( !textColor.isValid() )
01108 textColor = KoTextFormat::defaultTextColor( p );
01109 p->setPen( QPen( textColor ) );
01110
01111 KoTextZoomHandler * zh = textDocument()->paintingZoomHandler();
01112 assert( zh );
01113
01114
01115 bool rtl = str->isRightToLeft();
01116 int xLeft = zh->layoutUnitToPixelX( xLU - (rtl ? 0 : counterWidthLU) );
01117 int y = zh->layoutUnitToPixelY( yLU );
01118
01119 int base = zh->layoutUnitToPixelY( yLU, baseLU );
01120 int counterWidth = zh->layoutUnitToPixelX( xLU, counterWidthLU );
01121 int height = zh->layoutUnitToPixelY( yLU, format->height() );
01122
01123 QFont font( format->screenFont( zh ) );
01124
01125 if ( m_layout.counter->numbering() == KoParagCounter::NUM_FOOTNOTE )
01126 {
01127 int pointSize = ( ( font.pointSize() * 2 ) / 3 );
01128 font.setPointSize( pointSize );
01129 y -= ( height - QFontMetrics(font).height() );
01130 }
01131 p->setFont( font );
01132
01133
01134 if ( m_layout.counter->isBullet() )
01135 {
01136 int xBullet = xLeft + zh->layoutUnitToPixelX( m_layout.counter->bulletX() );
01137
01138
01139
01140 int width = zh->layoutUnitToPixelX( xLeft, format->width( ' ' ) );
01141
01142
01143
01144
01145 QString prefix = m_layout.counter->prefix();
01146 if ( !prefix.isEmpty() )
01147 {
01148 if ( rtl )
01149 prefix.prepend( ' ' );
01150 KoTextParag::drawFontEffects( p, format, zh, format->screenFont( zh ), textColor, xLeft, base, width, y, height, prefix[0] );
01151
01152 int posY =y + base - format->offsetFromBaseLine();
01153
01154
01155 int sy = format->shadowY( zh );
01156 if ( sy < 0)
01157 posY -= sy;
01158
01159 p->drawText( xLeft, posY, prefix );
01160 }
01161
01162 QRect er( xBullet + (rtl ? width : 0), y + height / 2 - width / 2, width, width );
01163
01164 int posY = 0;
01165 switch ( m_layout.counter->style() )
01166 {
01167 case KoParagCounter::STYLE_DISCBULLET:
01168 p->setBrush( QBrush(textColor) );
01169 p->drawEllipse( er );
01170 p->setBrush( Qt::NoBrush );
01171 break;
01172 case KoParagCounter::STYLE_SQUAREBULLET:
01173 p->fillRect( er, QBrush(textColor) );
01174 break;
01175 case KoParagCounter::STYLE_BOXBULLET:
01176 p->drawRect( er );
01177 break;
01178 case KoParagCounter::STYLE_CIRCLEBULLET:
01179 p->drawEllipse( er );
01180 break;
01181 case KoParagCounter::STYLE_CUSTOMBULLET:
01182 {
01183
01184
01185 if ( !m_layout.counter->customBulletFont().isEmpty() )
01186 {
01187 QFont bulletFont( p->font() );
01188 bulletFont.setFamily( m_layout.counter->customBulletFont() );
01189 p->setFont( bulletFont );
01190 }
01191 KoTextParag::drawFontEffects( p, format, zh, format->screenFont( zh ), textColor, xBullet, base, width, y, height, ' ' );
01192
01193 posY = y + base- format->offsetFromBaseLine();
01194
01195
01196 int sy = format->shadowY( zh );
01197 if ( sy < 0)
01198 posY -= sy;
01199
01200 p->drawText( xBullet, posY, m_layout.counter->customBulletCharacter() );
01201 break;
01202 }
01203 default:
01204 break;
01205 }
01206
01207 QString suffix = m_layout.counter->suffix();
01208 if ( !suffix.isEmpty() )
01209 {
01210 if ( !rtl )
01211 suffix += ' ' ;
01212
01213 KoTextParag::drawFontEffects( p, format, zh, format->screenFont( zh ), textColor, xBullet + width, base, counterWidth, y,height, suffix[0] );
01214
01215 int posY =y + base- format->offsetFromBaseLine();
01216
01217
01218 int sy = format->shadowY( zh );
01219 if ( sy < 0)
01220 posY -= sy;
01221
01222 p->drawText( xBullet + width, posY, suffix, -1 );
01223 }
01224 }
01225 else
01226 {
01227 QString counterText = m_layout.counter->text( this );
01228
01229
01230 if ( !counterText.isEmpty() )
01231 {
01232 KoTextParag::drawFontEffects( p, format, zh, format->screenFont( zh ), textColor, xLeft, base, counterWidth, y, height, counterText[0] );
01233
01234 counterText += ' ' ;
01235
01236 int posY =y + base - format->offsetFromBaseLine();
01237
01238
01239 int sy = format->shadowY( zh );
01240 if ( sy < 0)
01241 posY -= sy;
01242
01243 p->drawText( xLeft, posY , counterText, -1 );
01244 }
01245 }
01246 p->restore();
01247 }
01248
01249 int KoTextParag::breakableTopMargin() const
01250 {
01251 KoTextZoomHandler * zh = textDocument()->formattingZoomHandler();
01252 return zh->ptToLayoutUnitPixY(
01253 m_layout.margins[ Q3StyleSheetItem::MarginTop ] );
01254 }
01255
01256 int KoTextParag::topMargin() const
01257 {
01258 KoTextZoomHandler * zh = textDocument()->formattingZoomHandler();
01259 return zh->ptToLayoutUnitPixY(
01260 m_layout.margins[ Q3StyleSheetItem::MarginTop ]
01261 + ( ( prev() && prev()->joinBorder() && prev()->bottomBorder() == m_layout.bottomBorder &&
01262 prev()->topBorder() == m_layout.topBorder && prev()->leftBorder() == m_layout.leftBorder &&
01263 prev()->rightBorder() == m_layout.rightBorder) ? 0 : m_layout.topBorder.width() ) );
01264 }
01265
01266 int KoTextParag::bottomMargin() const
01267 {
01268 KoTextZoomHandler * zh = textDocument()->formattingZoomHandler();
01269 return zh->ptToLayoutUnitPixY(
01270 m_layout.margins[ Q3StyleSheetItem::MarginBottom ]
01271 + ( ( joinBorder() && next() && next()->bottomBorder() == m_layout.bottomBorder &&
01272 next()->topBorder() == m_layout.topBorder && next()->leftBorder() == m_layout.leftBorder &&
01273 next()->rightBorder() == m_layout.rightBorder) ? 0 : m_layout.bottomBorder.width() ) );
01274 }
01275
01276 int KoTextParag::leftMargin() const
01277 {
01278 KoTextZoomHandler * zh = textDocument()->formattingZoomHandler();
01279 return zh->ptToLayoutUnitPixX(
01280 m_layout.margins[ Q3StyleSheetItem::MarginLeft ]
01281 + m_layout.leftBorder.width() );
01282 }
01283
01284 int KoTextParag::rightMargin() const
01285 {
01286 KoTextZoomHandler * zh = textDocument()->formattingZoomHandler();
01287 int cw=0;
01288 if( m_layout.counter && str->isRightToLeft() &&
01289 (( m_layout.counter->alignment() == Qt::AlignRight ) || ( m_layout.counter->alignment() == Qt::AlignLeft )))
01290 cw = counterWidth();
01291
01292 return zh->ptToLayoutUnitPixX(
01293 m_layout.margins[ Q3StyleSheetItem::MarginRight ]
01294 + m_layout.rightBorder.width() )
01295 + cw;
01296 }
01297
01298 int KoTextParag::firstLineMargin() const
01299 {
01300 KoTextZoomHandler * zh = textDocument()->formattingZoomHandler();
01301 return zh->ptToLayoutUnitPixY(
01302 m_layout.margins[ Q3StyleSheetItem::MarginFirstLine ] );
01303 }
01304
01305 int KoTextParag::lineSpacing( int line ) const
01306 {
01307 Q_ASSERT( isValid() );
01308 if ( m_layout.lineSpacingType == KoParagLayout::LS_SINGLE )
01309 return 0;
01310 else {
01311 if( line >= (int)lineStarts.count() )
01312 {
01313 kError() << "KoTextParag::lineSpacing assert(line<lines) failed: line=" << line << " lines=" << lineStarts.count() << endl;
01314 return 0;
01315 }
01316 QMap<int, KoTextParagLineStart*>::ConstIterator it = lineStarts.begin();
01317 while ( line-- > 0 )
01318 ++it;
01319 return (*it)->lineSpacing;
01320 }
01321 }
01322
01323
01324 int KoTextParag::calculateLineSpacing( int line, int startChar, int lastChar ) const
01325 {
01326 KoTextZoomHandler * zh = textDocument()->formattingZoomHandler();
01327
01328 int shadow = 0;
01329 if ( m_layout.lineSpacingType == KoParagLayout::LS_SINGLE )
01330 return shadow;
01331 else if ( m_layout.lineSpacingType == KoParagLayout::LS_CUSTOM )
01332 return zh->ptToLayoutUnitPixY( m_layout.lineSpacingValue() ) + shadow;
01333 else {
01334 if( line >= (int)lineStarts.count() )
01335 {
01336 kError() << "KoTextParag::lineSpacing assert(line<lines) failed: line=" << line << " lines=" << lineStarts.count() << endl;
01337 return 0+shadow;
01338 }
01339 QMap<int, KoTextParagLineStart*>::ConstIterator it = lineStarts.begin();
01340 while ( line-- > 0 )
01341 ++it;
01342
01343
01344 switch ( m_layout.lineSpacingType )
01345 {
01346 case KoParagLayout::LS_MULTIPLE:
01347 {
01348 double n = m_layout.lineSpacingValue() - 1.0;
01349 return shadow + qRound( n * heightForLineSpacing( startChar, lastChar ) );
01350 }
01351 case KoParagLayout::LS_ONEANDHALF:
01352 {
01353
01354 return shadow + heightForLineSpacing( startChar, lastChar ) / 2;
01355 }
01356 case KoParagLayout::LS_DOUBLE:
01357 {
01358
01359 return shadow + heightForLineSpacing( startChar, lastChar );
01360 }
01361 case KoParagLayout::LS_AT_LEAST:
01362 {
01363 int atLeast = zh->ptToLayoutUnitPixY( m_layout.lineSpacingValue() );
01364 const int lineHeight = ( *it )->h;
01365 int h = qMax( lineHeight, atLeast );
01366
01367 return shadow + h - lineHeight;
01368 }
01369 case KoParagLayout::LS_FIXED:
01370 {
01371 const int lineHeight = ( *it )->h;
01372 return shadow + zh->ptToLayoutUnitPixY( m_layout.lineSpacingValue() ) - lineHeight;
01373 }
01374
01375 case KoParagLayout::LS_SINGLE:
01376 case KoParagLayout::LS_CUSTOM:
01377 break;
01378 }
01379 }
01380 kWarning() << "Unhandled linespacing type : " << m_layout.lineSpacingType << endl;
01381 return 0+shadow;
01382 }
01383
01384 QRect KoTextParag::pixelRect( KoTextZoomHandler *zh ) const
01385 {
01386 QRect rct( zh->layoutUnitToPixel( rect() ) );
01387
01388
01389
01390
01391 if ( prev() )
01392 {
01393 QRect prevRect( zh->layoutUnitToPixel( prev()->rect() ) );
01394 if ( rct.top() < prevRect.bottom() + 1 )
01395 {
01396
01397 rct.setTop( prevRect.bottom() + 1 );
01398 }
01399 }
01400 return rct;
01401 }
01402
01403
01404
01405 void KoTextParag::paint( QPainter &painter, const QColorGroup &cg, KoTextCursor *cursor, bool drawSelections,
01406 int clipx, int clipy, int clipw, int cliph )
01407 {
01408 #ifdef DEBUG_PAINT
01409 kDebug(32500) << "KoTextParag::paint ===== id=" << paragId() << " clipx=" << clipx << " clipy=" << clipy << " clipw=" << clipw << " cliph=" << cliph << endl;
01410 kDebug(32500) << " clipw in pix (approx) : " << textDocument()->paintingZoomHandler()->layoutUnitToPixelX( clipw ) << " cliph in pix (approx) : " << textDocument()->paintingZoomHandler()->layoutUnitToPixelX( cliph ) << endl;
01411 #endif
01412
01413 KoTextZoomHandler * zh = textDocument()->paintingZoomHandler();
01414 assert(zh);
01415
01416 QRect paraRect = pixelRect( zh );
01417
01418
01419 int leftMarginPix = zh->layoutUnitToPixelX( leftMargin() );
01420 int firstLineOffset = zh->layoutUnitToPixelX( firstLineMargin() );
01421
01422
01423
01424 int leftExtent = qMin ( leftMarginPix, leftMarginPix + firstLineOffset );
01425 int rightExtent = paraRect.width() - zh->layoutUnitToPixelX( rightMargin() );
01426
01427
01428 if ( backgroundColor().isValid() )
01429 {
01430
01431
01432 int backgroundWidth = rightExtent - leftExtent + 1;
01433 int backgroundHeight = pixelRect( zh ).height();
01434 painter.fillRect( leftExtent, 0,
01435 backgroundWidth, backgroundHeight,
01436 backgroundColor() );
01437 }
01438
01439
01440 if ( m_layout.counter && m_layout.counter->numbering() != KoParagCounter::NUM_NONE && m_lineChanged <= 0 )
01441 {
01442 int cy, h, baseLine;
01443 lineInfo( 0, cy, h, baseLine );
01444 int xLabel = at(0)->x;
01445 if ( str->isRightToLeft() )
01446 xLabel += at(0)->width;
01447 drawLabel( &painter, xLabel, cy, 0, 0, baseLine, cg );
01448 }
01449
01450 paintLines( painter, cg, cursor, drawSelections, clipx, clipy, clipw, cliph );
01451
01452
01453 if ( m_layout.hasBorder() )
01454 {
01455 bool const drawTopBorder = !prev() || !prev()->joinBorder() || prev()->bottomBorder() != bottomBorder() || prev()->topBorder() != topBorder() || prev()->leftBorder() != leftBorder() || prev()->rightBorder() != rightBorder();
01456 bool const drawBottomBorder = !joinBorder() || !next() || next()->bottomBorder() != bottomBorder() || next()->topBorder() != topBorder() || next()->leftBorder() != leftBorder() || next()->rightBorder() != rightBorder();
01457
01458
01459
01460
01461
01462
01463
01464
01465
01466 QRect r;
01467 r.setLeft( leftExtent );
01468 r.setRight( rightExtent );
01469 r.setTop( zh->layoutUnitToPixelY(lineY( 0 )) );
01470
01471 int lastLine = lines() - 1;
01472
01473
01474 int paragBottom = pixelRect(zh).height()-1;
01475
01476
01477 if ( m_layout.bottomBorder.width() > 0 && drawBottomBorder)
01478 paragBottom -= zh->layoutUnitToPixelY( lineSpacing( lastLine ) );
01479 paragBottom -= KoBorder::zoomWidthY( m_layout.bottomBorder.width(), zh, 0 );
01480
01481
01482 r.setBottom( paragBottom );
01483
01484
01485 KoBorder::drawBorders( painter, zh, r,
01486 m_layout.leftBorder, m_layout.rightBorder, m_layout.topBorder, m_layout.bottomBorder,
01487 0, QPen(), drawTopBorder, drawBottomBorder );
01488 }
01489 }
01490
01491
01492 void KoTextParag::paintLines( QPainter &painter, const QColorGroup &cg, KoTextCursor *cursor, bool drawSelections,
01493 int clipx, int clipy, int clipw, int cliph )
01494 {
01495 if ( !visible )
01496 return;
01497
01498
01499
01500
01501
01502 #define CHECK_PIXELXADJ
01503
01504 int curx = -1, cury = 0, curh = 0, curline = 0;
01505 int xstart, xend = 0;
01506
01507 QString qstr = str->toString();
01508 qstr.replace( QChar(0x00a0U), ' ' );
01509
01510 const int nSels = doc ? doc->numSelections() : 1;
01511 Q3MemArray<int> selectionStarts( nSels );
01512 Q3MemArray<int> selectionEnds( nSels );
01513 if ( drawSelections ) {
01514 bool hasASelection = false;
01515 for ( int i = 0; i < nSels; ++i ) {
01516 if ( !hasSelection( i ) ) {
01517 selectionStarts[ i ] = -1;
01518 selectionEnds[ i ] = -1;
01519 } else {
01520 hasASelection = true;
01521 selectionStarts[ i ] = selectionStart( i );
01522 int end = selectionEnd( i );
01523 if ( end == length() - 1 && n && n->hasSelection( i ) )
01524 end++;
01525 selectionEnds[ i ] = end;
01526 }
01527 }
01528 if ( !hasASelection )
01529 drawSelections = false;
01530 }
01531
01532
01533 int line = m_lineChanged;
01534 if (line<0) line = 0;
01535
01536 int numLines = lines();
01537 #ifdef DEBUG_PAINT
01538 kDebug(32500) << " paintLines: from line " << line << " to " << numLines-1 << endl;
01539 #endif
01540 for( ; line<numLines ; line++ )
01541 {
01542
01543 int nextLine;
01544 int startOfLine;
01545 lineStartOfLine(line, &startOfLine);
01546 if (line == numLines-1 )
01547 nextLine = length();
01548 else
01549 lineStartOfLine(line+1, &nextLine);
01550
01551
01552 int cy, h, baseLine;
01553 lineInfo( line, cy, h, baseLine );
01554 if ( clipy != -1 && cy > clipy - r.y() + cliph )
01555 break;
01556
01557
01558 int paintStart = startOfLine;
01559 KoTextStringChar* chr = at(startOfLine);
01560 KoTextStringChar* nextchr = chr;
01561
01562
01563 for(int i=startOfLine;i<nextLine;i++)
01564 {
01565 chr = nextchr;
01566 if ( i < nextLine-1 )
01567 nextchr = at( i+1 );
01568
01569
01570 bool flush = ( i == nextLine - 1 );
01571
01572
01573
01574 flush = flush || ( nextchr->format() != chr->format() );
01575
01576
01577
01578 if ( !flush && chr->format()->attributeFont() == KoTextFormat::ATT_SMALL_CAPS )
01579 {
01580 bool isLowercase = chr->c.upper() != chr->c;
01581 bool nextLowercase = nextchr->c.upper() != nextchr->c;
01582 flush = isLowercase != nextLowercase;
01583 }
01584
01585 flush = flush || nextchr->startOfRun;
01586
01587 flush = flush || ( nextchr->rightToLeft != chr->rightToLeft );
01588 #ifdef CHECK_PIXELXADJ
01589
01590
01591 flush = flush || ( nextchr->pixelxadj != chr->pixelxadj && nextchr->charStop );
01592 #endif
01593
01594 flush = flush || ( chr->c == '\t' || nextchr->c == '\t' );
01595
01596 flush = flush || ( chr->c.unicode() == 0xad );
01597
01598 flush = flush || chr->isCustom();
01599
01600 flush = flush || nextchr->isCustom();
01601
01602 if ((alignment() & Qt::AlignJustify) == Qt::AlignJustify )
01603
01604 flush = flush || chr->whiteSpace;
01605
01606 if (!flush && chr->format()->wordByWord() && chr->format()->isStrikedOrUnderlined())
01607 flush = flush || chr->whiteSpace || nextchr->whiteSpace;
01608
01609 flush = flush || ( i - paintStart >= 256 );
01610
01611 if ( drawSelections ) {
01612
01613 bool selectionChange = false;
01614 if ( drawSelections ) {
01615 for ( int j = 0; j < nSels; ++j ) {
01616 selectionChange = selectionStarts[ j ] == i+1 || selectionEnds[ j ] == i+1;
01617 if ( selectionChange )
01618 break;
01619 }
01620 }
01621 flush = flush || selectionChange;
01622 }
01623
01624
01625 if ( cursor && this == cursor->parag() && i == cursor->index() ) {
01626 curx = cursor->x();
01627 curline = line;
01628 KoTextStringChar *c = chr;
01629 if ( i > 0 )
01630 --c;
01631 curh = c->height();
01632 cury = cy + baseLine - c->ascent();
01633 }
01634
01635 if ( flush ) {
01636
01637 KoTextStringChar* cStart = at( paintStart );
01638 if ( chr->rightToLeft ) {
01639 xstart = chr->x;
01640 xend = cStart->x + cStart->width;
01641 } else {
01642 xstart = cStart->x;
01643 if ( i < length() - 1 && !str->at( i + 1 ).lineStart &&
01644 str->at( i + 1 ).rightToLeft == chr->rightToLeft )
01645 xend = str->at( i + 1 ).x;
01646 else
01647 xend = chr->x + chr->width;
01648 }
01649
01650 if ( (clipx == -1 || clipw == -1) || (xend >= clipx && xstart <= clipx + clipw) ) {
01651 if ( !chr->isCustom() ) {
01652 drawParagString( painter, qstr, paintStart, i - paintStart + 1, xstart, cy,
01653 baseLine, xend-xstart, h, drawSelections,
01654 chr->format(), selectionStarts, selectionEnds,
01655 cg, chr->rightToLeft, line );
01656 }
01657 else
01658 if ( chr->customItem()->placement() == KoTextCustomItem::PlaceInline ) {
01659 chr->customItem()->draw( &painter, chr->x, cy + baseLine - chr->customItem()->ascent(),
01660 clipx - r.x(), clipy - r.y(), clipw, cliph, cg,
01661 drawSelections && nSels && selectionStarts[ 0 ] <= i && selectionEnds[ 0 ] > i );
01662 }
01663 }
01664 paintStart = i+1;
01665 }
01666 }
01667 }
01668
01669
01670 if ( curx != -1 && cursor ) {
01671 drawCursor( painter, cursor, curx, cury, curh, cg );
01672 }
01673 }
01674
01675
01676
01677
01678 void KoTextParag::drawParagString( QPainter &painter, const QString &str, int start, int len, int startX,
01679 int lastY, int baseLine, int bw, int h, bool drawSelections,
01680 KoTextFormat *format, const Q3MemArray<int> &selectionStarts,
01681 const Q3MemArray<int> &selectionEnds, const QColorGroup &cg, bool rightToLeft, int line )
01682 {
01683 KoTextZoomHandler * zh = textDocument()->paintingZoomHandler();
01684 assert(zh);
01685
01686 #ifdef DEBUG_PAINT
01687 kDebug(32500) << "KoTextParag::drawParagString drawing from " << start << " to " << start+len << endl;
01688 kDebug(32500) << " startX in LU: " << startX << " lastY in LU:" << lastY
01689 << " baseLine in LU:" << baseLine << endl;
01690 #endif
01691
01692
01693
01694
01695 int shadowOffsetX_pix = zh->layoutUnitToPixelX( format->offsetX() );
01696 int shadowOffsetY_pix = zh->layoutUnitToPixelY( format->offsetY() );
01697
01698
01699 int startX_pix = zh->layoutUnitToPixelX( startX ) ;
01700 #ifdef DEBUG_PAINT
01701 kDebug(32500) << "KoTextParag::drawParagString startX in pixels : " << startX_pix << " bw=" << bw << endl;
01702 #endif
01703
01704 int bw_pix = zh->layoutUnitToPixelX( startX, bw );
01705 int lastY_pix = zh->layoutUnitToPixelY( lastY );
01706 int baseLine_pix = zh->layoutUnitToPixelY( lastY, baseLine );
01707 int h_pix = zh->layoutUnitToPixelY( lastY, h );
01708 #ifdef DEBUG_PAINT
01709 kDebug(32500) << "KoTextParag::drawParagString h(LU)=" << h << " lastY(LU)=" << lastY
01710 << " h(PIX)=" << h_pix << " lastY(PIX)=" << lastY_pix
01711 << " baseLine(PIX)=" << baseLine_pix << endl;
01712 #endif
01713
01714 if ( format->textBackgroundColor().isValid() )
01715 painter.fillRect( startX_pix, lastY_pix, bw_pix, h_pix, format->textBackgroundColor() );
01716
01717
01718 int draw_len = len;
01719 int draw_startX = startX;
01720 int draw_bw = bw_pix;
01721 if ( at( start + len - 1 )->c == '\n' )
01722 {
01723 draw_len--;
01724 draw_bw -= at( start + len - 1 )->pixelwidth;
01725 if ( rightToLeft && draw_len > 0 )
01726 draw_startX = at( start + draw_len - 1 )->x;
01727 }
01728
01729
01730
01731 if ( drawSelections ) {
01732 bool inSelection = false;
01733 const int nSels = doc ? doc->numSelections() : 1;
01734 for ( int j = 0; j < nSels; ++j ) {
01735 if ( start >= selectionStarts[ j ] && start < selectionEnds[ j ] ) {
01736 inSelection = true;
01737 switch (j) {
01738 case KoTextDocument::Standard:
01739 painter.fillRect( startX_pix, lastY_pix, bw_pix, h_pix, cg.color( QColorGroup::Highlight ) );
01740 break;
01741 case KoTextDocument::InputMethodPreedit:
01742
01743 break;
01744 default:
01745 painter.fillRect( startX_pix, lastY_pix, bw_pix, h_pix, doc ? doc->selectionColor( j ) : cg.color( QColorGroup::Highlight ) );
01746 break;
01747 }
01748 }
01749 }
01750 if ( !inSelection )
01751 drawSelections = false;
01752 }
01753
01754
01755 const int nSels = doc ? doc->numSelections() : 1;
01756 if ( KoTextDocument::InputMethodPreedit < nSels
01757 && doc->hasSelection( KoTextDocument::InputMethodPreedit )
01758 && start >= selectionStarts[ KoTextDocument::InputMethodPreedit ]
01759 && start < selectionEnds[ KoTextDocument::InputMethodPreedit ] )
01760 {
01761 QColor textColor( format->color() );
01762 painter.setPen( QPen( textColor ) );
01763
01764 QPoint p1( startX_pix, lastY_pix + h_pix - 1 );
01765 QPoint p2( startX_pix + bw_pix, lastY_pix + h_pix - 1 );
01766 painter.drawLine( p1, p2 );
01767 }
01768
01769 if ( draw_len > 0 )
01770 {
01771 int draw_startX_pix = zh->layoutUnitToPixelX( draw_startX ) ;
01772 draw_startX_pix += shadowOffsetX_pix;
01773 lastY_pix += shadowOffsetY_pix;
01774
01775 if ( format->shadowDistanceX() != 0 || format->shadowDistanceY() != 0 ) {
01776 int sx = format->shadowX( zh );
01777 int sy = format->shadowY( zh );
01778 if ( sx != 0 || sy != 0 )
01779 {
01780 painter.save();
01781 painter.translate( sx, sy );
01782 drawParagStringInternal( painter, str, start, draw_len, draw_startX_pix,
01783 lastY_pix, baseLine_pix,
01784 draw_bw,
01785 h_pix, false ,
01786 format, selectionStarts,
01787 selectionEnds, cg, rightToLeft, line, zh, true );
01788 painter.restore();
01789 }
01790 }
01791
01792 drawParagStringInternal( painter, str, start, draw_len, draw_startX_pix,
01793 lastY_pix, baseLine_pix,
01794 draw_bw,
01795 h_pix, drawSelections, format, selectionStarts,
01796 selectionEnds, cg, rightToLeft, line, zh, false );
01797 }
01798
01799 bool forPrint = ( painter.device()->devType() == QInternal::Printer );
01800 if ( textDocument()->drawFormattingChars() && !forPrint )
01801 {
01802 drawFormattingChars( painter, start, len,
01803 lastY_pix, baseLine_pix, h_pix,
01804 drawSelections,
01805 format, selectionStarts,
01806 selectionEnds, cg, rightToLeft,
01807 line, zh, AllFormattingChars );
01808 }
01809 }
01810
01811
01812
01813
01814
01815 void KoTextParag::drawParagStringInternal( QPainter &painter, const QString &s, int start, int len, int startX,
01816 int lastY, int baseLine, int bw, int h, bool drawSelections,
01817 KoTextFormat *format, const Q3MemArray<int> &selectionStarts,
01818 const Q3MemArray<int> &selectionEnds, const QColorGroup &cg, bool rightToLeft, int line, KoTextZoomHandler* zh, bool drawingShadow )
01819 {
01820 #ifdef DEBUG_PAINT
01821 kDebug(32500) << "KoTextParag::drawParagStringInternal start=" << start << " len=" << len << " : '" << s.mid(start,len) << "'" << endl;
01822 kDebug(32500) << "In pixels: startX=" << startX << " lastY=" << lastY << " baseLine=" << baseLine
01823 << " bw=" << bw << " h=" << h << " rightToLeft=" << rightToLeft << endl;
01824 #endif
01825 if ( drawingShadow && format->shadowDistanceX() == 0 && format->shadowDistanceY() == 0 )
01826 return;
01827
01828 QColor textColor( drawingShadow ? format->shadowColor() : format->color() );
01829 if ( !textColor.isValid() )
01830 textColor = KoTextFormat::defaultTextColor( &painter );
01831
01832
01833 QFont font( format->screenFont( zh ) );
01834 if ( format->attributeFont() == KoTextFormat::ATT_SMALL_CAPS && s[start].upper() != s[start] )
01835 font = format->smallCapsFont( zh, true );
01836
01837 #if 0
01838 QFontInfo fi( font );
01839 kDebug(32500) << "KoTextParag::drawParagStringInternal requested font " << font.pointSizeF() << " using font " << fi.pointSize() << "pt (format font: " << format->font().pointSizeFloat() << "pt)" << endl;
01840 QFontMetrics fm( font );
01841 kDebug(32500) << "Real font: " << fi.family() << ". Font height in pixels: " << fm.height() << endl;
01842 #endif
01843
01844
01845 QString str( s );
01846 if ( str[ (int)str.length() - 1 ].unicode() == 0xad )
01847 str.remove( str.length() - 1, 1 );
01848 painter.setPen( QPen( textColor ) );
01849 painter.setFont( font );
01850
01851 KoTextDocument* doc = document();
01852
01853 if ( drawSelections ) {
01854 const int nSels = doc ? doc->numSelections() : 1;
01855 for ( int j = 0; j < nSels; ++j ) {
01856 if ( start >= selectionStarts[ j ] && start < selectionEnds[ j ] ) {
01857 if ( doc->invertSelectionText( j ) )
01858 textColor = cg.color( QColorGroup::HighlightedText );
01859 painter.setPen( QPen( textColor ) );
01860 break;
01861 }
01862 }
01863 }
01864
01865 Qt::LayoutDirection dir = rightToLeft ? Qt::RightToLeft : Qt::LeftToRight;
01866
01867 if ( dir != Qt::RightToLeft && start + len == length() )
01868 {
01869 len--;
01870 if ( len <= 0 )
01871 return;
01872 bw-=at(length()-1)->pixelwidth;
01873 }
01874 KoTextParag::drawFontEffects( &painter, format, zh, font, textColor, startX, baseLine, bw, lastY, h, str[start] );
01875
01876 if ( str[ start ] != '\t' && str[ start ].unicode() != 0xad ) {
01877 str = format->displayedString( str );
01878 if ( format->vAlign() == KoTextFormat::AlignNormal ) {
01879 int posY = lastY + baseLine;
01880
01881
01882 int sy = format->shadowY( zh );
01883 if ( sy < 0)
01884 posY -= sy;
01885
01886 painter.drawText( startX, posY, str, start, len );
01887 #ifdef BIDI_DEBUG
01888 painter.save();
01889 painter.setPen ( Qt::red );
01890 painter.drawLine( startX, lastY, startX, lastY + baseLine );
01891 painter.drawLine( startX, lastY + baseLine/2, startX + 10, lastY + baseLine/2 );
01892 int w = 0;
01893 int i = 0;
01894 while( i < len )
01895 w += painter.fontMetrics().charWidth( str, start + i++ );
01896 painter.setPen ( Qt::blue );
01897 painter.drawLine( startX + w - 1, lastY, startX + w - 1, lastY + baseLine );
01898 painter.drawLine( startX + w - 1, lastY + baseLine/2, startX + w - 1 - 10, lastY + baseLine/2 );
01899 painter.restore();
01900 #endif
01901 } else if ( format->vAlign() == KoTextFormat::AlignSuperScript ) {
01902 int posY =lastY + baseLine - ( painter.fontMetrics().height() / 2 );
01903
01904
01905 int sy = format->shadowY( zh );
01906 if ( sy < 0)
01907 posY -= sy;
01908 painter.drawText( startX, posY, str, start, len );
01909 } else if ( format->vAlign() == KoTextFormat::AlignSubScript ) {
01910 int posY =lastY + baseLine + ( painter.fontMetrics().height() / 6 );
01911
01912
01913 int sy = format->shadowY( zh );
01914 if ( sy < 0)
01915 posY -= sy;
01916 painter.drawText( startX, posY, str, start, len );
01917 } else if ( format->vAlign() == KoTextFormat::AlignCustom ) {
01918 int posY = lastY + baseLine - format->offsetFromBaseLine();
01919
01920
01921 int sy = format->shadowY( zh );
01922 if ( sy < 0)
01923 posY -= sy;
01924 painter.drawText( startX, posY, str, start, len );
01925 }
01926 }
01927 if ( str[ start ] == '\t' && m_tabCache.contains( start ) ) {
01928 painter.save();
01929 KoTextZoomHandler * zh = textDocument()->paintingZoomHandler();
01930 const KoTabulator& tab = m_layout.tabList()[ m_tabCache[ start ] ];
01931 int lineWidth = zh->zoomItYOld( tab.ptWidth );
01932 switch ( tab.filling ) {
01933 case TF_DOTS:
01934 painter.setPen( QPen( textColor, lineWidth, Qt::DotLine ) );
01935 painter.drawLine( startX, lastY + baseLine, startX + bw, lastY + baseLine );
01936 break;
01937 case TF_LINE:
01938 painter.setPen( QPen( textColor, lineWidth, Qt::SolidLine ) );
01939 painter.drawLine( startX, lastY + baseLine, startX + bw, lastY + baseLine );
01940 break;
01941 case TF_DASH:
01942 painter.setPen( QPen( textColor, lineWidth, Qt::DashLine ) );
01943 painter.drawLine( startX, lastY + baseLine, startX + bw, lastY + baseLine );
01944 break;
01945 case TF_DASH_DOT:
01946 painter.setPen( QPen( textColor, lineWidth, Qt::DashDotLine ) );
01947 painter.drawLine( startX, lastY + baseLine, startX + bw, lastY + baseLine );
01948 break;
01949 case TF_DASH_DOT_DOT:
01950 painter.setPen( QPen( textColor, lineWidth, Qt::DashDotDotLine ) );
01951 painter.drawLine( startX, lastY + baseLine, startX + bw, lastY + baseLine );
01952 break;
01953
01954 default:
01955 break;
01956 }
01957 painter.restore();
01958 }
01959
01960 if ( start+len < length() && at( start+len )->lineStart )
01961 {
01962 #ifdef DEBUG_PAINT
01963
01964 #endif
01965 bool drawHyphen = at( start+len-1 )->c.unicode() == 0xad;
01966 drawHyphen = drawHyphen || lineHyphenated( line );
01967 if ( drawHyphen ) {
01968 #ifdef DEBUG_PAINT
01969 kDebug(32500) << "drawing hyphen at x=" << startX+bw << endl;
01970 #endif
01971 painter.drawText( startX + bw, lastY + baseLine, "-" );
01972 }
01973 }
01974
01975
01976 if(
01977 painter.device()->devType() != QInternal::Printer &&
01978 format->isMisspelled() &&
01979 !drawingShadow &&
01980 textDocument()->drawingMissingSpellLine() )
01981 {
01982 painter.save();
01983 painter.setPen( QPen( Qt::red, 1 ) );
01984
01985
01986 for( int zigzag_line = 0; zigzag_line < 3; ++zigzag_line )
01987 {
01988 for( int zigzag_x = zigzag_line; zigzag_x < bw; zigzag_x += 4 )
01989 {
01990 painter.drawPoint(
01991 startX + zigzag_x,
01992 lastY + baseLine + h/12 - 1 + zigzag_line );
01993 }
01994 }
01995
01996
01997 for( int zigzag_x = 3; zigzag_x < bw; zigzag_x += 4 )
01998 {
01999 painter.drawPoint(
02000 startX + zigzag_x,
02001 lastY + baseLine + h/12 );
02002 }
02003
02004 painter.restore();
02005 }
02006 }
02007
02008 bool KoTextParag::lineHyphenated( int l ) const
02009 {
02010 if ( l > (int)lineStarts.count() - 1 ) {
02011 kWarning() << "KoTextParag::lineHyphenated: line " << l << " out of range!" << endl;
02012 return false;
02013 }
02014
02015 if ( !isValid() )
02016 const_cast<KoTextParag*>(this)->format();
02017
02018 QMap<int, KoTextParagLineStart*>::ConstIterator it = lineStarts.begin();
02019 while ( l-- > 0 )
02020 ++it;
02021 return ( *it )->hyphenated;
02022 }
02023
02025 void KoTextParag::drawCursor( QPainter &painter, KoTextCursor *cursor, int curx, int cury, int curh, const QColorGroup &cg )
02026 {
02027 KoTextZoomHandler * zh = textDocument()->paintingZoomHandler();
02028 int x = zh->layoutUnitToPixelX( curx ) ;
02029
02030 KoTextParag::drawCursorDefault( painter, cursor, x,
02031 zh->layoutUnitToPixelY( cury ),
02032 zh->layoutUnitToPixelY( cury, curh ), cg );
02033 }
02034
02035
02036 void KoTextParag::copyParagData( KoTextParag *parag )
02037 {
02038
02039 KoParagStyle * style = parag->style();
02040
02041 bool styleApplied = false;
02042 if ( style )
02043 {
02044 KoParagStyle * newStyle = style->followingStyle();
02045 if ( newStyle && style != newStyle )
02046 {
02047 setParagLayout( newStyle->paragLayout() );
02048 KoTextFormat * format = &newStyle->format();
02049 setFormat( format );
02050 format->addRef();
02051 str->setFormat( 0, format, true );
02052 styleApplied = true;
02053 }
02054 }
02055
02056
02057
02058
02059
02060 if (!styleApplied)
02061 {
02062 setParagLayout( parag->paragLayout() );
02063
02064 parag->m_layout.pageBreaking &= ~KoParagLayout::HardFrameBreakBefore;
02065 parag->m_layout.pageBreaking &= ~KoParagLayout::HardFrameBreakAfter;
02066
02067 if ( m_layout.counter && m_layout.counter->numbering() == KoParagCounter::NUM_FOOTNOTE )
02068 setNoCounter();
02069
02070 if ( m_layout.counter )
02071 m_layout.counter->setRestartCounter(false);
02072
02073
02074 setFormat( parag->at( parag->length()-1 )->format() );
02075
02076
02077 }
02078
02079
02080
02081
02082
02083 }
02084
02085 void KoTextParag::setTabList( const KoTabulatorList &tabList )
02086 {
02087 KoTabulatorList lst( tabList );
02088 m_layout.setTabList( lst );
02089 if ( !tabList.isEmpty() )
02090 {
02091 KoTextZoomHandler* zh = textDocument()->formattingZoomHandler();
02092 int * tabs = new int[ tabList.count() + 1 ];
02093 KoTabulatorList::Iterator it = lst.begin();
02094 unsigned int i = 0;
02095 for ( ; it != lst.end() ; ++it, ++i )
02096 tabs[i] = zh->ptToLayoutUnitPixX( (*it).ptPos );
02097 tabs[i] = 0;
02098 assert( i == tabList.count() );
02099 setTabArray( tabs );
02100 } else
02101 {
02102 setTabArray( 0 );
02103 }
02104 invalidate( 0 );
02105 }
02106
02108 int KoTextParag::nextTab( int chnum, int x, int availableWidth )
02109 {
02110 if ( !m_layout.tabList().isEmpty() )
02111 {
02112
02113
02114 int * tArray = tabArray();
02115 int i = 0;
02116 if ( str->isRightToLeft() )
02117 i = m_layout.tabList().size() - 1;
02118 KoTextZoomHandler* zh = textDocument()->formattingZoomHandler();
02119
02120 while ( i >= 0 && i < (int)m_layout.tabList().size() ) {
02121
02122 int tab = tArray[ i ];
02123
02124
02125
02126
02127 if ( tab > availableWidth ) {
02128
02129 tab = availableWidth;
02130 }
02131
02132 if ( str->isRightToLeft() )
02133 tab = availableWidth - tab;
02134
02135 if ( tab > x ) {
02136 int type = m_layout.tabList()[i].type;
02137
02138
02139 if ( str->isRightToLeft() )
02140 if ( type == T_RIGHT )
02141 type = T_LEFT;
02142 else if ( type == T_LEFT )
02143 type = T_RIGHT;
02144
02145 switch ( type ) {
02146 case T_RIGHT:
02147 case T_CENTER:
02148 {
02149
02150 int c = chnum + 1;
02151 int w = 0;
02152 while ( c < str->length() - 1 && str->at( c ).c != '\t' && str->at( c ).c != '\n' )
02153 {
02154 KoTextStringChar & ch = str->at( c );
02155
02156
02157 if ( ch.isCustom() )
02158 w += ch.customItem()->width;
02159 else
02160 {
02161 KoTextFormat *charFormat = ch.format();
02162 int ww = charFormat->charWidth( zh, false, &ch, this, c );
02163 ww = KoTextZoomHandler::ptToLayoutUnitPt( ww );
02164 w += ww;
02165 }
02166 ++c;
02167 }
02168
02169 m_tabCache[chnum] = i;
02170
02171 if ( type == T_RIGHT )
02172 return tab - w;
02173 else
02174 return tab - w/2;
02175 }
02176 case T_DEC_PNT:
02177 {
02178
02179
02180 int c = chnum + 1;
02181 int w = 0;
02182 while ( c < str->length()-1 && str->at( c ).c != '\t' && str->at( c ).c != '\n' )
02183 {
02184 KoTextStringChar & ch = str->at( c );
02185 if ( ch.c == m_layout.tabList()[i].alignChar )
02186 {
02187
02188 int ww = ch.format()->charWidth( zh, false, &ch, this, c );
02189 ww = KoTextZoomHandler::ptToLayoutUnitPt( ww );
02190 if ( str->isRightToLeft() )
02191 {
02192 w = ww / 2;
02193 ++c;
02194 continue;
02195 }
02196 else
02197 {
02198 w += ww / 2;
02199 break;
02200 }
02201 }
02202
02203
02204 if ( ch.isCustom() )
02205 w += ch.customItem()->width;
02206 else
02207 {
02208 int ww = ch.format()->charWidth( zh, false, &ch, this, c );
02209 w += KoTextZoomHandler::ptToLayoutUnitPt( ww );
02210 }
02211
02212 ++c;
02213 }
02214 m_tabCache[chnum] = i;
02215 return tab - w;
02216 }
02217 default:
02218 m_tabCache[chnum] = i;
02219 return tab;
02220 }
02221 }
02222 if ( str->isRightToLeft() )
02223 --i;
02224 else
02225 ++i;
02226 }
02227 }
02228
02229 return KoTextParag::nextTabDefault( chnum, x );
02230 }
02231
02232 void KoTextParag::applyStyle( KoParagStyle *style )
02233 {
02234 setParagLayout( style->paragLayout() );
02235 KoTextFormat *newFormat = &style->format();
02236 setFormat( 0, str->length(), newFormat );
02237 setFormat( newFormat );
02238 }
02239
02240 void KoTextParag::setParagLayout( const KoParagLayout & layout, int flags, int marginIndex )
02241 {
02242
02243 if ( flags & KoParagLayout::Alignment )
02244 setAlign( layout.alignment );
02245 if ( flags & KoParagLayout::Margins ) {
02246 if ( marginIndex == -1 )
02247 setMargins( layout.margins );
02248 else
02249 setMargin( (Q3StyleSheetItem::Margin)marginIndex, layout.margins[marginIndex] );
02250 }
02251 if ( flags & KoParagLayout::LineSpacing )
02252 {
02253 setLineSpacingType( layout.lineSpacingType );
02254 setLineSpacing( layout.lineSpacingValue() );
02255 }
02256 if ( flags & KoParagLayout::Borders )
02257 {
02258 setLeftBorder( layout.leftBorder );
02259 setRightBorder( layout.rightBorder );
02260 setTopBorder( layout.topBorder );
02261 setBottomBorder( layout.bottomBorder );
02262 setJoinBorder( layout.joinBorder );
02263 }
02264 if ( flags & KoParagLayout::BackgroundColor )
02265 {
02266 setBackgroundColor( layout.backgroundColor );
02267 }
02268 if ( flags & KoParagLayout::BulletNumber )
02269 setCounter( layout.counter );
02270 if ( flags & KoParagLayout::Tabulator )
02271 setTabList( layout.tabList() );
02272 if ( flags == KoParagLayout::All )
02273 {
02274 setDirection( static_cast<QChar::Direction>(layout.direction) );
02275
02276 setStyle( layout.style );
02277 }
02278 }
02279
02280 void KoTextParag::setCustomItem( int index, KoTextCustomItem * custom, KoTextFormat * currentFormat )
02281 {
02282
02283
02284 if ( currentFormat )
02285 setFormat( index, 1, currentFormat );
02286 at( index )->setCustomItem( custom );
02287
02288 document()->registerCustomItem( custom, this );
02289 custom->recalc();
02290 invalidate( 0 );
02291 setChanged( true );
02292 }
02293
02294 void KoTextParag::removeCustomItem( int index )
02295 {
02296 Q_ASSERT( at( index )->isCustom() );
02297 KoTextCustomItem * item = at( index )->customItem();
02298 at( index )->loseCustomItem();
02299
02300 document()->unregisterCustomItem( item, this );
02301 }
02302
02303
02304 int KoTextParag::findCustomItem( const KoTextCustomItem * custom ) const
02305 {
02306 int len = str->length();
02307 for ( int i = 0; i < len; ++i )
02308 {
02309 KoTextStringChar & ch = str->at(i);
02310 if ( ch.isCustom() && ch.customItem() == custom )
02311 return i;
02312 }
02313 kWarning() << "KoTextParag::findCustomItem custom item " << (void*)custom
02314 << " not found in paragraph " << paragId() << endl;
02315 return 0;
02316 }
02317
02318 #ifndef NDEBUG
02319 void KoTextParag::printRTDebug( int info )
02320 {
02321 QString specialFlags;
02322 if ( str->needsSpellCheck() )
02323 specialFlags += " needsSpellCheck=true";
02324 if ( wasMovedDown() )
02325 specialFlags += " wasMovedDown=true";
02326 if ( partOfTableOfContents() )
02327 specialFlags += " part-of-TOC=true";
02328 kDebug(32500) << "Paragraph " << this << " (" << paragId() << ") [changed="
02329 << hasChanged() << ", valid=" << isValid()
02330 << specialFlags
02331 << "] ------------------ " << endl;
02332 if ( prev() && prev()->paragId() + 1 != paragId() )
02333 kWarning() << " Previous paragraph " << prev() << " has ID " << prev()->paragId() << endl;
02334 if ( next() && next()->paragId() != paragId() + 1 )
02335 kWarning() << " Next paragraph " << next() << " has ID " << next()->paragId() << endl;
02336
02337
02338 kDebug(32500) << " Style: " << style() << " " << ( style() ? style()->name().toLocal8Bit().data() : "NO STYLE" ) << endl;
02339 kDebug(32500) << " Text: '" << str->toString() << "'" << endl;
02340 if ( info == 0 )
02341 {
02342 if ( m_layout.counter )
02343 {
02344 m_layout.counter->printRTDebug( this );
02345 }
02346 static const char * const s_align[] = { "Auto", "Left", "Right", "ERROR", "HCenter", "ERR", "ERR", "ERR", "Justify", };
02347 static const char * const s_linespacing[] = { "Single", "1.5", "2", "custom", "atLeast", "Multiple", "Fixed" };
02348 static const char * const s_dir[] = { "DirL", "DirR", "DirEN", "DirES", "DirET", "DirAN", "DirCS", "DirB", "DirS", "DirWS", "DirON", "DirLRE", "DirLRO", "DirAL", "DirRLE", "DirRLO", "DirPDF", "DirNSM", "DirBN" };
02349 kDebug(32500) << " align: " << s_align[alignment()] << " resolveAlignment: " << s_align[resolveAlignment()]
02350 << " isRTL:" << str->isRightToLeft()
02351 << " dir: " << s_dir[direction()] << endl;
02352 QRect pixr = pixelRect( textDocument()->paintingZoomHandler() );
02353 kDebug(32500) << " rect() : " << DEBUGRECT( rect() )
02354 << " pixelRect() : " << DEBUGRECT( pixr ) << endl;
02355 kDebug(32500) << " topMargin()=" << topMargin() << " bottomMargin()=" << bottomMargin()
02356 << " leftMargin()=" << leftMargin() << " firstLineMargin()=" << firstLineMargin()
02357 << " rightMargin()=" << rightMargin() << endl;
02358 if ( kwLineSpacingType() != KoParagLayout::LS_SINGLE )
02359 kDebug(32500) << " linespacing type=" << s_linespacing[ -kwLineSpacingType() ]
02360 << " value=" << kwLineSpacing() << endl;
02361 const int pageBreaking = m_layout.pageBreaking;
02362 QStringList pageBreakingFlags;
02363 if ( pageBreaking & KoParagLayout::KeepLinesTogether )
02364 pageBreakingFlags.append( "KeepLinesTogether" );
02365 if ( pageBreaking & KoParagLayout::HardFrameBreakBefore )
02366 pageBreakingFlags.append( "HardFrameBreakBefore" );
02367 if ( pageBreaking & KoParagLayout::HardFrameBreakAfter )
02368 pageBreakingFlags.append( "HardFrameBreakAfter" );
02369 if ( pageBreaking & KoParagLayout::KeepWithPrevious )
02370 pageBreakingFlags.append( "KeepWithPrevious" );
02371 if ( pageBreaking & KoParagLayout::KeepWithNext )
02372 pageBreakingFlags.append( "KeepWithNext" );
02373 if ( !pageBreakingFlags.isEmpty() )
02374 kDebug(32500) << " page Breaking: " << pageBreakingFlags.join(",") << endl;
02375
02376 static const char * const tabtype[] = { "T_LEFT", "T_CENTER", "T_RIGHT", "T_DEC_PNT", "error!!!" };
02377 KoTabulatorList tabList = m_layout.tabList();
02378 if ( tabList.isEmpty() ) {
02379 if ( str->toString().find( '\t' ) != -1 )
02380 kDebug(32500) << "Tab width: " << textDocument()->tabStopWidth() << endl;
02381 } else {
02382 KoTabulatorList::Iterator it = tabList.begin();
02383 for ( ; it != tabList.end() ; it++ )
02384 kDebug(32500) << "Tab type:" << tabtype[(*it).type] << " at: " << (*it).ptPos << endl;
02385 }
02386 } else if ( info == 1 )
02387 {
02388 kDebug(32500) << " Paragraph format=" << paragFormat() << " " << paragFormat()->key()
02389 << " fontsize:" << dynamic_cast<KoTextFormat *>(paragFormat())->pointSize() << endl;
02390
02391 for ( int line = 0 ; line < lines(); ++ line ) {
02392 int y, h, baseLine;
02393 lineInfo( line, y, h, baseLine );
02394 int startOfLine;
02395 lineStartOfLine( line, &startOfLine );
02396 kDebug(32500) << " Line " << line << " y=" << y << " height=" << h << " baseLine=" << baseLine << " startOfLine(index)=" << startOfLine << endl;
02397 }
02398 kDebug(32500) << endl;
02399 KoTextString * s = string();
02400 int lastX = 0;
02401 int lastW = 0;
02402 for ( int i = 0 ; i < s->length() ; ++i )
02403 {
02404 KoTextStringChar & ch = s->at(i);
02405 int pixelx = textDocument()->formattingZoomHandler()->layoutUnitToPixelX( ch.x )
02406 + ch.pixelxadj;
02407 if ( ch.lineStart )
02408 kDebug(32500) << "LINESTART" << endl;
02409 QString attrs = " ";
02410 if ( ch.whiteSpace )
02411 attrs += "whitespace ";
02412 if ( !ch.charStop )
02413 attrs += "notCharStop ";
02414 if ( ch.wordStop )
02415 attrs += "wordStop ";
02416 attrs.truncate( attrs.length() - 1 );
02417
02418 kDebug(32500) << i << ": '" << QString(ch.c).rightJustified(2)
02419 << "' (" << QString::number( ch.c.unicode() ).rightJustified(3) << ")"
02420 << " x(LU)=" << ch.x
02421 << " w(LU)=" << ch.width
02422 << " x(PIX)=" << pixelx
02423 << " (xadj=" << + ch.pixelxadj << ")"
02424 << " w(PIX)=" << ch.pixelwidth
02425 << " height=" << ch.height()
02426 << attrs
02427
02428
02429
02430 << endl;
02431
02432
02433 if ( ch.format() != textDocument()->formatCollection()->defaultFormat() )
02434 Q_ASSERT( textDocument()->formatCollection()->dict()[ch.format()->key()] );
02435
02436 if ( !str->isBidi() && !ch.lineStart )
02437 Q_ASSERT( lastX + lastW == pixelx );
02438 lastX = pixelx;
02439 lastW = ch.pixelwidth;
02440 if ( ch.isCustom() )
02441 {
02442 KoTextCustomItem * item = ch.customItem();
02443 kDebug(32500) << " - custom item " << item
02444 << " ownline=" << item->ownLine()
02445 << " size=" << item->width << "x" << item->height
02446 << " ascent=" << item->ascent()
02447 << endl;
02448 }
02449 }
02450 }
02451 }
02452 #endif
02453
02454 void KoTextParag::drawFontEffects( QPainter * p, KoTextFormat *format, KoTextZoomHandler *zh, QFont font, const QColor & color, int startX, int baseLine, int bw, int lastY, int , QChar firstChar )
02455 {
02456
02457
02458 if ( !format->isStrikedOrUnderlined() )
02459 return;
02460
02461
02462
02463 if ( format->wordByWord() && firstChar.isSpace() )
02464 return;
02465
02466 double dimd;
02467 int y;
02468 int offset = 0;
02469 if (format->vAlign() == KoTextFormat::AlignSubScript )
02470 offset = p->fontMetrics().height() / 6;
02471 else if (format->vAlign() == KoTextFormat::AlignSuperScript )
02472 offset = -p->fontMetrics().height() / 2;
02473
02474 dimd = KoBorder::zoomWidthY( format->underLineWidth(), zh, 1 );
02475 if((format->vAlign() == KoTextFormat::AlignSuperScript) ||
02476 (format->vAlign() == KoTextFormat::AlignSubScript ) || (format->vAlign() == KoTextFormat::AlignCustom ))
02477 dimd*=format->relativeTextSize();
02478 y = lastY + baseLine + offset - ( (format->vAlign() == KoTextFormat::AlignCustom)?format->offsetFromBaseLine():0 );
02479
02480 if ( format->doubleUnderline())
02481 {
02482 QColor col = format->textUnderlineColor().isValid() ? format->textUnderlineColor(): color ;
02483 int dim=static_cast<int>(0.75*dimd);
02484 dim=dim?dim:1;
02485 p->save();
02486
02487 switch( format->underlineStyle())
02488 {
02489 case KoTextFormat::U_SOLID:
02490 p->setPen( QPen( col, dim, Qt::SolidLine ) );
02491 break;
02492 case KoTextFormat::U_DASH:
02493 p->setPen( QPen( col, dim, Qt::DashLine ) );
02494 break;
02495 case KoTextFormat::U_DOT:
02496 p->setPen( QPen( col, dim, Qt::DotLine ) );
02497 break;
02498 case KoTextFormat::U_DASH_DOT:
02499 p->setPen( QPen( col, dim, Qt::DashDotLine ) );
02500 break;
02501 case KoTextFormat::U_DASH_DOT_DOT:
02502 p->setPen( QPen( col, dim, Qt::DashDotDotLine ) );
02503 break;
02504 default:
02505 p->setPen( QPen( color, dim, Qt::SolidLine ) );
02506 }
02507
02508 y += static_cast<int>(1.125*dimd);
02509 p->drawLine( startX, y, startX + bw, y );
02510 y += static_cast<int>(1.5*dimd);
02511 p->drawLine( startX, y, startX + bw, y );
02512 p->restore();
02513 if ( font.underline() ) {
02514 font.setUnderline( false );
02515 p->setFont( font );
02516 }
02517 }
02518 else if ( format->underline() ||
02519 format->underlineType() == KoTextFormat::U_SIMPLE_BOLD)
02520 {
02521
02522 QColor col = format->textUnderlineColor().isValid() ? format->textUnderlineColor(): color ;
02523 p->save();
02524 int dim=(format->underlineType() == KoTextFormat::U_SIMPLE_BOLD)?static_cast<int>(2*dimd):static_cast<int>(dimd);
02525 dim=dim?dim:1;
02526 y += static_cast<int>(1.875*dimd);
02527
02528 switch( format->underlineStyle() )
02529 {
02530 case KoTextFormat::U_SOLID:
02531 p->setPen( QPen( col, dim, Qt::SolidLine ) );
02532 break;
02533 case KoTextFormat::U_DASH:
02534 p->setPen( QPen( col, dim, Qt::DashLine ) );
02535 break;
02536 case KoTextFormat::U_DOT:
02537 p->setPen( QPen( col, dim, Qt::DotLine ) );
02538 break;
02539 case KoTextFormat::U_DASH_DOT:
02540 p->setPen( QPen( col, dim, Qt::DashDotLine ) );
02541 break;
02542 case KoTextFormat::U_DASH_DOT_DOT:
02543 p->setPen( QPen( col, dim, Qt::DashDotDotLine ) );
02544 break;
02545 default:
02546 p->setPen( QPen( col, dim, Qt::SolidLine ) );
02547 }
02548
02549 p->drawLine( startX, y, startX + bw, y );
02550 p->restore();
02551 font.setUnderline( false );
02552 p->setFont( font );
02553 }
02554 else if ( format->waveUnderline() )
02555 {
02556 int dim=static_cast<int>(dimd);
02557 dim=dim?dim:1;
02558 y += dim;
02559 QColor col = format->textUnderlineColor().isValid() ? format->textUnderlineColor(): color ;
02560 p->save();
02561 int offset = 2 * dim;
02562 QPen pen(col, dim, Qt::SolidLine);
02563 pen.setCapStyle(Qt::RoundCap);
02564 p->setPen(pen);
02565 Q_ASSERT(offset);
02566 double anc=acos(1.0-2*(static_cast<double>(offset-(startX)%offset)/static_cast<double>(offset)))/3.1415*180;
02567 int pos=1;
02568
02569 if(2*((startX/offset)/2)==startX/offset)
02570 pos*=-1;
02571
02572 p->drawArc( (startX/offset)*offset, y, offset, offset, 0, -qRound(pos*anc*16) );
02573
02574 int zigzag_x = (startX/offset+1)*offset;
02575 for ( ; zigzag_x + offset <= bw+startX; zigzag_x += offset)
02576 {
02577 p->drawArc( zigzag_x, y, offset, offset, 0, pos*180*16 );
02578 pos*=-1;
02579 }
02580
02581 anc=acos(1.0-2*(static_cast<double>((startX+bw)%offset)/static_cast<double>(offset)))/3.1415*180;
02582 p->drawArc( zigzag_x, y, offset, offset, 180*16, -qRound(pos*anc*16) );
02583 p->restore();
02584 font.setUnderline( false );
02585 p->setFont( font );
02586 }
02587
02588 dimd = KoBorder::zoomWidthY( static_cast<double>(format->pointSize())/18.0, zh, 1 );
02589 if((format->vAlign() == KoTextFormat::AlignSuperScript) ||
02590 (format->vAlign() == KoTextFormat::AlignSubScript ) || (format->vAlign() == KoTextFormat::AlignCustom ))
02591 dimd*=format->relativeTextSize();
02592 y = lastY + baseLine + offset - ( (format->vAlign() == KoTextFormat::AlignCustom)?format->offsetFromBaseLine():0 );
02593
02594 if ( format->strikeOutType() == KoTextFormat::S_SIMPLE
02595 || format->strikeOutType() == KoTextFormat::S_SIMPLE_BOLD)
02596 {
02597 unsigned int dim = (format->strikeOutType() == KoTextFormat::S_SIMPLE_BOLD)? static_cast<int>(2*dimd) : static_cast<int>(dimd);
02598 p->save();
02599
02600 switch( format->strikeOutStyle() )
02601 {
02602 case KoTextFormat::S_SOLID:
02603 p->setPen( QPen( color, dim, Qt::SolidLine ) );
02604 break;
02605 case KoTextFormat::S_DASH:
02606 p->setPen( QPen( color, dim, Qt::DashLine ) );
02607 break;
02608 case KoTextFormat::S_DOT:
02609 p->setPen( QPen( color, dim, Qt::DotLine ) );
02610 break;
02611 case KoTextFormat::S_DASH_DOT:
02612 p->setPen( QPen( color, dim, Qt::DashDotLine ) );
02613 break;
02614 case KoTextFormat::S_DASH_DOT_DOT:
02615 p->setPen( QPen( color, dim, Qt::DashDotDotLine ) );
02616 break;
02617 default:
02618 p->setPen( QPen( color, dim, Qt::SolidLine ) );
02619 }
02620
02621 y -= static_cast<int>(5*dimd);
02622 p->drawLine( startX, y, startX + bw, y );
02623 p->restore();
02624 font.setStrikeOut( false );
02625 p->setFont( font );
02626 }
02627 else if ( format->strikeOutType() == KoTextFormat::S_DOUBLE )
02628 {
02629 unsigned int dim = static_cast<int>(dimd);
02630 p->save();
02631
02632 switch( format->strikeOutStyle() )
02633 {
02634 case KoTextFormat::S_SOLID:
02635 p->setPen( QPen( color, dim, Qt::SolidLine ) );
02636 break;
02637 case KoTextFormat::S_DASH:
02638 p->setPen( QPen( color, dim, Qt::DashLine ) );
02639 break;
02640 case KoTextFormat::S_DOT:
02641 p->setPen( QPen( color, dim, Qt::DotLine ) );
02642 break;
02643 case KoTextFormat::S_DASH_DOT:
02644 p->setPen( QPen( color, dim, Qt::DashDotLine ) );
02645 break;
02646 case KoTextFormat::S_DASH_DOT_DOT:
02647 p->setPen( QPen( color, dim, Qt::DashDotDotLine ) );
02648 break;
02649 default:
02650 p->setPen( QPen( color, dim, Qt::SolidLine ) );
02651 }
02652
02653 y -= static_cast<int>(4*dimd);
02654 p->drawLine( startX, y, startX + bw, y);
02655 y -= static_cast<int>(2*dimd);
02656 p->drawLine( startX, y, startX + bw, y);
02657 p->restore();
02658 font.setStrikeOut( false );
02659 p->setFont( font );
02660 }
02661
02662 }
02663
02664
02665 QString KoTextParag::toString( int from, int length ) const
02666 {
02667 QString str;
02668 if ( from == 0 && m_layout.counter && m_layout.counter->numbering() != KoParagCounter::NUM_FOOTNOTE )
02669 str += m_layout.counter->text( this ) + ' ';
02670 if ( length == -1 )
02671 length = this->length() - 1 - from;
02672 for ( int i = from ; i < (length+from) ; ++i )
02673 {
02674 KoTextStringChar *ch = at( i );
02675 if ( ch->isCustom() )
02676 {
02677 KoVariable * var = dynamic_cast<KoVariable *>(ch->customItem());
02678 if ( var )
02679 str += var->text(true);
02680 else
02681 str +=' ';
02682 }
02683 else
02684 str += ch->c;
02685 }
02686 return str;
02687 }
02688
02689 void KoTextParag::loadOasisSpan( const QDomElement& parent, KoOasisContext& context, uint& pos )
02690 {
02691
02692
02693 QDomNode node;
02694 for ( node = parent.firstChild(); !node.isNull(); node = node.nextSibling() )
02695 {
02696 QDomElement ts = node.toElement();
02697 QString textData;
02698 const QString localName( ts.localName() );
02699 const bool isTextNS = ts.namespaceURI() == KoXmlNS::text;
02700 KoTextCustomItem* customItem = 0;
02701
02702
02703 context.styleStack().save();
02704
02705
02706 if ( node.isText() )
02707 {
02708 textData = node.toText().data();
02709 }
02710 else if ( isTextNS && localName == "span" )
02711 {
02712 context.styleStack().save();
02713 context.fillStyleStack( ts, KoXmlNS::text, "style-name", "text" );
02714 loadOasisSpan( ts, context, pos );
02715 context.styleStack().restore();
02716 }
02717 else if ( isTextNS && localName == "s" )
02718 {
02719 int howmany = 1;
02720 if (ts.hasAttributeNS( KoXmlNS::text, "c"))
02721 howmany = ts.attributeNS( KoXmlNS::text, "c", QString::null).toInt();
02722
02723 textData.fill(32, howmany);
02724 }
02725 else if ( isTextNS && localName == "tab" )
02726 {
02727 textData = '\t';
02728 }
02729 else if ( isTextNS && localName == "line-break" )
02730 {
02731 textData = '\n';
02732 }
02733 else if ( isTextNS && localName == "number" )
02734 {
02735
02736
02737 }
02738 else if ( node.isProcessingInstruction() )
02739 {
02740 QDomProcessingInstruction pi = node.toProcessingInstruction();
02741 if ( pi.target() == "opendocument" && pi.data().startsWith( "cursor-position" ) )
02742 {
02743 context.setCursorPosition( this, pos );
02744 }
02745 }
02746 else
02747 {
02748 bool handled = false;
02749
02750 KoVariable* var = context.variableCollection().loadOasisField( textDocument(), ts, context );
02751 if ( var )
02752 {
02753 textData = "#";
02754 customItem = var;
02755 handled = true;
02756 }
02757 if ( !handled )
02758 {
02759 handled = textDocument()->loadSpanTag( ts, context,
02760 this, pos,
02761 textData, customItem );
02762 if ( !handled )
02763 {
02764 kWarning(32500) << "Ignoring tag " << ts.tagName() << endl;
02765 context.styleStack().restore();
02766 continue;
02767 }
02768 }
02769 }
02770
02771 const uint length = textData.length();
02772 if ( length )
02773 {
02774 insert( pos, textData );
02775 if ( customItem )
02776 setCustomItem( pos, customItem, 0 );
02777 KoTextFormat f;
02778 f.load( context );
02779
02780 setFormat( pos, length, document()->formatCollection()->format( &f ), true );
02781 pos += length;
02782 }
02783 context.styleStack().restore();
02784 }
02785 }
02786
02787 KoParagLayout KoTextParag::loadParagLayout( KoOasisContext& context, KoStyleCollection *styleCollection, bool findStyle )
02788 {
02789 KoParagLayout layout;
02790
02791
02792 if ( findStyle )
02793 {
02794 KoParagStyle *style;
02795
02796
02797 QString styleName = context.styleStack().userStyleName( "paragraph" );
02798 if ( !styleName.isEmpty() )
02799 {
02800 style = styleCollection->findStyle( styleName );
02801
02802 if (!style)
02803 style = styleCollection->findStyleByDisplayName( context.styleStack().userStyleDisplayName( "paragraph" ) );
02804 if (!style)
02805 {
02806 kError(32500) << "Cannot find style \"" << styleName << "\" - using Standard" << endl;
02807 style = styleCollection->findStyle( "Standard" );
02808 }
02809
02810 }
02811 else
02812 {
02813 kError(32500) << "No style name !? - using Standard" << endl;
02814 style = styleCollection->findStyle( "Standard" );
02815 }
02816 Q_ASSERT(style);
02817 layout.style = style;
02818 }
02819
02820 KoParagLayout::loadOasisParagLayout( layout, context );
02821
02822 return layout;
02823 }
02824
02825 void KoTextParag::loadOasis( const QDomElement& parent, KoOasisContext& context, KoStyleCollection *styleCollection, uint& pos )
02826 {
02827
02828 KoParagLayout paragLayout = loadParagLayout( context, styleCollection, true );
02829 setParagLayout( paragLayout );
02830
02831
02832 KoTextFormat defaultFormat;
02833 defaultFormat.load( context );
02834 setFormat( document()->formatCollection()->format( &defaultFormat ) );
02835
02836
02837 loadOasisSpan( parent, context, pos );
02838
02839
02840 const int len = str->length();
02841 Q_ASSERT( len >= 1 );
02842 setFormat( len - 1, 1, paragFormat(), true );
02843
02844 setChanged( true );
02845 invalidate( 0 );
02846 }
02847
02848 void KoTextParag::saveOasis( KoXmlWriter& writer, KoSavingContext& context,
02849 int from , int to ,
02850 bool ) const
02851 {
02852 KoGenStyles& mainStyles = context.mainStyles();
02853
02854
02855 QString parentStyleName;
02856 if ( m_layout.style )
02857 parentStyleName = m_layout.style->name();
02858
02859 KoGenStyle autoStyle( KoGenStyle::STYLE_AUTO, "paragraph", parentStyleName );
02860 paragFormat()->save( autoStyle, context );
02861 m_layout.saveOasis( autoStyle, context, false );
02862
02863
02864 if ( !prev() ) {
02865 if ( context.variableSettings() )
02866 autoStyle.addProperty( "style:page-number", context.variableSettings()->startingPageNumber() );
02867
02868 autoStyle.addAttribute( "style:master-page-name", "Standard" );
02869 }
02870
02871
02872 QString autoParagStyleName = mainStyles.lookup( autoStyle, "P", KoGenStyles::ForceNumbering );
02873
02874 KoParagCounter* paragCounter = m_layout.counter;
02875
02876 bool outline = m_layout.style && m_layout.style->isOutline() && paragCounter;
02877 bool normalList = paragCounter && paragCounter->style() != KoParagCounter::STYLE_NONE && !outline;
02878 if ( normalList )
02879 {
02880 writer.startElement( "text:numbered-paragraph" );
02881 writer.addAttribute( "text:level", (int)paragCounter->depth() + 1 );
02882 if ( paragCounter->restartCounter() )
02883 writer.addAttribute( "text:start-value", paragCounter->startNumber() );
02884
02885 KoGenStyle listStyle( KoGenStyle::STYLE_AUTO_LIST );
02886 paragCounter->saveOasis( listStyle );
02887
02888 QString autoListStyleName = mainStyles.lookup( listStyle, "L", KoGenStyles::ForceNumbering );
02889 writer.addAttribute( "text:style-name", autoListStyleName );
02890
02891 QString textNumber = m_layout.counter->text( this );
02892 if ( !textNumber.isEmpty() )
02893 {
02894
02895 writer.startElement( "text:number" );
02896 writer.addTextNode( textNumber );
02897 writer.endElement();
02898 }
02899 }
02900 else if ( outline )
02901 {
02902 writer.startElement( "text:h", false );
02903 writer.addAttribute( "text:style-name", autoParagStyleName );
02904 writer.addAttribute( "text:outline-level", (int)paragCounter->depth() + 1 );
02905 if ( paragCounter->numbering() == KoParagCounter::NUM_NONE )
02906 writer.addAttribute( "text:is-list-header", "true" );
02907
02908 QString textNumber = paragCounter->text( this );
02909 if ( !textNumber.isEmpty() )
02910 {
02911
02912 writer.startElement( "text:number" );
02913 writer.addTextNode( textNumber );
02914 writer.endElement();
02915 }
02916 }
02917
02918 if ( !outline )
02919 {
02920 writer.startElement( "text:p", false );
02921 writer.addAttribute( "text:style-name", autoParagStyleName );
02922 }
02923
02924 QString text = str->toString();
02925 Q_ASSERT( text.right(1)[0] == ' ' );
02926
02927 const int cursorIndex = context.cursorTextParagraph() == this ? context.cursorTextIndex() : -1;
02928
02929
02930
02931
02932 #define WRITESPAN( next ) { \
02933 if ( curFormat == paragFormat() ) { \
02934 writer.addTextSpan( text.mid( startPos, next - startPos ), m_tabCache ); \
02935 } else { \
02936 KoGenStyle gs( KoGenStyle::STYLE_AUTO, "text" ); \
02937 curFormat->save( gs, context, paragFormat() ); \
02938 writer.startElement( "text:span" ); \
02939 if ( !gs.isEmpty() ) { \
02940 const QString autoStyleName = mainStyles.lookup( gs, "T" ); \
02941 writer.addAttribute( "text:style-name", autoStyleName ); \
02942 } \
02943 writer.addTextSpan( text.mid( startPos, next - startPos ), m_tabCache ); \
02944 writer.endElement(); \
02945 } \
02946 }
02947 #define ISSTARTBOOKMARK( i ) bkStartIter != bookmarkStarts.end() && (*bkStartIter).pos == i
02948 #define ISENDBOOKMARK( i ) bkEndIter != bookmarkEnds.end() && (*bkEndIter).pos == i
02949 #define CHECKPOS( i ) \
02950 if ( cursorIndex == i ) { \
02951 writer.addProcessingInstruction( "opendocument cursor-position" ); \
02952 } \
02953 if ( ISSTARTBOOKMARK( i ) ) { \
02954 if ( (*bkStartIter).startEqualsEnd ) \
02955 writer.startElement( "text:bookmark" ); \
02956 else \
02957 writer.startElement( "text:bookmark-start" ); \
02958 writer.addAttribute( "text:name", (*bkStartIter).name ); \
02959 writer.endElement(); \
02960 ++bkStartIter; \
02961 } \
02962 if ( ISENDBOOKMARK( i ) ) { \
02963 writer.startElement( "text:bookmark-end" ); \
02964 writer.addAttribute( "text:name", (*bkEndIter).name ); \
02965 writer.endElement(); \
02966 ++bkEndIter; \
02967 }
02968
02969
02970
02971
02972
02973 typedef KoSavingContext::BookmarkPositions BookmarkPositions;
02974 BookmarkPositions bookmarkStarts = context.bookmarkStarts();
02975 BookmarkPositions::const_iterator bkStartIter = bookmarkStarts.begin();
02976 while ( bkStartIter != bookmarkStarts.end() && (*bkStartIter).pos < from )
02977 ++bkStartIter;
02978
02979 BookmarkPositions bookmarkEnds = context.bookmarkEnds();
02980 BookmarkPositions::const_iterator bkEndIter = bookmarkEnds.begin();
02981 while ( bkEndIter != bookmarkEnds.end() && (*bkEndIter).pos < from )
02982 ++bkEndIter;
02983
02984 KoTextFormat *curFormat = 0;
02985 KoTextFormat *lastFormatRaw = 0;
02986 KoTextFormat *lastFormatFixed = 0;
02987 int startPos = from;
02988 for ( int i = from; i <= to; ++i ) {
02989 KoTextStringChar & ch = str->at(i);
02990 KoTextFormat * newFormat = static_cast<KoTextFormat *>( ch.format() );
02991 if ( newFormat->isMisspelled() ) {
02992 if ( newFormat == lastFormatRaw )
02993 newFormat = lastFormatFixed;
02994 else
02995 {
02996 lastFormatRaw = newFormat;
02997
02998
02999 KoTextFormat tmpFormat( *newFormat );
03000 tmpFormat.setMisspelled( false );
03001 newFormat = formatCollection()->format( &tmpFormat );
03002 lastFormatFixed = newFormat;
03003 }
03004 }
03005 if ( !curFormat )
03006 curFormat = newFormat;
03007 if ( newFormat != curFormat
03008 || ch.isCustom() || cursorIndex == i || ISSTARTBOOKMARK( i ) || ISENDBOOKMARK( i ) )
03009 {
03010 WRITESPAN( i )
03011 startPos = i;
03012 curFormat = newFormat;
03013 }
03014 CHECKPOS( i )
03015 if ( ch.isCustom() ) {
03016 KoGenStyle gs( KoGenStyle::STYLE_AUTO, "text" );
03017 curFormat->save( gs, context, paragFormat() );
03018 writer.startElement( "text:span" );
03019 if ( !gs.isEmpty() ) {
03020 const QString autoStyleName = mainStyles.lookup( gs, "T" );
03021 writer.addAttribute( "text:style-name", autoStyleName );
03022 }
03023 KoTextCustomItem* customItem = ch.customItem();
03024 customItem->saveOasis( writer, context );
03025 writer.endElement();
03026 startPos = i + 1;
03027 }
03028 }
03029
03030
03031
03032 if ( to >= startPos ) {
03033 WRITESPAN( to + 1 )
03034 }
03035 CHECKPOS( to + 1 )
03036
03037 writer.endElement();
03038 if ( normalList )
03039 writer.endElement();
03040 }
03041
03042 void KoTextParag::applyListStyle( KoOasisContext& context, int restartNumbering, bool orderedList, bool heading, int level )
03043 {
03044
03045 delete m_layout.counter;
03046 m_layout.counter = new KoParagCounter;
03047 m_layout.counter->loadOasis( context, restartNumbering, orderedList, heading, level );
03048
03049 const QDomElement listStyleProperties = context.listStyleStack().currentListStyleProperties();
03050 if ( listStyleProperties.hasAttributeNS( KoXmlNS::text, "space-before" ) )
03051 {
03052 double spaceBefore = KoUnit::parseValue( listStyleProperties.attributeNS( KoXmlNS::text, "space-before", QString::null ) );
03053 m_layout.margins[ Q3StyleSheetItem::MarginLeft ] += spaceBefore;
03054 }
03055
03056 }
03057
03058 int KoTextParag::documentWidth() const
03059 {
03060 return doc ? doc->width() : 0;
03061 }
03062
03063
03064
03065
03066
03067
03068 int KoTextParag::documentX() const
03069 {
03070 return doc ? doc->x() : 0;
03071 }
03072
03073 int KoTextParag::documentY() const
03074 {
03075 return doc ? doc->y() : 0;
03076 }
03077
03078 void KoTextParag::fixParagWidth( bool viewFormattingChars )
03079 {
03080
03081 if ( viewFormattingChars && lineStartList().count() == 1 )
03082 {
03083 KoTextFormat * lastFormat = at( length() - 1 )->format();
03084 setWidth( qMin( rect().width() + lastFormat->width('x'), doc->width() ) );
03085 }
03086
03087 }
03088
03089
03090 void KoTextParag::drawFormattingChars( QPainter &painter, int start, int len,
03091 int lastY_pix, int baseLine_pix, int h_pix,
03092 bool ,
03093 KoTextFormat * , const Q3MemArray<int> &,
03094 const Q3MemArray<int> &, const QColorGroup & ,
03095 bool rightToLeft, int , KoTextZoomHandler* zh,
03096 int whichFormattingChars )
03097 {
03098 if ( !whichFormattingChars )
03099 return;
03100 painter.save();
03101
03102 QPen pen( KGlobalSettings::linkColor() );
03103 painter.setPen( pen );
03104
03105 if ( start + len == length() && ( whichFormattingChars & FormattingEndParag ) )
03106 {
03107
03108 KoTextStringChar &ch = str->at( length() - 1 );
03109 KoTextFormat* format = static_cast<KoTextFormat *>( ch.format() );
03110 int w = format->charWidth( zh, true, &ch, this, 'X' );
03111 int size = qMin( w, h_pix * 3 / 4 );
03112
03113
03114 int x;
03115 if ( rightToLeft )
03116 x = zh->layoutUnitToPixelX( ch.x ) + ch.pixelwidth - 1;
03117 else
03118 x = zh->layoutUnitToPixelX( ch.x ) + w;
03119 int y = lastY_pix + baseLine_pix;
03120
03121 painter.drawLine( (int)(x - size * 0.2), y - size, (int)(x - size * 0.2), y );
03122 painter.drawLine( (int)(x - size * 0.5), y - size, (int)(x - size * 0.5), y );
03123 painter.drawLine( x, y, (int)(x - size * 0.7), y );
03124 painter.drawLine( x, y - size, (int)(x - size * 0.5), y - size);
03125 painter.drawArc( x - size, y - size, size, (int)(size / 2), -90*16, -180*16 );
03126 #ifdef DEBUG_FORMATTING
03127 painter.setPen( Qt::blue );
03128 painter.drawRect( zh->layoutUnitToPixelX( ch.x ) - 1, lastY_pix, ch.pixelwidth, baseLine_pix );
03129 QPen pen( cg.color( QColorGroup::Highlight ) );
03130 painter.setPen( pen );
03131 #endif
03132 }
03133
03134
03135 if ( (whichFormattingChars & FormattingSpace) ||
03136 (whichFormattingChars & FormattingTabs) ||
03137 (whichFormattingChars & FormattingBreak) )
03138 {
03139 int end = qMin( start + len, length() - 1 );
03140 for ( int i = start ; i < end ; ++i )
03141 {
03142 KoTextStringChar &ch = str->at(i);
03143 #ifdef DEBUG_FORMATTING
03144 painter.setPen( (i % 2)? Qt::red: Qt::green );
03145 painter.drawRect( zh->layoutUnitToPixelX( ch.x ) - 1, lastY_pix, ch.pixelwidth, baseLine_pix );
03146 QPen pen( cg.color( QColorGroup::Highlight ) );
03147 painter.setPen( pen );
03148 #endif
03149 if ( ch.isCustom() )
03150 continue;
03151 if ( (ch.c == ' ' || ch.c.unicode() == 0x00a0U)
03152 && (whichFormattingChars & FormattingSpace))
03153 {
03154
03155
03156 int w = zh->layoutUnitToPixelX( ch.format()->width( ' ' ) );
03157 int height = zh->layoutUnitToPixelY( ch.ascent() );
03158 int size = qMax( 2, qMin( w/2, height/3 ) );
03159 int x = zh->layoutUnitToPixelX( ch.x );
03160 QRect spcRect( x + (ch.pixelwidth - size) / 2, lastY_pix + baseLine_pix - (height - size) / 2, size, size );
03161 if ( ch.c == ' ' )
03162 painter.drawRect( spcRect );
03163 else
03164 painter.fillRect( spcRect, pen.color() );
03165 }
03166 else if ( ch.c == '\t' && (whichFormattingChars & FormattingTabs) )
03167 {
03168
03169
03170
03171
03172
03173
03174
03175 int availWidth = ch.pixelwidth;
03176
03177 KoTextFormat* format = ch.format();
03178 int x = zh->layoutUnitToPixelX( ch.x ) + availWidth / 2;
03179 int charWidth = format->screenFontMetrics( zh ).width( 'W' );
03180 int size = qMin( availWidth, charWidth ) / 2 ;
03181 int y = lastY_pix + baseLine_pix - zh->layoutUnitToPixelY( ch.ascent()/2 );
03182 int arrowsize = zh->zoomItYOld( 2 );
03183 painter.drawLine( x - size, y, x + size, y );
03184 if ( rightToLeft )
03185 {
03186 painter.drawLine( x - size, y, x - size + arrowsize, y - arrowsize );
03187 painter.drawLine( x - size, y, x - size + arrowsize, y + arrowsize );
03188 }
03189 else
03190 {
03191 painter.drawLine( x + size, y, x + size - arrowsize, y - arrowsize );
03192 painter.drawLine( x + size, y, x + size - arrowsize, y + arrowsize );
03193 }
03194 }
03195 else if ( ch.c == '\n' && (whichFormattingChars & FormattingBreak) )
03196 {
03197
03198 KoTextFormat* format = static_cast<KoTextFormat *>( ch.format() );
03199 int w = format->charWidth( zh, true, &ch, this, 'X' );
03200 int size = qMin( w, h_pix * 3 / 4 );
03201 int arrowsize = zh->zoomItYOld( 2 );
03202
03203
03204 int y = lastY_pix + baseLine_pix - arrowsize;
03205
03206 if ( rightToLeft )
03207 {
03208 int x = zh->layoutUnitToPixelX( ch.x ) + ch.pixelwidth - 1;
03209 painter.drawLine( x - size, y - size, x - size, y );
03210 painter.drawLine( x - size, y, (int)(x - size * 0.3), y );
03211
03212 painter.drawLine( (int)(x - size * 0.3), y, (int)(x - size * 0.3 - arrowsize), y - arrowsize );
03213 painter.drawLine( (int)(x - size * 0.3), y, (int)(x - size * 0.3 - arrowsize), y + arrowsize );
03214 }
03215 else
03216 {
03217 int x = zh->layoutUnitToPixelX( ch.x ) + w - 1;
03218 painter.drawLine( x, y - size, x, y );
03219 painter.drawLine( x, y, (int)(x - size * 0.7), y );
03220
03221 painter.drawLine( (int)(x - size * 0.7), y, (int)(x - size * 0.7 + arrowsize), y - arrowsize );
03222 painter.drawLine( (int)(x - size * 0.7), y, (int)(x - size * 0.7 + arrowsize), y + arrowsize );
03223 }
03224 }
03225 }
03226 painter.restore();
03227 }
03228 }
03229
03230 int KoTextParag::heightForLineSpacing( int startChar, int lastChar ) const
03231 {
03232 int h = 0;
03233 int end = qMin( lastChar, length() - 1 );
03234 for( int i = startChar; i <= end; ++i )
03235 {
03236 const KoTextStringChar &chr = str->at( i );
03237 if ( !chr.isCustom() )
03238 h = qMax( h, chr.format()->height() );
03239 }
03240 return h;
03241 }