| 1 | /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
 | 
|---|
| 2 |  * Qwt Widget Library
 | 
|---|
| 3 |  * Copyright (C) 1997   Josef Wilgen
 | 
|---|
| 4 |  * Copyright (C) 2002   Uwe Rathmann
 | 
|---|
| 5 |  *
 | 
|---|
| 6 |  * This library is free software; you can redistribute it and/or
 | 
|---|
| 7 |  * modify it under the terms of the Qwt License, Version 1.0
 | 
|---|
| 8 |  *****************************************************************************/
 | 
|---|
| 9 | 
 | 
|---|
| 10 | #include "qwt_legend.h"
 | 
|---|
| 11 | #include "qwt_legend_label.h"
 | 
|---|
| 12 | #include "qwt_dyngrid_layout.h"
 | 
|---|
| 13 | #include "qwt_math.h"
 | 
|---|
| 14 | #include "qwt_plot_item.h"
 | 
|---|
| 15 | #include "qwt_painter.h"
 | 
|---|
| 16 | #include <qapplication.h>
 | 
|---|
| 17 | #include <qscrollbar.h>
 | 
|---|
| 18 | #include <qscrollarea.h>
 | 
|---|
| 19 | #include <qpainter.h>
 | 
|---|
| 20 | #include <qstyle.h>
 | 
|---|
| 21 | #include <qstyleoption.h>
 | 
|---|
| 22 | 
 | 
|---|
| 23 | class QwtLegendMap
 | 
|---|
| 24 | {
 | 
|---|
| 25 | public:
 | 
|---|
| 26 |     inline bool isEmpty() const { return d_entries.isEmpty(); }
 | 
|---|
| 27 | 
 | 
|---|
| 28 |     void insert( const QVariant &, const QList<QWidget *> & );
 | 
|---|
| 29 |     void remove( const QVariant & );
 | 
|---|
| 30 | 
 | 
|---|
| 31 |     void removeWidget( const QWidget * );
 | 
|---|
| 32 | 
 | 
|---|
| 33 |     QList<QWidget *> legendWidgets( const QVariant & ) const;
 | 
|---|
| 34 |     QVariant itemInfo( const QWidget * ) const;
 | 
|---|
| 35 | 
 | 
|---|
| 36 | private:
 | 
|---|
| 37 |     // we don't know anything about itemInfo and therefore don't have
 | 
|---|
| 38 |     // any key that can be used for a map or hashtab.
 | 
|---|
| 39 |     // But a simple linear list is o.k. here, as we will never have
 | 
|---|
| 40 |     // more than a few entries.
 | 
|---|
| 41 | 
 | 
|---|
| 42 |     class Entry
 | 
|---|
| 43 |     {
 | 
|---|
| 44 |     public:
 | 
|---|
| 45 |         QVariant itemInfo;
 | 
|---|
| 46 |         QList<QWidget *> widgets;
 | 
|---|
| 47 |     };
 | 
|---|
| 48 | 
 | 
|---|
| 49 |     QList< Entry > d_entries;
 | 
|---|
| 50 | };
 | 
|---|
| 51 | 
 | 
|---|
| 52 | void QwtLegendMap::insert( const QVariant &itemInfo,
 | 
|---|
| 53 |     const QList<QWidget *> &widgets )
 | 
|---|
| 54 | {
 | 
|---|
| 55 |     for ( int i = 0; i < d_entries.size(); i++ )
 | 
|---|
| 56 |     {
 | 
|---|
| 57 |         Entry &entry = d_entries[i];
 | 
|---|
| 58 |         if ( entry.itemInfo == itemInfo )
 | 
|---|
| 59 |         {
 | 
|---|
| 60 |             entry.widgets = widgets;
 | 
|---|
| 61 |             return;
 | 
|---|
| 62 |         }
 | 
|---|
| 63 |     }
 | 
|---|
| 64 | 
 | 
|---|
| 65 |     Entry newEntry;
 | 
|---|
| 66 |     newEntry.itemInfo = itemInfo;
 | 
|---|
| 67 |     newEntry.widgets = widgets;
 | 
|---|
| 68 | 
 | 
|---|
| 69 |     d_entries += newEntry;
 | 
|---|
| 70 | }
 | 
|---|
| 71 | 
 | 
|---|
| 72 | void QwtLegendMap::remove( const QVariant &itemInfo )
 | 
|---|
| 73 | {
 | 
|---|
| 74 |     for ( int i = 0; i < d_entries.size(); i++ )
 | 
|---|
| 75 |     {
 | 
|---|
| 76 |         Entry &entry = d_entries[i];
 | 
|---|
| 77 |         if ( entry.itemInfo == itemInfo )
 | 
|---|
| 78 |         {
 | 
|---|
| 79 |             d_entries.removeAt( i );
 | 
|---|
| 80 |             return;
 | 
|---|
| 81 |         }
 | 
|---|
| 82 |     }
 | 
|---|
| 83 | }
 | 
|---|
| 84 | 
 | 
|---|
| 85 | void QwtLegendMap::removeWidget( const QWidget *widget )
 | 
|---|
| 86 | {
 | 
|---|
| 87 |     QWidget *w = const_cast<QWidget *>( widget );
 | 
|---|
| 88 | 
 | 
|---|
| 89 |     for ( int i = 0; i < d_entries.size(); i++ )
 | 
|---|
| 90 |         d_entries[ i ].widgets.removeAll( w );
 | 
|---|
| 91 | }
 | 
|---|
| 92 | 
 | 
|---|
| 93 | QVariant QwtLegendMap::itemInfo( const QWidget *widget ) const
 | 
|---|
| 94 | {
 | 
|---|
| 95 |     if ( widget != NULL )
 | 
|---|
| 96 |     {
 | 
|---|
| 97 |         QWidget *w = const_cast<QWidget *>( widget );
 | 
|---|
| 98 | 
 | 
|---|
| 99 |         for ( int i = 0; i < d_entries.size(); i++ )
 | 
|---|
| 100 |         {
 | 
|---|
| 101 |             const Entry &entry = d_entries[i];
 | 
|---|
| 102 |             if ( entry.widgets.indexOf( w ) >= 0 )
 | 
|---|
| 103 |                 return entry.itemInfo;
 | 
|---|
| 104 |         }
 | 
|---|
| 105 |     }
 | 
|---|
| 106 | 
 | 
|---|
| 107 |     return QVariant();
 | 
|---|
| 108 | }
 | 
