qwt_panner.cpp 12.1 KB
Newer Older
pixhawk's avatar
pixhawk committed
1
2
3
4
5
6
7
8
9
/* -*- 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
 *****************************************************************************/

Bryant's avatar
Bryant committed
10
11
12
#include "qwt_panner.h"
#include "qwt_picker.h"
#include "qwt_painter.h"
pixhawk's avatar
pixhawk committed
13
14
15
16
#include <qpainter.h>
#include <qpixmap.h>
#include <qevent.h>
#include <qcursor.h>
Bryant's avatar
Bryant committed
17
#include <qbitmap.h>
pixhawk's avatar
pixhawk committed
18

Bryant's avatar
Bryant committed
19
static QVector<QwtPicker *> qwtActivePickers( QWidget *w )
pixhawk's avatar
pixhawk committed
20
{
Bryant's avatar
Bryant committed
21
    QVector<QwtPicker *> pickers;
pixhawk's avatar
pixhawk committed
22
23

    QObjectList children = w->children();
Bryant's avatar
Bryant committed
24
25
26
27
28
    for ( int i = 0; i < children.size(); i++ )
    {
        QwtPicker *picker = qobject_cast<QwtPicker *>( children[i] );
        if ( picker && picker->isEnabled() )
            pickers += picker;
pixhawk's avatar
pixhawk committed
29
30
31
32
33
34
35
36
37
    }

    return pickers;
}

class QwtPanner::PrivateData
{
public:
    PrivateData():
Bryant's avatar
Bryant committed
38
39
40
41
        button( Qt::LeftButton ),
        buttonModifiers( Qt::NoModifier ),
        abortKey( Qt::Key_Escape ),
        abortKeyModifiers( Qt::NoModifier ),
pixhawk's avatar
pixhawk committed
42
#ifndef QT_NO_CURSOR
Bryant's avatar
Bryant committed
43
44
45
        cursor( NULL ),
        restoreCursor( NULL ),
        hasCursor( false ),
pixhawk's avatar
pixhawk committed
46
#endif
Bryant's avatar
Bryant committed
47
48
        isEnabled( false )
    {
pixhawk's avatar
pixhawk committed
49
50
51
        orientations = Qt::Vertical | Qt::Horizontal;
    }

Bryant's avatar
Bryant committed
52
53
    ~PrivateData()
    {
pixhawk's avatar
pixhawk committed
54
55
56
57
58
#ifndef QT_NO_CURSOR
        delete cursor;
        delete restoreCursor;
#endif
    }
59

Bryant's avatar
Bryant committed
60
61
62
    Qt::MouseButton button;
    Qt::KeyboardModifiers  buttonModifiers;

pixhawk's avatar
pixhawk committed
63
    int abortKey;
Bryant's avatar
Bryant committed
64
    Qt::KeyboardModifiers abortKeyModifiers;
pixhawk's avatar
pixhawk committed
65
66
67
68
69

    QPoint initialPos;
    QPoint pos;

    QPixmap pixmap;
Bryant's avatar
Bryant committed
70
71
    QBitmap contentsMask;

pixhawk's avatar
pixhawk committed
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#ifndef QT_NO_CURSOR
    QCursor *cursor;
    QCursor *restoreCursor;
    bool hasCursor;
#endif
    bool isEnabled;
    Qt::Orientations orientations;
};

/*!
  Creates an panner that is enabled for the left mouse button.

  \param parent Parent widget to be panned
*/
Bryant's avatar
Bryant committed
86
87
QwtPanner::QwtPanner( QWidget *parent ):
    QWidget( parent )
pixhawk's avatar
pixhawk committed
88
89
90
{
    d_data = new PrivateData();

Bryant's avatar
Bryant committed
91
92
93
    setAttribute( Qt::WA_TransparentForMouseEvents );
    setAttribute( Qt::WA_NoSystemBackground );
    setFocusPolicy( Qt::NoFocus );
pixhawk's avatar
pixhawk committed
94
95
    hide();

Bryant's avatar
Bryant committed
96
    setEnabled( true );
pixhawk's avatar
pixhawk committed
97
98
99
100
101
102
103
104
105
}

//! Destructor
QwtPanner::~QwtPanner()
{
    delete d_data;
}

/*!
Bryant's avatar
Bryant committed
106
107
   Change the mouse button and modifiers used for panning
   The defaults are Qt::LeftButton and Qt::NoModifier
pixhawk's avatar
pixhawk committed
108
*/
Bryant's avatar
Bryant committed
109
110
void QwtPanner::setMouseButton( Qt::MouseButton button,
    Qt::KeyboardModifiers modifiers )
