KD Chart 2 [rev.2.4]
|
00001 /**************************************************************************** 00002 ** Copyright (C) 2001-2012 Klaralvdalens Datakonsult AB. All rights reserved. 00003 ** 00004 ** This file is part of the KD Chart library. 00005 ** 00006 ** Licensees holding valid commercial KD Chart licenses may use this file in 00007 ** accordance with the KD Chart Commercial License Agreement provided with 00008 ** the Software. 00009 ** 00010 ** 00011 ** This file may be distributed and/or modified under the terms of the 00012 ** GNU General Public License version 2 and version 3 as published by the 00013 ** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. 00014 ** 00015 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE 00016 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 00017 ** 00018 ** Contact info@kdab.com if any conditions of this licensing are not 00019 ** clear to you. 00020 ** 00021 **********************************************************************/ 00022 00023 #include "KDChartLayoutItems.h" 00024 #include "KDTextDocument.h" 00025 #include "KDChartAbstractArea.h" 00026 #include "KDChartAbstractDiagram.h" 00027 #include "KDChartBackgroundAttributes.h" 00028 #include "KDChartFrameAttributes.h" 00029 #include "KDChartPaintContext.h" 00030 #include "KDChartPainterSaver_p.h" 00031 #include "KDChartPrintingParameters.h" 00032 #include <QTextCursor> 00033 #include <QTextBlockFormat> 00034 #include <QTextDocumentFragment> 00035 #include <QAbstractTextDocumentLayout> 00036 #include <QLayout> 00037 #include <QPainter> 00038 #include <QDebug> 00039 #include <QCoreApplication> 00040 #include <QApplication> 00041 #include <QStringList> 00042 #include <QStyle> 00043 00044 #include <KDABLibFakes> 00045 00046 #include <math.h> 00047 00048 #define PI 3.141592653589793 00049 00050 00051 00052 //#define DEBUG_ITEMS_PAINT 00053 00062 void KDChart::AbstractLayoutItem::setParentWidget( QWidget* widget ) 00063 { 00064 mParent = widget; 00065 } 00066 00067 void KDChart::AbstractLayoutItem::paintAll( QPainter& painter ) 00068 { 00069 paint( &painter ); 00070 } 00071 00075 void KDChart::AbstractLayoutItem::paintCtx( PaintContext* context ) 00076 { 00077 if( context ) 00078 paint( context->painter() ); 00079 } 00080 00084 void KDChart::AbstractLayoutItem::sizeHintChanged()const 00085 { 00086 // This is exactly like what QWidget::updateGeometry does. 00087 // qDebug("KDChart::AbstractLayoutItem::sizeHintChanged() called"); 00088 if( mParent ) { 00089 if ( mParent->layout() ) 00090 mParent->layout()->invalidate(); 00091 else 00092 QApplication::postEvent( mParent, new QEvent( QEvent::LayoutRequest ) ); 00093 } 00094 } 00095 00096 KDChart::TextBubbleLayoutItem::TextBubbleLayoutItem( const QString& text, 00097 const KDChart::TextAttributes& attributes, 00098 const QObject* area, 00099 KDChartEnums::MeasureOrientation orientation, 00100 Qt::Alignment alignment ) 00101 : AbstractLayoutItem( alignment ), 00102 m_text( new TextLayoutItem( text, attributes, area, orientation, alignment ) ) 00103 { 00104 } 00105 00106 KDChart::TextBubbleLayoutItem::TextBubbleLayoutItem() 00107 : AbstractLayoutItem( Qt::AlignLeft ), 00108 m_text( new TextLayoutItem() ) 00109 { 00110 } 00111 00112 KDChart::TextBubbleLayoutItem::~TextBubbleLayoutItem() 00113 { 00114 delete m_text; 00115 } 00116 00117 void KDChart::TextBubbleLayoutItem::setAutoReferenceArea( const QObject* area ) 00118 { 00119 m_text->setAutoReferenceArea( area ); 00120 } 00121 00122 const QObject* KDChart::TextBubbleLayoutItem::autoReferenceArea() const 00123 { 00124 return m_text->autoReferenceArea(); 00125 } 00126 00127 void KDChart::TextBubbleLayoutItem::setText( const QString& text ) 00128 { 00129 m_text->setText( text ); 00130 } 00131 00132 QString KDChart::TextBubbleLayoutItem::text() const 00133 { 00134 return m_text->text(); 00135 } 00136 00137 void KDChart::TextBubbleLayoutItem::setTextAttributes( const TextAttributes& a ) 00138 { 00139 m_text->setTextAttributes( a ); 00140 } 00141 00142 KDChart::TextAttributes KDChart::TextBubbleLayoutItem::textAttributes() const 00143 { 00144 return m_text->textAttributes(); 00145 } 00146 00147 bool KDChart::TextBubbleLayoutItem::isEmpty() const 00148 { 00149 return m_text->isEmpty(); 00150 } 00151 00152 Qt::Orientations KDChart::TextBubbleLayoutItem::expandingDirections() const 00153 { 00154 return m_text->expandingDirections(); 00155 } 00156 00157 QSize KDChart::TextBubbleLayoutItem::maximumSize() const 00158 { 00159 const int border = borderWidth(); 00160 return m_text->maximumSize() + QSize( 2 * border, 2 * border ); 00161 } 00162 00163 QSize KDChart::TextBubbleLayoutItem::minimumSize() const 00164 { 00165 const int border = borderWidth(); 00166 return m_text->minimumSize() + QSize( 2 * border, 2 * border ); 00167 } 00168 00169 QSize KDChart::TextBubbleLayoutItem::sizeHint() const 00170 { 00171 const int border = borderWidth(); 00172 return m_text->sizeHint() + QSize( 2 * border, 2 * border ); 00173 } 00174 00175 void KDChart::TextBubbleLayoutItem::setGeometry( const QRect& r ) 00176 { 00177 const int border = borderWidth(); 00178 m_text->setGeometry( r.adjusted( border, border, -border, -border ) ); 00179 } 00180 00181 QRect KDChart::TextBubbleLayoutItem::geometry() const 00182 { 00183 const int border = borderWidth(); 00184 return m_text->geometry().adjusted( -border, -border, border, border ); 00185 } 00186 00187 void KDChart::TextBubbleLayoutItem::paint( QPainter* painter ) 00188 { 00189 const QPen oldPen = painter->pen(); 00190 const QBrush oldBrush = painter->brush(); 00191 painter->setPen( Qt::black ); 00192 painter->setBrush( QColor( 255, 255, 220 ) ); 00193 painter->drawRoundRect( geometry(), 10 ); 00194 painter->setPen( oldPen ); 00195 painter->setBrush( oldBrush ); 00196 m_text->paint( painter ); 00197 } 00198 00199 int KDChart::TextBubbleLayoutItem::borderWidth() const 00200 { 00201 return 1; 00202 } 00203 00204 KDChart::TextLayoutItem::TextLayoutItem( const QString& text, 00205 const KDChart::TextAttributes& attributes, 00206 const QObject* area, 00207 KDChartEnums::MeasureOrientation orientation, 00208 Qt::Alignment alignment ) 00209 : AbstractLayoutItem( alignment ) 00210 , mText( text ) 00211 , mTextAlignment( alignment ) 00212 , mAttributes( attributes ) 00213 , mAutoReferenceArea( area ) 00214 , mAutoReferenceOrientation( orientation ) 00215 , cachedSizeHint() // default this to invalid to force just-in-time calculation before first use of sizeHint() 00216 , cachedFontSize( 0.0 ) 00217 , cachedFont( mAttributes.font() ) 00218 { 00219 } 00220 00221 KDChart::TextLayoutItem::TextLayoutItem() 00222 : AbstractLayoutItem( Qt::AlignLeft ) 00223 , mText() 00224 , mTextAlignment( Qt::AlignLeft ) 00225 , mAttributes() 00226 , mAutoReferenceArea( 0 ) 00227 , mAutoReferenceOrientation( KDChartEnums::MeasureOrientationHorizontal ) 00228 , cachedSizeHint() // default this to invalid to force just-in-time calculation before first use of sizeHint() 00229 , cachedFontSize( 0.0 ) 00230 , cachedFont( mAttributes.font() ) 00231 { 00232 00233 } 00234 00235 void KDChart::TextLayoutItem::setAutoReferenceArea( const QObject* area ) 00236 { 00237 mAutoReferenceArea = area; 00238 cachedSizeHint = QSize(); 00239 sizeHint(); 00240 } 00241 00242 const QObject* KDChart::TextLayoutItem::autoReferenceArea() const 00243 { 00244 return mAutoReferenceArea; 00245 } 00246 00247 void KDChart::TextLayoutItem::setText(const QString & text) 00248 { 00249 mText = text; 00250 cachedSizeHint = QSize(); 00251 sizeHint(); 00252 if( mParent ) 00253 mParent->update(); 00254 } 00255 00256 QString KDChart::TextLayoutItem::text() const 00257 { 00258 return mText; 00259 } 00260 00261 void KDChart::TextLayoutItem::setTextAlignment( Qt::Alignment alignment) 00262 { 00263 if( mTextAlignment == alignment ) 00264 return; 00265 mTextAlignment = alignment; 00266 if( mParent ) 00267 mParent->update(); 00268 } 00269 00270 Qt::Alignment KDChart::TextLayoutItem::textAlignment() const 00271 { 00272 return mTextAlignment; 00273 } 00274 00280 void KDChart::TextLayoutItem::setTextAttributes( const TextAttributes &a ) 00281 { 00282 mAttributes = a; 00283 cachedFont = a.font(); 00284 cachedSizeHint = QSize(); // invalidate size hint 00285 sizeHint(); 00286 if( mParent ) 00287 mParent->update(); 00288 } 00289 00295 KDChart::TextAttributes KDChart::TextLayoutItem::textAttributes() const 00296 { 00297 return mAttributes; 00298 } 00299 00300 00301 Qt::Orientations KDChart::TextLayoutItem::expandingDirections() const 00302 { 00303 return 0; // Grow neither vertically nor horizontally 00304 } 00305 00306 QRect KDChart::TextLayoutItem::geometry() const 00307 { 00308 return mRect; 00309 } 00310 00311 bool KDChart::TextLayoutItem::isEmpty() const 00312 { 00313 return false; // never empty, otherwise the layout item would not exist 00314 } 00315 00316 QSize KDChart::TextLayoutItem::maximumSize() const 00317 { 00318 return sizeHint(); // PENDING(kalle) Review, quite inflexible 00319 } 00320 00321 QSize KDChart::TextLayoutItem::minimumSize() const 00322 { 00323 return sizeHint(); // PENDING(kalle) Review, quite inflexible 00324 } 00325 00326 void KDChart::TextLayoutItem::setGeometry( const QRect& r ) 00327 { 00328 mRect = r; 00329 } 00330 00331 QPointF rotatedPoint( const QPointF& pt, qreal rotation, const QPointF& center ) 00332 { 00333 const qreal angle = PI * rotation / 180.0; 00334 const qreal cosAngle = cos( angle ); 00335 const qreal sinAngle = -sin( angle ); 00336 return QPointF( 00337 (cosAngle * ( pt.x() - center.x() ) + sinAngle * ( pt.y() - center.y() ) ), 00338 (cosAngle * ( pt.y() - center.y() ) - sinAngle * ( pt.x() - center.x() ) ) ) + center; 00339 } 00340 00341 00342 QRectF rotatedRect( const QRectF& oldRect, qreal angleInt, const QPointF& center ) 00343 { 00344 const QRect rect( oldRect.translated( center ).toRect() ); 00345 const qreal angle = PI * angleInt / 180.0; 00346 const qreal cosAngle = cos( angle ); 00347 const qreal sinAngle = sin( angle ); 00348 QMatrix rotationMatrix(cosAngle, sinAngle, -sinAngle, cosAngle, 0, 0); 00349 QPolygon rotPts; 00350 rotPts << rotationMatrix.map(rect.topLeft()) //QPoint(0,0) 00351 << rotationMatrix.map(rect.topRight()) 00352 << rotationMatrix.map(rect.bottomRight()) 00353 << rotationMatrix.map(rect.bottomLeft()); 00354 //<< rotatedPoint(rect.topRight(), angleInt, center).toPoint() 00355 //<< rotatedPoint(rect.bottomRight(), angleInt, center).toPoint() 00356 //<< rotatedPoint(rect.bottomLeft(), angleInt, center).toPoint(); 00357 return rotPts.boundingRect(); 00358 /* 00359 const QPointF topLeft( rotatedPoint( oldRect.topLeft(), angle, center ) ); 00360 const QPointF topRight( rotatedPoint( oldRect.topRight(), angle, center ) ); 00361 const QPointF bottomLeft( rotatedPoint( oldRect.bottomLeft(), angle, center ) ); 00362 const QPointF bottomRight( rotatedPoint( oldRect.bottomRight(), angle, center ) ); 00363 00364 const qreal x = qMin( qMin( topLeft.x(), topRight.x() ), qMin( bottomLeft.x(), topLeft.x() ) ); 00365 const qreal y = qMin( qMin( topLeft.y(), topRight.y() ), qMin( bottomLeft.y(), topLeft.y() ) ); 00366 const qreal width = qMax( qMax( topLeft.x(), topRight.x() ), qMax( bottomLeft.x(), topLeft.x() ) ) - x; 00367 const qreal height = qMax( qMax( topLeft.y(), topRight.y() ), qMax( bottomLeft.y(), topLeft.y() ) ) - y; 00368 00369 return QRectF( x, y, width, height ); 00370 */ 00371 } 00372 00373 /* 00374 QRectF rotatedRect( const QRectF& rect, qreal angle ) 00375 { 00376 const QPointF topLeft( rotatedPoint( rect.topLeft(), angle ) ); 00377 //const QPointF topRight( rotatedPoint( rect.topRight(), angle ) ); 00378 //const QPointF bottomLeft( rotatedPoint( rect.bottomLeft(), angle ) ); 00379 #if 1 00380 const QPointF bottomRight( rotatedPoint( rect.bottomRight(), angle ) ); 00381 const QRectF result( topLeft, QSizeF( bottomRight.x() - topLeft.x(), bottomRight.y() - topLeft.y() ) ); 00382 #else 00383 const QPointF siz( rotatedPoint( QPointF( rect.size().width(), rect.size().height() ), angle ) ); 00384 const QRectF result( 00385 topLeft, 00386 QSizeF( siz.x(), //bottomRight.x() - topLeft.x(), 00387 siz.y() ) ); //bottomRight.y() - topLeft.y() ) ); 00388 //qDebug() << "angle" << angle << "\nbefore:" << rect << "\n after:" << result; 00389 #endif 00390 return result; 00391 }*/ 00392 00393 qreal KDChart::TextLayoutItem::fitFontSizeToGeometry() const 00394 { 00395 QFont f = realFont(); 00396 const qreal origResult = f.pointSizeF(); 00397 qreal result = origResult; 00398 const qreal minSize = mAttributes.minimalFontSize().value(); 00399 const QSize mySize = geometry().size(); 00400 if( mySize.isNull() ) 00401 return result; 00402 00403 const QString t = text(); 00404 QFontMetrics fm( f ); 00405 while( true ) 00406 { 00407 const QSizeF textSize = rotatedRect( fm.boundingRect( t ), mAttributes.rotation() ).normalized().size(); 00408 00409 if( textSize.height() <= mySize.height() && textSize.width() <= mySize.width() ) 00410 return result; 00411 00412 result -= 0.5; 00413 if ( minSize > 0 && result < minSize ) 00414 return result += 0.5; 00415 if( result <= 0.0 ) 00416 return origResult; 00417 f.setPointSizeF( result ); 00418 fm = QFontMetrics( f ); 00419 } 00420 } 00421 00422 qreal KDChart::TextLayoutItem::realFontSize() const 00423 { 00424 return mAttributes.calculatedFontSize( mAutoReferenceArea, mAutoReferenceOrientation ); 00425 } 00426 00427 00428 bool KDChart::TextLayoutItem::realFontWasRecalculated() const 00429 { 00430 const qreal fntSiz = realFontSize(); 00431 const bool bRecalcDone = 00432 ( ( ! cachedSizeHint.isValid() ) || ( cachedFontSize != fntSiz ) ); 00433 00434 if( bRecalcDone && fntSiz > 0.0 ){ 00435 cachedFontSize = fntSiz; 00436 cachedFont.setPointSizeF( fntSiz ); 00437 } 00438 return bRecalcDone; 00439 } 00440 00441 00442 QFont KDChart::TextLayoutItem::realFont() const 00443 { 00444 realFontWasRecalculated(); // we can safely ignore the boolean return value 00445 return cachedFont; 00446 } 00447 00448 QPolygon KDChart::TextLayoutItem::rotatedCorners() const 00449 { 00450 // the angle in rad 00451 const qreal angle = mAttributes.rotation() * PI / 180.0; 00452 QSize size = unrotatedSizeHint(); 00453 00454 // my P1 - P4 (the four points of the rotated area) 00455 QPointF P1( size.height() * sin( angle ), 0 ); 00456 QPointF P2( size.height() * sin( angle ) + size.width() * cos( angle ), size.width() * sin( angle ) ); 00457 QPointF P3( size.width() * cos( angle ), size.width() * sin( angle ) + size.height() * cos( angle ) ); 00458 QPointF P4( 0, size.height() * cos( angle ) ); 00459 00460 QPolygon result; 00461 result << P1.toPoint() << P2.toPoint() << P3.toPoint() << P4.toPoint(); 00462 return result; 00463 } 00464 00465 bool KDChart::TextLayoutItem::intersects( const TextLayoutItem& other, const QPointF& myPos, const QPointF& otherPos ) const 00466 { 00467 return intersects( other, myPos.toPoint(), otherPos.toPoint() ); 00468 } 00469 00470 bool KDChart::TextLayoutItem::intersects( const TextLayoutItem& other, const QPoint& myPos, const QPoint& otherPos ) const 00471 { 00472 if ( mAttributes.rotation() != other.mAttributes.rotation() ) 00473 { 00474 // that's the code for the common case: the rotation angles don't need to match here 00475 QPolygon myPolygon( rotatedCorners() ); 00476 QPolygon otherPolygon( other.rotatedCorners() ); 00477 00478 // move the polygons to their positions 00479 myPolygon.translate( myPos ); 00480 otherPolygon.translate( otherPos ); 00481 00482 // create regions out of it 00483 QRegion myRegion( myPolygon ); 00484 QRegion otherRegion( otherPolygon ); 00485 00486 // now the question - do they intersect or not? 00487 return ! myRegion.intersect( otherRegion ).isEmpty(); 00488 00489 } else { 00490 // and that's the code for the special case: the rotation angles match, which is less time consuming in calculation 00491 const qreal angle = mAttributes.rotation() * PI / 180.0; 00492 // both sizes 00493 const QSizeF mySize( unrotatedSizeHint() ); 00494 const QSizeF otherSize( other.unrotatedSizeHint() ); 00495 00496 // that's myP1 relative to myPos 00497 QPointF myP1( mySize.height() * sin( angle ), 0.0 ); 00498 // that's otherP1 to myPos 00499 QPointF otherP1 = QPointF( otherSize.height() * sin( angle ), 0.0 ) + otherPos - myPos; 00500 00501 // now rotate both points the negative angle around myPos 00502 myP1 = QPointF( myP1.x() * cos( -angle ), myP1.x() * sin( -angle ) ); 00503 qreal r = sqrt( otherP1.x() * otherP1.x() + otherP1.y() * otherP1.y() ); 00504 otherP1 = QPointF( r * cos( -angle ), r * sin( -angle ) ); 00505 00506 // finally we look, whether both rectangles intersect or even not 00507 const bool res = QRectF( myP1, mySize ).intersects( QRectF( otherP1, otherSize ) ); 00508 //qDebug() << res << QRectF( myP1, mySize ) << QRectF( otherP1, otherSize ); 00509 return res; 00510 } 00511 } 00512 00513 QSize KDChart::TextLayoutItem::sizeHint() const 00514 { 00515 QPoint dummy; 00516 return sizeHintAndRotatedCorners(dummy,dummy,dummy,dummy); 00517 } 00518 00519 QSize KDChart::TextLayoutItem::sizeHintAndRotatedCorners( 00520 QPoint& topLeftPt, QPoint& topRightPt, QPoint& bottomRightPt, QPoint& bottomLeftPt) const 00521 { 00522 if( realFontWasRecalculated() || mAttributes.rotation() ) 00523 { 00524 const QSize newSizeHint( calcSizeHint( cachedFont, 00525 topLeftPt, topRightPt, bottomRightPt, bottomLeftPt ) ); 00526 if( newSizeHint != cachedSizeHint ){ 00527 cachedSizeHint = newSizeHint; 00528 sizeHintChanged(); 00529 } 00530 cachedTopLeft = topLeftPt; 00531 cachedTopRight = topRightPt; 00532 cachedBottomRight = bottomRightPt; 00533 cachedBottomLeft = bottomLeftPt; 00534 }else{ 00535 topLeftPt = cachedTopLeft; 00536 topRightPt = cachedTopRight; 00537 bottomRightPt = cachedBottomRight; 00538 bottomLeftPt = cachedBottomLeft; 00539 } 00540 //qDebug() << "-------- KDChart::TextLayoutItem::sizeHint() returns:"<<cachedSizeHint<<" ----------"; 00541 return cachedSizeHint; 00542 } 00543 00544 QSize KDChart::TextLayoutItem::sizeHintUnrotated() const 00545 { 00546 realFontWasRecalculated(); // make sure the cached font is updated if needed 00547 return unrotatedSizeHint( cachedFont ); 00548 } 00549 00550 00551 // PENDING(kalle) Support auto shrink 00552 00553 00554 QSize KDChart::TextLayoutItem::unrotatedSizeHint( QFont fnt ) const 00555 { 00556 if ( fnt == QFont() ) 00557 fnt = realFont(); // this *is* the chached font in most of the time 00558 00559 const QFontMetricsF met( fnt, GlobalMeasureScaling::paintDevice() ); 00560 QSize ret(0, 0); 00561 // note: boundingRect() does NOT take any newlines into account 00562 // so we need to calculate the size by combining several 00563 // rectangles: one per line. This fixes bugz issue #3720. 00564 // (khz, 2007 04 14) 00565 QStringList lines = mText.split(QString::fromAscii("\n")); 00566 for (int i = 0; i < lines.size(); ++i) { 00567 const QSize lSize = met.boundingRect(lines.at(i) ).toRect().size(); 00568 ret.setWidth(qMax( ret.width(), lSize.width() )); 00569 ret.rheight() += lSize.height(); 00570 } 00571 00572 int frame = QApplication::style()->pixelMetric( QStyle::PM_ButtonMargin, 0, 0 ); 00573 // fine-tuning for small font sizes: the frame must not be so big, if the font is tiny 00574 frame = qMin( frame, ret.height() * 2 / 3 ); 00575 //qDebug() << "frame:"<< frame; 00576 ret += QSize( frame, frame ); 00577 return ret; 00578 //const QFontMetricsF met( fnt, GlobalMeasureScaling::paintDevice() ); 00579 //const int frame = QApplication::style()->pixelMetric( QStyle::PM_ButtonMargin, 0, 0 ); 00580 //return 00581 // met.boundingRect( mText ).size().toSize() + QSize( frame, frame ); 00582 } 00583 00584 00585 QSize KDChart::TextLayoutItem::calcSizeHint( 00586 QFont fnt, QPoint& topLeftPt, QPoint& topRightPt, QPoint& bottomRightPt, QPoint& bottomLeftPt ) const 00587 { 00588 const QSize siz( unrotatedSizeHint( fnt )); 00589 //qDebug() << "-------- siz: "<<siz; 00590 if( ! mAttributes.rotation() ){ 00591 topLeftPt = QPoint(0,0); 00592 topRightPt = QPoint(siz.width(),0); 00593 bottomRightPt = QPoint(siz.width(),siz.height()); 00594 bottomLeftPt = QPoint(0,siz.height()); 00595 return siz; 00596 } 00597 00598 const QRect rect(QPoint(0, 0), siz + QSize(4,4)); 00599 const qreal angle = PI * mAttributes.rotation() / 180.0; 00600 const qreal cosAngle = cos( angle ); 00601 const qreal sinAngle = sin( angle ); 00602 QMatrix rotationMatrix(cosAngle, sinAngle, -sinAngle, cosAngle, 0, 0); 00603 QPolygon rotPts; 00604 rotPts << rotationMatrix.map(rect.topLeft()) 00605 << rotationMatrix.map(rect.topRight()) 00606 << rotationMatrix.map(rect.bottomRight()) 00607 << rotationMatrix.map(rect.bottomLeft()); 00608 QSize rotSiz( rotPts.boundingRect().size() ); 00609 //qDebug() << "-------- KDChart::TextLayoutItem::calcSizeHint() returns:"<<rotSiz<<rotPts; 00610 topLeftPt = rotPts[0]; 00611 topRightPt = rotPts[1]; 00612 bottomRightPt = rotPts[2]; 00613 bottomLeftPt = rotPts[3]; 00614 return rotSiz; 00615 } 00616 00617 00618 void KDChart::TextLayoutItem::paint( QPainter* painter ) 00619 { 00620 // make sure, cached font is updated, if needed: 00621 // sizeHint(); 00622 00623 if( !mRect.isValid() ) 00624 return; 00625 00626 const PainterSaver painterSaver( painter ); 00627 QFont f = realFont(); 00628 if ( mAttributes.autoShrink() ) 00629 f.setPointSizeF( fitFontSizeToGeometry() ); 00630 painter->setFont( f ); 00631 QRectF rect( geometry() ); 00632 00633 // #ifdef DEBUG_ITEMS_PAINT 00634 // painter->setPen( Qt::black ); 00635 // painter->drawRect( rect ); 00636 // #endif 00637 painter->translate( rect.center() ); 00638 rect.moveTopLeft( QPointF( - rect.width() / 2, - rect.height() / 2 ) ); 00639 #ifdef DEBUG_ITEMS_PAINT 00640 painter->setPen( Qt::blue ); 00641 painter->drawRect( rect ); 00642 painter->drawRect( QRect(QPoint((rect.topLeft().toPoint() + rect.bottomLeft().toPoint()) / 2 - QPoint(2,2)), QSize(3,3)) ); 00643 //painter->drawRect( QRect(QPoint((rect.topRight().toPoint() + rect.bottomRight().toPoint()) / 2 - QPoint(2,2)), QSize(3,3)) ); 00644 #endif 00645 painter->rotate( mAttributes.rotation() ); 00646 rect = rotatedRect( rect, mAttributes.rotation() ); 00647 #ifdef DEBUG_ITEMS_PAINT 00648 painter->setPen( Qt::red ); 00649 painter->drawRect( rect ); 00650 painter->drawRect( QRect(QPoint((rect.topLeft().toPoint() + rect.bottomLeft().toPoint()) / 2 - QPoint(2,2)), QSize(3,3)) ); 00651 //painter->drawRect( QRect(QPoint((rect.topRight().toPoint() + rect.bottomRight().toPoint()) / 2 - QPoint(2,2)), QSize(3,3)) ); 00652 #endif 00653 painter->setPen( PrintingParameters::scalePen( mAttributes.pen() ) ); 00654 // QTextDocument* document = mAttributes.textDocument(); 00655 // if ( document ) { 00656 // document->setPageSize(QSize(rect.width(), rect.height())); 00657 // document->setHtml(mText); 00658 // QAbstractTextDocumentLayout::PaintContext paintcontext; 00659 // paintcontext.clip = rect; 00660 // document->documentLayout()->draw(painter, paintcontext); 00661 // } else { 00662 painter->drawText( rect, mTextAlignment, mText ); 00663 // } 00664 00665 // if ( calcSizeHint( realFont() ).width() > rect.width() ) 00666 // qDebug() << "rect.width()" << rect.width() << "text.width()" << calcSizeHint( realFont() ).width(); 00667 // 00668 // //painter->drawText( rect, Qt::AlignHCenter | Qt::AlignVCenter, mText ); 00669 } 00670 00671 KDChart::HorizontalLineLayoutItem::HorizontalLineLayoutItem() 00672 : AbstractLayoutItem( Qt::AlignCenter ) 00673 { 00674 } 00675 00676 Qt::Orientations KDChart::HorizontalLineLayoutItem::expandingDirections() const 00677 { 00678 return Qt::Vertical|Qt::Horizontal; // Grow both vertically, and horizontally 00679 } 00680 00681 QRect KDChart::HorizontalLineLayoutItem::geometry() const 00682 { 00683 return mRect; 00684 } 00685 00686 bool KDChart::HorizontalLineLayoutItem::isEmpty() const 00687 { 00688 return false; // never empty, otherwise the layout item would not exist 00689 } 00690 00691 QSize KDChart::HorizontalLineLayoutItem::maximumSize() const 00692 { 00693 return QSize( QWIDGETSIZE_MAX, QWIDGETSIZE_MAX ); 00694 } 00695 00696 QSize KDChart::HorizontalLineLayoutItem::minimumSize() const 00697 { 00698 return QSize( 0, 0 ); 00699 } 00700 00701 void KDChart::HorizontalLineLayoutItem::setGeometry( const QRect& r ) 00702 { 00703 mRect = r; 00704 } 00705 00706 QSize KDChart::HorizontalLineLayoutItem::sizeHint() const 00707 { 00708 return QSize( -1, 3 ); // see qframe.cpp 00709 } 00710 00711 00712 void KDChart::HorizontalLineLayoutItem::paint( QPainter* painter ) 00713 { 00714 if( !mRect.isValid() ) 00715 return; 00716 00717 painter->drawLine( QPointF( mRect.left(), mRect.center().y() ), 00718 QPointF( mRect.right(), mRect.center().y() ) ); 00719 } 00720 00721 00722 KDChart::VerticalLineLayoutItem::VerticalLineLayoutItem() 00723 : AbstractLayoutItem( Qt::AlignCenter ) 00724 { 00725 } 00726 00727 Qt::Orientations KDChart::VerticalLineLayoutItem::expandingDirections() const 00728 { 00729 return Qt::Vertical|Qt::Vertical; // Grow both vertically, and horizontally 00730 } 00731 00732 QRect KDChart::VerticalLineLayoutItem::geometry() const 00733 { 00734 return mRect; 00735 } 00736 00737 bool KDChart::VerticalLineLayoutItem::isEmpty() const 00738 { 00739 return false; // never empty, otherwise the layout item would not exist 00740 } 00741 00742 QSize KDChart::VerticalLineLayoutItem::maximumSize() const 00743 { 00744 return QSize( QWIDGETSIZE_MAX, QWIDGETSIZE_MAX ); 00745 } 00746 00747 QSize KDChart::VerticalLineLayoutItem::minimumSize() const 00748 { 00749 return QSize( 0, 0 ); 00750 } 00751 00752 void KDChart::VerticalLineLayoutItem::setGeometry( const QRect& r ) 00753 { 00754 mRect = r; 00755 } 00756 00757 QSize KDChart::VerticalLineLayoutItem::sizeHint() const 00758 { 00759 return QSize( 3, -1 ); // see qframe.cpp 00760 } 00761 00762 00763 void KDChart::VerticalLineLayoutItem::paint( QPainter* painter ) 00764 { 00765 if( !mRect.isValid() ) 00766 return; 00767 00768 painter->drawLine( QPointF( mRect.center().x(), mRect.top() ), 00769 QPointF( mRect.center().x(), mRect.bottom() ) ); 00770 } 00771 00772 00773 00774 KDChart::MarkerLayoutItem::MarkerLayoutItem( KDChart::AbstractDiagram* diagram, 00775 const MarkerAttributes& marker, 00776 const QBrush& brush, const QPen& pen, 00777 Qt::Alignment alignment ) 00778 : AbstractLayoutItem( alignment ) 00779 , mDiagram( diagram ) 00780 , mMarker( marker ) 00781 , mBrush( brush ) 00782 , mPen( pen ) 00783 { 00784 } 00785 00786 Qt::Orientations KDChart::MarkerLayoutItem::expandingDirections() const 00787 { 00788 return 0; // Grow neither vertically nor horizontally 00789 } 00790 00791 QRect KDChart::MarkerLayoutItem::geometry() const 00792 { 00793 return mRect; 00794 } 00795 00796 bool KDChart::MarkerLayoutItem::isEmpty() const 00797 { 00798 return false; // never empty, otherwise the layout item would not exist 00799 } 00800 00801 QSize KDChart::MarkerLayoutItem::maximumSize() const 00802 { 00803 return sizeHint(); // PENDING(kalle) Review, quite inflexible 00804 } 00805 00806 QSize KDChart::MarkerLayoutItem::minimumSize() const 00807 { 00808 return sizeHint(); // PENDING(kalle) Review, quite inflexible 00809 } 00810 00811 void KDChart::MarkerLayoutItem::setGeometry( const QRect& r ) 00812 { 00813 mRect = r; 00814 } 00815 00816 QSize KDChart::MarkerLayoutItem::sizeHint() const 00817 { 00818 //qDebug() << "KDChart::MarkerLayoutItem::sizeHint() returns:"<<mMarker.markerSize().toSize(); 00819 return mMarker.markerSize().toSize(); 00820 } 00821 00822 void KDChart::MarkerLayoutItem::paint( QPainter* painter ) 00823 { 00824 paintIntoRect( painter, mRect, mDiagram, mMarker, mBrush, mPen ); 00825 } 00826 00827 void KDChart::MarkerLayoutItem::paintIntoRect( 00828 QPainter* painter, 00829 const QRect& rect, 00830 AbstractDiagram* diagram, 00831 const MarkerAttributes& marker, 00832 const QBrush& brush, 00833 const QPen& pen ) 00834 { 00835 if( ! rect.isValid() ) 00836 return; 00837 00838 // The layout management may assign a larger rect than what we 00839 // wanted. We need to adjust the position. 00840 const QSize siz = marker.markerSize().toSize(); 00841 QPointF pos = rect.topLeft(); 00842 pos += QPointF( static_cast<qreal>(( rect.width() - siz.width()) / 2.0 ), 00843 static_cast<qreal>(( rect.height() - siz.height()) / 2.0 ) ); 00844 00845 #ifdef DEBUG_ITEMS_PAINT 00846 QPointF oldPos = pos; 00847 #endif 00848 00849 // And finally, drawMarker() assumes the position to be the center 00850 // of the marker, adjust again. 00851 pos += QPointF( static_cast<qreal>( siz.width() ) / 2.0, 00852 static_cast<qreal>( siz.height() )/ 2.0 ); 00853 00854 diagram->paintMarker( painter, marker, brush, pen, pos.toPoint(), siz ); 00855 00856 #ifdef DEBUG_ITEMS_PAINT 00857 const QPen oldPen( painter->pen() ); 00858 painter->setPen( Qt::red ); 00859 painter->drawRect( QRect(oldPos.toPoint(), siz) ); 00860 painter->setPen( oldPen ); 00861 #endif 00862 } 00863 00864 00865 KDChart::LineLayoutItem::LineLayoutItem( KDChart::AbstractDiagram* diagram, 00866 int length, 00867 const QPen& pen, 00868 Qt::Alignment alignment ) 00869 : AbstractLayoutItem( alignment ) 00870 , mDiagram( diagram ) 00871 , mLength( length ) 00872 , mPen( pen ) 00873 { 00874 //have a mini pen width 00875 if ( pen.width() < 2 ) 00876 mPen.setWidth( 2 ); 00877 } 00878 00879 Qt::Orientations KDChart::LineLayoutItem::expandingDirections() const 00880 { 00881 return 0; // Grow neither vertically nor horizontally 00882 } 00883 00884 QRect KDChart::LineLayoutItem::geometry() const 00885 { 00886 return mRect; 00887 } 00888 00889 bool KDChart::LineLayoutItem::isEmpty() const 00890 { 00891 return false; // never empty, otherwise the layout item would not exist 00892 } 00893 00894 QSize KDChart::LineLayoutItem::maximumSize() const 00895 { 00896 return sizeHint(); // PENDING(kalle) Review, quite inflexible 00897 } 00898 00899 QSize KDChart::LineLayoutItem::minimumSize() const 00900 { 00901 return sizeHint(); // PENDING(kalle) Review, quite inflexible 00902 } 00903 00904 void KDChart::LineLayoutItem::setGeometry( const QRect& r ) 00905 { 00906 mRect = r; 00907 } 00908 00909 QSize KDChart::LineLayoutItem::sizeHint() const 00910 { 00911 return QSize( mLength, mPen.width()+2 ); 00912 } 00913 00914 void KDChart::LineLayoutItem::paint( QPainter* painter ) 00915 { 00916 paintIntoRect( painter, mRect, mPen ); 00917 } 00918 00919 void KDChart::LineLayoutItem::paintIntoRect( 00920 QPainter* painter, 00921 const QRect& rect, 00922 const QPen& pen ) 00923 { 00924 if( ! rect.isValid() ) 00925 return; 00926 00927 const QPen oldPen = painter->pen(); 00928 painter->setPen( PrintingParameters::scalePen( pen ) ); 00929 const qreal y = rect.center().y(); 00930 painter->drawLine( QPointF( rect.left(), y ), 00931 QPointF( rect.right(), y ) ); 00932 painter->setPen( oldPen ); 00933 } 00934 00935 00936 KDChart::LineWithMarkerLayoutItem::LineWithMarkerLayoutItem( 00937 KDChart::AbstractDiagram* diagram, 00938 int lineLength, 00939 const QPen& linePen, 00940 int markerOffs, 00941 const MarkerAttributes& marker, 00942 const QBrush& markerBrush, 00943 const QPen& markerPen, 00944 Qt::Alignment alignment ) 00945 : AbstractLayoutItem( alignment ) 00946 , mDiagram( diagram ) 00947 , mLineLength( lineLength ) 00948 , mLinePen( linePen ) 00949 , mMarkerOffs( markerOffs ) 00950 , mMarker( marker ) 00951 , mMarkerBrush( markerBrush ) 00952 , mMarkerPen( markerPen ) 00953 { 00954 } 00955 00956 Qt::Orientations KDChart::LineWithMarkerLayoutItem::expandingDirections() const 00957 { 00958 return 0; // Grow neither vertically nor horizontally 00959 } 00960 00961 QRect KDChart::LineWithMarkerLayoutItem::geometry() const 00962 { 00963 return mRect; 00964 } 00965 00966 bool KDChart::LineWithMarkerLayoutItem::isEmpty() const 00967 { 00968 return false; // never empty, otherwise the layout item would not exist 00969 } 00970 00971 QSize KDChart::LineWithMarkerLayoutItem::maximumSize() const 00972 { 00973 return sizeHint(); // PENDING(kalle) Review, quite inflexible 00974 } 00975 00976 QSize KDChart::LineWithMarkerLayoutItem::minimumSize() const 00977 { 00978 return sizeHint(); // PENDING(kalle) Review, quite inflexible 00979 } 00980 00981 void KDChart::LineWithMarkerLayoutItem::setGeometry( const QRect& r ) 00982 { 00983 mRect = r; 00984 } 00985 00986 QSize KDChart::LineWithMarkerLayoutItem::sizeHint() const 00987 { 00988 const QSize sizeM = mMarker.markerSize().toSize(); 00989 const QSize sizeL = QSize( mLineLength, mLinePen.width()+2 ); 00990 return QSize( qMax(sizeM.width(), sizeL.width()), 00991 qMax(sizeM.height(), sizeL.height()) ); 00992 } 00993 00994 void KDChart::LineWithMarkerLayoutItem::paint( QPainter* painter ) 00995 { 00996 // paint the line over the full width, into the vertical middle of the rect 00997 LineLayoutItem::paintIntoRect( painter, mRect, mLinePen ); 00998 00999 // paint the marker with the given offset from the left side of the line 01000 const QRect r( 01001 QPoint( mRect.x()+mMarkerOffs, mRect.y() ), 01002 QSize( mMarker.markerSize().toSize().width(), mRect.height() ) ); 01003 MarkerLayoutItem::paintIntoRect( 01004 painter, r, mDiagram, mMarker, mMarkerBrush, mMarkerPen ); 01005 } 01006 01007 01008 01009 KDChart::AutoSpacerLayoutItem::AutoSpacerLayoutItem( 01010 bool layoutIsAtTopPosition, QHBoxLayout *rightLeftLayout, 01011 bool layoutIsAtLeftPosition, QVBoxLayout *topBottomLayout ) 01012 : AbstractLayoutItem( Qt::AlignCenter ) 01013 , mLayoutIsAtTopPosition( layoutIsAtTopPosition ) 01014 , mRightLeftLayout( rightLeftLayout ) 01015 , mLayoutIsAtLeftPosition( layoutIsAtLeftPosition ) 01016 , mTopBottomLayout( topBottomLayout ) 01017 { 01018 } 01019 01020 Qt::Orientations KDChart::AutoSpacerLayoutItem::expandingDirections() const 01021 { 01022 return 0; // Grow neither vertically nor horizontally 01023 } 01024 01025 QRect KDChart::AutoSpacerLayoutItem::geometry() const 01026 { 01027 return mRect; 01028 } 01029 01030 bool KDChart::AutoSpacerLayoutItem::isEmpty() const 01031 { 01032 return true; // never empty, otherwise the layout item would not exist 01033 } 01034 01035 QSize KDChart::AutoSpacerLayoutItem::maximumSize() const 01036 { 01037 return sizeHint(); 01038 } 01039 01040 QSize KDChart::AutoSpacerLayoutItem::minimumSize() const 01041 { 01042 return sizeHint(); 01043 } 01044 01045 void KDChart::AutoSpacerLayoutItem::setGeometry( const QRect& r ) 01046 { 01047 mRect = r; 01048 } 01049 01050 01051 static void updateCommonBrush( QBrush& commonBrush, bool& bStart, const KDChart::AbstractArea& area ) 01052 { 01053 const KDChart::BackgroundAttributes ba( area.backgroundAttributes() ); 01054 const bool hasSimpleBrush = ( 01055 ! area.frameAttributes().isVisible() && 01056 ba.isVisible() && 01057 ba.pixmapMode() == KDChart::BackgroundAttributes::BackgroundPixmapModeNone && 01058 ba.brush().gradient() == 0 ); 01059 if( bStart ){ 01060 bStart = false; 01061 commonBrush = hasSimpleBrush ? ba.brush() : QBrush(); 01062 }else{ 01063 if( ! hasSimpleBrush || ba.brush() != commonBrush ) 01064 { 01065 commonBrush = QBrush(); 01066 } 01067 } 01068 } 01069 01070 QSize KDChart::AutoSpacerLayoutItem::sizeHint() const 01071 { 01072 QBrush commonBrush; 01073 bool bStart=true; 01074 // calculate the maximal overlap of the top/bottom axes: 01075 int topBottomOverlap = 0; 01076 if( mTopBottomLayout ){ 01077 for (int i = 0; i < mTopBottomLayout->count(); ++i){ 01078 AbstractArea* area = dynamic_cast<AbstractArea*>(mTopBottomLayout->itemAt(i)); 01079 if( area ){ 01080 //qDebug() << "AutoSpacerLayoutItem testing" << area; 01081 topBottomOverlap = 01082 mLayoutIsAtLeftPosition 01083 ? qMax( topBottomOverlap, area->rightOverlap() ) 01084 : qMax( topBottomOverlap, area->leftOverlap() ); 01085 updateCommonBrush( commonBrush, bStart, *area ); 01086 } 01087 } 01088 } 01089 // calculate the maximal overlap of the left/right axes: 01090 int leftRightOverlap = 0; 01091 if( mRightLeftLayout ){ 01092 for (int i = 0; i < mRightLeftLayout->count(); ++i){ 01093 AbstractArea* area = dynamic_cast<AbstractArea*>(mRightLeftLayout->itemAt(i)); 01094 if( area ){ 01095 //qDebug() << "AutoSpacerLayoutItem testing" << area; 01096 leftRightOverlap = 01097 mLayoutIsAtTopPosition 01098 ? qMax( leftRightOverlap, area->bottomOverlap() ) 01099 : qMax( leftRightOverlap, area->topOverlap() ); 01100 updateCommonBrush( commonBrush, bStart, *area ); 01101 } 01102 } 01103 } 01104 if( topBottomOverlap > 0 && leftRightOverlap > 0 ) 01105 mCommonBrush = commonBrush; 01106 else 01107 mCommonBrush = QBrush(); 01108 mCachedSize = QSize( topBottomOverlap, leftRightOverlap ); 01109 //qDebug() << mCachedSize; 01110 return mCachedSize; 01111 } 01112 01113 01114 void KDChart::AutoSpacerLayoutItem::paint( QPainter* painter ) 01115 { 01116 if( mParentLayout && mRect.isValid() && mCachedSize.isValid() && 01117 mCommonBrush.style() != Qt::NoBrush ) 01118 { 01119 QPoint p1( mRect.topLeft() ); 01120 QPoint p2( mRect.bottomRight() ); 01121 if( mLayoutIsAtLeftPosition ) 01122 p1.rx() += mCachedSize.width() - mParentLayout->spacing(); 01123 else 01124 p2.rx() -= mCachedSize.width() - mParentLayout->spacing(); 01125 if( mLayoutIsAtTopPosition ){ 01126 p1.ry() += mCachedSize.height() - mParentLayout->spacing() - 1; 01127 p2.ry() -= 1; 01128 }else 01129 p2.ry() -= mCachedSize.height() - mParentLayout->spacing() - 1; 01130 //qDebug() << mLayoutIsAtTopPosition << mLayoutIsAtLeftPosition; 01131 //qDebug() << mRect; 01132 //qDebug() << mParentLayout->margin(); 01133 //qDebug() << QRect( p1, p2 ); 01134 const QPoint oldBrushOrigin( painter->brushOrigin() ); 01135 const QBrush oldBrush( painter->brush() ); 01136 const QPen oldPen( painter->pen() ); 01137 const QPointF newTopLeft( painter->deviceMatrix().map( p1 ) ); 01138 painter->setBrushOrigin( newTopLeft ); 01139 painter->setBrush( mCommonBrush ); 01140 painter->setPen( Qt::NoPen ); 01141 painter->drawRect( QRect( p1, p2 ) ); 01142 painter->setBrushOrigin( oldBrushOrigin ); 01143 painter->setBrush( oldBrush ); 01144 painter->setPen( oldPen ); 01145 } 01146 // debug code: 01147 #if 0 01148 //qDebug() << "KDChart::AutoSpacerLayoutItem::paint()"; 01149 if( !mRect.isValid() ) 01150 return; 01151 01152 painter->drawRect( mRect ); 01153 painter->drawLine( QPointF( mRect.x(), mRect.top() ), 01154 QPointF( mRect.right(), mRect.bottom() ) ); 01155 painter->drawLine( QPointF( mRect.right(), mRect.top() ), 01156 QPointF( mRect.x(), mRect.bottom() ) ); 01157 #endif 01158 }