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         setFocusPolicy(Qt::NoFocus);
00110     }
00111 
00112     virtual bool viewportEvent(QEvent *e) 
00113     {
00114         bool ok = QScrollArea::viewportEvent(e);
00115 
00116         if ( e->type() == QEvent::Resize )
00117         {
00118             QApplication::sendEvent(contentsWidget, 
00119                 new QEvent(QEvent::LayoutRequest));
00120         }
00121         return ok;
00122     }
00123 
00124     QSize viewportSize(int w, int h) const
00125     {
00126         const int sbHeight = horizontalScrollBar()->sizeHint().height();
00127         const int sbWidth = verticalScrollBar()->sizeHint().width();
00128     
00129         const int cw = contentsRect().width();
00130         const int ch = contentsRect().height();
00131 
00132         int vw = cw;
00133         int vh = ch;
00134 
00135         if ( w > vw )
00136             vh -= sbHeight;
00137 
00138         if ( h > vh )
00139         {
00140             vw -= sbWidth;
00141             if ( w > vw && vh == ch )
00142                 vh -= sbHeight;
00143         }
00144         return QSize(vw, vh);
00145     }
00146 
00147     QWidget *contentsWidget;
00148 };
00149 
00150 #endif
00151 
00152 
00153 void QwtLegend::PrivateData::LegendMap::insert(
00154     const QwtPlotItem *item, QWidget *widget)
00155 {
00156     d_itemMap.insert(item, widget);
00157     d_widgetMap.insert(widget, item);
00158 }
00159 
00160 void QwtLegend::PrivateData::LegendMap::remove(const QwtPlotItem *item)
00161 {
00162     QWidget *widget = d_itemMap[item];
00163     d_itemMap.remove(item);
00164     d_widgetMap.remove(widget);
00165 }
00166 
00167 void QwtLegend::PrivateData::LegendMap::remove(QWidget *widget)
00168 {
00169     const QwtPlotItem *item = d_widgetMap[widget];
00170     d_itemMap.remove(item);
00171     d_widgetMap.remove(widget);
00172 }
00173 
00174 void QwtLegend::PrivateData::LegendMap::clear()
00175 {
00176     
00177     /*
00178        We can't delete the widgets in the following loop, because
00179        we would get ChildRemoved events, changing d_itemMap, while
00180        we are iterating.
00181      */
00182 
00183 #if QT_VERSION < 0x040000
00184     QValueList<QWidget *> widgets;
00185 
00186     QMap<const QwtPlotItem *, QWidget *>::const_iterator it;
00187     for ( it = d_itemMap.begin(); it != d_itemMap.end(); ++it ) 
00188         widgets.append(it.data());
00189 #else
00190     QList<QWidget *> widgets;
00191 
00192     QMap<const QwtPlotItem *, QWidget *>::const_iterator it;
00193     for ( it = d_itemMap.begin(); it != d_itemMap.end(); ++it ) 
00194         widgets.append(it.value());
00195 #endif
00196 
00197     d_itemMap.clear();
00198     d_widgetMap.clear();
00199 
00200     for ( int i = 0; i < (int)widgets.size(); i++ )
00201         delete widgets[i];
00202 }
00203 
00204 uint QwtLegend::PrivateData::LegendMap::count() const
00205 {
00206     return d_itemMap.count();
00207 }
00208 
00209 inline const QWidget *QwtLegend::PrivateData::LegendMap::find(const QwtPlotItem *item) const
00210 {
00211     if ( !d_itemMap.contains((QwtPlotItem *)item) )
00212         return NULL;
00213 
00214     return d_itemMap[(QwtPlotItem *)item];
00215 }
00216 
00217 inline QWidget *QwtLegend::PrivateData::LegendMap::find(const QwtPlotItem *item)
00218 {
00219     if ( !d_itemMap.contains((QwtPlotItem *)item) )
00220         return NULL;
00221 
00222     return d_itemMap[(QwtPlotItem *)item];
00223 }
00224 
00225 inline const QwtPlotItem *QwtLegend::PrivateData::LegendMap::find(
00226     const QWidget *widget) const
00227 {
00228     if ( !d_widgetMap.contains((QWidget *)widget) )
00229         return NULL;
00230 
00231     return d_widgetMap[(QWidget *)widget];
00232 }
00233 
00234 inline QwtPlotItem *QwtLegend::PrivateData::LegendMap::find(
00235     const QWidget *widget)
00236 {
00237     if ( !d_widgetMap.contains((QWidget *)widget) )
00238         return NULL;
00239 
00240     return (QwtPlotItem *)d_widgetMap[(QWidget *)widget];
00241 }
00242 
00243 inline const QMap<QWidget *, const QwtPlotItem *> &
00244     QwtLegend::PrivateData::LegendMap::widgetMap() const
00245 {
00246     return d_widgetMap;
00247 } 
00248 
00249 inline QMap<QWidget *, const QwtPlotItem *> &
00250     QwtLegend::PrivateData::LegendMap::widgetMap() 
00251 {
00252     return d_widgetMap;
00253 } 
00254 
00258 QwtLegend::QwtLegend(QWidget *parent): 
00259     QFrame(parent)
00260 {
00261     setFrameStyle(NoFrame);
00262 
00263     d_data = new QwtLegend::PrivateData;
00264     d_data->itemMode = QwtLegend::ReadOnlyItem;
00265     d_data->displayPolicy = QwtLegend::AutoIdentifier;
00266     d_data->identifierMode = QwtLegendItem::ShowLine | 
00267         QwtLegendItem::ShowSymbol | QwtLegendItem::ShowText;
00268 
00269     d_data->view = new QwtLegend::PrivateData::LegendView(this);
00270     d_data->view->setFrameStyle(NoFrame);
00271 
00272     QwtDynGridLayout *layout = new QwtDynGridLayout(
00273         d_data->view->contentsWidget);
00274 #if QT_VERSION < 0x040000
00275     layout->setAutoAdd(true);
00276 #endif
00277     layout->setAlignment(Qt::AlignHCenter | Qt::AlignTop);
00278 
00279     d_data->view->contentsWidget->installEventFilter(this);
00280 }
00281 
00283 QwtLegend::~QwtLegend()
00284 {
00285     delete d_data;
00286 }
00287 
00296 void QwtLegend::setDisplayPolicy(LegendDisplayPolicy policy, int mode)
00297 {
00298     d_data->displayPolicy = policy;
00299     if (-1 != mode)
00300        d_data->identifierMode = mode;
00301 
00302     QMap<QWidget *, const QwtPlotItem *> &map = 
00303         d_data->map.widgetMap();
00304 
00305     QMap<QWidget *, const QwtPlotItem *>::iterator it;
00306     for ( it = map.begin(); it != map.end(); ++it ) 
00307     {
00308 #if QT_VERSION < 0x040000
00309         QwtPlotItem *item = (QwtPlotItem *)it.data();
00310 #else
00311         QwtPlotItem *item = (QwtPlotItem *)it.value();
00312 #endif
00313         if ( item )
00314             item->updateLegend(this);
00315     }
00316 }
00317 
00324 QwtLegend::LegendDisplayPolicy QwtLegend::displayPolicy() const 
00325 { 
00326     return d_data->displayPolicy; 
00327 }
00328 
00329 void QwtLegend::setItemMode(LegendItemMode mode)
00330 {
00331     d_data->itemMode = mode;
00332 }
00333 
00334 QwtLegend::LegendItemMode QwtLegend::itemMode() const
00335 {
00336     return d_data->itemMode;
00337 }
00338 
00346 int QwtLegend::identifierMode() const
00347 {
00348     return d_data->identifierMode;
00349 }
00350 
00355 QWidget *QwtLegend::contentsWidget() 
00356 { 
00357     return d_data->view->contentsWidget; 
00358 }
00359 
00360 QScrollBar *QwtLegend::horizontalScrollBar() const
00361 {
00362     return d_data->view->horizontalScrollBar();
00363 }
00364 
00365 QScrollBar *QwtLegend::verticalScrollBar() const
00366 {
00367     return d_data->view->horizontalScrollBar();
00368 }
00369 
00375 const QWidget *QwtLegend::contentsWidget() const 
00376 { 
00377     return d_data->view->contentsWidget; 
00378 }
00379 
00386 void QwtLegend::insert(const QwtPlotItem *plotItem, QWidget *legendItem)
00387 {
00388     if ( legendItem == NULL || plotItem == NULL )
00389         return;
00390 
00391     QWidget *contentsWidget = d_data->view->contentsWidget;
00392 
00393     if ( legendItem->parent() != contentsWidget )
00394     {
00395 #if QT_VERSION >= 0x040000
00396         legendItem->setParent(contentsWidget);
00397 #else
00398         legendItem->reparent(contentsWidget, QPoint(0, 0));
00399 #endif
00400     }
00401 
00402     legendItem->show();
00403 
00404     d_data->map.insert(plotItem, legendItem);
00405 
00406     layoutContents();
00407 
00408     if ( contentsWidget->layout() )
00409     {
00410 #if QT_VERSION >= 0x040000
00411         contentsWidget->layout()->addWidget(legendItem);
00412 #endif
00413 
00414         // set tab focus chain
00415 
00416         QWidget *w = NULL;
00417 
00418 #if QT_VERSION < 0x040000
00419         QLayoutIterator layoutIterator = 
00420             contentsWidget->layout()->iterator();
00421         for ( QLayoutItem *item = layoutIterator.current();
00422             item != 0; item = ++layoutIterator)
00423         {
00424 #else
00425         for (int i = 0; i < contentsWidget->layout()->count(); i++)
00426         {
00427             QLayoutItem *item = contentsWidget->layout()->itemAt(i);
00428 #endif
00429             if ( w && item->widget() )
00430             {
00431                 QWidget::setTabOrder(w, item->widget());
00432                 w = item->widget();
00433             }
00434         }
00435     }
00436     if ( parentWidget() && parentWidget()->layout() == NULL )
00437     {
00438        /*
00439           updateGeometry() doesn't post LayoutRequest in certain
00440           situations, like when we are hidden. But we want the
00441           parent widget notified, so it can show/hide the legend
00442           depending on its items.
00443         */
00444 #if QT_VERSION < 0x040000
00445         QApplication::postEvent(parentWidget(),
00446             new QEvent(QEvent::LayoutHint));
00447 #else
00448         QApplication::postEvent(parentWidget(),
00449             new QEvent(QEvent::LayoutRequest));
00450 #endif
00451     }
00452 }
00453 
00460 QWidget *QwtLegend::find(const QwtPlotItem *plotItem) const
00461 {
00462     return d_data->map.find(plotItem);
00463 }
00464 
00471 QwtPlotItem *QwtLegend::find(const QWidget *legendItem) const
00472 {
00473     return d_data->map.find(legendItem);
00474 }
00475 
00482 void QwtLegend::remove(const QwtPlotItem *plotItem)
00483 { 
00484     QWidget *legendItem = d_data->map.find(plotItem);
00485     d_data->map.remove(legendItem); 
00486     delete legendItem;
00487 }
00488 
00490 void QwtLegend::clear()
00491 {
00492 #if QT_VERSION < 0x040000
00493     bool doUpdate = isUpdatesEnabled();
00494 #else
00495     bool doUpdate = updatesEnabled();
00496 #endif
00497     setUpdatesEnabled(false);
00498 
00499     d_data->map.clear();
00500 
00501     setUpdatesEnabled(doUpdate);
00502     update();
00503 }
00504 
00506 QSize QwtLegend::sizeHint() const
00507 {
00508     QSize hint = d_data->view->contentsWidget->sizeHint();
00509     hint += QSize(2 * frameWidth(), 2 * frameWidth());
00510 
00511     return hint;
00512 }
00513 
00518 int QwtLegend::heightForWidth(int width) const
00519 {
00520     width -= 2 * frameWidth();
00521 
00522     int h = d_data->view->contentsWidget->heightForWidth(width);
00523 #if QT_VERSION < 0x040000
00524 
00525     // Asking the layout is the default implementation in Qt4 
00526 
00527     if ( h <= 0 ) 
00528     {
00529         QLayout *l = d_data->view->contentsWidget->layout();
00530         if ( l && l->hasHeightForWidth() )
00531             h = l->heightForWidth(width);
00532     }
00533 #endif
00534     if ( h >= 0 )
00535         h += 2 * frameWidth();
00536 
00537     return h;
00538 }
00539 
00543 void QwtLegend::layoutContents()
00544 {
00545     const QSize visibleSize = d_data->view->viewport()->size();
00546 
00547     const QLayout *l = d_data->view->contentsWidget->layout();
00548     if ( l && l->inherits("QwtDynGridLayout") )
00549     {
00550         const QwtDynGridLayout *tl = (const QwtDynGridLayout *)l;
00551 
00552         const int minW = int(tl->maxItemWidth()) + 2 * tl->margin();
00553 
00554         int w = qwtMax(visibleSize.width(), minW);
00555         int h = qwtMax(tl->heightForWidth(w), visibleSize.height());
00556 
00557         const int vpWidth = d_data->view->viewportSize(w, h).width();
00558         if ( w > vpWidth )
00559         {
00560             w = qwtMax(vpWidth, minW);
00561             h = qwtMax(tl->heightForWidth(w), visibleSize.height());
00562         }
00563 
00564         d_data->view->contentsWidget->resize(w, h);
00565 #if QT_VERSION < 0x040000
00566         d_data->view->resizeContents(w, h);
00567 #endif
00568     }
00569 }
00570 
00571 /*
00572   Filter layout related events of QwtLegend::contentsWidget().
00573 
00574   \param o Object to be filtered
00575   \param e Event
00576 */
00577 
00578 bool QwtLegend::eventFilter(QObject *o, QEvent *e)
00579 {
00580     if ( o == d_data->view->contentsWidget )
00581     {
00582         switch(e->type())
00583         {
00584             case QEvent::ChildRemoved:
00585             {   
00586                 const QChildEvent *ce = (const QChildEvent *)e;
00587                 if ( ce->child()->isWidgetType() )
00588                     d_data->map.remove((QWidget *)ce->child());
00589                 break;
00590             }
00591 #if QT_VERSION < 0x040000
00592             case QEvent::LayoutHint:
00593 #else
00594             case QEvent::LayoutRequest:
00595 #endif
00596             {
00597                 layoutContents();
00598                 break;
00599             }
00600 #if QT_VERSION < 0x040000
00601             case QEvent::Resize:
00602             {
00603                 updateGeometry();
00604                 break;
00605             }
00606 #endif
00607             default:
00608                 break;
00609         }
00610     }
00611     
00612     return QFrame::eventFilter(o, e);
00613 }
00614 
00615 
00617 bool QwtLegend::isEmpty() const
00618 {
00619     return d_data->map.count() == 0;
00620 }
00621 
00623 uint QwtLegend::itemCount() const
00624 {
00625     return d_data->map.count();
00626 }
00627 
00628 #if QT_VERSION < 0x040000
00629 QValueList<QWidget *> QwtLegend::legendItems() const
00630 #else
00631 QList<QWidget *> QwtLegend::legendItems() const
00632 #endif
00633 {
00634     const QMap<QWidget *, const QwtPlotItem *> &map = 
00635         d_data->map.widgetMap();
00636 
00637 #if QT_VERSION < 0x040000
00638     QValueList<QWidget *> list;
00639 #else
00640     QList<QWidget *> list;
00641 #endif
00642 
00643     QMap<QWidget *, const QwtPlotItem *>::const_iterator it;
00644     for ( it = map.begin(); it != map.end(); ++it ) 
00645         list += it.key();
00646 
00647     return list;
00648 }
00649 
00654 void QwtLegend::resizeEvent(QResizeEvent *e)
00655 {
00656     QFrame::resizeEvent(e);
00657     d_data->view->setGeometry(contentsRect());
00658 }

Generated on Mon Feb 26 21:22:37 2007 for Qwt User's Guide by  doxygen 1.4.6