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_painter.h"
15 #include "qwt_graphic.h"
16 
17 #include <qapplication.h>
18 #include <qscrollbar.h>
19 #include <qscrollarea.h>
20 #include <qpainter.h>
21 #include <qmargins.h>
22 
23 namespace
24 {
25  class QwtLegendMap
26  {
27  public:
28  inline bool isEmpty() const { return d_entries.isEmpty(); }
29 
30  void insert( const QVariant &, const QList<QWidget *> & );
31  void remove( const QVariant & );
32 
33  void removeWidget( const QWidget * );
34 
35  QList<QWidget *> legendWidgets( const QVariant & ) const;
36  QVariant itemInfo( const QWidget * ) const;
37 
38  private:
39  // we don't know anything about itemInfo and therefore don't have
40  // any key that can be used for a map or hashtab.
41  // But a simple linear list is o.k. here, as we will never have
42  // more than a few entries.
43 
44  class Entry
45  {
46  public:
47  QVariant itemInfo;
48  QList<QWidget *> widgets;
49  };
50 
51  QList< Entry > d_entries;
52  };
53 }
54 
55 void QwtLegendMap::insert( const QVariant &itemInfo,
56  const QList<QWidget *> &widgets )
57 {
58  for ( int i = 0; i < d_entries.size(); i++ )
59  {
60  Entry &entry = d_entries[i];
61  if ( entry.itemInfo == itemInfo )
62  {
63  entry.widgets = widgets;
64  return;
65  }
66  }
67 
68  Entry newEntry;
69  newEntry.itemInfo = itemInfo;
70  newEntry.widgets = widgets;
71 
72  d_entries += newEntry;
73 }
74 
75 void QwtLegendMap::remove( const QVariant &itemInfo )
76 {
77  for ( int i = 0; i < d_entries.size(); i++ )
78  {
79  Entry &entry = d_entries[i];
80  if ( entry.itemInfo == itemInfo )
81  {
82  d_entries.removeAt( i );
83  return;
84  }
85  }
86 }
87 
88 void QwtLegendMap::removeWidget( const QWidget *widget )
89 {
90  QWidget *w = const_cast<QWidget *>( widget );
91 
92  for ( int i = 0; i < d_entries.size(); i++ )
93  d_entries[ i ].widgets.removeAll( w );
94 }
95 
96 QVariant QwtLegendMap::itemInfo( const QWidget *widget ) const
97 {
98  if ( widget != NULL )
99  {
100  QWidget *w = const_cast<QWidget *>( widget );
101 
102  for ( int i = 0; i < d_entries.size(); i++ )
103  {
104  const Entry &entry = d_entries[i];
105  if ( entry.widgets.indexOf( w ) >= 0 )
106  return entry.itemInfo;
107  }
108  }
109 
110  return QVariant();
111 }
112 
113 QList<QWidget *> QwtLegendMap::legendWidgets( const QVariant &itemInfo ) const
114 {
115  if ( itemInfo.isValid() )
116  {
117  for ( int i = 0; i < d_entries.size(); i++ )
118  {
119  const Entry &entry = d_entries[i];
120  if ( entry.itemInfo == itemInfo )
121  return entry.widgets;
122  }
123  }
124 
125  return QList<QWidget *>();
126 }
127 
129 {
130 public:
132  itemMode( QwtLegendData::ReadOnly ),
133  view( NULL )
134  {
135  }
136 
138  QwtLegendMap itemMap;
139 
140  class LegendView;
141  LegendView *view;
142 };
143 
144 class QwtLegend::PrivateData::LegendView QWT_FINAL: public QScrollArea
145 {
146 public:
147  explicit LegendView( QWidget *parent ):
148  QScrollArea( parent )
149  {
150  contentsWidget = new QWidget( this );
151  contentsWidget->setObjectName( "QwtLegendViewContents" );
152 
153  setWidget( contentsWidget );
154  setWidgetResizable( false );
155 
156  viewport()->setObjectName( "QwtLegendViewport" );
157 
158  // QScrollArea::setWidget internally sets autoFillBackground to true
159  // But we don't want a background.
160  contentsWidget->setAutoFillBackground( false );
161  viewport()->setAutoFillBackground( false );
162  }
163 
164  virtual bool event( QEvent *event ) QWT_OVERRIDE
165  {
166  if ( event->type() == QEvent::PolishRequest )
167  {
168  setFocusPolicy( Qt::NoFocus );
169  }
170 
171  if ( event->type() == QEvent::Resize )
172  {
173  // adjust the size to en/disable the scrollbars
174  // before QScrollArea adjusts the viewport size
175 
176  const QRect cr = contentsRect();
177 
178  int w = cr.width();
179  int h = contentsWidget->heightForWidth( cr.width() );
180  if ( h > w )
181  {
182  w -= verticalScrollBar()->sizeHint().width();
183  h = contentsWidget->heightForWidth( w );
184  }
185 
186  contentsWidget->resize( w, h );
187  }
188 
189  return QScrollArea::event( event );
190  }
191 
192  virtual bool viewportEvent( QEvent *event ) QWT_OVERRIDE
193  {
194  bool ok = QScrollArea::viewportEvent( event );
195 
196  if ( event->type() == QEvent::Resize )
197  {
198  layoutContents();
199  }
200  return ok;
201  }
202 
203  QSize viewportSize( int w, int h ) const
204  {
205  const int sbHeight = horizontalScrollBar()->sizeHint().height();
206  const int sbWidth = verticalScrollBar()->sizeHint().width();
207 
208  const int cw = contentsRect().width();
209  const int ch = contentsRect().height();
210 
211  int vw = cw;
212  int vh = ch;
213 
214  if ( w > vw )
215  vh -= sbHeight;
216 
217  if ( h > vh )
218  {
219  vw -= sbWidth;
220  if ( w > vw && vh == ch )
221  vh -= sbHeight;
222  }
223  return QSize( vw, vh );
224  }
225 
227  {
228  const QwtDynGridLayout *tl = qobject_cast<QwtDynGridLayout *>(
229  contentsWidget->layout() );
230  if ( tl == NULL )
231  return;
232 
233  const QSize visibleSize = viewport()->contentsRect().size();
234 
235  const QMargins m = tl->contentsMargins();
236  const int minW = tl->maxItemWidth() + m.left() + m.right();
237 
238  int w = qMax( visibleSize.width(), minW );
239  int h = qMax( tl->heightForWidth( w ), visibleSize.height() );
240 
241  const int vpWidth = viewportSize( w, h ).width();
242  if ( w > vpWidth )
243  {
244  w = qMax( vpWidth, minW );
245  h = qMax( tl->heightForWidth( w ), visibleSize.height() );
246  }
247 
248  contentsWidget->resize( w, h );
249  }
250 
251  QWidget *contentsWidget;
252 };
253 
258 QwtLegend::QwtLegend( QWidget *parent ):
259  QwtAbstractLegend( parent )
260 {
261  setFrameStyle( NoFrame );
262 
264 
265  d_data->view = new QwtLegend::PrivateData::LegendView( this );
266  d_data->view->setObjectName( "QwtLegendView" );
267  d_data->view->setFrameStyle( NoFrame );
268 
269  QwtDynGridLayout *gridLayout = new QwtDynGridLayout(
270  d_data->view->contentsWidget );
271  gridLayout->setAlignment( Qt::AlignHCenter | Qt::AlignTop );
272 
273  d_data->view->contentsWidget->installEventFilter( this );
274 
275  QVBoxLayout *layout = new QVBoxLayout( this );
276  layout->setContentsMargins( 0, 0, 0, 0 );
277  layout->addWidget( d_data->view );
278 }
279 
282 {
283  delete d_data;
284 }
285 
296 void QwtLegend::setMaxColumns( uint numColums )
297 {
298  QwtDynGridLayout *tl = qobject_cast<QwtDynGridLayout *>(
299  d_data->view->contentsWidget->layout() );
300  if ( tl )
301  tl->setMaxColumns( numColums );
302 
303  updateGeometry();
304 }
305 
311 {
312  uint maxCols = 0;
313 
314  const QwtDynGridLayout *tl = qobject_cast<const QwtDynGridLayout *>(
315  d_data->view->contentsWidget->layout() );
316  if ( tl )
317  maxCols = tl->maxColumns();
318 
319  return maxCols;
320 }
321 
336 {
337  d_data->itemMode = mode;
338 }
339 
345 {
346  return d_data->itemMode;
347 }
348 
356 {
357  return d_data->view->contentsWidget;
358 }
359 
365 {
366  return d_data->view->horizontalScrollBar();
367 }
368 
373 QScrollBar *QwtLegend::verticalScrollBar() const
374 {
375  return d_data->view->verticalScrollBar();
376 }
377 
385 const QWidget *QwtLegend::contentsWidget() const
386 {
387  return d_data->view->contentsWidget;
388 }
389 
396 void QwtLegend::updateLegend( const QVariant &itemInfo,
397  const QList<QwtLegendData> &legendData )
398 {
399  QList<QWidget *> widgetList = legendWidgets( itemInfo );
400 
401  if ( widgetList.size() != legendData.size() )
402  {
403  QLayout *contentsLayout = d_data->view->contentsWidget->layout();
404 
405  while ( widgetList.size() > legendData.size() )
406  {
407  QWidget *w = widgetList.takeLast();
408 
409  contentsLayout->removeWidget( w );
410 
411  // updates might be triggered by signals from the legend widget
412  // itself. So we better don't delete it here.
413 
414  w->hide();
415  w->deleteLater();
416  }
417 
418  widgetList.reserve( legendData.size() );
419 
420  for ( int i = widgetList.size(); i < legendData.size(); i++ )
421  {
422  QWidget *widget = createWidget( legendData[i] );
423 
424  if ( contentsLayout )
425  contentsLayout->addWidget( widget );
426 
427  if ( isVisible() )
428  {
429  // QLayout does a delayed show, with the effect, that
430  // the size hint will be wrong, when applications
431  // call replot() right after changing the list
432  // of plot items. So we better do the show now.
433 
434  widget->setVisible( true );
435  }
436 
437  widgetList += widget;
438  }
439 
440  if ( widgetList.isEmpty() )
441  {
442  d_data->itemMap.remove( itemInfo );
443  }
444  else
445  {
446  d_data->itemMap.insert( itemInfo, widgetList );
447  }
448 
449  updateTabOrder();
450  }
451 
452  for ( int i = 0; i < legendData.size(); i++ )
453  updateWidget( widgetList[i], legendData[i] );
454 }
455 
467 QWidget *QwtLegend::createWidget( const QwtLegendData &legendData ) const
468 {
469  Q_UNUSED( legendData );
470 
471  QwtLegendLabel *label = new QwtLegendLabel();
472  label->setItemMode( defaultItemMode() );
473 
474  connect( label, SIGNAL(clicked()), SLOT(itemClicked()) );
475  connect( label, SIGNAL(checked(bool)), SLOT(itemChecked(bool)) );
476 
477  return label;
478 }
479 
489 void QwtLegend::updateWidget( QWidget *widget, const QwtLegendData &legendData )
490 {
491  QwtLegendLabel *label = qobject_cast<QwtLegendLabel *>( widget );
492  if ( label )
493  {
494  label->setData( legendData );
495  if ( !legendData.value( QwtLegendData::ModeRole ).isValid() )
496  {
497  // use the default mode, when there is no specific
498  // hint from the legend data
499 
500  label->setItemMode( defaultItemMode() );
501  }
502  }
503 }
504 
506 {
507  QLayout *contentsLayout = d_data->view->contentsWidget->layout();
508  if ( contentsLayout )
509  {
510  // set tab focus chain
511 
512  QWidget *w = NULL;
513 
514  for ( int i = 0; i < contentsLayout->count(); i++ )
515  {
516  QLayoutItem *item = contentsLayout->itemAt( i );
517  if ( w && item->widget() )
518  QWidget::setTabOrder( w, item->widget() );
519 
520  w = item->widget();
521  }
522  }
523 }
524 
526 QSize QwtLegend::sizeHint() const
527 {
528  QSize hint = d_data->view->contentsWidget->sizeHint();
529  hint += QSize( 2 * frameWidth(), 2 * frameWidth() );
530 
531  return hint;
532 }
533 
538 int QwtLegend::heightForWidth( int width ) const
539 {
540  width -= 2 * frameWidth();
541 
542  int h = d_data->view->contentsWidget->heightForWidth( width );
543  if ( h >= 0 )
544  h += 2 * frameWidth();
545 
546  return h;
547 }
548 
549 
559 bool QwtLegend::eventFilter( QObject *object, QEvent *event )
560 {
561  if ( object == d_data->view->contentsWidget )
562  {
563  switch ( event->type() )
564  {
565  case QEvent::ChildRemoved:
566  {
567  const QChildEvent *ce =
568  static_cast<const QChildEvent *>(event);
569 
570  if ( ce->child()->isWidgetType() )
571  {
572  /*
573  We are called from the ~QObject and ce->child() is
574  no widget anymore. But all we need is the address
575  to remove it from the map.
576  */
577  QWidget *w = reinterpret_cast< QWidget * >( ce->child() );
578  d_data->itemMap.removeWidget( w );
579  }
580  break;
581  }
582  case QEvent::LayoutRequest:
583  {
584  d_data->view->layoutContents();
585 
586  if ( parentWidget() && parentWidget()->layout() == NULL )
587  {
588  /*
589  We want the parent widget ( usually QwtPlot ) to recalculate
590  its layout, when the contentsWidget has changed. But
591  because of the scroll view we have to forward the LayoutRequest
592  event manually.
593 
594  We don't use updateGeometry() because it doesn't post LayoutRequest
595  events when the legend is hidden. But we want the
596  parent widget notified, so it can show/hide the legend
597  depending on its items.
598  */
599  QApplication::postEvent( parentWidget(),
600  new QEvent( QEvent::LayoutRequest ) );
601  }
602  break;
603  }
604  default:
605  break;
606  }
607  }
608 
609  return QwtAbstractLegend::eventFilter( object, event );
610 }
611 
617 {
618  QWidget *w = qobject_cast<QWidget *>( sender() );
619  if ( w )
620  {
621  const QVariant itemInfo = d_data->itemMap.itemInfo( w );
622  if ( itemInfo.isValid() )
623  {
624  const QList<QWidget *> widgetList =
625  d_data->itemMap.legendWidgets( itemInfo );
626 
627  const int index = widgetList.indexOf( w );
628  if ( index >= 0 )
629  Q_EMIT clicked( itemInfo, index );
630  }
631  }
632 }
633 
638 void QwtLegend::itemChecked( bool on )
639 {
640  QWidget *w = qobject_cast<QWidget *>( sender() );
641  if ( w )
642  {
643  const QVariant itemInfo = d_data->itemMap.itemInfo( w );
644  if ( itemInfo.isValid() )
645  {
646  const QList<QWidget *> widgetList =
647  d_data->itemMap.legendWidgets( itemInfo );
648 
649  const int index = widgetList.indexOf( w );
650  if ( index >= 0 )
651  Q_EMIT checked( itemInfo, on, index );
652  }
653  }
654 }
655 
665 void QwtLegend::renderLegend( QPainter *painter,
666  const QRectF &rect, bool fillBackground ) const
667 {
668  if ( d_data->itemMap.isEmpty() )
669  return;
670 
671  if ( fillBackground )
672  {
673  if ( autoFillBackground() ||
674  testAttribute( Qt::WA_StyledBackground ) )
675  {
676  QwtPainter::drawBackgound( painter, rect, this );
677  }
678  }
679 
680  const QwtDynGridLayout *legendLayout =
681  qobject_cast<QwtDynGridLayout *>( contentsWidget()->layout() );
682  if ( legendLayout == NULL )
683  return;
684 
685  const QMargins m = contentsMargins();
686 
687  QRect layoutRect;
688  layoutRect.setLeft( qwtCeil( rect.left() ) + m.left() );
689  layoutRect.setTop( qwtCeil( rect.top() ) + m.top() );
690  layoutRect.setRight( qwtFloor( rect.right() ) - m.right() );
691  layoutRect.setBottom( qwtFloor( rect.bottom() ) - m.bottom() );
692 
693  uint numCols = legendLayout->columnsForWidth( layoutRect.width() );
694  const QList<QRect> itemRects =
695  legendLayout->layoutItems( layoutRect, numCols );
696 
697  int index = 0;
698 
699  for ( int i = 0; i < legendLayout->count(); i++ )
700  {
701  QLayoutItem *item = legendLayout->itemAt( i );
702  QWidget *w = item->widget();
703  if ( w )
704  {
705  painter->save();
706 
707  painter->setClipRect( itemRects[index], Qt::IntersectClip );
708  renderItem( painter, w, itemRects[index], fillBackground );
709 
710  index++;
711  painter->restore();
712  }
713  }
714 }
715 
727 void QwtLegend::renderItem( QPainter *painter,
728  const QWidget *widget, const QRectF &rect, bool fillBackground ) const
729 {
730  if ( fillBackground )
731  {
732  if ( widget->autoFillBackground() ||
733  widget->testAttribute( Qt::WA_StyledBackground ) )
734  {
735  QwtPainter::drawBackgound( painter, rect, widget );
736  }
737  }
738 
739  const QwtLegendLabel *label = qobject_cast<const QwtLegendLabel *>( widget );
740  if ( label )
741  {
742  // icon
743 
744  const QwtGraphic &icon = label->data().icon();
745  const QSizeF sz = icon.defaultSize();
746 
747  const QRectF iconRect( rect.x() + label->margin(),
748  rect.center().y() - 0.5 * sz.height(),
749  sz.width(), sz.height() );
750 
751  icon.render( painter, iconRect, Qt::KeepAspectRatio );
752 
753  // title
754 
755  QRectF titleRect = rect;
756  titleRect.setX( iconRect.right() + 2 * label->spacing() );
757 
758  QFont labelFont = label->font();
759  labelFont.resolve( QFont::AllPropertiesResolved );
760 
761  painter->setFont( labelFont );
762  painter->setPen( label->palette().color( QPalette::Text ) );
763 
764  const_cast< QwtLegendLabel *>( label )->drawText( painter, titleRect );
765  }
766 }
767 
773 QList<QWidget *> QwtLegend::legendWidgets( const QVariant &itemInfo ) const
774 {
775  return d_data->itemMap.legendWidgets( itemInfo );
776 }
777 
784 QWidget *QwtLegend::legendWidget( const QVariant &itemInfo ) const
785 {
786  const QList<QWidget *> list = d_data->itemMap.legendWidgets( itemInfo );
787  if ( list.isEmpty() )
788  return NULL;
789 
790  return list[0];
791 }
792 
800 QVariant QwtLegend::itemInfo( const QWidget *widget ) const
801 {
802  return d_data->itemMap.itemInfo( widget );
803 }
804 
806 bool QwtLegend::isEmpty() const
807 {
808  return d_data->itemMap.isEmpty();
809 }
810 
817 int QwtLegend::scrollExtent( Qt::Orientation orientation ) const
818 {
819  int extent = 0;
820 
821  if ( orientation == Qt::Horizontal )
822  extent = verticalScrollBar()->sizeHint().width();
823  else
824  extent = horizontalScrollBar()->sizeHint().height();
825 
826  return extent;
827 }
828 
829 #if QWT_MOC_INCLUDE
830 #include "moc_qwt_legend.cpp"
831 #endif
A widget representing something on a QwtLegend.
virtual bool event(QEvent *event) QWT_OVERRIDE
Definition: qwt_legend.cpp:164
virtual int heightForWidth(int) const QWT_OVERRIDE
virtual int maxItemWidth() const
void render(QPainter *) const
Replay all recorded painter commands.
virtual bool eventFilter(QObject *, QEvent *) QWT_OVERRIDE
Definition: qwt_legend.cpp:559
virtual bool viewportEvent(QEvent *event) QWT_OVERRIDE
Definition: qwt_legend.cpp:192
virtual bool isEmpty() const QWT_OVERRIDE
Definition: qwt_legend.cpp:806
virtual uint columnsForWidth(int width) const
Calculate the number of columns for a given width.
QScrollBar * horizontalScrollBar() const
Definition: qwt_legend.cpp:364
const QwtLegendData & data() const
QSizeF defaultSize() const
Default size.
void updateTabOrder()
Definition: qwt_legend.cpp:505
int qwtFloor(qreal value)
Definition: qwt_math.h:271
QwtLegendData::Mode defaultItemMode() const
Definition: qwt_legend.cpp:344
void setDefaultItemMode(QwtLegendData::Mode)
Set the default mode for legend labels.
Definition: qwt_legend.cpp:335
QWidget * contentsWidget
Definition: qwt_legend.cpp:251
QVariant itemInfo(const QWidget *) const
Definition: qwt_legend.cpp:800
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:310
virtual void updateLegend(const QVariant &, const QList< QwtLegendData > &) QWT_OVERRIDE
Update the entries for an item.
Definition: qwt_legend.cpp:396
QScrollBar * verticalScrollBar() const
Definition: qwt_legend.cpp:373
virtual int heightForWidth(int w) const QWT_OVERRIDE
Definition: qwt_legend.cpp:538
void setMaxColumns(uint maxColumns)
virtual QLayoutItem * itemAt(int index) const QWT_OVERRIDE
QVariant value(int role) const
QList< QRect > layoutItems(const QRect &, uint numColumns) const
QwtGraphic icon() const
A paint device for scalable graphics.
Definition: qwt_graphic.h:75
QwtLegend(QWidget *parent=NULL)
Definition: qwt_legend.cpp:258
QwtLegendData::Mode itemMode
Definition: qwt_legend.cpp:137
static void drawBackgound(QPainter *, const QRectF &, const QWidget *)
QSize viewportSize(int w, int h) const
Definition: qwt_legend.cpp:203
PrivateData * d_data
Definition: qwt_legend.h:115
#define QWT_FINAL
Definition: qwt_global.h:57
int spacing() const
virtual void renderLegend(QPainter *, const QRectF &, bool fillBackground) const QWT_OVERRIDE
Definition: qwt_legend.cpp:665
virtual QSize sizeHint() const QWT_OVERRIDE
Return a size hint.
Definition: qwt_legend.cpp:526
QList< QWidget * > legendWidgets(const QVariant &) const
Definition: qwt_legend.cpp:773
QWidget * legendWidget(const QVariant &) const
Definition: qwt_legend.cpp:784
Abstract base class for legend widgets.
QWidget * contentsWidget()
Definition: qwt_legend.cpp:355
void layoutContents()
Definition: qwt_legend.cpp:226
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:467
virtual ~QwtLegend()
Destructor.
Definition: qwt_legend.cpp:281
virtual int scrollExtent(Qt::Orientation) const QWT_OVERRIDE
Definition: qwt_legend.cpp:817
void setMaxColumns(uint numColums)
Set the maximum number of entries in a row.
Definition: qwt_legend.cpp:296
void setData(const QwtLegendData &)
void remove(lua_State *L, int rawindex, int count)
Definition: sol.hpp:8933
void checked(const QVariant &itemInfo, bool on, int index)
void clicked(const QVariant &itemInfo, int index)
void itemChecked(bool)
Definition: qwt_legend.cpp:638
virtual void updateWidget(QWidget *, const QwtLegendData &)
Update the widget.
Definition: qwt_legend.cpp:489
Mode
Mode defining how a legend entry interacts.
uint maxColumns() const
Return the upper limit for the number of columns.
int qwtCeil(qreal value)
Definition: qwt_math.h:262
virtual int count() const QWT_OVERRIDE
Attributes of an entry on a legend.
virtual void renderItem(QPainter *, const QWidget *, const QRectF &, bool fillBackground) const
Definition: qwt_legend.cpp:727
void itemClicked()
Definition: qwt_legend.cpp:616
#define QWT_OVERRIDE
Definition: qwt_global.h:53
void setItemMode(QwtLegendData::Mode)
LegendView(QWidget *parent)
Definition: qwt_legend.cpp:147


plotjuggler
Author(s): Davide Faconti
autogenerated on Sun Dec 6 2020 03:48:10