1 /*
2 * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved.
3 *
4 * This file is part of the KD Chart library.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of
9 * the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20 #include "KChartLayoutItems.h"
21
22 #include "KTextDocument.h"
23 #include "KChartAbstractArea.h"
24 #include "KChartAbstractDiagram.h"
25 #include "KChartBackgroundAttributes.h"
26 #include "KChartFrameAttributes.h"
27 #include "KChartPaintContext.h"
28 #include "KChartPainterSaver_p.h"
29 #include "KChartPrintingParameters.h"
30 #include "KChartMath_p.h"
31
32 #include <QTextCursor>
33 #include <QTextBlockFormat>
34 #include <QTextDocumentFragment>
35 #include <QAbstractTextDocumentLayout>
36 #include <QLayout>
37 #include <QPainter>
38 #include <QDebug>
39 #include <QCoreApplication>
40 #include <QApplication>
41 #include <QStringList>
42 #include <QStyle>
43
44
45 //#define DEBUG_ITEMS_PAINT
46
setParentWidget(QWidget * widget)47 void KChart::AbstractLayoutItem::setParentWidget( QWidget* widget )
48 {
49 mParent = widget;
50 }
51
paintAll(QPainter & painter)52 void KChart::AbstractLayoutItem::paintAll( QPainter& painter )
53 {
54 paint( &painter );
55 }
56
paintCtx(PaintContext * context)57 void KChart::AbstractLayoutItem::paintCtx( PaintContext* context )
58 {
59 if ( context )
60 paint( context->painter() );
61 }
62
sizeHintChanged() const63 void KChart::AbstractLayoutItem::sizeHintChanged() const
64 {
65 // This is exactly like what QWidget::updateGeometry does.
66 // qDebug("KChart::AbstractLayoutItem::sizeHintChanged() called");
67 if ( mParent ) {
68 if ( mParent->layout() )
69 mParent->layout()->invalidate();
70 else
71 QApplication::postEvent( mParent, new QEvent( QEvent::LayoutRequest ) );
72 }
73 }
74
TextBubbleLayoutItem(const QString & text,const KChart::TextAttributes & attributes,const QObject * area,KChartEnums::MeasureOrientation orientation,Qt::Alignment alignment)75 KChart::TextBubbleLayoutItem::TextBubbleLayoutItem( const QString& text,
76 const KChart::TextAttributes& attributes,
77 const QObject* area,
78 KChartEnums::MeasureOrientation orientation,
79 Qt::Alignment alignment )
80 : AbstractLayoutItem( alignment ),
81 m_text( new TextLayoutItem( text, attributes, area, orientation, alignment ) )
82 {
83 }
84
TextBubbleLayoutItem()85 KChart::TextBubbleLayoutItem::TextBubbleLayoutItem()
86 : AbstractLayoutItem( Qt::AlignLeft ),
87 m_text( new TextLayoutItem() )
88 {
89 }
90
~TextBubbleLayoutItem()91 KChart::TextBubbleLayoutItem::~TextBubbleLayoutItem()
92 {
93 delete m_text;
94 }
95
setAutoReferenceArea(const QObject * area)96 void KChart::TextBubbleLayoutItem::setAutoReferenceArea( const QObject* area )
97 {
98 m_text->setAutoReferenceArea( area );
99 }
100
autoReferenceArea() const101 const QObject* KChart::TextBubbleLayoutItem::autoReferenceArea() const
102 {
103 return m_text->autoReferenceArea();
104 }
105
setText(const QString & text)106 void KChart::TextBubbleLayoutItem::setText( const QString& text )
107 {
108 m_text->setText( text );
109 }
110
text() const111 QString KChart::TextBubbleLayoutItem::text() const
112 {
113 return m_text->text();
114 }
115
setTextAttributes(const TextAttributes & a)116 void KChart::TextBubbleLayoutItem::setTextAttributes( const TextAttributes& a )
117 {
118 m_text->setTextAttributes( a );
119 }
120
textAttributes() const121 KChart::TextAttributes KChart::TextBubbleLayoutItem::textAttributes() const
122 {
123 return m_text->textAttributes();
124 }
125
isEmpty() const126 bool KChart::TextBubbleLayoutItem::isEmpty() const
127 {
128 return m_text->isEmpty();
129 }
130
expandingDirections() const131 Qt::Orientations KChart::TextBubbleLayoutItem::expandingDirections() const
132 {
133 return m_text->expandingDirections();
134 }
135
maximumSize() const136 QSize KChart::TextBubbleLayoutItem::maximumSize() const
137 {
138 const int border = borderWidth();
139 return m_text->maximumSize() + QSize( 2 * border, 2 * border );
140 }
141
minimumSize() const142 QSize KChart::TextBubbleLayoutItem::minimumSize() const
143 {
144 const int border = borderWidth();
145 return m_text->minimumSize() + QSize( 2 * border, 2 * border );
146 }
147
sizeHint() const148 QSize KChart::TextBubbleLayoutItem::sizeHint() const
149 {
150 const int border = borderWidth();
151 return m_text->sizeHint() + QSize( 2 * border, 2 * border );
152 }
153
setGeometry(const QRect & r)154 void KChart::TextBubbleLayoutItem::setGeometry( const QRect& r )
155 {
156 const int border = borderWidth();
157 m_text->setGeometry( r.adjusted( border, border, -border, -border ) );
158 }
159
geometry() const160 QRect KChart::TextBubbleLayoutItem::geometry() const
161 {
162 const int border = borderWidth();
163 return m_text->geometry().adjusted( -border, -border, border, border );
164 }
165
paint(QPainter * painter)166 void KChart::TextBubbleLayoutItem::paint( QPainter* painter )
167 {
168 const QPen oldPen = painter->pen();
169 const QBrush oldBrush = painter->brush();
170 painter->setPen( Qt::black );
171 painter->setBrush( QColor( 255, 255, 220 ) );
172 painter->drawRoundRect( geometry(), 10 );
173 painter->setPen( oldPen );
174 painter->setBrush( oldBrush );
175 m_text->paint( painter );
176 }
177
borderWidth() const178 int KChart::TextBubbleLayoutItem::borderWidth() const
179 {
180 return 1;
181 }
182
TextLayoutItem(const QString & text,const KChart::TextAttributes & attributes,const QObject * area,KChartEnums::MeasureOrientation orientation,Qt::Alignment alignment)183 KChart::TextLayoutItem::TextLayoutItem( const QString& text,
184 const KChart::TextAttributes& attributes,
185 const QObject* area,
186 KChartEnums::MeasureOrientation orientation,
187 Qt::Alignment alignment )
188 : AbstractLayoutItem( alignment )
189 , mText( text )
190 , mTextAlignment( alignment )
191 , mAttributes( attributes )
192 , mAutoReferenceArea( area )
193 , mAutoReferenceOrientation( orientation )
194 , cachedSizeHint() // default this to invalid to force just-in-time calculation before first use of sizeHint()
195 , cachedFontSize( 0.0 )
196 , cachedFont( mAttributes.font() )
197 {
198 }
199
TextLayoutItem()200 KChart::TextLayoutItem::TextLayoutItem()
201 : AbstractLayoutItem( Qt::AlignLeft )
202 , mText()
203 , mTextAlignment( Qt::AlignLeft )
204 , mAttributes()
205 , mAutoReferenceArea( nullptr )
206 , mAutoReferenceOrientation( KChartEnums::MeasureOrientationHorizontal )
207 , cachedSizeHint() // default this to invalid to force just-in-time calculation before first use of sizeHint()
208 , cachedFontSize( 0.0 )
209 , cachedFont( mAttributes.font() )
210 {
211
212 }
213
setAutoReferenceArea(const QObject * area)214 void KChart::TextLayoutItem::setAutoReferenceArea( const QObject* area )
215 {
216 mAutoReferenceArea = area;
217 cachedSizeHint = QSize();
218 sizeHint();
219 }
220
autoReferenceArea() const221 const QObject* KChart::TextLayoutItem::autoReferenceArea() const
222 {
223 return mAutoReferenceArea;
224 }
225
setText(const QString & text)226 void KChart::TextLayoutItem::setText(const QString & text)
227 {
228 mText = text;
229 cachedSizeHint = QSize();
230 sizeHint();
231 if ( mParent )
232 mParent->update();
233 }
234
text() const235 QString KChart::TextLayoutItem::text() const
236 {
237 return mText;
238 }
239
setTextAlignment(Qt::Alignment alignment)240 void KChart::TextLayoutItem::setTextAlignment( Qt::Alignment alignment)
241 {
242 if ( mTextAlignment == alignment )
243 return;
244 mTextAlignment = alignment;
245 if ( mParent )
246 mParent->update();
247 }
248
textAlignment() const249 Qt::Alignment KChart::TextLayoutItem::textAlignment() const
250 {
251 return mTextAlignment;
252 }
253
setTextAttributes(const TextAttributes & a)254 void KChart::TextLayoutItem::setTextAttributes( const TextAttributes &a )
255 {
256 mAttributes = a;
257 cachedFont = a.font();
258 cachedSizeHint = QSize(); // invalidate size hint
259 sizeHint();
260 if ( mParent )
261 mParent->update();
262 }
263
textAttributes() const264 KChart::TextAttributes KChart::TextLayoutItem::textAttributes() const
265 {
266 return mAttributes;
267 }
268
269
expandingDirections() const270 Qt::Orientations KChart::TextLayoutItem::expandingDirections() const
271 {
272 return Qt::Orientations(); // Grow neither vertically nor horizontally
273 }
274
geometry() const275 QRect KChart::TextLayoutItem::geometry() const
276 {
277 return mRect;
278 }
279
isEmpty() const280 bool KChart::TextLayoutItem::isEmpty() const
281 {
282 return false; // never empty, otherwise the layout item would not exist
283 }
284
maximumSize() const285 QSize KChart::TextLayoutItem::maximumSize() const
286 {
287 return sizeHint(); // PENDING(kalle) Review, quite inflexible
288 }
289
minimumSize() const290 QSize KChart::TextLayoutItem::minimumSize() const
291 {
292 return sizeHint(); // PENDING(kalle) Review, quite inflexible
293 }
294
setGeometry(const QRect & r)295 void KChart::TextLayoutItem::setGeometry( const QRect& r )
296 {
297 mRect = r;
298 }
299
300 // returns the bounding box of rect rotated around its center
rotatedRect(const QRectF & rect,qreal rotation)301 QRectF rotatedRect( const QRectF& rect, qreal rotation )
302 {
303 QTransform t;
304 QPointF center = rect.center();
305 t.translate( center.x(), center.y() );
306 t.rotate( rotation );
307 t.translate( -center.x(), -center.y() );
308 return t.mapRect( rect );
309 }
310
fitFontSizeToGeometry() const311 qreal KChart::TextLayoutItem::fitFontSizeToGeometry() const
312 {
313 QFont f = realFont();
314 const qreal origResult = f.pointSizeF();
315 qreal result = origResult;
316 const qreal minSize = mAttributes.minimalFontSize().value();
317 const QSize mySize = geometry().size();
318 if ( mySize.isNull() ) {
319 return result;
320 }
321
322 QFontMetrics fm( f );
323 while ( true ) {
324 const QSizeF textSize = rotatedRect( fm.boundingRect( mText ), mAttributes.rotation() ).normalized().size();
325
326 if ( textSize.height() <= mySize.height() && textSize.width() <= mySize.width() ) {
327 return result;
328 }
329
330 result -= 0.5;
331 if ( minSize > 0 && result < minSize ) {
332 return result + 0.5;
333 } else if ( result <= 0.0 ) {
334 return origResult;
335 }
336 f.setPointSizeF( result );
337 fm = QFontMetrics( f );
338 }
339 }
340
realFontSize() const341 qreal KChart::TextLayoutItem::realFontSize() const
342 {
343 return mAttributes.calculatedFontSize( mAutoReferenceArea, mAutoReferenceOrientation );
344 }
345
maybeUpdateRealFont() const346 bool KChart::TextLayoutItem::maybeUpdateRealFont() const
347 {
348 const qreal fntSiz = realFontSize();
349 const bool doUpdate = !cachedSizeHint.isValid() || cachedFontSize != fntSiz;
350
351 if ( doUpdate && fntSiz > 0.0 ) {
352 cachedFontSize = fntSiz;
353 cachedFont.setPointSizeF( fntSiz );
354 }
355 return doUpdate; // "didUpdate" by now
356 }
357
realFont() const358 QFont KChart::TextLayoutItem::realFont() const
359 {
360 maybeUpdateRealFont();
361 return cachedFont;
362 }
363
boundingPolygon() const364 QPolygon KChart::TextLayoutItem::boundingPolygon() const
365 {
366 // should probably call sizeHint() here, but that one is expensive (see TODO there)
367 return mCachedBoundingPolygon;
368 }
369
intersects(const TextLayoutItem & other,const QPointF & myPos,const QPointF & otherPos) const370 bool KChart::TextLayoutItem::intersects( const TextLayoutItem& other, const QPointF& myPos, const QPointF& otherPos ) const
371 {
372 return intersects( other, myPos.toPoint(), otherPos.toPoint() );
373 }
374
intersects(const TextLayoutItem & other,const QPoint & myPos,const QPoint & otherPos) const375 bool KChart::TextLayoutItem::intersects( const TextLayoutItem& other, const QPoint& myPos, const QPoint& otherPos ) const
376 {
377 QRegion myRegion( boundingPolygon().translated( myPos - otherPos ) );
378 QRegion otherRegion( other.boundingPolygon() );
379
380 return myRegion.intersects( otherRegion );
381 }
382
sizeHint() const383 QSize KChart::TextLayoutItem::sizeHint() const
384 {
385 // ### we only really need to recalculate the size hint when mAttributes.rotation has *changed*
386 if ( maybeUpdateRealFont() || mAttributes.rotation() || !cachedSizeHint.isValid() ) {
387 const QSize newSizeHint( calcSizeHint( cachedFont ) );
388 Q_ASSERT( newSizeHint.isValid() );
389 if ( newSizeHint != cachedSizeHint ) {
390 cachedSizeHint = newSizeHint;
391 sizeHintChanged();
392 }
393 }
394 return cachedSizeHint;
395 }
396
sizeHintUnrotated() const397 QSize KChart::TextLayoutItem::sizeHintUnrotated() const
398 {
399 maybeUpdateRealFont(); // make sure the cached font is up to date
400 return unrotatedSizeHint( cachedFont );
401 }
402
403
404 // PENDING(kalle) Support auto shrink
405
406
unrotatedTextSize(QFont fnt) const407 QSize KChart::TextLayoutItem::unrotatedTextSize( QFont fnt ) const
408 {
409 if ( fnt == QFont() ) {
410 fnt = realFont(); // this is the cached font in most cases
411 }
412
413 const QFontMetricsF fm( fnt, GlobalMeasureScaling::paintDevice() );
414 QRect veryLarge( 0, 0, 100000, 100000 );
415 // this overload of boundingRect() interprets \n as line breaks, not as regular characters.
416 return fm.boundingRect( veryLarge, Qt::AlignLeft | Qt::AlignTop, mText ).size().toSize();
417 }
418
marginWidth() const419 int KChart::TextLayoutItem::marginWidth() const
420 {
421 return marginWidth( unrotatedTextSize() );
422 }
423
marginWidth(const QSize & textSize) const424 int KChart::TextLayoutItem::marginWidth( const QSize& textSize ) const
425 {
426 return qMin ( QApplication::style()->pixelMetric( QStyle::PM_ButtonMargin, nullptr, nullptr ),
427 // decrease frame size if the text is small
428 textSize.height() * 2 / 3 );
429 }
430
unrotatedSizeHint(const QFont & fnt) const431 QSize KChart::TextLayoutItem::unrotatedSizeHint( const QFont& fnt ) const
432 {
433 QSize ret = unrotatedTextSize( fnt );
434 const int margin = marginWidth( ret );
435 ret += QSize( margin, margin );
436 return ret;
437 }
438
calcSizeHint(const QFont & font) const439 QSize KChart::TextLayoutItem::calcSizeHint( const QFont& font ) const
440 {
441 const QSize size = unrotatedSizeHint( font );
442 QPoint topLeft( -size.width() * 0.5, -size.height() * 0.5 );
443 if ( !mAttributes.rotation() ) {
444 mCachedBoundingPolygon.resize( 4 );
445 // using the same winding order as returned by QPolygon QTransform::mapToPolygon(const QRect&),
446 // which is: 0-1: top edge, 1-2: right edge, 2-3: bottom edge, 3-0: left edge (of input rect)
447 mCachedBoundingPolygon[ 0 ] = topLeft;
448 mCachedBoundingPolygon[ 1 ] = topLeft + QPoint( size.width(), 0 ); // top right
449 mCachedBoundingPolygon[ 2 ] = topLeft + QPoint( size.width(), size.height() ); // bottom right
450 mCachedBoundingPolygon[ 3 ] = topLeft + QPoint( 0, size.height() ); // bottom left
451 return size;
452 }
453
454 const QRect rect( topLeft, size );
455 QTransform t;
456 t.rotate( mAttributes.rotation() );
457 mCachedBoundingPolygon = t.mapToPolygon( rect );
458
459 return mCachedBoundingPolygon.boundingRect().size();
460 }
461
paint(QPainter * painter)462 void KChart::TextLayoutItem::paint( QPainter* painter )
463 {
464 if ( !mRect.isValid() ) {
465 return;
466 }
467 const PainterSaver painterSaver( painter );
468 QFont f = realFont();
469 if ( mAttributes.autoShrink() ) {
470 f.setPointSizeF( fitFontSizeToGeometry() );
471 }
472 painter->setFont( f );
473
474 QSize innerSize = unrotatedTextSize();
475 QRectF rect = QRectF( QPointF( 0, 0 ), innerSize );
476 rect.translate( -rect.center() );
477 painter->translate( mRect.center() );
478 painter->rotate( mAttributes.rotation() );
479 #ifdef DEBUG_ITEMS_PAINT
480 painter->setPen( Qt::red );
481 painter->drawRect( rect );
482 #endif
483
484 painter->setPen( PrintingParameters::scalePen( mAttributes.pen() ) );
485 QTextDocument* document = mAttributes.textDocument();
486 if ( document ) {
487 document->setPageSize( rect.size() );
488 document->setHtml( mText );
489 QAbstractTextDocumentLayout::PaintContext paintcontext;
490 // ### this doesn't work for rotated painting because clip does not translate the painting
491 // TODO translate the painting either using a QTransform or one of QPainter's transform stages
492 paintcontext.clip = rect;
493 document->documentLayout()->draw( painter, paintcontext );
494 } else {
495 painter->drawText( rect, mTextAlignment, mText );
496 }
497 }
498
HorizontalLineLayoutItem()499 KChart::HorizontalLineLayoutItem::HorizontalLineLayoutItem()
500 : AbstractLayoutItem( Qt::AlignCenter )
501 {
502 }
503
expandingDirections() const504 Qt::Orientations KChart::HorizontalLineLayoutItem::expandingDirections() const
505 {
506 return Qt::Horizontal;
507 }
508
geometry() const509 QRect KChart::HorizontalLineLayoutItem::geometry() const
510 {
511 return mRect;
512 }
513
isEmpty() const514 bool KChart::HorizontalLineLayoutItem::isEmpty() const
515 {
516 return false; // never empty, otherwise the layout item would not exist
517 }
518
maximumSize() const519 QSize KChart::HorizontalLineLayoutItem::maximumSize() const
520 {
521 return QSize( QWIDGETSIZE_MAX, QWIDGETSIZE_MAX );
522 }
523
minimumSize() const524 QSize KChart::HorizontalLineLayoutItem::minimumSize() const
525 {
526 return QSize( 0, 0 );
527 }
528
setGeometry(const QRect & r)529 void KChart::HorizontalLineLayoutItem::setGeometry( const QRect& r )
530 {
531 mRect = r;
532 }
533
sizeHint() const534 QSize KChart::HorizontalLineLayoutItem::sizeHint() const
535 {
536 return QSize( -1, 3 ); // see qframe.cpp
537 }
538
539
paint(QPainter * painter)540 void KChart::HorizontalLineLayoutItem::paint( QPainter* painter )
541 {
542 if ( !mRect.isValid() )
543 return;
544
545 painter->drawLine( QPointF( mRect.left(), mRect.center().y() ),
546 QPointF( mRect.right(), mRect.center().y() ) );
547 }
548
549
VerticalLineLayoutItem()550 KChart::VerticalLineLayoutItem::VerticalLineLayoutItem()
551 : AbstractLayoutItem( Qt::AlignCenter )
552 {
553 }
554
expandingDirections() const555 Qt::Orientations KChart::VerticalLineLayoutItem::expandingDirections() const
556 {
557 return Qt::Vertical;
558 }
559
geometry() const560 QRect KChart::VerticalLineLayoutItem::geometry() const
561 {
562 return mRect;
563 }
564
isEmpty() const565 bool KChart::VerticalLineLayoutItem::isEmpty() const
566 {
567 return false; // never empty, otherwise the layout item would not exist
568 }
569
maximumSize() const570 QSize KChart::VerticalLineLayoutItem::maximumSize() const
571 {
572 return QSize( QWIDGETSIZE_MAX, QWIDGETSIZE_MAX );
573 }
574
minimumSize() const575 QSize KChart::VerticalLineLayoutItem::minimumSize() const
576 {
577 return QSize( 0, 0 );
578 }
579
setGeometry(const QRect & r)580 void KChart::VerticalLineLayoutItem::setGeometry( const QRect& r )
581 {
582 mRect = r;
583 }
584
sizeHint() const585 QSize KChart::VerticalLineLayoutItem::sizeHint() const
586 {
587 return QSize( 3, -1 ); // see qframe.cpp
588 }
589
590
paint(QPainter * painter)591 void KChart::VerticalLineLayoutItem::paint( QPainter* painter )
592 {
593 if ( !mRect.isValid() )
594 return;
595
596 painter->drawLine( QPointF( mRect.center().x(), mRect.top() ),
597 QPointF( mRect.center().x(), mRect.bottom() ) );
598 }
599
600
601
MarkerLayoutItem(KChart::AbstractDiagram * diagram,const MarkerAttributes & marker,const QBrush & brush,const QPen & pen,Qt::Alignment alignment)602 KChart::MarkerLayoutItem::MarkerLayoutItem( KChart::AbstractDiagram* diagram,
603 const MarkerAttributes& marker,
604 const QBrush& brush, const QPen& pen,
605 Qt::Alignment alignment )
606 : AbstractLayoutItem( alignment )
607 , mDiagram( diagram )
608 , mMarker( marker )
609 , mBrush( brush )
610 , mPen( pen )
611 {
612 }
613
expandingDirections() const614 Qt::Orientations KChart::MarkerLayoutItem::expandingDirections() const
615 {
616 return Qt::Orientations(); // Grow neither vertically nor horizontally
617 }
618
geometry() const619 QRect KChart::MarkerLayoutItem::geometry() const
620 {
621 return mRect;
622 }
623
isEmpty() const624 bool KChart::MarkerLayoutItem::isEmpty() const
625 {
626 return false; // never empty, otherwise the layout item would not exist
627 }
628
maximumSize() const629 QSize KChart::MarkerLayoutItem::maximumSize() const
630 {
631 return sizeHint(); // PENDING(kalle) Review, quite inflexible
632 }
633
minimumSize() const634 QSize KChart::MarkerLayoutItem::minimumSize() const
635 {
636 return sizeHint(); // PENDING(kalle) Review, quite inflexible
637 }
638
setGeometry(const QRect & r)639 void KChart::MarkerLayoutItem::setGeometry( const QRect& r )
640 {
641 mRect = r;
642 }
643
sizeHint() const644 QSize KChart::MarkerLayoutItem::sizeHint() const
645 {
646 //qDebug() << "KChart::MarkerLayoutItem::sizeHint() returns:"<<mMarker.markerSize().toSize();
647 return mMarker.markerSize().toSize();
648 }
649
paint(QPainter * painter)650 void KChart::MarkerLayoutItem::paint( QPainter* painter )
651 {
652 paintIntoRect( painter, mRect, mDiagram, mMarker, mBrush, mPen );
653 }
654
paintIntoRect(QPainter * painter,const QRect & rect,AbstractDiagram * diagram,const MarkerAttributes & marker,const QBrush & brush,const QPen & pen)655 void KChart::MarkerLayoutItem::paintIntoRect(
656 QPainter* painter,
657 const QRect& rect,
658 AbstractDiagram* diagram,
659 const MarkerAttributes& marker,
660 const QBrush& brush,
661 const QPen& pen )
662 {
663 if ( !rect.isValid() )
664 return;
665
666 // The layout management may assign a larger rect than what we
667 // wanted. We need to adjust the position.
668 const QSize siz = marker.markerSize().toSize();
669 QPointF pos = rect.topLeft();
670 pos += QPointF( static_cast<qreal>(( rect.width() - siz.width()) / 2.0 ),
671 static_cast<qreal>(( rect.height() - siz.height()) / 2.0 ) );
672
673 #ifdef DEBUG_ITEMS_PAINT
674 QPointF oldPos = pos;
675 #endif
676
677 // And finally, drawMarker() assumes the position to be the center
678 // of the marker, adjust again.
679 pos += QPointF( static_cast<qreal>( siz.width() ) / 2.0,
680 static_cast<qreal>( siz.height() )/ 2.0 );
681
682 diagram->paintMarker( painter, marker, brush, pen, pos.toPoint(), siz );
683
684 #ifdef DEBUG_ITEMS_PAINT
685 const QPen oldPen( painter->pen() );
686 painter->setPen( Qt::red );
687 painter->drawRect( QRect( oldPos.toPoint(), siz ) );
688 painter->setPen( oldPen );
689 #endif
690 }
691
692
LineLayoutItem(KChart::AbstractDiagram * diagram,int length,const QPen & pen,Qt::Alignment legendLineSymbolAlignment,Qt::Alignment alignment)693 KChart::LineLayoutItem::LineLayoutItem( KChart::AbstractDiagram* diagram,
694 int length,
695 const QPen& pen,
696 Qt::Alignment legendLineSymbolAlignment,
697 Qt::Alignment alignment )
698 : AbstractLayoutItem( alignment )
699 , mDiagram( diagram )
700 , mLength( length )
701 , mPen( pen )
702 , mLegendLineSymbolAlignment(legendLineSymbolAlignment)
703 {
704 // enforce a minimum pen width
705 if ( pen.width() < 2 )
706 mPen.setWidth( 2 );
707 }
708
expandingDirections() const709 Qt::Orientations KChart::LineLayoutItem::expandingDirections() const
710 {
711 return Qt::Orientations(); // Grow neither vertically nor horizontally
712 }
713
geometry() const714 QRect KChart::LineLayoutItem::geometry() const
715 {
716 return mRect;
717 }
718
isEmpty() const719 bool KChart::LineLayoutItem::isEmpty() const
720 {
721 return false; // never empty, otherwise the layout item would not exist
722 }
723
maximumSize() const724 QSize KChart::LineLayoutItem::maximumSize() const
725 {
726 return sizeHint(); // PENDING(kalle) Review, quite inflexible
727 }
728
minimumSize() const729 QSize KChart::LineLayoutItem::minimumSize() const
730 {
731 return sizeHint(); // PENDING(kalle) Review, quite inflexible
732 }
733
setGeometry(const QRect & r)734 void KChart::LineLayoutItem::setGeometry( const QRect& r )
735 {
736 mRect = r;
737 }
738
sizeHint() const739 QSize KChart::LineLayoutItem::sizeHint() const
740 {
741 return QSize( mLength, mPen.width() + 2 );
742 }
743
744
setLegendLineSymbolAlignment(Qt::Alignment legendLineSymbolAlignment)745 void KChart::LineLayoutItem::setLegendLineSymbolAlignment(Qt::Alignment legendLineSymbolAlignment)
746 {
747 if (mLegendLineSymbolAlignment == legendLineSymbolAlignment)
748 return;
749
750 mLegendLineSymbolAlignment = legendLineSymbolAlignment;
751 }
752
legendLineSymbolAlignment() const753 Qt::Alignment KChart::LineLayoutItem::legendLineSymbolAlignment() const
754 {
755 return mLegendLineSymbolAlignment;
756 }
757
paint(QPainter * painter)758 void KChart::LineLayoutItem::paint( QPainter* painter )
759 {
760 paintIntoRect( painter, mRect, mPen, mLegendLineSymbolAlignment );
761 }
762
paintIntoRect(QPainter * painter,const QRect & rect,const QPen & pen,Qt::Alignment lineAlignment)763 void KChart::LineLayoutItem::paintIntoRect(
764 QPainter* painter,
765 const QRect& rect,
766 const QPen& pen,
767 Qt::Alignment lineAlignment)
768 {
769 if ( ! rect.isValid() )
770 return;
771
772 const QPen oldPen = painter->pen();
773 painter->setPen( PrintingParameters::scalePen( pen ) );
774 qreal y = 0;
775 if (lineAlignment == Qt::AlignTop)
776 y = rect.top();
777 else if (lineAlignment == Qt::AlignBottom)
778 y = rect.bottom();
779 else
780 y = rect.center().y();
781
782 painter->drawLine( QPointF( rect.left(), y ),
783 QPointF( rect.right(), y ) );
784 painter->setPen( oldPen );
785 }
786
787
LineWithMarkerLayoutItem(KChart::AbstractDiagram * diagram,int lineLength,const QPen & linePen,int markerOffs,const MarkerAttributes & marker,const QBrush & markerBrush,const QPen & markerPen,Qt::Alignment alignment)788 KChart::LineWithMarkerLayoutItem::LineWithMarkerLayoutItem(
789 KChart::AbstractDiagram* diagram,
790 int lineLength,
791 const QPen& linePen,
792 int markerOffs,
793 const MarkerAttributes& marker,
794 const QBrush& markerBrush,
795 const QPen& markerPen,
796 Qt::Alignment alignment )
797 : AbstractLayoutItem( alignment )
798 , mDiagram( diagram )
799 , mLineLength( lineLength )
800 , mLinePen( linePen )
801 , mMarkerOffs( markerOffs )
802 , mMarker( marker )
803 , mMarkerBrush( markerBrush )
804 , mMarkerPen( markerPen )
805 {
806 }
807
expandingDirections() const808 Qt::Orientations KChart::LineWithMarkerLayoutItem::expandingDirections() const
809 {
810 return Qt::Orientations(); // Grow neither vertically nor horizontally
811 }
812
geometry() const813 QRect KChart::LineWithMarkerLayoutItem::geometry() const
814 {
815 return mRect;
816 }
817
isEmpty() const818 bool KChart::LineWithMarkerLayoutItem::isEmpty() const
819 {
820 return false; // never empty, otherwise the layout item would not exist
821 }
822
maximumSize() const823 QSize KChart::LineWithMarkerLayoutItem::maximumSize() const
824 {
825 return sizeHint(); // PENDING(kalle) Review, quite inflexible
826 }
827
minimumSize() const828 QSize KChart::LineWithMarkerLayoutItem::minimumSize() const
829 {
830 return sizeHint(); // PENDING(kalle) Review, quite inflexible
831 }
832
setGeometry(const QRect & r)833 void KChart::LineWithMarkerLayoutItem::setGeometry( const QRect& r )
834 {
835 mRect = r;
836 }
837
sizeHint() const838 QSize KChart::LineWithMarkerLayoutItem::sizeHint() const
839 {
840 const QSize lineSize( mLineLength, mLinePen.width() + 2 );
841 return lineSize.expandedTo( mMarker.markerSize().toSize() );
842 }
843
paint(QPainter * painter)844 void KChart::LineWithMarkerLayoutItem::paint( QPainter* painter )
845 {
846 // paint the line over the full width, into the vertical middle of the rect
847 LineLayoutItem::paintIntoRect( painter, mRect, mLinePen, Qt::AlignCenter );
848
849 // paint the marker with the given offset from the left side of the line
850 const QRect r(
851 QPoint( mRect.x()+mMarkerOffs, mRect.y() ),
852 QSize( mMarker.markerSize().toSize().width(), mRect.height() ) );
853 MarkerLayoutItem::paintIntoRect(
854 painter, r, mDiagram, mMarker, mMarkerBrush, mMarkerPen );
855 }
856
AutoSpacerLayoutItem(bool layoutIsAtTopPosition,QHBoxLayout * rightLeftLayout,bool layoutIsAtLeftPosition,QVBoxLayout * topBottomLayout)857 KChart::AutoSpacerLayoutItem::AutoSpacerLayoutItem(
858 bool layoutIsAtTopPosition, QHBoxLayout *rightLeftLayout,
859 bool layoutIsAtLeftPosition, QVBoxLayout *topBottomLayout )
860 : AbstractLayoutItem( Qt::AlignCenter )
861 , mLayoutIsAtTopPosition( layoutIsAtTopPosition )
862 , mRightLeftLayout( rightLeftLayout )
863 , mLayoutIsAtLeftPosition( layoutIsAtLeftPosition )
864 , mTopBottomLayout( topBottomLayout )
865 {
866 }
867
expandingDirections() const868 Qt::Orientations KChart::AutoSpacerLayoutItem::expandingDirections() const
869 {
870 return Qt::Orientations(); // Grow neither vertically nor horizontally
871 }
872
geometry() const873 QRect KChart::AutoSpacerLayoutItem::geometry() const
874 {
875 return mRect;
876 }
877
isEmpty() const878 bool KChart::AutoSpacerLayoutItem::isEmpty() const
879 {
880 return true; // never empty, otherwise the layout item would not exist
881 }
882
maximumSize() const883 QSize KChart::AutoSpacerLayoutItem::maximumSize() const
884 {
885 return sizeHint();
886 }
887
minimumSize() const888 QSize KChart::AutoSpacerLayoutItem::minimumSize() const
889 {
890 return sizeHint();
891 }
892
setGeometry(const QRect & r)893 void KChart::AutoSpacerLayoutItem::setGeometry( const QRect& r )
894 {
895 mRect = r;
896 }
897
898
updateCommonBrush(QBrush & commonBrush,bool & bStart,const KChart::AbstractArea & area)899 static void updateCommonBrush( QBrush& commonBrush, bool& bStart, const KChart::AbstractArea& area )
900 {
901 const KChart::BackgroundAttributes ba( area.backgroundAttributes() );
902 const bool hasSimpleBrush = (
903 ! area.frameAttributes().isVisible() &&
904 ba.isVisible() &&
905 ba.pixmapMode() == KChart::BackgroundAttributes::BackgroundPixmapModeNone &&
906 ba.brush().gradient() == nullptr );
907 if ( bStart ) {
908 bStart = false;
909 commonBrush = hasSimpleBrush ? ba.brush() : QBrush();
910 } else {
911 if ( ! hasSimpleBrush || ba.brush() != commonBrush )
912 {
913 commonBrush = QBrush();
914 }
915 }
916 }
917
sizeHint() const918 QSize KChart::AutoSpacerLayoutItem::sizeHint() const
919 {
920 QBrush commonBrush;
921 bool bStart=true;
922 // calculate the maximal overlap of the top/bottom axes:
923 int topBottomOverlap = 0;
924 if ( mTopBottomLayout ) {
925 for (int i = 0; i < mTopBottomLayout->count(); ++i) {
926 AbstractArea* area = dynamic_cast<AbstractArea*>(mTopBottomLayout->itemAt(i));
927 if ( area ) {
928 //qDebug() << "AutoSpacerLayoutItem testing" << area;
929 topBottomOverlap = qMax( topBottomOverlap,
930 mLayoutIsAtLeftPosition ? area->rightOverlap()
931 : area->leftOverlap() );
932 updateCommonBrush( commonBrush, bStart, *area );
933 }
934 }
935 }
936 // calculate the maximal overlap of the left/right axes:
937 int leftRightOverlap = 0;
938 if ( mRightLeftLayout ) {
939 for (int i = 0; i < mRightLeftLayout->count(); ++i) {
940 AbstractArea* area = dynamic_cast<AbstractArea*>(mRightLeftLayout->itemAt(i));
941 if ( area ) {
942 //qDebug() << "AutoSpacerLayoutItem testing" << area;
943 leftRightOverlap = qMax( leftRightOverlap,
944 mLayoutIsAtTopPosition ? area->bottomOverlap()
945 : area->topOverlap() );
946 updateCommonBrush( commonBrush, bStart, *area );
947 }
948 }
949 }
950 if ( topBottomOverlap > 0 && leftRightOverlap > 0 )
951 mCommonBrush = commonBrush;
952 else
953 mCommonBrush = QBrush();
954 mCachedSize = QSize( topBottomOverlap, leftRightOverlap );
955 //qDebug() << mCachedSize;
956 return mCachedSize;
957 }
958
959
paint(QPainter * painter)960 void KChart::AutoSpacerLayoutItem::paint( QPainter* painter )
961 {
962 if ( mParentLayout && mRect.isValid() && mCachedSize.isValid() &&
963 mCommonBrush.style() != Qt::NoBrush )
964 {
965 QPoint p1( mRect.topLeft() );
966 QPoint p2( mRect.bottomRight() );
967 if ( mLayoutIsAtLeftPosition )
968 p1.rx() += mCachedSize.width() - mParentLayout->spacing();
969 else
970 p2.rx() -= mCachedSize.width() - mParentLayout->spacing();
971 if ( mLayoutIsAtTopPosition ) {
972 p1.ry() += mCachedSize.height() - mParentLayout->spacing() - 1;
973 p2.ry() -= 1;
974 } else
975 p2.ry() -= mCachedSize.height() - mParentLayout->spacing() - 1;
976 //qDebug() << mLayoutIsAtTopPosition << mLayoutIsAtLeftPosition;
977 //qDebug() << mRect;
978 //qDebug() << mParentLayout->margin();
979 //qDebug() << QRect( p1, p2 );
980 const QPoint oldBrushOrigin( painter->brushOrigin() );
981 const QBrush oldBrush( painter->brush() );
982 const QPen oldPen( painter->pen() );
983 const QPointF newTopLeft( painter->deviceMatrix().map( p1 ) );
984 painter->setBrushOrigin( newTopLeft );
985 painter->setBrush( mCommonBrush );
986 painter->setPen( Qt::NoPen );
987 painter->drawRect( QRect( p1, p2 ) );
988 painter->setBrushOrigin( oldBrushOrigin );
989 painter->setBrush( oldBrush );
990 painter->setPen( oldPen );
991 }
992 // debug code:
993 #if 0
994 //qDebug() << "KChart::AutoSpacerLayoutItem::paint()";
995 if ( !mRect.isValid() )
996 return;
997
998 painter->drawRect( mRect );
999 painter->drawLine( QPointF( mRect.topLeft(), mRect.bottomRight() ) );
1000 painter->drawLine( QPointF( mRect.topRight(), mRect.bottomLeft() ) );
1001 #endif
1002 }
1003