lib Library API Documentation

kotextparag.cc

00001 /* This file is part of the KDE project
00002    Copyright (C) 2001 David Faure <faure@kde.org>
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License as published by the Free Software Foundation; either
00007    version 2 of the License, or (at your option) any later version.
00008 
00009    This library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this library; see the file COPYING.LIB.  If not, write to
00016    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00017    Boston, MA 02111-1307, USA.
00018 */
00019 
00020 //#include "kotextparag.h"
00021 #include "kotextdocument.h"
00022 #include "koparagcounter.h"
00023 #include "kozoomhandler.h"
00024 #include "kostyle.h"
00025 #include <kglobal.h>
00026 #include <klocale.h>
00027 #include <assert.h>
00028 #include <kdebug.h>
00029 #include "kovariable.h"
00030 
00031 //#define DEBUG_PAINT
00032 
00033 #include <qglobal.h>
00034 #if QT_VERSION >= 0x030200
00035 #define INDIC
00036 #endif
00037 
00038 // Return the counter associated with this paragraph.
00039 KoParagCounter *KoTextParag::counter()
00040 {
00041     if ( !m_layout.counter )
00042         return 0L;
00043 
00044     // Garbage collect un-needed counters.
00045     if ( m_layout.counter->numbering() == KoParagCounter::NUM_NONE )
00046         setNoCounter();
00047     return m_layout.counter;
00048 }
00049 
00050 void KoTextParag::setMargin( QStyleSheetItem::Margin m, double _i )
00051 {
00052     //kdDebug(32500) << "KoTextParag::setMargin " << m << " margin " << _i << endl;
00053     m_layout.margins[m] = _i;
00054     if ( m == QStyleSheetItem::MarginTop && prev() )
00055         prev()->invalidate(0);     // for top margin (post-1.1: remove this, not necessary anymore)
00056     invalidate(0);
00057 }
00058 
00059 void KoTextParag::setMargins( const double * margins )
00060 {
00061     for ( int i = 0 ; i < 5 ; ++i )
00062         m_layout.margins[i] = margins[i];
00063     invalidate(0);
00064 }
00065 
00066 void KoTextParag::setAlign( int align )
00067 {
00068     Q_ASSERT( align <= Qt::AlignJustify );
00069     align &= Qt::AlignHorizontal_Mask;
00070     setAlignment( align );
00071     m_layout.alignment = align;
00072 }
00073 
00074 int KoTextParag::resolveAlignment() const
00075 {
00076     if ( m_layout.alignment == Qt::AlignAuto )
00077         return string()->isRightToLeft() ? Qt::AlignRight : Qt::AlignLeft;
00078     return m_layout.alignment;
00079 }
00080 
00081 void KoTextParag::setLineSpacing( double _i )
00082 {
00083     m_layout.setLineSpacingValue(_i);
00084     invalidate(0);
00085 }
00086 
00087 void KoTextParag::setLineSpacingType( KoParagLayout::SpacingType _type )
00088 {
00089     m_layout.lineSpacingType = _type;
00090     invalidate(0);
00091 }
00092 
00093 void KoTextParag::setTopBorder( const KoBorder & _brd )
00094 {
00095     m_layout.topBorder = _brd;
00096     invalidate(0);
00097 }
00098 
00099 void KoTextParag::setBottomBorder( const KoBorder & _brd )
00100 {
00101     m_layout.bottomBorder = _brd;
00102     invalidate(0);
00103 }
00104 
00105 void KoTextParag::setNoCounter()
00106 {
00107     delete m_layout.counter;
00108     m_layout.counter = 0L;
00109     invalidateCounters();
00110 }
00111 
00112 void KoTextParag::setCounter( const KoParagCounter & counter )
00113 {
00114     // Garbage collect unnneeded counters.
00115     if ( counter.numbering() == KoParagCounter::NUM_NONE )
00116     {
00117         setNoCounter();
00118     }
00119     else
00120     {
00121         delete m_layout.counter;
00122         m_layout.counter = new KoParagCounter( counter );
00123 
00124         // Invalidate the counters
00125         invalidateCounters();
00126     }
00127 }
00128 
00129 void KoTextParag::invalidateCounters()
00130 {
00131     // Invalidate this paragraph and all the following ones
00132     // (Numbering may have changed)
00133     invalidate( 0 );
00134     if ( m_layout.counter )
00135         m_layout.counter->invalidate();
00136     KoTextParag *s = next();
00137     while ( s ) {
00138         if ( s->m_layout.counter )
00139             s->m_layout.counter->invalidate();
00140         s->invalidate( 0 );
00141         s = s->next();
00142     }
00143 }
00144 
00145 int KoTextParag::counterWidth() const
00146 {
00147     if ( !m_layout.counter )
00148         return 0;
00149 
00150     return m_layout.counter->width( this );
00151 }
00152 
00153 // Draw the complete label (i.e. heading/list numbers/bullets) for this paragraph.
00154 // This is called by KoTextParag::paint.
00155 void KoTextParag::drawLabel( QPainter* p, int xLU, int yLU, int /*wLU*/, int /*hLU*/, int baseLU, const QColorGroup& /*cg*/ )
00156 {
00157     if ( !m_layout.counter ) // shouldn't happen
00158         return;
00159 
00160     if ( m_layout.counter->numbering() == KoParagCounter::NUM_NONE )
00161     {   // Garbage collect unnneeded counter.
00162         delete m_layout.counter;
00163         m_layout.counter = 0L;
00164         return;
00165     }
00166 
00167     int counterWidthLU = m_layout.counter->width( this );
00168 
00169     // We use the formatting of the first char as the formatting of the counter
00170     // But without bold/italic
00171     KoTextFormat counterFormat( *KoParagCounter::counterFormat( this ) );
00172     counterFormat.setBold( false );
00173     counterFormat.setItalic( false );
00174     KoTextFormat* format = &counterFormat;
00175     p->save();
00176 
00177     QColor textColor( format->color() );
00178     if ( !textColor.isValid() ) // Resolve the color at this point
00179         textColor = KoTextFormat::defaultTextColor( p );
00180     p->setPen( QPen( textColor ) );
00181 
00182     KoZoomHandler * zh = textDocument()->paintingZoomHandler();
00183     assert( zh );
00184     //bool forPrint = ( p->device()->devType() == QInternal::Printer );
00185 
00186     bool rtl = str->isRightToLeft(); // when true, we put suffix+counter+prefix at the RIGHT of the paragraph.
00187     int xLeft = zh->layoutUnitToPixelX( xLU - (rtl ? 0 : counterWidthLU) );
00188     int y = zh->layoutUnitToPixelY( yLU );
00189     //int h = zh->layoutUnitToPixelY( yLU, hLU );
00190     int base = zh->layoutUnitToPixelY( yLU, baseLU );
00191     int counterWidth = zh->layoutUnitToPixelX( xLU, counterWidthLU );
00192     int height = zh->layoutUnitToPixelY( yLU, format->height() );
00193 
00194     QFont font( format->screenFont( zh ) );
00195     // Footnote numbers are in superscript (in WP and Word, not in OO)
00196     if ( m_layout.counter->numbering() == KoParagCounter::NUM_FOOTNOTE )
00197     {
00198         int pointSize = ( ( font.pointSize() * 2 ) / 3 );
00199         font.setPointSize( pointSize );
00200         y -= ( height - QFontMetrics(font).height() );
00201     }
00202     p->setFont( font );
00203 
00204     // Now draw any bullet that is required over the space left for it.
00205     if ( m_layout.counter->isBullet() )
00206     {
00207     int xBullet = xLeft + zh->layoutUnitToPixelX( m_layout.counter->bulletX() );
00208 
00209         //kdDebug(32500) << "KoTextParag::drawLabel xLU=" << xLU << " counterWidthLU=" << counterWidthLU << endl;
00210     // The width and height of the bullet is the width of one space
00211         int width = zh->layoutUnitToPixelX( xLeft, format->width( ' ' ) );
00212 
00213         //kdDebug(32500) << "Pix: xLeft=" << xLeft << " counterWidth=" << counterWidth
00214         //          << " xBullet=" << xBullet << " width=" << width << endl;
00215 
00216         QString prefix = m_layout.counter->prefix();
00217         if ( !prefix.isEmpty() )
00218         {
00219             if ( rtl )
00220                 prefix.prepend( ' ' /*the space before the bullet in RTL mode*/ );
00221             KoTextParag::drawFontEffects( p, format, zh, format->screenFont( zh ), textColor, xLeft, base, width, y, height, prefix[0] );
00222 
00223             int posY =y + base - format->offsetFromBaseLine();
00224             //we must move to bottom text because we create
00225             //shadow to 'top'.
00226             int sy = format->shadowY( zh );
00227             if ( sy < 0)
00228                 posY -= sy;
00229 
00230             p->drawText( xLeft, posY, prefix );
00231         }
00232 
00233         QRect er( xBullet + (rtl ? width : 0), y + height / 2 - width / 2, width, width );
00234         // Draw the bullet.
00235         int posY = 0;
00236         switch ( m_layout.counter->style() )
00237         {
00238             case KoParagCounter::STYLE_DISCBULLET:
00239                 p->setBrush( QBrush(textColor) );
00240                 p->drawEllipse( er );
00241                 p->setBrush( Qt::NoBrush );
00242                 break;
00243             case KoParagCounter::STYLE_SQUAREBULLET:
00244                 p->fillRect( er, QBrush(textColor) );
00245                 break;
00246             case KoParagCounter::STYLE_BOXBULLET:
00247                 p->drawRect( er );
00248                 break;
00249             case KoParagCounter::STYLE_CIRCLEBULLET:
00250                 p->drawEllipse( er );
00251                 break;
00252             case KoParagCounter::STYLE_CUSTOMBULLET:
00253             {
00254                 // The user has selected a symbol from a special font. Override the paragraph
00255                 // font with the given family. This conserves the right size etc.
00256                 if ( !m_layout.counter->customBulletFont().isEmpty() )
00257                 {
00258                     QFont bulletFont( p->font() );
00259                     bulletFont.setFamily( m_layout.counter->customBulletFont() );
00260                     p->setFont( bulletFont );
00261                 }
00262                 KoTextParag::drawFontEffects( p, format, zh, format->screenFont( zh ), textColor, xBullet, base, width, y, height, ' ' );
00263 
00264                 posY = y + base- format->offsetFromBaseLine();
00265                 //we must move to bottom text because we create
00266                 //shadow to 'top'.
00267                 int sy = format->shadowY( zh );
00268                 if ( sy < 0)
00269                     posY -= sy;
00270 
00271                 p->drawText( xBullet, posY, m_layout.counter->customBulletCharacter() );
00272                 break;
00273             }
00274             default:
00275                 break;
00276         }
00277 
00278         QString suffix = m_layout.counter->suffix();
00279         if ( !suffix.isEmpty() )
00280         {
00281             if ( !rtl )
00282                 suffix += ' ' /*the space after the bullet*/;
00283 
00284             KoTextParag::drawFontEffects( p, format, zh, format->screenFont( zh ), textColor, xBullet + width, base, counterWidth, y,height, suffix[0] );
00285 
00286             int posY =y + base- format->offsetFromBaseLine();
00287             //we must move to bottom text because we create
00288             //shadow to 'top'.
00289             int sy = format->shadowY( zh );
00290             if ( sy < 0)
00291                 posY -= sy;
00292 
00293             p->drawText( xBullet + width, posY, suffix, -1 );
00294         }
00295     }
00296     else
00297     {
00298         QString counterText = m_layout.counter->text( this );
00299         // There are no bullets...any parent bullets have already been suppressed.
00300         // Just draw the text! Note: one space is always appended.
00301         if ( !counterText.isEmpty() )
00302         {
00303             KoTextParag::drawFontEffects( p, format, zh, format->screenFont( zh ), textColor, xLeft, base, counterWidth, y, height, counterText[0] );
00304 
00305             counterText += ' ' /*the space after the bullet (before in RTL mode)*/;
00306 
00307             int posY =y + base - format->offsetFromBaseLine();
00308             //we must move to bottom text because we create
00309             //shadow to 'top'.
00310             int sy = format->shadowY( zh );
00311             if ( sy < 0)
00312                 posY -= sy;
00313 
00314             p->drawText( xLeft, posY , counterText, -1 );
00315         }
00316     }
00317     p->restore();
00318 }
00319 
00320 int KoTextParag::breakableTopMargin() const
00321 {
00322     KoZoomHandler * zh = textDocument()->formattingZoomHandler();
00323     return zh->ptToLayoutUnitPixY(
00324         m_layout.margins[ QStyleSheetItem::MarginTop ] );
00325 }
00326 
00327 int KoTextParag::topMargin() const
00328 {
00329     KoZoomHandler * zh = textDocument()->formattingZoomHandler();
00330     return zh->ptToLayoutUnitPixY(
00331         m_layout.margins[ QStyleSheetItem::MarginTop ]
00332         + m_layout.topBorder.width() );
00333 }
00334 
00335 int KoTextParag::bottomMargin() const
00336 {
00337     KoZoomHandler * zh = textDocument()->formattingZoomHandler();
00338     return zh->ptToLayoutUnitPixY(
00339         m_layout.margins[ QStyleSheetItem::MarginBottom ]
00340         + m_layout.bottomBorder.width() );
00341 }
00342 
00343 int KoTextParag::leftMargin() const
00344 {
00345     KoZoomHandler * zh = textDocument()->formattingZoomHandler();
00346     return zh->ptToLayoutUnitPixX(
00347         m_layout.margins[ QStyleSheetItem::MarginLeft ]
00348         + m_layout.leftBorder.width() );
00349 }
00350 
00351 int KoTextParag::rightMargin() const
00352 {
00353     KoZoomHandler * zh = textDocument()->formattingZoomHandler();
00354     int cw=0;
00355     if( m_layout.counter && str->isRightToLeft() &&
00356         (( m_layout.counter->alignment() == Qt::AlignRight ) || ( m_layout.counter->alignment() == Qt::AlignAuto )))
00357         cw = counterWidth();
00358 
00359     return zh->ptToLayoutUnitPixX(
00360         m_layout.margins[ QStyleSheetItem::MarginRight ]
00361         + m_layout.rightBorder.width() )
00362         + cw; /* in layout units already */
00363 }
00364 
00365 int KoTextParag::firstLineMargin() const
00366 {
00367     KoZoomHandler * zh = textDocument()->formattingZoomHandler();
00368     return zh->ptToLayoutUnitPixY(
00369         m_layout.margins[ QStyleSheetItem::MarginFirstLine ] );
00370 }
00371 
00372 int KoTextParag::lineSpacing( int line ) const
00373 {
00374     KoZoomHandler * zh = textDocument()->formattingZoomHandler();
00375     // TODO add shadow in KoTextFormatter!
00376     int shadow = 0; //QABS( zh->ptToLayoutUnitPixY( shadowDistanceY() ) );
00377     if ( m_layout.lineSpacingType == KoParagLayout::LS_SINGLE )
00378         return shadow;
00379     else if ( m_layout.lineSpacingType == KoParagLayout::LS_CUSTOM )
00380         return zh->ptToLayoutUnitPixY( m_layout.lineSpacingValue() ) + shadow;
00381     else {
00382         KoTextParag * that = const_cast<KoTextParag *>(this);
00383         if( line >= (int)that->lineStartList().count() )
00384         {
00385             kdError() << "KoTextParag::lineSpacing assert(line<lines) failed: line=" << line << " lines=" << that->lineStartList().count() << endl;
00386             return 0+shadow;
00387         }
00388         QMap<int, KoTextParagLineStart*>::ConstIterator it = that->lineStartList().begin();
00389         while ( line-- > 0 )
00390             ++it;
00391         if ( isValid() )
00392             return (*it)->lineSpacing;
00393 
00394         int height = ( *it )->h;
00395         //kdDebug(32500) << " line spacing type: " << m_layout.lineSpacingType << " value:" << m_layout.lineSpacingValue() << " line_height=" << height << endl;
00396         switch ( m_layout.lineSpacingType )
00397         {
00398         case KoParagLayout::LS_MULTIPLE:
00399         {
00400             double n = QMAX( m_layout.lineSpacingValue() - 1.0, 0.0 );
00401             return shadow + qRound( n * height );
00402         }
00403         case KoParagLayout::LS_ONEANDHALF:
00404         {
00405             // Special case of LS_MULTIPLE, with n=1.5
00406             return shadow + height / 2;
00407         }
00408         case KoParagLayout::LS_DOUBLE:
00409         {
00410             // Special case of LS_MULTIPLE, with n=1
00411             return shadow + height;
00412         }
00413         case KoParagLayout::LS_AT_LEAST:
00414         {
00415             int atLeast = zh->ptToLayoutUnitPixY( m_layout.lineSpacingValue() );
00416             int h = QMAX( height, atLeast );
00417             // height is now the required total height
00418             return shadow + h - height;
00419         }
00420         case KoParagLayout::LS_FIXED:
00421         {
00422             return shadow + zh->ptToLayoutUnitPixY( m_layout.lineSpacingValue() ) - height;
00423         }
00424         // Silence compiler warnings
00425         case KoParagLayout::LS_SINGLE:
00426         case KoParagLayout::LS_CUSTOM:
00427             break;
00428         }
00429     }
00430     kdWarning() << "Unhandled linespacing type : " << m_layout.lineSpacingType << endl;
00431     return 0+shadow;
00432 }
00433 
00434 QRect KoTextParag::pixelRect( KoZoomHandler *zh ) const
00435 {
00436     QRect rct( zh->layoutUnitToPixel( rect() ) );
00437     //kdDebug(32500) << "   pixelRect for parag " << paragId()
00438     //               << ": rect=" << rect() << " pixelRect=" << rct << endl;
00439 
00440     // After division we almost always end up with the top overwriting the bottom of the parag above
00441     if ( prev() )
00442     {
00443         QRect prevRect( zh->layoutUnitToPixel( prev()->rect() ) );
00444         if ( rct.top() < prevRect.bottom() + 1 )
00445         {
00446             //kdDebug(32500) << "   pixelRect: rct.top() adjusted to " << prevRect.bottom() + 1 << " (was " << rct.top() << ")" << endl;
00447             rct.setTop( prevRect.bottom() + 1 );
00448         }
00449     }
00450     return rct;
00451 }
00452 
00453 // Paint this paragraph. This is called by KoTextDocument::drawParagWYSIWYG
00454 // (KoTextDocument::drawWithoutDoubleBuffer when printing)
00455 void KoTextParag::paint( QPainter &painter, const QColorGroup &cg, KoTextCursor *cursor, bool drawSelections,
00456                          int clipx, int clipy, int clipw, int cliph )
00457 {
00458 #ifdef DEBUG_PAINT
00459     kdDebug(32500) << "KoTextParag::paint =====  id=" << paragId() << " clipx=" << clipx << " clipy=" << clipy << " clipw=" << clipw << " cliph=" << cliph << endl;
00460     kdDebug(32500) << " clipw in pix (approx) : " << textDocument()->paintingZoomHandler()->layoutUnitToPixelX( clipw ) << " cliph in pix (approx) : " << textDocument()->paintingZoomHandler()->layoutUnitToPixelX( cliph ) << endl;
00461 #endif
00462 
00463     // Let's call drawLabel ourselves, rather than having to deal with QStyleSheetItem to get paintLines to call it!
00464     if ( m_layout.counter && m_layout.counter->numbering() != KoParagCounter::NUM_NONE && m_lineChanged <= 0 )
00465     {
00466         int cy, h, baseLine;
00467         lineInfo( 0, cy, h, baseLine );
00468         int xLabel = at(0)->x;
00469         if ( str->isRightToLeft() )
00470             xLabel += at(0)->width;
00471         drawLabel( &painter, xLabel, cy, 0, 0, baseLine, cg );
00472     }
00473 
00474     paintLines( painter, cg, cursor, drawSelections, clipx, clipy, clipw, cliph );
00475 
00476     // Now draw paragraph border
00477     if ( m_layout.hasBorder() )
00478     {
00479         KoZoomHandler * zh = textDocument()->paintingZoomHandler();
00480         assert(zh);
00481 
00482         QRect r;
00483         // Old solution: stick to the text
00484         //r.setLeft( at( 0 )->x - counterWidth() - 1 );
00485         //r.setRight( rect().width() - rightMargin() - 1 );
00486 
00487         // New solution: occupy the full width
00488         // Note that this is what OpenOffice does too.
00489         // For something closer to the text, we need a border feature in KoTextFormat, I guess.
00490 
00491         // drawBorders paints outside the give rect, so we need to 'subtract' the border
00492         // width on all sides.
00493         r.setLeft( KoBorder::zoomWidthX( m_layout.leftBorder.width(), zh, 0 ) );
00494         // The +1 is because if border is 1 pixel, nothing to subtract. 2 pixels -> subtract 1.
00495         r.setRight( zh->layoutUnitToPixelX(rect().width()) - KoBorder::zoomWidthX( m_layout.rightBorder.width(), zh, 0 ) );
00496         r.setTop( zh->layoutUnitToPixelY(lineY( 0 )) );
00497 
00498         int lastLine = lines() - 1;
00499         // We need to start from the pixelRect, to make sure the bottom border is entirely painted.
00500         // This is a case where we DO want to subtract pixels to pixels...
00501         int paragBottom = pixelRect(zh).height()-1;
00502         // If we don't have a bottom border, we need go as low as possible ( to touch the next parag's border ).
00503         // If we have a bottom border, then we rather exclude the linespacing. Looks nicer. OO does that too.
00504         if ( m_layout.bottomBorder.width() > 0 )
00505             paragBottom -= zh->layoutUnitToPixelY( lineSpacing( lastLine ) );
00506         paragBottom -= KoBorder::zoomWidthY( m_layout.bottomBorder.width(), zh, 0 );
00507         //kdDebug(32500) << "Parag border: paragBottom=" << paragBottom
00508         //               << " bottom border width = " << KoBorder::zoomWidthY( m_layout.bottomBorder.width(), zh, 0 ) << endl;
00509         r.setBottom( paragBottom );
00510 
00511         //kdDebug(32500) << "KoTextParag::paint documentWidth=" << documentWidth() << " LU (" << zh->layoutUnitToPixelX(documentWidth()) << " pixels) bordersRect=" << r << endl;
00512         KoBorder::drawBorders( painter, zh, r,
00513                                m_layout.leftBorder, m_layout.rightBorder, m_layout.topBorder, m_layout.bottomBorder,
00514                                0, QPen() );
00515     }
00516 }
00517 
00518 
00519 void KoTextParag::paintLines( QPainter &painter, const QColorGroup &cg, KoTextCursor *cursor, bool drawSelections,
00520             int clipx, int clipy, int clipw, int cliph )
00521 {
00522     if ( !visible )
00523     return;
00524     //KoTextStringChar *chr = at( 0 );
00525     //if (!chr) { kdDebug(32500) << "paragraph " << (void*)this << " " << paragId() << ", can't paint, EMPTY !" << endl;
00526 
00527     // This is necessary with the current code, but in theory it shouldn't
00528     // be necessary, if Xft really gives us fully proportionnal chars....
00529 #define CHECK_PIXELXADJ
00530 
00531     int curx = -1, cury = 0, curh = 0, curline = 0;
00532     int xstart, xend = 0;
00533 
00534     QString qstr = str->toString();
00535     qstr.replace( QChar(0x00a0U), ' ' ); // Not all fonts have non-breakable-space glyph
00536 
00537     const int nSels = doc ? doc->numSelections() : 1;
00538     QMemArray<int> selectionStarts( nSels );
00539     QMemArray<int> selectionEnds( nSels );
00540     if ( drawSelections ) {
00541     bool hasASelection = FALSE;
00542     for ( int i = 0; i < nSels; ++i ) {
00543         if ( !hasSelection( i ) ) {
00544         selectionStarts[ i ] = -1;
00545         selectionEnds[ i ] = -1;
00546         } else {
00547         hasASelection = TRUE;
00548         selectionStarts[ i ] = selectionStart( i );
00549         int end = selectionEnd( i );
00550         if ( end == length() - 1 && n && n->hasSelection( i ) )
00551             end++;
00552         selectionEnds[ i ] = end;
00553         }
00554     }
00555     if ( !hasASelection )
00556         drawSelections = FALSE;
00557     }
00558 
00559     // Draw the lines!
00560     int line = m_lineChanged;
00561     if (line<0) line = 0;
00562 
00563     int numLines = lines();
00564 #ifdef DEBUG_PAINT
00565     kdDebug(32500) << " paintLines: from line " << line << " to " << numLines-1 << endl;
00566 #endif
00567     for( ; line<numLines ; line++ )
00568     {
00569     // get the start and length of the line
00570     int nextLine;
00571         int startOfLine;
00572         lineStartOfLine(line, &startOfLine);
00573     if (line == numLines-1 )
00574             nextLine = length();
00575     else
00576             lineStartOfLine(line+1, &nextLine);
00577 
00578     // init this line
00579         int cy, h, baseLine;
00580     lineInfo( line, cy, h, baseLine );
00581     if ( clipy != -1 && cy > clipy - r.y() + cliph ) // outside clip area, leave
00582         break;
00583 
00584         // Vars related to the current "run of text"
00585     int paintStart = startOfLine;
00586     KoTextStringChar* chr = at(startOfLine);
00587         KoTextStringChar* nextchr = chr;
00588 
00589     // okay, paint the line!
00590     for(int i=startOfLine;i<nextLine;i++)
00591     {
00592             chr = nextchr;
00593             if ( i < nextLine-1 )
00594                 nextchr = at( i+1 );
00595 
00596             // we flush at end of line
00597             bool flush = ( i == nextLine - 1 );
00598             // Optimization note: QRT uses "flush |=", which doesn't have shortcut optimization
00599 
00600             // we flush on format changes
00601         flush = flush || ( nextchr->format() != chr->format() );
00602         // we flush on link changes
00603         //flush = flush || ( nextchr->isLink() != chr->isLink() );
00604             // we flush on small caps changes
00605             if ( !flush && chr->format()->attributeFont() == KoTextFormat::ATT_SMALL_CAPS )
00606             {
00607                 bool isLowercase = chr->c.upper() != chr->c;
00608                 bool nextLowercase = nextchr->c.upper() != nextchr->c;
00609                 flush = isLowercase != nextLowercase;
00610             }
00611         // we flush on start of run
00612         flush = flush || nextchr->startOfRun;
00613         // we flush on bidi changes
00614         flush = flush || ( nextchr->rightToLeft != chr->rightToLeft );
00615 #ifdef CHECK_PIXELXADJ
00616             // we flush when the value of pixelxadj changes
00617 #ifndef INDIC
00618             flush = flush || ( nextchr->pixelxadj != chr->pixelxadj );
00619 #else
00620             // [unless inside a ligature]
00621             flush = flush || ( nextchr->pixelxadj != chr->pixelxadj && nextchr->charStop );
00622 #endif
00623 #endif
00624         // we flush before and after tabs
00625         flush = flush || ( chr->c == '\t' || nextchr->c == '\t' );
00626         // we flush on soft hypens
00627         flush = flush || ( chr->c.unicode() == 0xad );
00628         // we flush on custom items
00629         flush = flush || chr->isCustom();
00630         // we flush before custom items
00631         flush = flush || nextchr->isCustom();
00632         // when painting justified we flush on spaces
00633         if ((alignment() & Qt::AlignJustify) == Qt::AlignJustify )
00634         //flush = flush || QTextFormatter::isBreakable( str, i );
00635 #ifndef INDIC
00636                 flush = flush || chr->c.isSpace();
00637 #else
00638                 flush = flush || chr->whiteSpace;
00639 #endif
00640         // when underlining or striking "word by word" we flush before/after spaces
00641         if (!flush && chr->format()->wordByWord() && chr->format()->isStrikedOrUnderlined())
00642 #ifndef INDIC
00643                 flush = flush || chr->c.isSpace() || nextchr->c.isSpace();
00644 #else
00645                 flush = flush || chr->whiteSpace || nextchr->whiteSpace;
00646 #endif
00647         // we flush when the string is getting too long
00648         flush = flush || ( i - paintStart >= 256 );
00649         // we flush when the selection state changes
00650         if ( drawSelections ) {
00651                 // check if selection state changed - TODO update from QRT
00652         bool selectionChange = FALSE;
00653         if ( drawSelections ) {
00654             for ( int j = 0; j < nSels; ++j ) {
00655             selectionChange = selectionStarts[ j ] == i+1 || selectionEnds[ j ] == i+1;
00656             if ( selectionChange )
00657                 break;
00658             }
00659         }
00660                 flush = flush || selectionChange;
00661             }
00662 
00663             // check for cursor mark
00664             if ( cursor && this == cursor->parag() && i == cursor->index() ) {
00665                 curx = cursor->x();
00666                 curline = line;
00667                 KoTextStringChar *c = chr;
00668                 if ( i > 0 )
00669                     --c;
00670                 curh = c->height();
00671                 cury = cy + baseLine - c->ascent();
00672             }
00673 
00674             if ( flush ) {  // something changed, draw what we have so far
00675 
00676                 KoTextStringChar* cStart = at( paintStart );
00677                 if ( chr->rightToLeft ) {
00678                     xstart = chr->x;
00679                     xend = cStart->x + cStart->width;
00680                 } else {
00681                     xstart = cStart->x;
00682                         if ( i < length() - 1 && !str->at( i + 1 ).lineStart &&
00683                          str->at( i + 1 ).rightToLeft == chr->rightToLeft )
00684                         xend = str->at( i + 1 ).x;
00685                     else
00686                         xend = chr->x + chr->width;
00687                 }
00688 
00689                 if ( (clipx == -1 || clipw == -1) || (xend >= clipx && xstart <= clipx + clipw) ) {
00690                     if ( !chr->isCustom() ) {
00691                         drawParagString( painter, qstr, paintStart, i - paintStart + 1, xstart, cy,
00692                                          baseLine, xend-xstart, h, drawSelections,
00693                                          chr->format(), selectionStarts, selectionEnds,
00694                                          cg, chr->rightToLeft, line );
00695                     }
00696                     else
00697                         if ( chr->customItem()->placement() == KoTextCustomItem::PlaceInline ) {
00698                             chr->customItem()->draw( &painter, chr->x, cy + baseLine - chr->customItem()->ascent(), clipx - r.x(), clipy - r.y(), clipw, cliph, cg,
00699                                                      drawSelections && nSels && selectionStarts[ 0 ] <= i && selectionEnds[ 0 ] > i );
00700                         }
00701                 }
00702                 paintStart = i+1;
00703             }
00704         } // end of character loop
00705     } // end of line loop
00706 
00707     // if we should draw a cursor, draw it now
00708     if ( curx != -1 && cursor ) {
00709         drawCursor( painter, cursor, curx, cury, curh, cg );
00710     }
00711 }
00712 
00713 // Called by KoTextParag::paintLines
00714 // Draw a set of characters with the same formattings.
00715 // Reimplemented here to convert coordinates first, and call @ref drawFormattingChars.
00716 void KoTextParag::drawParagString( QPainter &painter, const QString &str, int start, int len, int startX,
00717                                    int lastY, int baseLine, int bw, int h, bool drawSelections,
00718                                    KoTextFormat *format, const QMemArray<int> &selectionStarts,
00719                                    const QMemArray<int> &selectionEnds, const QColorGroup &cg, bool rightToLeft, int line )
00720 {
00721     KoZoomHandler * zh = textDocument()->paintingZoomHandler();
00722     assert(zh);
00723 
00724 #ifdef DEBUG_PAINT
00725     kdDebug(32500) << "KoTextParag::drawParagString drawing from " << start << " to " << start+len << endl;
00726     kdDebug(32500) << " startX in LU: " << startX << " lastY in LU:" << lastY
00727                    << " baseLine in LU:" << baseLine << endl;
00728 #endif
00729 
00730     // Calculate offset (e.g. due to shadow on left or top)
00731     // Important: don't use the 2-args methods here, offsets are not heights
00732     // (0 should be 0, not 1) (#63256)
00733     int shadowOffsetX_pix = zh->layoutUnitToPixelX( format->offsetX() );
00734     int shadowOffsetY_pix = zh->layoutUnitToPixelY( format->offsetY() );
00735 
00736     // Calculate startX in pixels
00737     int startX_pix = zh->layoutUnitToPixelX( startX ) /* + at( rightToLeft ? start+len-1 : start )->pixelxadj */;
00738 #ifdef DEBUG_PAINT
00739     kdDebug(32500) << "KoTextParag::drawParagString startX in pixels : " << startX_pix /*<< " adjustment:" << at( rightToLeft ? start+len-1 : start )->pixelxadj*/ << " bw=" << bw << endl;
00740 #endif
00741 
00742     int bw_pix = zh->layoutUnitToPixelX( startX, bw );
00743     int lastY_pix = zh->layoutUnitToPixelY( lastY );
00744     int baseLine_pix = zh->layoutUnitToPixelY( lastY, baseLine ); // 2 args=>+1. Is that correct?
00745     int h_pix = zh->layoutUnitToPixelY( lastY, h );
00746 #ifdef DEBUG_PAINT
00747     kdDebug(32500) << "KoTextParag::drawParagString h(LU)=" << h << " lastY(LU)=" << lastY
00748                    << " h(PIX)=" << h_pix << " lastY(PIX)=" << lastY_pix
00749                    << " baseLine(PIX)=" << baseLine_pix << endl;
00750 #endif
00751 
00752     if ( format->textBackgroundColor().isValid() )
00753         painter.fillRect( startX_pix, lastY_pix, bw_pix, h_pix, format->textBackgroundColor() );
00754 
00755     // don't want to draw line breaks but want them when drawing formatting chars
00756     int draw_len = len;
00757     int draw_startX = startX;
00758     int draw_bw = bw_pix;
00759     if ( at( start + len - 1 )->c == '\n' )
00760     {
00761         draw_len--;
00762         draw_bw -= at( start + len - 1 )->pixelwidth;
00763         if ( rightToLeft && draw_len > 0 )
00764             draw_startX = at( start + draw_len - 1 )->x;
00765     }
00766 
00767     // Draw selection (moved here to do it before applying the offset from the shadow)
00768     // (and because it's not part of the shadow drawing)
00769     if ( drawSelections ) {
00770         bool inSelection = false;
00771     const int nSels = doc ? doc->numSelections() : 1;
00772     for ( int j = 0; j < nSels; ++j ) {
00773         if ( start >= selectionStarts[ j ] && start < selectionEnds[ j ] ) {
00774                 inSelection = true;
00775         if ( j == KoTextDocument::Standard )
00776             painter.fillRect( startX_pix, lastY_pix, bw_pix, h_pix, cg.color( QColorGroup::Highlight ) );
00777         else
00778             painter.fillRect( startX_pix, lastY_pix, bw_pix, h_pix, doc ? doc->selectionColor( j ) : cg.color( QColorGroup::Highlight ) );
00779                 break;
00780         }
00781     }
00782         if ( !inSelection )
00783             drawSelections = false; // save time in drawParagStringInternal
00784     }
00785 
00786     if ( draw_len > 0 )
00787     {
00788         int draw_startX_pix = zh->layoutUnitToPixelX( draw_startX ) /* + at( rightToLeft ? start+draw_len-1 : start )->pixelxadj*/;
00789         draw_startX_pix += shadowOffsetX_pix;
00790         lastY_pix += shadowOffsetY_pix;
00791 
00792         if ( format->shadowDistanceX() != 0 || format->shadowDistanceY() != 0 ) {
00793             int sx = format->shadowX( zh );
00794             int sy = format->shadowY( zh );
00795             if ( sx != 0 || sy != 0 )
00796             {
00797                 painter.save();
00798                 painter.translate( sx, sy );
00799                 drawParagStringInternal( painter, str, start, draw_len, draw_startX_pix,
00800                                          lastY_pix, baseLine_pix,
00801                                          draw_bw,
00802                                          h_pix, FALSE /*drawSelections*/,
00803                                          format, selectionStarts,
00804                                          selectionEnds, cg, rightToLeft, line, zh, true );
00805                 painter.restore();
00806             }
00807         }
00808 
00809         drawParagStringInternal( painter, str, start, draw_len, draw_startX_pix,
00810                                  lastY_pix, baseLine_pix,
00811                                  draw_bw,
00812                                  h_pix, drawSelections, format, selectionStarts,
00813                                  selectionEnds, cg, rightToLeft, line, zh, false );
00814     }
00815 
00816     bool forPrint = ( painter.device()->devType() == QInternal::Printer );
00817     if ( textDocument()->drawFormattingChars() && !forPrint )
00818     {
00819         drawFormattingChars( painter, start, len,
00820                              lastY_pix, baseLine_pix, h_pix,
00821                              drawSelections,
00822                              format, selectionStarts,
00823                              selectionEnds, cg, rightToLeft,
00824                              line, zh, AllFormattingChars );
00825     }
00826 }
00827 
00828 // Copied from the original KoTextParag
00829 // (we have to copy it here, so that color & font changes don't require changing
00830 // a local copy of the text format)
00831 // And we have to keep it separate from drawParagString to avoid s/startX/startX_pix/ etc.
00832 void KoTextParag::drawParagStringInternal( QPainter &painter, const QString &s, int start, int len, int startX,
00833                                    int lastY, int baseLine, int bw, int h, bool drawSelections,
00834                                    KoTextFormat *format, const QMemArray<int> &selectionStarts,
00835                                    const QMemArray<int> &selectionEnds, const QColorGroup &cg, bool rightToLeft, int line, KoZoomHandler* zh, bool drawingShadow )
00836 {
00837 #ifdef DEBUG_PAINT
00838     kdDebug(32500) << "KoTextParag::drawParagStringInternal start=" << start << " len=" << len << " : '" << s.mid(start,len) << "'" << endl;
00839     kdDebug(32500) << "In pixels:  startX=" << startX << " lastY=" << lastY << " baseLine=" << baseLine
00840                    << " bw=" << bw << " h=" << h << " rightToLeft=" << rightToLeft << endl;
00841 #endif
00842     if ( drawingShadow && format->shadowDistanceX() == 0 && format->shadowDistanceY() == 0 )
00843         return;
00844     // 1) Sort out the color
00845     QColor textColor( drawingShadow ? format->shadowColor() : format->color() );
00846     if ( !textColor.isValid() ) // Resolve the color at this point
00847         textColor = KoTextFormat::defaultTextColor( &painter );
00848 
00849     // 2) Sort out the font
00850     QFont font( format->screenFont( zh ) );
00851     if ( format->attributeFont() == KoTextFormat::ATT_SMALL_CAPS && s[start].upper() != s[start] )
00852         font = format->smallCapsFont( zh, true );
00853 
00854 #if 0
00855     QFontInfo fi( font );
00856     kdDebug(32500) << "KoTextParag::drawParagStringInternal requested font " << font.pointSizeFloat() << " using font " << fi.pointSize() << "pt (format font: " << format->font().pointSizeFloat() << "pt)" << endl;
00857     QFontMetrics fm( font );
00858     kdDebug(32500) << "Real font: " << fi.family() << ". Font height in pixels: " << fm.height() << endl;
00859 #endif
00860 
00861     // 3) Paint
00862     QString str( s );
00863     if ( str[ (int)str.length() - 1 ].unicode() == 0xad )
00864         str.remove( str.length() - 1, 1 );
00865     painter.setPen( QPen( textColor ) );
00866     painter.setFont( font );
00867 
00868     KoTextDocument* doc = document();
00869 
00870     if ( drawSelections ) {
00871     const int nSels = doc ? doc->numSelections() : 1;
00872     for ( int j = 0; j < nSels; ++j ) {
00873         if ( start >= selectionStarts[ j ] && start < selectionEnds[ j ] ) {
00874         if ( !doc || doc->invertSelectionText( j ) )
00875             textColor = cg.color( QColorGroup::HighlightedText );
00876             painter.setPen( QPen( textColor ) );
00877                     break;
00878             }
00879         }
00880     }
00881 
00882     QPainter::TextDirection dir = rightToLeft ? QPainter::RTL : QPainter::LTR;
00883 
00884     if ( dir != QPainter::RTL && start + len == length() ) // don't draw the last character (trailing space)
00885     {
00886        len--;
00887        if ( len <= 0 )
00888            return;
00889        bw-=at(length()-1)->pixelwidth;
00890     }
00891     KoTextParag::drawFontEffects( &painter, format, zh, font, textColor, startX, baseLine, bw, lastY, h, str[start] );
00892 
00893     if ( str[ start ] != '\t' && str[ start ].unicode() != 0xad ) {
00894         str = format->displayedString( str ); // #### This converts the whole string, instead of from start to start+len!
00895     if ( format->vAlign() == KoTextFormat::AlignNormal ) {
00896             int posY = lastY + baseLine - format->offsetFromBaseLine();
00897             //we must move to bottom text because we create
00898             //shadow to 'top'.
00899             int sy = format->shadowY( zh );
00900             if ( sy < 0)
00901                 posY -= sy;
00902         painter.drawText( startX, posY, str, start, len, dir );
00903 #ifdef BIDI_DEBUG
00904         painter.save();
00905         painter.setPen ( Qt::red );
00906         painter.drawLine( startX, lastY, startX, lastY + baseLine );
00907         painter.drawLine( startX, lastY + baseLine/2, startX + 10, lastY + baseLine/2 );
00908         int w = 0;
00909         int i = 0;
00910         while( i < len )
00911         w += painter.fontMetrics().charWidth( str, start + i++ );
00912         painter.setPen ( Qt::blue );
00913         painter.drawLine( startX + w - 1, lastY, startX + w - 1, lastY + baseLine );
00914         painter.drawLine( startX + w - 1, lastY + baseLine/2, startX + w - 1 - 10, lastY + baseLine/2 );
00915         painter.restore();
00916 #endif
00917     } else if ( format->vAlign() == KoTextFormat::AlignSuperScript ) {
00918             int posY =lastY + baseLine - ( painter.fontMetrics().height() / 2 )-format->offsetFromBaseLine();
00919             //we must move to bottom text because we create
00920             //shadow to 'top'.
00921             int sy = format->shadowY( zh );
00922             if ( sy < 0)
00923                 posY -= sy;
00924         painter.drawText( startX, posY, str, start, len, dir );
00925     } else if ( format->vAlign() == KoTextFormat::AlignSubScript ) {
00926             int posY =lastY + baseLine + ( painter.fontMetrics().height() / 6 )-format->offsetFromBaseLine();
00927             //we must move to bottom text because we create
00928             //shadow to 'top'.
00929             int sy = format->shadowY( zh );
00930             if ( sy < 0)
00931                 posY -= sy;
00932         painter.drawText( startX, posY, str, start, len, dir );
00933     }
00934     }
00935     if ( str[ start ] == '\t' && m_tabCache.contains( start ) ) {
00936     painter.save();
00937     KoZoomHandler * zh = textDocument()->paintingZoomHandler();
00938     const KoTabulator& tab = m_layout.tabList()[ m_tabCache[ start ] ];
00939     int lineWidth = zh->zoomItY( tab.ptWidth );
00940     switch ( tab.filling ) {
00941         case TF_DOTS:
00942         painter.setPen( QPen( textColor, lineWidth, Qt::DotLine ) );
00943         painter.drawLine( startX, lastY + baseLine, startX + bw, lastY + baseLine );
00944         break;
00945         case TF_LINE:
00946         painter.setPen( QPen( textColor, lineWidth, Qt::SolidLine ) );
00947         painter.drawLine( startX, lastY + baseLine, startX + bw, lastY + baseLine );
00948             case TF_DASH:
00949         painter.setPen( QPen( textColor, lineWidth, Qt::DashLine ) );
00950         painter.drawLine( startX, lastY + baseLine, startX + bw, lastY + baseLine );
00951         break;
00952             case TF_DASH_DOT:
00953         painter.setPen( QPen( textColor, lineWidth, Qt::DashDotLine ) );
00954         painter.drawLine( startX, lastY + baseLine, startX + bw, lastY + baseLine );
00955         break;
00956             case TF_DASH_DOT_DOT:
00957         painter.setPen( QPen( textColor, lineWidth, Qt::DashDotDotLine ) );
00958         painter.drawLine( startX, lastY + baseLine, startX + bw, lastY + baseLine );
00959         break;
00960 
00961             default:
00962                 break;
00963     }
00964     painter.restore();
00965     }
00966 
00967     if ( start+len < length() && at( start+len )->lineStart )
00968     {
00969 #ifdef DEBUG_PAINT
00970         //kdDebug(32500) << "we are drawing the end of line " << line << ". Auto-hyphenated: " << lineHyphenated( line ) << endl;
00971 #endif
00972         bool drawHyphen = at( start+len-1 )->c.unicode() == 0xad;
00973         drawHyphen = drawHyphen || lineHyphenated( line );
00974         if ( drawHyphen ) {
00975 #ifdef DEBUG_PAINT
00976             kdDebug(32500) << "drawing hyphen at x=" << startX+bw << endl;
00977 #endif
00978             painter.drawText( startX + bw, lastY + baseLine, "-" ); // \xad gives squares with some fonts (!?)
00979         }
00980     }
00981 
00982     // Paint a zigzag line for "wrong" background spellchecking checked words:
00983     if(
00984         painter.device()->devType() != QInternal::Printer &&
00985         format->isMisspelled() &&
00986         !drawingShadow &&
00987         textDocument()->drawingMissingSpellLine() )
00988     {
00989         painter.save();
00990         painter.setPen( QPen( Qt::red, 1 ) );
00991 
00992         // Draw 3 pixel lines with increasing offset and distance 4:
00993         for( int zigzag_line = 0; zigzag_line < 3; ++zigzag_line )
00994         {
00995             for( int zigzag_x = zigzag_line; zigzag_x < bw; zigzag_x += 4 )
00996             {
00997                 painter.drawPoint(
00998                     startX + zigzag_x,
00999                     lastY + baseLine + h/12 - 1 + zigzag_line );
01000             }
01001         }
01002 
01003         // "Double" the pixel number for the middle line:
01004         for( int zigzag_x = 3; zigzag_x < bw; zigzag_x += 4 )
01005         {
01006             painter.drawPoint(
01007                 startX + zigzag_x,
01008                 lastY + baseLine + h/12 );
01009         }
01010 
01011         painter.restore();
01012     }
01013 }
01014 
01015 bool KoTextParag::lineHyphenated( int l ) const
01016 {
01017     if ( l > (int)lineStarts.count() - 1 ) {
01018     kdWarning() << "KoTextParag::lineHyphenated: line " << l << " out of range!" << endl;
01019     return false;
01020     }
01021 
01022     if ( !isValid() )
01023     const_cast<KoTextParag*>(this)->format();
01024 
01025     QMap<int, KoTextParagLineStart*>::ConstIterator it = lineStarts.begin();
01026     while ( l-- > 0 )
01027     ++it;
01028     return ( *it )->hyphenated;
01029 }
01030 
01032 void KoTextParag::drawCursor( QPainter &painter, KoTextCursor *cursor, int curx, int cury, int curh, const QColorGroup &cg )
01033 {
01034     KoZoomHandler * zh = textDocument()->paintingZoomHandler();
01035     int x = zh->layoutUnitToPixelX( curx ) /*+ cursor->parag()->at( cursor->index() )->pixelxadj*/;
01036     //kdDebug(32500) << "  drawCursor: LU: [cur]x=" << curx << ", cury=" << cury << " -> PIX: x=" << x << ", y=" << zh->layoutUnitToPixelY( cury ) << endl;
01037     KoTextParag::drawCursorDefault( painter, cursor, x,
01038                             zh->layoutUnitToPixelY( cury ),
01039                             zh->layoutUnitToPixelY( cury, curh ), cg );
01040 }
01041 
01042 // Reimplemented from KoTextParag
01043 void KoTextParag::copyParagData( KoTextParag *parag )
01044 {
01045     // Style of the previous paragraph
01046     KoStyle * style = parag->style();
01047     // Obey "following style" setting
01048     bool styleApplied = false;
01049     if ( style )
01050     {
01051         KoStyle * newStyle = style->followingStyle();
01052         if ( newStyle && style != newStyle ) // if same style, keep paragraph-specific changes as usual
01053         {
01054             setParagLayout( newStyle->paragLayout() );
01055             KoTextFormat * format = &newStyle->format();
01056             setFormat( format );
01057             format->addRef();
01058             string()->setFormat( 0, format, true ); // prepare format for text insertion
01059             styleApplied = true;
01060         }
01061     }
01062     // This should never happen in KWord, but it happens in KPresenter
01063     //else
01064     //    kdWarning() << "Paragraph has no style " << paragId() << endl;
01065 
01066     // No "following style" setting, or same style -> copy layout & format of previous paragraph
01067     if (!styleApplied)
01068     {
01069         setParagLayout( parag->paragLayout() );
01070         // Remove pagebreak flags from initial parag - they got copied to the new parag
01071         parag->m_layout.pageBreaking &= ~KoParagLayout::HardFrameBreakBefore;
01072         parag->m_layout.pageBreaking &= ~KoParagLayout::HardFrameBreakAfter;
01073         // Remove footnote counter text from second parag
01074         if ( m_layout.counter && m_layout.counter->numbering() == KoParagCounter::NUM_FOOTNOTE )
01075             setNoCounter();
01076         // Do not copy 'restart numbering at this paragraph' option (would be silly)
01077         if ( m_layout.counter )
01078             m_layout.counter->setRestartCounter(false);
01079 
01080         // set parag format to the format of the trailing space of the previous parag
01081         setFormat( parag->at( parag->length()-1 )->format() );
01082         // KoTextCursor::splitAndInsertEmptyParag takes care of setting the format
01083         // for the chars in the new parag
01084     }
01085 
01086     // Note: we don't call the original KoTextParag::copyParagData on purpose.
01087     // We don't want setListStyle to get called - it ruins our stylesheetitems
01088     // And we don't care about copying the stylesheetitems directly,
01089     // applying the parag layout will create them
01090 }
01091 
01092 void KoTextParag::setTabList( const KoTabulatorList &tabList )
01093 {
01094     KoTabulatorList lst( tabList );
01095     m_layout.setTabList( lst );
01096     if ( !tabList.isEmpty() )
01097     {
01098         KoZoomHandler* zh = textDocument()->formattingZoomHandler();
01099         int * tabs = new int[ tabList.count() + 1 ]; // will be deleted by ~KoTextParag
01100         KoTabulatorList::Iterator it = lst.begin();
01101         unsigned int i = 0;
01102         for ( ; it != lst.end() ; ++it, ++i )
01103             tabs[i] = zh->ptToLayoutUnitPixX( (*it).ptPos );
01104         tabs[i] = 0;
01105         assert( i == tabList.count() );
01106         setTabArray( tabs );
01107     } else
01108     {
01109         setTabArray( 0 );
01110     }
01111     invalidate( 0 );
01112 }
01113 
01115 int KoTextParag::nextTab( int chnum, int x )
01116 {
01117     if ( !m_layout.tabList().isEmpty() )
01118     {
01119         // Fetch the zoomed and sorted tab positions from KoTextParag
01120         // We stored them there for faster access
01121         int * tArray = tabArray();
01122         int i = 0;
01123         if ( string()->isRightToLeft() )
01124             i = m_layout.tabList().size() - 1;
01125 
01126         while ( i >= 0 && i < (int)m_layout.tabList().size() ) {
01127             //kdDebug(32500) << "KoTextParag::nextTab tArray[" << i << "]=" << tArray[i] << " type " << m_layout.tabList()[i].type << endl;
01128             int tab = tArray[ i ];
01129             if ( string()->isRightToLeft() )
01130                 tab = rect().width() - tab;
01131 
01132             if ( tab > x ) {
01133                 int type = m_layout.tabList()[i].type;
01134 
01135                 // fix the tab type for right to left text
01136                 if ( string()->isRightToLeft() )
01137                     if ( type == T_RIGHT )
01138                         type = T_LEFT;
01139                     else if ( type == T_LEFT )
01140                         type = T_RIGHT;
01141 
01142                 switch ( type ) {
01143                 case T_RIGHT:
01144                 case T_CENTER:
01145                 {
01146                     // Look for the next tab (or EOL)
01147                     int c = chnum + 1;
01148                     int w = 0;
01149                     while ( c < string()->length() - 1 && string()->at( c ).c != '\t' && string()->at( c ).c != '\n' )
01150                     {
01151                         KoTextStringChar & ch = string()->at( c );
01152                         // Determine char width
01153                         // This must be done in the same way as in KoTextFormatter::format() or there can be different rounding errors.
01154                         if ( ch.isCustom() )
01155                             w += ch.customItem()->width;
01156                         else
01157                         {
01158                             KoTextFormat *charFormat = ch.format();
01159                             int ww = charFormat->charWidth( textDocument()->formattingZoomHandler(), false, &ch, this, c );
01160                             ww = KoTextZoomHandler::ptToLayoutUnitPt( ww );
01161                             w += ww;
01162                         }
01163                         ++c;
01164                     }
01165 
01166                     m_tabCache[chnum] = i;
01167 
01168                     if ( type == T_RIGHT )
01169                         return tab - w;
01170                     else // T_CENTER
01171                         return tab - w/2;
01172                 }
01173                 case T_DEC_PNT:
01174                 {
01175                     // Look for the next tab (or EOL), and for alignChar
01176                     // Default to right-aligned if no decimal point found (behavior from msword)
01177                     int c = chnum + 1;
01178                     int w = 0;
01179                     while ( c < string()->length()-1 && string()->at( c ).c != '\t' && string()->at( c ).c != '\n' )
01180                     {
01181                         KoTextStringChar & ch = string()->at( c );
01182                         if ( ch.c == m_layout.tabList()[i].alignChar )
01183                         {
01184                             if ( string()->isRightToLeft() )
01185                             {
01186                                 w = ch.width /*string()->width( c )*/ / 2; // center around the decimal point
01187                                 ++c;
01188                                 continue;
01189                             }
01190                             else
01191                             {
01192                                 w += ch.width /*string()->width( c )*/ / 2; // center around the decimal point
01193                                 break;
01194                             }
01195                         }
01196 
01197                         // Determine char width
01198                         if ( ch.isCustom() )
01199                             w += ch.customItem()->width;
01200                         else
01201                         {
01202                             KoTextFormat *charFormat = ch.format();
01203                             int ww = charFormat->charWidth( textDocument()->formattingZoomHandler(), false, &ch, this, c );
01204                             ww = KoTextZoomHandler::ptToLayoutUnitPt( ww );
01205                             w += ww;
01206                         }
01207 
01208                         ++c;
01209                     }
01210                     m_tabCache[chnum] = i;
01211                     return tab - w;
01212                 }
01213                 default: // case T_LEFT:
01214                     m_tabCache[chnum] = i;
01215                     return tab;
01216                 }
01217             }
01218             if ( string()->isRightToLeft() )
01219                 --i;
01220             else
01221                 ++i;
01222         }
01223     }
01224     // No tab list, use tab-stop-width. qrichtext.cpp has the code :)
01225     return KoTextParag::nextTabDefault( chnum, x );
01226 }
01227 
01228 void KoTextParag::applyStyle( KoStyle *style )
01229 {
01230     setParagLayout( style->paragLayout() );
01231     KoTextFormat *newFormat = &style->format();
01232     setFormat( 0, string()->length(), newFormat );
01233     setFormat( newFormat );
01234 }
01235 
01236 void KoTextParag::setParagLayout( const KoParagLayout & layout, int flags )
01237 {
01238     //kdDebug(32500) << "KoTextParag::setParagLayout flags=" << flags << endl;
01239     if ( flags & KoParagLayout::Alignment )
01240         setAlign( layout.alignment );
01241     if ( flags & KoParagLayout::Margins )
01242         setMargins( layout.margins );
01243     if ( flags & KoParagLayout::LineSpacing )
01244     {
01245         setLineSpacingType( layout.lineSpacingType );
01246         setLineSpacing( layout.lineSpacingValue() );
01247     }
01248     if ( flags & KoParagLayout::Borders )
01249     {
01250         setLeftBorder( layout.leftBorder );
01251         setRightBorder( layout.rightBorder );
01252         setTopBorder( layout.topBorder );
01253         setBottomBorder( layout.bottomBorder );
01254     }
01255     if ( flags & KoParagLayout::BulletNumber )
01256         setCounter( layout.counter );
01257     if ( flags & KoParagLayout::Tabulator )
01258         setTabList( layout.tabList() );
01259     if ( flags == KoParagLayout::All )
01260     {
01261         setDirection( static_cast<QChar::Direction>(layout.direction) );
01262         // Don't call applyStyle from here, it would overwrite any paragraph-specific settings
01263         setStyle( layout.style );
01264     }
01265 }
01266 
01267 void KoTextParag::setCustomItem( int index, KoTextCustomItem * custom, KoTextFormat * currentFormat )
01268 {
01269     //kdDebug(32500) << "KoTextParag::setCustomItem " << index << "  " << (void*)custom
01270     //               << "  currentFormat=" << (void*)currentFormat << endl;
01271     if ( currentFormat )
01272         setFormat( index, 1, currentFormat );
01273     at( index )->setCustomItem( custom );
01274     //addCustomItem();
01275     document()->registerCustomItem( custom, this );
01276     custom->recalc(); // calc value (e.g. for variables) and set initial size
01277     invalidate( 0 );
01278     setChanged( true );
01279 }
01280 
01281 void KoTextParag::removeCustomItem( int index )
01282 {
01283     Q_ASSERT( at( index )->isCustom() );
01284     KoTextCustomItem * item = at( index )->customItem();
01285     at( index )->loseCustomItem();
01286     //KoTextParag::removeCustomItem();
01287     document()->unregisterCustomItem( item, this );
01288 }
01289 
01290 
01291 int KoTextParag::findCustomItem( const KoTextCustomItem * custom ) const
01292 {
01293     int len = string()->length();
01294     for ( int i = 0; i < len; ++i )
01295     {
01296         KoTextStringChar & ch = string()->at(i);
01297         if ( ch.isCustom() && ch.customItem() == custom )
01298             return i;
01299     }
01300     kdWarning() << "KoTextParag::findCustomItem custom item " << (void*)custom
01301               << " not found in paragraph " << paragId() << endl;
01302     return 0;
01303 }
01304 
01305 #ifndef NDEBUG
01306 void KoTextParag::printRTDebug( int info )
01307 {
01308     kdDebug(32500) << "Paragraph " << this << " (" << paragId() << ") [changed="
01309               << hasChanged() << ", valid=" << isValid()
01310               << ", needsSpellCheck=" << string()->needsSpellCheck()
01311               << ", wasMovedDown=" << wasMovedDown()
01312               // not used << ", lastInFrame=" << isLastInFrame()
01313               << "] ------------------ " << endl;
01314     if ( prev() && prev()->paragId() + 1 != paragId() )
01315         kdWarning() << "  Previous paragraph " << prev() << " has ID " << prev()->paragId() << endl;
01316     if ( next() && next()->paragId() != paragId() + 1 )
01317         kdWarning() << "  Next paragraph " << next() << " has ID " << next()->paragId() << endl;
01318     //if ( !next() )
01319     //    kdDebug(32500) << "  next is 0L" << endl;
01320     /*
01321       static const char * const dm[] = { "DisplayBlock", "DisplayInline", "DisplayListItem", "DisplayNone" };
01322       QPtrVector<QStyleSheetItem> vec = styleSheetItems();
01323       for ( uint i = 0 ; i < vec.size() ; ++i )
01324       {
01325       QStyleSheetItem * item = vec[i];
01326       kdDebug(32500) << "  StyleSheet Item " << item << " '" << item->name() << "'" << endl;
01327       kdDebug(32500) << "        italic=" << item->fontItalic() << " underline=" << item->fontUnderline() << " fontSize=" << item->fontSize() << endl;
01328       kdDebug(32500) << "        align=" << item->alignment() << " leftMargin=" << item->margin(QStyleSheetItem::MarginLeft) << " rightMargin=" << item->margin(QStyleSheetItem::MarginRight) << " topMargin=" << item->margin(QStyleSheetItem::MarginTop) << " bottomMargin=" << item->margin(QStyleSheetItem::MarginBottom) << endl;
01329       kdDebug(32500) << "        displaymode=" << dm[item->displayMode()] << endl;
01330       }*/
01331     kdDebug(32500) << "  Style: " << style() << " " << ( style() ? style()->name().local8Bit().data() : "NO STYLE" ) << endl;
01332     kdDebug(32500) << "  Text: '" << string()->toString() << "'" << endl;
01333     if ( info == 0 ) // paragraph info
01334     {
01335         if ( m_layout.counter )
01336         {
01337             QString additionalInfo;
01338             if ( m_layout.counter->restartCounter() )
01339                 additionalInfo = "[restartCounter]";
01340             static const char * const s_numbering[] = { "List", "Chapter", "None", "Footnote" };
01341             kdDebug(32500) << "  Counter style=" << m_layout.counter->style()
01342                       << " numbering=" << s_numbering[ m_layout.counter->numbering() ]
01343                       << " depth=" << m_layout.counter->depth()
01344                       << " number=" << m_layout.counter->number( this )
01345                       << " text='" << m_layout.counter->text( this ) << "'"
01346                       << " width=" << m_layout.counter->width( this )
01347                       << additionalInfo << endl;
01348         }
01349         static const char * const s_align[] = { "Auto", "Left", "Right", "ERROR", "HCenter", "ERR", "ERR", "ERR", "Justify", };
01350         static const char * const s_linespacing[] = { "Single", "1.5", "2", "custom", "atLeast", "Multiple", "Fixed" };
01351         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" };
01352         kdDebug(32500) << "  align: " << s_align[alignment()] << "  resolveAlignment: " << s_align[resolveAlignment()]
01353                   << "  isRTL:" << string()->isRightToLeft()
01354                   << "  dir: " << s_dir[direction()] << endl;
01355         QRect pixr = pixelRect( textDocument()->paintingZoomHandler() );
01356         kdDebug(32500) << "  rect() : " << DEBUGRECT( rect() )
01357                   << "  pixelRect() : " << DEBUGRECT( pixr ) << endl;
01358         kdDebug(32500) << "  topMargin()=" << topMargin() << " bottomMargin()=" << bottomMargin()
01359                   << " leftMargin()=" << leftMargin() << " firstLineMargin()=" << firstLineMargin()
01360                   << " rightMargin()=" << rightMargin() << endl;
01361         if ( kwLineSpacingType() != KoParagLayout::LS_SINGLE )
01362             kdDebug(32500) << "  linespacing type=" << s_linespacing[ -kwLineSpacingType() ]
01363                            << " value=" << kwLineSpacing() << endl;
01364 
01365         static const char * const tabtype[] = { "T_LEFT", "T_CENTER", "T_RIGHT", "T_DEC_PNT", "error!!!" };
01366         KoTabulatorList tabList = m_layout.tabList();
01367         if ( tabList.isEmpty() ) {
01368             if ( string()->toString().find( '\t' ) != -1 )
01369                 kdDebug(32500) << "Tab width: " << textDocument()->tabStopWidth() << endl;
01370         } else {
01371             KoTabulatorList::Iterator it = tabList.begin();
01372             for ( ; it != tabList.end() ; it++ )
01373                 kdDebug(32500) << "Tab type:" << tabtype[(*it).type] << " at: " << (*it).ptPos << endl;
01374         }
01375     } else if ( info == 1 ) // formatting info
01376     {
01377         kdDebug(32500) << "  Paragraph format=" << paragFormat() << " " << paragFormat()->key()
01378                   << " fontsize:" << dynamic_cast<KoTextFormat *>(paragFormat())->pointSize() << endl;
01379 
01380         for ( int line = 0 ; line < lines(); ++ line ) {
01381             int y, h, baseLine;
01382             lineInfo( line, y, h, baseLine );
01383             int startOfLine;
01384             lineStartOfLine( line, &startOfLine );
01385             kdDebug(32500) << "  Line " << line << " y=" << y << " height=" << h << " baseLine=" << baseLine << " startOfLine(index)=" << startOfLine << endl;
01386         }
01387         kdDebug(32500) << endl;
01388         KoTextString * s = string();
01389         int lastX = 0; // pixels
01390         int lastW = 0; // pixels
01391         for ( int i = 0 ; i < s->length() ; ++i )
01392         {
01393             KoTextStringChar & ch = s->at(i);
01394             int pixelx =  textDocument()->formattingZoomHandler()->layoutUnitToPixelX( ch.x )
01395                           + ch.pixelxadj;
01396             if ( ch.lineStart )
01397                 kdDebug(32500) << "LINESTART" << endl;
01398             kdDebug(32500) << i << ": '" << QString(ch.c) << "' (" << ch.c.unicode() << ")"
01399                       << " x(LU)=" << ch.x
01400                       << " w(LU)=" << ch.width//s->width(i)
01401                       << " x(PIX)=" << pixelx
01402                       << " (xadj=" << + ch.pixelxadj << ")"
01403                       << " w(PIX)=" << ch.pixelwidth
01404                       << " height=" << ch.height()
01405                 //      << " format=" << ch.format()
01406                       << " \"" << ch.format()->key() << "\" "
01407                 //<< " fontsize:" << dynamic_cast<KoTextFormat *>(ch.format())->pointSize()
01408                       << endl;
01409 
01410         // Check that the format is in the collection (i.e. its defaultFormat or in the dict)
01411         if ( ch.format() != textDocument()->formatCollection()->defaultFormat() )
01412                 Q_ASSERT( textDocument()->formatCollection()->dict()[ch.format()->key()] );
01413 
01414             if ( !string()->isBidi() && !ch.lineStart )
01415                 Q_ASSERT( lastX + lastW == pixelx ); // looks like some rounding problem with justified spaces
01416             lastX = pixelx;
01417             lastW = ch.pixelwidth;
01418             if ( ch.isCustom() )
01419             {
01420                 KoTextCustomItem * item = ch.customItem();
01421                 kdDebug(32500) << " - custom item " << item
01422                           << " ownline=" << item->ownLine()
01423                           << " size=" << item->width << "x" << item->height
01424                           << endl;
01425             }
01426         }
01427     }
01428 }
01429 #endif
01430 
01431 void KoTextParag::drawFontEffects( QPainter * p, KoTextFormat *format, KoZoomHandler *zh, QFont font, const QColor & color, int startX, int baseLine, int bw, int lastY, int /*h*/, QChar firstChar )
01432 {
01433     // This is about drawing underlines and strikeouts
01434     // So abort immediately if there's none to draw.
01435     if ( !format->isStrikedOrUnderlined() )
01436         return;
01437     //kdDebug(32500) << "drawFontEffects wordByWord=" << format->wordByWord() <<
01438     //    " firstChar='" << QString(firstChar) << "'" << endl;
01439     // paintLines ensures that we're called word by word if wordByWord is true.
01440     if ( format->wordByWord() && firstChar.isSpace() )
01441         return;
01442 
01443     double dimd;
01444     int y;
01445     int offset = 0;
01446     if (format->vAlign() == KoTextFormat::AlignSubScript )
01447         offset = p->fontMetrics().height() / 6;
01448     else if (format->vAlign() == KoTextFormat::AlignSuperScript )
01449         offset = -p->fontMetrics().height() / 2;
01450 
01451     dimd = KoBorder::zoomWidthY( format->underLineWidth(), zh, 1 );
01452     if((format->vAlign() == KoTextFormat::AlignSuperScript) ||
01453     (format->vAlign() == KoTextFormat::AlignSubScript ))
01454     dimd*=format->relativeTextSize();
01455     y = lastY + baseLine + offset - format->offsetFromBaseLine();
01456 
01457     if ( format->doubleUnderline())
01458     {
01459         QColor col = format->textUnderlineColor().isValid() ? format->textUnderlineColor(): color ;
01460     int dim=static_cast<int>(0.75*dimd);
01461     dim=dim?dim:1; //width of line should be at least 1
01462         p->save();
01463 
01464         switch( format->underlineStyle())
01465         {
01466         case KoTextFormat::U_SOLID:
01467             p->setPen( QPen( col, dim, Qt::SolidLine ) );
01468             break;
01469         case KoTextFormat::U_DASH:
01470             p->setPen( QPen( col, dim, Qt::DashLine ) );
01471             break;
01472         case KoTextFormat::U_DOT:
01473             p->setPen( QPen( col, dim, Qt::DotLine ) );
01474             break;
01475         case KoTextFormat::U_DASH_DOT:
01476             p->setPen( QPen( col, dim, Qt::DashDotLine ) );
01477             break;
01478         case KoTextFormat::U_DASH_DOT_DOT:
01479             p->setPen( QPen( col, dim, Qt::DashDotDotLine ) );
01480             break;
01481         default:
01482             p->setPen( QPen( color, dim, Qt::SolidLine ) );
01483         }
01484 
01485         y += static_cast<int>(1.125*dimd); // slightly under the baseline if possible
01486         p->drawLine( startX, y, startX + bw, y );
01487         y += static_cast<int>(1.5*dimd);
01488         p->drawLine( startX, y, startX + bw, y );
01489         p->restore();
01490         if ( font.underline() ) { // can this happen?
01491             font.setUnderline( FALSE );
01492             p->setFont( font );
01493         }
01494     }
01495     else if ( format->underline() ||
01496                 format->underlineType() == KoTextFormat::U_SIMPLE_BOLD)
01497     {
01498 
01499         QColor col = format->textUnderlineColor().isValid() ? format->textUnderlineColor(): color ;
01500         p->save();
01501     int dim=(format->underlineType() == KoTextFormat::U_SIMPLE_BOLD)?static_cast<int>(2*dimd):static_cast<int>(dimd);
01502     dim=dim?dim:1; //width of line should be at least 1
01503         y += static_cast<int>(1.875*dimd);
01504 
01505         switch( format->underlineStyle() )
01506         {
01507         case KoTextFormat::U_SOLID:
01508             p->setPen( QPen( col, dim, Qt::SolidLine ) );
01509             break;
01510         case KoTextFormat::U_DASH:
01511             p->setPen( QPen( col, dim, Qt::DashLine ) );
01512             break;
01513         case KoTextFormat::U_DOT:
01514             p->setPen( QPen( col, dim, Qt::DotLine ) );
01515             break;
01516         case KoTextFormat::U_DASH_DOT:
01517             p->setPen( QPen( col, dim, Qt::DashDotLine ) );
01518             break;
01519         case KoTextFormat::U_DASH_DOT_DOT:
01520             p->setPen( QPen( col, dim, Qt::DashDotDotLine ) );
01521             break;
01522         default:
01523             p->setPen( QPen( col, dim, Qt::SolidLine ) );
01524         }
01525 
01526         p->drawLine( startX, y, startX + bw, y );
01527         p->restore();
01528         font.setUnderline( FALSE );
01529         p->setFont( font );
01530     }
01531     else if ( format->waveUnderline() )
01532     {
01533     int dim=static_cast<int>(dimd);
01534     dim=dim?dim:1; //width of line should be at least 1
01535         y += dim;
01536         QColor col = format->textUnderlineColor().isValid() ? format->textUnderlineColor(): color ;
01537         p->save();
01538     int offset = 2 * dim;
01539     QPen pen(col, dim, Qt::SolidLine);
01540     pen.setCapStyle(Qt::RoundCap);
01541     p->setPen(pen);
01542     Q_ASSERT(offset);
01543     double anc=acos(1.0-2*(static_cast<double>(offset-(startX)%offset)/static_cast<double>(offset)))/3.1415*180;
01544     int pos=1;
01545     //set starting position
01546     if(2*((startX/offset)/2)==startX/offset)
01547         pos*=-1;
01548     //draw first part of wave
01549     p->drawArc( (startX/offset)*offset, y, offset, offset, 0, -qRound(pos*anc*16) );
01550         //now the main part
01551     int zigzag_x = (startX/offset+1)*offset;
01552     for ( ; zigzag_x + offset <= bw+startX; zigzag_x += offset)
01553         {
01554         p->drawArc( zigzag_x, y, offset, offset, 0, pos*180*16 );
01555         pos*=-1;
01556         }
01557     //and here we finish
01558     anc=acos(1.0-2*(static_cast<double>((startX+bw)%offset)/static_cast<double>(offset)))/3.1415*180;
01559     p->drawArc( zigzag_x, y, offset, offset, 180*16, -qRound(pos*anc*16) );
01560     p->restore();
01561         font.setUnderline( FALSE );
01562         p->setFont( font );
01563     }
01564 
01565     dimd = KoBorder::zoomWidthY( static_cast<double>(format->pointSize())/18.0, zh, 1 );
01566     if((format->vAlign() == KoTextFormat::AlignSuperScript) ||
01567     (format->vAlign() == KoTextFormat::AlignSubScript ))
01568     dimd*=format->relativeTextSize();
01569     y = lastY + baseLine + offset - format->offsetFromBaseLine();
01570 
01571     if ( format->strikeOutType() == KoTextFormat::S_SIMPLE
01572          || format->strikeOutType() == KoTextFormat::S_SIMPLE_BOLD)
01573     {
01574         unsigned int dim = (format->strikeOutType() == KoTextFormat::S_SIMPLE_BOLD)? static_cast<int>(2*dimd) : static_cast<int>(dimd);
01575         p->save();
01576 
01577         switch( format->strikeOutStyle() )
01578         {
01579         case KoTextFormat::S_SOLID:
01580             p->setPen( QPen( color, dim, Qt::SolidLine ) );
01581             break;
01582         case KoTextFormat::S_DASH:
01583             p->setPen( QPen( color, dim, Qt::DashLine ) );
01584             break;
01585         case KoTextFormat::S_DOT:
01586             p->setPen( QPen( color, dim, Qt::DotLine ) );
01587             break;
01588         case KoTextFormat::S_DASH_DOT:
01589             p->setPen( QPen( color, dim, Qt::DashDotLine ) );
01590             break;
01591         case KoTextFormat::S_DASH_DOT_DOT:
01592             p->setPen( QPen( color, dim, Qt::DashDotDotLine ) );
01593             break;
01594         default:
01595             p->setPen( QPen( color, dim, Qt::SolidLine ) );
01596         }
01597 
01598         y -= static_cast<int>(5*dimd);
01599         p->drawLine( startX, y, startX + bw, y );
01600         p->restore();
01601         font.setStrikeOut( FALSE );
01602         p->setFont( font );
01603     }
01604     else if ( format->strikeOutType() == KoTextFormat::S_DOUBLE )
01605     {
01606         unsigned int dim = static_cast<int>(dimd);
01607         p->save();
01608 
01609         switch( format->strikeOutStyle() )
01610         {
01611         case KoTextFormat::S_SOLID:
01612             p->setPen( QPen( color, dim, Qt::SolidLine ) );
01613             break;
01614         case KoTextFormat::S_DASH:
01615             p->setPen( QPen( color, dim, Qt::DashLine ) );
01616             break;
01617         case KoTextFormat::S_DOT:
01618             p->setPen( QPen( color, dim, Qt::DotLine ) );
01619             break;
01620         case KoTextFormat::S_DASH_DOT:
01621             p->setPen( QPen( color, dim, Qt::DashDotLine ) );
01622             break;
01623         case KoTextFormat::S_DASH_DOT_DOT:
01624             p->setPen( QPen( color, dim, Qt::DashDotDotLine ) );
01625             break;
01626         default:
01627             p->setPen( QPen( color, dim, Qt::SolidLine ) );
01628         }
01629 
01630     y -= static_cast<int>(4*dimd);
01631         p->drawLine( startX, y, startX + bw, y);
01632     y -= static_cast<int>(2*dimd);
01633         p->drawLine( startX, y, startX + bw, y);
01634         p->restore();
01635         font.setStrikeOut( FALSE );
01636         p->setFont( font );
01637     }
01638 
01639 }
01640 
01641 // ### is this method correct for RTL text?
01642 QString KoTextParag::toString( int from, int length ) const
01643 {
01644     QString str;
01645     if ( from == 0 && m_layout.counter )
01646         str += m_layout.counter->text( this ) + ' ';
01647     if ( length == -1 )
01648         length = this->length() - from;
01649     for ( int i = from ; i < (length+from) ; ++i )
01650     {
01651         KoTextStringChar *ch = at( i );
01652         if ( ch->isCustom() )
01653         {
01654             KoVariable * var = dynamic_cast<KoVariable *>(ch->customItem());
01655             if ( var )
01656                 str += var->text(true);
01657             else //frame inline
01658                 str +=' ';
01659         }
01660         else
01661             str += ch->c;
01662     }
01663     return str;
01664 }
01665 
01666 int KoTextParag::documentWidth() const
01667 {
01668     return doc ? doc->width() : 0; //docRect.width();
01669 }
01670 
01671 //int KoTextParag::documentVisibleWidth() const
01672 //{
01673 //    return doc ? doc->visibleWidth() : 0; //docRect.width();
01674 //}
01675 
01676 int KoTextParag::documentX() const
01677 {
01678     return doc ? doc->x() : 0; //docRect.x();
01679 }
01680 
01681 int KoTextParag::documentY() const
01682 {
01683     return doc ? doc->y() : 0; //docRect.y();
01684 }
01685 
01686 void KoTextParag::fixParagWidth( bool viewFormattingChars )
01687 {
01688     // Fixing the parag rect for the formatting chars (only CR here, KWord handles framebreak).
01689     if ( viewFormattingChars && lineStartList().count() == 1 ) // don't use lines() here, parag not formatted yet
01690     {
01691         KoTextFormat * lastFormat = at( length() - 1 )->format();
01692         setWidth( QMIN( rect().width() + lastFormat->width('x'), doc->width() ) );
01693     }
01694     // Warning, if adding anything else here, adjust KWTextFrameSet::fixParagWidth
01695 }
01696 
01697 // Called by KoTextParag::drawParagString - all params are in pixel coordinates
01698 void KoTextParag::drawFormattingChars( QPainter &painter, int start, int len,
01699                                        int lastY_pix, int baseLine_pix, int h_pix, // in pixels
01700                                        bool /*drawSelections*/,
01701                                        KoTextFormat * /*lastFormat*/, const QMemArray<int> &/*selectionStarts*/,
01702                                        const QMemArray<int> &/*selectionEnds*/, const QColorGroup &cg,
01703                                        bool rightToLeft, int /*line*/, KoZoomHandler* zh,
01704                                        int whichFormattingChars )
01705 {
01706     if ( !whichFormattingChars )
01707         return;
01708     painter.save();
01709     QPen pen( cg.color( QColorGroup::Highlight ) );
01710     painter.setPen( pen );
01711     //kdDebug() << "KWTextParag::drawFormattingChars start=" << start << " len=" << len << " length=" << length() << endl;
01712     if ( start + len == length() && ( whichFormattingChars & FormattingEndParag ) )
01713     {
01714         // drawing the end of the parag
01715         KoTextStringChar &ch = string()->at( length() - 1 );
01716         KoTextFormat* format = static_cast<KoTextFormat *>( ch.format() );
01717         int w = format->charWidth( zh, true, &ch, this, 'X' );
01718         int size = QMIN( w, h_pix * 3 / 4 );
01719         // x,y is the bottom right corner of the ¶
01720         //kdDebug() << "startX=" << startX << " bw=" << bw << " w=" << w << endl;
01721         int x;
01722         if ( rightToLeft )
01723             x = zh->layoutUnitToPixelX( ch.x ) /*+ ch.pixelxadj*/ + ch.pixelwidth - 1;
01724         else
01725             x = zh->layoutUnitToPixelX( ch.x ) /*+ ch.pixelxadj*/ + w;
01726         int y = lastY_pix + baseLine_pix;
01727         //kdDebug() << "KWTextParag::drawFormattingChars drawing CR at " << x << "," << y << endl;
01728         painter.drawLine( (int)(x - size * 0.2), y - size, (int)(x - size * 0.2), y );
01729         painter.drawLine( (int)(x - size * 0.5), y - size, (int)(x - size * 0.5), y );
01730         painter.drawLine( x, y, (int)(x - size * 0.7), y );
01731         painter.drawLine( x, y - size, (int)(x - size * 0.5), y - size);
01732         painter.drawArc( x - size, y - size, size, (int)(size / 2), -90*16, -180*16 );
01733 #ifdef DEBUG_FORMATTING
01734         painter.setPen( Qt::blue );
01735         painter.drawRect( zh->layoutUnitToPixelX( ch.x ) /*+ ch.pixelxadj*/ - 1, lastY_pix, ch.pixelwidth, baseLine_pix );
01736         QPen pen( cg.color( QColorGroup::Highlight ) );
01737         painter.setPen( pen );
01738 #endif
01739     }
01740 
01741     // Now draw spaces, tabs and newlines
01742     if ( (whichFormattingChars & FormattingSpace) ||
01743          (whichFormattingChars & FormattingTabs) ||
01744          (whichFormattingChars & FormattingBreak) )
01745     {
01746         int end = QMIN( start + len, length() - 1 ); // don't look at the trailing space
01747         for ( int i = start ; i < end ; ++i )
01748         {
01749             KoTextStringChar &ch = string()->at(i);
01750 #ifdef DEBUG_FORMATTING
01751             painter.setPen( (i % 2)? Qt::red: Qt::green );
01752             painter.drawRect( zh->layoutUnitToPixelX( ch.x ) /*+ ch.pixelxadj*/ - 1, lastY_pix, ch.pixelwidth, baseLine_pix );
01753             QPen pen( cg.color( QColorGroup::Highlight ) );
01754             painter.setPen( pen );
01755 #endif
01756             if ( ch.isCustom() )
01757                 continue;
01758             if ( (ch.c == ' ' || ch.c.unicode() == 0x00a0U)
01759                  && (whichFormattingChars & FormattingSpace))
01760             {
01761                 // Don't use ch.pixelwidth here. We want a square with
01762                 // the same size for all spaces, even the justified ones
01763                 int w = zh->layoutUnitToPixelX( ch.format()->width( ' ' ) );
01764                 int height = zh->layoutUnitToPixelY( ch.ascent() );
01765                 int size = QMAX( 2, QMIN( w/2, height/3 ) ); // Enfore that it's a square, and that it's visible
01766                 int x = zh->layoutUnitToPixelX( ch.x ); // + ch.pixelxadj;
01767                 QRect spcRect( x + (ch.pixelwidth - size) / 2, lastY_pix + baseLine_pix - (height - size) / 2, size, size );
01768                 if ( ch.c == ' ' )
01769                     painter.drawRect( spcRect );
01770                 else // nbsp
01771                     painter.fillRect( spcRect, pen.color() );
01772             }
01773             else if ( ch.c == '\t' && (whichFormattingChars & FormattingTabs) )
01774             {
01775                 /*KoTextStringChar &nextch = string()->at(i+1);
01776                   int nextx = (nextch.x > ch.x) ? nextch.x : rect().width();
01777                   //kdDebug() << "tab x=" << ch.x << " nextch.x=" << nextch.x
01778                   //          << " nextx=" << nextx << " startX=" << startX << " bw=" << bw << endl;
01779                   int availWidth = nextx - ch.x - 1;
01780                   availWidth=zh->layoutUnitToPixelX(availWidth);*/
01781 
01782                 int availWidth = ch.pixelwidth;
01783 
01784                 KoTextFormat* format = ch.format();
01785                 int x = zh->layoutUnitToPixelX( ch.x ) /*+ ch.pixelxadj*/ + availWidth / 2;
01786                 int charWidth = format->screenFontMetrics( zh ).width( 'W' );
01787                 int size = QMIN( availWidth, charWidth ) / 2 ; // actually the half size
01788                 int y = lastY_pix + baseLine_pix - zh->layoutUnitToPixelY( ch.ascent()/2 );
01789                 int arrowsize = zh->zoomItY( 2 );
01790                 painter.drawLine( x - size, y, x + size, y );
01791                 if ( rightToLeft )
01792                 {
01793                     painter.drawLine( x - size, y, x - size + arrowsize, y - arrowsize );
01794                     painter.drawLine( x - size, y, x - size + arrowsize, y + arrowsize );
01795                 }
01796                 else
01797                 {
01798                     painter.drawLine( x + size, y, x + size - arrowsize, y - arrowsize );
01799                     painter.drawLine( x + size, y, x + size - arrowsize, y + arrowsize );
01800                 }
01801             }
01802             else if ( ch.c == '\n' && (whichFormattingChars & FormattingBreak) )
01803             {
01804                 // draw line break
01805                 KoTextFormat* format = static_cast<KoTextFormat *>( ch.format() );
01806                 int w = format->charWidth( zh, true, &ch, this, 'X' );
01807                 int size = QMIN( w, h_pix * 3 / 4 );
01808                 int arrowsize = zh->zoomItY( 2 );
01809                 // x,y is the bottom right corner of the reversed L
01810                 //kdDebug() << "startX=" << startX << " bw=" << bw << " w=" << w << endl;
01811                 int y = lastY_pix + baseLine_pix - arrowsize;
01812                 //kdDebug() << "KWTextParag::drawFormattingChars drawing Line Break at " << x << "," << y << endl;
01813                 if ( rightToLeft )
01814                 {
01815                     int x = zh->layoutUnitToPixelX( ch.x ) /*+ ch.pixelxadj*/ + ch.pixelwidth - 1;
01816                     painter.drawLine( x - size, y - size, x - size, y );
01817                     painter.drawLine( x - size, y, (int)(x - size * 0.3), y );
01818                     // Now the arrow
01819                     painter.drawLine( (int)(x - size * 0.3), y, (int)(x - size * 0.3 - arrowsize), y - arrowsize );
01820                     painter.drawLine( (int)(x - size * 0.3), y, (int)(x - size * 0.3 - arrowsize), y + arrowsize );
01821                 }
01822                 else
01823                 {
01824                     int x = zh->layoutUnitToPixelX( ch.x ) /*+ ch.pixelxadj*/ + w - 1;
01825                     painter.drawLine( x, y - size, x, y );
01826                     painter.drawLine( x, y, (int)(x - size * 0.7), y );
01827                     // Now the arrow
01828                     painter.drawLine( (int)(x - size * 0.7), y, (int)(x - size * 0.7 + arrowsize), y - arrowsize );
01829                     painter.drawLine( (int)(x - size * 0.7), y, (int)(x - size * 0.7 + arrowsize), y + arrowsize );
01830                 }
01831             }
01832         }
01833         painter.restore();
01834     }
01835 }
KDE Logo
This file is part of the documentation for lib Library Version 1.3.5.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Nov 17 13:19:26 2004 by doxygen 1.3.5 written by Dimitri van Heesch, © 1997-2003