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 06:54:19 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003