source: ntrip/trunk/BNC/qwt/qwt_plot_legenditem.cpp@ 9045

Last change on this file since 9045 was 8127, checked in by stoecker, 8 years ago

update qwt and qwtpolar, many QT5 fixes (unfinished)

File size: 19.4 KB
Line 
1/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
2 * Qwt Widget Library
3 * Copyright (C) 1997 Josef Wilgen
4 * Copyright (C) 2002 Uwe Rathmann
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the Qwt License, Version 1.0
8 *****************************************************************************/
9
10#include "qwt_plot_legenditem.h"
11#include "qwt_dyngrid_layout.h"
12#include "qwt_scale_map.h"
13#include "qwt_painter.h"
14#include <qlayoutitem.h>
15#include <qpen.h>
16#include <qbrush.h>
17#include <qpainter.h>
18#include <qmath.h>
19
20class QwtLegendLayoutItem: public QLayoutItem
21{
22public:
23 QwtLegendLayoutItem( const QwtPlotLegendItem *, const QwtPlotItem * );
24 virtual ~QwtLegendLayoutItem();
25
26 const QwtPlotItem *plotItem() const;
27
28 void setData( const QwtLegendData & );
29 const QwtLegendData &data() const;
30
31 virtual Qt::Orientations expandingDirections() const;
32 virtual QRect geometry() const;
33 virtual bool hasHeightForWidth() const;
34 virtual int heightForWidth( int w ) const;
35 virtual bool isEmpty() const;
36 virtual QSize maximumSize() const;
37 virtual int minimumHeightForWidth( int w ) const;
38 virtual QSize minimumSize() const;
39 virtual void setGeometry( const QRect & r );
40 virtual QSize sizeHint() const;
41
42private:
43
44 const QwtPlotLegendItem *d_legendItem;
45 const QwtPlotItem *d_plotItem;
46 QwtLegendData d_data;
47
48 QRect d_rect;
49};
50
51QwtLegendLayoutItem::QwtLegendLayoutItem(
52 const QwtPlotLegendItem *legendItem, const QwtPlotItem *plotItem ):
53 d_legendItem( legendItem ),
54 d_plotItem( plotItem)
55{
56}
57
58QwtLegendLayoutItem::~QwtLegendLayoutItem()
59{
60}
61
62const QwtPlotItem *QwtLegendLayoutItem::plotItem() const
63{
64 return d_plotItem;
65}
66
67void QwtLegendLayoutItem::setData( const QwtLegendData &data )
68{
69 d_data = data;
70}
71
72const QwtLegendData &QwtLegendLayoutItem::data() const
73{
74 return d_data;
75}
76
77Qt::Orientations QwtLegendLayoutItem::expandingDirections() const
78{
79 return Qt::Horizontal;
80}
81
82bool QwtLegendLayoutItem::hasHeightForWidth() const
83{
84 return !d_data.title().isEmpty();
85}
86
87int QwtLegendLayoutItem::minimumHeightForWidth( int w ) const
88{
89 return d_legendItem->heightForWidth( d_data, w );
90}
91
92int QwtLegendLayoutItem::heightForWidth( int w ) const
93{
94 return d_legendItem->heightForWidth( d_data, w );
95}
96
97bool QwtLegendLayoutItem::isEmpty() const
98{
99 return false;
100}
101
102QSize QwtLegendLayoutItem::maximumSize() const
103{
104 return QSize( QLAYOUTSIZE_MAX, QLAYOUTSIZE_MAX );
105}
106
107QSize QwtLegendLayoutItem::minimumSize() const
108{
109 return d_legendItem->minimumSize( d_data );
110}
111
112QSize QwtLegendLayoutItem::sizeHint() const
113{
114 return minimumSize();
115}
116
117void QwtLegendLayoutItem::setGeometry( const QRect &rect )
118{
119 d_rect = rect;
120}
121
122QRect QwtLegendLayoutItem::geometry() const
123{
124 return d_rect;
125}
126
127class QwtPlotLegendItem::PrivateData
128{
129public:
130 PrivateData():
131 itemMargin( 4 ),
132 itemSpacing( 4 ),
133 borderRadius( 0.0 ),
134 borderPen( Qt::NoPen ),
135 backgroundBrush( Qt::NoBrush ),
136 backgroundMode( QwtPlotLegendItem::LegendBackground ),
137 borderDistance( 10 ),
138 alignment( Qt::AlignRight | Qt::AlignBottom )
139 {
140 layout = new QwtDynGridLayout();
141 layout->setMaxColumns( 2 );
142
143 layout->setSpacing( 0 );
144 layout->setContentsMargins( 0, 0, 0, 0 );
145 }
146
147 ~PrivateData()
148 {
149 delete layout;
150 }
151
152 QFont font;
153 QPen textPen;
154 int itemMargin;
155 int itemSpacing;
156
157 double borderRadius;
158 QPen borderPen;
159 QBrush backgroundBrush;
160 QwtPlotLegendItem::BackgroundMode backgroundMode;
161
162 int borderDistance;
163 Qt::Alignment alignment;
164
165 QMap< const QwtPlotItem *, QList<QwtLegendLayoutItem *> > map;
166 QwtDynGridLayout *layout;
167};
168
169//! Constructor
170QwtPlotLegendItem::QwtPlotLegendItem():
171 QwtPlotItem( QwtText( "Legend" ) )
172{
173 d_data = new PrivateData;
174
175 setItemInterest( QwtPlotItem::LegendInterest, true );
176 setZ( 100.0 );
177}
178
179//! Destructor
180QwtPlotLegendItem::~QwtPlotLegendItem()
181{
182 clearLegend();
183 delete d_data;
184}
185
186//! \return QwtPlotItem::Rtti_PlotLegend
187int QwtPlotLegendItem::rtti() const
188{
189 return QwtPlotItem::Rtti_PlotLegend;
190}
191
192/*!
193 \brief Set the alignmnet
194
195 Alignment means the position of the legend relative
196 to the geometry of the plot canvas.
197
198 \param alignment Alignment flags
199
200 \sa alignment(), setMaxColumns()
201
202 \note To align a legend with many items horizontally
203 the number of columns need to be limited
204 */
205void QwtPlotLegendItem::setAlignment( Qt::Alignment alignment )
206{
207 if ( d_data->alignment != alignment )
208 {
209 d_data->alignment = alignment;
210 itemChanged();
211 }
212}
213
214/*!
215 \return Alignment flags
216 \sa setAlignment()
217 */
218Qt::Alignment QwtPlotLegendItem::alignment() const
219{
220 return d_data->alignment;
221}
222
223/*!
224 \brief Limit the number of columns
225
226 When aligning the legend horizontally ( Qt::AlignLeft, Qt::AlignRight )
227 the number of columns needs to be limited to avoid, that
228 the width of the legend grows with an increasing number of entries.
229
230 \param maxColumns Maximum number of columns. 0 means unlimited.
231 \sa maxColumns(), QwtDynGridLayout::setMaxColumns()
232 */
233void QwtPlotLegendItem::setMaxColumns( uint maxColumns )
234{
235 if ( maxColumns != d_data->layout->maxColumns() )
236 {
237 d_data->layout->setMaxColumns( maxColumns );
238 itemChanged();
239 }
240}
241
242/*!
243 \return Maximum number of columns
244 \sa maxColumns(), QwtDynGridLayout::maxColumns()
245 */
246uint QwtPlotLegendItem::maxColumns() const
247{
248 return d_data->layout->maxColumns();
249}
250
251/*!
252 \brief Set the margin around legend items
253
254 The default setting for the margin is 0.
255
256 \param margin Margin in pixels
257 \sa margin(), setSpacing(), setItemMargin(), setItemSpacing
258 */
259void QwtPlotLegendItem::setMargin( int margin )
260{
261 margin = qMax( margin, 0 );
262 if ( margin != this->margin() )
263 {
264 d_data->layout->setContentsMargins(
265 margin, margin, margin, margin );
266
267 itemChanged();
268 }
269}
270
271/*!
272 \return Margin around the legend items
273 \sa setMargin(), spacing(), itemMargin(), itemSpacing()
274 */
275int QwtPlotLegendItem::margin() const
276{
277 int left;
278 d_data->layout->getContentsMargins( &left, NULL, NULL, NULL );
279
280 return left;
281}
282
283/*!
284 \brief Set the spacing between the legend items
285
286 \param spacing Spacing in pixels
287 \sa spacing(), setMargin()
288*/
289void QwtPlotLegendItem::setSpacing( int spacing )
290{
291 spacing = qMax( spacing, 0 );
292 if ( spacing != d_data->layout->spacing() )
293 {
294 d_data->layout->setSpacing( spacing );
295 itemChanged();
296 }
297}
298
299/*!
300 \return Spacing between the legend items
301 \sa setSpacing(), margin(), itemSpacing(), itemMargin()
302 */
303int QwtPlotLegendItem::spacing() const
304{
305 return d_data->layout->spacing();
306}
307
308/*!
309 Set the margin around each item
310
311 \param margin Margin
312 \sa itemMargin(), setItemSpacing(), setMargin(), setSpacing()
313 */
314void QwtPlotLegendItem::setItemMargin( int margin )
315{
316 margin = qMax( margin, 0 );
317 if ( margin != d_data->itemMargin )
318 {
319 d_data->itemMargin = margin;
320
321 d_data->layout->invalidate();
322 itemChanged();
323 }
324}
325
326/*!
327 \return Margin around each item
328 \sa setItemMargin(), itemSpacing(), margin(), spacing()
329*/
330int QwtPlotLegendItem::itemMargin() const
331{
332 return d_data->itemMargin;
333}
334
335/*!
336 Set the spacing inside of each item
337
338 \param spacing Spacing
339 \sa itemSpacing(), setItemMargin(), setMargin(), setSpacing()
340 */
341void QwtPlotLegendItem::setItemSpacing( int spacing )
342{
343 spacing = qMax( spacing, 0 );
344 if ( spacing != d_data->itemSpacing )
345 {
346 d_data->itemSpacing = spacing;
347
348 d_data->layout->invalidate();
349 itemChanged();
350 }
351
352}
353
354/*!
355 \return Spacing inside of each item
356 \sa setItemSpacing(), itemMargin(), margin(), spacing()
357*/
358int QwtPlotLegendItem::itemSpacing() const
359{
360 return d_data->itemSpacing;
361}
362
363/*!
364 Change the font used for drawing the text label
365
366 \param font Legend font
367 \sa font()
368*/
369void QwtPlotLegendItem::setFont( const QFont &font )
370{
371 if ( font != d_data->font )
372 {
373 d_data->font = font;
374
375 d_data->layout->invalidate();
376 itemChanged();
377 }
378}
379
380/*!
381 \return Font used for drawing the text label
382 \sa setFont()
383*/
384QFont QwtPlotLegendItem::font() const
385{
386 return d_data->font;
387}
388
389/*!
390 \brief Set the margin between the legend and the canvas border
391
392 The default setting for the margin is 10 pixels.
393
394 \param distance Margin in pixels
395 \sa setMargin()
396 */
397void QwtPlotLegendItem::setBorderDistance( int distance )
398{
399 if ( distance < 0 )
400 distance = -1;
401
402 if ( distance != d_data->borderDistance )
403 {
404 d_data->borderDistance = distance;
405 itemChanged();
406 }
407}
408
409/*!
410 \return Margin between the legend and the canvas border
411 \sa margin()
412 */
413int QwtPlotLegendItem::borderDistance() const
414{
415 return d_data->borderDistance;
416}
417
418/*!
419 Set the radius for the border
420
421 \param radius A value <= 0 defines a rectangular border
422 \sa borderRadius(), setBorderPen()
423 */
424void QwtPlotLegendItem::setBorderRadius( double radius )
425{
426 radius = qMax( 0.0, radius );
427
428 if ( radius != d_data->borderRadius )
429 {
430 d_data->borderRadius = radius;
431 itemChanged();
432 }
433}
434
435/*!
436 \return Radius of the border
437 \sa setBorderRadius(), setBorderPen()
438 */
439double QwtPlotLegendItem::borderRadius() const
440{
441 return d_data->borderRadius;
442}
443
444/*!
445 Set the pen for drawing the border
446
447 \param pen Border pen
448 \sa borderPen(), setBackgroundBrush()
449 */
450void QwtPlotLegendItem::setBorderPen( const QPen &pen )
451{
452 if ( d_data->borderPen != pen )
453 {
454 d_data->borderPen = pen;
455 itemChanged();
456 }
457}
458
459/*!
460 \return Pen for drawing the border
461 \sa setBorderPen(), backgroundBrush()
462 */
463QPen QwtPlotLegendItem::borderPen() const
464{
465 return d_data->borderPen;
466}
467
468/*!
469 \brief Set the background brush
470
471 The brush is used to fill the background
472
473 \param brush Brush
474 \sa backgroundBrush(), setBackgroundMode(), drawBackground()
475 */
476void QwtPlotLegendItem::setBackgroundBrush( const QBrush &brush )
477{
478 if ( d_data->backgroundBrush != brush )
479 {
480 d_data->backgroundBrush = brush;
481 itemChanged();
482 }
483}
484
485/*!
486 \return Brush is used to fill the background
487 \sa setBackgroundBrush(), backgroundMode(), drawBackground()
488 */
489QBrush QwtPlotLegendItem::backgroundBrush() const
490{
491 return d_data->backgroundBrush;
492}
493
494/*!
495 \brief Set the background mode
496
497 Depending on the mode the complete legend or each item
498 might have an background.
499
500 The default setting is LegendBackground.
501
502 \sa backgroundMode(), setBackgroundBrush(), drawBackground()
503 */
504void QwtPlotLegendItem::setBackgroundMode( BackgroundMode mode )
505{
506 if ( mode != d_data->backgroundMode )
507 {
508 d_data->backgroundMode = mode;
509 itemChanged();
510 }
511}
512
513/*!
514 \return backgroundMode
515 \sa setBackgroundMode(), backgroundBrush(), drawBackground()
516 */
517QwtPlotLegendItem::BackgroundMode QwtPlotLegendItem::backgroundMode() const
518{
519 return d_data->backgroundMode;
520}
521
522/*!
523 \brief Set the pen for drawing text labels
524
525 \param pen Text pen
526 \sa textPen(), setFont()
527 */
528void QwtPlotLegendItem::setTextPen( const QPen &pen )
529{
530 if ( d_data->textPen != pen )
531 {
532 d_data->textPen = pen;
533 itemChanged();
534 }
535}
536
537/*!
538 \return Pen for drawing text labels
539 \sa setTextPen(), font()
540 */
541QPen QwtPlotLegendItem::textPen() const
542{
543 return d_data->textPen;
544}
545
546/*!
547 Draw the legend
548
549 \param painter Painter
550 \param xMap x Scale Map
551 \param yMap y Scale Map
552 \param canvasRect Contents rectangle of the canvas in painter coordinates
553*/
554void QwtPlotLegendItem::draw( QPainter *painter,
555 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
556 const QRectF &canvasRect ) const
557{
558 Q_UNUSED( xMap );
559 Q_UNUSED( yMap );
560
561 d_data->layout->setGeometry( geometry( canvasRect ) );
562 if ( d_data->layout->geometry().isEmpty() )
563 {
564 // don't draw a legend when having no content
565 return;
566 }
567
568 if ( d_data->backgroundMode == QwtPlotLegendItem::LegendBackground )
569 drawBackground( painter, d_data->layout->geometry() );
570
571 for ( int i = 0; i < d_data->layout->count(); i++ )
572 {
573 const QwtLegendLayoutItem *layoutItem =
574 static_cast<QwtLegendLayoutItem *>( d_data->layout->itemAt( i ) );
575
576 if ( d_data->backgroundMode == QwtPlotLegendItem::ItemBackground )
577 drawBackground( painter, layoutItem->geometry() );
578
579 painter->save();
580
581 drawLegendData( painter, layoutItem->plotItem(),
582 layoutItem->data(), layoutItem->geometry() );
583
584 painter->restore();
585 }
586}
587
588/*!
589 Draw a rounded rect
590
591 \param painter Painter
592 \param rect Bounding rectangle
593
594 \sa setBorderRadius(), setBorderPen(),
595 setBackgroundBrush(), setBackgroundMode()
596 */
597void QwtPlotLegendItem::drawBackground(
598 QPainter *painter, const QRectF &rect ) const
599{
600 painter->save();
601
602 painter->setPen( d_data->borderPen );
603 painter->setBrush( d_data->backgroundBrush );
604
605 const double radius = d_data->borderRadius;
606 painter->drawRoundedRect( rect, radius, radius );
607
608 painter->restore();
609}
610
611/*!
612 Calculate the geometry of the legend on the canvas
613
614 \param canvasRect Geometry of the canvas
615 \return Geometry of the legend
616*/
617QRect QwtPlotLegendItem::geometry( const QRectF &canvasRect ) const
618{
619 QRect rect;
620 rect.setSize( d_data->layout->sizeHint() );
621
622 int margin = d_data->borderDistance;
623 if ( d_data->alignment & Qt::AlignHCenter )
624 {
625 int x = qRound( canvasRect.center().x() );
626 rect.moveCenter( QPoint( x, rect.center().y() ) );
627 }
628 else if ( d_data->alignment & Qt::AlignRight )
629 {
630 rect.moveRight( qFloor( canvasRect.right() - margin ) );
631 }
632 else
633 {
634 rect.moveLeft( qCeil( canvasRect.left() + margin ) );
635 }
636
637 if ( d_data->alignment & Qt::AlignVCenter )
638 {
639 int y = qRound( canvasRect.center().y() );
640 rect.moveCenter( QPoint( rect.center().x(), y ) );
641 }
642 else if ( d_data->alignment & Qt::AlignBottom )
643 {
644 rect.moveBottom( qFloor( canvasRect.bottom() - margin ) );
645 }
646 else
647 {
648 rect.moveTop( qCeil( canvasRect.top() + margin ) );
649 }
650
651 return rect;
652}
653
654/*!
655 Update the legend items according to modifications of a
656 plot item
657
658 \param plotItem Plot item
659 \param data Attributes of the legend entries
660 */
661void QwtPlotLegendItem::updateLegend( const QwtPlotItem *plotItem,
662 const QList<QwtLegendData> &data )
663{
664 if ( plotItem == NULL )
665 return;
666
667 QList<QwtLegendLayoutItem *> layoutItems;
668
669 QMap<const QwtPlotItem *, QList<QwtLegendLayoutItem *> >::iterator it =
670 d_data->map.find( plotItem );
671 if ( it != d_data->map.end() )
672 layoutItems = it.value();
673
674 bool changed = false;
675
676 if ( data.size() != layoutItems.size() )
677 {
678 changed = true;
679
680 for ( int i = 0; i < layoutItems.size(); i++ )
681 {
682 d_data->layout->removeItem( layoutItems[i] );
683 delete layoutItems[i];
684 }
685 layoutItems.clear();
686
687 if ( it != d_data->map.end() )
688 d_data->map.remove( plotItem );
689
690 if ( !data.isEmpty() )
691 {
692 for ( int i = 0; i < data.size(); i++ )
693 {
694 QwtLegendLayoutItem *layoutItem =
695 new QwtLegendLayoutItem( this, plotItem );
696 d_data->layout->addItem( layoutItem );
697 layoutItems += layoutItem;
698 }
699
700 d_data->map.insert( plotItem, layoutItems );
701 }
702 }
703
704 for ( int i = 0; i < data.size(); i++ )
705 {
706 if ( layoutItems[i]->data().values() != data[i].values() )
707 {
708 layoutItems[i]->setData( data[i] );
709 changed = true;
710 }
711 }
712
713 if ( changed )
714 {
715 d_data->layout->invalidate();
716 itemChanged();
717 }
718}
719
720//! Remove all items from the legend
721void QwtPlotLegendItem::clearLegend()
722{
723 if ( !d_data->map.isEmpty() )
724 {
725 d_data->map.clear();
726
727 for ( int i = d_data->layout->count() - 1; i >= 0; i-- )
728 delete d_data->layout->takeAt( i );
729
730 itemChanged();
731 }
732}
733
734/*!
735 Draw an entry on the legend
736
737 \param painter Qt Painter
738 \param plotItem Plot item, represented by the entry
739 \param data Attributes of the legend entry
740 \param rect Bounding rectangle for the entry
741 */
742void QwtPlotLegendItem::drawLegendData( QPainter *painter,
743 const QwtPlotItem *plotItem, const QwtLegendData &data,
744 const QRectF &rect ) const
745{
746 Q_UNUSED( plotItem );
747
748 const int m = d_data->itemMargin;
749 const QRectF r = rect.toRect().adjusted( m, m, -m, -m );
750
751 painter->setClipRect( r, Qt::IntersectClip );
752
753 int titleOff = 0;
754
755 const QwtGraphic graphic = data.icon();
756 if ( !graphic.isEmpty() )
757 {
758 QRectF iconRect( r.topLeft(), graphic.defaultSize() );
759
760 iconRect.moveCenter(
761 QPoint( iconRect.center().x(), rect.center().y() ) );
762
763 graphic.render( painter, iconRect, Qt::KeepAspectRatio );
764
765 titleOff += iconRect.width() + d_data->itemSpacing;
766 }
767
768 const QwtText text = data.title();
769 if ( !text.isEmpty() )
770 {
771 painter->setPen( textPen() );
772 painter->setFont( font() );
773
774 const QRectF textRect = r.adjusted( titleOff, 0, 0, 0 );
775 text.draw( painter, textRect );
776 }
777}
778
779/*!
780 Minimum size hint needed to display an entry
781
782 \param data Attributes of the legend entry
783 \return Minimum size
784 */
785QSize QwtPlotLegendItem::minimumSize( const QwtLegendData &data ) const
786{
787 QSize size( 2 * d_data->itemMargin, 2 * d_data->itemMargin );
788
789 if ( !data.isValid() )
790 return size;
791
792 const QwtGraphic graphic = data.icon();
793 const QwtText text = data.title();
794
795 int w = 0;
796 int h = 0;
797
798 if ( !graphic.isNull() )
799 {
800 w = graphic.width();
801 h = graphic.height();
802 }
803
804 if ( !text.isEmpty() )
805 {
806 const QSizeF sz = text.textSize( font() );
807
808 w += qCeil( sz.width() );
809 h = qMax( h, qCeil( sz.height() ) );
810 }
811
812 if ( graphic.width() > 0 && !text.isEmpty() )
813 w += d_data->itemSpacing;
814
815 size += QSize( w, h );
816 return size;
817}
818
819/*!
820 \return The preferred height, for a width.
821 \param data Attributes of the legend entry
822 \param width Width
823*/
824int QwtPlotLegendItem::heightForWidth(
825 const QwtLegendData &data, int width ) const
826{
827 width -= 2 * d_data->itemMargin;
828
829 const QwtGraphic graphic = data.icon();
830 const QwtText text = data.title();
831
832 if ( text.isEmpty() )
833 return graphic.height();
834
835 if ( graphic.width() > 0 )
836 width -= graphic.width() + d_data->itemSpacing;
837
838 int h = text.heightForWidth( width, font() );
839 h += 2 * d_data->itemMargin;
840
841 return qMax( graphic.height(), h );
842}
843
844/*!
845 \return All plot items with an entry on the legend
846 \note A plot item might have more than one entry on the legend
847 */
848QList< const QwtPlotItem * > QwtPlotLegendItem::plotItems() const
849{
850 return d_data->map.keys();
851}
852
853/*!
854 \return Geometries of the items of a plot item
855 \note Usually a plot item has only one entry on the legend
856*/
857QList< QRect > QwtPlotLegendItem::legendGeometries(
858 const QwtPlotItem *plotItem ) const
859{
860 QList<QwtLegendLayoutItem *> layoutItems;
861
862 QMap<const QwtPlotItem *, QList<QwtLegendLayoutItem *> >::iterator it =
863 d_data->map.find( plotItem );
864 if ( it != d_data->map.end() )
865 layoutItems = it.value();
866
867 QList<QRect> geometries;
868 for ( int i = 0; i < layoutItems.size(); i++ )
869 geometries += layoutItems[i]->geometry();
870
871 return geometries;
872}
Note: See TracBrowser for help on using the repository browser.