|---|
| 109 | 
 | 
|---|
| 110 | QList<QWidget *> QwtLegendMap::legendWidgets( const QVariant &itemInfo ) const
 | 
|---|
| 111 | {
 | 
|---|
| 112 |     if ( itemInfo.isValid() )
 | 
|---|
| 113 |     {
 | 
|---|
| 114 |         for ( int i = 0; i < d_entries.size(); i++ )
 | 
|---|
| 115 |         {
 | 
|---|
| 116 |             const Entry &entry = d_entries[i];
 | 
|---|
| 117 |             if ( entry.itemInfo == itemInfo )
 | 
|---|
| 118 |                 return entry.widgets;
 | 
|---|
| 119 |         }
 | 
|---|
| 120 |     }
 | 
|---|
| 121 | 
 | 
|---|
| 122 |     return QList<QWidget *>();
 | 
|---|
| 123 | }
 | 
|---|
| 124 | 
 | 
|---|
| 125 | class QwtLegend::PrivateData
 | 
|---|
| 126 | {
 | 
|---|
| 127 | public:
 | 
|---|
| 128 |     PrivateData():
 | 
|---|
| 129 |         itemMode( QwtLegendData::ReadOnly ),
 | 
|---|
| 130 |         view( NULL )
 | 
|---|
| 131 |     {
 | 
|---|
| 132 |     }
 | 
|---|
| 133 | 
 | 
|---|
| 134 |     QwtLegendData::Mode itemMode;
 | 
|---|
| 135 |     QwtLegendMap itemMap;
 | 
|---|
| 136 | 
 | 
|---|
| 137 |     class LegendView;
 | 
|---|
| 138 |     LegendView *view;
 | 
|---|
| 139 | };
 | 
|---|
| 140 | 
 | 
|---|
| 141 | class QwtLegend::PrivateData::LegendView: public QScrollArea
 | 
|---|
| 142 | {
 | 
|---|
| 143 | public:
 | 
|---|
| 144 |     explicit LegendView( QWidget *parent ):
 | 
|---|
| 145 |         QScrollArea( parent )
 | 
|---|
| 146 |     {
 | 
|---|
| 147 |         contentsWidget = new QWidget( this );
 | 
|---|
| 148 |         contentsWidget->setObjectName( "QwtLegendViewContents" );
 | 
|---|
| 149 | 
 | 
|---|
| 150 |         setWidget( contentsWidget );
 | 
|---|
| 151 |         setWidgetResizable( false );
 | 
|---|
| 152 | 
 | 
|---|
| 153 |         viewport()->setObjectName( "QwtLegendViewport" );
 | 
|---|
| 154 | 
 | 
|---|
| 155 |         // QScrollArea::setWidget internally sets autoFillBackground to true
 | 
|---|
| 156 |         // But we don't want a background.
 | 
|---|
| 157 |         contentsWidget->setAutoFillBackground( false );
 | 
|---|
| 158 |         viewport()->setAutoFillBackground( false );
 | 
|---|
| 159 |     }
 | 
|---|
| 160 | 
 | 
|---|
| 161 |     virtual bool event( QEvent *event )
 | 
|---|
| 162 |     {
 | 
|---|
| 163 |         if ( event->type() == QEvent::PolishRequest )
 | 
|---|
| 164 |         {
 | 
|---|
| 165 |             setFocusPolicy( Qt::NoFocus );
 | 
|---|
| 166 |         }
 | 
|---|
| 167 | 
 | 
|---|
| 168 |         if ( event->type() == QEvent::Resize )
 | 
|---|
| 169 |         {
 | 
|---|
| 170 |             // adjust the size to en/disable the scrollbars
 | 
|---|
| 171 |             // before QScrollArea adjusts the viewport size
 | 
|---|
| 172 | 
 | 
|---|
| 173 |             const QRect cr = contentsRect();
 | 
|---|
| 174 | 
 | 
|---|
| 175 |             int w = cr.width();
 | 
|---|
| 176 |             int h = contentsWidget->heightForWidth( cr.width() );
 | 
|---|
| 177 |             if ( h > w )
 | 
|---|
| 178 |             {
 | 
|---|
| 179 |                 w -= verticalScrollBar()->sizeHint().width();
 | 
|---|
| 180 |                 h = contentsWidget->heightForWidth( w );
 | 
|---|
| 181 |             }
 | 
|---|
| 182 | 
 | 
|---|
| 183 |             contentsWidget->resize( w, h );
 | 
|---|
| 184 |         }
 | 
|---|
| 185 | 
 | 
|---|
| 186 |         return QScrollArea::event( event );
 | 
|---|
| 187 |     }
 | 
|---|
| 188 | 
 | 
|---|
| 189 |     virtual bool viewportEvent( QEvent *event )
 | 
|---|
| 190 |     {
 | 
|---|
| 191 |         bool ok = QScrollArea::viewportEvent( event );
 | 
|---|
| 192 | 
 | 
|---|
| 193 |         if ( event->type() == QEvent::Resize )
 | 
|---|
| 194 |         {
 | 
|---|
| 195 |             layoutContents();
 | 
|---|
| 196 |         }
 | 
|---|
| 197 |         return ok;
 | 
|---|
| 198 |     }
 | 
|---|
| 199 | 
 | 
|---|
| 200 |     QSize viewportSize( int w, int h ) const
 | 
|---|
| 201 |     {
 | 
|---|
| 202 |         const int sbHeight = horizontalScrollBar()->sizeHint().height();
 | 
|---|
| 203 |         const int sbWidth = verticalScrollBar()->sizeHint().width();
 | 
|---|
| 204 | 
 | 
|---|
| 205 |         const int cw = contentsRect().width();
 | 
|---|
| 206 |         const int ch = contentsRect().height();
 | 
|---|
| 207 | 
 | 
|---|
| 208 |         int vw = cw;
 | 
|---|
| 209 |         int vh = ch;
 | 
|---|
| 210 | 
 | 
|---|
| 211 |         if ( w > vw )
 | 
|---|
| 212 |             vh -= sbHeight;
 | 
|---|
| 213 | 
 | 
|---|
| 214 |         if ( h > vh )
 | 
|---|
| 215 |         {
 | 
|---|
| 216 |             vw -= sbWidth;
 | 
|---|
| 217 |             if ( w > vw && vh == ch )
 | 
|---|
| 218 |                 vh -= sbHeight;
 | 
|---|
| 219 |         }
 | 
|---|
| 220 |         return QSize( vw, vh );
 | 
|---|
| 221 |     }
 | 
|---|
| 222 | 
 | 
|---|
| 223 |     void layoutContents()
 | 
|---|
| 224 |     {
 | 
|---|
| 225 |         const QwtDynGridLayout *tl = qobject_cast<QwtDynGridLayout *>(
 | 
|---|
| 226 |             contentsWidget->layout() );
 | 
|---|
| 227 |         if ( tl == NULL )
 | 
|---|
| 228 |             return;
 | 
|---|
| 229 | 
 | 
|---|
| 230 |         const QSize visibleSize = viewport()->contentsRect().size();
 | 
|---|
| 231 | 
 | 
|---|
| 232 |         const int minW = int( tl->maxItemWidth() ) + 2 * tl->margin();
 | 
|---|
| 233 | 
 | 
|---|
| 234 |         int w = qMax( visibleSize.width(), minW );
 | 
|---|
| 235 |         int h = qMax( tl->heightForWidth( w ), visibleSize.height() );
 | 
|---|
| 236 | 
 | 
|---|
| 237 |         const int vpWidth = viewportSize( w, h ).width();
 | 
|---|
| 238 |         if ( w > vpWidth )
 | 
|---|
| 239 |         {
 | 
|---|
| 240 |             w = qMax( vpWidth, minW );
 | 
|---|
| 241 |             h = qMax( tl->heightForWidth( w ), visibleSize.height() );
 | 
|---|
| 242 |         }
 | 
|---|
| 243 | 
 | 
|---|
| 244 |         contentsWidget->resize( w, h );
 | 
|---|
| 245 |     }
 | 
|---|
| 246 | 
 | 
|---|
| 247 |     QWidget *contentsWidget;
 | 
|---|
| 248 | };
 | 
