qwt_legend.cpp 20.5 KB
Newer Older
pixhawk's avatar
pixhawk committed
1 2 3 4
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
 * Qwt Widget Library
 * Copyright (C) 1997   Josef Wilgen
 * Copyright (C) 2002   Uwe Rathmann
5
 *
pixhawk's avatar
pixhawk committed
6 7 8 9
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the Qwt License, Version 1.0
 *****************************************************************************/

Bryant's avatar
Bryant committed
10 11 12 13 14 15
#include "qwt_legend.h"
#include "qwt_legend_label.h"
#include "qwt_dyngrid_layout.h"
#include "qwt_math.h"
#include "qwt_plot_item.h"
#include "qwt_painter.h"
16 17
#include <qapplication.h>
#include <qscrollbar.h>
Bryant's avatar
Bryant committed
18 19 20 21
#include <qscrollarea.h>
#include <qpainter.h>
#include <qstyle.h>
#include <qstyleoption.h>
pixhawk's avatar
pixhawk committed
22

Bryant's avatar
Bryant committed
23
class QwtLegendMap
pixhawk's avatar
pixhawk committed
24 25
{
public:
Bryant's avatar
Bryant committed
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
    inline bool isEmpty() const { return d_entries.isEmpty(); }

    void insert( const QVariant &, const QList<QWidget *> & );
    void remove( const QVariant & );

    void removeWidget( const QWidget * );

    QList<QWidget *> legendWidgets( const QVariant & ) const;
    QVariant itemInfo( const QWidget * ) const;

private:
    // we don't know anything about itemInfo and therefore don't have
    // any key that can be used for a map or hashtab.
    // But a simple linear list is o.k. here, as we will never have
    // more than a few entries.

    class Entry
pixhawk's avatar
pixhawk committed
43 44
    {
    public:
Bryant's avatar
Bryant committed
45 46 47
        QVariant itemInfo;
        QList<QWidget *> widgets;
    };
pixhawk's avatar
pixhawk committed
48

Bryant's avatar
Bryant committed
49 50
    QList< Entry > d_entries;
};
pixhawk's avatar
pixhawk committed
51

Bryant's avatar
Bryant committed
52 53 54 55 56 57 58 59 60 61 62 63
void QwtLegendMap::insert( const QVariant &itemInfo, 
    const QList<QWidget *> &widgets )
{
    for ( int i = 0; i < d_entries.size(); i++ )
    {
        Entry &entry = d_entries[i];
        if ( entry.itemInfo == itemInfo )
        {
            entry.widgets = widgets;
            return;
        }
    }
pixhawk's avatar
pixhawk committed
64

Bryant's avatar
Bryant committed
65 66 67
    Entry newEntry;
    newEntry.itemInfo = itemInfo;
    newEntry.widgets = widgets;
pixhawk's avatar
pixhawk committed
68

Bryant's avatar
Bryant committed
69 70
    d_entries += newEntry;
}
pixhawk's avatar
pixhawk committed
71

Bryant's avatar
Bryant committed
72 73 74 75 76 77 78 79 80 81 82 83
void QwtLegendMap::remove( const QVariant &itemInfo )
{
    for ( int i = 0; i < d_entries.size(); i++ )
    {
        Entry &entry = d_entries[i];
        if ( entry.itemInfo == itemInfo )
        {
            d_entries.removeAt( i );
            return;
        }
    }
}
pixhawk's avatar
pixhawk committed
84

Bryant's avatar
Bryant committed
85 86 87
void QwtLegendMap::removeWidget( const QWidget *widget )
{
    QWidget *w = const_cast<QWidget *>( widget );
pixhawk's avatar
pixhawk committed
88

Bryant's avatar
Bryant committed
89 90 91
    for ( int i = 0; i < d_entries.size(); i++ )
        d_entries[ i ].widgets.removeAll( w );
}
pixhawk's avatar
pixhawk committed
92

Bryant's avatar
Bryant committed
93 94 95 96 97
QVariant QwtLegendMap::itemInfo( const QWidget *widget ) const
{
    if ( widget != NULL )
    {
        QWidget *w = const_cast<QWidget *>( widget );
pixhawk's avatar
pixhawk committed
98

Bryant's avatar
Bryant committed
99 100 101 102 103 104 105
        for ( int i = 0; i < d_entries.size(); i++ )
        {
            const Entry &entry = d_entries[i];
            if ( entry.widgets.indexOf( w ) >= 0 )
                return entry.itemInfo;
        }
    }
pixhawk's avatar
pixhawk committed
106

Bryant's avatar
Bryant committed
107 108 109 110 111 112 113 114 115 116 117 118 119 120
    return QVariant();
}