pixhawk's avatar
pixhawk committed
111
112
{
    d_data->button = button;
Bryant's avatar
Bryant committed
113
    d_data->buttonModifiers = modifiers;
pixhawk's avatar
pixhawk committed
114
115
}

Bryant's avatar
Bryant committed
116
117
118
//! Get mouse button and modifiers used for panning
void QwtPanner::getMouseButton( Qt::MouseButton &button,
    Qt::KeyboardModifiers &modifiers ) const
pixhawk's avatar
pixhawk committed
119
120
{
    button = d_data->button;
Bryant's avatar
Bryant committed
121
    modifiers = d_data->buttonModifiers;
pixhawk's avatar
pixhawk committed
122
123
124
125
}

/*!
   Change the abort key
Bryant's avatar
Bryant committed
126
127
128
129
   The defaults are Qt::Key_Escape and Qt::NoModifiers

   \param key Key ( See Qt::Keycode )
   \param modifiers Keyboard modifiers
pixhawk's avatar
pixhawk committed
130
*/
Bryant's avatar
Bryant committed
131
132
void QwtPanner::setAbortKey( int key, 
    Qt::KeyboardModifiers modifiers )
pixhawk's avatar
pixhawk committed
133
134
{
    d_data->abortKey = key;
Bryant's avatar
Bryant committed
135
    d_data->abortKeyModifiers = modifiers;
pixhawk's avatar
pixhawk committed
136
137
}

Bryant's avatar
Bryant committed
138
139
140
//! Get the abort key and modifiers
void QwtPanner::getAbortKey( int &key, 
    Qt::KeyboardModifiers &modifiers ) const
pixhawk's avatar
pixhawk committed
141
142
{
    key = d_data->abortKey;
Bryant's avatar
Bryant committed
143
    modifiers = d_data->abortKeyModifiers;
pixhawk's avatar
pixhawk committed
144
145
146
147
148
149
150
151
152
153
154
}

/*!
   Change the cursor, that is active while panning
   The default is the cursor of the parent widget.

   \param cursor New cursor

   \sa setCursor()
*/
#ifndef QT_NO_CURSOR
Bryant's avatar
Bryant committed
155
void QwtPanner::setCursor( const QCursor &cursor )
pixhawk's avatar
pixhawk committed
156
{
Bryant's avatar
Bryant committed
157
    d_data->cursor = new QCursor( cursor );
pixhawk's avatar
pixhawk committed
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
}
#endif

/*!
   \return Cursor that is active while panning
   \sa setCursor()
*/
#ifndef QT_NO_CURSOR
const QCursor QwtPanner::cursor() const
{
    if ( d_data->cursor )
        return *d_data->cursor;

    if ( parentWidget() )
        return parentWidget()->cursor();

    return QCursor();
}
#endif

178
/*!
pixhawk's avatar
pixhawk committed
179
  \brief En/disable the panner
180

pixhawk's avatar
pixhawk committed
181
182
183
184
185
186
  When enabled is true an event filter is installed for
  the observed widget, otherwise the event filter is removed.

  \param on true or false
  \sa isEnabled(), eventFilter()
*/
Bryant's avatar
Bryant committed
187
void QwtPanner::setEnabled( bool on )
pixhawk's avatar
pixhawk committed
188
{
Bryant's avatar
Bryant committed
189
190
    if ( d_data->isEnabled != on )
    {
pixhawk's avatar
pixhawk committed
191
192
193
        d_data->isEnabled = on;

        QWidget *w = parentWidget();
Bryant's avatar
Bryant committed
194
195
196
197
198
199
200
201
202
        if ( w )
        {
            if ( d_data->isEnabled )
            {
                w->installEventFilter( this );
            }
            else
            {
                w->removeEventFilter( this );
pixhawk's avatar
pixhawk committed
203
204
205
206
207
208
209
210
211
212
213
214
                hide();
            }
        }
    }
}

/*!
   Set the orientations, where panning is enabled
   The default value is in both directions: Qt::Horizontal | Qt::Vertical

   /param o Orientation
*/
Bryant's avatar
Bryant committed
215
void QwtPanner::setOrientations( Qt::Orientations o )
pixhawk's avatar
pixhawk committed
216
217
218
219
220
221
222
223
224
225
{
    d_data->orientations = o;
}

//! Return the orientation, where paning is enabled
Qt::Orientations QwtPanner::orientations() const
{
    return d_data->orientations;
}

