Skip to content
qwt_panner.cpp 12.5 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
 *****************************************************************************/

// vim: expandtab

#include <qpainter.h>
#include <qpixmap.h>
#include <qevent.h>
#include <qframe.h>
#include <qcursor.h>
#if QT_VERSION < 0x040000
#include <qobjectlist.h>
#endif
#include "qwt_picker.h"
#include "qwt_array.h"
#include "qwt_panner.h"

static QwtArray<QwtPicker *> activePickers(QWidget *w)
{
    QwtArray<QwtPicker *> pickers;

#if QT_VERSION >= 0x040000
    QObjectList children = w->children();
    for ( int i = 0; i < children.size(); i++ ) {
pixhawk's avatar
pixhawk committed
        QObject *obj = children[i];
        if ( obj->inherits("QwtPicker") ) {
pixhawk's avatar
pixhawk committed
            QwtPicker *picker = (QwtPicker *)obj;
            if ( picker->isEnabled() )
                pickers += picker;
        }
    }
#else
    QObjectList *children = (QObjectList *)w->children();
    if ( children ) {
        for ( QObjectListIterator it(*children); it.current(); ++it ) {
pixhawk's avatar
pixhawk committed
            QObject *obj = (QObject *)it.current();
            if ( obj->inherits("QwtPicker") ) {
pixhawk's avatar
pixhawk committed
                QwtPicker *picker = (QwtPicker *)obj;
                if ( picker->isEnabled() ) {
pixhawk's avatar
pixhawk committed
                    pickers.resize(pickers.size() + 1);
                    pickers[int(pickers.size()) - 1] = picker;
                }
            }
        }
    }
#endif

    return pickers;
}

class QwtPanner::PrivateData
{
public:
    PrivateData():
        button(Qt::LeftButton),
        buttonState(Qt::NoButton),
        abortKey(Qt::Key_Escape),
        abortKeyState(Qt::NoButton),
#ifndef QT_NO_CURSOR
        cursor(NULL),
        restoreCursor(NULL),
        hasCursor(false),
#endif
        isEnabled(false) {
pixhawk's avatar
pixhawk committed
#if QT_VERSION >= 0x040000
        orientations = Qt::Vertical | Qt::Horizontal;
#else
        orientations[Qt::Vertical] = true;
        orientations[Qt::Horizontal] = true;
#endif
    }

    ~PrivateData() {
pixhawk's avatar
pixhawk committed
#ifndef QT_NO_CURSOR
        delete cursor;
        delete restoreCursor;
#endif
    }
pixhawk's avatar
pixhawk committed
    int button;
    int buttonState;
    int abortKey;
    int abortKeyState;

    QPoint initialPos;
    QPoint pos;

    QPixmap pixmap;
#ifndef QT_NO_CURSOR
    QCursor *cursor;
    QCursor *restoreCursor;
    bool hasCursor;
#endif
    bool isEnabled;
#if QT_VERSION >= 0x040000
    Qt::Orientations orientations;
#else
    bool orientations[2];
#endif
};

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

  \param parent Parent widget to be panned
*/
QwtPanner::QwtPanner(QWidget *parent):
    QWidget(parent)
{
    d_data = new PrivateData();

#if QT_VERSION >= 0x040000
    setAttribute(Qt::WA_TransparentForMouseEvents);
    setAttribute(Qt::WA_NoSystemBackground);
    setFocusPolicy(Qt::NoFocus);
#else
    setBackgroundMode(Qt::NoBackground);
    setFocusPolicy(QWidget::NoFocus);
#endif
    hide();

    setEnabled(true);
}

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

/*!
   Change the mouse button
   The defaults are Qt::LeftButton and Qt::NoButton
*/
void QwtPanner::setMouseButton(int button, int buttonState)
{
    d_data->button = button;
    d_data->buttonState = buttonState;
}

//! Get the mouse button
void QwtPanner::getMouseButton(int &button, int &buttonState) const
{
    button = d_data->button;
    buttonState = d_data->buttonState;
}