QList<QWidget *> QwtLegendMap::legendWidgets( const QVariant &itemInfo ) const
{
    if ( itemInfo.isValid() )
    {
        for ( int i = 0; i < d_entries.size(); i++ )
        {
            const Entry &entry = d_entries[i];
            if ( entry.itemInfo == itemInfo )
                return entry.widgets;
        }
    }
pixhawk's avatar
pixhawk committed
121

Bryant's avatar
Bryant committed
122 123
    return QList<QWidget *>();
}
pixhawk's avatar
pixhawk committed
124

Bryant's avatar
Bryant committed
125
class QwtLegend::PrivateData
pixhawk's avatar
pixhawk committed
126 127
{
public:
Bryant's avatar
Bryant committed
128 129 130 131 132
    PrivateData():
        itemMode( QwtLegendData::ReadOnly ),
        view( NULL )
    {
    }
pixhawk's avatar
pixhawk committed
133

Bryant's avatar
Bryant committed
134 135
    QwtLegendData::Mode itemMode;
    QwtLegendMap itemMap;
pixhawk's avatar
pixhawk committed
136

Bryant's avatar
Bryant committed
137 138 139
    class LegendView;
    LegendView *view;
};
pixhawk's avatar
pixhawk committed
140

Bryant's avatar
Bryant committed
141 142 143 144 145 146 147 148
class QwtLegend::PrivateData::LegendView: public QScrollArea
{
public:
    LegendView( QWidget *parent ):
        QScrollArea( parent )
    {
        contentsWidget = new QWidget( this );
        contentsWidget->setObjectName( "QwtLegendViewContents" );
pixhawk's avatar
pixhawk committed
149

Bryant's avatar
Bryant committed
150 151
        setWidget( contentsWidget );
        setWidgetResizable( false );
pixhawk's avatar
pixhawk committed
152

Bryant's avatar
Bryant committed
153
        viewport()->setObjectName( "QwtLegendViewport" );
pixhawk's avatar
pixhawk committed
154

Bryant's avatar
Bryant committed
155 156 157 158
        // QScrollArea::setWidget internally sets autoFillBackground to true
        // But we don't want a background.
        contentsWidget->setAutoFillBackground( false );
        viewport()->setAutoFillBackground( false );
pixhawk's avatar
pixhawk committed
159 160
    }

Bryant's avatar
Bryant committed
161 162 163 164 165 166
    virtual bool event( QEvent *event )
    {
        if ( event->type() == QEvent::PolishRequest )
        {
            setFocusPolicy( Qt::NoFocus );
        }
pixhawk's avatar
pixhawk committed
167

Bryant's avatar
Bryant committed
168 169 170 171
        if ( event->type() == QEvent::Resize )
        {
            // adjust the size to en/disable the scrollbars
            // before QScrollArea adjusts the viewport size
pixhawk's avatar
pixhawk committed
172

Bryant's avatar
Bryant committed
173
            const QRect cr = contentsRect();
pixhawk's avatar
pixhawk committed
174

Bryant's avatar
Bryant committed
175 176 177 178 179 180 181 182 183 184
            int w = cr.width();
            int h = contentsWidget->heightForWidth( cr.width() );
            if ( h > w )
            {
                w -= verticalScrollBar()->sizeHint().width();
                h = contentsWidget->heightForWidth( w );
            }

            contentsWidget->resize( w, h );
        }
pixhawk's avatar
pixhawk committed
185

Bryant's avatar
Bryant committed
186
        return QScrollArea::event( event );
pixhawk's avatar
pixhawk committed
187 188
    }

Bryant's avatar
Bryant committed
189 190 191
    virtual bool viewportEvent( QEvent *event )
    {
        bool ok = QScrollArea::viewportEvent( event );
pixhawk's avatar
pixhawk committed
192

Bryant's avatar
Bryant committed
193 194 195
        if ( event->type() == QEvent::Resize )
        {
            layoutContents();
pixhawk's avatar
pixhawk committed
196 197 198 199
        }
        return ok;
    }

Bryant's avatar
Bryant committed
200 201
    QSize viewportSize( int w, int h ) const
    {
pixhawk's avatar
pixhawk committed
202 203
        const int sbHeight = horizontalScrollBar()->sizeHint().height();
        const int sbWidth = verticalScrollBar()->sizeHint().width();
204

pixhawk's avatar
pixhawk committed
205 206 207 208 209 210 211 212 213
        const int cw = contentsRect().width();
        const int ch = contentsRect().height();

        int vw = cw;
        int vh = ch;

        if ( w > vw )
            vh -= sbHeight;

Bryant's avatar
Bryant committed
214 215
        if ( h > vh )
        {
pixhawk's avatar
pixhawk committed
216 217 218 219
            vw -= sbWidth;
            if ( w > vw && vh == ch )
                vh -= sbHeight;
        }
Bryant's avatar
Bryant committed
220
        return QSize( vw, vh );
pixhawk's avatar
pixhawk committed
221 222
    }

Bryant's avatar
Bryant committed
223 224 225 226 227 228
    void layoutContents()
    {
        const QwtDynGridLayout *tl = qobject_cast<QwtDynGridLayout *>(
            contentsWidget->layout() );
        if ( tl == NULL )
            return;
229

Bryant's avatar
Bryant committed
230
        const QSize visibleSize = viewport()->contentsRect().size();
pixhawk's avatar
pixhawk committed
231

Bryant's avatar
Bryant committed
232
        const int minW = int( tl->maxItemWidth() ) + 2 * tl->margin();
pixhawk's avatar
pixhawk committed
233

Bryant's avatar
Bryant committed
234 235
        int w = qMax( visibleSize.width(), minW );
        int h = qMax( tl->heightForWidth( w ), visibleSize.height() );
pixhawk's avatar
pixhawk committed
236

Bryant's avatar
Bryant committed
237 238 239 240 241 242
        const int vpWidth = viewportSize( w, h ).width();
        if ( w > vpWidth )
        {
            w = qMax( vpWidth, minW );
            h = qMax( tl->heightForWidth( w ), visibleSize.height() );
        }
pixhawk's avatar
pixhawk committed
243

Bryant's avatar
Bryant committed
244 245
        contentsWidget->resize( w, h );
    }
pixhawk's avatar
pixhawk committed
246

Bryant's avatar
Bryant committed
247 248
    QWidget *contentsWidget;
};
pixhawk's avatar
pixhawk committed
249