226
/*!
Bryant's avatar
Bryant committed
227
   \return True if an orientation is enabled
pixhawk's avatar
pixhawk committed
228
229
   \sa orientations(), setOrientations()
*/
Bryant's avatar
Bryant committed
230
bool QwtPanner::isOrientationEnabled( Qt::Orientation o ) const
pixhawk's avatar
pixhawk committed
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
{
    return d_data->orientations & o;
}

/*!
  \return true when enabled, false otherwise
  \sa setEnabled, eventFilter()
*/
bool QwtPanner::isEnabled() const
{
    return d_data->isEnabled;
}

/*!
   \brief Paint event

   Repaint the grabbed pixmap on its current position and
   fill the empty spaces by the background of the parent widget.

   \param pe Paint event
*/
Bryant's avatar
Bryant committed
252
void QwtPanner::paintEvent( QPaintEvent *pe )
pixhawk's avatar
pixhawk committed
253
{
Bryant's avatar
Bryant committed
254
255
    int dx = d_data->pos.x() - d_data->initialPos.x();
    int dy = d_data->pos.y() - d_data->initialPos.y();
pixhawk's avatar
pixhawk committed
256

Bryant's avatar
Bryant committed
257
258
    QRect r( 0, 0, d_data->pixmap.width(), d_data->pixmap.height() );
    r.moveCenter( QPoint( r.center().x() + dx, r.center().y() + dy ) );
pixhawk's avatar
pixhawk committed
259

Bryant's avatar
Bryant committed
260
261
    QPixmap pm( size() );
    QwtPainter::fillPixmap( parentWidget(), pm );
pixhawk's avatar
pixhawk committed
262

Bryant's avatar
Bryant committed
263
    QPainter painter( &pm );
pixhawk's avatar
pixhawk committed
264

Bryant's avatar
Bryant committed
265
266
267
268
269
270
271
272
273
274
    if ( !d_data->contentsMask.isNull() )
    {
        QPixmap masked = d_data->pixmap;
        masked.setMask( d_data->contentsMask );
        painter.drawPixmap( r, masked );
    }
    else
    {
        painter.drawPixmap( r, d_data->pixmap );
    }
pixhawk's avatar
pixhawk committed
275
276
277

    painter.end();

Bryant's avatar
Bryant committed
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
    if ( !d_data->contentsMask.isNull() )
        pm.setMask( d_data->contentsMask );

    painter.begin( this );
    painter.setClipRegion( pe->region() );
    painter.drawPixmap( 0, 0, pm );
}

/*!
  \brief Calculate a mask for the contents of the panned widget

  Sometimes only parts of the contents of a widget should be
  panned. F.e. for a widget with a styled background with rounded borders
  only the area inside of the border should be panned.

  \return An empty bitmap, indicating no mask
*/
QBitmap QwtPanner::contentsMask() const
{
    return QBitmap();
}

/*!
  Grab the widget into a pixmap.
  \return Grabbed pixmap
*/
QPixmap QwtPanner::grab() const
{
#if QT_VERSION >= 0x050000
    return parentWidget()->grab( parentWidget()->rect() );
#else
    return QPixmap::grabWidget( parentWidget() );
#endif
pixhawk's avatar
pixhawk committed
311
312
}

313
/*!
pixhawk's avatar
pixhawk committed
314
315
  \brief Event filter

Bryant's avatar
Bryant committed
316
317
318
319
320
321
322
323
  When isEnabled() is true mouse events of the
  observed widget are filtered.

  \param object Object to be filtered
  \param event Event

  \return Always false, beside for paint events for the
          parent widget.
pixhawk's avatar
pixhawk committed
324
325
326
327

  \sa widgetMousePressEvent(), widgetMouseReleaseEvent(),
      widgetMouseMoveEvent()
*/
Bryant's avatar
Bryant committed
328
bool QwtPanner::eventFilter( QObject *object, QEvent *event )
pixhawk's avatar
pixhawk committed
329
{
Bryant's avatar
Bryant committed
330
    if ( object == NULL || object != parentWidget() )
pixhawk's avatar
pixhawk committed
331
332
        return false;

Bryant's avatar
Bryant committed
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
    switch ( event->type() )
    {
        case QEvent::MouseButtonPress:
        {
            widgetMousePressEvent( static_cast<QMouseEvent *>( event ) );
            break;
        }
        case QEvent::MouseMove:
        {
            widgetMouseMoveEvent( static_cast<QMouseEvent *>( event ) );
            break;
        }
        case QEvent::MouseButtonRelease:
        {
            widgetMouseReleaseEvent( static_cast<QMouseEvent *>( event ) );
            break;
        }
        case QEvent::KeyPress:
        {
            widgetKeyPressEvent( static_cast<QKeyEvent *>( event ) );
            break;
        }
        case QEvent::KeyRelease:
        {
            widgetKeyReleaseEvent( static_cast<QKeyEvent *>( event ) );
            break;
        }
        case QEvent::Paint:
        {
            if ( isVisible() )
                return true;
            break;
        }
        default:;
pixhawk's avatar
pixhawk committed
367
368
369
370
371
372
373
374
    }

    return false;
}

