# 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             QEvent event(QEvent::LayoutRequest);
    00119             QApplication::sendEvent(contentsWidget, &event);
    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 }