Bryant's avatar
Bryant committed
250 251 252 253 254 255
/*!
  Constructor
  \param parent Parent widget
*/
QwtLegend::QwtLegend( QWidget *parent ):
    QwtAbstractLegend( parent )
pixhawk's avatar
pixhawk committed
256
{
Bryant's avatar
Bryant committed
257
    setFrameStyle( NoFrame );
pixhawk's avatar
pixhawk committed
258

Bryant's avatar
Bryant committed
259
    d_data = new QwtLegend::PrivateData;
pixhawk's avatar
pixhawk committed
260

Bryant's avatar
Bryant committed
261 262 263
    d_data->view = new QwtLegend::PrivateData::LegendView( this );
    d_data->view->setObjectName( "QwtLegendView" );
    d_data->view->setFrameStyle( NoFrame );
pixhawk's avatar
pixhawk committed
264

Bryant's avatar
Bryant committed
265 266 267 268 269
    QwtDynGridLayout *gridLayout = new QwtDynGridLayout(
        d_data->view->contentsWidget );
    gridLayout->setAlignment( Qt::AlignHCenter | Qt::AlignTop );

    d_data->view->contentsWidget->installEventFilter( this );
pixhawk's avatar
pixhawk committed
270

Bryant's avatar
Bryant committed
271 272 273
    QVBoxLayout *layout = new QVBoxLayout( this );
    layout->setContentsMargins( 0, 0, 0, 0 );
    layout->addWidget( d_data->view );
pixhawk's avatar
pixhawk committed
274 275
}

Bryant's avatar
Bryant committed
276 277
//! Destructor
QwtLegend::~QwtLegend()
pixhawk's avatar
pixhawk committed
278
{
Bryant's avatar
Bryant committed
279
    delete d_data;
pixhawk's avatar
pixhawk committed
280 281
}

Bryant's avatar
Bryant committed
282 283
/*!
  \brief Set the maximum number of entries in a row
pixhawk's avatar
pixhawk committed
284

Bryant's avatar
Bryant committed
285 286
  F.e when the maximum is set to 1 all items are aligned
  vertically. 0 means unlimited
pixhawk's avatar
pixhawk committed
287

Bryant's avatar
Bryant committed
288
  \param numColums Maximum number of entries in a row
pixhawk's avatar
pixhawk committed
289

Bryant's avatar
Bryant committed
290 291 292
  \sa maxColumns(), QwtDynGridLayout::setMaxColumns()
 */