|---|
| 249 | 
 | 
|---|
| 250 | /*!
 | 
|---|
| 251 |   Constructor
 | 
|---|
| 252 |   \param parent Parent widget
 | 
|---|
| 253 | */
 | 
|---|
| 254 | QwtLegend::QwtLegend( QWidget *parent ):
 | 
|---|
| 255 |     QwtAbstractLegend( parent )
 | 
|---|
| 256 | {
 | 
|---|
| 257 |     setFrameStyle( NoFrame );
 | 
|---|
| 258 | 
 | 
|---|
| 259 |     d_data = new QwtLegend::PrivateData;
 | 
|---|
| 260 | 
 | 
|---|
| 261 |     d_data->view = new QwtLegend::PrivateData::LegendView( this );
 | 
|---|
| 262 |     d_data->view->setObjectName( "QwtLegendView" );
 | 
|---|
| 263 |     d_data->view->setFrameStyle( NoFrame );
 | 
|---|
| 264 | 
 | 
|---|
| 265 |     QwtDynGridLayout *gridLayout = new QwtDynGridLayout(
 | 
|---|
| 266 |         d_data->view->contentsWidget );
 | 
|---|
| 267 |     gridLayout->setAlignment( Qt::AlignHCenter | Qt::AlignTop );
 | 
|---|
| 268 | 
 | 
|---|
| 269 |     d_data->view->contentsWidget->installEventFilter( this );
 | 
|---|
| 270 | 
 | 
|---|
| 271 |     QVBoxLayout *layout = new QVBoxLayout( this );
 | 
|---|
| 272 |     layout->setContentsMargins( 0, 0, 0, 0 );
 | 
|---|
| 273 |     layout->addWidget( d_data->view );
 | 
|---|
| 274 | }
 | 
|---|
| 275 | 
 | 
|---|
| 276 | //! Destructor
 | 
|---|
| 277 | QwtLegend::~QwtLegend()
 | 
|---|
| 278 | {
 | 
|---|
| 279 |     delete d_data;
 | 
|---|
| 280 | }
 | 
|---|
| 281 | 
 | 
|---|
| 282 | /*!
 | 
|---|
| 283 |   \brief Set the maximum number of entries in a row
 | 
|---|
| 284 | 
 | 
|---|
| 285 |   F.e when the maximum is set to 1 all items are aligned
 | 
|---|
| 286 |   vertically. 0 means unlimited
 | 
|---|
| 287 | 
 | 
|---|
| 288 |   \param numColums Maximum number of entries in a row
 | 
|---|
| 289 | 
 | 
|---|
| 290 |   \sa maxColumns(), QwtDynGridLayout::setMaxColumns()
 | 
|---|
| 291 |  */
 | 
|---|
| 292 | void QwtLegend::setMaxColumns( uint numColums )
 | 
|---|
| 293 | {
 | 
|---|
| 294 |     QwtDynGridLayout *tl = qobject_cast<QwtDynGridLayout *>(
 | 
|---|
| 295 |         d_data->view->contentsWidget->layout() );
 | 
|---|
| 296 |     if ( tl )
 | 
|---|
| 297 |         tl->setMaxColumns( numColums );
 | 
|---|
| 298 | 
 | 
|---|
| 299 |     updateGeometry();
 | 
|---|
| 300 | }
 | 
|---|
| 301 | 
 | 
|---|
| 302 | /*!
 | 
|---|
| 303 |   \return Maximum number of entries in a row
 | 
|---|
| 304 |   \sa setMaxColumns(), QwtDynGridLayout::maxColumns()
 | 
|---|
| 305 |  */
 | 
|---|
| 306 | uint QwtLegend::maxColumns() const
 | 
|---|
| 307 | {
 | 
|---|
| 308 |     uint maxCols = 0;
 | 
|---|
| 309 | 
 | 
|---|
| 310 |     const QwtDynGridLayout *tl = qobject_cast<const QwtDynGridLayout *>(
 | 
|---|
| 311 |         d_data->view->contentsWidget->layout() );
 | 
|---|
| 312 |     if ( tl )
 | 
|---|
| 313 |         maxCols = tl->maxColumns();
 | 
|---|
| 314 | 
 | 
|---|
| 315 |     return maxCols;
 | 
|---|
| 316 | }
 | 
