/**************************************************************************** ** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. ** ** This file is part of the KD Chart library. ** ** Licensees holding valid commercial KD Chart licenses may use this file in ** accordance with the KD Chart Commercial License Agreement provided with ** the Software. ** ** ** This file may be distributed and/or modified under the terms of the ** GNU General Public License version 2 and version 3 as published by the ** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. ** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ** Contact info@kdab.com if any conditions of this licensing are not ** clear to you. ** **********************************************************************/ #include "KDChartLayoutItems.h" #include "KDTextDocument.h" #include "KDChartAbstractArea.h" #include "KDChartAbstractDiagram.h" #include "KDChartBackgroundAttributes.h" #include "KDChartFrameAttributes.h" #include "KDChartPaintContext.h" #include "KDChartPainterSaver_p.h" #include "KDChartPrintingParameters.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #define PI 3.141592653589793 //#define DEBUG_ITEMS_PAINT /** Inform the item about its widget: This enables the item, to trigger that widget's update, whenever the size of the item's contents has changed. Thus, you need to call setParentWidget on every item, that has a non-fixed size. */ void KDChart::AbstractLayoutItem::setParentWidget( QWidget* widget ) { mParent = widget; } void KDChart::AbstractLayoutItem::paintAll( QPainter& painter ) { paint( &painter ); } /** * Default impl: Paint the complete item using its layouted position and size. */ void KDChart::AbstractLayoutItem::paintCtx( PaintContext* context ) { if( context ) paint( context->painter() ); } /** Report changed size hint: ask the parent widget to recalculate the layout. */ void KDChart::AbstractLayoutItem::sizeHintChanged()const { // This is exactly like what QWidget::updateGeometry does. // qDebug("KDChart::AbstractLayoutItem::sizeHintChanged() called"); if( mParent ) { if ( mParent->layout() ) mParent->layout()->invalidate(); else QApplication::postEvent( mParent, new QEvent( QEvent::LayoutRequest ) ); } } KDChart::TextBubbleLayoutItem::TextBubbleLayoutItem( const QString& text, const KDChart::TextAttributes& attributes, const QObject* area, KDChartEnums::MeasureOrientation orientation, Qt::Alignment alignment ) : AbstractLayoutItem( alignment ), m_text( new TextLayoutItem( text, attributes, area, orientation, alignment ) ) { } KDChart::TextBubbleLayoutItem::TextBubbleLayoutItem() : AbstractLayoutItem( Qt::AlignLeft ), m_text( new TextLayoutItem() ) { } KDChart::TextBubbleLayoutItem::~TextBubbleLayoutItem() { delete m_text; } void KDChart::TextBubbleLayoutItem::setAutoReferenceArea( const QObject* area ) { m_text->setAutoReferenceArea( area ); } const QObject* KDChart::TextBubbleLayoutItem::autoReferenceArea() const { return m_text->autoReferenceArea(); } void KDChart::TextBubbleLayoutItem::setText( const QString& text ) { m_text->setText( text ); } QString KDChart::TextBubbleLayoutItem::text() const { return m_text->text(); } void KDChart::TextBubbleLayoutItem::setTextAttributes( const TextAttributes& a ) { m_text->setTextAttributes( a ); } KDChart::TextAttributes KDChart::TextBubbleLayoutItem::textAttributes() const { return m_text->textAttributes(); } bool KDChart::TextBubbleLayoutItem::isEmpty() const { return m_text->isEmpty(); } Qt::Orientations KDChart::TextBubbleLayoutItem::expandingDirections() const { return m_text->expandingDirections(); } QSize KDChart::TextBubbleLayoutItem::maximumSize() const { const int border = borderWidth(); return m_text->maximumSize() + QSize( 2 * border, 2 * border ); } QSize KDChart::TextBubbleLayoutItem::minimumSize() const { const int border = borderWidth(); return m_text->minimumSize() + QSize( 2 * border, 2 * border ); } QSize KDChart::TextBubbleLayoutItem::sizeHint() const { const int border = borderWidth(); return m_text->sizeHint() + QSize( 2 * border, 2 * border ); } void KDChart::TextBubbleLayoutItem::setGeometry( const QRect& r ) { const int border = borderWidth(); m_text->setGeometry( r.adjusted( border, border, -border, -border ) ); } QRect KDChart::TextBubbleLayoutItem::geometry() const { const int border = borderWidth(); return m_text->geometry().adjusted( -border, -border, border, border ); } void KDChart::TextBubbleLayoutItem::paint( QPainter* painter ) { const QPen oldPen = painter->pen(); const QBrush oldBrush = painter->brush(); painter->setPen( Qt::black ); painter->setBrush( QColor( 255, 255, 220 ) ); painter->drawRoundRect( geometry(), 10 ); painter->setPen( oldPen ); painter->setBrush( oldBrush ); m_text->paint( painter ); } int KDChart::TextBubbleLayoutItem::borderWidth() const { return 1; } KDChart::TextLayoutItem::TextLayoutItem( const QString& text, const KDChart::TextAttributes& attributes, const QObject* area, KDChartEnums::MeasureOrientation orientation, Qt::Alignment alignment ) : AbstractLayoutItem( alignment ) , mText( text ) , mTextAlignment( alignment ) , mAttributes( attributes ) , mAutoReferenceArea( area ) , mAutoReferenceOrientation( orientation ) , cachedSizeHint() // default this to invalid to force just-in-time calculation before first use of sizeHint() , cachedFontSize( 0.0 ) , cachedFont( mAttributes.font() ) { } KDChart::TextLayoutItem::TextLayoutItem() : AbstractLayoutItem( Qt::AlignLeft ) , mText() , mTextAlignment( Qt::AlignLeft ) , mAttributes() , mAutoReferenceArea( 0 ) , mAutoReferenceOrientation( KDChartEnums::MeasureOrientationHorizontal ) , cachedSizeHint() // default this to invalid to force just-in-time calculation before first use of sizeHint() , cachedFontSize( 0.0 ) , cachedFont( mAttributes.font() ) { } void KDChart::TextLayoutItem::setAutoReferenceArea( const QObject* area ) { mAutoReferenceArea = area; cachedSizeHint = QSize(); sizeHint(); } const QObject* KDChart::TextLayoutItem::autoReferenceArea() const { return mAutoReferenceArea; } void KDChart::TextLayoutItem::setText(const QString & text) { mText = text; cachedSizeHint = QSize(); sizeHint(); if( mParent ) mParent->update(); } QString KDChart::TextLayoutItem::text() const { return mText; } void KDChart::TextLayoutItem::setTextAlignment( Qt::Alignment alignment) { if( mTextAlignment == alignment ) return; mTextAlignment = alignment; if( mParent ) mParent->update(); } Qt::Alignment KDChart::TextLayoutItem::textAlignment() const { return mTextAlignment; } /** \brief Use this to specify the text attributes to be used for this item. \sa textAttributes */ void KDChart::TextLayoutItem::setTextAttributes( const TextAttributes &a ) { mAttributes = a; cachedFont = a.font(); cachedSizeHint = QSize(); // invalidate size hint sizeHint(); if( mParent ) mParent->update(); } /** Returns the text attributes to be used for this item. \sa setTextAttributes */ KDChart::TextAttributes KDChart::TextLayoutItem::textAttributes() const { return mAttributes; } Qt::Orientations KDChart::TextLayoutItem::expandingDirections() const { return 0; // Grow neither vertically nor horizontally } QRect KDChart::TextLayoutItem::geometry() const { return mRect; } bool KDChart::TextLayoutItem::isEmpty() const { return false; // never empty, otherwise the layout item would not exist } QSize KDChart::TextLayoutItem::maximumSize() const { return sizeHint(); // PENDING(kalle) Review, quite inflexible } QSize KDChart::TextLayoutItem::minimumSize() const { return sizeHint(); // PENDING(kalle) Review, quite inflexible } void KDChart::TextLayoutItem::setGeometry( const QRect& r ) { mRect = r; } QPointF rotatedPoint( const QPointF& pt, qreal rotation, const QPointF& center ) { const qreal angle = PI * rotation / 180.0; const qreal cosAngle = cos( angle ); const qreal sinAngle = -sin( angle ); return QPointF( (cosAngle * ( pt.x() - center.x() ) + sinAngle * ( pt.y() - center.y() ) ), (cosAngle * ( pt.y() - center.y() ) - sinAngle * ( pt.x() - center.x() ) ) ) + center; } QRectF rotatedRect( const QRectF& oldRect, qreal angleInt, const QPointF& center ) { const QRect rect( oldRect.translated( center ).toRect() ); const qreal angle = PI * angleInt / 180.0; const qreal cosAngle = cos( angle ); const qreal sinAngle = sin( angle ); QMatrix rotationMatrix(cosAngle, sinAngle, -sinAngle, cosAngle, 0, 0); QPolygon rotPts; rotPts << rotationMatrix.map(rect.topLeft()) //QPoint(0,0) << rotationMatrix.map(rect.topRight()) << rotationMatrix.map(rect.bottomRight()) << rotationMatrix.map(rect.bottomLeft()); //<< rotatedPoint(rect.topRight(), angleInt, center).toPoint() //<< rotatedPoint(rect.bottomRight(), angleInt, center).toPoint() //<< rotatedPoint(rect.bottomLeft(), angleInt, center).toPoint(); return rotPts.boundingRect(); /* const QPointF topLeft( rotatedPoint( oldRect.topLeft(), angle, center ) ); const QPointF topRight( rotatedPoint( oldRect.topRight(), angle, center ) ); const QPointF bottomLeft( rotatedPoint( oldRect.bottomLeft(), angle, center ) ); const QPointF bottomRight( rotatedPoint( oldRect.bottomRight(), angle, center ) ); const qreal x = qMin( qMin( topLeft.x(), topRight.x() ), qMin( bottomLeft.x(), topLeft.x() ) ); const qreal y = qMin( qMin( topLeft.y(), topRight.y() ), qMin( bottomLeft.y(), topLeft.y() ) ); const qreal width = qMax( qMax( topLeft.x(), topRight.x() ), qMax( bottomLeft.x(), topLeft.x() ) ) - x; const qreal height = qMax( qMax( topLeft.y(), topRight.y() ), qMax( bottomLeft.y(), topLeft.y() ) ) - y; return QRectF( x, y, width, height ); */ } /* QRectF rotatedRect( const QRectF& rect, qreal angle ) { const QPointF topLeft( rotatedPoint( rect.topLeft(), angle ) ); //const QPointF topRight( rotatedPoint( rect.topRight(), angle ) ); //const QPointF bottomLeft( rotatedPoint( rect.bottomLeft(), angle ) ); #if 1 const QPointF bottomRight( rotatedPoint( rect.bottomRight(), angle ) ); const QRectF result( topLeft, QSizeF( bottomRight.x() - topLeft.x(), bottomRight.y() - topLeft.y() ) ); #else const QPointF siz( rotatedPoint( QPointF( rect.size().width(), rect.size().height() ), angle ) ); const QRectF result( topLeft, QSizeF( siz.x(), //bottomRight.x() - topLeft.x(), siz.y() ) ); //bottomRight.y() - topLeft.y() ) ); //qDebug() << "angle" << angle << "\nbefore:" << rect << "\n after:" << result; #endif return result; }*/ qreal KDChart::TextLayoutItem::fitFontSizeToGeometry() const { QFont f = realFont(); const qreal origResult = f.pointSizeF(); qreal result = origResult; const qreal minSize = mAttributes.minimalFontSize().value(); const QSize mySize = geometry().size(); if( mySize.isNull() ) return result; const QString t = text(); QFontMetrics fm( f ); while( true ) { const QSizeF textSize = rotatedRect( fm.boundingRect( t ), mAttributes.rotation() ).normalized().size(); if( textSize.height() <= mySize.height() && textSize.width() <= mySize.width() ) return result; result -= 0.5; if ( minSize > 0 && result < minSize ) return result += 0.5; if( result <= 0.0 ) return origResult; f.setPointSizeF( result ); fm = QFontMetrics( f ); } } qreal KDChart::TextLayoutItem::realFontSize() const { return mAttributes.calculatedFontSize( mAutoReferenceArea, mAutoReferenceOrientation ); } bool KDChart::TextLayoutItem::realFontWasRecalculated() const { const qreal fntSiz = realFontSize(); const bool bRecalcDone = ( ( ! cachedSizeHint.isValid() ) || ( cachedFontSize != fntSiz ) ); if( bRecalcDone && fntSiz > 0.0 ){ cachedFontSize = fntSiz; cachedFont.setPointSizeF( fntSiz ); } return bRecalcDone; } QFont KDChart::TextLayoutItem::realFont() const { realFontWasRecalculated(); // we can safely ignore the boolean return value return cachedFont; } QPolygon KDChart::TextLayoutItem::rotatedCorners() const { // the angle in rad const qreal angle = mAttributes.rotation() * PI / 180.0; QSize size = unrotatedSizeHint(); // my P1 - P4 (the four points of the rotated area) QPointF P1( size.height() * sin( angle ), 0 ); QPointF P2( size.height() * sin( angle ) + size.width() * cos( angle ), size.width() * sin( angle ) ); QPointF P3( size.width() * cos( angle ), size.width() * sin( angle ) + size.height() * cos( angle ) ); QPointF P4( 0, size.height() * cos( angle ) ); QPolygon result; result << P1.toPoint() << P2.toPoint() << P3.toPoint() << P4.toPoint(); return result; } bool KDChart::TextLayoutItem::intersects( const TextLayoutItem& other, const QPointF& myPos, const QPointF& otherPos ) const { return intersects( other, myPos.toPoint(), otherPos.toPoint() ); } bool KDChart::TextLayoutItem::intersects( const TextLayoutItem& other, const QPoint& myPos, const QPoint& otherPos ) const { if ( mAttributes.rotation() != other.mAttributes.rotation() ) { // that's the code for the common case: the rotation angles don't need to match here QPolygon myPolygon( rotatedCorners() ); QPolygon otherPolygon( other.rotatedCorners() ); // move the polygons to their positions myPolygon.translate( myPos ); otherPolygon.translate( otherPos ); // create regions out of it QRegion myRegion( myPolygon ); QRegion otherRegion( otherPolygon ); // now the question - do they intersect or not? return ! myRegion.intersect( otherRegion ).isEmpty(); } else { // and that's the code for the special case: the rotation angles match, which is less time consuming in calculation const qreal angle = mAttributes.rotation() * PI / 180.0; // both sizes const QSizeF mySize( unrotatedSizeHint() ); const QSizeF otherSize( other.unrotatedSizeHint() ); // that's myP1 relative to myPos QPointF myP1( mySize.height() * sin( angle ), 0.0 ); // that's otherP1 to myPos QPointF otherP1 = QPointF( otherSize.height() * sin( angle ), 0.0 ) + otherPos - myPos; // now rotate both points the negative angle around myPos myP1 = QPointF( myP1.x() * cos( -angle ), myP1.x() * sin( -angle ) ); qreal r = sqrt( otherP1.x() * otherP1.x() + otherP1.y() * otherP1.y() ); otherP1 = QPointF( r * cos( -angle ), r * sin( -angle ) ); // finally we look, whether both rectangles intersect or even not const bool res = QRectF( myP1, mySize ).intersects( QRectF( otherP1, otherSize ) ); //qDebug() << res << QRectF( myP1, mySize ) << QRectF( otherP1, otherSize ); return res; } } QSize KDChart::TextLayoutItem::sizeHint() const { QPoint dummy; return sizeHintAndRotatedCorners(dummy,dummy,dummy,dummy); } QSize KDChart::TextLayoutItem::sizeHintAndRotatedCorners( QPoint& topLeftPt, QPoint& topRightPt, QPoint& bottomRightPt, QPoint& bottomLeftPt) const { if( realFontWasRecalculated() || mAttributes.rotation() ) { const QSize newSizeHint( calcSizeHint( cachedFont, topLeftPt, topRightPt, bottomRightPt, bottomLeftPt ) ); if( newSizeHint != cachedSizeHint ){ cachedSizeHint = newSizeHint; sizeHintChanged(); } cachedTopLeft = topLeftPt; cachedTopRight = topRightPt; cachedBottomRight = bottomRightPt; cachedBottomLeft = bottomLeftPt; }else{ topLeftPt = cachedTopLeft; topRightPt = cachedTopRight; bottomRightPt = cachedBottomRight; bottomLeftPt = cachedBottomLeft; } //qDebug() << "-------- KDChart::TextLayoutItem::sizeHint() returns:"<pixelMetric( QStyle::PM_ButtonMargin, 0, 0 ); // fine-tuning for small font sizes: the frame must not be so big, if the font is tiny frame = qMin( frame, ret.height() * 2 / 3 ); //qDebug() << "frame:"<< frame; ret += QSize( frame, frame ); return ret; //const QFontMetricsF met( fnt, GlobalMeasureScaling::paintDevice() ); //const int frame = QApplication::style()->pixelMetric( QStyle::PM_ButtonMargin, 0, 0 ); //return // met.boundingRect( mText ).size().toSize() + QSize( frame, frame ); } QSize KDChart::TextLayoutItem::calcSizeHint( QFont fnt, QPoint& topLeftPt, QPoint& topRightPt, QPoint& bottomRightPt, QPoint& bottomLeftPt ) const { const QSize siz( unrotatedSizeHint( fnt )); //qDebug() << "-------- siz: "<setFont( f ); QRectF rect( geometry() ); // #ifdef DEBUG_ITEMS_PAINT // painter->setPen( Qt::black ); // painter->drawRect( rect ); // #endif painter->translate( rect.center() ); rect.moveTopLeft( QPointF( - rect.width() / 2, - rect.height() / 2 ) ); #ifdef DEBUG_ITEMS_PAINT painter->setPen( Qt::blue ); painter->drawRect( rect ); painter->drawRect( QRect(QPoint((rect.topLeft().toPoint() + rect.bottomLeft().toPoint()) / 2 - QPoint(2,2)), QSize(3,3)) ); //painter->drawRect( QRect(QPoint((rect.topRight().toPoint() + rect.bottomRight().toPoint()) / 2 - QPoint(2,2)), QSize(3,3)) ); #endif painter->rotate( mAttributes.rotation() ); rect = rotatedRect( rect, mAttributes.rotation() ); #ifdef DEBUG_ITEMS_PAINT painter->setPen( Qt::red ); painter->drawRect( rect ); painter->drawRect( QRect(QPoint((rect.topLeft().toPoint() + rect.bottomLeft().toPoint()) / 2 - QPoint(2,2)), QSize(3,3)) ); //painter->drawRect( QRect(QPoint((rect.topRight().toPoint() + rect.bottomRight().toPoint()) / 2 - QPoint(2,2)), QSize(3,3)) ); #endif painter->setPen( PrintingParameters::scalePen( mAttributes.pen() ) ); QFontMetrics fontMetrics( f ); const int AHight = fontMetrics.boundingRect( QChar::fromAscii( 'A' ) ).height(); const qreal AVCenter = fontMetrics.ascent() - AHight / 2.0; // Make sure that capital letters are vertically centered. This looks much // better than just centering the text's bounding rect. rect.translate( 0.0, rect.height() / 2.0 - AVCenter ); //painter->drawText( rect, Qt::AlignHCenter | Qt::AlignTop, mText ); painter->drawText( rect, mTextAlignment, mText ); // if ( calcSizeHint( realFont() ).width() > rect.width() ) // qDebug() << "rect.width()" << rect.width() << "text.width()" << calcSizeHint( realFont() ).width(); // // //painter->drawText( rect, Qt::AlignHCenter | Qt::AlignVCenter, mText ); } KDChart::HorizontalLineLayoutItem::HorizontalLineLayoutItem() : AbstractLayoutItem( Qt::AlignCenter ) { } Qt::Orientations KDChart::HorizontalLineLayoutItem::expandingDirections() const { return Qt::Vertical|Qt::Horizontal; // Grow both vertically, and horizontally } QRect KDChart::HorizontalLineLayoutItem::geometry() const { return mRect; } bool KDChart::HorizontalLineLayoutItem::isEmpty() const { return false; // never empty, otherwise the layout item would not exist } QSize KDChart::HorizontalLineLayoutItem::maximumSize() const { return QSize( QWIDGETSIZE_MAX, QWIDGETSIZE_MAX ); } QSize KDChart::HorizontalLineLayoutItem::minimumSize() const { return QSize( 0, 0 ); } void KDChart::HorizontalLineLayoutItem::setGeometry( const QRect& r ) { mRect = r; } QSize KDChart::HorizontalLineLayoutItem::sizeHint() const { return QSize( -1, 3 ); // see qframe.cpp } void KDChart::HorizontalLineLayoutItem::paint( QPainter* painter ) { if( !mRect.isValid() ) return; painter->drawLine( QPointF( mRect.left(), mRect.center().y() ), QPointF( mRect.right(), mRect.center().y() ) ); } KDChart::VerticalLineLayoutItem::VerticalLineLayoutItem() : AbstractLayoutItem( Qt::AlignCenter ) { } Qt::Orientations KDChart::VerticalLineLayoutItem::expandingDirections() const { return Qt::Vertical|Qt::Vertical; // Grow both vertically, and horizontally } QRect KDChart::VerticalLineLayoutItem::geometry() const { return mRect; } bool KDChart::VerticalLineLayoutItem::isEmpty() const { return false; // never empty, otherwise the layout item would not exist } QSize KDChart::VerticalLineLayoutItem::maximumSize() const { return QSize( QWIDGETSIZE_MAX, QWIDGETSIZE_MAX ); } QSize KDChart::VerticalLineLayoutItem::minimumSize() const { return QSize( 0, 0 ); } void KDChart::VerticalLineLayoutItem::setGeometry( const QRect& r ) { mRect = r; } QSize KDChart::VerticalLineLayoutItem::sizeHint() const { return QSize( 3, -1 ); // see qframe.cpp } void KDChart::VerticalLineLayoutItem::paint( QPainter* painter ) { if( !mRect.isValid() ) return; painter->drawLine( QPointF( mRect.center().x(), mRect.top() ), QPointF( mRect.center().x(), mRect.bottom() ) ); } KDChart::MarkerLayoutItem::MarkerLayoutItem( KDChart::AbstractDiagram* diagram, const MarkerAttributes& marker, const QBrush& brush, const QPen& pen, Qt::Alignment alignment ) : AbstractLayoutItem( alignment ) , mDiagram( diagram ) , mMarker( marker ) , mBrush( brush ) , mPen( pen ) { } Qt::Orientations KDChart::MarkerLayoutItem::expandingDirections() const { return 0; // Grow neither vertically nor horizontally } QRect KDChart::MarkerLayoutItem::geometry() const { return mRect; } bool KDChart::MarkerLayoutItem::isEmpty() const { return false; // never empty, otherwise the layout item would not exist } QSize KDChart::MarkerLayoutItem::maximumSize() const { return sizeHint(); // PENDING(kalle) Review, quite inflexible } QSize KDChart::MarkerLayoutItem::minimumSize() const { return sizeHint(); // PENDING(kalle) Review, quite inflexible } void KDChart::MarkerLayoutItem::setGeometry( const QRect& r ) { mRect = r; } QSize KDChart::MarkerLayoutItem::sizeHint() const { //qDebug() << "KDChart::MarkerLayoutItem::sizeHint() returns:"<(( rect.width() - siz.width()) / 2.0 ), static_cast(( rect.height() - siz.height()) / 2.0 ) ); #ifdef DEBUG_ITEMS_PAINT QPointF oldPos = pos; #endif // And finally, drawMarker() assumes the position to be the center // of the marker, adjust again. pos += QPointF( static_cast( siz.width() ) / 2.0, static_cast( siz.height() )/ 2.0 ); diagram->paintMarker( painter, marker, brush, pen, pos.toPoint(), siz ); #ifdef DEBUG_ITEMS_PAINT const QPen oldPen( painter->pen() ); painter->setPen( Qt::red ); painter->drawRect( QRect(oldPos.toPoint(), siz) ); painter->setPen( oldPen ); #endif } KDChart::LineLayoutItem::LineLayoutItem( KDChart::AbstractDiagram* diagram, int length, const QPen& pen, Qt::Alignment alignment ) : AbstractLayoutItem( alignment ) , mDiagram( diagram ) , mLength( length ) , mPen( pen ) { //have a mini pen width if ( pen.width() < 2 ) mPen.setWidth( 2 ); } Qt::Orientations KDChart::LineLayoutItem::expandingDirections() const { return 0; // Grow neither vertically nor horizontally } QRect KDChart::LineLayoutItem::geometry() const { return mRect; } bool KDChart::LineLayoutItem::isEmpty() const { return false; // never empty, otherwise the layout item would not exist } QSize KDChart::LineLayoutItem::maximumSize() const { return sizeHint(); // PENDING(kalle) Review, quite inflexible } QSize KDChart::LineLayoutItem::minimumSize() const { return sizeHint(); // PENDING(kalle) Review, quite inflexible } void KDChart::LineLayoutItem::setGeometry( const QRect& r ) { mRect = r; } QSize KDChart::LineLayoutItem::sizeHint() const { return QSize( mLength, mPen.width()+2 ); } void KDChart::LineLayoutItem::paint( QPainter* painter ) { paintIntoRect( painter, mRect, mPen ); } void KDChart::LineLayoutItem::paintIntoRect( QPainter* painter, const QRect& rect, const QPen& pen ) { if( ! rect.isValid() ) return; const QPen oldPen = painter->pen(); painter->setPen( PrintingParameters::scalePen( pen ) ); const qreal y = rect.center().y(); painter->drawLine( QPointF( rect.left(), y ), QPointF( rect.right(), y ) ); painter->setPen( oldPen ); } KDChart::LineWithMarkerLayoutItem::LineWithMarkerLayoutItem( KDChart::AbstractDiagram* diagram, int lineLength, const QPen& linePen, int markerOffs, const MarkerAttributes& marker, const QBrush& markerBrush, const QPen& markerPen, Qt::Alignment alignment ) : AbstractLayoutItem( alignment ) , mDiagram( diagram ) , mLineLength( lineLength ) , mLinePen( linePen ) , mMarkerOffs( markerOffs ) , mMarker( marker ) , mMarkerBrush( markerBrush ) , mMarkerPen( markerPen ) { } Qt::Orientations KDChart::LineWithMarkerLayoutItem::expandingDirections() const { return 0; // Grow neither vertically nor horizontally } QRect KDChart::LineWithMarkerLayoutItem::geometry() const { return mRect; } bool KDChart::LineWithMarkerLayoutItem::isEmpty() const { return false; // never empty, otherwise the layout item would not exist } QSize KDChart::LineWithMarkerLayoutItem::maximumSize() const { return sizeHint(); // PENDING(kalle) Review, quite inflexible } QSize KDChart::LineWithMarkerLayoutItem::minimumSize() const { return sizeHint(); // PENDING(kalle) Review, quite inflexible } void KDChart::LineWithMarkerLayoutItem::setGeometry( const QRect& r ) { mRect = r; } QSize KDChart::LineWithMarkerLayoutItem::sizeHint() const { const QSize sizeM = mMarker.markerSize().toSize(); const QSize sizeL = QSize( mLineLength, mLinePen.width()+2 ); return QSize( qMax(sizeM.width(), sizeL.width()), qMax(sizeM.height(), sizeL.height()) ); } void KDChart::LineWithMarkerLayoutItem::paint( QPainter* painter ) { // paint the line over the full width, into the vertical middle of the rect LineLayoutItem::paintIntoRect( painter, mRect, mLinePen ); // paint the marker with the given offset from the left side of the line const QRect r( QPoint( mRect.x()+mMarkerOffs, mRect.y() ), QSize( mMarker.markerSize().toSize().width(), mRect.height() ) ); MarkerLayoutItem::paintIntoRect( painter, r, mDiagram, mMarker, mMarkerBrush, mMarkerPen ); } KDChart::AutoSpacerLayoutItem::AutoSpacerLayoutItem( bool layoutIsAtTopPosition, QHBoxLayout *rightLeftLayout, bool layoutIsAtLeftPosition, QVBoxLayout *topBottomLayout ) : AbstractLayoutItem( Qt::AlignCenter ) , mLayoutIsAtTopPosition( layoutIsAtTopPosition ) , mRightLeftLayout( rightLeftLayout ) , mLayoutIsAtLeftPosition( layoutIsAtLeftPosition ) , mTopBottomLayout( topBottomLayout ) { } Qt::Orientations KDChart::AutoSpacerLayoutItem::expandingDirections() const { return 0; // Grow neither vertically nor horizontally } QRect KDChart::AutoSpacerLayoutItem::geometry() const { return mRect; } bool KDChart::AutoSpacerLayoutItem::isEmpty() const { return true; // never empty, otherwise the layout item would not exist } QSize KDChart::AutoSpacerLayoutItem::maximumSize() const { return sizeHint(); } QSize KDChart::AutoSpacerLayoutItem::minimumSize() const { return sizeHint(); } void KDChart::AutoSpacerLayoutItem::setGeometry( const QRect& r ) { mRect = r; } static void updateCommonBrush( QBrush& commonBrush, bool& bStart, const KDChart::AbstractArea& area ) { const KDChart::BackgroundAttributes ba( area.backgroundAttributes() ); const bool hasSimpleBrush = ( ! area.frameAttributes().isVisible() && ba.isVisible() && ba.pixmapMode() == KDChart::BackgroundAttributes::BackgroundPixmapModeNone && ba.brush().gradient() == 0 ); if( bStart ){ bStart = false; commonBrush = hasSimpleBrush ? ba.brush() : QBrush(); }else{ if( ! hasSimpleBrush || ba.brush() != commonBrush ) { commonBrush = QBrush(); } } } QSize KDChart::AutoSpacerLayoutItem::sizeHint() const { QBrush commonBrush; bool bStart=true; // calculate the maximal overlap of the top/bottom axes: int topBottomOverlap = 0; if( mTopBottomLayout ){ for (int i = 0; i < mTopBottomLayout->count(); ++i){ AbstractArea* area = dynamic_cast(mTopBottomLayout->itemAt(i)); if( area ){ //qDebug() << "AutoSpacerLayoutItem testing" << area; topBottomOverlap = mLayoutIsAtLeftPosition ? qMax( topBottomOverlap, area->rightOverlap() ) : qMax( topBottomOverlap, area->leftOverlap() ); updateCommonBrush( commonBrush, bStart, *area ); } } } // calculate the maximal overlap of the left/right axes: int leftRightOverlap = 0; if( mRightLeftLayout ){ for (int i = 0; i < mRightLeftLayout->count(); ++i){ AbstractArea* area = dynamic_cast(mRightLeftLayout->itemAt(i)); if( area ){ //qDebug() << "AutoSpacerLayoutItem testing" << area; leftRightOverlap = mLayoutIsAtTopPosition ? qMax( leftRightOverlap, area->bottomOverlap() ) : qMax( leftRightOverlap, area->topOverlap() ); updateCommonBrush( commonBrush, bStart, *area ); } } } if( topBottomOverlap > 0 && leftRightOverlap > 0 ) mCommonBrush = commonBrush; else mCommonBrush = QBrush(); mCachedSize = QSize( topBottomOverlap, leftRightOverlap ); //qDebug() << mCachedSize; return mCachedSize; } void KDChart::AutoSpacerLayoutItem::paint( QPainter* painter ) { if( mParentLayout && mRect.isValid() && mCachedSize.isValid() && mCommonBrush.style() != Qt::NoBrush ) { QPoint p1( mRect.topLeft() ); QPoint p2( mRect.bottomRight() ); if( mLayoutIsAtLeftPosition ) p1.rx() += mCachedSize.width() - mParentLayout->spacing(); else p2.rx() -= mCachedSize.width() - mParentLayout->spacing(); if( mLayoutIsAtTopPosition ){ p1.ry() += mCachedSize.height() - mParentLayout->spacing() - 1; p2.ry() -= 1; }else p2.ry() -= mCachedSize.height() - mParentLayout->spacing() - 1; //qDebug() << mLayoutIsAtTopPosition << mLayoutIsAtLeftPosition; //qDebug() << mRect; //qDebug() << mParentLayout->margin(); //qDebug() << QRect( p1, p2 ); const QPoint oldBrushOrigin( painter->brushOrigin() ); const QBrush oldBrush( painter->brush() ); const QPen oldPen( painter->pen() ); const QPointF newTopLeft( painter->deviceMatrix().map( p1 ) ); painter->setBrushOrigin( newTopLeft ); painter->setBrush( mCommonBrush ); painter->setPen( Qt::NoPen ); painter->drawRect( QRect( p1, p2 ) ); painter->setBrushOrigin( oldBrushOrigin ); painter->setBrush( oldBrush ); painter->setPen( oldPen ); } // debug code: #if 0 //qDebug() << "KDChart::AutoSpacerLayoutItem::paint()"; if( !mRect.isValid() ) return; painter->drawRect( mRect ); painter->drawLine( QPointF( mRect.x(), mRect.top() ), QPointF( mRect.right(), mRect.bottom() ) ); painter->drawLine( QPointF( mRect.right(), mRect.top() ), QPointF( mRect.x(), mRect.bottom() ) ); #endif }