Skip to content
qwt_panner.cpp 12.1 KiB
Newer Older
pixhawk's avatar
pixhawk committed
/* -*- 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
#include "qwt_panner.h"
#include "qwt_picker.h"
#include "qwt_painter.h"
pixhawk's avatar
pixhawk committed
#include <qpainter.h>
#include <qpixmap.h>
#include <qevent.h>
#include <qcursor.h>
Bryant's avatar
Bryant committed
#include <qbitmap.h>
pixhawk's avatar
pixhawk committed

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

    QObjectList children = w->children();
Bryant's avatar
Bryant committed
    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
    }

    return pickers;
}

class QwtPanner::PrivateData
{
public:
    PrivateData():
Bryant's avatar
Bryant committed
        button( Qt::LeftButton ),
        buttonModifiers( Qt::NoModifier ),
        abortKey( Qt::Key_Escape ),
        abortKeyModifiers( Qt::NoModifier ),
pixhawk's avatar
pixhawk committed
#ifndef QT_NO_CURSOR
Bryant's avatar
Bryant committed
        cursor( NULL ),
        restoreCursor( NULL ),
        hasCursor( false ),
pixhawk's avatar
pixhawk committed
#endif
Bryant's avatar
Bryant committed
        isEnabled( false )
    {
pixhawk's avatar
pixhawk committed
        orientations = Qt::Vertical | Qt::Horizontal;
    }

Bryant's avatar
Bryant committed
    ~PrivateData()
    {
pixhawk's avatar
pixhawk committed
#ifndef QT_NO_CURSOR
        delete cursor;
        delete restoreCursor;
#endif
    }
Bryant's avatar
Bryant committed
    Qt::MouseButton button;
    Qt::KeyboardModifiers  buttonModifiers;

pixhawk's avatar
pixhawk committed
    int abortKey;
Bryant's avatar
Bryant committed
    Qt::KeyboardModifiers abortKeyModifiers;
pixhawk's avatar
pixhawk committed

    QPoint initialPos;
    QPoint pos;

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

pixhawk's avatar
pixhawk committed
#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
QwtPanner::QwtPanner( QWidget *parent ):
    QWidget( parent )
pixhawk's avatar
pixhawk committed
{
    d_data = new PrivateData();

Bryant's avatar
Bryant committed
    setAttribute( Qt::WA_TransparentForMouseEvents );
    setAttribute( Qt::WA_NoSystemBackground );
    setFocusPolicy( Qt::NoFocus );
pixhawk's avatar
pixhawk committed
    hide();

Bryant's avatar
Bryant committed
    setEnabled( true );
pixhawk's avatar
pixhawk committed
}

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

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

Bryant's avatar
Bryant committed
//! Get mouse button and modifiers used for panning
void QwtPanner::getMouseButton( Qt::MouseButton &button,
    Qt::KeyboardModifiers &modifiers ) const
pixhawk's avatar
pixhawk committed
{
    button = d_data->button;
Bryant's avatar
Bryant committed
    modifiers = d_data->buttonModifiers;
pixhawk's avatar
pixhawk committed
}

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

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

Bryant's avatar
Bryant committed
//! Get the abort key and modifiers
void QwtPanner::getAbortKey( int &key, 
    Qt::KeyboardModifiers &modifiers ) const
pixhawk's avatar
pixhawk committed
{
    key = d_data->abortKey;
Bryant's avatar
Bryant committed
    modifiers = d_data->abortKeyModifiers;
pixhawk's avatar
pixhawk committed
}

/*!
   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
void QwtPanner::setCursor( const QCursor &cursor )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    d_data->cursor = new QCursor( cursor );
pixhawk's avatar
pixhawk committed
}
#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

pixhawk's avatar
pixhawk committed
  \brief En/disable the panner
pixhawk's avatar
pixhawk committed
  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
void QwtPanner::setEnabled( bool on )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( d_data->isEnabled != on )
    {
pixhawk's avatar
pixhawk committed
        d_data->isEnabled = on;

        QWidget *w = parentWidget();
Bryant's avatar
Bryant committed
        if ( w )
        {
            if ( d_data->isEnabled )
            {
                w->installEventFilter( this );
            }
            else
            {
                w->removeEventFilter( this );
pixhawk's avatar
pixhawk committed
                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
void QwtPanner::setOrientations( Qt::Orientations o )
pixhawk's avatar
pixhawk committed
{
    d_data->orientations = o;
}

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

Bryant's avatar
Bryant committed
   \return True if an orientation is enabled
pixhawk's avatar
pixhawk committed
   \sa orientations(), setOrientations()
*/
Bryant's avatar
Bryant committed
bool QwtPanner::isOrientationEnabled( Qt::Orientation o ) const
pixhawk's avatar
pixhawk committed
{
    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
void QwtPanner::paintEvent( QPaintEvent *pe )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    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

Bryant's avatar
Bryant committed
    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

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

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

Bryant's avatar
Bryant committed
    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

    painter.end();

Bryant's avatar
Bryant committed
    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
}

