KD Chart 2
[rev.2.5]
|
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 00049 //#define DEBUG_ITEMS_PAINT 00050 00059 void KDChart::AbstractLayoutItem::setParentWidget( QWidget* widget ) 00060 { 00061 mParent = widget; 00062 } 00063 00064 void KDChart::AbstractLayoutItem::paintAll( QPainter& painter ) 00065 { 00066 paint( &painter ); 00067 } 00068 00072 void KDChart::AbstractLayoutItem::paintCtx( PaintContext* context ) 00073 { 00074 if( context ) 00075 paint( context->painter() ); 00076 } 00077 00081 void KDChart::AbstractLayoutItem::sizeHintChanged() const 00082 { 00083 // This is exactly like what QWidget::updateGeometry does. 00084 // qDebug("KDChart::AbstractLayoutItem::sizeHintChanged() called"); 00085 if( mParent ) { 00086 if ( mParent->layout() ) 00087 mParent->layout()->invalidate(); 00088 else 00089 QApplication::postEvent( mParent, new QEvent( QEvent::LayoutRequest ) ); 00090 } 00091 } 00092 00093 KDChart::TextBubbleLayoutItem::TextBubbleLayoutItem( const QString& text, 00094 const KDChart::TextAttributes& attributes, 00095 const QObject* area, 00096 KDChartEnums::MeasureOrientation orientation, 00097 Qt::Alignment alignment ) 00098 : AbstractLayoutItem( alignment ), 00099 m_text( new TextLayoutItem( text, attributes, area, orientation, alignment ) ) 00100 { 00101 } 00102 00103 KDChart::TextBubbleLayoutItem::TextBubbleLayoutItem() 00104 : AbstractLayoutItem( Qt::AlignLeft ), 00105 m_text( new TextLayoutItem() ) 00106 { 00107 } 00108 00109 KDChart::TextBubbleLayoutItem::~TextBubbleLayoutItem() 00110 { 00111 delete m_text; 00112 } 00113 00114 void KDChart::TextBubbleLayoutItem::setAutoReferenceArea( const QObject* area ) 00115 { 00116 m_text->setAutoReferenceArea( area ); 00117 } 00118 00119 const QObject* KDChart::TextBubbleLayoutItem::autoReferenceArea() const 00120 { 00121 return m_text->autoReferenceArea(); 00122 } 00123 00124 void KDChart::TextBubbleLayoutItem::setText( const QString& text ) 00125 { 00126 m_text->setText( text ); 00127 } 00128 00129 QString KDChart::TextBubbleLayoutItem::text() const 00130 { 00131 return m_text->text(); 00132 } 00133 00134 void KDChart::TextBubbleLayoutItem::setTextAttributes( const TextAttributes& a ) 00135 { 00136 m_text->setTextAttributes( a ); 00137 } 00138 00139 KDChart::TextAttributes KDChart::TextBubbleLayoutItem::textAttributes() const 00140 { 00141 return m_text->textAttributes(); 00142 } 00143 00144 bool KDChart::TextBubbleLayoutItem::isEmpty() const 00145 { 00146 return m_text->isEmpty(); 00147 } 00148 00149 Qt::Orientations KDChart::TextBubbleLayoutItem::expandingDirections() const 00150 { 00151 return m_text->expandingDirections(); 00152 } 00153 00154 QSize KDChart::TextBubbleLayoutItem::maximumSize() const 00155 { 00156 const int border = borderWidth(); 00157 return m_text->maximumSize() + QSize( 2 * border, 2 * border ); 00158 } 00159 00160 QSize KDChart::TextBubbleLayoutItem::minimumSize() const 00161 { 00162 const int border = borderWidth(); 00163 return m_text->minimumSize() + QSize( 2 * border, 2 * border ); 00164 } 00165 00166 QSize KDChart::TextBubbleLayoutItem::sizeHint() const 00167 { 00168 const int border = borderWidth(); 00169 return m_text->sizeHint() + QSize( 2 * border, 2 * border ); 00170 } 00171 00172 void KDChart::TextBubbleLayoutItem::setGeometry( const QRect& r ) 00173 { 00174 const int border = borderWidth(); 00175 m_text->setGeometry( r.adjusted( border, border, -border, -border ) ); 00176 } 00177 00178 QRect KDChart::TextBubbleLayoutItem::geometry() const 00179 { 00180 const int border = borderWidth(); 00181 return m_text->geometry().adjusted( -border, -border, border, border ); 00182 } 00183 00184 void KDChart::TextBubbleLayoutItem::paint( QPainter* painter ) 00185 { 00186 const QPen oldPen = painter->pen(); 00187 const QBrush oldBrush = painter->brush(); 00188 painter->setPen( Qt::black ); 00189 painter->setBrush( QColor( 255, 255, 220 ) ); 00190 painter->drawRoundRect( geometry(), 10 ); 00191 painter->setPen( oldPen ); 00192 painter->setBrush( oldBrush ); 00193 m_text->paint( painter ); 00194 } 00195 00196 int KDChart::TextBubbleLayoutItem::borderWidth() const 00197 { 00198 return 1; 00199 } 00200 00201 KDChart::TextLayoutItem::TextLayoutItem( const QString& text, 00202 const KDChart::TextAttributes& attributes, 00203 const QObject* area, 00204 KDChartEnums::MeasureOrientation orientation, 00205 Qt::Alignment alignment ) 00206 : AbstractLayoutItem( alignment ) 00207 , mText( text ) 00208 , mTextAlignment( alignment ) 00209 , mAttributes( attributes ) 00210 , mAutoReferenceArea( area ) 00211 , mAutoReferenceOrientation( orientation ) 00212 , cachedSizeHint() // default this to invalid to force just-in-time calculation before first use of sizeHint() 00213 , cachedFontSize( 0.0 ) 00214 , cachedFont( mAttributes.font() ) 00215 { 00216 } 00217 00218 KDChart::TextLayoutItem::TextLayoutItem() 00219 : AbstractLayoutItem( Qt::AlignLeft ) 00220 , mText() 00221 , mTextAlignment( Qt::AlignLeft ) 00222 , mAttributes() 00223 , mAutoReferenceArea( 0 ) 00224 , mAutoReferenceOrientation( KDChartEnums::MeasureOrientationHorizontal ) 00225 , cachedSizeHint() // default this to invalid to force just-in-time calculation before first use of sizeHint() 00226 , cachedFontSize( 0.0 ) 00227 , cachedFont( mAttributes.font() ) 00228 { 00229 00230 } 00231 00232 void KDChart::TextLayoutItem::setAutoReferenceArea( const QObject* area ) 00233 { 00234 mAutoReferenceArea = area; 00235 cachedSizeHint = QSize(); 00236 sizeHint(); 00237 } 00238 00239 const QObject* KDChart::TextLayoutItem::autoReferenceArea() const 00240 { 00241 return mAutoReferenceArea; 00242 } 00243 00244 void KDChart::TextLayoutItem::setText(const QString & text) 00245 { 00246 mText = text; 00247 cachedSizeHint = QSize(); 00248 sizeHint(); 00249 if( mParent ) 00250 mParent->update(); 00251 } 00252 00253 QString KDChart::TextLayoutItem::text() const 00254 { 00255 return mText; 00256 } 00257 00258 void KDChart::TextLayoutItem::setTextAlignment( Qt::Alignment alignment) 00259 { 00260 if( mTextAlignment == alignment ) 00261 return; 00262 mTextAlignment = alignment; 00263 if( mParent ) 00264 mParent->update(); 00265 } 00266 00267 Qt::Alignment KDChart::TextLayoutItem::textAlignment() const 00268 { 00269 return mTextAlignment; 00270 } 00271 00277 void KDChart::TextLayoutItem::setTextAttributes( const TextAttributes &a ) 00278 { 00279 mAttributes = a; 00280 cachedFont = a.font(); 00281 cachedSizeHint = QSize(); // invalidate size hint 00282 sizeHint(); 00283 if( mParent ) 00284 mParent->update(); 00285 } 00286 00292 KDChart::TextAttributes KDChart::TextLayoutItem::textAttributes() const 00293 { 00294 return mAttributes; 00295 } 00296 00297 00298 Qt::Orientations KDChart::TextLayoutItem::expandingDirections() const 00299 { 00300 return 0; // Grow neither vertically nor horizontally 00301 } 00302 00303 QRect KDChart::TextLayoutItem::geometry() const 00304 { 00305 return mRect; 00306 } 00307 00308 bool KDChart::TextLayoutItem::isEmpty() const 00309 { 00310 return false; // never empty, otherwise the layout item would not exist 00311 } 00312 00313 QSize KDChart::TextLayoutItem::maximumSize() const 00314 { 00315 return sizeHint(); // PENDING(kalle) Review, quite inflexible 00316 } 00317 00318 QSize KDChart::TextLayoutItem::minimumSize() const 00319 { 00320 return sizeHint(); // PENDING(kalle) Review, quite inflexible 00321 } 00322 00323 void KDChart::TextLayoutItem::setGeometry( const QRect& r ) 00324 { 00325 mRect = r; 00326 } 00327 00328 // returns the bounding box of rect rotated around its center 00329 QRectF rotatedRect( const QRectF& rect, qreal rotation ) 00330 { 00331 QTransform t; 00332 QPointF center = rect.center(); 00333 t.translate( center.x(), center.y() ); 00334 t.rotate( rotation ); 00335 t.translate( -center.x(), -center.y() ); 00336 return t.mapRect( rect ); 00337 } 00338 00339 qreal KDChart::TextLayoutItem::fitFontSizeToGeometry() const 00340 { 00341 QFont f = realFont(); 00342 const qreal origResult = f.pointSizeF(); 00343 qreal result = origResult; 00344 const qreal minSize = mAttributes.minimalFontSize().value(); 00345 const QSize mySize = geometry().size(); 00346 if ( mySize.isNull() ) { 00347 return result; 00348 } 00349 00350 QFontMetrics fm( f ); 00351 while ( true ) { 00352 const QSizeF textSize = rotatedRect( fm.boundingRect( mText ), mAttributes.rotation() ).normalized().size(); 00353 00354 if ( textSize.height() <= mySize.height() && textSize.width() <= mySize.width() ){ 00355 return result; 00356 } 00357 00358 result -= 0.5; 00359 if ( minSize > 0 && result < minSize ) { 00360 return result + 0.5; 00361 } else if ( result <= 0.0 ) { 00362 return origResult; 00363 } 00364 f.setPointSizeF( result ); 00365 fm = QFontMetrics( f ); 00366 } 00367 } 00368 00369 qreal KDChart::TextLayoutItem::realFontSize() const 00370 { 00371 return mAttributes.calculatedFontSize( mAutoReferenceArea, mAutoReferenceOrientation ); 00372 } 00373 00374 bool KDChart::TextLayoutItem::maybeUpdateRealFont() const 00375 { 00376 const qreal fntSiz = realFontSize(); 00377 const bool doUpdate = !cachedSizeHint.isValid() || cachedFontSize != fntSiz; 00378 00379 if ( doUpdate && fntSiz > 0.0 ) { 00380 cachedFontSize = fntSiz; 00381 cachedFont.setPointSizeF( fntSiz ); 00382 } 00383 return doUpdate; // "didUpdate" by now 00384 } 00385 00386 QFont KDChart::TextLayoutItem::realFont() const 00387 { 00388 maybeUpdateRealFont(); 00389 return cachedFont; 00390 } 00391 00392 QPolygon KDChart::TextLayoutItem::boundingPolygon() const 00393 { 00394 // should probably call sizeHint() here, but that one is expensive (see TODO there) 00395 return mCachedBoundingPolygon; 00396 } 00397 00398 bool KDChart::TextLayoutItem::intersects( const TextLayoutItem& other, const QPointF& myPos, const QPointF& otherPos ) const 00399 { 00400 return intersects( other, myPos.toPoint(), otherPos.toPoint() ); 00401 } 00402 00403 bool KDChart::TextLayoutItem::intersects( const TextLayoutItem& other, const QPoint& myPos, const QPoint& otherPos ) const 00404 { 00405 if ( mAttributes.rotation() != other.mAttributes.rotation() ) 00406 { 00407 // that's the code for the common case: the rotation angles don't need to match here 00408 QPolygon myPolygon = boundingPolygon().translated( myPos ); 00409 QPolygon otherPolygon = other.boundingPolygon().translated( otherPos ); 00410 00411 // create regions out of it 00412 QRegion myRegion( myPolygon ); 00413 QRegion otherRegion( otherPolygon ); 00414 00415 // now the question - do they intersect or not? 00416 return ! myRegion.intersect( otherRegion ).isEmpty(); 00417 00418 } else { 00419 // the rotation angles match so we can use a faster algorithm 00420 const qreal angle = DEGTORAD( mAttributes.rotation() ); 00421 // both sizes 00422 const QSizeF mySize( unrotatedSizeHint() ); 00423 const QSizeF otherSize( other.unrotatedSizeHint() ); 00424 00425 // that's myP1 relative to myPos 00426 QPointF myP1( mySize.height() * sin( angle ), 0.0 ); 00427 // that's otherP1 to myPos 00428 QPointF otherP1 = QPointF( otherSize.height() * sin( angle ), 0.0 ) + otherPos - myPos; 00429 00430 // now rotate both points the negative angle around myPos 00431 myP1 = QPointF( myP1.x() * cos( -angle ), myP1.x() * sin( -angle ) ); 00432 qreal r = sqrt( otherP1.x() * otherP1.x() + otherP1.y() * otherP1.y() ); 00433 if ( myP1.x() == otherP1.x() ) { // vertical 00434 otherP1 = QPointF( r * sin( -angle ), r * cos( -angle ) ); 00435 } else if ( myP1.y() == otherP1.y() ) { // horizontal 00436 otherP1 = QPointF( r * cos( -angle ), r * sin( -angle ) ); 00437 } 00438 00439 return QRectF( myP1, mySize ).intersects( QRectF( otherP1, otherSize ) ); 00440 } 00441 } 00442 00443 QSize KDChart::TextLayoutItem::sizeHint() const 00444 { 00445 // ### we only really need to recalculate the size hint when mAttributes.rotation has *changed* 00446 if ( maybeUpdateRealFont() || mAttributes.rotation() || !cachedSizeHint.isValid() ) { 00447 const QSize newSizeHint( calcSizeHint( cachedFont ) ); 00448 Q_ASSERT( newSizeHint.isValid() ); 00449 if ( newSizeHint != cachedSizeHint ) { 00450 cachedSizeHint = newSizeHint; 00451 sizeHintChanged(); 00452 } 00453 } 00454 return cachedSizeHint; 00455 } 00456 00457 QSize KDChart::TextLayoutItem::sizeHintUnrotated() const 00458 { 00459 maybeUpdateRealFont(); // make sure the cached font is up to date 00460 return unrotatedSizeHint( cachedFont ); 00461 } 00462 00463 00464 // PENDING(kalle) Support auto shrink 00465 00466 00467 QSize KDChart::TextLayoutItem::unrotatedTextSize( QFont fnt ) const 00468 { 00469 if ( fnt == QFont() ) { 00470 fnt = realFont(); // this is the cached font in most cases 00471 } 00472 00473 const QFontMetricsF fm( fnt, GlobalMeasureScaling::paintDevice() ); 00474 QRect veryLarge( 0, 0, 100000, 100000 ); 00475 // this overload of boundingRect() interprets \n as line breaks, not as regular characters. 00476 return fm.boundingRect( veryLarge, Qt::AlignLeft | Qt::AlignTop, mText ).size().toSize(); 00477 } 00478 00479 int KDChart::TextLayoutItem::marginWidth() const 00480 { 00481 return marginWidth( unrotatedTextSize() ); 00482 } 00483 00484 int KDChart::TextLayoutItem::marginWidth( const QSize& textSize ) const 00485 { 00486 return qMin ( QApplication::style()->pixelMetric( QStyle::PM_ButtonMargin, 0, 0 ), 00487 // decrease frame size if the text is small 00488 textSize.height() * 2 / 3 ); 00489 } 00490 00491 QSize KDChart::TextLayoutItem::unrotatedSizeHint( const QFont& fnt ) const 00492 { 00493 QSize ret = unrotatedTextSize( fnt ); 00494 const int margin = marginWidth( ret ); 00495 ret += QSize( margin, margin ); 00496 return ret; 00497 } 00498 00499 QSize KDChart::TextLayoutItem::calcSizeHint( const QFont& font ) const 00500 { 00501 const QSize size = unrotatedSizeHint( font ); 00502 QPoint topLeft( -size.width() * 0.5, -size.height() * 0.5 ); 00503 if ( !mAttributes.rotation() ) { 00504 mCachedBoundingPolygon.resize( 4 ); 00505 // using the same winding order as returned by QPolygon QTransform::mapToPolygon(const QRect&), 00506 // which is: 0-1: top edge, 1-2: right edge, 2-3: bottom edge, 3-0: left edge (of input rect) 00507 mCachedBoundingPolygon[ 0 ] = topLeft; 00508 mCachedBoundingPolygon[ 1 ] = topLeft + QPoint( size.width(), 0 ); // top right 00509 mCachedBoundingPolygon[ 2 ] = topLeft + QPoint( size.width(), size.height() ); // bottom right 00510 mCachedBoundingPolygon[ 3 ] = topLeft + QPoint( 0, size.height() ); // bottom left 00511 return size; 00512 } 00513 00514 const QRect rect( topLeft, size ); 00515 QTransform t; 00516 t.rotate( mAttributes.rotation() ); 00517 mCachedBoundingPolygon = t.mapToPolygon( rect ); 00518 00519 return mCachedBoundingPolygon.boundingRect().size(); 00520 } 00521 00522 void KDChart::TextLayoutItem::paint( QPainter* painter ) 00523 { 00524 if ( !mRect.isValid() ) { 00525 return; 00526 } 00527 const PainterSaver painterSaver( painter ); 00528 QFont f = realFont(); 00529 if ( mAttributes.autoShrink() ) { 00530 f.setPointSizeF( fitFontSizeToGeometry() ); 00531 } 00532 painter->setFont( f ); 00533 00534 QSize innerSize = unrotatedTextSize(); 00535 QRectF rect = QRectF( QPointF( 0, 0 ), innerSize ); 00536 rect.translate( -rect.center() ); 00537 painter->translate( mRect.center() ); 00538 painter->rotate( mAttributes.rotation() ); 00539 #ifdef DEBUG_ITEMS_PAINT 00540 painter->setPen( Qt::red ); 00541 painter->drawRect( rect ); 00542 #endif 00543 00544 painter->setPen( PrintingParameters::scalePen( mAttributes.pen() ) ); 00545 QTextDocument* document = mAttributes.textDocument(); 00546 if ( document ) { 00547 document->setPageSize( rect.size() ); 00548 document->setHtml( mText ); 00549 QAbstractTextDocumentLayout::PaintContext paintcontext; 00550 // ### this doesn't work for rotated painting because clip does not translate the painting 00551 // TODO translate the painting either using a QTransform or one of QPainter's transform stages 00552 paintcontext.clip = rect; 00553 document->documentLayout()->draw( painter, paintcontext ); 00554 } else { 00555 painter->drawText( rect, mTextAlignment, mText ); 00556 } 00557 } 00558 00559 KDChart::HorizontalLineLayoutItem::HorizontalLineLayoutItem() 00560 : AbstractLayoutItem( Qt::AlignCenter ) 00561 { 00562 } 00563 00564 Qt::Orientations KDChart::HorizontalLineLayoutItem::expandingDirections() const 00565 { 00566 return Qt::Vertical|Qt::Horizontal; // Grow both vertically, and horizontally 00567 } 00568 00569 QRect KDChart::HorizontalLineLayoutItem::geometry() const 00570 { 00571 return mRect; 00572 } 00573 00574 bool KDChart::HorizontalLineLayoutItem::isEmpty() const 00575 { 00576 return false; // never empty, otherwise the layout item would not exist 00577 } 00578 00579 QSize KDChart::HorizontalLineLayoutItem::maximumSize() const 00580 { 00581 return QSize( QWIDGETSIZE_MAX, QWIDGETSIZE_MAX ); 00582 } 00583 00584 QSize KDChart::HorizontalLineLayoutItem::minimumSize() const 00585 { 00586 return QSize( 0, 0 ); 00587 } 00588 00589 void KDChart::HorizontalLineLayoutItem::setGeometry( const QRect& r ) 00590 { 00591 mRect = r; 00592 } 00593 00594 QSize KDChart::HorizontalLineLayoutItem::sizeHint() const 00595 { 00596 return QSize( -1, 3 ); // see qframe.cpp 00597 } 00598 00599 00600 void KDChart::HorizontalLineLayoutItem::paint( QPainter* painter ) 00601 { 00602 if( !mRect.isValid() ) 00603 return; 00604 00605 painter->drawLine( QPointF( mRect.left(), mRect.center().y() ), 00606 QPointF( mRect.right(), mRect.center().y() ) ); 00607 } 00608 00609 00610 KDChart::VerticalLineLayoutItem::VerticalLineLayoutItem() 00611 : AbstractLayoutItem( Qt::AlignCenter ) 00612 { 00613 } 00614 00615 Qt::Orientations KDChart::VerticalLineLayoutItem::expandingDirections() const 00616 { 00617 return Qt::Vertical|Qt::Vertical; // Grow both vertically, and horizontally 00618 } 00619 00620 QRect KDChart::VerticalLineLayoutItem::geometry() const 00621 { 00622 return mRect; 00623 } 00624 00625 bool KDChart::VerticalLineLayoutItem::isEmpty() const 00626 { 00627 return false; // never empty, otherwise the layout item would not exist 00628 } 00629 00630 QSize KDChart::VerticalLineLayoutItem::maximumSize() const 00631 { 00632 return QSize( QWIDGETSIZE_MAX, QWIDGETSIZE_MAX ); 00633 } 00634 00635 QSize KDChart::VerticalLineLayoutItem::minimumSize() const 00636 { 00637 return QSize( 0, 0 ); 00638 } 00639 00640 void KDChart::VerticalLineLayoutItem::setGeometry( const QRect& r ) 00641 { 00642 mRect = r; 00643 } 00644 00645 QSize KDChart::VerticalLineLayoutItem::sizeHint() const 00646 { 00647 return QSize( 3, -1 ); // see qframe.cpp 00648 } 00649 00650 00651 void KDChart::VerticalLineLayoutItem::paint( QPainter* painter ) 00652 { 00653 if( !mRect.isValid() ) 00654 return; 00655 00656 painter->drawLine( QPointF( mRect.center().x(), mRect.top() ), 00657 QPointF( mRect.center().x(), mRect.bottom() ) ); 00658 } 00659 00660 00661 00662 KDChart::MarkerLayoutItem::MarkerLayoutItem( KDChart::AbstractDiagram* diagram, 00663 const MarkerAttributes& marker, 00664 const QBrush& brush, const QPen& pen, 00665 Qt::Alignment alignment ) 00666 : AbstractLayoutItem( alignment ) 00667 , mDiagram( diagram ) 00668 , mMarker( marker ) 00669 , mBrush( brush ) 00670 , mPen( pen ) 00671 { 00672 } 00673 00674 Qt::Orientations KDChart::MarkerLayoutItem::expandingDirections() const 00675 { 00676 return 0; // Grow neither vertically nor horizontally 00677 } 00678 00679 QRect KDChart::MarkerLayoutItem::geometry() const 00680 { 00681 return mRect; 00682 } 00683 00684 bool KDChart::MarkerLayoutItem::isEmpty() const 00685 { 00686 return false; // never empty, otherwise the layout item would not exist 00687 } 00688 00689 QSize KDChart::MarkerLayoutItem::maximumSize() const 00690 { 00691 return sizeHint(); // PENDING(kalle) Review, quite inflexible 00692 } 00693 00694 QSize KDChart::MarkerLayoutItem::minimumSize() const 00695 { 00696 return sizeHint(); // PENDING(kalle) Review, quite inflexible 00697 } 00698 00699 void KDChart::MarkerLayoutItem::setGeometry( const QRect& r ) 00700 { 00701 mRect = r; 00702 } 00703 00704 QSize KDChart::MarkerLayoutItem::sizeHint() const 00705 { 00706 //qDebug() << "KDChart::MarkerLayoutItem::sizeHint() returns:"<<mMarker.markerSize().toSize(); 00707 return mMarker.markerSize().toSize(); 00708 } 00709 00710 void KDChart::MarkerLayoutItem::paint( QPainter* painter ) 00711 { 00712 paintIntoRect( painter, mRect, mDiagram, mMarker, mBrush, mPen ); 00713 } 00714 00715 void KDChart::MarkerLayoutItem::paintIntoRect( 00716 QPainter* painter, 00717 const QRect& rect, 00718 AbstractDiagram* diagram, 00719 const MarkerAttributes& marker, 00720 const QBrush& brush, 00721 const QPen& pen ) 00722 { 00723 if( ! rect.isValid() ) 00724 return; 00725 00726 // The layout management may assign a larger rect than what we 00727 // wanted. We need to adjust the position. 00728 const QSize siz = marker.markerSize().toSize(); 00729 QPointF pos = rect.topLeft(); 00730 pos += QPointF( static_cast<qreal>(( rect.width() - siz.width()) / 2.0 ), 00731 static_cast<qreal>(( rect.height() - siz.height()) / 2.0 ) ); 00732 00733 #ifdef DEBUG_ITEMS_PAINT 00734 QPointF oldPos = pos; 00735 #endif 00736 00737 // And finally, drawMarker() assumes the position to be the center 00738 // of the marker, adjust again. 00739 pos += QPointF( static_cast<qreal>( siz.width() ) / 2.0, 00740 static_cast<qreal>( siz.height() )/ 2.0 ); 00741 00742 diagram->paintMarker( painter, marker, brush, pen, pos.toPoint(), siz ); 00743 00744 #ifdef DEBUG_ITEMS_PAINT 00745 const QPen oldPen( painter->pen() ); 00746 painter->setPen( Qt::red ); 00747 painter->drawRect( QRect(oldPos.toPoint(), siz) ); 00748 painter->setPen( oldPen ); 00749 #endif 00750 } 00751 00752 00753 KDChart::LineLayoutItem::LineLayoutItem( KDChart::AbstractDiagram* diagram, 00754 int length, 00755 const QPen& pen, 00756 Qt::Alignment legendLineSymbolAlignment, 00757 Qt::Alignment alignment ) 00758 : AbstractLayoutItem( alignment ) 00759 , mDiagram( diagram ) 00760 , mLength( length ) 00761 , mPen( pen ) 00762 , mLegendLineSymbolAlignment(legendLineSymbolAlignment) 00763 { 00764 //have a mini pen width 00765 if ( pen.width() < 2 ) 00766 mPen.setWidth( 2 ); 00767 } 00768 00769 Qt::Orientations KDChart::LineLayoutItem::expandingDirections() const 00770 { 00771 return 0; // Grow neither vertically nor horizontally 00772 } 00773 00774 QRect KDChart::LineLayoutItem::geometry() const 00775 { 00776 return mRect; 00777 } 00778 00779 bool KDChart::LineLayoutItem::isEmpty() const 00780 { 00781 return false; // never empty, otherwise the layout item would not exist 00782 } 00783 00784 QSize KDChart::LineLayoutItem::maximumSize() const 00785 { 00786 return sizeHint(); // PENDING(kalle) Review, quite inflexible 00787 } 00788 00789 QSize KDChart::LineLayoutItem::minimumSize() const 00790 { 00791 return sizeHint(); // PENDING(kalle) Review, quite inflexible 00792 } 00793 00794 void KDChart::LineLayoutItem::setGeometry( const QRect& r ) 00795 { 00796 mRect = r; 00797 } 00798 00799 QSize KDChart::LineLayoutItem::sizeHint() const 00800 { 00801 return QSize( mLength, mPen.width()+2 ); 00802 } 00803 00804 00805 void KDChart::LineLayoutItem::setLegendLineSymbolAlignment(Qt::Alignment legendLineSymbolAlignment) 00806 { 00807 if(mLegendLineSymbolAlignment == legendLineSymbolAlignment) 00808 return; 00809 00810 mLegendLineSymbolAlignment = legendLineSymbolAlignment; 00811 } 00812 00813 Qt::Alignment KDChart::LineLayoutItem::legendLineSymbolAlignment() const 00814 { 00815 return mLegendLineSymbolAlignment; 00816 } 00817 00818 void KDChart::LineLayoutItem::paint( QPainter* painter ) 00819 { 00820 paintIntoRect( painter, mRect, mPen, mLegendLineSymbolAlignment ); 00821 } 00822 00823 void KDChart::LineLayoutItem::paintIntoRect( 00824 QPainter* painter, 00825 const QRect& rect, 00826 const QPen& pen, 00827 Qt::Alignment lineAlignment) 00828 { 00829 if( ! rect.isValid() ) 00830 return; 00831 00832 const QPen oldPen = painter->pen(); 00833 painter->setPen( PrintingParameters::scalePen( pen ) ); 00834 qreal y = 0; 00835 if(lineAlignment == Qt::AlignTop) 00836 y = rect.top(); 00837 else if(lineAlignment == Qt::AlignBottom) 00838 y = rect.bottom(); 00839 else 00840 y = rect.center().y(); 00841 00842 painter->drawLine( QPointF( rect.left(), y ), 00843 QPointF( rect.right(), y ) ); 00844 painter->setPen( oldPen ); 00845 } 00846 00847 00848 KDChart::LineWithMarkerLayoutItem::LineWithMarkerLayoutItem( 00849 KDChart::AbstractDiagram* diagram, 00850 int lineLength, 00851 const QPen& linePen, 00852 int markerOffs, 00853 const MarkerAttributes& marker, 00854 const QBrush& markerBrush, 00855 const QPen& markerPen, 00856 Qt::Alignment alignment ) 00857 : AbstractLayoutItem( alignment ) 00858 , mDiagram( diagram ) 00859 , mLineLength( lineLength ) 00860 , mLinePen( linePen ) 00861 , mMarkerOffs( markerOffs ) 00862 , mMarker( marker ) 00863 , mMarkerBrush( markerBrush ) 00864 , mMarkerPen( markerPen ) 00865 { 00866 } 00867 00868 Qt::Orientations KDChart::LineWithMarkerLayoutItem::expandingDirections() const 00869 { 00870 return 0; // Grow neither vertically nor horizontally 00871 } 00872 00873 QRect KDChart::LineWithMarkerLayoutItem::geometry() const 00874 { 00875 return mRect; 00876 } 00877 00878 bool KDChart::LineWithMarkerLayoutItem::isEmpty() const 00879 { 00880 return false; // never empty, otherwise the layout item would not exist 00881 } 00882 00883 QSize KDChart::LineWithMarkerLayoutItem::maximumSize() const 00884 { 00885 return sizeHint(); // PENDING(kalle) Review, quite inflexible 00886 } 00887 00888 QSize KDChart::LineWithMarkerLayoutItem::minimumSize() const 00889 { 00890 return sizeHint(); // PENDING(kalle) Review, quite inflexible 00891 } 00892 00893 void KDChart::LineWithMarkerLayoutItem::setGeometry( const QRect& r ) 00894 { 00895 mRect = r; 00896 } 00897 00898 QSize KDChart::LineWithMarkerLayoutItem::sizeHint() const 00899 { 00900 const QSize sizeM = mMarker.markerSize().toSize(); 00901 const QSize sizeL = QSize( mLineLength, mLinePen.width()+2 ); 00902 return QSize( qMax(sizeM.width(), sizeL.width()), 00903 qMax(sizeM.height(), sizeL.height()) ); 00904 } 00905 00906 void KDChart::LineWithMarkerLayoutItem::paint( QPainter* painter ) 00907 { 00908 // paint the line over the full width, into the vertical middle of the rect 00909 LineLayoutItem::paintIntoRect( painter, mRect, mLinePen, Qt::AlignCenter ); 00910 00911 // paint the marker with the given offset from the left side of the line 00912 const QRect r( 00913 QPoint( mRect.x()+mMarkerOffs, mRect.y() ), 00914 QSize( mMarker.markerSize().toSize().width(), mRect.height() ) ); 00915 MarkerLayoutItem::paintIntoRect( 00916 painter, r, mDiagram, mMarker, mMarkerBrush, mMarkerPen ); 00917 } 00918 00919 KDChart::AutoSpacerLayoutItem::AutoSpacerLayoutItem( 00920 bool layoutIsAtTopPosition, QHBoxLayout *rightLeftLayout, 00921 bool layoutIsAtLeftPosition, QVBoxLayout *topBottomLayout ) 00922 : AbstractLayoutItem( Qt::AlignCenter ) 00923 , mLayoutIsAtTopPosition( layoutIsAtTopPosition ) 00924 , mRightLeftLayout( rightLeftLayout ) 00925 , mLayoutIsAtLeftPosition( layoutIsAtLeftPosition ) 00926 , mTopBottomLayout( topBottomLayout ) 00927 { 00928 } 00929 00930 Qt::Orientations KDChart::AutoSpacerLayoutItem::expandingDirections() const 00931 { 00932 return 0; // Grow neither vertically nor horizontally 00933 } 00934 00935 QRect KDChart::AutoSpacerLayoutItem::geometry() const 00936 { 00937 return mRect; 00938 } 00939 00940 bool KDChart::AutoSpacerLayoutItem::isEmpty() const 00941 { 00942 return true; // never empty, otherwise the layout item would not exist 00943 } 00944 00945 QSize KDChart::AutoSpacerLayoutItem::maximumSize() const 00946 { 00947 return sizeHint(); 00948 } 00949 00950 QSize KDChart::AutoSpacerLayoutItem::minimumSize() const 00951 { 00952 return sizeHint(); 00953 } 00954 00955 void KDChart::AutoSpacerLayoutItem::setGeometry( const QRect& r ) 00956 { 00957 mRect = r; 00958 } 00959 00960 00961 static void updateCommonBrush( QBrush& commonBrush, bool& bStart, const KDChart::AbstractArea& area ) 00962 { 00963 const KDChart::BackgroundAttributes ba( area.backgroundAttributes() ); 00964 const bool hasSimpleBrush = ( 00965 ! area.frameAttributes().isVisible() && 00966 ba.isVisible() && 00967 ba.pixmapMode() == KDChart::BackgroundAttributes::BackgroundPixmapModeNone && 00968 ba.brush().gradient() == 0 ); 00969 if( bStart ){ 00970 bStart = false; 00971 commonBrush = hasSimpleBrush ? ba.brush() : QBrush(); 00972 }else{ 00973 if( ! hasSimpleBrush || ba.brush() != commonBrush ) 00974 { 00975 commonBrush = QBrush(); 00976 } 00977 } 00978 } 00979 00980 QSize KDChart::AutoSpacerLayoutItem::sizeHint() const 00981 { 00982 QBrush commonBrush; 00983 bool bStart=true; 00984 // calculate the maximal overlap of the top/bottom axes: 00985 int topBottomOverlap = 0; 00986 if( mTopBottomLayout ){ 00987 for (int i = 0; i < mTopBottomLayout->count(); ++i){ 00988 AbstractArea* area = dynamic_cast<AbstractArea*>(mTopBottomLayout->itemAt(i)); 00989 if( area ){ 00990 //qDebug() << "AutoSpacerLayoutItem testing" << area; 00991 topBottomOverlap = qMax( topBottomOverlap, 00992 mLayoutIsAtLeftPosition ? area->rightOverlap() 00993 : area->leftOverlap() ); 00994 updateCommonBrush( commonBrush, bStart, *area ); 00995 } 00996 } 00997 } 00998 // calculate the maximal overlap of the left/right axes: 00999 int leftRightOverlap = 0; 01000 if( mRightLeftLayout ){ 01001 for (int i = 0; i < mRightLeftLayout->count(); ++i){ 01002 AbstractArea* area = dynamic_cast<AbstractArea*>(mRightLeftLayout->itemAt(i)); 01003 if( area ){ 01004 //qDebug() << "AutoSpacerLayoutItem testing" << area; 01005 leftRightOverlap = qMax( leftRightOverlap, 01006 mLayoutIsAtTopPosition ? area->bottomOverlap() 01007 : area->topOverlap() ); 01008 updateCommonBrush( commonBrush, bStart, *area ); 01009 } 01010 } 01011 } 01012 if( topBottomOverlap > 0 && leftRightOverlap > 0 ) 01013 mCommonBrush = commonBrush; 01014 else 01015 mCommonBrush = QBrush(); 01016 mCachedSize = QSize( topBottomOverlap, leftRightOverlap ); 01017 //qDebug() << mCachedSize; 01018 return mCachedSize; 01019 } 01020 01021 01022 void KDChart::AutoSpacerLayoutItem::paint( QPainter* painter ) 01023 { 01024 if( mParentLayout && mRect.isValid() && mCachedSize.isValid() && 01025 mCommonBrush.style() != Qt::NoBrush ) 01026 { 01027 QPoint p1( mRect.topLeft() ); 01028 QPoint p2( mRect.bottomRight() ); 01029 if( mLayoutIsAtLeftPosition ) 01030 p1.rx() += mCachedSize.width() - mParentLayout->spacing(); 01031 else 01032 p2.rx() -= mCachedSize.width() - mParentLayout->spacing(); 01033 if( mLayoutIsAtTopPosition ){ 01034 p1.ry() += mCachedSize.height() - mParentLayout->spacing() - 1; 01035 p2.ry() -= 1; 01036 }else 01037 p2.ry() -= mCachedSize.height() - mParentLayout->spacing() - 1; 01038 //qDebug() << mLayoutIsAtTopPosition << mLayoutIsAtLeftPosition; 01039 //qDebug() << mRect; 01040 //qDebug() << mParentLayout->margin(); 01041 //qDebug() << QRect( p1, p2 ); 01042 const QPoint oldBrushOrigin( painter->brushOrigin() ); 01043 const QBrush oldBrush( painter->brush() ); 01044 const QPen oldPen( painter->pen() ); 01045 const QPointF newTopLeft( painter->deviceMatrix().map( p1 ) ); 01046 painter->setBrushOrigin( newTopLeft ); 01047 painter->setBrush( mCommonBrush ); 01048 painter->setPen( Qt::NoPen ); 01049 painter->drawRect( QRect( p1, p2 ) ); 01050 painter->setBrushOrigin( oldBrushOrigin ); 01051 painter->setBrush( oldBrush ); 01052 painter->setPen( oldPen ); 01053 } 01054 // debug code: 01055 #if 0 01056 //qDebug() << "KDChart::AutoSpacerLayoutItem::paint()"; 01057 if( !mRect.isValid() ) 01058 return; 01059 01060 painter->drawRect( mRect ); 01061 painter->drawLine( QPointF( mRect.topLeft(), mRect.bottomRight() ) ); 01062 painter->drawLine( QPointF( mRect.topRight(), mRect.bottomLeft() ) ); 01063 #endif 01064 }