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         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                 // QLayout does a delayed show, with the effect, that
00422                 // the size hint will be wrong, when applications
00423                 // call replot() right after changing the list
00424                 // of plot items. So we better do the show now.
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             // use the default mode, when there is no specific
00490             // hint from the legend data
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         // set tab focus chain
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                        We want the parent widget ( usually QwtPlot ) to recalculate
00576                        its layout, when the contentsWidget has changed. But
00577                        because of the scroll view we have to forward the LayoutRequest
00578                        event manually.
00579 
00580                        We don't use updateGeometry() because it doesn't post LayoutRequest
00581                        events when the legend is hidden. But we want the
00582                        parent widget notified, so it can show/hide the legend
00583                        depending on its items.
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         // icon
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         // title
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 


plotjuggler
Author(s): Davide Faconti
autogenerated on Fri Sep 1 2017 02:41:56