|---|
| 317 | 
 | 
|---|
| 318 | /*!
 | 
|---|
| 319 |   \brief Set the default mode for legend labels
 | 
|---|
| 320 | 
 | 
|---|
| 321 |   Legend labels will be constructed according to the
 | 
|---|
| 322 |   attributes in a QwtLegendData object. When it doesn't
 | 
|---|
| 323 |   contain a value for the QwtLegendData::ModeRole the
 | 
|---|
| 324 |   label will be initialized with the default mode of the legend.
 | 
|---|
| 325 | 
 | 
|---|
| 326 |   \param mode Default item mode
 | 
|---|
| 327 | 
 | 
|---|
| 328 |   \sa itemMode(), QwtLegendData::value(), QwtPlotItem::legendData()
 | 
|---|
| 329 |   \note Changing the mode doesn't have any effect on existing labels.
 | 
|---|
| 330 |  */
 | 
|---|
| 331 | void QwtLegend::setDefaultItemMode( QwtLegendData::Mode mode )
 | 
|---|
| 332 | {
 | 
|---|
| 333 |     d_data->itemMode = mode;
 | 
|---|
| 334 | }
 | 
|---|
| 335 | 
 | 
|---|
| 336 | /*!
 | 
|---|
| 337 |   \return Default item mode
 | 
|---|
| 338 |   \sa setDefaultItemMode()
 | 
|---|
| 339 | */
 | 
|---|
| 340 | QwtLegendData::Mode QwtLegend::defaultItemMode() const
 | 
|---|
| 341 | {
 | 
|---|
| 342 |     return d_data->itemMode;
 | 
|---|
| 343 | }
 | 
|---|
| 344 | 
 | 
|---|
| 345 | /*!
 | 
|---|
| 346 |   The contents widget is the only child of the viewport of
 | 
|---|
| 347 |   the internal QScrollArea and the parent widget of all legend items.
 | 
|---|
| 348 | 
 | 
|---|
| 349 |   \return Container widget of the legend items
 | 
|---|
| 350 | */
 | 
|---|
| 351 | QWidget *QwtLegend::contentsWidget()
 | 
|---|
| 352 | {
 | 
|---|
| 353 |     return d_data->view->contentsWidget;
 | 
|---|
| 354 | }
 | 
|---|
| 355 | 
 | 
|---|
| 356 | /*!
 | 
|---|
| 357 |   \return Horizontal scrollbar
 | 
|---|
| 358 |   \sa verticalScrollBar()
 | 
|---|
| 359 | */
 | 
|---|
| 360 | QScrollBar *QwtLegend::horizontalScrollBar() const
 | 
|---|
| 361 | {
 | 
|---|
| 362 |     return d_data->view->horizontalScrollBar();
 | 
|---|
| 363 | }
 | 
|---|
| 364 | 
 | 
|---|
| 365 | /*!
 | 
|---|
| 366 |   \return Vertical scrollbar
 | 
|---|
| 367 |   \sa horizontalScrollBar()
 | 
|---|
| 368 | */
 | 
|---|
| 369 | QScrollBar *QwtLegend::verticalScrollBar() const
 | 
|---|
| 370 | {
 | 
|---|
| 371 |     return d_data->view->verticalScrollBar();
 | 
|---|
| 372 | }
 | 
|---|
| 373 | 
 | 
|---|
| 374 | /*!
 | 
|---|
| 375 |   The contents widget is the only child of the viewport of
 | 
|---|
| 376 |   the internal QScrollArea and the parent widget of all legend items.
 | 
|---|
| 377 | 
 | 
|---|
| 378 |   \return Container widget of the legend items
 | 
|---|
| 379 | 
 | 
|---|
| 380 | */
 | 
|---|
| 381 | const QWidget *QwtLegend::contentsWidget() const
 | 
|---|
| 382 | {
 | 
|---|
| 383 |     return d_data->view->contentsWidget;
 | 
|---|
| 384 | }
 | 
|---|
| 385 | 
 | 
|---|
| 386 | /*!
 | 
|---|
| 387 |   \brief Update the entries for an item
 | 
|---|
| 388 | 
 | 
|---|
| 389 |   \param itemInfo Info for an item
 | 
|---|
| 390 |   \param legendData List of legend entry attributes for the item
 | 
|---|
| 391 |  */
 | 
|---|
| 392 | void QwtLegend::updateLegend( const QVariant &itemInfo,
 | 
|---|
| 393 |     const QList<QwtLegendData> &legendData )
 | 
