You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
546 lines
12 KiB
546 lines
12 KiB
/* -*- 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++ ) { |
|
QObject *obj = children[i]; |
|
if ( obj->inherits("QwtPicker") ) { |
|
QwtPicker *picker = (QwtPicker *)obj; |
|
if ( picker->isEnabled() ) |
|
pickers += picker; |
|
} |
|
} |
|
#else |
|
QObjectList *children = (QObjectList *)w->children(); |
|
if ( children ) { |
|
for ( QObjectListIterator it(*children); it.current(); ++it ) { |
|
QObject *obj = (QObject *)it.current(); |
|
if ( obj->inherits("QwtPicker") ) { |
|
QwtPicker *picker = (QwtPicker *)obj; |
|
if ( picker->isEnabled() ) { |
|
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) { |
|
#if QT_VERSION >= 0x040000 |
|
orientations = Qt::Vertical | Qt::Horizontal; |
|
#else |
|
orientations[Qt::Vertical] = true; |
|
orientations[Qt::Horizontal] = true; |
|
#endif |
|
} |
|
|
|
~PrivateData() { |
|
#ifndef QT_NO_CURSOR |
|
delete cursor; |
|
delete restoreCursor; |
|
#endif |
|
} |
|
|
|
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 |
|
|
|
/*! |
|
\brief En/disable the panner |
|
|
|
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 ) { |
|
d_data->isEnabled = on; |
|
|
|
QWidget *w = parentWidget(); |
|
if ( w ) { |
|
if ( d_data->isEnabled ) { |
|
w->installEventFilter(this); |
|
} else { |
|
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 |
|
|
|
/*! |
|
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); |
|
} |
|
|
|
/*! |
|
\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: |
|
; |
|
} |
|
|
|
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) ) |
|
#else |
|
if ( (me->modifiers() & Qt::KeyboardModifierMask) != |
|
(int)(d_data->buttonState & Qt::KeyboardModifierMask) ) |
|
#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") ) { |
|
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()); |
|
|
|
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) ) { |
|
d_data->pos = pos; |
|
update(); |
|
|
|
emit moved(d_data->pos.x() - d_data->initialPos.x(), |
|
d_data->pos.y() - d_data->initialPos.y()); |
|
} |
|
} |
|
|
|
/*! |
|
Handle a mouse release event for the observed widget. |
|
|
|
\param me Mouse event |
|
\sa eventFilter(), widgetMousePressEvent(), |
|
widgetMouseMoveEvent(), |
|
*/ |
|
void QwtPanner::widgetMouseReleaseEvent(QMouseEvent *me) |
|
{ |
|
if ( isVisible() ) { |
|
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()); |
|
} |
|
} |
|
} |
|
|
|
/*! |
|
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 ) { |
|
const bool matched = |
|
#if QT_VERSION < 0x040000 |
|
(ke->state() & Qt::KeyButtonMask) == |
|
(d_data->abortKeyState & Qt::KeyButtonMask); |
|
#else |
|
(ke->modifiers() & Qt::KeyboardModifierMask) == |
|
(int)(d_data->abortKeyState & Qt::KeyboardModifierMask); |
|
#endif |
|
if ( matched ) { |
|
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 ) { |
|
#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 ) { |
|
w->setCursor(*d_data->restoreCursor); |
|
delete d_data->restoreCursor; |
|
d_data->restoreCursor = NULL; |
|
} else |
|
w->unsetCursor(); |
|
} |
|
} |
|
#endif
|
|
|