/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ // vim: expandtab #include #include #if QT_VERSION >= 0x040000 #include #endif #include "qwt_math.h" #include "qwt_dyngrid_layout.h" #include "qwt_legend_itemmanager.h" #include "qwt_legend_item.h" #include "qwt_legend.h" class QwtLegend::PrivateData { public: class LegendMap { public: void insert(const QwtLegendItemManager *, QWidget *); void remove(const QwtLegendItemManager *); void remove(QWidget *); void clear(); uint count() const; inline const QWidget *find(const QwtLegendItemManager *) const; inline QWidget *find(const QwtLegendItemManager *); inline const QwtLegendItemManager *find(const QWidget *) const; inline QwtLegendItemManager *find(const QWidget *); const QMap &widgetMap() const; QMap &widgetMap(); private: QMap d_widgetMap; QMap d_itemMap; }; QwtLegend::LegendItemMode itemMode; QwtLegend::LegendDisplayPolicy displayPolicy; int identifierMode; LegendMap map; class LegendView; LegendView *view; }; #if QT_VERSION < 0x040000 #include class QwtLegend::PrivateData::LegendView: public QScrollView { public: LegendView(QWidget *parent): QScrollView(parent) { setResizePolicy(Manual); viewport()->setBackgroundMode(Qt::NoBackground); // Avoid flicker contentsWidget = new QWidget(viewport()); addChild(contentsWidget); } void viewportResizeEvent(QResizeEvent *e) { QScrollView::viewportResizeEvent(e); // It's not safe to update the layout now, because // we are in an internal update of the scrollview framework. // So we delay the update by posting a LayoutHint. QApplication::postEvent(contentsWidget, new QEvent(QEvent::LayoutHint)); } QWidget *contentsWidget; }; #else // QT_VERSION >= 0x040000 #include class QwtLegend::PrivateData::LegendView: public QScrollArea { public: LegendView(QWidget *parent): QScrollArea(parent) { contentsWidget = new QWidget(this); setWidget(contentsWidget); setWidgetResizable(false); setFocusPolicy(Qt::NoFocus); } virtual bool viewportEvent(QEvent *e) { bool ok = QScrollArea::viewportEvent(e); if ( e->type() == QEvent::Resize ) { QEvent event(QEvent::LayoutRequest); QApplication::sendEvent(contentsWidget, &event); } return ok; } QSize viewportSize(int w, int h) const { const int sbHeight = horizontalScrollBar()->sizeHint().height(); const int sbWidth = verticalScrollBar()->sizeHint().width(); const int cw = contentsRect().width(); const int ch = contentsRect().height(); int vw = cw; int vh = ch; if ( w > vw ) vh -= sbHeight; if ( h > vh ) { vw -= sbWidth; if ( w > vw && vh == ch ) vh -= sbHeight; } return QSize(vw, vh); } QWidget *contentsWidget; }; #endif void QwtLegend::PrivateData::LegendMap::insert( const QwtLegendItemManager *item, QWidget *widget) { d_itemMap.insert(item, widget); d_widgetMap.insert(widget, item); } void QwtLegend::PrivateData::LegendMap::remove(const QwtLegendItemManager *item) { QWidget *widget = d_itemMap[item]; d_itemMap.remove(item); d_widgetMap.remove(widget); } void QwtLegend::PrivateData::LegendMap::remove(QWidget *widget) { const QwtLegendItemManager *item = d_widgetMap[widget]; d_itemMap.remove(item); d_widgetMap.remove(widget); } void QwtLegend::PrivateData::LegendMap::clear() { /* We can't delete the widgets in the following loop, because we would get ChildRemoved events, changing d_itemMap, while we are iterating. */ #if QT_VERSION < 0x040000 QValueList widgets; QMap::const_iterator it; for ( it = d_itemMap.begin(); it != d_itemMap.end(); ++it ) widgets.append(it.data()); #else QList widgets; QMap::const_iterator it; for ( it = d_itemMap.begin(); it != d_itemMap.end(); ++it ) widgets.append(it.value()); #endif d_itemMap.clear(); d_widgetMap.clear(); for ( int i = 0; i < (int)widgets.size(); i++ ) delete widgets[i]; } uint QwtLegend::PrivateData::LegendMap::count() const { return d_itemMap.count(); } inline const QWidget *QwtLegend::PrivateData::LegendMap::find(const QwtLegendItemManager *item) const { if ( !d_itemMap.contains((QwtLegendItemManager *)item) ) return NULL; return d_itemMap[(QwtLegendItemManager *)item]; } inline QWidget *QwtLegend::PrivateData::LegendMap::find(const QwtLegendItemManager *item) { if ( !d_itemMap.contains((QwtLegendItemManager *)item) ) return NULL; return d_itemMap[(QwtLegendItemManager *)item]; } inline const QwtLegendItemManager *QwtLegend::PrivateData::LegendMap::find( const QWidget *widget) const { if ( !d_widgetMap.contains((QWidget *)widget) ) return NULL; return d_widgetMap[(QWidget *)widget]; } inline QwtLegendItemManager *QwtLegend::PrivateData::LegendMap::find( const QWidget *widget) { if ( !d_widgetMap.contains((QWidget *)widget) ) return NULL; return (QwtLegendItemManager *)d_widgetMap[(QWidget *)widget]; } inline const QMap & QwtLegend::PrivateData::LegendMap::widgetMap() const { return d_widgetMap; } inline QMap & QwtLegend::PrivateData::LegendMap::widgetMap() { return d_widgetMap; } /*! \param parent Parent widget */ QwtLegend::QwtLegend(QWidget *parent): QFrame(parent) { setFrameStyle(NoFrame); d_data = new QwtLegend::PrivateData; d_data->itemMode = QwtLegend::ReadOnlyItem; d_data->displayPolicy = QwtLegend::AutoIdentifier; d_data->identifierMode = QwtLegendItem::ShowLine | QwtLegendItem::ShowSymbol | QwtLegendItem::ShowText; d_data->view = new QwtLegend::PrivateData::LegendView(this); d_data->view->setFrameStyle(NoFrame); QwtDynGridLayout *layout = new QwtDynGridLayout( d_data->view->contentsWidget); #if QT_VERSION < 0x040000 layout->setAutoAdd(true); #endif layout->setAlignment(Qt::AlignHCenter | Qt::AlignTop); d_data->view->contentsWidget->installEventFilter(this); } //! Destructor QwtLegend::~QwtLegend() { delete d_data; } /*! Set the legend display policy to: \param policy Legend display policy \param mode Identifier mode (or'd ShowLine, ShowSymbol, ShowText) \sa displayPolicy, LegendDisplayPolicy */ void QwtLegend::setDisplayPolicy(LegendDisplayPolicy policy, int mode) { d_data->displayPolicy = policy; if (-1 != mode) d_data->identifierMode = mode; QMap &map = d_data->map.widgetMap(); QMap::iterator it; for ( it = map.begin(); it != map.end(); ++it ) { #if QT_VERSION < 0x040000 QwtLegendItemManager *item = (QwtLegendItemManager *)it.data(); #else QwtLegendItemManager *item = (QwtLegendItemManager *)it.value(); #endif if ( item ) item->updateLegend(this); } } /*! \return the legend display policy. Default is LegendDisplayPolicy::Auto. \sa setDisplayPolicy, LegendDisplayPolicy */ QwtLegend::LegendDisplayPolicy QwtLegend::displayPolicy() const { return d_data->displayPolicy; } void QwtLegend::setItemMode(LegendItemMode mode) { d_data->itemMode = mode; } QwtLegend::LegendItemMode QwtLegend::itemMode() const { return d_data->itemMode; } /*! \return the IdentifierMode to be used in combination with LegendDisplayPolicy::Fixed. Default is ShowLine | ShowSymbol | ShowText. */ int QwtLegend::identifierMode() const { return d_data->identifierMode; } /*! The contents widget is the only child of the viewport() and the parent widget of all legend items. */ QWidget *QwtLegend::contentsWidget() { return d_data->view->contentsWidget; } QScrollBar *QwtLegend::horizontalScrollBar() const { return d_data->view->horizontalScrollBar(); } QScrollBar *QwtLegend::verticalScrollBar() const { return d_data->view->verticalScrollBar(); } /*! The contents widget is the only child of the viewport() and the parent widget of all legend items. */ const QWidget *QwtLegend::contentsWidget() const { return d_data->view->contentsWidget; } /*! Insert a new item for a plot item \param plotItem Plot item \param legendItem New legend item \note The parent of item will be changed to QwtLegend::contentsWidget() */ void QwtLegend::insert(const QwtLegendItemManager *plotItem, QWidget *legendItem) { if ( legendItem == NULL || plotItem == NULL ) return; QWidget *contentsWidget = d_data->view->contentsWidget; if ( legendItem->parent() != contentsWidget ) { #if QT_VERSION >= 0x040000 legendItem->setParent(contentsWidget); #else legendItem->reparent(contentsWidget, QPoint(0, 0)); #endif } legendItem->show(); d_data->map.insert(plotItem, legendItem); layoutContents(); if ( contentsWidget->layout() ) { #if QT_VERSION >= 0x040000 contentsWidget->layout()->addWidget(legendItem); #endif // set tab focus chain QWidget *w = NULL; #if QT_VERSION < 0x040000 QLayoutIterator layoutIterator = contentsWidget->layout()->iterator(); for ( QLayoutItem *item = layoutIterator.current(); item != 0; item = ++layoutIterator) { #else for (int i = 0; i < contentsWidget->layout()->count(); i++) { QLayoutItem *item = contentsWidget->layout()->itemAt(i); #endif if ( w && item->widget() ) { QWidget::setTabOrder(w, item->widget()); w = item->widget(); } } } if ( parentWidget() && parentWidget()->layout() == NULL ) { /* updateGeometry() doesn't post LayoutRequest in certain situations, like when we are hidden. But we want the parent widget notified, so it can show/hide the legend depending on its items. */ #if QT_VERSION < 0x040000 QApplication::postEvent(parentWidget(), new QEvent(QEvent::LayoutHint)); #else QApplication::postEvent(parentWidget(), new QEvent(QEvent::LayoutRequest)); #endif } } /*! Find the widget that represents a plot item \param plotItem Plot item \return Widget on the legend, or NULL */ QWidget *QwtLegend::find(const QwtLegendItemManager *plotItem) const { return d_data->map.find(plotItem); } /*! Find the widget that represents a plot item \param plotItem Plot item \return Widget on the legend, or NULL */ QwtLegendItemManager *QwtLegend::find(const QWidget *legendItem) const { return d_data->map.find(legendItem); } /*! Find the corresponding item for a plotItem and remove it from the item list. \param plotItem Plot item */ void QwtLegend::remove(const QwtLegendItemManager *plotItem) { QWidget *legendItem = d_data->map.find(plotItem); d_data->map.remove(legendItem); delete legendItem; } //! Remove all items. void QwtLegend::clear() { #if QT_VERSION < 0x040000 bool doUpdate = isUpdatesEnabled(); #else bool doUpdate = updatesEnabled(); #endif setUpdatesEnabled(false); d_data->map.clear(); setUpdatesEnabled(doUpdate); update(); } //! Return a size hint. QSize QwtLegend::sizeHint() const { QSize hint = d_data->view->contentsWidget->sizeHint(); hint += QSize(2 * frameWidth(), 2 * frameWidth()); return hint; } /*! \return The preferred height, for the width w. \param width Width */ int QwtLegend::heightForWidth(int width) const { width -= 2 * frameWidth(); int h = d_data->view->contentsWidget->heightForWidth(width); #if QT_VERSION < 0x040000 // Asking the layout is the default implementation in Qt4 if ( h <= 0 ) { QLayout *l = d_data->view->contentsWidget->layout(); if ( l && l->hasHeightForWidth() ) h = l->heightForWidth(width); } #endif if ( h >= 0 ) h += 2 * frameWidth(); return h; } /*! Adjust contents widget and item layout to the size of the viewport(). */ void QwtLegend::layoutContents() { const QSize visibleSize = d_data->view->viewport()->size(); const QLayout *l = d_data->view->contentsWidget->layout(); if ( l && l->inherits("QwtDynGridLayout") ) { const QwtDynGridLayout *tl = (const QwtDynGridLayout *)l; const int minW = int(tl->maxItemWidth()) + 2 * tl->margin(); int w = qwtMax(visibleSize.width(), minW); int h = qwtMax(tl->heightForWidth(w), visibleSize.height()); const int vpWidth = d_data->view->viewportSize(w, h).width(); if ( w > vpWidth ) { w = qwtMax(vpWidth, minW); h = qwtMax(tl->heightForWidth(w), visibleSize.height()); } d_data->view->contentsWidget->resize(w, h); #if QT_VERSION < 0x040000 d_data->view->resizeContents(w, h); #endif } } /* Filter layout related events of QwtLegend::contentsWidget(). \param o Object to be filtered \param e Event */ bool QwtLegend::eventFilter(QObject *o, QEvent *e) { if ( o == d_data->view->contentsWidget ) { switch(e->type()) { case QEvent::ChildRemoved: { const QChildEvent *ce = (const QChildEvent *)e; if ( ce->child()->isWidgetType() ) d_data->map.remove((QWidget *)ce->child()); break; } #if QT_VERSION < 0x040000 case QEvent::LayoutHint: #else case QEvent::LayoutRequest: #endif { layoutContents(); break; } #if QT_VERSION < 0x040000 case QEvent::Resize: { updateGeometry(); break; } #endif default: break; } } return QFrame::eventFilter(o, e); } //! Return true, if there are no legend items. bool QwtLegend::isEmpty() const { return d_data->map.count() == 0; } //! Return the number of legend items. uint QwtLegend::itemCount() const { return d_data->map.count(); } #if QT_VERSION < 0x040000 QValueList QwtLegend::legendItems() const #else QList QwtLegend::legendItems() const #endif { const QMap &map = d_data->map.widgetMap(); #if QT_VERSION < 0x040000 QValueList list; #else QList list; #endif QMap::const_iterator it; for ( it = map.begin(); it != map.end(); ++it ) list += it.key(); return list; } /*! Resize event \param e Event */ void QwtLegend::resizeEvent(QResizeEvent *e) { QFrame::resizeEvent(e); d_data->view->setGeometry(contentsRect()); }