|---|
| 394 | {
 | 
|---|
| 395 |     QList<QWidget *> widgetList = legendWidgets( itemInfo );
 | 
|---|
| 396 | 
 | 
|---|
| 397 |     if ( widgetList.size() != legendData.size() )
 | 
|---|
| 398 |     {
 | 
|---|
| 399 |         QLayout *contentsLayout = d_data->view->contentsWidget->layout();
 | 
|---|
| 400 | 
 | 
|---|
| 401 |         while ( widgetList.size() > legendData.size() )
 | 
|---|
| 402 |         {
 | 
|---|
| 403 |             QWidget *w = widgetList.takeLast();
 | 
|---|
| 404 | 
 | 
|---|
| 405 |             contentsLayout->removeWidget( w );
 | 
|---|
| 406 | 
 | 
|---|
| 407 |             // updates might be triggered by signals from the legend widget
 | 
|---|
| 408 |             // itself. So we better don't delete it here.
 | 
|---|
| 409 | 
 | 
|---|
| 410 |             w->hide();
 | 
|---|
| 411 |             w->deleteLater();
 | 
|---|
| 412 |         }
 | 
|---|
| 413 | 
 | 
|---|
| 414 | #if QT_VERSION >= 0x040700
 | 
|---|
| 415 |         widgetList.reserve( legendData.size() );
 | 
|---|
| 416 | #endif
 | 
|---|
| 417 | 
 | 
|---|
| 418 |         for ( int i = widgetList.size(); i < legendData.size(); i++ )
 | 
|---|
| 419 |         {
 | 
|---|
| 420 |             QWidget *widget = createWidget( legendData[i] );
 | 
|---|
| 421 | 
 | 
|---|
| 422 |             if ( contentsLayout )
 | 
|---|
| 423 |                 contentsLayout->addWidget( widget );
 | 
|---|
| 424 | 
 | 
|---|
| 425 |             if ( isVisible() )
 | 
|---|
| 426 |             {
 | 
|---|
| 427 |                 // QLayout does a delayed show, with the effect, that
 | 
|---|
| 428 |                 // the size hint will be wrong, when applications
 | 
|---|
| 429 |                 // call replot() right after changing the list
 | 
|---|
| 430 |                 // of plot items. So we better do the show now.
 | 
|---|
| 431 | 
 | 
|---|
| 432 |                 widget->setVisible( true );
 | 
|---|
| 433 |             }
 | 
|---|
| 434 | 
 | 
|---|
| 435 |             widgetList += widget;
 | 
|---|
| 436 |         }
 | 
|---|
| 437 | 
 | 
|---|
| 438 |         if ( widgetList.isEmpty() )
 | 
|---|
| 439 |         {
 | 
|---|
| 440 |             d_data->itemMap.remove( itemInfo );
 | 
|---|
| 441 |         }
 | 
|---|
| 442 |         else
 | 
|---|
| 443 |         {
 | 
|---|
| 444 |             d_data->itemMap.insert( itemInfo, widgetList );
 | 
|---|
| 445 |         }
 | 
|---|
| 446 | 
 | 
|---|
| 447 |         updateTabOrder();
 | 
|---|
| 448 |     }
 | 
|---|
| 449 | 
 | 
|---|
| 450 |     for ( int i = 0; i < legendData.size(); i++ )
 | 
|---|
| 451 |         updateWidget( widgetList[i], legendData[i] );
 | 
|---|
| 452 | }
 | 
|---|
| 453 | 
 | 
|---|
| 454 | /*!
 | 
|---|
| 455 |   \brief Create a widget to be inserted into the legend
 | 
|---|
| 456 | 
 | 
|---|
| 457 |   The default implementation returns a QwtLegendLabel.
 | 
|---|
| 458 | 
 | 
|---|
| 459 |   \param legendData Attributes of the legend entry
 | 
|---|
| 460 |   \return Widget representing data on the legend
 | 
|---|
| 461 | 
 | 
|---|
| 462 |   \note updateWidget() will called soon after createWidget()
 | 
|---|
| 463 |         with the same attributes.
 | 
|---|
| 464 |  */
 | 
|---|
| 465 | QWidget *QwtLegend::createWidget( const QwtLegendData &legendData ) const
 | 
|---|
| 466 | {
 | 
|---|
| 467 |     Q_UNUSED( legendData );
 | 
|---|
| 468 | 
 | 
|---|
| 469 |     QwtLegendLabel *label = new QwtLegendLabel();
 | 
|---|
| 470 |     label->setItemMode( defaultItemMode() );
 | 
|---|
| 471 | 
 | 
|---|
| 472 |     connect( label, SIGNAL(clicked()), SLOT(itemClicked()) );
 | 
|---|
| 473 |     connect( label, SIGNAL(checked(bool)), SLOT(itemChecked(bool)) );
 | 
|---|
| 474 | 
 | 
|---|
| 475 |     return label;
 | 
|---|
| 476 | }
 | 
|---|
| 477 | 
 | 
|---|
| 478 | /*!
 | 
|---|
| 479 |   \brief Update the widget
 | 
|---|
| 480 | 
 | 
|---|
| 481 |   \param widget Usually a QwtLegendLabel
 | 
|---|
| 482 |   \param legendData Attributes to be displayed
 | 
|---|
| 483 | 
 | 
|---|
| 484 |   \sa createWidget()
 | 
|---|
| 485 |   \note When widget is no QwtLegendLabel updateWidget() does nothing.
 | 
|---|
| 486 |  */
 | 
|---|
| 487 | void QwtLegend::updateWidget( QWidget *widget, const QwtLegendData &legendData )
 | 
|---|
| 488 | {
 | 
|---|
| 489 |     QwtLegendLabel *label = qobject_cast<QwtLegendLabel *>( widget );
 | 
|---|
| 490 |     if ( label )
 | 
|---|
| 491 |     {
 | 
|---|
| 492 |         label->setData( legendData );
 | 
|---|
| 493 |         if ( !legendData.value( QwtLegendData::ModeRole ).isValid() )
 | 
|---|
| 494 |         {
 | 
|---|
| 495 |             // use the default mode, when there is no specific
 | 
|---|
| 496 |             // hint from the legend data
 | 
|---|
| 497 | 
 | 
|---|
| 498 |             label->setItemMode( defaultItemMode() );
 | 
|---|
| 499 |         }
 | 
|---|
| 500 |     }
 | 
|---|
| 501 | }
 | 
|---|
| 502 | 
 | 
|---|
| 503 | void QwtLegend::updateTabOrder()
 | 
|---|
| 504 | {
 | 
|---|
| 505 |     QLayout *contentsLayout = d_data->view->contentsWidget->layout();
 | 
|---|
| 506 |     if ( contentsLayout )
 | 
|---|
| 507 |     {
 | 
|---|
| 508 |         // set tab focus chain
 | 
|---|
| 509 | 
 | 
|---|
| 510 |         QWidget *w = NULL;
 | 
|---|
| 511 | 
 | 
|---|
| 512 |         for ( int i = 0; i < contentsLayout->count(); i++ )
 | 
|---|
| 513 |         {
 | 
|---|
| 514 |             QLayoutItem *item = contentsLayout->itemAt( i );
 | 
|---|
| 515 |             if ( w && item->widget() )
 | 
|---|
| 516 |                 QWidget::setTabOrder( w, item->widget() );
 | 
|---|
| 517 | 
 | 
|---|
| 518 |             w = item->widget();
 | 
|---|
| 519 |         }
 | 
|---|
| 520 |     }
 | 
|---|
| 521 | }
 | 
|---|
| 522 | 
 | 
