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 }