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 for ( int i = widgetList.size(); i < data.size(); i++ )
00413 {
00414 QWidget *widget = createWidget( data[i] );
00415
00416 if ( contentsLayout )
00417 contentsLayout->addWidget( widget );
00418
00419 if ( isVisible() )
00420 {
00421
00422
00423
00424
00425
00426 widget->setVisible( true );
00427 }
00428
00429 widgetList += widget;
00430 }
00431
00432 if ( widgetList.isEmpty() )
00433 {
00434 d_data->itemMap.remove( itemInfo );
00435 }
00436 else
00437 {
00438 d_data->itemMap.insert( itemInfo, widgetList );
00439 }
00440
00441 updateTabOrder();
00442 }
00443
00444 for ( int i = 0; i < data.size(); i++ )
00445 updateWidget( widgetList[i], data[i] );
00446 }
00447
00459 QWidget *QwtLegend::createWidget( const QwtLegendData &data ) const
00460 {
00461 Q_UNUSED( data );
00462
00463 QwtLegendLabel *label = new QwtLegendLabel();
00464 label->setItemMode( defaultItemMode() );
00465
00466 connect( label, SIGNAL( clicked() ), SLOT( itemClicked() ) );
00467 connect( label, SIGNAL( checked( bool ) ), SLOT( itemChecked( bool ) ) );
00468
00469 return label;
00470 }
00471
00481 void QwtLegend::updateWidget( QWidget *widget, const QwtLegendData &data )
00482 {
00483 QwtLegendLabel *label = qobject_cast<QwtLegendLabel *>( widget );
00484 if ( label )
00485 {
00486 label->setData( data );
00487 if ( !data.value( QwtLegendData::ModeRole ).isValid() )
00488 {
00489
00490
00491
00492 label->setItemMode( defaultItemMode() );
00493 }
00494 }
00495 }
00496
00497 void QwtLegend::updateTabOrder()
00498 {
00499 QLayout *contentsLayout = d_data->view->contentsWidget->layout();
00500 if ( contentsLayout )
00501 {
00502
00503
00504 QWidget *w = NULL;
00505
00506 for ( int i = 0; i < contentsLayout->count(); i++ )
00507 {
00508 QLayoutItem *item = contentsLayout->itemAt( i );
00509 if ( w && item->widget() )
00510 QWidget::setTabOrder( w, item->widget() );
00511
00512 w = item->widget();
00513 }
00514 }
00515 }
00516
00518 QSize QwtLegend::sizeHint() const
00519 {
00520 QSize hint = d_data->view->contentsWidget->sizeHint();
00521 hint += QSize( 2 * frameWidth(), 2 * frameWidth() );
00522
00523 return hint;
00524 }
00525
00530 int QwtLegend::heightForWidth( int width ) const
00531 {
00532 width -= 2 * frameWidth();
00533
00534 int h = d_data->view->contentsWidget->heightForWidth( width );
00535 if ( h >= 0 )
00536 h += 2 * frameWidth();
00537
00538 return h;
00539 }
00540
00541
00551 bool QwtLegend::eventFilter( QObject *object, QEvent *event )
00552 {
00553 if ( object == d_data->view->contentsWidget )
00554 {
00555 switch ( event->type() )
00556 {
00557 case QEvent::ChildRemoved:
00558 {
00559 const QChildEvent *ce =
00560 static_cast<const QChildEvent *>(event);
00561 if ( ce->child()->isWidgetType() )
00562 {
00563 QWidget *w = static_cast< QWidget * >( ce->child() );
00564 d_data->itemMap.removeWidget( w );
00565 }
00566 break;
00567 }
00568 case QEvent::LayoutRequest:
00569 {
00570 d_data->view->layoutContents();
00571
00572 if ( parentWidget() && parentWidget()->layout() == NULL )
00573 {
00574
00575
00576
00577
00578
00579
00580
00581
00582
00583
00584
00585 QApplication::postEvent( parentWidget(),
00586 new QEvent( QEvent::LayoutRequest ) );
00587 }
00588 break;
00589 }
00590 default:
00591 break;
00592 }
00593 }
00594
00595 return QwtAbstractLegend::eventFilter( object, event );
00596 }
00597
00602 void QwtLegend::itemClicked()
00603 {
00604 QWidget *w = qobject_cast<QWidget *>( sender() );
00605 if ( w )
00606 {
00607 const QVariant itemInfo = d_data->itemMap.itemInfo( w );
00608 if ( itemInfo.isValid() )
00609 {
00610 const QList<QWidget *> widgetList =
00611 d_data->itemMap.legendWidgets( itemInfo );
00612
00613 const int index = widgetList.indexOf( w );
00614 if ( index >= 0 )
00615 Q_EMIT clicked( itemInfo, index );
00616 }
00617 }
00618 }
00619
00624 void QwtLegend::itemChecked( bool on )
00625 {
00626 QWidget *w = qobject_cast<QWidget *>( sender() );
00627 if ( w )
00628 {
00629 const QVariant itemInfo = d_data->itemMap.itemInfo( w );
00630 if ( itemInfo.isValid() )
00631 {
00632 const QList<QWidget *> widgetList =
00633 d_data->itemMap.legendWidgets( itemInfo );
00634
00635 const int index = widgetList.indexOf( w );
00636 if ( index >= 0 )
00637 Q_EMIT checked( itemInfo, on, index );
00638 }
00639 }
00640 }
00641
00651 void QwtLegend::renderLegend( QPainter *painter,
00652 const QRectF &rect, bool fillBackground ) const
00653 {
00654 if ( d_data->itemMap.isEmpty() )
00655 return;
00656
00657 if ( fillBackground )
00658 {
00659 if ( autoFillBackground() ||
00660 testAttribute( Qt::WA_StyledBackground ) )
00661 {
00662 QwtPainter::drawBackgound( painter, rect, this );
00663 }
00664 }
00665
00666 const QwtDynGridLayout *legendLayout =
00667 qobject_cast<QwtDynGridLayout *>( contentsWidget()->layout() );
00668 if ( legendLayout == NULL )
00669 return;
00670
00671 int left, right, top, bottom;
00672 getContentsMargins( &left, &top, &right, &bottom );
00673
00674 QRect layoutRect;
00675 layoutRect.setLeft( qCeil( rect.left() ) + left );
00676 layoutRect.setTop( qCeil( rect.top() ) + top );
00677 layoutRect.setRight( qFloor( rect.right() ) - right );
00678 layoutRect.setBottom( qFloor( rect.bottom() ) - bottom );
00679
00680 uint numCols = legendLayout->columnsForWidth( layoutRect.width() );
00681 QList<QRect> itemRects =
00682 legendLayout->layoutItems( layoutRect, numCols );
00683
00684 int index = 0;
00685
00686 for ( int i = 0; i < legendLayout->count(); i++ )
00687 {
00688 QLayoutItem *item = legendLayout->itemAt( i );
00689 QWidget *w = item->widget();
00690 if ( w )
00691 {
00692 painter->save();
00693
00694 painter->setClipRect( itemRects[index], Qt::IntersectClip );
00695 renderItem( painter, w, itemRects[index], fillBackground );
00696
00697 index++;
00698 painter->restore();
00699 }
00700 }
00701 }
00702
00714 void QwtLegend::renderItem( QPainter *painter,
00715 const QWidget *widget, const QRectF &rect, bool fillBackground ) const
00716 {
00717 if ( fillBackground )
00718 {
00719 if ( widget->autoFillBackground() ||
00720 widget->testAttribute( Qt::WA_StyledBackground ) )
00721 {
00722 QwtPainter::drawBackgound( painter, rect, widget );
00723 }
00724 }
00725
00726 const QwtLegendLabel *label = qobject_cast<const QwtLegendLabel *>( widget );
00727 if ( label )
00728 {
00729
00730
00731 const QwtGraphic &icon = label->data().icon();
00732 const QSizeF sz = icon.defaultSize();
00733
00734 const QRectF iconRect( rect.x() + label->margin(),
00735 rect.center().y() - 0.5 * sz.height(),
00736 sz.width(), sz.height() );
00737
00738 icon.render( painter, iconRect, Qt::KeepAspectRatio );
00739
00740
00741
00742 QRectF titleRect = rect;
00743 titleRect.setX( iconRect.right() + 2 * label->spacing() );
00744
00745 painter->setFont( label->font() );
00746 painter->setPen( label->palette().color( QPalette::Text ) );
00747 const_cast< QwtLegendLabel *>( label )->drawText( painter, titleRect );
00748 }
00749 }
00750
00756 QList<QWidget *> QwtLegend::legendWidgets( const QVariant &itemInfo ) const
00757 {
00758 return d_data->itemMap.legendWidgets( itemInfo );
00759 }
00760
00767 QWidget *QwtLegend::legendWidget( const QVariant &itemInfo ) const
00768 {
00769 const QList<QWidget *> list = d_data->itemMap.legendWidgets( itemInfo );
00770 if ( list.isEmpty() )
00771 return NULL;
00772
00773 return list[0];
00774 }
00775
00783 QVariant QwtLegend::itemInfo( const QWidget *widget ) const
00784 {
00785 return d_data->itemMap.itemInfo( widget );
00786 }
00787
00789 bool QwtLegend::isEmpty() const
00790 {
00791 return d_data->itemMap.isEmpty();
00792 }
00793
00800 int QwtLegend::scrollExtent( Qt::Orientation orientation ) const
00801 {
00802 int extent = 0;
00803
00804 if ( orientation == Qt::Horizontal )
00805 extent = verticalScrollBar()->sizeHint().width();
00806 else
00807 extent = horizontalScrollBar()->sizeHint().height();
00808
00809 return extent;
00810 }
00811