pixhawk's avatar
pixhawk committed
  \brief Event filter

Bryant's avatar
Bryant committed
  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

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

Bryant's avatar
Bryant committed
    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
    }

    return false;
}

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

Bryant's avatar
Bryant committed
  \param mouseEvent Mouse event
pixhawk's avatar
pixhawk committed
  \sa eventFilter(), widgetMouseReleaseEvent(),
      widgetMouseMoveEvent(),
*/
Bryant's avatar
Bryant committed
void QwtPanner::widgetMousePressEvent( QMouseEvent *mouseEvent )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( ( mouseEvent->button() != d_data->button )
        || ( mouseEvent->modifiers() != d_data->buttonModifiers ) )
    {
pixhawk's avatar
pixhawk committed
        return;
Bryant's avatar
Bryant committed
    }
pixhawk's avatar
pixhawk committed

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

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

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

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

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

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

Bryant's avatar
Bryant committed
    for ( int i = 0; i < pickers.size(); i++ )
        pickers[i]->setEnabled( true );
pixhawk's avatar
pixhawk committed

    show();
}

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

Bryant's avatar
Bryant committed
  \param mouseEvent Mouse event
pixhawk's avatar
pixhawk committed
  \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent()
*/
Bryant's avatar
Bryant committed
void QwtPanner::widgetMouseMoveEvent( QMouseEvent *mouseEvent )
pixhawk's avatar
pixhawk committed
{
    if ( !isVisible() )
        return;

Bryant's avatar
Bryant committed
    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

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

Bryant's avatar
Bryant committed
        Q_EMIT moved( d_data->pos.x() - d_data->initialPos.x(),
            d_data->pos.y() - d_data->initialPos.y() );
pixhawk's avatar
pixhawk committed
    }
}

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

Bryant's avatar
Bryant committed
  \param mouseEvent Mouse event
pixhawk's avatar
pixhawk committed
  \sa eventFilter(), widgetMousePressEvent(),
      widgetMouseMoveEvent(),
*/
Bryant's avatar
Bryant committed
void QwtPanner::widgetMouseReleaseEvent( QMouseEvent *mouseEvent )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( isVisible() )
    {
pixhawk's avatar
pixhawk committed
        hide();
#ifndef QT_NO_CURSOR
Bryant's avatar
Bryant committed
        showCursor( false );
pixhawk's avatar
pixhawk committed
#endif

Bryant's avatar
Bryant committed
        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

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

Bryant's avatar
Bryant committed
        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
        }
    }
}

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

Bryant's avatar
Bryant committed
  \param keyEvent Key event
pixhawk's avatar
pixhawk committed
  \sa eventFilter(), widgetKeyReleaseEvent()
*/
Bryant's avatar
Bryant committed
void QwtPanner::widgetKeyPressEvent( QKeyEvent *keyEvent )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( ( keyEvent->key() == d_data->abortKey )
        && ( keyEvent->modifiers() == d_data->abortKeyModifiers ) )
    {
        hide();

pixhawk's avatar
pixhawk committed
#ifndef QT_NO_CURSOR
Bryant's avatar
Bryant committed
        showCursor( false );
pixhawk's avatar
pixhawk committed
#endif
Bryant's avatar
Bryant committed
        d_data->pixmap = QPixmap();
pixhawk's avatar
pixhawk committed
    }
}

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

Bryant's avatar
Bryant committed
  \param keyEvent Key event
pixhawk's avatar
pixhawk committed
  \sa eventFilter(), widgetKeyReleaseEvent()
*/
Bryant's avatar
Bryant committed
void QwtPanner::widgetKeyReleaseEvent( QKeyEvent *keyEvent )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    Q_UNUSED( keyEvent );
pixhawk's avatar
pixhawk committed
}

#ifndef QT_NO_CURSOR
Bryant's avatar
Bryant committed
void QwtPanner::showCursor( bool on )
pixhawk's avatar
pixhawk committed
{
    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
    if ( on )
    {
        if ( w->testAttribute( Qt::WA_SetCursor ) )
pixhawk's avatar
pixhawk committed
        {
            delete d_data->restoreCursor;
Bryant's avatar
Bryant committed
            d_data->restoreCursor = new QCursor( w->cursor() );
pixhawk's avatar
pixhawk committed
        }
Bryant's avatar
Bryant committed
        w->setCursor( *d_data->cursor );
    }
    else
    {
        if ( d_data->restoreCursor )
        {
            w->setCursor( *d_data->restoreCursor );
pixhawk's avatar
pixhawk committed
            delete d_data->restoreCursor;
            d_data->restoreCursor = NULL;
Bryant's avatar
Bryant committed
        }
        else
pixhawk's avatar
pixhawk committed
            w->unsetCursor();
    }
}
#endif