00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #include "qwt_legend.h"
00011 #include "qwt_legend_label.h"
00012 #include "qwt_dyngrid_layout.h"
00013 #include "qwt_math.h"
00014 #include "qwt_plot_item.h"
00015 #include "qwt_painter.h"
00016 #include <qapplication.h>
00017 #include <qscrollbar.h>
00018 #include <qscrollarea.h>
00019 #include <qpainter.h>
00020 #include <qstyle.h>
00021 #include <qstyleoption.h>
00022
00023 class QwtLegendMap
00024 {
00025 public:
00026 inline bool isEmpty() const { return d_entries.isEmpty(); }
00027
00028 void insert( const QVariant &, const QList<QWidget *> & );
00029 void remove( const QVariant & );
00030
00031 void removeWidget( const QWidget * );
00032
00033 QList<QWidget *> legendWidgets( const QVariant & ) const;
00034 QVariant itemInfo( const QWidget * ) const;
00035
00036 private:
00037
00038
00039
00040
00041
00042 class Entry
00043 {
00044 public:
00045 QVariant itemInfo;
00046 QList<QWidget *> widgets;
00047 };
00048
00049 QList< Entry > d_entries;
00050 };
00051
00052 void QwtLegendMap::insert( const QVariant &itemInfo,
00053 const QList<QWidget *> &widgets )
00054 {
00055 for ( int i = 0; i < d_entries.size(); i++ )
00056 {
00057 Entry &entry = d_entries[i];
00058 if ( entry.itemInfo == itemInfo )
00059 {
00060 entry.widgets = widgets;
00061 return;
00062 }
00063 }
00064
00065 Entry newEntry;
00066 newEntry.itemInfo = itemInfo;
00067 newEntry.widgets = widgets;
00068
00069 d_entries += newEntry;
00070 }
00071
00072 void QwtLegendMap::remove( const QVariant &itemInfo )
00073 {
00074 for ( int i = 0; i < d_entries.size(); i++ )
00075 {
00076 Entry &entry = d_entries[i];
00077 if ( entry.itemInfo == itemInfo )
00078 {
00079 d_entries.removeAt( i );
00080 return;
00081 }
00082 }
00083 }
00084
00085 void QwtLegendMap::removeWidget( const QWidget *widget )
00086 {
00087 QWidget *w = const_cast<QWidget *>( widget );
00088
00089 for ( int i = 0; i < d_entries.size(); i++ )
00090 d_entries[ i ].widgets.removeAll( w );
00091 }
00092
00093 QVariant QwtLegendMap::itemInfo( const QWidget *widget ) const
00094 {
00095 if ( widget != NULL )
00096 {
00097 QWidget *w = const_cast<QWidget *>( widget );
00098
00099 for ( int i = 0; i < d_entries.size(); i++ )
00100 {
00101 const Entry &entry = d_entries[i];
00102 if ( entry.widgets.indexOf( w ) >= 0 )
00103 return entry.itemInfo;
00104 }
00105 }
00106
00107 return QVariant();
00108 }
00109
00110 QList<QWidget *> QwtLegendMap::legendWidgets( const QVariant &itemInfo ) const
00111 {
00112 if ( itemInfo.isValid() )
00113 {
00114 for ( int i = 0; i < d_entries.size(); i++ )
00115 {
00116 const Entry &entry = d_entries[i];
00117 if ( entry.itemInfo == itemInfo )
00118 return entry.widgets;
00119 }
00120 }
00121
00122 return QList<QWidget *>();
00123 }
00124
00125 class QwtLegend::PrivateData
00126 {
00127 public:
00128 PrivateData():
00129 itemMode( QwtLegendData::ReadOnly ),
00130 view( NULL )
00131 {
00132 }
00133
00134 QwtLegendData::Mode itemMode;
00135 QwtLegendMap itemMap;
00136
00137 class LegendView;
00138 LegendView *view;
00139 };
00140
00141 class QwtLegend::PrivateData::LegendView: public QScrollArea
00142 {
00143 public:
00144 explicit LegendView( QWidget *parent ):
00145 QScrollArea( parent )
00146 {
00147 contentsWidget = new QWidget( this );
00148 contentsWidget->setObjectName( "QwtLegendViewContents" );
00149
00150 setWidget( contentsWidget );
00151 setWidgetResizable( false );
00152
00153 viewport()->setObjectName( "QwtLegendViewport" );
00154
00155
00156
00157 contentsWidget->setAutoFillBackground( false );
00158 viewport()->setAutoFillBackground( false );
00159 }
00160
00161 virtual bool event( QEvent *event )
00162 {
00163 if ( event->type() == QEvent::PolishRequest )
00164 {
00165 setFocusPolicy( Qt::NoFocus );
00166 }
00167
00168 if ( event->type() == QEvent::Resize )
00169 {
00170
00171
00172
00173 const QRect cr = contentsRect();
00174
00175 int w = cr.width();
00176 int h = contentsWidget->heightForWidth( cr.width() );
00177 if ( h > w )
00178 {
00179 w -= verticalScrollBar()->sizeHint().width();
00180 h = contentsWidget->heightForWidth( w );
00181 }
00182
00183 contentsWidget->resize( w, h );
00184 }
00185
00186 return QScrollArea::event( event );
00187 }
00188
00189 virtual bool viewportEvent( QEvent *event )
00190 {
00191 bool ok = QScrollArea::viewportEvent( event );
00192
00193 if ( event->type() == QEvent::Resize )
00194 {
00195 layoutContents();
00196 }
00197 return ok;
00198 }
00199
00200 QSize viewportSize( int w, int h ) const
00201 {
00202 const int sbHeight = horizontalScrollBar()->sizeHint().height();
00203 const int sbWidth = verticalScrollBar()->sizeHint().width();
00204
00205 const int cw = contentsRect().width();
00206 const int ch = contentsRect().height();
00207
00208 int vw = cw;
00209 int vh = ch;
00210
00211 if ( w > vw )
00212 vh -= sbHeight;
00213
00214 if ( h > vh )
00215 {
00216 vw -= sbWidth;
00217 if ( w > vw && vh == ch )
00218 vh -= sbHeight;
00219 }
00220 return QSize( vw, vh );
00221 }
00222
00223 void layoutContents()
00224 {
00225 const QwtDynGridLayout *tl = qobject_cast<QwtDynGridLayout *>(
00226 contentsWidget->layout() );
00227 if ( tl == NULL )
00228 return;
00229
00230 const QSize visibleSize = viewport()->contentsRect().size();
00231
00232 const int minW = int( tl->maxItemWidth() ) + 2 * tl->margin();
00233
00234 int w = qMax( visibleSize.width(), minW );
00235 int h = qMax( tl->heightForWidth( w ), visibleSize.height() );
00236
00237 const int vpWidth = viewportSize( w, h ).width();
00238 if ( w > vpWidth )
00239 {
00240 w = qMax( vpWidth, minW );
00241 h = qMax( tl->heightForWidth( w ), visibleSize.height() );
00242 }
00243
00244 contentsWidget->resize( w, h );
00245 }
00246
00247 QWidget *contentsWidget;
00248 };
00249
00254 QwtLegend::QwtLegend( QWidget *parent ):
00255 QwtAbstractLegend( parent )
00256 {
00257 setFrameStyle( NoFrame );
00258
00259 d_data = new QwtLegend::PrivateData;
00260
00261 d_data->view = new QwtLegend::PrivateData::LegendView( this );
00262 d_data->view->setObjectName( "QwtLegendView" );
00263 d_data->view->setFrameStyle( NoFrame );
00264
00265 QwtDynGridLayout *gridLayout = new QwtDynGridLayout(
00266 d_data->view->contentsWidget );
00267 gridLayout->setAlignment( Qt::AlignHCenter | Qt::AlignTop );
00268
00269 d_data->view->contentsWidget->installEventFilter( this );
00270
00271 QVBoxLayout *layout = new QVBoxLayout( this );
00272 layout->setContentsMargins( 0, 0, 0, 0 );
00273 layout->addWidget( d_data->view );
00274 }
00275
00277 QwtLegend::~QwtLegend()
00278 {
00279 delete d_data;
00280 }
00281
00292 void QwtLegend::setMaxColumns( uint numColums )
00293 {
00294 QwtDynGridLayout *tl = qobject_cast<QwtDynGridLayout *>(
00295 d_data->view->contentsWidget->layout() );
00296 if ( tl )
00297 tl->setMaxColumns( numColums );
00298 }
00299
00304 uint QwtLegend::maxColumns() const
00305 {
00306 uint maxCols = 0;
00307
00308 const QwtDynGridLayout *tl = qobject_cast<const QwtDynGridLayout *>(
00309 d_data->view->contentsWidget->layout() );
00310 if ( tl )
00311 maxCols = tl->maxColumns();
00312
00313 return maxCols;
00314 }
00315
00329 void QwtLegend::setDefaultItemMode( QwtLegendData::Mode mode )
00330 {
00331 d_data->itemMode = mode;
00332 }
00333
00338 QwtLegendData::Mode QwtLegend::defaultItemMode() const
00339 {
00340 return d_data->itemMode;
00341 }
00342
00349 QWidget *QwtLegend::contentsWidget()
00350 {
00351 return d_data->view->contentsWidget;
00352 }
00353
00358 QScrollBar *QwtLegend::horizontalScrollBar() const
00359 {
00360 return d_data->view->horizontalScrollBar();
00361 }
00362
00367 QScrollBar *QwtLegend::verticalScrollBar() const
00368 {
00369 return d_data->view->verticalScrollBar();
00370 }
00371
00379 const QWidget *QwtLegend::contentsWidget() const
00380 {
00381 return d_data->view->contentsWidget;
00382 }
00383
00390 void QwtLegend::updateLegend( const QVariant &itemInfo,
00391 const QList<QwtLegendData> &data )
00392 {
00393 QList<QWidget *> widgetList = legendWidgets( itemInfo );
00394
00395 if ( widgetList.size() != data.size() )
00396 {
00397 QLayout *contentsLayout = d_data->view->contentsWidget->layout();
00398
00399 while ( widgetList.size() > data.size() )
00400 {
00401 QWidget *w = widgetList.takeLast();
00402
00403 contentsLayout->removeWidget( w );
00404
00405
00406
00407
00408 w->hide();
00409 w->deleteLater();
00410 }
00411
00412 #if QT_VERSION >= 0x040700
00413 widgetList.reserve( data.size() );
00414 #endif
00415
00416 for ( int i = widgetList.size(); i < data.size(); i++ )
00417 {
00418 QWidget *widget = createWidget( data[i] );
00419
00420 if ( contentsLayout )
00421 contentsLayout->addWidget( widget );
00422
00423 if ( isVisible() )
00424 {
00425
00426
00427
00428
00429
00430 widget->setVisible( true );
00431 }
00432
00433 widgetList += widget;
00434 }
00435
00436 if ( widgetList.isEmpty() )
00437 {
00438 d_data->itemMap.remove( itemInfo );
00439 }
00440 else
00441 {
00442 d_data->itemMap.insert( itemInfo, widgetList );
00443 }
00444
00445 updateTabOrder();
00446 }
00447
00448 for ( int i = 0; i < data.size(); i++ )
00449 updateWidget( widgetList[i], data[i] );
00450 }
00451
00463 QWidget *QwtLegend::createWidget( const QwtLegendData &data ) const
00464 {
00465 Q_UNUSED( data );
00466
00467 QwtLegendLabel *label = new QwtLegendLabel();
00468 label->setItemMode( defaultItemMode() );
00469
00470 connect( label, SIGNAL( clicked() ), SLOT( itemClicked() ) );
00471 connect( label, SIGNAL( checked( bool ) ), SLOT( itemChecked( bool ) ) );
00472
00473 return label;
00474 }
00475
00485 void QwtLegend::updateWidget( QWidget *widget, const QwtLegendData &data )
00486 {
00487 QwtLegendLabel *label = qobject_cast<QwtLegendLabel *>( widget );
00488 if ( label )
00489 {
00490 label->setData( data );
00491 if ( !data.value( QwtLegendData::ModeRole ).isValid() )
00492 {
00493
00494
00495
00496 label->setItemMode( defaultItemMode() );
00497 }
00498 }
00499 }
00500
00501 void QwtLegend::updateTabOrder()
00502 {
00503 QLayout *contentsLayout = d_data->view->contentsWidget->layout();
00504 if ( contentsLayout )
00505 {
00506
00507
00508 QWidget *w = NULL;
00509
00510 for ( int i = 0; i < contentsLayout->count(); i++ )
00511 {
00512 QLayoutItem *item = contentsLayout->itemAt( i );
00513 if ( w && item->widget() )
00514 QWidget::setTabOrder( w, item->widget() );
00515
00516 w = item->widget();
00517 }
00518 }
00519 }
00520
00522 QSize QwtLegend::sizeHint() const
00523 {
00524 QSize hint = d_data->view->contentsWidget->sizeHint();
00525 hint += QSize( 2 * frameWidth(), 2 * frameWidth() );
00526
00527 return hint;
00528 }
00529
00534 int QwtLegend::heightForWidth( int width ) const
00535 {
00536 width -= 2 * frameWidth();
00537
00538 int h = d_data->view->contentsWidget->heightForWidth( width );
00539 if ( h >= 0 )
00540 h += 2 * frameWidth();
00541
00542 return h;
00543 }
00544
00545
00555 bool QwtLegend::eventFilter( QObject *object, QEvent *event )
00556 {
00557 if ( object == d_data->view->contentsWidget )
00558 {
00559 switch ( event->type() )
00560 {
00561 case QEvent::ChildRemoved:
00562 {
00563 const QChildEvent *ce =
00564 static_cast<const QChildEvent *>(event);
00565 if ( ce->child()->isWidgetType() )
00566 {
00567 QWidget *w = static_cast< QWidget * >( ce->child() );
00568 d_data->itemMap.removeWidget( w );
00569 }
00570 break;
00571 }
00572 case QEvent::LayoutRequest:
00573 {
00574 d_data->view->layoutContents();
00575
00576 if ( parentWidget() && parentWidget()->layout() == NULL )
00577 {
00578
00579
00580
00581
00582
00583
00584
00585
00586
00587
00588
00589 QApplication::postEvent( parentWidget(),
00590 new QEvent( QEvent::LayoutRequest ) );
00591 }
00592 break;
00593 }
00594 default:
00595 break;
00596 }
00597 }
00598
00599 return QwtAbstractLegend::eventFilter( object, event );
00600 }
00601
00606 void QwtLegend::itemClicked()
00607 {
00608 QWidget *w = qobject_cast<QWidget *>( sender() );
00609 if ( w )
00610 {
00611 const QVariant itemInfo = d_data->itemMap.itemInfo( w );
00612 if ( itemInfo.isValid() )
00613 {
00614 const QList<QWidget *> widgetList =
00615 d_data->itemMap.legendWidgets( itemInfo );
00616
00617 const int index = widgetList.indexOf( w );
00618 if ( index >= 0 )
00619 Q_EMIT clicked( itemInfo, index );
00620 }
00621 }
00622 }
00623
00628 void QwtLegend::itemChecked( bool on )
00629 {
00630 QWidget *w = qobject_cast<QWidget *>( sender() );
00631 if ( w )
00632 {
00633 const QVariant itemInfo = d_data->itemMap.itemInfo( w );
00634 if ( itemInfo.isValid() )
00635 {
00636 const QList<QWidget *> widgetList =
00637 d_data->itemMap.legendWidgets( itemInfo );
00638
00639 const int index = widgetList.indexOf( w );
00640 if ( index >= 0 )
00641 Q_EMIT checked( itemInfo, on, index );
00642 }
00643 }
00644 }
00645
00655 void QwtLegend::renderLegend( QPainter *painter,
00656 const QRectF &rect, bool fillBackground ) const
00657 {
00658 if ( d_data->itemMap.isEmpty() )
00659 return;
00660
00661 if ( fillBackground )
00662 {
00663 if ( autoFillBackground() ||
00664 testAttribute( Qt::WA_StyledBackground ) )
00665 {
00666 QwtPainter::drawBackgound( painter, rect, this );
00667 }
00668 }
00669
00670 const QwtDynGridLayout *legendLayout =
00671 qobject_cast<QwtDynGridLayout *>( contentsWidget()->layout() );
00672 if ( legendLayout == NULL )
00673 return;
00674
00675 int left, right, top, bottom;
00676 getContentsMargins( &left, &top, &right, &bottom );
00677
00678 QRect layoutRect;
00679 layoutRect.setLeft( qCeil( rect.left() ) + left );
00680 layoutRect.setTop( qCeil( rect.top() ) + top );
00681 layoutRect.setRight( qFloor( rect.right() ) - right );
00682 layoutRect.setBottom( qFloor( rect.bottom() ) - bottom );
00683
00684 uint numCols = legendLayout->columnsForWidth( layoutRect.width() );
00685 const QList<QRect> itemRects =
00686 legendLayout->layoutItems( layoutRect, numCols );
00687
00688 int index = 0;
00689
00690 for ( int i = 0; i < legendLayout->count(); i++ )
00691 {
00692 QLayoutItem *item = legendLayout->itemAt( i );
00693 QWidget *w = item->widget();
00694 if ( w )
00695 {
00696 painter->save();
00697
00698 painter->setClipRect( itemRects[index], Qt::IntersectClip );
00699 renderItem( painter, w, itemRects[index], fillBackground );
00700
00701 index++;
00702 painter->restore();
00703 }
00704 }
00705 }
00706
00718 void QwtLegend::renderItem( QPainter *painter,
00719 const QWidget *widget, const QRectF &rect, bool fillBackground ) const
00720 {
00721 if ( fillBackground )
00722 {
00723 if ( widget->autoFillBackground() ||
00724 widget->testAttribute( Qt::WA_StyledBackground ) )
00725 {
00726 QwtPainter::drawBackgound( painter, rect, widget );
00727 }
00728 }
00729
00730 const QwtLegendLabel *label = qobject_cast<const QwtLegendLabel *>( widget );
00731 if ( label )
00732 {
00733
00734
00735 const QwtGraphic &icon = label->data().icon();
00736 const QSizeF sz = icon.defaultSize();
00737
00738 const QRectF iconRect( rect.x() + label->margin(),
00739 rect.center().y() - 0.5 * sz.height(),
00740 sz.width(), sz.height() );
00741
00742 icon.render( painter, iconRect, Qt::KeepAspectRatio );
00743
00744
00745
00746 QRectF titleRect = rect;
00747 titleRect.setX( iconRect.right() + 2 * label->spacing() );
00748
00749 painter->setFont( label->font() );
00750 painter->setPen( label->palette().color( QPalette::Text ) );
00751 const_cast< QwtLegendLabel *>( label )->drawText( painter, titleRect );
00752 }
00753 }
00754
00760 QList<QWidget *> QwtLegend::legendWidgets( const QVariant &itemInfo ) const
00761 {
00762 return d_data->itemMap.legendWidgets( itemInfo );
00763 }
00764
00771 QWidget *QwtLegend::legendWidget( const QVariant &itemInfo ) const
00772 {
00773 const QList<QWidget *> list = d_data->itemMap.legendWidgets( itemInfo );
00774 if ( list.isEmpty() )
00775 return NULL;
00776
00777 return list[0];
00778 }
00779
00787 QVariant QwtLegend::itemInfo( const QWidget *widget ) const
00788 {
00789 return d_data->itemMap.itemInfo( widget );
00790 }
00791
00793 bool QwtLegend::isEmpty() const
00794 {
00795 return d_data->itemMap.isEmpty();
00796 }
00797
00804 int QwtLegend::scrollExtent( Qt::Orientation orientation ) const
00805 {
00806 int extent = 0;
00807
00808 if ( orientation == Qt::Horizontal )
00809 extent = verticalScrollBar()->sizeHint().width();
00810 else
00811 extent = horizontalScrollBar()->sizeHint().height();
00812
00813 return extent;
00814 }
00815