void QwtLegend::setMaxColumns( uint numColums )
pixhawk's avatar
pixhawk committed
293
{
Bryant's avatar
Bryant committed
294 295 296 297
    QwtDynGridLayout *tl = qobject_cast<QwtDynGridLayout *>(
        d_data->view->contentsWidget->layout() );
    if ( tl )
        tl->setMaxColumns( numColums );
298
}
pixhawk's avatar
pixhawk committed
299 300

/*!
Bryant's avatar
Bryant committed
301 302 303 304
  \return Maximum number of entries in a row
  \sa setMaxColumns(), QwtDynGridLayout::maxColumns()
 */
uint QwtLegend::maxColumns() const
pixhawk's avatar
pixhawk committed
305
{
Bryant's avatar
Bryant committed
306
    uint maxCols = 0;
pixhawk's avatar
pixhawk committed
307

Bryant's avatar
Bryant committed
308 309 310 311
    const QwtDynGridLayout *tl = qobject_cast<const QwtDynGridLayout *>(
        d_data->view->contentsWidget->layout() );
    if ( tl )
        maxCols = tl->maxColumns();
pixhawk's avatar
pixhawk committed
312

Bryant's avatar
Bryant committed
313
    return maxCols;
pixhawk's avatar
pixhawk committed
314 315 316
}

/*!
Bryant's avatar
Bryant committed
317
  \brief Set the default mode for legend labels
pixhawk's avatar
pixhawk committed
318

Bryant's avatar
Bryant committed
319 320 321 322
  Legend labels will be constructed according to the
  attributes in a QwtLegendData object. When it doesn't
  contain a value for the QwtLegendData::ModeRole the
  label will be initialized with the default mode of the legend.
pixhawk's avatar
pixhawk committed
323

Bryant's avatar
Bryant committed
324
  \param mode Default item mode
pixhawk's avatar
pixhawk committed
325

Bryant's avatar
Bryant committed
326 327 328 329
  \sa itemMode(), QwtLegendData::value(), QwtPlotItem::legendData()
  \note Changing the mode doesn't have any effect on existing labels.
 */
void QwtLegend::setDefaultItemMode( QwtLegendData::Mode mode )
pixhawk's avatar
pixhawk committed
330 331 332 333 334
{
    d_data->itemMode = mode;
}

/*!
Bryant's avatar
Bryant committed
335 336
  \return Default item mode
  \sa setDefaultItemMode()
pixhawk's avatar
pixhawk committed
337
*/
Bryant's avatar
Bryant committed
338
QwtLegendData::Mode QwtLegend::defaultItemMode() const
pixhawk's avatar
pixhawk committed
339
{
Bryant's avatar
Bryant committed
340
    return d_data->itemMode;
pixhawk's avatar
pixhawk committed
341 342
}

343
/*!
Bryant's avatar
Bryant committed
344 345 346 347
  The contents widget is the only child of the viewport of 
  the internal QScrollArea and the parent widget of all legend items.

  \return Container widget of the legend items
pixhawk's avatar
pixhawk committed
348
*/
349 350 351
QWidget *QwtLegend::contentsWidget()
{
    return d_data->view->contentsWidget;
pixhawk's avatar
pixhawk committed
352 353
}

Bryant's avatar
Bryant committed
354 355 356 357
/*!
  \return Horizontal scrollbar
  \sa verticalScrollBar()
*/
pixhawk's avatar
pixhawk committed
358 359 360 361 362
QScrollBar *QwtLegend::horizontalScrollBar() const
{
    return d_data->view->horizontalScrollBar();
}

Bryant's avatar
Bryant committed
363 364 365 366
/*!
  \return Vertical scrollbar
  \sa horizontalScrollBar()
*/
pixhawk's avatar
pixhawk committed
367 368 369 370 371
QScrollBar *QwtLegend::verticalScrollBar() const
{
    return d_data->view->verticalScrollBar();
}