/*!
   Change the abort key
   The defaults are Qt::Key_Escape and Qt::NoButton
*/
void QwtPanner::setAbortKey(int key, int state)
{
    d_data->abortKey = key;
    d_data->abortKeyState = state;
}

//! Get the abort key
void QwtPanner::getAbortKey(int &key, int &state) const
{
    key = d_data->abortKey;
    state = d_data->abortKeyState;
}

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

        QWidget *w = parentWidget();
        if ( w ) {
            if ( d_data->isEnabled ) {
pixhawk's avatar
pixhawk committed
                w->installEventFilter(this);
pixhawk's avatar
pixhawk committed
                w->removeEventFilter(this);
                hide();
            }
        }
    }
}

#if QT_VERSION >= 0x040000
/*!
   Set the orientations, where panning is enabled
   The default value is in both directions: Qt::Horizontal | Qt::Vertical

   /param o Orientation
*/
void QwtPanner::setOrientations(Qt::Orientations o)
{
    d_data->orientations = o;
}

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

#else
void QwtPanner::enableOrientation(Qt::Orientation o, bool enable)
{
    if ( o == Qt::Vertical || o == Qt::Horizontal )
        d_data->orientations[o] = enable;
}
#endif

pixhawk's avatar
pixhawk committed
   Return true if a orientatio is enabled
   \sa orientations(), setOrientations()
*/
bool QwtPanner::isOrientationEnabled(Qt::Orientation o) const
{
#if QT_VERSION >= 0x040000
    return d_data->orientations & o;
#else
    if ( o == Qt::Vertical || o == Qt::Horizontal )
        return d_data->orientations[o];
    return false;
#endif
}

