File indexing completed on 2024-05-12 04:20:33

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