|---|
| 523 | //! Return a size hint.
 | 
|---|
| 524 | QSize QwtLegend::sizeHint() const
 | 
|---|
| 525 | {
 | 
|---|
| 526 |     QSize hint = d_data->view->contentsWidget->sizeHint();
 | 
|---|
| 527 |     hint += QSize( 2 * frameWidth(), 2 * frameWidth() );
 | 
|---|
| 528 | 
 | 
|---|
| 529 |     return hint;
 | 
|---|
| 530 | }
 | 
|---|
| 531 | 
 | 
|---|
| 532 | /*!
 | 
|---|
| 533 |   \return The preferred height, for a width.
 | 
|---|
| 534 |   \param width Width
 | 
|---|
| 535 | */
 | 
|---|
| 536 | int QwtLegend::heightForWidth( int width ) const
 | 
|---|
| 537 | {
 | 
|---|
| 538 |     width -= 2 * frameWidth();
 | 
|---|
| 539 | 
 | 
|---|
| 540 |     int h = d_data->view->contentsWidget->heightForWidth( width );
 | 
|---|
| 541 |     if ( h >= 0 )
 | 
|---|
| 542 |         h += 2 * frameWidth();
 | 
|---|
| 543 | 
 | 
|---|
| 544 |     return h;
 | 
|---|
| 545 | }
 | 
|---|
| 546 | 
 | 
|---|
| 547 | 
 | 
|---|
| 548 | /*!
 | 
|---|
| 549 |   Handle QEvent::ChildRemoved andQEvent::LayoutRequest events
 | 
|---|
| 550 |   for the contentsWidget().
 | 
|---|
| 551 | 
 | 
|---|
| 552 |   \param object Object to be filtered
 | 
|---|
| 553 |   \param event Event
 | 
|---|
| 554 | 
 | 
|---|
| 555 |   \return Forwarded to QwtAbstractLegend::eventFilter()
 | 
|---|
| 556 | */
 | 
|---|
| 557 | bool QwtLegend::eventFilter( QObject *object, QEvent *event )
 | 
|---|
| 558 | {
 | 
|---|
| 559 |     if ( object == d_data->view->contentsWidget )
 | 
|---|
| 560 |     {
 | 
|---|
| 561 |         switch ( event->type() )
 | 
|---|
| 562 |         {
 | 
|---|
| 563 |             case QEvent::ChildRemoved:
 | 
|---|
| 564 |             {
 | 
|---|
| 565 |                 const QChildEvent *ce =
 | 
|---|
| 566 |                     static_cast<const QChildEvent *>(event);
 | 
|---|
| 567 | 
 | 
|---|
| 568 |                 if ( ce->child()->isWidgetType() )
 | 
|---|
| 569 |                 {
 | 
|---|
| 570 |                     /*
 | 
|---|
| 571 |                         We are called from the ~QObject and ce->child() is
 | 
|---|
| 572 |                         no widget anymore. But all we need is the address
 | 
|---|
| 573 |                         to remove it from the map.
 | 
|---|
| 574 |                      */
 | 
|---|
| 575 |                     QWidget *w = reinterpret_cast< QWidget * >( ce->child() );
 | 
|---|
| 576 |                     d_data->itemMap.removeWidget( w );
 | 
|---|
| 577 |                 }
 | 
|---|
| 578 |                 break;
 | 
|---|
| 579 |             }
 | 
|---|
| 580 |             case QEvent::LayoutRequest:
 | 
|---|
| 581 |             {
 | 
|---|
| 582 |                 d_data->view->layoutContents();
 | 
|---|
| 583 | 
 | 
|---|
| 584 |                 if ( parentWidget() && parentWidget()->layout() == NULL )
 | 
|---|
| 585 |                 {
 | 
|---|
| 586 |                     /*
 | 
|---|
| 587 |                        We want the parent widget ( usually QwtPlot ) to recalculate
 | 
|---|
| 588 |                        its layout, when the contentsWidget has changed. But
 | 
|---|
| 589 |                        because of the scroll view we have to forward the LayoutRequest
 | 
|---|
| 590 |                        event manually.
 | 
|---|
| 591 | 
 | 
|---|
| 592 |                        We don't use updateGeometry() because it doesn't post LayoutRequest
 | 
|---|
| 593 |                        events when the legend is hidden. But we want the
 | 
|---|
| 594 |                        parent widget notified, so it can show/hide the legend
 | 
|---|
| 595 |                        depending on its items.
 | 
|---|
| 596 |                      */
 | 
|---|
| 597 |                     QApplication::postEvent( parentWidget(),
 | 
|---|
| 598 |                         new QEvent( QEvent::LayoutRequest ) );
 | 
|---|
| 599 |                 }
 | 
|---|
| 600 |                 break;
 | 
|---|
| 601 |             }
 | 
|---|
| 602 |             default:
 | 
|---|
| 603 |                 break;
 | 
|---|
| 604 |         }
 | 
|---|
| 605 |     }
 | 
|---|
| 606 | 
 | 
|---|
| 607 |     return QwtAbstractLegend::eventFilter( object, event );
 | 
|---|
| 608 | }
 | 
|---|
| 609 | 
 | 
|---|
| 610 | /*!
 | 
|---|
| 611 |   Called internally when the legend has been clicked on.
 | 
|---|
| 612 |   Emits a clicked() signal.
 | 
|---|
| 613 | */
 | 
|---|
| 614 | void QwtLegend::itemClicked()
 | 
|---|
| 615 | {
 | 
|---|
| 616 |     QWidget *w = qobject_cast<QWidget *>( sender() );
 | 
|---|
| 617 |     if ( w )
 | 
|---|
| 618 |     {
 | 
|---|
| 619 |         const QVariant itemInfo = d_data->itemMap.itemInfo( w );
 | 
|---|
| 620 |         if ( itemInfo.isValid() )
 | 
|---|
| 621 |         {
 | 
|---|
| 622 |             const QList<QWidget *> widgetList =
 | 
|---|
| 623 |                 d_data->itemMap.legendWidgets( itemInfo );
 | 
|---|
| 624 | 
 | 
|---|
| 625 |             const int index = widgetList.indexOf( w );
 | 
|---|
| 626 |             if ( index >= 0 )
 | 
|---|
| 627 |                 Q_EMIT clicked( itemInfo, index );
 | 
|---|
| 628 |         }
 | 
|---|
| 629 |     }
 | 
|---|
| 630 | }
 | 