372
/*!
Bryant's avatar
Bryant committed
373 374
  The contents widget is the only child of the viewport of 
  the internal QScrollArea and the parent widget of all legend items.
pixhawk's avatar
pixhawk committed
375

Bryant's avatar
Bryant committed
376 377 378
  \return Container widget of the legend items

*/
379 380 381
const QWidget *QwtLegend::contentsWidget() const
{
    return d_data->view->contentsWidget;
pixhawk's avatar
pixhawk committed
382 383 384
}

/*!
Bryant's avatar
Bryant committed
385 386 387 388 389 390 391
  \brief Update the entries for an item

  \param itemInfo Info for an item
  \param data List of legend entry attributes for the item
 */
void QwtLegend::updateLegend( const QVariant &itemInfo, 
    const QList<QwtLegendData> &data )
pixhawk's avatar
pixhawk committed
392
{
Bryant's avatar
Bryant committed
393
    QList<QWidget *> widgetList = legendWidgets( itemInfo );
pixhawk's avatar
pixhawk committed
394

Bryant's avatar
Bryant committed
395 396 397
    if ( widgetList.size() != data.size() )
    {
        QLayout *contentsLayout = d_data->view->contentsWidget->layout();
pixhawk's avatar
pixhawk committed
398

Bryant's avatar
Bryant committed
399 400 401
        while ( widgetList.size() > data.size() )
        {
            QWidget *w = widgetList.takeLast();
pixhawk's avatar
pixhawk committed
402

Bryant's avatar
Bryant committed
403
            contentsLayout->removeWidget( w );
pixhawk's avatar
pixhawk committed
404

Bryant's avatar
Bryant committed
405 406
            // updates might be triggered by signals from the legend widget
            // itself. So we better don't delete it here.
pixhawk's avatar
pixhawk committed
407

Bryant's avatar
Bryant committed
408 409 410
            w->hide();
            w->deleteLater();
        }
pixhawk's avatar
pixhawk committed
411

Bryant's avatar
Bryant committed
412 413 414
        for ( int i = widgetList.size(); i < data.size(); i++ )
        {
            QWidget *widget = createWidget( data[i] );
pixhawk's avatar
pixhawk committed
415

Bryant's avatar
Bryant committed
416 417
            if ( contentsLayout )
                contentsLayout->addWidget( widget );
pixhawk's avatar
pixhawk committed
418

Bryant's avatar
Bryant committed
419 420
            widgetList += widget;
        }
pixhawk's avatar
pixhawk committed
421

Bryant's avatar
Bryant committed
422 423 424
        if ( widgetList.isEmpty() )
        {
            d_data->itemMap.remove( itemInfo );
pixhawk's avatar
pixhawk committed
425
        }
Bryant's avatar
Bryant committed
426 427 428 429 430 431
        else
        {
            d_data->itemMap.insert( itemInfo, widgetList );
        }

        updateTabOrder();
pixhawk's avatar
pixhawk committed
432
    }
Bryant's avatar
Bryant committed
433 434 435
    
    for ( int i = 0; i < data.size(); i++ )
        updateWidget( widgetList[i], data[i] );
pixhawk's avatar
pixhawk committed
436 437 438
}

/*!
Bryant's avatar
Bryant committed
439
  \brief Create a widget to be inserted into the legend
pixhawk's avatar
pixhawk committed
440

Bryant's avatar
Bryant committed
441 442 443 444 445 446 447 448 449
  The default implementation returns a QwtLegendLabel.

  \param data Attributes of the legend entry
  \return Widget representing data on the legend
  
  \note updateWidget() will called soon after createWidget()
        with the same attributes.
 */
QWidget *QwtLegend::createWidget( const QwtLegendData &data ) const
pixhawk's avatar
pixhawk committed
450
{
Bryant's avatar
Bryant committed
451
    Q_UNUSED( data );
pixhawk's avatar
pixhawk committed
452

Bryant's avatar
Bryant committed
453 454
    QwtLegendLabel *label = new QwtLegendLabel();
    label->setItemMode( defaultItemMode() );
pixhawk's avatar
pixhawk committed
455

Bryant's avatar
Bryant committed
456 457 458 459
    connect( label, SIGNAL( clicked() ), SLOT( itemClicked() ) );
    connect( label, SIGNAL( checked( bool ) ), SLOT( itemChecked( bool ) ) );

    return label;
pixhawk's avatar
pixhawk committed
460 461
}

462
/*!
Bryant's avatar
Bryant committed
463
  \brief Update the widget 
pixhawk's avatar
pixhawk committed
464

Bryant's avatar
Bryant committed
465 466 467 468 469 470 471
  \param widget Usually a QwtLegendLabel
  \param data Attributes to be displayed

  \sa createWidget()
  \note When widget is no QwtLegendLabel updateWidget() does nothing.
 */
