qwt_legend.cpp
Go to the documentation of this file.
00001 /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
00002  * Qwt Widget Library
00003  * Copyright (C) 1997   Josef Wilgen
00004  * Copyright (C) 2002   Uwe Rathmann
00005  *
00006  * This library is free software; you can redistribute it and/or
00007  * modify it under the terms of the Qwt License, Version 1.0
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     // we don't know anything about itemInfo and therefore don't have
00038     // any key that can be used for a map or hashtab.
00039     // But a simple linear list is o.k. here, as we will never have
00040     // more than a few entries.
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         // QScrollArea::setWidget internally sets autoFillBackground to true
00156         // But we don't want a background.
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             // adjust the size to en/disable the scrollbars
00171             // before QScrollArea adjusts the viewport size
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             // updates might be triggered by signals from the legend widget
00406             // itself. So we better don't delete it here.
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                 // QLayout does a delayed show, with the effect, that
00426                 // the size hint will be wrong, when applications
00427                 // call replot() right after changing the list
00428                 // of plot items. So we better do the show now.
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             // use the default mode, when there is no specific
00494             // hint from the legend data
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         // set tab focus chain
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                        We want the parent widget ( usually QwtPlot ) to recalculate
00580                        its layout, when the contentsWidget has changed. But
00581                        because of the scroll view we have to forward the LayoutRequest
00582                        event manually.
00583 
00584                        We don't use updateGeometry() because it doesn't post LayoutRequest
00585                        events when the legend is hidden. But we want the
00586                        parent widget notified, so it can show/hide the legend
00587                        depending on its items.
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         // icon
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         // title
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 


plotjuggler
Author(s): Davide Faconti
autogenerated on Wed Jul 3 2019 19:28:04