|---|
| 631 | 
 | 
|---|
| 632 | /*!
 | 
|---|
| 633 |   Called internally when the legend has been checked
 | 
|---|
| 634 |   Emits a checked() signal.
 | 
|---|
| 635 | */
 | 
|---|
| 636 | void QwtLegend::itemChecked( bool on )
 | 
|---|
| 637 | {
 | 
|---|
| 638 |     QWidget *w = qobject_cast<QWidget *>( sender() );
 | 
|---|
| 639 |     if ( w )
 | 
|---|
| 640 |     {
 | 
|---|
| 641 |         const QVariant itemInfo = d_data->itemMap.itemInfo( w );
 | 
|---|
| 642 |         if ( itemInfo.isValid() )
 | 
|---|
| 643 |         {
 | 
|---|
| 644 |             const QList<QWidget *> widgetList =
 | 
|---|
| 645 |                 d_data->itemMap.legendWidgets( itemInfo );
 | 
|---|
| 646 | 
 | 
|---|
| 647 |             const int index = widgetList.indexOf( w );
 | 
|---|
| 648 |             if ( index >= 0 )
 | 
|---|
| 649 |                 Q_EMIT checked( itemInfo, on, index );
 | 
|---|
| 650 |         }
 | 
|---|
| 651 |     }
 | 
|---|
| 652 | }
 | 
|---|
| 653 | 
 | 
|---|
| 654 | /*!
 | 
|---|
| 655 |   Render the legend into a given rectangle.
 | 
|---|
| 656 | 
 | 
|---|
| 657 |   \param painter Painter
 | 
|---|
| 658 |   \param rect Bounding rectangle
 | 
|---|
| 659 |   \param fillBackground When true, fill rect with the widget background
 | 
|---|
| 660 | 
 | 
|---|
| 661 |   \sa renderLegend() is used by QwtPlotRenderer - not by QwtLegend itself
 | 
|---|
| 662 | */
 | 
|---|
| 663 | void QwtLegend::renderLegend( QPainter *painter,
 | 
|---|
| 664 |     const QRectF &rect, bool fillBackground ) const
 | 
|---|
| 665 | {
 | 
|---|
| 666 |     if ( d_data->itemMap.isEmpty() )
 | 
|---|
| 667 |         return;
 | 
|---|
| 668 | 
 | 
|---|
| 669 |     if ( fillBackground )
 | 
|---|
| 670 |     {
 | 
|---|
| 671 |         if ( autoFillBackground() ||
 | 
|---|
| 672 |             testAttribute( Qt::WA_StyledBackground ) )
 | 
|---|
| 673 |         {
 | 
|---|
| 674 |             QwtPainter::drawBackgound( painter, rect, this );
 | 
|---|
| 675 |         }
 | 
|---|
| 676 |     }
 | 
|---|
| 677 | 
 | 
|---|
| 678 |     const QwtDynGridLayout *legendLayout =
 | 
|---|
| 679 |         qobject_cast<QwtDynGridLayout *>( contentsWidget()->layout() );
 | 
|---|
| 680 |     if ( legendLayout == NULL )
 | 
|---|
| 681 |         return;
 | 
|---|
| 682 | 
 | 
|---|
| 683 |     int left, right, top, bottom;
 | 
|---|
| 684 |     getContentsMargins( &left, &top, &right, &bottom );
 | 
|---|
| 685 | 
 | 
|---|
| 686 |     QRect layoutRect;
 | 
|---|
| 687 |     layoutRect.setLeft( qCeil( rect.left() ) + left );
 | 
|---|
| 688 |     layoutRect.setTop( qCeil( rect.top() ) + top );
 | 
|---|
| 689 |     layoutRect.setRight( qFloor( rect.right() ) - right );
 | 
|---|
| 690 |     layoutRect.setBottom( qFloor( rect.bottom() ) - bottom );
 | 
|---|
| 691 | 
 | 
|---|
| 692 |     uint numCols = legendLayout->columnsForWidth( layoutRect.width() );
 | 
|---|
| 693 |     const QList<QRect> itemRects =
 | 
|---|
| 694 |         legendLayout->layoutItems( layoutRect, numCols );
 | 
|---|
| 695 | 
 | 
|---|
| 696 |     int index = 0;
 | 
|---|
| 697 | 
 | 
|---|
| 698 |     for ( int i = 0; i < legendLayout->count(); i++ )
 | 
|---|
| 699 |     {
 | 
|---|
| 700 |         QLayoutItem *item = legendLayout->itemAt( i );
 | 
|---|
| 701 |         QWidget *w = item->widget();
 | 
|---|
| 702 |         if ( w )
 | 
|---|
| 703 |         {
 | 
|---|
| 704 |             painter->save();
 | 
|---|
| 705 | 
 | 
|---|
| 706 |             painter->setClipRect( itemRects[index], Qt::IntersectClip );
 | 
|---|
| 707 |             renderItem( painter, w, itemRects[index], fillBackground );
 | 
|---|
| 708 | 
 | 
|---|
| 709 |             index++;
 | 
|---|
| 710 |             painter->restore();
 | 
|---|
| 711 |         }
 | 
|---|
| 712 |     }
 | 
|---|
| 713 | }
 | 
|---|
| 714 | 
 | 
|---|
| 715 | /*!
 | 
|---|
| 716 |   Render a legend entry into a given rectangle.
 | 
|---|
| 717 | 
 | 
|---|
| 718 |   \param painter Painter
 | 
|---|
| 719 |   \param widget Widget representing a legend entry
 | 
|---|
| 720 |   \param rect Bounding rectangle
 | 
|---|
| 721 |   \param fillBackground When true, fill rect with the widget background
 | 
|---|
| 722 | 
 | 
|---|
| 723 |   \note When widget is not derived from QwtLegendLabel renderItem
 | 
|---|
| 724 |         does nothing beside the background
 | 
|---|
| 725 | */
 | 
|---|
| 726 | void QwtLegend::renderItem( QPainter *painter,
 | 
|---|
| 727 |     const QWidget *widget, const QRectF &rect, bool fillBackground ) const
 | 
