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