/*!
  \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
*/
void QwtPanner::paintEvent(QPaintEvent *pe)
{
    QPixmap pm(size());

    QPainter painter(&pm);

    const QColor bg =
#if QT_VERSION < 0x040000
        parentWidget()->palette().color(
            QPalette::Normal, QColorGroup::Background);
#else
        parentWidget()->palette().color(
            QPalette::Normal, QPalette::Background);
#endif

    painter.setPen(Qt::NoPen);
    painter.setBrush(QBrush(bg));
    painter.drawRect(0, 0, pm.width(), pm.height());

    int dx = d_data->pos.x() - d_data->initialPos.x();
    int dy = d_data->pos.y() - d_data->initialPos.y();

    QRect r(0, 0, d_data->pixmap.width(), d_data->pixmap.height());
    r.moveCenter(QPoint(r.center().x() + dx, r.center().y() + dy));

    painter.drawPixmap(r, d_data->pixmap);
    painter.end();

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

pixhawk's avatar
pixhawk committed
  \brief Event filter

  When isEnabled() the mouse events of the observed widget are filtered.

  \sa widgetMousePressEvent(), widgetMouseReleaseEvent(),
      widgetMouseMoveEvent()
*/
bool QwtPanner::eventFilter(QObject *o, QEvent *e)
{
    if ( o == NULL || o != parentWidget() )
        return false;

    switch(e->type()) {
    case QEvent::MouseButtonPress: {
        widgetMousePressEvent((QMouseEvent *)e);
        break;
    }
    case QEvent::MouseMove: {
        widgetMouseMoveEvent((QMouseEvent *)e);
        break;
    }
    case QEvent::MouseButtonRelease: {
        widgetMouseReleaseEvent((QMouseEvent *)e);
        break;
    }
    case QEvent::KeyPress: {
        widgetKeyPressEvent((QKeyEvent *)e);
        break;
    }
    case QEvent::KeyRelease: {
        widgetKeyReleaseEvent((QKeyEvent *)e);
        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.

  \param me Mouse event
  \sa eventFilter(), widgetMouseReleaseEvent(),
      widgetMouseMoveEvent(),
*/
void QwtPanner::widgetMousePressEvent(QMouseEvent *me)
{
    if ( me->button() != d_data->button )
        return;

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

#if QT_VERSION < 0x040000
    if ( (me->state() & Qt::KeyButtonMask) !=
            (d_data->buttonState & Qt::KeyButtonMask) )
pixhawk's avatar
pixhawk committed
#else
    if ( (me->modifiers() & Qt::KeyboardModifierMask) !=
            (int)(d_data->buttonState & Qt::KeyboardModifierMask) )
pixhawk's avatar
pixhawk committed
#endif
    {
        return;
    }

#ifndef QT_NO_CURSOR
    showCursor(true);
#endif

    d_data->initialPos = d_data->pos = me->pos();

    QRect cr = parentWidget()->rect();
    if ( parentWidget()->inherits("QFrame") ) {
pixhawk's avatar
pixhawk committed
        const QFrame* frame = (QFrame*)parentWidget();
        cr = frame->contentsRect();
    }
    setGeometry(cr);

    // We don't want to grab the picker !
    QwtArray<QwtPicker *> pickers = activePickers(parentWidget());
    for ( int i = 0; i < (int)pickers.size(); i++ )
        pickers[i]->setEnabled(false);

    d_data->pixmap = QPixmap::grabWidget(parentWidget(),
                                         cr.x(), cr.y(), cr.width(), cr.height());
pixhawk's avatar
pixhawk committed

    for ( int i = 0; i < (int)pickers.size(); i++ )
        pickers[i]->setEnabled(true);

    show();
}

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

  \param me Mouse event
  \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent()
*/
void QwtPanner::widgetMouseMoveEvent(QMouseEvent *me)
{
    if ( !isVisible() )
        return;

    QPoint pos = me->pos();
    if ( !isOrientationEnabled(Qt::Horizontal) )
        pos.setX(d_data->initialPos.x());
    if ( !isOrientationEnabled(Qt::Vertical) )
        pos.setY(d_data->initialPos.y());

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

        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.

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

        QPoint pos = me->pos();
        if ( !isOrientationEnabled(Qt::Horizontal) )
            pos.setX(d_data->initialPos.x());
        if ( !isOrientationEnabled(Qt::Vertical) )
            pos.setY(d_data->initialPos.y());

        d_data->pixmap = QPixmap();
        d_data->pos = pos;

        if ( d_data->pos != d_data->initialPos ) {
            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.

  \param ke Key event
  \sa eventFilter(), widgetKeyReleaseEvent()
*/
void QwtPanner::widgetKeyPressEvent(QKeyEvent *ke)
{
    if ( ke->key() == d_data->abortKey ) {
pixhawk's avatar
pixhawk committed
        const bool matched =
#if QT_VERSION < 0x040000
            (ke->state() & Qt::KeyButtonMask) ==
            (d_data->abortKeyState & Qt::KeyButtonMask);
pixhawk's avatar
pixhawk committed
#else
            (ke->modifiers() & Qt::KeyboardModifierMask) ==
            (int)(d_data->abortKeyState & Qt::KeyboardModifierMask);
pixhawk's avatar
pixhawk committed
#endif
        if ( matched ) {
pixhawk's avatar
pixhawk committed
            hide();
#ifndef QT_NO_CURSOR
            showCursor(false);
#endif
            d_data->pixmap = QPixmap();
        }
    }
}

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

  \param ke Key event
  \sa eventFilter(), widgetKeyReleaseEvent()
*/
void QwtPanner::widgetKeyReleaseEvent(QKeyEvent *)
{
}

#ifndef QT_NO_CURSOR
void QwtPanner::showCursor(bool on)
{
    if ( on == d_data->hasCursor )
        return;

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

    d_data->hasCursor = on;

    if ( on ) {
pixhawk's avatar
pixhawk committed
#if QT_VERSION < 0x040000
        if ( w->testWState(WState_OwnCursor) )
#else
        if ( w->testAttribute(Qt::WA_SetCursor) )
#endif
        {
            delete d_data->restoreCursor;
            d_data->restoreCursor = new QCursor(w->cursor());
        }
        w->setCursor(*d_data->cursor);
    } else {
        if ( d_data->restoreCursor ) {
pixhawk's avatar
pixhawk committed
            w->setCursor(*d_data->restoreCursor);
            delete d_data->restoreCursor;
            d_data->restoreCursor = NULL;
pixhawk's avatar
pixhawk committed
            w->unsetCursor();
    }
}
#endif