void QwtLegend::updateWidget( QWidget *widget, const QwtLegendData &data )
472
{
Bryant's avatar
Bryant committed
473 474 475 476 477 478 479 480 481 482 483 484
    QwtLegendLabel *label = qobject_cast<QwtLegendLabel *>( widget );
    if ( label )
    {
        label->setData( data );
        if ( !data.value( QwtLegendData::ModeRole ).isValid() )
        {
            // use the default mode, when there is no specific
            // hint from the legend data

            label->setItemMode( defaultItemMode() );
        }
    }
pixhawk's avatar
pixhawk committed
485 486
}

Bryant's avatar
Bryant committed
487
void QwtLegend::updateTabOrder()
pixhawk's avatar
pixhawk committed
488
{
Bryant's avatar
Bryant committed
489 490 491 492
    QLayout *contentsLayout = d_data->view->contentsWidget->layout();
    if ( contentsLayout )
    {
        // set tab focus chain
pixhawk's avatar
pixhawk committed
493

Bryant's avatar
Bryant committed
494 495 496 497 498 499 500
        QWidget *w = NULL;

        for ( int i = 0; i < contentsLayout->count(); i++ )
        {
            QLayoutItem *item = contentsLayout->itemAt( i );
            if ( w && item->widget() )
                QWidget::setTabOrder( w, item->widget() );
pixhawk's avatar
pixhawk committed
501

Bryant's avatar
Bryant committed
502 503 504
            w = item->widget();
        }
    }
pixhawk's avatar
pixhawk committed
505 506 507 508 509 510
}

//! Return a size hint.
QSize QwtLegend::sizeHint() const
{
    QSize hint = d_data->view->contentsWidget->sizeHint();
Bryant's avatar
Bryant committed
511
    hint += QSize( 2 * frameWidth(), 2 * frameWidth() );
pixhawk's avatar
pixhawk committed
512 513 514 515 516

    return hint;
}

/*!
Bryant's avatar
Bryant committed
517
  \return The preferred height, for a width.
pixhawk's avatar
pixhawk committed
518 519
  \param width Width
*/
Bryant's avatar
Bryant committed
520
int QwtLegend::heightForWidth( int width ) const
pixhawk's avatar
pixhawk committed
521 522 523
{
    width -= 2 * frameWidth();

Bryant's avatar
Bryant committed
524
    int h = d_data->view->contentsWidget->heightForWidth( width );
pixhawk's avatar
pixhawk committed
525 526 527 528 529 530
    if ( h >= 0 )
        h += 2 * frameWidth();

    return h;
}

Bryant's avatar
Bryant committed
531

pixhawk's avatar
pixhawk committed
532
/*!
Bryant's avatar
Bryant committed
533 534
  Handle QEvent::ChildRemoved andQEvent::LayoutRequest events 
  for the contentsWidget().
pixhawk's avatar
pixhawk committed
535

Bryant's avatar
Bryant committed
536 537
  \param object Object to be filtered
  \param event Event
pixhawk's avatar
pixhawk committed
538

Bryant's avatar
Bryant committed
539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583
  \return Forwarded to QwtAbstractLegend::eventFilter()
*/
bool QwtLegend::eventFilter( QObject *object, QEvent *event )
{
    if ( object == d_data->view->contentsWidget )
    {
        switch ( event->type() )
        {
            case QEvent::ChildRemoved:
            {
                const QChildEvent *ce = 
                    static_cast<const QChildEvent *>(event);
                if ( ce->child()->isWidgetType() )
                {
                    QWidget *w = static_cast< QWidget * >( ce->child() );
                    d_data->itemMap.removeWidget( w );
                }
                break;
            }
            case QEvent::LayoutRequest:
            {
                d_data->view->layoutContents();

                if ( parentWidget() && parentWidget()->layout() == NULL )
                {
                    /*
                       We want the parent widget ( usually QwtPlot ) to recalculate
                       its layout, when the contentsWidget has changed. But
                       because of the scroll view we have to forward the LayoutRequest
                       event manually.

                       We don't use updateGeometry() because it doesn't post LayoutRequest
                       events when the legend is hidden. But we want the
                       parent widget notified, so it can show/hide the legend
                       depending on its items.
                     */
                    QApplication::postEvent( parentWidget(),
                        new QEvent( QEvent::LayoutRequest ) );
                }                
                break;
            }
            default:
                break;
        }
    }
pixhawk's avatar
pixhawk committed
584

Bryant's avatar
Bryant committed
585 586
    return QwtAbstractLegend::eventFilter( object, event );
}
pixhawk's avatar
pixhawk committed
587

