qwt_legend.cpp
Go to the documentation of this file.
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_legend.h"
11 #include "qwt_legend_label.h"
12 #include "qwt_dyngrid_layout.h"
13 #include "qwt_math.h"
14 #include "qwt_plot_item.h"
15 #include "qwt_painter.h"
16 #include <qapplication.h>
17 #include <qscrollbar.h>
18 #include <qscrollarea.h>
19 #include <qpainter.h>
20 #include <qstyle.h>
21 #include <qstyleoption.h>
22 
24 {
25 public:
26  inline bool isEmpty() const { return d_entries.isEmpty(); }
27 
28  void insert( const QVariant &, const QList<QWidget *> & );
29  void remove( const QVariant & );
30 
31  void removeWidget( const QWidget * );
32 
33  QList<QWidget *> legendWidgets( const QVariant & ) const;
34  QVariant itemInfo( const QWidget * ) const;
35 
36 private:
37  // we don't know anything about itemInfo and therefore don't have
38  // any key that can be used for a map or hashtab.
39  // But a simple linear list is o.k. here, as we will never have
40  // more than a few entries.
41 
42  class Entry
43  {
44  public:
45  QVariant itemInfo;
46  QList<QWidget *> widgets;
47  };
48 
49  QList< Entry > d_entries;
50 };
51 
52 void QwtLegendMap::insert( const QVariant &itemInfo,
53  const QList<QWidget *> &widgets )
54 {
55  for ( int i = 0; i < d_entries.size(); i++ )
56  {
57  Entry &entry = d_entries[i];
58  if ( entry.itemInfo == itemInfo )
59  {
60  entry.widgets = widgets;
61  return;
62  }
63  }
64 
65  Entry newEntry;
66  newEntry.itemInfo = itemInfo;
67  newEntry.widgets = widgets;
68 
69  d_entries += newEntry;
70 }
71 
72 void QwtLegendMap::remove( const QVariant &itemInfo )
73 {
74  for ( int i = 0; i < d_entries.size(); i++ )
75  {
76  Entry &entry = d_entries[i];
77  if ( entry.itemInfo == itemInfo )
78  {
79  d_entries.removeAt( i );
80  return;
81  }
82  }
83 }
84 
85 void QwtLegendMap::removeWidget( const QWidget *widget )
86 {
87  QWidget *w = const_cast<QWidget *>( widget );
88 
89  for ( int i = 0; i < d_entries.size(); i++ )
90  d_entries[ i ].widgets.removeAll( w );
91 }
92 
93 QVariant QwtLegendMap::itemInfo( const QWidget *widget ) const
94 {
95  if ( widget != NULL )
96  {
97  QWidget *w = const_cast<QWidget *>( widget );
98 
99  for ( int i = 0; i < d_entries.size(); i++ )
100  {
101  const Entry &entry = d_entries[i];
102  if ( entry.widgets.indexOf( w ) >= 0 )
103  return entry.itemInfo;
104  }
105  }
106 
107  return QVariant();
108 }
109 
110 QList<QWidget *> QwtLegendMap::legendWidgets( const QVariant &itemInfo ) const
111 {
112  if ( itemInfo.isValid() )
113  {
114  for ( int i = 0; i < d_entries.size(); i++ )
115  {
116  const Entry &entry = d_entries[i];
117  if ( entry.itemInfo == itemInfo )
118  return entry.widgets;
119  }
120  }
121 
122  return QList<QWidget *>();
123 }
124 
126 {
127 public:
129  itemMode( QwtLegendData::ReadOnly ),
130  view( NULL )
131  {
132  }
133 
136 
137  class LegendView;
138  LegendView *view;
139 };
140 
141 class QwtLegend::PrivateData::LegendView: public QScrollArea
142 {
143 public:
144  explicit LegendView( QWidget *parent ):
145  QScrollArea( parent )
146  {
147  contentsWidget = new QWidget( this );
148  contentsWidget->setObjectName( "QwtLegendViewContents" );
149 
150  setWidget( contentsWidget );
151  setWidgetResizable( false );
152 
153  viewport()->setObjectName( "QwtLegendViewport" );
154 
155  // QScrollArea::setWidget internally sets autoFillBackground to true
156  // But we don't want a background.
157  contentsWidget->setAutoFillBackground( false );
158  viewport()->setAutoFillBackground( false );
159  }
160 
161  virtual bool event( QEvent *event )
162  {
163  if ( event->type() == QEvent::PolishRequest )
164  {
165  setFocusPolicy( Qt::NoFocus );
166  }
167 
168  if ( event->type() == QEvent::Resize )
169  {
170  // adjust the size to en/disable the scrollbars
171  // before QScrollArea adjusts the viewport size
172 
173  const QRect cr = contentsRect();
174 
175  int w = cr.width();
176  int h = contentsWidget->heightForWidth( cr.width() );
177  if ( h > w )
178  {
179  w -= verticalScrollBar()->sizeHint().width();
180  h = contentsWidget->heightForWidth( w );
181  }
182 
183  contentsWidget->resize( w, h );
184  }
185 
186  return QScrollArea::event( event );
187  }
188 
189  virtual bool viewportEvent( QEvent *event )
190  {
191  bool ok = QScrollArea::viewportEvent( event );
192 
193  if ( event->type() == QEvent::Resize )
194  {
195  layoutContents();
196  }
197  return ok;
198  }
199 
200  QSize viewportSize( int w, int h ) const
201  {
202  const int sbHeight = horizontalScrollBar()->sizeHint().height();
203  const int sbWidth = verticalScrollBar()->sizeHint().width();
204 
205  const int cw = contentsRect().width();
206  const int ch = contentsRect().height();
207 
208  int vw = cw;
209  int vh = ch;
210 
211  if ( w > vw )
212  vh -= sbHeight;
213 
214  if ( h > vh )
215  {
216  vw -= sbWidth;
217  if ( w > vw && vh == ch )
218  vh -= sbHeight;
219  }
220  return QSize( vw, vh );
221  }
222 
224  {
225  const QwtDynGridLayout *tl = qobject_cast<QwtDynGridLayout *>(
226  contentsWidget->layout() );
227  if ( tl == NULL )
228  return;
229 
230  const QSize visibleSize = viewport()->contentsRect().size();
231 
232  const int minW = int( tl->maxItemWidth() ) + 2 * tl->margin();
233 
234  int w = qMax( visibleSize.width(), minW );
235  int h = qMax( tl->heightForWidth( w ), visibleSize.height() );
236 
237  const int vpWidth = viewportSize( w, h ).width();
238  if ( w > vpWidth )
239  {
240  w = qMax( vpWidth, minW );
241  h = qMax( tl->heightForWidth( w ), visibleSize.height() );
242  }
243 
244  contentsWidget->resize( w, h );
245  }
246 
247  QWidget *contentsWidget;
248 };
249 
254 QwtLegend::QwtLegend( QWidget *parent ):
255  QwtAbstractLegend( parent )
256 {
257  setFrameStyle( NoFrame );
258 
260 
262  d_data->view->setObjectName( "QwtLegendView" );
263  d_data->view->setFrameStyle( NoFrame );
264 
265  QwtDynGridLayout *gridLayout = new QwtDynGridLayout(
267  gridLayout->setAlignment( Qt::AlignHCenter | Qt::AlignTop );
268 
269  d_data->view->contentsWidget->installEventFilter( this );
270 
271  QVBoxLayout *layout = new QVBoxLayout( this );
272  layout->setContentsMargins( 0, 0, 0, 0 );
273  layout->addWidget( d_data->view );
274 }
275 
278 {
279  delete d_data;
280 }
281 
292 void QwtLegend::setMaxColumns( uint numColums )
293 {
294  QwtDynGridLayout *tl = qobject_cast<QwtDynGridLayout *>(
295  d_data->view->contentsWidget->layout() );
296  if ( tl )
297  tl->setMaxColumns( numColums );
298 }
299 
305 {
306  uint maxCols = 0;
307 
308  const QwtDynGridLayout *tl = qobject_cast<const QwtDynGridLayout *>(
309  d_data->view->contentsWidget->layout() );
310  if ( tl )
311  maxCols = tl->maxColumns();
312 
313  return maxCols;
314 }
315 
330 {
331  d_data->itemMode = mode;
332 }
333 
339 {
340  return d_data->itemMode;
341 }
342 
350 {
351  return d_data->view->contentsWidget;
352 }
353 
359 {
360  return d_data->view->horizontalScrollBar();
361 }
362 
367 QScrollBar *QwtLegend::verticalScrollBar() const
368 {
369  return d_data->view->verticalScrollBar();
370 }
371 
379 const QWidget *QwtLegend::contentsWidget() const
380 {
381  return d_data->view->contentsWidget;
382 }
383 
390 void QwtLegend::updateLegend( const QVariant &itemInfo,
391  const QList<QwtLegendData> &data )
392 {
393  QList<QWidget *> widgetList = legendWidgets( itemInfo );
394 
395  if ( widgetList.size() != data.size() )
396  {
397  QLayout *contentsLayout = d_data->view->contentsWidget->layout();
398 
399  while ( widgetList.size() > data.size() )
400  {
401  QWidget *w = widgetList.takeLast();
402 
403  contentsLayout->removeWidget( w );
404 
405  // updates might be triggered by signals from the legend widget
406  // itself. So we better don't delete it here.
407 
408  w->hide();
409  w->deleteLater();
410  }
411 
412 #if QT_VERSION >= 0x040700
413  widgetList.reserve( data.size() );
414 #endif
415 
416  for ( int i = widgetList.size(); i < data.size(); i++ )
417  {
418  QWidget *widget = createWidget( data[i] );
419 
420  if ( contentsLayout )
421  contentsLayout->addWidget( widget );
422 
423  if ( isVisible() )
424  {
425  // QLayout does a delayed show, with the effect, that
426  // the size hint will be wrong, when applications
427  // call replot() right after changing the list
428  // of plot items. So we better do the show now.
429 
430  widget->setVisible( true );
431  }
432 
433  widgetList += widget;
434  }
435 
436  if ( widgetList.isEmpty() )
437  {
438  d_data->itemMap.remove( itemInfo );
439  }
440  else
441  {
442  d_data->itemMap.insert( itemInfo, widgetList );
443  }
444 
445  updateTabOrder();
446  }
447 
448  for ( int i = 0; i < data.size(); i++ )
449  updateWidget( widgetList[i], data[i] );
450 }
451 
463 QWidget *QwtLegend::createWidget( const QwtLegendData &data ) const
464 {
465  Q_UNUSED( data );
466 
467  QwtLegendLabel *label = new QwtLegendLabel();
468  label->setItemMode( defaultItemMode() );
469 
470  connect( label, SIGNAL( clicked() ), SLOT( itemClicked() ) );
471  connect( label, SIGNAL( checked( bool ) ), SLOT( itemChecked( bool ) ) );
472 
473  return label;
474 }
475 
485 void QwtLegend::updateWidget( QWidget *widget, const QwtLegendData &data )
486 {
487  QwtLegendLabel *label = qobject_cast<QwtLegendLabel *>( widget );
488  if ( label )
489  {
490  label->setData( data );
491  if ( !data.value( QwtLegendData::ModeRole ).isValid() )
492  {
493  // use the default mode, when there is no specific
494  // hint from the legend data
495 
496  label->setItemMode( defaultItemMode() );
497  }
498  }
499 }
500 
502 {
503  QLayout *contentsLayout = d_data->view->contentsWidget->layout();
504  if ( contentsLayout )
505  {
506  // set tab focus chain
507 
508  QWidget *w = NULL;
509 
510  for ( int i = 0; i < contentsLayout->count(); i++ )
511  {
512  QLayoutItem *item = contentsLayout->itemAt( i );
513  if ( w && item->widget() )
514  QWidget::setTabOrder( w, item->widget() );
515 
516  w = item->widget();
517  }
518  }
519 }
520 
522 QSize QwtLegend::sizeHint() const
523 {
524  QSize hint = d_data->view->contentsWidget->sizeHint();
525  hint += QSize( 2 * frameWidth(), 2 * frameWidth() );
526 
527  return hint;
528 }
529 
534 int QwtLegend::heightForWidth( int width ) const
535 {
536  width -= 2 * frameWidth();
537 
538  int h = d_data->view->contentsWidget->heightForWidth( width );
539  if ( h >= 0 )
540  h += 2 * frameWidth();
541 
542  return h;
543 }
544 
545 
555 bool QwtLegend::eventFilter( QObject *object, QEvent *event )
556 {
557  if ( object == d_data->view->contentsWidget )
558  {
559  switch ( event->type() )
560  {
561  case QEvent::ChildRemoved:
562  {
563  const QChildEvent *ce =
564  static_cast<const QChildEvent *>(event);
565  if ( ce->child()->isWidgetType() )
566  {
567  QWidget *w = static_cast< QWidget * >( ce->child() );
569  }
570  break;
571  }
572  case QEvent::LayoutRequest:
573  {
575 
576  if ( parentWidget() && parentWidget()->layout() == NULL )
577  {
578  /*
579  We want the parent widget ( usually QwtPlot ) to recalculate
580  its layout, when the contentsWidget has changed. But
581  because of the scroll view we have to forward the LayoutRequest
582  event manually.
583 
584  We don't use updateGeometry() because it doesn't post LayoutRequest
585  events when the legend is hidden. But we want the
586  parent widget notified, so it can show/hide the legend
587  depending on its items.
588  */
589  QApplication::postEvent( parentWidget(),
590  new QEvent( QEvent::LayoutRequest ) );
591  }
592  break;
593  }
594  default:
595  break;
596  }
597  }
598 
599  return QwtAbstractLegend::eventFilter( object, event );
600 }
601 
607 {
608  QWidget *w = qobject_cast<QWidget *>( sender() );
609  if ( w )
610  {
611  const QVariant itemInfo = d_data->itemMap.itemInfo( w );
612  if ( itemInfo.isValid() )
613  {
614  const QList<QWidget *> widgetList =
615  d_data->itemMap.legendWidgets( itemInfo );
616 
617  const int index = widgetList.indexOf( w );
618  if ( index >= 0 )
619  Q_EMIT clicked( itemInfo, index );
620  }
621  }
622 }
623 
628 void QwtLegend::itemChecked( bool on )
629 {
630  QWidget *w = qobject_cast<QWidget *>( sender() );
631  if ( w )
632  {
633  const QVariant itemInfo = d_data->itemMap.itemInfo( w );
634  if ( itemInfo.isValid() )
635  {
636  const QList<QWidget *> widgetList =
637  d_data->itemMap.legendWidgets( itemInfo );
638 
639  const int index = widgetList.indexOf( w );
640  if ( index >= 0 )
641  Q_EMIT checked( itemInfo, on, index );
642  }
643  }
644 }
645 
655 void QwtLegend::renderLegend( QPainter *painter,
656  const QRectF &rect, bool fillBackground ) const
657 {
658  if ( d_data->itemMap.isEmpty() )
659  return;
660 
661  if ( fillBackground )
662  {
663  if ( autoFillBackground() ||
664  testAttribute( Qt::WA_StyledBackground ) )
665  {
666  QwtPainter::drawBackgound( painter, rect, this );
667  }
668  }
669 
670  const QwtDynGridLayout *legendLayout =
671  qobject_cast<QwtDynGridLayout *>( contentsWidget()->layout() );
672  if ( legendLayout == NULL )
673  return;
674 
675  int left, right, top, bottom;
676  getContentsMargins( &left, &top, &right, &bottom );
677 
678  QRect layoutRect;
679  layoutRect.setLeft( qCeil( rect.left() ) + left );
680  layoutRect.setTop( qCeil( rect.top() ) + top );
681  layoutRect.setRight( qFloor( rect.right() ) - right );
682  layoutRect.setBottom( qFloor( rect.bottom() ) - bottom );
683 
684  uint numCols = legendLayout->columnsForWidth( layoutRect.width() );
685  const QList<QRect> itemRects =
686  legendLayout->layoutItems( layoutRect, numCols );
687 
688  int index = 0;
689 
690  for ( int i = 0; i < legendLayout->count(); i++ )
691  {
692  QLayoutItem *item = legendLayout->itemAt( i );
693  QWidget *w = item->widget();
694  if ( w )
695  {
696  painter->save();
697 
698  painter->setClipRect( itemRects[index], Qt::IntersectClip );
699  renderItem( painter, w, itemRects[index], fillBackground );
700 
701  index++;
702  painter->restore();
703  }
704  }
705 }
706 
718 void QwtLegend::renderItem( QPainter *painter,
719  const QWidget *widget, const QRectF &rect, bool fillBackground ) const
720 {
721  if ( fillBackground )
722  {
723  if ( widget->autoFillBackground() ||
724  widget->testAttribute( Qt::WA_StyledBackground ) )
725  {
726  QwtPainter::drawBackgound( painter, rect, widget );
727  }
728  }
729 
730  const QwtLegendLabel *label = qobject_cast<const QwtLegendLabel *>( widget );
731  if ( label )
732  {
733  // icon
734 
735  const QwtGraphic &icon = label->data().icon();
736  const QSizeF sz = icon.defaultSize();
737 
738  const QRectF iconRect( rect.x() + label->margin(),
739  rect.center().y() - 0.5 * sz.height(),
740  sz.width(), sz.height() );
741 
742  icon.render( painter, iconRect, Qt::KeepAspectRatio );
743 
744  // title
745 
746  QRectF titleRect = rect;
747  titleRect.setX( iconRect.right() + 2 * label->spacing() );
748 
749  painter->setFont( label->font() );
750  painter->setPen( label->palette().color( QPalette::Text ) );
751  const_cast< QwtLegendLabel *>( label )->drawText( painter, titleRect );
752  }
753 }
754 
760 QList<QWidget *> QwtLegend::legendWidgets( const QVariant &itemInfo ) const
761 {
762  return d_data->itemMap.legendWidgets( itemInfo );
763 }
764 
771 QWidget *QwtLegend::legendWidget( const QVariant &itemInfo ) const
772 {
773  const QList<QWidget *> list = d_data->itemMap.legendWidgets( itemInfo );
774  if ( list.isEmpty() )
775  return NULL;
776 
777  return list[0];
778 }
779 
787 QVariant QwtLegend::itemInfo( const QWidget *widget ) const
788 {
789  return d_data->itemMap.itemInfo( widget );
790 }
791 
793 bool QwtLegend::isEmpty() const
794 {
795  return d_data->itemMap.isEmpty();
796 }
797 
804 int QwtLegend::scrollExtent( Qt::Orientation orientation ) const
805 {
806  int extent = 0;
807 
808  if ( orientation == Qt::Horizontal )
809  extent = verticalScrollBar()->sizeHint().width();
810  else
811  extent = horizontalScrollBar()->sizeHint().height();
812 
813  return extent;
814 }
815 
A widget representing something on a QwtLegend.
virtual void updateWidget(QWidget *widget, const QwtLegendData &data)
Update the widget.
Definition: qwt_legend.cpp:485
virtual int heightForWidth(int) const
virtual int maxItemWidth() const
void render(QPainter *) const
Replay all recorded painter commands.
virtual bool isEmpty() const
Definition: qwt_legend.cpp:793
void removeWidget(const QWidget *)
Definition: qwt_legend.cpp:85
QVariant itemInfo(const QWidget *) const
Definition: qwt_legend.cpp:93
virtual uint columnsForWidth(int width) const
Calculate the number of columns for a given width.
QScrollBar * horizontalScrollBar() const
Definition: qwt_legend.cpp:358
virtual bool eventFilter(QObject *, QEvent *)
Definition: qwt_legend.cpp:555
virtual bool viewportEvent(QEvent *event)
Definition: qwt_legend.cpp:189
const QwtLegendData & data() const
QSizeF defaultSize() const
Default size.
static void drawBackgound(QPainter *painter, const QRectF &rect, const QWidget *widget)
void remove(const QVariant &)
Definition: qwt_legend.cpp:72
void updateTabOrder()
Definition: qwt_legend.cpp:501
QwtLegendData::Mode defaultItemMode() const
Definition: qwt_legend.cpp:338
void setDefaultItemMode(QwtLegendData::Mode)
Set the default mode for legend labels.
Definition: qwt_legend.cpp:329
void setMaxColumns(uint maxCols)
QSize viewportSize(int w, int h) const
Definition: qwt_legend.cpp:200
virtual QSize sizeHint() const
Return a size hint.
Definition: qwt_legend.cpp:522
virtual int count() const
QVariant itemInfo(const QWidget *) const
Definition: qwt_legend.cpp:787
virtual QLayoutItem * itemAt(int index) const
The QwtDynGridLayout class lays out widgets in a grid, adjusting the number of columns and rows to th...
uint maxColumns() const
Definition: qwt_legend.cpp:304
QScrollBar * verticalScrollBar() const
Definition: qwt_legend.cpp:367
void insert(const QVariant &, const QList< QWidget * > &)
Definition: qwt_legend.cpp:52
QList< Entry > d_entries
Definition: qwt_legend.cpp:49
virtual int scrollExtent(Qt::Orientation) const
Definition: qwt_legend.cpp:804
virtual void updateLegend(const QVariant &, const QList< QwtLegendData > &)
Update the entries for an item.
Definition: qwt_legend.cpp:390
QVariant value(int role) const
QwtGraphic icon() const
A paint device for scalable graphics.
Definition: qwt_graphic.h:74
QwtLegend(QWidget *parent=NULL)
Definition: qwt_legend.cpp:254
QwtLegendData::Mode itemMode
Definition: qwt_legend.cpp:134
QList< QWidget * > legendWidgets(const QVariant &) const
Definition: qwt_legend.cpp:110
PrivateData * d_data
Definition: qwt_legend.h:113
QList< QWidget * > widgets
Definition: qwt_legend.cpp:46
int spacing() const
QList< QWidget * > legendWidgets(const QVariant &) const
Definition: qwt_legend.cpp:760
TFSIMD_FORCE_INLINE const tfScalar & w() const
QWidget * legendWidget(const QVariant &) const
Definition: qwt_legend.cpp:771
Abstract base class for legend widgets.
QWidget * contentsWidget()
Definition: qwt_legend.cpp:349
int margin
Return label&#39;s text margin in pixels.
virtual QWidget * createWidget(const QwtLegendData &) const
Create a widget to be inserted into the legend.
Definition: qwt_legend.cpp:463
virtual ~QwtLegend()
Destructor.
Definition: qwt_legend.cpp:277
virtual void renderLegend(QPainter *, const QRectF &, bool fillBackground) const
Definition: qwt_legend.cpp:655
void setMaxColumns(uint numColums)
Set the maximum number of entries in a row.
Definition: qwt_legend.cpp:292
void setData(const QwtLegendData &)
virtual bool event(QEvent *event)
Definition: qwt_legend.cpp:161
void checked(const QVariant &itemInfo, bool on, int index)
QList< QRect > layoutItems(const QRect &, uint numCols) const
int i
void clicked(const QVariant &itemInfo, int index)
void itemChecked(bool)
Definition: qwt_legend.cpp:628
Mode
Mode defining how a legend entry interacts.
uint maxColumns() const
Return the upper limit for the number of columns.
virtual int heightForWidth(int w) const
Definition: qwt_legend.cpp:534
Attributes of an entry on a legend.
virtual void renderItem(QPainter *, const QWidget *, const QRectF &, bool fillBackground) const
Definition: qwt_legend.cpp:718
void itemClicked()
Definition: qwt_legend.cpp:606
void setItemMode(QwtLegendData::Mode)
bool isEmpty() const
Definition: qwt_legend.cpp:26


plotjuggler
Author(s): Davide Faconti
autogenerated on Sat Jul 6 2019 03:44:17