qwt_legend.cpp

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 // vim: expandtab
00011 
00012 #include <qapplication.h> 
00013 #include <qmap.h> 
00014 #if QT_VERSION >= 0x040000
00015 #include <qscrollbar.h> 
00016 #endif
00017 #include "qwt_math.h"
00018 #include "qwt_dyngrid_layout.h"
00019 #include "qwt_plot_item.h"
00020 #include "qwt_legend_item.h"
00021 #include "qwt_legend.h"
00022 
00023 class QwtLegend::PrivateData
00024 {
00025 public:
00026     class LegendMap
00027     {
00028     public:
00029         void insert(const QwtPlotItem *, QWidget *);
00030 
00031         void remove(const QwtPlotItem *);
00032         void remove(QWidget *);
00033 
00034         void clear();
00035 
00036         uint count() const;
00037 
00038         inline const QWidget *find(const QwtPlotItem *) const;
00039         inline QWidget *find(const QwtPlotItem *);
00040 
00041         inline const QwtPlotItem *find(const QWidget *) const;
00042         inline QwtPlotItem *find(const QWidget *);
00043 
00044         const QMap<QWidget *, const QwtPlotItem *> &widgetMap() const;
00045         QMap<QWidget *, const QwtPlotItem *> &widgetMap();
00046 
00047     private:
00048         QMap<QWidget *, const QwtPlotItem *> d_widgetMap;
00049         QMap<const QwtPlotItem *, QWidget *> d_itemMap;
00050     };
00051 
00052     QwtLegend::LegendItemMode itemMode;
00053     QwtLegend::LegendDisplayPolicy displayPolicy;
00054     int identifierMode;
00055 
00056     LegendMap map;
00057 
00058     class LegendView;
00059     LegendView *view;
00060 };
00061 
00062 #if QT_VERSION < 0x040000
00063 #include <qscrollview.h>
00064 
00065 class QwtLegend::PrivateData::LegendView: public QScrollView
00066 {
00067 public:
00068     LegendView(QWidget *parent):
00069         QScrollView(parent)
00070     {
00071         setResizePolicy(Manual);
00072 
00073         viewport()->setBackgroundMode(Qt::NoBackground); // Avoid flicker
00074 
00075         contentsWidget = new QWidget(viewport());
00076 
00077         addChild(contentsWidget);
00078     }
00079 
00080     void viewportResizeEvent(QResizeEvent *e)
00081     {
00082         QScrollView::viewportResizeEvent(e);
00083 
00084         // It's not safe to update the layout now, because
00085         // we are in an internal update of the scrollview framework.
00086         // So we delay the update by posting a LayoutHint.
00087 
00088         QApplication::postEvent(contentsWidget, 
00089             new QEvent(QEvent::LayoutHint));
00090     }
00091 
00092     QWidget *contentsWidget;
00093 };
00094 
00095 #else // QT_VERSION >= 0x040000
00096 
00097 #include <qscrollarea.h>
00098 
00099 class QwtLegend::PrivateData::LegendView: public QScrollArea
00100 {
00101 public:
00102     LegendView(QWidget *parent):
00103         QScrollArea(parent)
00104     {
00105         contentsWidget = new QWidget(this);
00106 
00107         setWidget(contentsWidget);
00108         setWidgetResizable(false);
00109     }
00110 
00111     virtual bool viewportEvent(QEvent *e) 
00112     {
00113         bool ok = QScrollArea::viewportEvent(e);
00114 
00115         if ( e->type() == QEvent::Resize )
00116         {
00117             QApplication::postEvent(contentsWidget, 
00118                 new QEvent(QEvent::LayoutRequest));
00119         }
00120         return ok;
00121     }
00122 
00123     QSize viewportSize(int w, int h) const
00124     {
00125         const int sbHeight = horizontalScrollBar()->sizeHint().height();
00126         const int sbWidth = verticalScrollBar()->sizeHint().width();
00127     
00128         const int cw = contentsRect().width();
00129         const int ch = contentsRect().height();
00130 
00131         int vw = cw;
00132         int vh = ch;
00133 
00134         if ( w > vw )
00135             vh -= sbHeight;
00136 
00137         if ( h > vh )
00138         {
00139             vw -= sbWidth;
00140             if ( w > vw && vh == ch )
00141                 vh -= sbHeight;
00142         }
00143         return QSize(vw, vh);
00144     }
00145 
00146     QWidget *contentsWidget;
00147 };
00148 
00149 #endif
00150 
00151 
00152 void QwtLegend::PrivateData::LegendMap::insert(
00153     const QwtPlotItem *item, QWidget *widget)
00154 {
00155     d_itemMap.insert(item, widget);
00156     d_widgetMap.insert(widget, item);
00157 }
00158 
00159 void QwtLegend::PrivateData::LegendMap::remove(const QwtPlotItem *item)
00160 {
00161     QWidget *widget = d_itemMap[item];
00162     d_itemMap.remove(item);
00163     d_widgetMap.remove(widget);
00164 }
00165 
00166 void QwtLegend::PrivateData::LegendMap::remove(QWidget *widget)
00167 {
00168     const QwtPlotItem *item = d_widgetMap[widget];
00169     d_itemMap.remove(item);
00170     d_widgetMap.remove(widget);
00171 }
00172 
00173 void QwtLegend::PrivateData::LegendMap::clear()
00174 {
00175     
00176     /*
00177        We can't delete the widgets in the following loop, because
00178        we would get ChildRemoved events, changing d_itemMap, while
00179        we are iterating.
00180      */
00181 
00182 #if QT_VERSION < 0x040000
00183     QValueList<QWidget *> widgets;
00184 
00185     QMap<const QwtPlotItem *, QWidget *>::const_iterator it;
00186     for ( it = d_itemMap.begin(); it != d_itemMap.end(); ++it ) 
00187         widgets.append(it.data());
00188 #else
00189     QList<QWidget *> widgets;
00190 
00191     QMap<const QwtPlotItem *, QWidget *>::const_iterator it;
00192     for ( it = d_itemMap.begin(); it != d_itemMap.end(); ++it ) 
00193         widgets.append(it.value());
00194 #endif
00195 
00196     d_itemMap.clear();
00197     d_widgetMap.clear();
00198 
00199     for ( int i = 0; i < (int)widgets.size(); i++ )
00200         delete widgets[i];
00201 }
00202 
00203 uint QwtLegend::PrivateData::LegendMap::count() const
00204 {
00205     return d_itemMap.count();
00206 }
00207 
00208 inline const QWidget *QwtLegend::PrivateData::LegendMap::find(const QwtPlotItem *item) const
00209 {
00210     if ( !d_itemMap.contains((QwtPlotItem *)item) )
00211         return NULL;
00212 
00213     return d_itemMap[(QwtPlotItem *)item];
00214 }
00215 
00216 inline QWidget *QwtLegend::PrivateData::LegendMap::find(const QwtPlotItem *item)
00217 {
00218     if ( !d_itemMap.contains((QwtPlotItem *)item) )
00219         return NULL;
00220 
00221     return d_itemMap[(QwtPlotItem *)item];
00222 }
00223 
00224 inline const QwtPlotItem *QwtLegend::PrivateData::LegendMap::find(
00225     const QWidget *widget) const
00226 {
00227     if ( !d_widgetMap.contains((QWidget *)widget) )
00228         return NULL;
00229 
00230     return d_widgetMap[(QWidget *)widget];
00231 }
00232 
00233 inline QwtPlotItem *QwtLegend::PrivateData::LegendMap::find(
00234     const QWidget *widget)
00235 {
00236     if ( !d_widgetMap.contains((QWidget *)widget) )
00237         return NULL;
00238 
00239     return (QwtPlotItem *)d_widgetMap[(QWidget *)widget];
00240 }
00241 
00242 inline const QMap<QWidget *, const QwtPlotItem *> &
00243     QwtLegend::PrivateData::LegendMap::widgetMap() const
00244 {
00245     return d_widgetMap;
00246 } 
00247 
00248 inline QMap<QWidget *, const QwtPlotItem *> &
00249     QwtLegend::PrivateData::LegendMap::widgetMap() 
00250 {
00251     return d_widgetMap;
00252 } 
00253 
00257 QwtLegend::QwtLegend(QWidget *parent): 
00258     QFrame(parent)
00259 {
00260     setFrameStyle(NoFrame);
00261 
00262     d_data = new QwtLegend::PrivateData;
00263     d_data->itemMode = QwtLegend::ReadOnlyItem;
00264     d_data->displayPolicy = QwtLegend::AutoIdentifier;
00265     d_data->identifierMode = QwtLegendItem::ShowLine | 
00266         QwtLegendItem::ShowSymbol | QwtLegendItem::ShowText;
00267 
00268     d_data->view = new QwtLegend::PrivateData::LegendView(this);
00269     d_data->view->setFrameStyle(NoFrame);
00270 
00271     QwtDynGridLayout *layout = new QwtDynGridLayout(
00272         d_data->view->contentsWidget);
00273 #if QT_VERSION < 0x040000
00274     layout->setAutoAdd(true);
00275 #endif
00276     layout->setAlignment(Qt::AlignHCenter | Qt::AlignTop);
00277 
00278     d_data->view->contentsWidget->installEventFilter(this);
00279 }
00280 
00282 QwtLegend::~QwtLegend()
00283 {
00284     delete d_data;
00285 }
00286 
00295 void QwtLegend::setDisplayPolicy(LegendDisplayPolicy policy, int mode)
00296 {
00297     d_data->displayPolicy = policy;
00298     if (-1 != mode)
00299        d_data->identifierMode = mode;
00300 
00301     QMap<QWidget *, const QwtPlotItem *> &map = 
00302         d_data->map.widgetMap();
00303 
00304     QMap<QWidget *, const QwtPlotItem *>::iterator it;
00305     for ( it = map.begin(); it != map.end(); ++it ) 
00306     {
00307 #if QT_VERSION < 0x040000
00308         QwtPlotItem *item = (QwtPlotItem *)it.data();
00309 #else
00310         QwtPlotItem *item = (QwtPlotItem *)it.value();
00311 #endif
00312         if ( item )
00313             item->updateLegend(this);
00314     }
00315 }
00316 
00323 QwtLegend::LegendDisplayPolicy QwtLegend::displayPolicy() const 
00324 { 
00325     return d_data->displayPolicy; 
00326 }
00327 
00328 void QwtLegend::setItemMode(LegendItemMode mode)
00329 {
00330     d_data->itemMode = mode;
00331 }
00332 
00333 QwtLegend::LegendItemMode QwtLegend::itemMode() const
00334 {
00335     return d_data->itemMode;
00336 }
00337 
00345 int QwtLegend::identifierMode() const
00346 {
00347     return d_data->identifierMode;
00348 }
00349 
00354 QWidget *QwtLegend::contentsWidget() 
00355 { 
00356     return d_data->view->contentsWidget; 
00357 }
00358 
00359 QScrollBar *QwtLegend::horizontalScrollBar() const
00360 {
00361     return d_data->view->horizontalScrollBar();
00362 }
00363 
00364 QScrollBar *QwtLegend::verticalScrollBar() const
00365 {
00366     return d_data->view->horizontalScrollBar();
00367 }
00368 
00374 const QWidget *QwtLegend::contentsWidget() const 
00375 { 
00376     return d_data->view->contentsWidget; 
00377 }
00378 
00385 void QwtLegend::insert(const QwtPlotItem *plotItem, QWidget *legendItem)
00386 {
00387     if ( legendItem == NULL || plotItem == NULL )
00388         return;
00389 
00390     QWidget *contentsWidget = d_data->view->contentsWidget;
00391 
00392     if ( legendItem->parent() != contentsWidget )
00393     {
00394 #if QT_VERSION >= 0x040000
00395         legendItem->setParent(contentsWidget);
00396 #else
00397         legendItem->reparent(contentsWidget, QPoint(0, 0));
00398 #endif
00399     }
00400 
00401     legendItem->show();
00402 
00403     d_data->map.insert(plotItem, legendItem);
00404 
00405     layoutContents();
00406 
00407     if ( contentsWidget->layout() )
00408     {
00409 #if QT_VERSION >= 0x040000
00410         contentsWidget->layout()->addWidget(legendItem);
00411 #endif
00412 
00413         // set tab focus chain
00414 
00415         QWidget *w = NULL;
00416 
00417 #if QT_VERSION < 0x040000
00418         QLayoutIterator layoutIterator = 
00419             contentsWidget->layout()->iterator();
00420         for ( QLayoutItem *item = layoutIterator.current();
00421             item != 0; item = ++layoutIterator)
00422         {
00423 #else
00424         for (int i = 0; i < contentsWidget->layout()->count(); i++)
00425         {
00426             QLayoutItem *item = contentsWidget->layout()->itemAt(i);
00427 #endif
00428             if ( w && item->widget() )
00429             {
00430                 QWidget::setTabOrder(w, item->widget());
00431                 w = item->widget();
00432             }
00433         }
00434     }
00435     if ( parentWidget() && parentWidget()->layout() == NULL )
00436     {
00437        /*
00438           updateGeometry() doesn't post LayoutRequest in certain
00439           situations, like when we are hidden. But we want the
00440           parent widget notified, so it can show/hide the legend
00441           depending on its items.
00442         */
00443 #if QT_VERSION < 0x040000
00444         QApplication::postEvent(parentWidget(),
00445             new QEvent(QEvent::LayoutHint));
00446 #else
00447         QApplication::postEvent(parentWidget(),
00448             new QEvent(QEvent::LayoutRequest));
00449 #endif
00450     }
00451 }
00452 
00459 QWidget *QwtLegend::find(const QwtPlotItem *plotItem) const
00460 {
00461     return d_data->map.find(plotItem);
00462 }
00463 
00470 QwtPlotItem *QwtLegend::find(const QWidget *legendItem) const
00471 {
00472     return d_data->map.find(legendItem);
00473 }
00474 
00481 void QwtLegend::remove(const QwtPlotItem *plotItem)
00482 { 
00483     QWidget *legendItem = d_data->map.find(plotItem);
00484     d_data->map.remove(legendItem); 
00485     delete legendItem;
00486 }
00487 
00489 void QwtLegend::clear()
00490 {
00491 #if QT_VERSION < 0x040000
00492     bool doUpdate = isUpdatesEnabled();
00493 #else
00494     bool doUpdate = updatesEnabled();
00495 #endif
00496     setUpdatesEnabled(false);
00497 
00498     d_data->map.clear();
00499 
00500     setUpdatesEnabled(doUpdate);
00501     update();
00502 }
00503 
00505 QSize QwtLegend::sizeHint() const
00506 {
00507     QSize hint = d_data->view->contentsWidget->sizeHint();
00508     hint += QSize(2 * frameWidth(), 2 * frameWidth());
00509 
00510     return hint;
00511 }
00512 
00517 int QwtLegend::heightForWidth(int width) const
00518 {
00519     width -= 2 * frameWidth();
00520 
00521     int h = d_data->view->contentsWidget->heightForWidth(width);
00522 #if QT_VERSION < 0x040000
00523 
00524     // Asking the layout is the default implementation in Qt4 
00525 
00526     if ( h <= 0 ) 
00527     {
00528         QLayout *l = d_data->view->contentsWidget->layout();
00529         if ( l && l->hasHeightForWidth() )
00530             h = l->heightForWidth(width);
00531     }
00532 #endif
00533     if ( h >= 0 )
00534         h += 2 * frameWidth();
00535 
00536     return h;
00537 }
00538 
00542 void QwtLegend::layoutContents()
00543 {
00544     const QSize visibleSize = d_data->view->viewport()->size();
00545 
00546     const QLayout *l = d_data->view->contentsWidget->layout();
00547     if ( l && l->inherits("QwtDynGridLayout") )
00548     {
00549         const QwtDynGridLayout *tl = (const QwtDynGridLayout *)l;
00550 
00551         const int minW = int(tl->maxItemWidth()) + 2 * tl->margin();
00552 
00553         int w = qwtMax(visibleSize.width(), minW);
00554         int h = qwtMax(tl->heightForWidth(w), visibleSize.height());
00555 
00556         const int vpWidth = d_data->view->viewportSize(w, h).width();
00557         if ( w > vpWidth )
00558         {
00559             w = qwtMax(vpWidth, minW);
00560             h = qwtMax(tl->heightForWidth(w), visibleSize.height());
00561         }
00562 
00563         d_data->view->contentsWidget->resize(w, h);
00564 #if QT_VERSION < 0x040000
00565         d_data->view->resizeContents(w, h);
00566 #endif
00567     }
00568 }
00569 
00570 /*
00571   Filter layout related events of QwtLegend::contentsWidget().
00572 
00573   \param o Object to be filtered
00574   \param e Event
00575 */
00576 
00577 bool QwtLegend::eventFilter(QObject *o, QEvent *e)
00578 {
00579     if ( o == d_data->view->contentsWidget )
00580     {
00581         switch(e->type())
00582         {
00583             case QEvent::ChildRemoved:
00584             {   
00585                 const QChildEvent *ce = (const QChildEvent *)e;
00586                 if ( ce->child()->isWidgetType() )
00587                     d_data->map.remove((QWidget *)ce->child());
00588                 break;
00589             }
00590 #if QT_VERSION < 0x040000
00591             case QEvent::LayoutHint:
00592 #else
00593             case QEvent::LayoutRequest:
00594 #endif
00595             {
00596                 layoutContents();
00597                 break;
00598             }
00599 #if QT_VERSION < 0x040000
00600             case QEvent::Resize:
00601             {
00602                 updateGeometry();
00603                 break;
00604             }
00605 #endif
00606             default:
00607                 break;
00608         }
00609     }
00610     
00611     return QFrame::eventFilter(o, e);
00612 }
00613 
00614 
00616 bool QwtLegend::isEmpty() const
00617 {
00618     return d_data->map.count() == 0;
00619 }
00620 
00622 uint QwtLegend::itemCount() const
00623 {
00624     return d_data->map.count();
00625 }
00626 
00627 #if QT_VERSION < 0x040000
00628 QValueList<QWidget *> QwtLegend::legendItems() const
00629 #else
00630 QList<QWidget *> QwtLegend::legendItems() const
00631 #endif
00632 {
00633     const QMap<QWidget *, const QwtPlotItem *> &map = 
00634         d_data->map.widgetMap();
00635 
00636 #if QT_VERSION < 0x040000
00637     QValueList<QWidget *> list;
00638 #else
00639     QList<QWidget *> list;
00640 #endif
00641 
00642     QMap<QWidget *, const QwtPlotItem *>::const_iterator it;
00643     for ( it = map.begin(); it != map.end(); ++it ) 
00644         list += it.key();
00645 
00646     return list;
00647 }
00648 
00653 void QwtLegend::resizeEvent(QResizeEvent *e)
00654 {
00655     QFrame::resizeEvent(e);
00656     d_data->view->setGeometry(contentsRect());
00657 }

Generated on Mon Nov 6 20:32:57 2006 for Qwt User's Guide by  doxygen 1.4.6