KD Chart 2  [rev.2.5.1]
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Pages
KDChartLayoutItems.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 ** Copyright (C) 2001-2013 Klaralvdalens Datakonsult AB. All rights reserved.
3 **
4 ** This file is part of the KD Chart library.
5 **
6 ** Licensees holding valid commercial KD Chart licenses may use this file in
7 ** accordance with the KD Chart Commercial License Agreement provided with
8 ** the Software.
9 **
10 **
11 ** This file may be distributed and/or modified under the terms of the
12 ** GNU General Public License version 2 and version 3 as published by the
13 ** Free Software Foundation and appearing in the file LICENSE.GPL.txt included.
14 **
15 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
16 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
17 **
18 ** Contact info@kdab.com if any conditions of this licensing are not
19 ** clear to you.
20 **
21 **********************************************************************/
22 
23 #include "KDChartLayoutItems.h"
24 #include "KDTextDocument.h"
25 #include "KDChartAbstractArea.h"
26 #include "KDChartAbstractDiagram.h"
28 #include "KDChartFrameAttributes.h"
29 #include "KDChartPaintContext.h"
30 #include "KDChartPainterSaver_p.h"
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 #include <KDABLibFakes>
45 
46 #include <math.h>
47 
48 
49 //#define DEBUG_ITEMS_PAINT
50 
60 {
61  mParent = widget;
62 }
63 
64 void KDChart::AbstractLayoutItem::paintAll( QPainter& painter )
65 {
66  paint( &painter );
67 }
68 
73 {
74  if ( context )
75  paint( context->painter() );
76 }
77 
82 {
83  // This is exactly like what QWidget::updateGeometry does.
84 // qDebug("KDChart::AbstractLayoutItem::sizeHintChanged() called");
85  if ( mParent ) {
86  if ( mParent->layout() )
87  mParent->layout()->invalidate();
88  else
89  QApplication::postEvent( mParent, new QEvent( QEvent::LayoutRequest ) );
90  }
91 }
92 
94  const KDChart::TextAttributes& attributes,
95  const QObject* area,
97  Qt::Alignment alignment )
98  : AbstractLayoutItem( alignment ),
99  m_text( new TextLayoutItem( text, attributes, area, orientation, alignment ) )
100 {
101 }
102 
104  : AbstractLayoutItem( Qt::AlignLeft ),
105  m_text( new TextLayoutItem() )
106 {
107 }
108 
110 {
111  delete m_text;
112 }
113 
115 {
116  m_text->setAutoReferenceArea( area );
117 }
118 
120 {
121  return m_text->autoReferenceArea();
122 }
123 
124 void KDChart::TextBubbleLayoutItem::setText( const QString& text )
125 {
126  m_text->setText( text );
127 }
128 
130 {
131  return m_text->text();
132 }
133 
135 {
136  m_text->setTextAttributes( a );
137 }
138 
140 {
141  return m_text->textAttributes();
142 }
143 
145 {
146  return m_text->isEmpty();
147 }
148 
150 {
151  return m_text->expandingDirections();
152 }
153 
155 {
156  const int border = borderWidth();
157  return m_text->maximumSize() + QSize( 2 * border, 2 * border );
158 }
159 
161 {
162  const int border = borderWidth();
163  return m_text->minimumSize() + QSize( 2 * border, 2 * border );
164 }
165 
167 {
168  const int border = borderWidth();
169  return m_text->sizeHint() + QSize( 2 * border, 2 * border );
170 }
171 
173 {
174  const int border = borderWidth();
175  m_text->setGeometry( r.adjusted( border, border, -border, -border ) );
176 }
177 
179 {
180  const int border = borderWidth();
181  return m_text->geometry().adjusted( -border, -border, border, border );
182 }
183 
184 void KDChart::TextBubbleLayoutItem::paint( QPainter* painter )
185 {
186  const QPen oldPen = painter->pen();
187  const QBrush oldBrush = painter->brush();
188  painter->setPen( Qt::black );
189  painter->setBrush( QColor( 255, 255, 220 ) );
190  painter->drawRoundRect( geometry(), 10 );
191  painter->setPen( oldPen );
192  painter->setBrush( oldBrush );
193  m_text->paint( painter );
194 }
195 
197 {
198  return 1;
199 }
200 
202  const KDChart::TextAttributes& attributes,
203  const QObject* area,
205  Qt::Alignment alignment )
206  : AbstractLayoutItem( alignment )
207  , mText( text )
208  , mTextAlignment( alignment )
209  , mAttributes( attributes )
210  , mAutoReferenceArea( area )
211  , mAutoReferenceOrientation( orientation )
212  , cachedSizeHint() // default this to invalid to force just-in-time calculation before first use of sizeHint()
213  , cachedFontSize( 0.0 )
214  , cachedFont( mAttributes.font() )
215 {
216 }
217 
219  : AbstractLayoutItem( Qt::AlignLeft )
220  , mText()
221  , mTextAlignment( Qt::AlignLeft )
222  , mAttributes()
223  , mAutoReferenceArea( 0 )
224  , mAutoReferenceOrientation( KDChartEnums::MeasureOrientationHorizontal )
225  , cachedSizeHint() // default this to invalid to force just-in-time calculation before first use of sizeHint()
226  , cachedFontSize( 0.0 )
227  , cachedFont( mAttributes.font() )
228 {
229 
230 }
231 
233 {
234  mAutoReferenceArea = area;
235  cachedSizeHint = QSize();
236  sizeHint();
237 }
238 
240 {
241  return mAutoReferenceArea;
242 }
243 
244 void KDChart::TextLayoutItem::setText(const QString & text)
245 {
246  mText = text;
247  cachedSizeHint = QSize();
248  sizeHint();
249  if ( mParent )
250  mParent->update();
251 }
252 
254 {
255  return mText;
256 }
257 
258 void KDChart::TextLayoutItem::setTextAlignment( Qt::Alignment alignment)
259 {
260  if ( mTextAlignment == alignment )
261  return;
262  mTextAlignment = alignment;
263  if ( mParent )
264  mParent->update();
265 }
266 
268 {
269  return mTextAlignment;
270 }
271 
278 {
279  mAttributes = a;
280  cachedFont = a.font();
281  cachedSizeHint = QSize(); // invalidate size hint
282  sizeHint();
283  if ( mParent )
284  mParent->update();
285 }
286 
293 {
294  return mAttributes;
295 }
296 
297 
299 {
300  return 0; // Grow neither vertically nor horizontally
301 }
302 
304 {
305  return mRect;
306 }
307 
309 {
310  return false; // never empty, otherwise the layout item would not exist
311 }
312 
314 {
315  return sizeHint(); // PENDING(kalle) Review, quite inflexible
316 }
317 
319 {
320  return sizeHint(); // PENDING(kalle) Review, quite inflexible
321 }
322 
324 {
325  mRect = r;
326 }
327 
328 // returns the bounding box of rect rotated around its center
329 QRectF rotatedRect( const QRectF& rect, qreal rotation )
330 {
331  QTransform t;
332  QPointF center = rect.center();
333  t.translate( center.x(), center.y() );
334  t.rotate( rotation );
335  t.translate( -center.x(), -center.y() );
336  return t.mapRect( rect );
337 }
338 
339 qreal KDChart::TextLayoutItem::fitFontSizeToGeometry() const
340 {
341  QFont f = realFont();
342  const qreal origResult = f.pointSizeF();
343  qreal result = origResult;
344  const qreal minSize = mAttributes.minimalFontSize().value();
345  const QSize mySize = geometry().size();
346  if ( mySize.isNull() ) {
347  return result;
348  }
349 
350  QFontMetrics fm( f );
351  while ( true ) {
352  const QSizeF textSize = rotatedRect( fm.boundingRect( mText ), mAttributes.rotation() ).normalized().size();
353 
354  if ( textSize.height() <= mySize.height() && textSize.width() <= mySize.width() ) {
355  return result;
356  }
357 
358  result -= 0.5;
359  if ( minSize > 0 && result < minSize ) {
360  return result + 0.5;
361  } else if ( result <= 0.0 ) {
362  return origResult;
363  }
364  f.setPointSizeF( result );
365  fm = QFontMetrics( f );
366  }
367 }
368 
370 {
371  return mAttributes.calculatedFontSize( mAutoReferenceArea, mAutoReferenceOrientation );
372 }
373 
374 bool KDChart::TextLayoutItem::maybeUpdateRealFont() const
375 {
376  const qreal fntSiz = realFontSize();
377  const bool doUpdate = !cachedSizeHint.isValid() || cachedFontSize != fntSiz;
378 
379  if ( doUpdate && fntSiz > 0.0 ) {
380  cachedFontSize = fntSiz;
381  cachedFont.setPointSizeF( fntSiz );
382  }
383  return doUpdate; // "didUpdate" by now
384 }
385 
387 {
388  maybeUpdateRealFont();
389  return cachedFont;
390 }
391 
393 {
394  // should probably call sizeHint() here, but that one is expensive (see TODO there)
395  return mCachedBoundingPolygon;
396 }
397 
398 bool KDChart::TextLayoutItem::intersects( const TextLayoutItem& other, const QPointF& myPos, const QPointF& otherPos ) const
399 {
400  return intersects( other, myPos.toPoint(), otherPos.toPoint() );
401 }
402 
403 bool KDChart::TextLayoutItem::intersects( const TextLayoutItem& other, const QPoint& myPos, const QPoint& otherPos ) const
404 {
405  if ( mAttributes.rotation() != other.mAttributes.rotation() )
406  {
407  // that's the code for the common case: the rotation angles don't need to match here
408  QPolygon myPolygon = boundingPolygon().translated( myPos );
409  QPolygon otherPolygon = other.boundingPolygon().translated( otherPos );
410 
411  // create regions out of it
412  QRegion myRegion( myPolygon );
413  QRegion otherRegion( otherPolygon );
414 
415  // now the question - do they intersect or not?
416  return ! myRegion.intersected( otherRegion ).isEmpty();
417 
418  } else {
419  // the rotation angles match so we can use a faster algorithm
420  const qreal angle = DEGTORAD( mAttributes.rotation() );
421  // both sizes
422  const QSizeF mySize( unrotatedSizeHint() );
423  const QSizeF otherSize( other.unrotatedSizeHint() );
424 
425  // that's myP1 relative to myPos
426  QPointF myP1( mySize.height() * sin( angle ), 0.0 );
427  // that's otherP1 to myPos
428  QPointF otherP1 = QPointF( otherSize.height() * sin( angle ), 0.0 ) + otherPos - myPos;
429 
430  // now rotate both points the negative angle around myPos
431  myP1 = QPointF( myP1.x() * cos( -angle ), myP1.x() * sin( -angle ) );
432  qreal r = sqrt( otherP1.x() * otherP1.x() + otherP1.y() * otherP1.y() );
433  if ( myP1.x() == otherP1.x() ) { // vertical
434  otherP1 = QPointF( r * sin( -angle ), r * cos( -angle ) );
435  } else if ( myP1.y() == otherP1.y() ) { // horizontal
436  otherP1 = QPointF( r * cos( -angle ), r * sin( -angle ) );
437  }
438 
439  return QRectF( myP1, mySize ).intersects( QRectF( otherP1, otherSize ) );
440  }
441 }
442 
444 {
445  // ### we only really need to recalculate the size hint when mAttributes.rotation has *changed*
446  if ( maybeUpdateRealFont() || mAttributes.rotation() || !cachedSizeHint.isValid() ) {
447  const QSize newSizeHint( calcSizeHint( cachedFont ) );
448  Q_ASSERT( newSizeHint.isValid() );
449  if ( newSizeHint != cachedSizeHint ) {
450  cachedSizeHint = newSizeHint;
451  sizeHintChanged();
452  }
453  }
454  return cachedSizeHint;
455 }
456 
458 {
459  maybeUpdateRealFont(); // make sure the cached font is up to date
460  return unrotatedSizeHint( cachedFont );
461 }
462 
463 
464 // PENDING(kalle) Support auto shrink
465 
466 
467 QSize KDChart::TextLayoutItem::unrotatedTextSize( QFont fnt ) const
468 {
469  if ( fnt == QFont() ) {
470  fnt = realFont(); // this is the cached font in most cases
471  }
472 
473  const QFontMetricsF fm( fnt, GlobalMeasureScaling::paintDevice() );
474  QRect veryLarge( 0, 0, 100000, 100000 );
475  // this overload of boundingRect() interprets \n as line breaks, not as regular characters.
476  return fm.boundingRect( veryLarge, Qt::AlignLeft | Qt::AlignTop, mText ).size().toSize();
477 }
478 
480 {
481  return marginWidth( unrotatedTextSize() );
482 }
483 
484 int KDChart::TextLayoutItem::marginWidth( const QSize& textSize ) const
485 {
486  return qMin ( QApplication::style()->pixelMetric( QStyle::PM_ButtonMargin, 0, 0 ),
487  // decrease frame size if the text is small
488  textSize.height() * 2 / 3 );
489 }
490 
491 QSize KDChart::TextLayoutItem::unrotatedSizeHint( const QFont& fnt ) const
492 {
493  QSize ret = unrotatedTextSize( fnt );
494  const int margin = marginWidth( ret );
495  ret += QSize( margin, margin );
496  return ret;
497 }
498 
499 QSize KDChart::TextLayoutItem::calcSizeHint( const QFont& font ) const
500 {
501  const QSize size = unrotatedSizeHint( font );
502  QPoint topLeft( -size.width() * 0.5, -size.height() * 0.5 );
503  if ( !mAttributes.rotation() ) {
504  mCachedBoundingPolygon.resize( 4 );
505  // using the same winding order as returned by QPolygon QTransform::mapToPolygon(const QRect&),
506  // which is: 0-1: top edge, 1-2: right edge, 2-3: bottom edge, 3-0: left edge (of input rect)
507  mCachedBoundingPolygon[ 0 ] = topLeft;
508  mCachedBoundingPolygon[ 1 ] = topLeft + QPoint( size.width(), 0 ); // top right
509  mCachedBoundingPolygon[ 2 ] = topLeft + QPoint( size.width(), size.height() ); // bottom right
510  mCachedBoundingPolygon[ 3 ] = topLeft + QPoint( 0, size.height() ); // bottom left
511  return size;
512  }
513 
514  const QRect rect( topLeft, size );
515  QTransform t;
516  t.rotate( mAttributes.rotation() );
517  mCachedBoundingPolygon = t.mapToPolygon( rect );
518 
519  return mCachedBoundingPolygon.boundingRect().size();
520 }
521 
522 void KDChart::TextLayoutItem::paint( QPainter* painter )
523 {
524  if ( !mRect.isValid() ) {
525  return;
526  }
527  const PainterSaver painterSaver( painter );
528  QFont f = realFont();
529  if ( mAttributes.autoShrink() ) {
530  f.setPointSizeF( fitFontSizeToGeometry() );
531  }
532  painter->setFont( f );
533 
534  QSize innerSize = unrotatedTextSize();
535  QRectF rect = QRectF( QPointF( 0, 0 ), innerSize );
536  rect.translate( -rect.center() );
537  painter->translate( mRect.center() );
538  painter->rotate( mAttributes.rotation() );
539 #ifdef DEBUG_ITEMS_PAINT
540  painter->setPen( Qt::red );
541  painter->drawRect( rect );
542 #endif
543 
544  painter->setPen( PrintingParameters::scalePen( mAttributes.pen() ) );
545  QTextDocument* document = mAttributes.textDocument();
546  if ( document ) {
547  document->setPageSize( rect.size() );
548  document->setHtml( mText );
549  QAbstractTextDocumentLayout::PaintContext paintcontext;
550  // ### this doesn't work for rotated painting because clip does not translate the painting
551  // TODO translate the painting either using a QTransform or one of QPainter's transform stages
552  paintcontext.clip = rect;
553  document->documentLayout()->draw( painter, paintcontext );
554  } else {
555  painter->drawText( rect, mTextAlignment, mText );
556  }
557 }
558 
560  : AbstractLayoutItem( Qt::AlignCenter )
561 {
562 }
563 
565 {
566  return Qt::Vertical|Qt::Horizontal; // Grow both vertically, and horizontally
567 }
568 
570 {
571  return mRect;
572 }
573 
575 {
576  return false; // never empty, otherwise the layout item would not exist
577 }
578 
580 {
581  return QSize( QWIDGETSIZE_MAX, QWIDGETSIZE_MAX );
582 }
583 
585 {
586  return QSize( 0, 0 );
587 }
588 
590 {
591  mRect = r;
592 }
593 
595 {
596  return QSize( -1, 3 ); // see qframe.cpp
597 }
598 
599 
601 {
602  if ( !mRect.isValid() )
603  return;
604 
605  painter->drawLine( QPointF( mRect.left(), mRect.center().y() ),
606  QPointF( mRect.right(), mRect.center().y() ) );
607 }
608 
609 
611  : AbstractLayoutItem( Qt::AlignCenter )
612 {
613 }
614 
616 {
617  return Qt::Vertical|Qt::Vertical; // Grow both vertically, and horizontally
618 }
619 
621 {
622  return mRect;
623 }
624 
626 {
627  return false; // never empty, otherwise the layout item would not exist
628 }
629 
631 {
632  return QSize( QWIDGETSIZE_MAX, QWIDGETSIZE_MAX );
633 }
634 
636 {
637  return QSize( 0, 0 );
638 }
639 
641 {
642  mRect = r;
643 }
644 
646 {
647  return QSize( 3, -1 ); // see qframe.cpp
648 }
649 
650 
651 void KDChart::VerticalLineLayoutItem::paint( QPainter* painter )
652 {
653  if ( !mRect.isValid() )
654  return;
655 
656  painter->drawLine( QPointF( mRect.center().x(), mRect.top() ),
657  QPointF( mRect.center().x(), mRect.bottom() ) );
658 }
659 
660 
661 
663  const MarkerAttributes& marker,
664  const QBrush& brush, const QPen& pen,
665  Qt::Alignment alignment )
666  : AbstractLayoutItem( alignment )
667  , mDiagram( diagram )
668  , mMarker( marker )
669  , mBrush( brush )
670  , mPen( pen )
671 {
672 }
673 
675 {
676  return 0; // Grow neither vertically nor horizontally
677 }
678 
680 {
681  return mRect;
682 }
683 
685 {
686  return false; // never empty, otherwise the layout item would not exist
687 }
688 
690 {
691  return sizeHint(); // PENDING(kalle) Review, quite inflexible
692 }
693 
695 {
696  return sizeHint(); // PENDING(kalle) Review, quite inflexible
697 }
698 
700 {
701  mRect = r;
702 }
703 
705 {
706  //qDebug() << "KDChart::MarkerLayoutItem::sizeHint() returns:"<<mMarker.markerSize().toSize();
707  return mMarker.markerSize().toSize();
708 }
709 
710 void KDChart::MarkerLayoutItem::paint( QPainter* painter )
711 {
712  paintIntoRect( painter, mRect, mDiagram, mMarker, mBrush, mPen );
713 }
714 
716  QPainter* painter,
717  const QRect& rect,
718  AbstractDiagram* diagram,
719  const MarkerAttributes& marker,
720  const QBrush& brush,
721  const QPen& pen )
722 {
723  if ( ! rect.isValid() )
724  return;
725 
726  // The layout management may assign a larger rect than what we
727  // wanted. We need to adjust the position.
728  const QSize siz = marker.markerSize().toSize();
729  QPointF pos = rect.topLeft();
730  pos += QPointF( static_cast<qreal>(( rect.width() - siz.width()) / 2.0 ),
731  static_cast<qreal>(( rect.height() - siz.height()) / 2.0 ) );
732 
733 #ifdef DEBUG_ITEMS_PAINT
734  QPointF oldPos = pos;
735 #endif
736 
737 // And finally, drawMarker() assumes the position to be the center
738  // of the marker, adjust again.
739  pos += QPointF( static_cast<qreal>( siz.width() ) / 2.0,
740  static_cast<qreal>( siz.height() )/ 2.0 );
741 
742  diagram->paintMarker( painter, marker, brush, pen, pos.toPoint(), siz );
743 
744 #ifdef DEBUG_ITEMS_PAINT
745  const QPen oldPen( painter->pen() );
746  painter->setPen( Qt::red );
747  painter->drawRect( QRect(oldPos.toPoint(), siz) );
748  painter->setPen( oldPen );
749 #endif
750 }
751 
752 
754  int length,
755  const QPen& pen,
756  Qt::Alignment legendLineSymbolAlignment,
757  Qt::Alignment alignment )
758  : AbstractLayoutItem( alignment )
759  , mDiagram( diagram )
760  , mLength( length )
761  , mPen( pen )
762  , mLegendLineSymbolAlignment(legendLineSymbolAlignment)
763 {
764  //have a mini pen width
765  if ( pen.width() < 2 )
766  mPen.setWidth( 2 );
767 }
768 
770 {
771  return 0; // Grow neither vertically nor horizontally
772 }
773 
775 {
776  return mRect;
777 }
778 
780 {
781  return false; // never empty, otherwise the layout item would not exist
782 }
783 
785 {
786  return sizeHint(); // PENDING(kalle) Review, quite inflexible
787 }
788 
790 {
791  return sizeHint(); // PENDING(kalle) Review, quite inflexible
792 }
793 
795 {
796  mRect = r;
797 }
798 
800 {
801  return QSize( mLength, mPen.width()+2 );
802 }
803 
804 
805 void KDChart::LineLayoutItem::setLegendLineSymbolAlignment(Qt::Alignment legendLineSymbolAlignment)
806 {
807  if (mLegendLineSymbolAlignment == legendLineSymbolAlignment)
808  return;
809 
810  mLegendLineSymbolAlignment = legendLineSymbolAlignment;
811 }
812 
814 {
815  return mLegendLineSymbolAlignment;
816 }
817 
818 void KDChart::LineLayoutItem::paint( QPainter* painter )
819 {
820  paintIntoRect( painter, mRect, mPen, mLegendLineSymbolAlignment );
821 }
822 
824  QPainter* painter,
825  const QRect& rect,
826  const QPen& pen,
827  Qt::Alignment lineAlignment)
828 {
829  if ( ! rect.isValid() )
830  return;
831 
832  const QPen oldPen = painter->pen();
833  painter->setPen( PrintingParameters::scalePen( pen ) );
834  qreal y = 0;
835  if (lineAlignment == Qt::AlignTop)
836  y = rect.top();
837  else if (lineAlignment == Qt::AlignBottom)
838  y = rect.bottom();
839  else
840  y = rect.center().y();
841 
842  painter->drawLine( QPointF( rect.left(), y ),
843  QPointF( rect.right(), y ) );
844  painter->setPen( oldPen );
845 }
846 
847 
849  KDChart::AbstractDiagram* diagram,
850  int lineLength,
851  const QPen& linePen,
852  int markerOffs,
853  const MarkerAttributes& marker,
854  const QBrush& markerBrush,
855  const QPen& markerPen,
856  Qt::Alignment alignment )
857  : AbstractLayoutItem( alignment )
858  , mDiagram( diagram )
859  , mLineLength( lineLength )
860  , mLinePen( linePen )
861  , mMarkerOffs( markerOffs )
862  , mMarker( marker )
863  , mMarkerBrush( markerBrush )
864  , mMarkerPen( markerPen )
865 {
866 }
867 
869 {
870  return 0; // Grow neither vertically nor horizontally
871 }
872 
874 {
875  return mRect;
876 }
877 
879 {
880  return false; // never empty, otherwise the layout item would not exist
881 }
882 
884 {
885  return sizeHint(); // PENDING(kalle) Review, quite inflexible
886 }
887 
889 {
890  return sizeHint(); // PENDING(kalle) Review, quite inflexible
891 }
892 
894 {
895  mRect = r;
896 }
897 
899 {
900  const QSize sizeM = mMarker.markerSize().toSize();
901  const QSize sizeL = QSize( mLineLength, mLinePen.width()+2 );
902  return QSize( qMax(sizeM.width(), sizeL.width()),
903  qMax(sizeM.height(), sizeL.height()) );
904 }
905 
907 {
908  // paint the line over the full width, into the vertical middle of the rect
909  LineLayoutItem::paintIntoRect( painter, mRect, mLinePen, Qt::AlignCenter );
910 
911  // paint the marker with the given offset from the left side of the line
912  const QRect r(
913  QPoint( mRect.x()+mMarkerOffs, mRect.y() ),
914  QSize( mMarker.markerSize().toSize().width(), mRect.height() ) );
916  painter, r, mDiagram, mMarker, mMarkerBrush, mMarkerPen );
917 }
918 
920  bool layoutIsAtTopPosition, QHBoxLayout *rightLeftLayout,
921  bool layoutIsAtLeftPosition, QVBoxLayout *topBottomLayout )
922  : AbstractLayoutItem( Qt::AlignCenter )
923  , mLayoutIsAtTopPosition( layoutIsAtTopPosition )
924  , mRightLeftLayout( rightLeftLayout )
925  , mLayoutIsAtLeftPosition( layoutIsAtLeftPosition )
926  , mTopBottomLayout( topBottomLayout )
927 {
928 }
929 
931 {
932  return 0; // Grow neither vertically nor horizontally
933 }
934 
936 {
937  return mRect;
938 }
939 
941 {
942  return true; // never empty, otherwise the layout item would not exist
943 }
944 
946 {
947  return sizeHint();
948 }
949 
951 {
952  return sizeHint();
953 }
954 
956 {
957  mRect = r;
958 }
959 
960 
961 static void updateCommonBrush( QBrush& commonBrush, bool& bStart, const KDChart::AbstractArea& area )
962 {
964  const bool hasSimpleBrush = (
965  ! area.frameAttributes().isVisible() &&
966  ba.isVisible() &&
968  ba.brush().gradient() == 0 );
969  if ( bStart ) {
970  bStart = false;
971  commonBrush = hasSimpleBrush ? ba.brush() : QBrush();
972  } else {
973  if ( ! hasSimpleBrush || ba.brush() != commonBrush )
974  {
975  commonBrush = QBrush();
976  }
977  }
978 }
979 
981 {
982  QBrush commonBrush;
983  bool bStart=true;
984  // calculate the maximal overlap of the top/bottom axes:
985  int topBottomOverlap = 0;
986  if ( mTopBottomLayout ) {
987  for (int i = 0; i < mTopBottomLayout->count(); ++i) {
988  AbstractArea* area = dynamic_cast<AbstractArea*>(mTopBottomLayout->itemAt(i));
989  if ( area ) {
990  //qDebug() << "AutoSpacerLayoutItem testing" << area;
991  topBottomOverlap = qMax( topBottomOverlap,
992  mLayoutIsAtLeftPosition ? area->rightOverlap()
993  : area->leftOverlap() );
994  updateCommonBrush( commonBrush, bStart, *area );
995  }
996  }
997  }
998  // calculate the maximal overlap of the left/right axes:
999  int leftRightOverlap = 0;
1000  if ( mRightLeftLayout ) {
1001  for (int i = 0; i < mRightLeftLayout->count(); ++i) {
1002  AbstractArea* area = dynamic_cast<AbstractArea*>(mRightLeftLayout->itemAt(i));
1003  if ( area ) {
1004  //qDebug() << "AutoSpacerLayoutItem testing" << area;
1005  leftRightOverlap = qMax( leftRightOverlap,
1006  mLayoutIsAtTopPosition ? area->bottomOverlap()
1007  : area->topOverlap() );
1008  updateCommonBrush( commonBrush, bStart, *area );
1009  }
1010  }
1011  }
1012  if ( topBottomOverlap > 0 && leftRightOverlap > 0 )
1013  mCommonBrush = commonBrush;
1014  else
1015  mCommonBrush = QBrush();
1016  mCachedSize = QSize( topBottomOverlap, leftRightOverlap );
1017  //qDebug() << mCachedSize;
1018  return mCachedSize;
1019 }
1020 
1021 
1022 void KDChart::AutoSpacerLayoutItem::paint( QPainter* painter )
1023 {
1024  if ( mParentLayout && mRect.isValid() && mCachedSize.isValid() &&
1025  mCommonBrush.style() != Qt::NoBrush )
1026  {
1027  QPoint p1( mRect.topLeft() );
1028  QPoint p2( mRect.bottomRight() );
1029  if ( mLayoutIsAtLeftPosition )
1030  p1.rx() += mCachedSize.width() - mParentLayout->spacing();
1031  else
1032  p2.rx() -= mCachedSize.width() - mParentLayout->spacing();
1033  if ( mLayoutIsAtTopPosition ) {
1034  p1.ry() += mCachedSize.height() - mParentLayout->spacing() - 1;
1035  p2.ry() -= 1;
1036  } else
1037  p2.ry() -= mCachedSize.height() - mParentLayout->spacing() - 1;
1038  //qDebug() << mLayoutIsAtTopPosition << mLayoutIsAtLeftPosition;
1039  //qDebug() << mRect;
1040  //qDebug() << mParentLayout->margin();
1041  //qDebug() << QRect( p1, p2 );
1042  const QPoint oldBrushOrigin( painter->brushOrigin() );
1043  const QBrush oldBrush( painter->brush() );
1044  const QPen oldPen( painter->pen() );
1045  const QPointF newTopLeft( painter->deviceMatrix().map( p1 ) );
1046  painter->setBrushOrigin( newTopLeft );
1047  painter->setBrush( mCommonBrush );
1048  painter->setPen( Qt::NoPen );
1049  painter->drawRect( QRect( p1, p2 ) );
1050  painter->setBrushOrigin( oldBrushOrigin );
1051  painter->setBrush( oldBrush );
1052  painter->setPen( oldPen );
1053  }
1054  // debug code:
1055 #if 0
1056  //qDebug() << "KDChart::AutoSpacerLayoutItem::paint()";
1057  if ( !mRect.isValid() )
1058  return;
1059 
1060  painter->drawRect( mRect );
1061  painter->drawLine( QPointF( mRect.topLeft(), mRect.bottomRight() ) );
1062  painter->drawLine( QPointF( mRect.topRight(), mRect.bottomLeft() ) );
1063 #endif
1064 }

Klarälvdalens Datakonsult AB (KDAB)
Qt-related services and products
http://www.kdab.com/
http://www.kdab.com/products/kd-chart/