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