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