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