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 }