Bryant's avatar
Bryant committed
588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605
/*!
  Called internally when the legend has been clicked on.
  Emits a clicked() signal.
*/
void QwtLegend::itemClicked()
{
    QWidget *w = qobject_cast<QWidget *>( sender() );
    if ( w )
    {
        const QVariant itemInfo = d_data->itemMap.itemInfo( w );
        if ( itemInfo.isValid() )
        {
            const QList<QWidget *> widgetList =
                d_data->itemMap.legendWidgets( itemInfo );

            const int index = widgetList.indexOf( w );
            if ( index >= 0 )
                Q_EMIT clicked( itemInfo, index );
pixhawk's avatar
pixhawk committed
606
        }
Bryant's avatar
Bryant committed
607 608
    }
}
pixhawk's avatar
pixhawk committed
609

Bryant's avatar
Bryant committed
610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628
/*!
  Called internally when the legend has been checked
  Emits a checked() signal.
*/
void QwtLegend::itemChecked( bool on )
{
    QWidget *w = qobject_cast<QWidget *>( sender() );
    if ( w )
    {
        const QVariant itemInfo = d_data->itemMap.itemInfo( w );
        if ( itemInfo.isValid() )
        {
            const QList<QWidget *> widgetList =
                d_data->itemMap.legendWidgets( itemInfo );

            const int index = widgetList.indexOf( w );
            if ( index >= 0 )
                Q_EMIT checked( itemInfo, on, index );
        }
pixhawk's avatar
pixhawk committed
629 630 631
    }
}

Bryant's avatar
Bryant committed
632 633
/*!
  Render the legend into a given rectangle.
pixhawk's avatar
pixhawk committed
634

Bryant's avatar
Bryant committed
635 636 637
  \param painter Painter
  \param rect Bounding rectangle
  \param fillBackground When true, fill rect with the widget background 
pixhawk's avatar
pixhawk committed
638

Bryant's avatar
Bryant committed
639 640 641 642
  \sa renderLegend() is used by QwtPlotRenderer - not by QwtLegend itself
*/
void QwtLegend::renderLegend( QPainter *painter, 
    const QRectF &rect, bool fillBackground ) const
pixhawk's avatar
pixhawk committed
643
{
Bryant's avatar
Bryant committed
644 645 646 647 648 649 650 651 652
    if ( d_data->itemMap.isEmpty() )
        return;

    if ( fillBackground )
    {
        if ( autoFillBackground() ||
            testAttribute( Qt::WA_StyledBackground ) )
        {
            QwtPainter::drawBackgound( painter, rect, this );
pixhawk's avatar
pixhawk committed
653 654
        }
    }
655

Bryant's avatar
Bryant committed
656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690
    const QwtDynGridLayout *legendLayout = 
        qobject_cast<QwtDynGridLayout *>( contentsWidget()->layout() );
    if ( legendLayout == NULL )
        return;

    int left, right, top, bottom;
    getContentsMargins( &left, &top, &right, &bottom );

    QRect layoutRect; 
    layoutRect.setLeft( qCeil( rect.left() ) + left );
    layoutRect.setTop( qCeil( rect.top() ) + top );
    layoutRect.setRight( qFloor( rect.right() ) - right );
    layoutRect.setBottom( qFloor( rect.bottom() ) - bottom );

    uint numCols = legendLayout->columnsForWidth( layoutRect.width() );
    QList<QRect> itemRects =
        legendLayout->layoutItems( layoutRect, numCols );

    int index = 0;

    for ( int i = 0; i < legendLayout->count(); i++ )
    {
        QLayoutItem *item = legendLayout->itemAt( i );
        QWidget *w = item->widget();
        if ( w )
        {
            painter->save();

            painter->setClipRect( itemRects[index], Qt::IntersectClip );
            renderItem( painter, w, itemRects[index], fillBackground );

            index++;
            painter->restore();
        }
    }
pixhawk's avatar
pixhawk committed
691 692
}