|---|
| 728 | {
 | 
|---|
| 729 |     if ( fillBackground )
 | 
|---|
| 730 |     {
 | 
|---|
| 731 |         if ( widget->autoFillBackground() ||
 | 
|---|
| 732 |             widget->testAttribute( Qt::WA_StyledBackground ) )
 | 
|---|
| 733 |         {
 | 
|---|
| 734 |             QwtPainter::drawBackgound( painter, rect, widget );
 | 
|---|
| 735 |         }
 | 
|---|
| 736 |     }
 | 
|---|
| 737 | 
 | 
|---|
| 738 |     const QwtLegendLabel *label = qobject_cast<const QwtLegendLabel *>( widget );
 | 
|---|
| 739 |     if ( label )
 | 
|---|
| 740 |     {
 | 
|---|
| 741 |         // icon
 | 
|---|
| 742 | 
 | 
|---|
| 743 |         const QwtGraphic &icon = label->data().icon();
 | 
|---|
| 744 |         const QSizeF sz = icon.defaultSize();
 | 
|---|
| 745 | 
 | 
|---|
| 746 |         const QRectF iconRect( rect.x() + label->margin(),
 | 
|---|
| 747 |             rect.center().y() - 0.5 * sz.height(),
 | 
|---|
| 748 |             sz.width(), sz.height() );
 | 
|---|
| 749 | 
 | 
|---|
| 750 |         icon.render( painter, iconRect, Qt::KeepAspectRatio );
 | 
|---|
| 751 | 
 | 
|---|
| 752 |         // title
 | 
|---|
| 753 | 
 | 
|---|
| 754 |         QRectF titleRect = rect;
 | 
|---|
| 755 |         titleRect.setX( iconRect.right() + 2 * label->spacing() );
 | 
|---|
| 756 | 
 | 
|---|
| 757 |         QFont labelFont = label->font();
 | 
|---|
| 758 |         labelFont.resolve( QFont::AllPropertiesResolved );
 | 
|---|
| 759 | 
 | 
|---|
| 760 |         painter->setFont( labelFont );
 | 
|---|
| 761 |         painter->setPen( label->palette().color( QPalette::Text ) );
 | 
|---|
| 762 | 
 | 
|---|
| 763 |         const_cast< QwtLegendLabel *>( label )->drawText( painter, titleRect );
 | 
|---|
| 764 |     }
 | 
|---|
| 765 | }
 | 
|---|
| 766 | 
 | 
|---|
| 767 | /*!
 | 
|---|
| 768 |   \return List of widgets associated to a item
 | 
|---|
| 769 |   \param itemInfo Info about an item
 | 
|---|
| 770 |   \sa legendWidget(), itemInfo(), QwtPlot::itemToInfo()
 | 
|---|
| 771 |  */
 | 
|---|
| 772 | QList<QWidget *> QwtLegend::legendWidgets( const QVariant &itemInfo ) const
 | 
|---|
| 773 | {
 | 
|---|
| 774 |     return d_data->itemMap.legendWidgets( itemInfo );
 | 
|---|
| 775 | }
 | 
|---|
| 776 | 
 | 
|---|
| 777 | /*!
 | 
|---|
| 778 |   \return First widget in the list of widgets associated to an item
 | 
|---|
| 779 |   \param itemInfo Info about an item
 | 
|---|
| 780 |   \sa itemInfo(), QwtPlot::itemToInfo()
 | 
|---|
| 781 |   \note Almost all types of items have only one widget
 | 
|---|
| 782 | */
 | 
|---|
| 783 | QWidget *QwtLegend::legendWidget( const QVariant &itemInfo ) const
 | 
|---|
| 784 | {
 | 
|---|
| 785 |     const QList<QWidget *> list = d_data->itemMap.legendWidgets( itemInfo );
 | 
|---|
| 786 |     if ( list.isEmpty() )
 | 
|---|
| 787 |         return NULL;
 | 
|---|
| 788 | 
 | 
|---|
| 789 |     return list[0];
 | 
|---|
| 790 | }
 | 
|---|
| 791 | 
 | 
|---|
| 792 | /*!
 | 
|---|
| 793 |   Find the item that is associated to a widget
 | 
|---|
| 794 | 
 | 
|---|
| 795 |   \param widget Widget on the legend
 | 
|---|
| 796 |   \return Associated item info
 | 
|---|
| 797 |   \sa legendWidget()
 | 
|---|
| 798 |  */
 | 
|---|
| 799 | QVariant QwtLegend::itemInfo( const QWidget *widget ) const
 | 
|---|
| 800 | {
 | 
|---|
| 801 |     return d_data->itemMap.itemInfo( widget );
 | 
|---|
| 802 | }
 | 
|---|
| 803 | 
 | 
|---|
| 804 | //! \return True, when no item is inserted
 | 
|---|
| 805 | bool QwtLegend::isEmpty() const
 | 
|---|
| 806 | {
 | 
|---|
| 807 |     return d_data->itemMap.isEmpty();
 | 
|---|
| 808 | }
 | 
|---|
| 809 | 
 | 
|---|
| 810 | /*!
 | 
|---|
| 811 |     Return the extent, that is needed for the scrollbars
 | 
|---|
| 812 | 
 | 
|---|
| 813 |     \param orientation Orientation
 | 
|---|
| 814 |     \return The width of the vertical scrollbar for Qt::Horizontal and v.v.
 | 
|---|
| 815 |  */
 | 
|---|
| 816 | int QwtLegend::scrollExtent( Qt::Orientation orientation ) const
 | 
|---|
| 817 | {
 | 
|---|
| 818 |     int extent = 0;
 | 
|---|
| 819 | 
 | 
|---|
| 820 |     if ( orientation == Qt::Horizontal )
 | 
|---|
| 821 |         extent = verticalScrollBar()->sizeHint().width();
 | 
|---|
| 822 |     else
 | 
|---|
| 823 |         extent = horizontalScrollBar()->sizeHint().height();
 | 
|---|
| 824 | 
 | 
|---|
| 825 |     return extent;
 | 
|---|
| 826 | }
 | 
|---|
| 827 | 
 | 
|---|