/*!
  Handle a mouse press event for the observed widget.

Bryant's avatar
Bryant committed
375
  \param mouseEvent Mouse event
pixhawk's avatar
pixhawk committed
376
377
378
  \sa eventFilter(), widgetMouseReleaseEvent(),
      widgetMouseMoveEvent(),
*/
Bryant's avatar
Bryant committed
379
void QwtPanner::widgetMousePressEvent( QMouseEvent *mouseEvent )
pixhawk's avatar
pixhawk committed
380
{
Bryant's avatar
Bryant committed
381
382
383
    if ( ( mouseEvent->button() != d_data->button )
        || ( mouseEvent->modifiers() != d_data->buttonModifiers ) )
    {
pixhawk's avatar
pixhawk committed
384
        return;
Bryant's avatar
Bryant committed
385
    }
pixhawk's avatar
pixhawk committed
386
387
388
389
390
391

    QWidget *w = parentWidget();
    if ( w == NULL )
        return;

#ifndef QT_NO_CURSOR
Bryant's avatar
Bryant committed
392
    showCursor( true );
pixhawk's avatar
pixhawk committed
393
394
#endif

Bryant's avatar
Bryant committed
395
    d_data->initialPos = d_data->pos = mouseEvent->pos();
pixhawk's avatar
pixhawk committed
396

Bryant's avatar
Bryant committed
397
    setGeometry( parentWidget()->rect() );
pixhawk's avatar
pixhawk committed
398
399

    // We don't want to grab the picker !
Bryant's avatar
Bryant committed
400
401
402
    QVector<QwtPicker *> pickers = qwtActivePickers( parentWidget() );
    for ( int i = 0; i < pickers.size(); i++ )
        pickers[i]->setEnabled( false );
pixhawk's avatar
pixhawk committed
403

Bryant's avatar
Bryant committed
404
405
    d_data->pixmap = grab();
    d_data->contentsMask = contentsMask();
pixhawk's avatar
pixhawk committed
406

Bryant's avatar
Bryant committed
407
408
    for ( int i = 0; i < pickers.size(); i++ )
        pickers[i]->setEnabled( true );
pixhawk's avatar
pixhawk committed
409
410
411
412
413
414
415

    show();
}

/*!
  Handle a mouse move event for the observed widget.

Bryant's avatar
Bryant committed
416
  \param mouseEvent Mouse event
pixhawk's avatar
pixhawk committed
417
418
  \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent()
*/
Bryant's avatar
Bryant committed
419
void QwtPanner::widgetMouseMoveEvent( QMouseEvent *mouseEvent )
pixhawk's avatar
pixhawk committed
420
421
422
423
{
    if ( !isVisible() )
        return;

Bryant's avatar
Bryant committed
424
425
426
427
428
    QPoint pos = mouseEvent->pos();
    if ( !isOrientationEnabled( Qt::Horizontal ) )
        pos.setX( d_data->initialPos.x() );
    if ( !isOrientationEnabled( Qt::Vertical ) )
        pos.setY( d_data->initialPos.y() );
pixhawk's avatar
pixhawk committed
429

Bryant's avatar
Bryant committed
430
431
    if ( pos != d_data->pos && rect().contains( pos ) )
    {
pixhawk's avatar
pixhawk committed
432
433
434
        d_data->pos = pos;
        update();

Bryant's avatar
Bryant committed
435
436
        Q_EMIT moved( d_data->pos.x() - d_data->initialPos.x(),
            d_data->pos.y() - d_data->initialPos.y() );
pixhawk's avatar
pixhawk committed
437
438
439
440
441
442
    }
}

