File indexing completed on 2024-05-12 15:54:16

0001 /*
0002  * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB.  All rights reserved.
0003  *
0004  * This file is part of the KD Chart library.
0005  *
0006  * This program is free software; you can redistribute it and/or
0007  * modify it under the terms of the GNU General Public License as
0008  * published by the Free Software Foundation; either version 2 of
0009  * the License, or (at your option) any later version.
0010  *
0011  * This program is distributed in the hope that it will be useful,
0012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014  * GNU General Public License for more details.
0015  *
0016  * You should have received a copy of the GNU General Public License
0017  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
0018  */
0019 
0020 #include "KChartLayoutItems.h"
0021 
0022 #include "KTextDocument.h"
0023 #include "KChartAbstractArea.h"
0024 #include "KChartAbstractDiagram.h"
0025 #include "KChartBackgroundAttributes.h"
0026 #include "KChartFrameAttributes.h"
0027 #include "KChartPaintContext.h"
0028 #include "KChartPainterSaver_p.h"
0029 #include "KChartPrintingParameters.h"
0030 #include "KChartMath_p.h"
0031 
0032 #include <QTextCursor>
0033 #include <QTextBlockFormat>
0034 #include <QTextDocumentFragment>
0035 #include <QAbstractTextDocumentLayout>
0036 #include <QLayout>
0037 #include <QPainter>
0038 #include <QDebug>
0039 #include <QCoreApplication>
0040 #include <QApplication>
0041 #include <QStringList>
0042 #include <QStyle>
0043 
0044 
0045 //#define DEBUG_ITEMS_PAINT
0046 
0047 void KChart::AbstractLayoutItem::setParentWidget( QWidget* widget )
0048 {
0049     mParent = widget;
0050 }
0051 
0052 void KChart::AbstractLayoutItem::paintAll( QPainter& painter )
0053 {
0054     paint( &painter );
0055 }
0056 
0057 void KChart::AbstractLayoutItem::paintCtx( PaintContext* context )
0058 {
0059     if ( context )
0060         paint( context->painter() );
0061 }
0062 
0063 void KChart::AbstractLayoutItem::sizeHintChanged() const
0064 {
0065     // This is exactly like what QWidget::updateGeometry does.
0066 //  qDebug("KChart::AbstractLayoutItem::sizeHintChanged() called");
0067     if ( mParent ) {
0068         if ( mParent->layout() )
0069             mParent->layout()->invalidate();
0070         else
0071             QApplication::postEvent( mParent, new QEvent( QEvent::LayoutRequest ) );
0072     }
0073 }
0074 
0075 KChart::TextBubbleLayoutItem::TextBubbleLayoutItem( const QString& text,
0076                                          const KChart::TextAttributes& attributes,
0077                                          const QObject* area,
0078                                          KChartEnums::MeasureOrientation orientation,
0079                                          Qt::Alignment alignment )
0080     : AbstractLayoutItem( alignment ),
0081       m_text( new TextLayoutItem( text, attributes, area, orientation, alignment ) )
0082 {
0083 }
0084 
0085 KChart::TextBubbleLayoutItem::TextBubbleLayoutItem()
0086     : AbstractLayoutItem( Qt::AlignLeft ),
0087       m_text( new TextLayoutItem() )
0088 {
0089 }
0090 
0091 KChart::TextBubbleLayoutItem::~TextBubbleLayoutItem()
0092 {
0093     delete m_text;
0094 }
0095 
0096 void KChart::TextBubbleLayoutItem::setAutoReferenceArea( const QObject* area )
0097 {
0098     m_text->setAutoReferenceArea( area );
0099 }
0100 
0101 const QObject* KChart::TextBubbleLayoutItem::autoReferenceArea() const
0102 {
0103     return m_text->autoReferenceArea();
0104 }
0105 
0106 void KChart::TextBubbleLayoutItem::setText( const QString& text )
0107 {
0108     m_text->setText( text );
0109 }
0110 
0111 QString KChart::TextBubbleLayoutItem::text() const
0112 {
0113     return m_text->text();
0114 }
0115 
0116 void KChart::TextBubbleLayoutItem::setTextAttributes( const TextAttributes& a )
0117 {
0118     m_text->setTextAttributes( a );
0119 }
0120 
0121 KChart::TextAttributes KChart::TextBubbleLayoutItem::textAttributes() const
0122 {
0123     return m_text->textAttributes();
0124 }
0125 
0126 bool KChart::TextBubbleLayoutItem::isEmpty() const
0127 {
0128     return m_text->isEmpty();
0129 }
0130 
0131 Qt::Orientations KChart::TextBubbleLayoutItem::expandingDirections() const
0132 {
0133     return m_text->expandingDirections();
0134 }
0135 
0136 QSize KChart::TextBubbleLayoutItem::maximumSize() const
0137 {
0138     const int border = borderWidth();
0139     return m_text->maximumSize() + QSize( 2 * border, 2 * border );
0140 }
0141 
0142 QSize KChart::TextBubbleLayoutItem::minimumSize() const
0143 {
0144     const int border = borderWidth();
0145     return m_text->minimumSize() + QSize( 2 * border, 2 * border );
0146 }
0147 
0148 QSize KChart::TextBubbleLayoutItem::sizeHint() const
0149 {
0150     const int border = borderWidth();
0151     return m_text->sizeHint() + QSize( 2 * border, 2 * border );
0152 }
0153 
0154 void KChart::TextBubbleLayoutItem::setGeometry( const QRect& r )
0155 {
0156     const int border = borderWidth();
0157     m_text->setGeometry( r.adjusted( border, border, -border, -border ) );
0158 }
0159 
0160 QRect KChart::TextBubbleLayoutItem::geometry() const
0161 {
0162     const int border = borderWidth();
0163     return m_text->geometry().adjusted( -border, -border, border, border );
0164 }
0165 
0166 void KChart::TextBubbleLayoutItem::paint( QPainter* painter )
0167 {
0168     const QPen oldPen = painter->pen();
0169     const QBrush oldBrush = painter->brush();
0170     painter->setPen( Qt::black );
0171     painter->setBrush( QColor( 255, 255, 220 ) );
0172     painter->drawRoundRect( geometry(), 10 );
0173     painter->setPen( oldPen );
0174     painter->setBrush( oldBrush );
0175     m_text->paint( painter );
0176 }
0177 
0178 int KChart::TextBubbleLayoutItem::borderWidth() const
0179 {
0180     return 1;
0181 }
0182 
0183 KChart::TextLayoutItem::TextLayoutItem( const QString& text,
0184                                          const KChart::TextAttributes& attributes,
0185                                          const QObject* area,
0186                                          KChartEnums::MeasureOrientation orientation,
0187                                          Qt::Alignment alignment )
0188     : AbstractLayoutItem( alignment )
0189     , mText( text )
0190     , mTextAlignment( alignment )
0191     , mAttributes( attributes )
0192     , mAutoReferenceArea( area )
0193     , mAutoReferenceOrientation( orientation )
0194     , cachedSizeHint() // default this to invalid to force just-in-time calculation before first use of sizeHint()
0195     , cachedFontSize( 0.0 )
0196     , cachedFont( mAttributes.font() )
0197 {
0198 }
0199 
0200 KChart::TextLayoutItem::TextLayoutItem()
0201     : AbstractLayoutItem( Qt::AlignLeft )
0202     , mText()
0203     , mTextAlignment( Qt::AlignLeft )
0204     , mAttributes()
0205     , mAutoReferenceArea( nullptr )
0206     , mAutoReferenceOrientation( KChartEnums::MeasureOrientationHorizontal )
0207     , cachedSizeHint() // default this to invalid to force just-in-time calculation before first use of sizeHint()
0208     , cachedFontSize( 0.0 )
0209     , cachedFont( mAttributes.font() )
0210 {
0211 
0212 }
0213 
0214 void KChart::TextLayoutItem::setAutoReferenceArea( const QObject* area )
0215 {
0216     mAutoReferenceArea = area;
0217     cachedSizeHint = QSize();
0218     sizeHint();
0219 }
0220 
0221 const QObject* KChart::TextLayoutItem::autoReferenceArea() const
0222 {
0223     return mAutoReferenceArea;
0224 }
0225 
0226 void KChart::TextLayoutItem::setText(const QString & text)
0227 {
0228     mText = text;
0229     cachedSizeHint = QSize();
0230     sizeHint();
0231     if ( mParent )
0232         mParent->update();
0233 }
0234 
0235 QString KChart::TextLayoutItem::text() const
0236 {
0237     return mText;
0238 }
0239 
0240 void KChart::TextLayoutItem::setTextAlignment( Qt::Alignment alignment)
0241 {
0242     if ( mTextAlignment == alignment )
0243         return;
0244     mTextAlignment = alignment;
0245     if ( mParent )
0246         mParent->update();
0247 }
0248 
0249 Qt::Alignment KChart::TextLayoutItem::textAlignment() const
0250 {
0251     return mTextAlignment;
0252 }
0253 
0254 void KChart::TextLayoutItem::setTextAttributes( const TextAttributes &a )
0255 {
0256     mAttributes = a;
0257     cachedFont = a.font();
0258     cachedSizeHint = QSize(); // invalidate size hint
0259     sizeHint();
0260     if ( mParent )
0261         mParent->update();
0262 }
0263 
0264 KChart::TextAttributes KChart::TextLayoutItem::textAttributes() const
0265 {
0266     return mAttributes;
0267 }
0268 
0269 
0270 Qt::Orientations KChart::TextLayoutItem::expandingDirections() const
0271 {
0272     return Qt::Orientations(); // Grow neither vertically nor horizontally
0273 }
0274 
0275 QRect KChart::TextLayoutItem::geometry() const
0276 {
0277     return mRect;
0278 }
0279 
0280 bool KChart::TextLayoutItem::isEmpty() const
0281 {
0282     return false; // never empty, otherwise the layout item would not exist
0283 }
0284 
0285 QSize KChart::TextLayoutItem::maximumSize() const
0286 {
0287     return sizeHint(); // PENDING(kalle) Review, quite inflexible
0288 }
0289 
0290 QSize KChart::TextLayoutItem::minimumSize() const
0291 {
0292     return sizeHint(); // PENDING(kalle) Review, quite inflexible
0293 }
0294 
0295 void KChart::TextLayoutItem::setGeometry( const QRect& r )
0296 {
0297     mRect = r;
0298 }
0299 
0300 // returns the bounding box of rect rotated around its center
0301 QRectF rotatedRect( const QRectF& rect, qreal rotation )
0302 {
0303     QTransform t;
0304     QPointF center = rect.center();
0305     t.translate( center.x(), center.y() );
0306     t.rotate( rotation );
0307     t.translate( -center.x(), -center.y() );
0308     return t.mapRect( rect );
0309 }
0310 
0311 qreal KChart::TextLayoutItem::fitFontSizeToGeometry() const
0312 {
0313     QFont f = realFont();
0314     const qreal origResult = f.pointSizeF();
0315     qreal result = origResult;
0316     const qreal minSize = mAttributes.minimalFontSize().value();
0317     const QSize mySize = geometry().size();
0318     if ( mySize.isNull() ) {
0319         return result;
0320     }
0321 
0322     QFontMetrics fm( f );
0323     while ( true ) {
0324         const QSizeF textSize = rotatedRect( fm.boundingRect( mText ), mAttributes.rotation() ).normalized().size();
0325 
0326         if ( textSize.height() <= mySize.height() && textSize.width() <= mySize.width() ) {
0327             return result;
0328         }
0329 
0330         result -= 0.5;
0331         if ( minSize > 0 && result < minSize ) {
0332             return result + 0.5;
0333         } else if ( result <= 0.0 ) {
0334             return origResult;
0335         }
0336         f.setPointSizeF( result );
0337         fm = QFontMetrics( f );
0338     }
0339 }
0340 
0341 qreal KChart::TextLayoutItem::realFontSize() const
0342 {
0343     return mAttributes.calculatedFontSize( mAutoReferenceArea, mAutoReferenceOrientation );
0344 }
0345 
0346 bool KChart::TextLayoutItem::maybeUpdateRealFont() const
0347 {
0348     const qreal fntSiz = realFontSize();
0349     const bool doUpdate = !cachedSizeHint.isValid() || cachedFontSize != fntSiz;
0350 
0351     if ( doUpdate && fntSiz > 0.0 ) {
0352         cachedFontSize = fntSiz;
0353         cachedFont.setPointSizeF( fntSiz );
0354     }
0355     return doUpdate; // "didUpdate" by now
0356 }
0357 
0358 QFont KChart::TextLayoutItem::realFont() const
0359 {
0360     maybeUpdateRealFont();
0361     return cachedFont;
0362 }
0363 
0364 QPolygon KChart::TextLayoutItem::boundingPolygon() const
0365 {
0366     // should probably call sizeHint() here, but that one is expensive (see TODO there)
0367     return mCachedBoundingPolygon;
0368 }
0369 
0370 bool KChart::TextLayoutItem::intersects( const TextLayoutItem& other, const QPointF& myPos, const QPointF& otherPos ) const
0371 {
0372     return intersects( other, myPos.toPoint(), otherPos.toPoint() );
0373 }
0374 
0375 bool KChart::TextLayoutItem::intersects( const TextLayoutItem& other, const QPoint& myPos, const QPoint& otherPos ) const
0376 {
0377     QRegion myRegion( boundingPolygon().translated( myPos - otherPos ) );
0378     QRegion otherRegion( other.boundingPolygon() );
0379 
0380     return myRegion.intersects( otherRegion );
0381 }
0382 
0383 QSize KChart::TextLayoutItem::sizeHint() const
0384 {
0385     // ### we only really need to recalculate the size hint when mAttributes.rotation has *changed*
0386     if ( maybeUpdateRealFont() || mAttributes.rotation() || !cachedSizeHint.isValid() ) {
0387         const QSize newSizeHint( calcSizeHint( cachedFont ) );
0388         Q_ASSERT( newSizeHint.isValid() );
0389         if ( newSizeHint != cachedSizeHint ) {
0390             cachedSizeHint = newSizeHint;
0391             sizeHintChanged();
0392         }
0393     }
0394     return cachedSizeHint;
0395 }
0396 
0397 QSize KChart::TextLayoutItem::sizeHintUnrotated() const
0398 {
0399     maybeUpdateRealFont(); // make sure the cached font is up to date
0400     return unrotatedSizeHint( cachedFont );
0401 }
0402 
0403 
0404 // PENDING(kalle) Support auto shrink
0405 
0406 
0407 QSize KChart::TextLayoutItem::unrotatedTextSize( QFont fnt ) const
0408 {
0409     if ( fnt == QFont() ) {
0410         fnt = realFont(); // this is the cached font in most cases
0411     }
0412 
0413     const QFontMetricsF fm( fnt, GlobalMeasureScaling::paintDevice() );
0414     QRect veryLarge( 0, 0, 100000, 100000 );
0415     // this overload of boundingRect() interprets \n as line breaks, not as regular characters.
0416     return fm.boundingRect( veryLarge, Qt::AlignLeft | Qt::AlignTop, mText ).size().toSize();
0417 }
0418 
0419 int KChart::TextLayoutItem::marginWidth() const
0420 {
0421     return marginWidth( unrotatedTextSize() );
0422 }
0423 
0424 int KChart::TextLayoutItem::marginWidth( const QSize& textSize ) const
0425 {
0426     return qMin ( QApplication::style()->pixelMetric( QStyle::PM_ButtonMargin, nullptr, nullptr ),
0427                   // decrease frame size if the text is small
0428                   textSize.height() * 2 / 3 );
0429 }
0430 
0431 QSize KChart::TextLayoutItem::unrotatedSizeHint( const QFont& fnt ) const
0432 {
0433     QSize ret = unrotatedTextSize( fnt );
0434     const int margin = marginWidth( ret );
0435     ret += QSize( margin, margin );
0436     return ret;
0437 }
0438 
0439 QSize KChart::TextLayoutItem::calcSizeHint( const QFont& font ) const
0440 {
0441     const QSize size = unrotatedSizeHint( font );
0442     QPoint topLeft( -size.width() * 0.5, -size.height() * 0.5 );
0443     if ( !mAttributes.rotation() ) {
0444         mCachedBoundingPolygon.resize( 4 );
0445         // using the same winding order as returned by QPolygon QTransform::mapToPolygon(const QRect&),
0446         // which is: 0-1: top edge, 1-2: right edge, 2-3: bottom edge, 3-0: left edge (of input rect)
0447         mCachedBoundingPolygon[ 0 ] = topLeft;
0448         mCachedBoundingPolygon[ 1 ] = topLeft + QPoint( size.width(), 0 ); // top right
0449         mCachedBoundingPolygon[ 2 ] = topLeft + QPoint( size.width(), size.height() ); // bottom right
0450         mCachedBoundingPolygon[ 3 ] = topLeft + QPoint( 0, size.height() ); // bottom left
0451         return size;
0452     }
0453 
0454     const QRect rect( topLeft, size );
0455     QTransform t;
0456     t.rotate( mAttributes.rotation() );
0457     mCachedBoundingPolygon = t.mapToPolygon( rect );
0458 
0459     return mCachedBoundingPolygon.boundingRect().size();
0460 }
0461 
0462 void KChart::TextLayoutItem::paint( QPainter* painter )
0463 {
0464     if ( !mRect.isValid() ) {
0465         return;
0466     }
0467     const PainterSaver painterSaver( painter );
0468     QFont f = realFont();
0469     if ( mAttributes.autoShrink() ) {
0470         f.setPointSizeF( fitFontSizeToGeometry() );
0471     }
0472     painter->setFont( f );
0473 
0474     QSize innerSize = unrotatedTextSize();
0475     QRectF rect = QRectF( QPointF( 0, 0 ), innerSize );
0476     rect.translate( -rect.center() );
0477     painter->translate( mRect.center() );
0478     painter->rotate( mAttributes.rotation() );
0479 #ifdef DEBUG_ITEMS_PAINT
0480     painter->setPen( Qt::red );
0481     painter->drawRect( rect );
0482 #endif
0483 
0484     painter->setPen( PrintingParameters::scalePen( mAttributes.pen() ) );
0485     QTextDocument* document = mAttributes.textDocument();
0486     if ( document ) {
0487         document->setPageSize( rect.size() );
0488         document->setHtml( mText );
0489         QAbstractTextDocumentLayout::PaintContext paintcontext;
0490         // ### this doesn't work for rotated painting because clip does not translate the painting
0491         // TODO translate the painting either using a QTransform or one of QPainter's transform stages
0492         paintcontext.clip = rect;
0493         document->documentLayout()->draw( painter, paintcontext );
0494     } else {
0495         painter->drawText( rect, mTextAlignment, mText );
0496     }
0497 }
0498 
0499 KChart::HorizontalLineLayoutItem::HorizontalLineLayoutItem()
0500     : AbstractLayoutItem( Qt::AlignCenter )
0501 {
0502 }
0503 
0504 Qt::Orientations KChart::HorizontalLineLayoutItem::expandingDirections() const
0505 {
0506     return Qt::Horizontal;
0507 }
0508 
0509 QRect KChart::HorizontalLineLayoutItem::geometry() const
0510 {
0511     return mRect;
0512 }
0513 
0514 bool KChart::HorizontalLineLayoutItem::isEmpty() const
0515 {
0516     return false; // never empty, otherwise the layout item would not exist
0517 }
0518 
0519 QSize KChart::HorizontalLineLayoutItem::maximumSize() const
0520 {
0521     return QSize( QWIDGETSIZE_MAX, QWIDGETSIZE_MAX );
0522 }
0523 
0524 QSize KChart::HorizontalLineLayoutItem::minimumSize() const
0525 {
0526     return QSize( 0, 0 );
0527 }
0528 
0529 void KChart::HorizontalLineLayoutItem::setGeometry( const QRect& r )
0530 {
0531     mRect = r;
0532 }
0533 
0534 QSize KChart::HorizontalLineLayoutItem::sizeHint() const
0535 {
0536     return QSize( -1, 3 ); // see qframe.cpp
0537 }
0538 
0539 
0540 void KChart::HorizontalLineLayoutItem::paint( QPainter* painter )
0541 {
0542     if ( !mRect.isValid() )
0543         return;
0544 
0545     painter->drawLine( QPointF( mRect.left(), mRect.center().y() ),
0546                        QPointF( mRect.right(), mRect.center().y() ) );
0547 }
0548 
0549 
0550 KChart::VerticalLineLayoutItem::VerticalLineLayoutItem()
0551     : AbstractLayoutItem( Qt::AlignCenter )
0552 {
0553 }
0554 
0555 Qt::Orientations KChart::VerticalLineLayoutItem::expandingDirections() const
0556 {
0557     return Qt::Vertical;
0558 }
0559 
0560 QRect KChart::VerticalLineLayoutItem::geometry() const
0561 {
0562     return mRect;
0563 }
0564 
0565 bool KChart::VerticalLineLayoutItem::isEmpty() const
0566 {
0567     return false; // never empty, otherwise the layout item would not exist
0568 }
0569 
0570 QSize KChart::VerticalLineLayoutItem::maximumSize() const
0571 {
0572     return QSize( QWIDGETSIZE_MAX, QWIDGETSIZE_MAX );
0573 }
0574 
0575 QSize KChart::VerticalLineLayoutItem::minimumSize() const
0576 {
0577     return QSize( 0, 0 );
0578 }
0579 
0580 void KChart::VerticalLineLayoutItem::setGeometry( const QRect& r )
0581 {
0582     mRect = r;
0583 }
0584 
0585 QSize KChart::VerticalLineLayoutItem::sizeHint() const
0586 {
0587     return QSize( 3, -1 ); // see qframe.cpp
0588 }
0589 
0590 
0591 void KChart::VerticalLineLayoutItem::paint( QPainter* painter )
0592 {
0593     if ( !mRect.isValid() )
0594         return;
0595 
0596     painter->drawLine( QPointF( mRect.center().x(), mRect.top() ),
0597                        QPointF( mRect.center().x(), mRect.bottom() ) );
0598 }
0599 
0600 
0601 
0602 KChart::MarkerLayoutItem::MarkerLayoutItem( KChart::AbstractDiagram* diagram,
0603                                              const MarkerAttributes& marker,
0604                                              const QBrush& brush, const QPen& pen,
0605                                              Qt::Alignment alignment )
0606     : AbstractLayoutItem( alignment )
0607     , mDiagram( diagram )
0608     , mMarker( marker )
0609     , mBrush( brush )
0610     , mPen( pen )
0611 {
0612 }
0613 
0614 Qt::Orientations KChart::MarkerLayoutItem::expandingDirections() const
0615 {
0616     return Qt::Orientations(); // Grow neither vertically nor horizontally
0617 }
0618 
0619 QRect KChart::MarkerLayoutItem::geometry() const
0620 {
0621     return mRect;
0622 }
0623 
0624 bool KChart::MarkerLayoutItem::isEmpty() const
0625 {
0626     return false; // never empty, otherwise the layout item would not exist
0627 }
0628 
0629 QSize KChart::MarkerLayoutItem::maximumSize() const
0630 {
0631     return sizeHint(); // PENDING(kalle) Review, quite inflexible
0632 }
0633 
0634 QSize KChart::MarkerLayoutItem::minimumSize() const
0635 {
0636     return sizeHint(); // PENDING(kalle) Review, quite inflexible
0637 }
0638 
0639 void KChart::MarkerLayoutItem::setGeometry( const QRect& r )
0640 {
0641     mRect = r;
0642 }
0643 
0644 QSize KChart::MarkerLayoutItem::sizeHint() const
0645 {
0646     //qDebug() << "KChart::MarkerLayoutItem::sizeHint() returns:"<<mMarker.markerSize().toSize();
0647     return mMarker.markerSize().toSize();
0648 }
0649 
0650 void KChart::MarkerLayoutItem::paint( QPainter* painter )
0651 {
0652     paintIntoRect( painter, mRect, mDiagram, mMarker, mBrush, mPen );
0653 }
0654 
0655 void KChart::MarkerLayoutItem::paintIntoRect(
0656         QPainter* painter,
0657         const QRect& rect,
0658         AbstractDiagram* diagram,
0659         const MarkerAttributes& marker,
0660         const QBrush& brush,
0661         const QPen& pen )
0662 {
0663     if ( !rect.isValid() )
0664         return;
0665 
0666     // The layout management may assign a larger rect than what we
0667     // wanted. We need to adjust the position.
0668     const QSize siz = marker.markerSize().toSize();
0669     QPointF pos = rect.topLeft();
0670     pos += QPointF( static_cast<qreal>(( rect.width()  - siz.width()) / 2.0 ),
0671                     static_cast<qreal>(( rect.height() - siz.height()) / 2.0 ) );
0672 
0673 #ifdef DEBUG_ITEMS_PAINT
0674     QPointF oldPos = pos;
0675 #endif
0676 
0677 // And finally, drawMarker() assumes the position to be the center
0678     // of the marker, adjust again.
0679     pos += QPointF( static_cast<qreal>( siz.width() ) / 2.0,
0680                     static_cast<qreal>( siz.height() )/ 2.0 );
0681 
0682     diagram->paintMarker( painter, marker, brush, pen, pos.toPoint(), siz );
0683 
0684 #ifdef DEBUG_ITEMS_PAINT
0685     const QPen oldPen( painter->pen() );
0686     painter->setPen( Qt::red );
0687     painter->drawRect( QRect( oldPos.toPoint(), siz ) );
0688     painter->setPen( oldPen );
0689 #endif
0690 }
0691 
0692 
0693 KChart::LineLayoutItem::LineLayoutItem( KChart::AbstractDiagram* diagram,
0694                                          int length,
0695                                          const QPen& pen,
0696                                          Qt::Alignment legendLineSymbolAlignment,
0697                                          Qt::Alignment alignment )
0698     : AbstractLayoutItem( alignment )
0699     , mDiagram( diagram )
0700     , mLength( length )
0701     , mPen( pen )
0702     , mLegendLineSymbolAlignment(legendLineSymbolAlignment)
0703 {
0704     // enforce a minimum pen width
0705     if ( pen.width() < 2 )
0706         mPen.setWidth( 2 );
0707 }
0708 
0709 Qt::Orientations KChart::LineLayoutItem::expandingDirections() const
0710 {
0711     return Qt::Orientations(); // Grow neither vertically nor horizontally
0712 }
0713 
0714 QRect KChart::LineLayoutItem::geometry() const
0715 {
0716     return mRect;
0717 }
0718 
0719 bool KChart::LineLayoutItem::isEmpty() const
0720 {
0721     return false; // never empty, otherwise the layout item would not exist
0722 }
0723 
0724 QSize KChart::LineLayoutItem::maximumSize() const
0725 {
0726     return sizeHint(); // PENDING(kalle) Review, quite inflexible
0727 }
0728 
0729 QSize KChart::LineLayoutItem::minimumSize() const
0730 {
0731     return sizeHint(); // PENDING(kalle) Review, quite inflexible
0732 }
0733 
0734 void KChart::LineLayoutItem::setGeometry( const QRect& r )
0735 {
0736     mRect = r;
0737 }
0738 
0739 QSize KChart::LineLayoutItem::sizeHint() const
0740 {
0741     return QSize( mLength, mPen.width() + 2 );
0742 }
0743 
0744 
0745 void KChart::LineLayoutItem::setLegendLineSymbolAlignment(Qt::Alignment legendLineSymbolAlignment)
0746 {
0747     if (mLegendLineSymbolAlignment == legendLineSymbolAlignment)
0748         return;
0749 
0750     mLegendLineSymbolAlignment = legendLineSymbolAlignment;
0751 }
0752 
0753 Qt::Alignment KChart::LineLayoutItem::legendLineSymbolAlignment() const
0754 {
0755     return mLegendLineSymbolAlignment;
0756 }
0757 
0758 void KChart::LineLayoutItem::paint( QPainter* painter )
0759 {
0760     paintIntoRect( painter, mRect, mPen, mLegendLineSymbolAlignment );
0761 }
0762 
0763 void KChart::LineLayoutItem::paintIntoRect(
0764         QPainter* painter,
0765         const QRect& rect,
0766         const QPen& pen,
0767         Qt::Alignment lineAlignment)
0768 {
0769     if ( ! rect.isValid() )
0770         return;
0771 
0772     const QPen oldPen = painter->pen();
0773     painter->setPen( PrintingParameters::scalePen( pen ) );
0774     qreal y = 0;
0775     if (lineAlignment == Qt::AlignTop)
0776         y = rect.top();
0777     else if (lineAlignment == Qt::AlignBottom)
0778         y = rect.bottom();
0779     else
0780         y = rect.center().y();
0781 
0782     painter->drawLine( QPointF( rect.left(), y ),
0783                        QPointF( rect.right(), y ) );
0784     painter->setPen( oldPen );
0785 }
0786 
0787 
0788 KChart::LineWithMarkerLayoutItem::LineWithMarkerLayoutItem(
0789         KChart::AbstractDiagram* diagram,
0790         int lineLength,
0791         const QPen& linePen,
0792         int markerOffs,
0793         const MarkerAttributes& marker,
0794         const QBrush& markerBrush,
0795         const QPen& markerPen,
0796         Qt::Alignment alignment )
0797     : AbstractLayoutItem( alignment )
0798     , mDiagram( diagram )
0799     , mLineLength( lineLength )
0800     , mLinePen( linePen )
0801     , mMarkerOffs( markerOffs )
0802     , mMarker( marker )
0803     , mMarkerBrush( markerBrush )
0804     , mMarkerPen( markerPen )
0805 {
0806 }
0807 
0808 Qt::Orientations KChart::LineWithMarkerLayoutItem::expandingDirections() const
0809 {
0810     return Qt::Orientations(); // Grow neither vertically nor horizontally
0811 }
0812 
0813 QRect KChart::LineWithMarkerLayoutItem::geometry() const
0814 {
0815     return mRect;
0816 }
0817 
0818 bool KChart::LineWithMarkerLayoutItem::isEmpty() const
0819 {
0820     return false; // never empty, otherwise the layout item would not exist
0821 }
0822 
0823 QSize KChart::LineWithMarkerLayoutItem::maximumSize() const
0824 {
0825     return sizeHint(); // PENDING(kalle) Review, quite inflexible
0826 }
0827 
0828 QSize KChart::LineWithMarkerLayoutItem::minimumSize() const
0829 {
0830     return sizeHint(); // PENDING(kalle) Review, quite inflexible
0831 }
0832 
0833 void KChart::LineWithMarkerLayoutItem::setGeometry( const QRect& r )
0834 {
0835     mRect = r;
0836 }
0837 
0838 QSize KChart::LineWithMarkerLayoutItem::sizeHint() const
0839 {
0840     const QSize lineSize( mLineLength, mLinePen.width() + 2 );
0841     return lineSize.expandedTo( mMarker.markerSize().toSize() );
0842 }
0843 
0844 void KChart::LineWithMarkerLayoutItem::paint( QPainter* painter )
0845 {
0846     // paint the line over the full width, into the vertical middle of the rect
0847     LineLayoutItem::paintIntoRect( painter, mRect, mLinePen, Qt::AlignCenter );
0848 
0849     // paint the marker with the given offset from the left side of the line
0850     const QRect r(
0851             QPoint( mRect.x()+mMarkerOffs, mRect.y() ),
0852             QSize( mMarker.markerSize().toSize().width(), mRect.height() ) );
0853     MarkerLayoutItem::paintIntoRect(
0854             painter, r, mDiagram, mMarker, mMarkerBrush, mMarkerPen );
0855 }
0856 
0857 KChart::AutoSpacerLayoutItem::AutoSpacerLayoutItem(
0858         bool layoutIsAtTopPosition, QHBoxLayout *rightLeftLayout,
0859         bool layoutIsAtLeftPosition, QVBoxLayout *topBottomLayout )
0860     : AbstractLayoutItem( Qt::AlignCenter )
0861     , mLayoutIsAtTopPosition( layoutIsAtTopPosition )
0862     , mRightLeftLayout( rightLeftLayout )
0863     , mLayoutIsAtLeftPosition( layoutIsAtLeftPosition )
0864     , mTopBottomLayout( topBottomLayout )
0865 {
0866 }
0867 
0868 Qt::Orientations KChart::AutoSpacerLayoutItem::expandingDirections() const
0869 {
0870     return Qt::Orientations(); // Grow neither vertically nor horizontally
0871 }
0872 
0873 QRect KChart::AutoSpacerLayoutItem::geometry() const
0874 {
0875     return mRect;
0876 }
0877 
0878 bool KChart::AutoSpacerLayoutItem::isEmpty() const
0879 {
0880     return true; // never empty, otherwise the layout item would not exist
0881 }
0882 
0883 QSize KChart::AutoSpacerLayoutItem::maximumSize() const
0884 {
0885     return sizeHint();
0886 }
0887 
0888 QSize KChart::AutoSpacerLayoutItem::minimumSize() const
0889 {
0890     return sizeHint();
0891 }
0892 
0893 void KChart::AutoSpacerLayoutItem::setGeometry( const QRect& r )
0894 {
0895     mRect = r;
0896 }
0897 
0898 
0899 static void updateCommonBrush( QBrush& commonBrush, bool& bStart, const KChart::AbstractArea& area )
0900 {
0901     const KChart::BackgroundAttributes ba( area.backgroundAttributes() );
0902     const bool hasSimpleBrush = (
0903             ! area.frameAttributes().isVisible() &&
0904             ba.isVisible() &&
0905             ba.pixmapMode() == KChart::BackgroundAttributes::BackgroundPixmapModeNone &&
0906             ba.brush().gradient() == nullptr );
0907     if ( bStart ) {
0908         bStart = false;
0909         commonBrush = hasSimpleBrush ? ba.brush() : QBrush();
0910     } else {
0911         if ( ! hasSimpleBrush || ba.brush() != commonBrush )
0912         {
0913             commonBrush = QBrush();
0914         }
0915     }
0916 }
0917 
0918 QSize KChart::AutoSpacerLayoutItem::sizeHint() const
0919 {
0920     QBrush commonBrush;
0921     bool bStart=true;
0922     // calculate the maximal overlap of the top/bottom axes:
0923     int topBottomOverlap = 0;
0924     if ( mTopBottomLayout ) {
0925         for (int i = 0; i < mTopBottomLayout->count(); ++i) {
0926             AbstractArea* area = dynamic_cast<AbstractArea*>(mTopBottomLayout->itemAt(i));
0927             if ( area ) {
0928                 //qDebug() << "AutoSpacerLayoutItem testing" << area;
0929                 topBottomOverlap = qMax( topBottomOverlap,
0930                                          mLayoutIsAtLeftPosition ? area->rightOverlap()
0931                                                                  : area->leftOverlap() );
0932                 updateCommonBrush( commonBrush, bStart, *area );
0933             }
0934         }
0935     }
0936     // calculate the maximal overlap of the left/right axes:
0937     int leftRightOverlap = 0;
0938     if ( mRightLeftLayout ) {
0939         for (int i = 0; i < mRightLeftLayout->count(); ++i) {
0940             AbstractArea* area = dynamic_cast<AbstractArea*>(mRightLeftLayout->itemAt(i));
0941             if ( area ) {
0942                 //qDebug() << "AutoSpacerLayoutItem testing" << area;
0943                 leftRightOverlap = qMax( leftRightOverlap,
0944                                          mLayoutIsAtTopPosition ? area->bottomOverlap()
0945                                                                 : area->topOverlap() );
0946                 updateCommonBrush( commonBrush, bStart, *area );
0947             }
0948         }
0949     }
0950     if ( topBottomOverlap > 0 && leftRightOverlap > 0 )
0951         mCommonBrush = commonBrush;
0952     else
0953         mCommonBrush = QBrush();
0954     mCachedSize = QSize( topBottomOverlap, leftRightOverlap );
0955     //qDebug() << mCachedSize;
0956     return mCachedSize;
0957 }
0958 
0959 
0960 void KChart::AutoSpacerLayoutItem::paint( QPainter* painter )
0961 {
0962     if ( mParentLayout && mRect.isValid() && mCachedSize.isValid() &&
0963         mCommonBrush.style() != Qt::NoBrush )
0964     {
0965         QPoint p1( mRect.topLeft() );
0966         QPoint p2( mRect.bottomRight() );
0967         if ( mLayoutIsAtLeftPosition )
0968             p1.rx() += mCachedSize.width() - mParentLayout->spacing();
0969         else
0970             p2.rx() -= mCachedSize.width() - mParentLayout->spacing();
0971         if ( mLayoutIsAtTopPosition ) {
0972             p1.ry() += mCachedSize.height() - mParentLayout->spacing() - 1;
0973             p2.ry() -= 1;
0974         } else
0975             p2.ry() -= mCachedSize.height() - mParentLayout->spacing() - 1;
0976         //qDebug() << mLayoutIsAtTopPosition << mLayoutIsAtLeftPosition;
0977         //qDebug() << mRect;
0978         //qDebug() << mParentLayout->margin();
0979         //qDebug() << QRect( p1, p2 );
0980         const QPoint oldBrushOrigin( painter->brushOrigin() );
0981         const QBrush oldBrush( painter->brush() );
0982         const QPen   oldPen( painter->pen() );
0983         const QPointF newTopLeft( painter->deviceMatrix().map( p1 ) );
0984         painter->setBrushOrigin( newTopLeft );
0985         painter->setBrush( mCommonBrush );
0986         painter->setPen( Qt::NoPen );
0987         painter->drawRect( QRect( p1, p2 ) );
0988         painter->setBrushOrigin( oldBrushOrigin );
0989         painter->setBrush( oldBrush );
0990         painter->setPen( oldPen );
0991     }
0992     // debug code:
0993 #if 0
0994     //qDebug() << "KChart::AutoSpacerLayoutItem::paint()";
0995     if ( !mRect.isValid() )
0996         return;
0997 
0998     painter->drawRect( mRect );
0999     painter->drawLine( QPointF( mRect.topLeft(), mRect.bottomRight() ) );
1000     painter->drawLine( QPointF( mRect.topRight(), mRect.bottomLeft() ) );
1001 #endif
1002 }