Bryant's avatar
Bryant committed
693 694
/*!
  Render a legend entry into a given rectangle.
pixhawk's avatar
pixhawk committed
695

Bryant's avatar
Bryant committed
696 697 698 699 700 701 702 703 704 705
  \param painter Painter
  \param widget Widget representing a legend entry
  \param rect Bounding rectangle
  \param fillBackground When true, fill rect with the widget background 

  \note When widget is not derived from QwtLegendLabel renderItem
        does nothing beside the background
*/
void QwtLegend::renderItem( QPainter *painter, 
    const QWidget *widget, const QRectF &rect, bool fillBackground ) const
pixhawk's avatar
pixhawk committed
706
{
Bryant's avatar
Bryant committed
707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738
    if ( fillBackground )
    {
        if ( widget->autoFillBackground() ||
            widget->testAttribute( Qt::WA_StyledBackground ) )
        {
            QwtPainter::drawBackgound( painter, rect, widget );
        }
    }

    const QwtLegendLabel *label = qobject_cast<const QwtLegendLabel *>( widget );
    if ( label )
    {
        // icon

        const QwtGraphic &icon = label->data().icon();
        const QSizeF sz = icon.defaultSize();

        const QRectF iconRect( rect.x() + label->margin(),
            rect.center().y() - 0.5 * sz.height(), 
            sz.width(), sz.height() );

        icon.render( painter, iconRect, Qt::KeepAspectRatio );

        // title

        QRectF titleRect = rect;
        titleRect.setX( iconRect.right() + 2 * label->spacing() );

        painter->setFont( label->font() );
        painter->setPen( label->palette().color( QPalette::Text ) );
        const_cast< QwtLegendLabel *>( label )->drawText( painter, titleRect );
    }
pixhawk's avatar
pixhawk committed
739 740
}

Bryant's avatar
Bryant committed
741 742 743 744 745 746
/*!
  \return List of widgets associated to a item
  \param itemInfo Info about an item
  \sa legendWidget(), itemInfo(), QwtPlot::itemToInfo()
 */
QList<QWidget *> QwtLegend::legendWidgets( const QVariant &itemInfo ) const
pixhawk's avatar
pixhawk committed
747
{
Bryant's avatar
Bryant committed
748
    return d_data->itemMap.legendWidgets( itemInfo );
pixhawk's avatar
pixhawk committed
749 750
}

Bryant's avatar
Bryant committed
751 752 753 754 755 756 757
/*!
  \return First widget in the list of widgets associated to an item
  \param itemInfo Info about an item
  \sa itemInfo(), QwtPlot::itemToInfo()
  \note Almost all types of items have only one widget
*/
QWidget *QwtLegend::legendWidget( const QVariant &itemInfo ) const
pixhawk's avatar
pixhawk committed
758
{
Bryant's avatar
Bryant committed
759 760 761
    const QList<QWidget *> list = d_data->itemMap.legendWidgets( itemInfo );
    if ( list.isEmpty() )
        return NULL;
pixhawk's avatar
pixhawk committed
762

Bryant's avatar
Bryant committed
763 764 765 766 767
    return list[0];
}

/*!
  Find the item that is associated to a widget
pixhawk's avatar
pixhawk committed
768

Bryant's avatar
Bryant committed
769 770 771 772 773 774 775 776
  \param widget Widget on the legend
  \return Associated item info
  \sa legendWidget()
 */
QVariant QwtLegend::itemInfo( const QWidget *widget ) const
{
    return d_data->itemMap.itemInfo( widget );
}
pixhawk's avatar
pixhawk committed
777

Bryant's avatar
Bryant committed
778 779 780 781
//! \return True, when no item is inserted
bool QwtLegend::isEmpty() const
{
    return d_data->itemMap.isEmpty();
pixhawk's avatar
pixhawk committed
782 783 784
}

/*!
Bryant's avatar
Bryant committed
785 786 787 788 789 790
    Return the extent, that is needed for the scrollbars

    \param orientation Orientation ( 
    \return The width of the vertical scrollbar for Qt::Horizontal and v.v.
 */
int QwtLegend::scrollExtent( Qt::Orientation orientation ) const
pixhawk's avatar
pixhawk committed
791
{
Bryant's avatar
Bryant committed
792 793 794 795 796 797 798 799
    int extent = 0;

    if ( orientation == Qt::Horizontal )
        extent = verticalScrollBar()->sizeHint().width();
    else
        extent = horizontalScrollBar()->sizeHint().height();

    return extent;
pixhawk's avatar
pixhawk committed
800
}
Bryant's avatar
Bryant committed
801