/*!
  Handle a mouse release event for the observed widget.

Bryant's avatar
Bryant committed
443
  \param mouseEvent Mouse event
pixhawk's avatar
pixhawk committed
444
445
446
  \sa eventFilter(), widgetMousePressEvent(),
      widgetMouseMoveEvent(),
*/
Bryant's avatar
Bryant committed
447
void QwtPanner::widgetMouseReleaseEvent( QMouseEvent *mouseEvent )
pixhawk's avatar
pixhawk committed
448
{
Bryant's avatar
Bryant committed
449
450
    if ( isVisible() )
    {
pixhawk's avatar
pixhawk committed
451
452
        hide();
#ifndef QT_NO_CURSOR
Bryant's avatar
Bryant committed
453
        showCursor( false );
pixhawk's avatar
pixhawk committed
454
455
#endif

Bryant's avatar
Bryant committed
456
457
458
459
460
        QPoint pos = mouseEvent->pos();
        if ( !isOrientationEnabled( Qt::Horizontal ) )
            pos.setX( d_data->initialPos.x() );
        if ( !isOrientationEnabled( Qt::Vertical ) )
            pos.setY( d_data->initialPos.y() );
pixhawk's avatar
pixhawk committed
461
462

        d_data->pixmap = QPixmap();
Bryant's avatar
Bryant committed
463
        d_data->contentsMask = QBitmap();
pixhawk's avatar
pixhawk committed
464
465
        d_data->pos = pos;

Bryant's avatar
Bryant committed
466
467
468
469
        if ( d_data->pos != d_data->initialPos )
        {
            Q_EMIT panned( d_data->pos.x() - d_data->initialPos.x(),
                d_data->pos.y() - d_data->initialPos.y() );
pixhawk's avatar
pixhawk committed
470
471
472
473
474
475
476
        }
    }
}

/*!
  Handle a key press event for the observed widget.

Bryant's avatar
Bryant committed
477
  \param keyEvent Key event
pixhawk's avatar
pixhawk committed
478
479
  \sa eventFilter(), widgetKeyReleaseEvent()
*/
Bryant's avatar
Bryant committed
480
void QwtPanner::widgetKeyPressEvent( QKeyEvent *keyEvent )
pixhawk's avatar
pixhawk committed
481
{
Bryant's avatar
Bryant committed
482
483
484
485
486
    if ( ( keyEvent->key() == d_data->abortKey )
        && ( keyEvent->modifiers() == d_data->abortKeyModifiers ) )
    {
        hide();

pixhawk's avatar
pixhawk committed
487
#ifndef QT_NO_CURSOR
Bryant's avatar
Bryant committed
488
        showCursor( false );
pixhawk's avatar
pixhawk committed
489
#endif
Bryant's avatar
Bryant committed
490
        d_data->pixmap = QPixmap();
pixhawk's avatar
pixhawk committed
491
492
493
494
495
496
    }
}

/*!
  Handle a key release event for the observed widget.

Bryant's avatar
Bryant committed
497
  \param keyEvent Key event
pixhawk's avatar
pixhawk committed
498
499
  \sa eventFilter(), widgetKeyReleaseEvent()
*/
Bryant's avatar
Bryant committed
500
void QwtPanner::widgetKeyReleaseEvent( QKeyEvent *keyEvent )
pixhawk's avatar
pixhawk committed
501
{
Bryant's avatar
Bryant committed
502
    Q_UNUSED( keyEvent );
pixhawk's avatar
pixhawk committed
503
504
505
}

#ifndef QT_NO_CURSOR
Bryant's avatar
Bryant committed
506
void QwtPanner::showCursor( bool on )
pixhawk's avatar
pixhawk committed
507
508
509
510
511
512
513
514
515
516
{
    if ( on == d_data->hasCursor )
        return;

    QWidget *w = parentWidget();
    if ( w == NULL || d_data->cursor == NULL )
        return;

    d_data->hasCursor = on;

Bryant's avatar
Bryant committed
517
518
519
    if ( on )
    {
        if ( w->testAttribute( Qt::WA_SetCursor ) )
pixhawk's avatar
pixhawk committed
520
521
        {
            delete d_data->restoreCursor;
Bryant's avatar
Bryant committed
522
            d_data->restoreCursor = new QCursor( w->cursor() );
pixhawk's avatar
pixhawk committed
523
        }
Bryant's avatar
Bryant committed
524
525
526
527
528
529
530
        w->setCursor( *d_data->cursor );
    }
    else
    {
        if ( d_data->restoreCursor )
        {
            w->setCursor( *d_data->restoreCursor );
pixhawk's avatar
pixhawk committed
531
532
            delete d_data->restoreCursor;
            d_data->restoreCursor = NULL;
Bryant's avatar
Bryant committed
533
534
        }
        else
pixhawk's avatar
pixhawk committed
535
536
537
538
            w->unsetCursor();
    }
}
#endif