Main Page | Class Hierarchy | Alphabetical List | Compound List | File List | Compound Members | File Members | Related Pages

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 // vim: expandtab
00011 
00012 #include <qapplication.h> 
00013 #include <qpainter.h>
00014 #include <qpicture.h>
00015 #include <qbitmap.h>
00016 #include <qstyle.h>
00017 #include "qwt_legend.h"
00018 #include "qwt_painter.h"
00019 #include "qwt_dyngrid_layout.h"
00020 
00021 static const int IdentifierWidth = 8;
00029 QwtLegendButton::QwtLegendButton(
00030         const QwtSymbol &symbol, const QPen &pen, const QString &text,
00031         QWidget *parent, const char *name): 
00032     QPushButton(text, parent, name),
00033     d_identifierMode(QwtLegendButton::ShowLine
00034              | QwtLegendButton::ShowText), 
00035     d_symbol(symbol), 
00036     d_curvePen(pen)
00037 {
00038     setFlat(TRUE);
00039     updateIconset();
00040 }
00041 
00046 QwtLegendButton::QwtLegendButton(QWidget *parent, const char *name): 
00047     QPushButton(parent, name),
00048     d_identifierMode(QwtLegendButton::ShowLine
00049              | QwtLegendButton::ShowText), 
00050     d_curvePen(Qt::NoPen)
00051 {
00052     setFlat(TRUE);
00053     updateIconset();
00054 }
00055 
00063 void QwtLegendButton::setIdentifierMode(int mode) 
00064 {
00065     if ( mode != d_identifierMode )
00066     {
00067         d_identifierMode = mode;
00068         updateIconset();
00069     }
00070 }
00071 
00078 void QwtLegendButton::setSymbol(const QwtSymbol &symbol) 
00079 {
00080     if ( symbol != d_symbol )
00081     {
00082         d_symbol = symbol;
00083         updateIconset();
00084     }
00085 }
00086 
00093 void QwtLegendButton::setCurvePen(const QPen &pen) 
00094 {
00095     if ( pen != d_curvePen )
00096     {
00097         d_curvePen = pen;
00098         updateIconset();
00099     }
00100 }
00101 
00105 void QwtLegendButton::updateIconset()
00106 {
00107     const QFontMetrics fm(font());
00108 
00109     QPixmap pm(IdentifierWidth, fm.height());
00110     pm.fill(this, 0, 0);
00111 
00112     QPainter p(&pm);
00113     drawIdentifier(&p, QRect(0, 0, pm.width(), pm.height()) );
00114     p.end();
00115 
00116     pm.setMask(pm.createHeuristicMask());
00117 
00118     setIconSet(QIconSet(pm));
00119 }
00120 
00126 void QwtLegendButton::drawIdentifier(
00127     QPainter *painter, const QRect &rect) const
00128 {
00129     if ( rect.isEmpty() )
00130         return;
00131 
00132     if ( (d_identifierMode & ShowLine ) && (d_curvePen.style() != Qt::NoPen) )
00133     {
00134         QRect scaledRect = QwtPainter::scale(rect, painter);
00135 
00136         painter->save();
00137         painter->setPen(d_curvePen);
00138         painter->drawLine(scaledRect.left(), scaledRect.center().y(), 
00139             scaledRect.right(), scaledRect.center().y());
00140         painter->restore();
00141     }
00142 
00143     if ( (d_identifierMode & ShowSymbol) 
00144         && (d_symbol.style() != QwtSymbol::None) )
00145     {
00146         QRect symbolRect;
00147         symbolRect.setSize(d_symbol.size().boundedTo(rect.size()));
00148         symbolRect.moveCenter(rect.center());
00149 
00150         painter->save();
00151         d_symbol.draw(painter, symbolRect);
00152         painter->restore();
00153     }
00154 }
00155 
00164 void QwtLegendButton::drawContents(QPainter *painter, 
00165     const QRect &rect) const
00166 {
00167     const QFontMetrics fm(font());
00168 
00169     QPixmap pm(IdentifierWidth, fm.height());
00170 
00171     QRect identifierRect(rect.x() + 2, rect.y(), 
00172         IdentifierWidth, fm.height());
00173     identifierRect.moveCenter(QPoint(identifierRect.center().x(), 
00174         rect.center().y()));
00175 
00176     drawIdentifier(painter, identifierRect);
00177 
00178     QRect textRect = rect;
00179     textRect.setX(identifierRect.right() + 4);
00180     QwtPainter::drawText(painter, textRect, 
00181         AlignLeft | AlignVCenter, text());
00182 }
00183 
00184 class PaintFilter: public QPicture
00185 {
00186     // Filter drawButtonLabel paint commands, and change
00187     // them to align the text 4 pixels left to the icon.
00188 
00189 public:
00190     PaintFilter():
00191         d_textStart(0) 
00192     {
00193     }
00194 
00195     virtual bool cmd(int c, QPainter *p, QPDevCmdParam *param)
00196     {
00197         switch(c)
00198         {
00199             case PdcDrawPixmap:
00200             {
00201                 // we save the position of the pixmap.
00202 #if QT_VERSION < 300
00203                 d_textStart = param[0].point->x() 
00204                     + param[1].pixmap->width();
00205 #else
00206                 d_textStart = param[0].rect->right();
00207 #endif
00208                 d_textStart += 4;
00209                 break;
00210             }
00211             case PdcDrawTextFormatted:
00212             case PdcDrawText2Formatted:
00213             {
00214                 if ( d_textStart > 0 )
00215                 {
00216                     // we align the label 4 pixels left of
00217                     // the pixmap
00218 
00219                     QRect r = *(param[0].rect);
00220                     r.moveBy(d_textStart - r.x(), 0);
00221 
00222                     QPDevCmdParam changed[3];
00223                     changed[0].rect = &r;
00224                     changed[1].ival = param[1].ival;
00225                     changed[1].ival &= ~Qt::AlignHCenter;
00226                     changed[1].ival |= Qt::AlignLeft;
00227 
00228                     changed[2].str = param[2].str;
00229 
00230                     return QPicture::cmd(c, p, changed);
00231                 }
00232                 break;
00233             }
00234         }
00235         return QPicture::cmd(c, p, param);
00236     }
00237 private:
00238     int d_textStart;
00239 };
00240 
00249 void QwtLegendButton::drawButtonLabel(QPainter *painter)
00250 {
00251     // Most (maybe all ?) styles align the button text
00252     // centered. We need to have it left to the
00253     // icon. Unfortunately there is no easy way to do it,
00254     // so we record all paint commands to a QPicture,
00255     // filter the drawText command and replace the
00256     // alignment. 
00257 
00258     // In opposite to a regular QPushButton, QwtLegendButton
00259     // has more a character of an info label than a control element. 
00260     // So even in disabled state, we like to see the active palette.
00261 
00262     PaintFilter filter;
00263     
00264     bool disabled = (bool)testWState(WState_Disabled);
00265     clearWState(WState_Disabled);
00266 
00267     QPainter picPainter(&filter);
00268     QPushButton::drawButtonLabel(&picPainter);
00269     picPainter.end();
00270 
00271     if ( disabled )
00272         setWState(WState_Disabled); // reset state
00273 
00274     filter.play(painter); // paint the filtered paint commands
00275 }
00276 
00281 QSize QwtLegendButton::sizeHint() const
00282 {
00283     return minimumSizeHint();
00284 }
00285 
00290 QSize QwtLegendButton::minimumSizeHint() const
00291 {
00292     const QFontMetrics fm(font());
00293 
00294     int h = QPushButton::sizeHint().height();
00295 
00296     int w = iconSet()->pixmap(
00297         QIconSet::Small, QIconSet::Normal).width();
00298     w += 4;
00299     w += fm.size(ShowPrefix, text()).width();
00300 
00301 #if QT_VERSION < 300
00302     w += 2 * (style().buttonMargin() - 1);
00303     if ( isDefault() || autoDefault() ) 
00304         w += 2 * style().buttonDefaultIndicatorWidth();
00305 #else
00306     w = style().sizeFromContents(QStyle::CT_PushButton, 
00307         this, QSize(w, fm.height() + 2)).width();
00308 #endif
00309 
00310     return QSize(w, h).expandedTo(QApplication::globalStrut());
00311 }
00312 
00317 QwtLegend::QwtLegend(QWidget *parent, const char *name): 
00318     QScrollView(parent, name),
00319     d_displayPolicy(QwtLegend::Auto),
00320     d_identifierMode(QwtLegendButton::ShowLine
00321              | QwtLegendButton::ShowSymbol
00322              | QwtLegendButton::ShowText)
00323 {
00324     setFrameStyle(NoFrame);
00325     setResizePolicy(Manual);
00326 
00327     viewport()->setBackgroundMode(QWidget::NoBackground); // Avoid flicker
00328 
00329     d_contentsWidget = new QWidget(viewport());
00330     d_contentsWidget->installEventFilter(this);
00331 
00332     QwtDynGridLayout *layout = new QwtDynGridLayout(d_contentsWidget);
00333     layout->setAlignment(Qt::AlignHCenter | Qt::AlignTop);
00334     layout->setAutoAdd(TRUE);
00335 
00336     addChild(d_contentsWidget);
00337 }
00338 
00343 void QwtLegend::setDisplayPolicy(int policy, int mode)
00344 {
00345     d_displayPolicy = policy;
00346     if (-1 != mode)
00347        d_identifierMode = mode;
00348 }
00349 
00357 void QwtLegend::insertItem(QWidget *item, long key)
00358 {
00359     if ( item == NULL || key < 0 )
00360         return;
00361 
00362     if ( item->parent() != d_contentsWidget )
00363         item->reparent(d_contentsWidget, QPoint(0, 0));
00364 
00365     item->show();
00366 
00367     if ( d_items.count() > d_items.size() - 5 )
00368         d_items.resize(d_items.count() + 5);
00369 
00370     d_items.insert(key, item);
00371 
00372     layoutContents();
00373 
00374     QWidget *w = 0;
00375 
00376     if ( d_contentsWidget->layout() )
00377     {
00378         // set tab focus chain
00379 
00380         QLayoutIterator layoutIterator = 
00381             d_contentsWidget->layout()->iterator();
00382 
00383         for ( QLayoutItem *item = layoutIterator.current();
00384             item != 0; item = ++layoutIterator)
00385         {
00386             if ( w && item->widget() )
00387                 QWidget::setTabOrder(w, item->widget());
00388 
00389             w = item->widget();
00390         }
00391     }
00392 }
00393 
00399 long QwtLegend::key(const QWidget *item) const
00400 {
00401     QIntDictIterator<QWidget> it(d_items);
00402     for ( const QWidget *w = it.toFirst(); w != 0; w = ++it)
00403     {
00404         if ( w == item )
00405             return it.currentKey();
00406     }
00407     return -1;
00408 }
00409 
00411 void QwtLegend::clear()
00412 {
00413     // We can't delete the items while we are running
00414     // through the iterator. So we collect them in
00415     // a list first.
00416 
00417     QValueList<QWidget *> clearList;
00418     
00419     QIntDictIterator<QWidget> it(d_items);
00420     for ( QWidget *item = it.toFirst(); item != 0; item = ++it)
00421         clearList += item;
00422 
00423     for ( uint i = 0; i < clearList.count(); i++ )
00424         delete clearList[i];
00425 
00426 #if QT_VERSION < 232
00427     // In Qt 2.3.0 the ChildRemoved events are not sent, before the
00428     // first show of the legend. Thus the deleted items are not cleared
00429     // from the list in QwtLegend::eventFilter. In most cases
00430     // the following clear is useless, but is is safe to do so.
00431     
00432     d_items.clear();
00433 #endif
00434 }
00435 
00437 QIntDictIterator<QWidget> QwtLegend::itemIterator() const
00438 {
00439     return QIntDictIterator<QWidget>(d_items);
00440 }
00441 
00443 QSize QwtLegend::sizeHint() const
00444 {
00445     QSize hint = d_contentsWidget->sizeHint();
00446     hint += QSize(2 * frameWidth(), 2 * frameWidth());
00447     
00448     return hint;
00449 }
00450 
00454 int QwtLegend::heightForWidth(int w) const
00455 {
00456     w -= 2 * frameWidth();
00457 
00458     int h = d_contentsWidget->heightForWidth(w);
00459     if ( h <= 0 ) // not implemented, we try the layout
00460     {
00461         QLayout *l = d_contentsWidget->layout();
00462         if ( l && l->hasHeightForWidth() )
00463         {
00464             h = l->heightForWidth(w);
00465             h += 2 * frameWidth();
00466         }
00467     }
00468 
00469     return h;
00470 }
00471 
00475 void QwtLegend::layoutContents()
00476 {
00477     const QSize visibleSize = viewport()->size();
00478 
00479     const QLayout *l = d_contentsWidget->layout();
00480     if ( l && l->inherits("QwtDynGridLayout") )
00481     {
00482         const QwtDynGridLayout *tl = (const QwtDynGridLayout *)l;
00483 
00484         const int minW = int(tl->maxItemWidth()) + 2 * tl->margin();
00485 
00486         int w = QMAX(visibleSize.width(), minW);
00487         int h = QMAX(tl->heightForWidth(w), visibleSize.height());
00488 
00489         const int vpWidth = viewportSize(w, h).width();
00490 
00491         if ( w > vpWidth )
00492         {
00493             w = QMAX(vpWidth, minW);
00494             h = QMAX(tl->heightForWidth(w), visibleSize.height());
00495         }
00496 
00497         d_contentsWidget->resize(w, h);
00498         resizeContents(w, h);
00499     }
00500 }
00501 
00502 /*
00503   Filter QEvent::ChildRemoved, and QEvent::LayoutHint for
00504   QwtLegend::contentsWidget().
00505 */
00506 
00507 bool QwtLegend::eventFilter(QObject *o, QEvent *e)
00508 {
00509     if ( o == d_contentsWidget )
00510     {
00511         switch(e->type())
00512         {
00513             case QEvent::ChildRemoved:
00514             {   
00515                 const QChildEvent *ce = (const QChildEvent *)e;
00516                 if ( ce->child()->isWidgetType() )
00517                     (void)takeItem(key((QWidget *)ce->child()));
00518                 break;
00519             }
00520             case QEvent::LayoutHint:
00521             {
00522                 layoutContents();
00523                 break;
00524             }
00525             default:
00526                 break;
00527         }
00528     }
00529     
00530     return QScrollView::eventFilter(o, e);
00531 }
00532 
00533 /*
00534   Resize the viewport() and post a QEvent::LayoutHint to
00535   QwtLegend::contentsWidget() to update the layout.
00536 */
00537 void QwtLegend::viewportResizeEvent(QResizeEvent *e)
00538 {
00539     QScrollView::viewportResizeEvent(e);
00540 
00541     // It's not safe to update the layout now, because
00542     // we are in an internal update of the scrollview framework.
00543     // So we delay the update by posting a LayoutHint.
00544 
00545     QApplication::postEvent(d_contentsWidget, new QEvent(QEvent::LayoutHint));
00546 }

Generated on Fri Nov 7 14:11:45 2003 for Qwt Developer's Guide by doxygen 1.3.2