1
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
3
* Copyright (C) 1997 Josef Wilgen
4
* Copyright (C) 2002 Uwe Rathmann
6
* This library is free software; you can redistribute it and/or
7
* modify it under the terms of the Qwt License, Version 1.0
8
*****************************************************************************/
14
#include "qwt_plot_canvas.h"
15
#include "qwt_plot_zoomer.h"
16
#include "qwt_scale_div.h"
17
#if QT_VERSION < 0x040000
18
typedef QValueStack<QwtDoubleRect> QwtZoomStack;
20
typedef QStack<QwtDoubleRect> QwtZoomStack;
23
class QwtPlotZoomer::PrivateData
27
QwtZoomStack zoomStack;
33
\brief Create a zoomer for a plot canvas.
35
The zoomer is set to those x- and y-axis of the parent plot of the
36
canvas that are enabled. If both or no x-axis are enabled, the picker
37
is set to QwtPlot::xBottom. If both or no y-axis are
38
enabled, it is set to QwtPlot::yLeft.
40
The selectionFlags() are set to
41
QwtPicker::RectSelection & QwtPicker::ClickSelection, the
42
tracker mode to QwtPicker::ActiveOnly.
44
\param canvas Plot canvas to observe, also the parent object
45
\param doReplot Call replot for the attached plot before initializing
46
the zoomer with its scales. This might be necessary,
47
when the plot is in a state with pending scale changes.
49
\sa QwtPlot::autoReplot(), QwtPlot::replot(), QwtPlotPicker::setZoomBase()
51
QwtPlotZoomer::QwtPlotZoomer(QwtPlotCanvas *canvas, bool doReplot):
55
init(RectSelection & ClickSelection, ActiveOnly, doReplot);
59
\brief Create a zoomer for a plot canvas.
61
The selectionFlags() are set to
62
QwtPicker::RectSelection & QwtPicker::ClickSelection, the
63
tracker mode to QwtPicker::ActiveOnly.
65
\param xAxis X axis of the zoomer
66
\param yAxis Y axis of the zoomer
67
\param canvas Plot canvas to observe, also the parent object
68
\param doReplot Call replot for the attached plot before initializing
69
the zoomer with its scales. This might be necessary,
70
when the plot is in a state with pending scale changes.
72
\sa QwtPlot::autoReplot(), QwtPlot::replot(), QwtPlotPicker::setZoomBase()
75
QwtPlotZoomer::QwtPlotZoomer(int xAxis, int yAxis,
76
QwtPlotCanvas *canvas, bool doReplot):
77
QwtPlotPicker(xAxis, yAxis, canvas)
80
init(RectSelection & ClickSelection, ActiveOnly, doReplot);
84
Create a zoomer for a plot canvas.
86
\param xAxis X axis of the zoomer
87
\param yAxis Y axis of the zoomer
88
\param selectionFlags Or'd value of QwtPicker::RectSelectionType and
89
QwtPicker::SelectionMode.
90
QwtPicker::RectSelection will be auto added.
91
\param trackerMode Tracker mode
92
\param canvas Plot canvas to observe, also the parent object
93
\param doReplot Call replot for the attached plot before initializing
94
the zoomer with its scales. This might be necessary,
95
when the plot is in a state with pending scale changes.
97
\sa QwtPicker, QwtPicker::setSelectionFlags(), QwtPicker::setRubberBand(),
98
QwtPicker::setTrackerMode
100
\sa QwtPlot::autoReplot(), QwtPlot::replot(), setZoomBase()
103
QwtPlotZoomer::QwtPlotZoomer(int xAxis, int yAxis, int selectionFlags,
104
DisplayMode trackerMode, QwtPlotCanvas *canvas, bool doReplot):
105
QwtPlotPicker(xAxis, yAxis, canvas)
108
init(selectionFlags, trackerMode, doReplot);
111
//! Init the zoomer, used by the constructors
112
void QwtPlotZoomer::init(int selectionFlags,
113
DisplayMode trackerMode, bool doReplot)
115
d_data = new PrivateData;
117
d_data->maxStackDepth = -1;
119
setSelectionFlags(selectionFlags);
120
setTrackerMode(trackerMode);
121
setRubberBand(RectRubberBand);
123
if ( doReplot && plot() )
126
setZoomBase(scaleRect());
129
QwtPlotZoomer::~QwtPlotZoomer()
135
\brief Limit the number of recursive zoom operations to depth.
137
A value of -1 set the depth to unlimited, 0 disables zooming.
138
If the current zoom rectangle is below depth, the plot is unzoomed.
140
\param depth Maximum for the stack depth
142
\note depth doesn't include the zoom base, so zoomStack().count() might be
145
void QwtPlotZoomer::setMaxStackDepth(int depth)
147
d_data->maxStackDepth = depth;
151
// unzoom if the current depth is below d_data->maxStackDepth
154
int(d_data->zoomStack.count()) - 1 - depth; // -1 for the zoom base
159
for ( int i = int(d_data->zoomStack.count()) - 1;
160
i > int(d_data->zoomRectIndex); i-- )
162
(void)d_data->zoomStack.pop(); // remove trailing rects
169
\return Maximal depth of the zoom stack.
170
\sa setMaxStackDepth()
172
int QwtPlotZoomer::maxStackDepth() const
174
return d_data->maxStackDepth;
178
Return the zoom stack. zoomStack()[0] is the zoom base,
179
zoomStack()[1] the first zoomed rectangle.
181
const QwtZoomStack &QwtPlotZoomer::zoomStack() const
183
return d_data->zoomStack;
187
\return Initial rectangle of the zoomer
188
\sa setZoomBase(), zoomRect()
190
QwtDoubleRect QwtPlotZoomer::zoomBase() const
192
return d_data->zoomStack[0];
196
Reinitialized the zoom stack with scaleRect() as base.
198
\sa zoomBase(), scaleRect()
200
\warning Calling QwtPlot::setAxisScale() while QwtPlot::autoReplot() is false
201
leaves the axis in an 'intermediate' state.
202
In this case, to prevent buggy behaviour, you must call
203
QwtPlot::replot() before calling QwtPlotZoomer::setZoomBase().
204
This quirk will be removed in a future release.
206
\sa QwtPlot::autoReplot(), QwtPlot::replot().
208
void QwtPlotZoomer::setZoomBase()
210
const QwtPlot *plt = plot();
214
d_data->zoomStack.clear();
215
d_data->zoomStack.push(scaleRect());
216
d_data->zoomRectIndex = 0;
222
\brief Set the initial size of the zoomer.
224
base is united with the current scaleRect() and the zoom stack is
225
reinitalized with it as zoom base. plot is zoomed to scaleRect().
227
\param base Zoom base
229
\sa zoomBase(), scaleRect()
231
void QwtPlotZoomer::setZoomBase(const QwtDoubleRect &base)
233
const QwtPlot *plt = plot();
237
const QwtDoubleRect sRect = scaleRect();
238
const QwtDoubleRect bRect = base | sRect;
240
d_data->zoomStack.clear();
241
d_data->zoomStack.push(bRect);
242
d_data->zoomRectIndex = 0;
246
d_data->zoomStack.push(sRect);
247
d_data->zoomRectIndex++;
254
Rectangle at the current position on the zoom stack.
256
\sa QwtPlotZoomer::zoomRectIndex(), QwtPlotZoomer::scaleRect().
258
QwtDoubleRect QwtPlotZoomer::zoomRect() const
260
return d_data->zoomStack[d_data->zoomRectIndex];
264
\return Index of current position of zoom stack.
266
uint QwtPlotZoomer::zoomRectIndex() const
268
return d_data->zoomRectIndex;
274
Clears all rectangles above the current position of the
275
zoom stack and pushs the intersection of zoomRect() and
276
the normalized rect on it.
278
\note If the maximal stack depth is reached, zoom is ignored.
279
\note The zoomed signal is emitted.
282
void QwtPlotZoomer::zoom(const QwtDoubleRect &rect)
284
if ( d_data->maxStackDepth >= 0 &&
285
int(d_data->zoomRectIndex) >= d_data->maxStackDepth )
290
const QwtDoubleRect zoomRect = d_data->zoomStack[0] & rect.normalized();
291
if ( zoomRect != d_data->zoomStack[d_data->zoomRectIndex] )
293
for ( uint i = int(d_data->zoomStack.count()) - 1;
294
i > d_data->zoomRectIndex; i-- )
296
(void)d_data->zoomStack.pop();
299
d_data->zoomStack.push(zoomRect);
300
d_data->zoomRectIndex++;
304
emit zoomed(zoomRect);
309
\brief Zoom in or out
311
Activate a rectangle on the zoom stack with an offset relative
312
to the current position. Negative values of offest will zoom out,
313
positive zoom in. A value of 0 zooms out to the zoom base.
315
\param offset Offset relative to the current position of the zoom stack.
316
\note The zoomed signal is emitted.
319
void QwtPlotZoomer::zoom(int offset)
322
d_data->zoomRectIndex = 0;
325
int newIndex = d_data->zoomRectIndex + offset;
326
newIndex = qwtMax(0, newIndex);
327
newIndex = qwtMin(int(d_data->zoomStack.count()) - 1, newIndex);
329
d_data->zoomRectIndex = uint(newIndex);
334
emit zoomed(zoomRect());
338
Adjust the observed plot to zoomRect()
340
\note Initiates QwtPlot::replot
343
void QwtPlotZoomer::rescale()
345
QwtPlot *plt = plot();
349
const QwtDoubleRect &rect = d_data->zoomStack[d_data->zoomRectIndex];
350
if ( rect != scaleRect() )
352
const bool doReplot = plt->autoReplot();
353
plt->setAutoReplot(false);
355
double x1 = rect.left();
356
double x2 = rect.right();
357
if ( plt->axisScaleDiv(xAxis())->lBound() >
358
plt->axisScaleDiv(xAxis())->hBound() )
363
plt->setAxisScale(xAxis(), x1, x2);
365
double y1 = rect.top();
366
double y2 = rect.bottom();
367
if ( plt->axisScaleDiv(yAxis())->lBound() >
368
plt->axisScaleDiv(yAxis())->hBound() )
372
plt->setAxisScale(yAxis(), y1, y2);
374
plt->setAutoReplot(doReplot);
381
Reinitialize the axes, and set the zoom base to their scales.
387
void QwtPlotZoomer::setAxis(int xAxis, int yAxis)
389
if ( xAxis != QwtPlotPicker::xAxis() || yAxis != QwtPlotPicker::yAxis() )
391
QwtPlotPicker::setAxis(xAxis, yAxis);
392
setZoomBase(scaleRect());
397
Qt::MidButton zooms out one position on the zoom stack,
398
Qt::RightButton to the zoom base.
400
Changes the current position on the stack, but doesn't pop
403
\note The mouse events can be changed, using
404
QwtEventPattern::setMousePattern: 2, 1
406
void QwtPlotZoomer::widgetMouseReleaseEvent(QMouseEvent *me)
408
if ( mouseMatch(MouseSelect2, me) )
410
else if ( mouseMatch(MouseSelect3, me) )
412
else if ( mouseMatch(MouseSelect6, me) )
415
QwtPlotPicker::widgetMouseReleaseEvent(me);
419
Qt::Key_Plus zooms out, Qt::Key_Minus zooms in one position on the
420
zoom stack, Qt::Key_Escape zooms out to the zoom base.
422
Changes the current position on the stack, but doesn't pop
425
\note The keys codes can be changed, using
426
QwtEventPattern::setKeyPattern: 3, 4, 5
429
void QwtPlotZoomer::widgetKeyPressEvent(QKeyEvent *ke)
433
if ( keyMatch(KeyUndo, ke) )
435
else if ( keyMatch(KeyRedo, ke) )
437
else if ( keyMatch(KeyHome, ke) )
441
QwtPlotPicker::widgetKeyPressEvent(ke);
445
Move the current zoom rectangle.
450
\note The changed rectangle is limited by the zoom base
452
void QwtPlotZoomer::moveBy(double dx, double dy)
454
const QwtDoubleRect &rect = d_data->zoomStack[d_data->zoomRectIndex];
455
move(rect.left() + dx, rect.top() + dy);
459
Move the the current zoom rectangle.
464
\sa QwtDoubleRect::move
465
\note The changed rectangle is limited by the zoom base
467
void QwtPlotZoomer::move(double x, double y)
469
if ( x < zoomBase().left() )
470
x = zoomBase().left();
471
if ( x > zoomBase().right() - zoomRect().width() )
472
x = zoomBase().right() - zoomRect().width();
474
if ( y < zoomBase().top() )
475
y = zoomBase().top();
476
if ( y > zoomBase().bottom() - zoomRect().height() )
477
y = zoomBase().bottom() - zoomRect().height();
479
if ( x != zoomRect().left() || y != zoomRect().top() )
481
d_data->zoomStack[d_data->zoomRectIndex].moveTo(x, y);
487
\brief Check and correct a selected rectangle
489
Reject rectangles with a hight or width < 2, otherwise
490
expand the selected rectangle to a minimum size of 11x11
493
\return true If rect is accepted, or has been changed
494
to a accepted rectangle.
497
bool QwtPlotZoomer::accept(QwtPolygon &pa) const
499
if ( pa.count() < 2 )
502
QRect rect = QRect(pa[0], pa[int(pa.count()) - 1]);
503
#if QT_VERSION < 0x040000
504
rect = rect.normalize();
506
rect = rect.normalized();
509
const int minSize = 2;
510
if (rect.width() < minSize && rect.height() < minSize )
513
const int minZoomSize = 11;
515
const QPoint center = rect.center();
516
rect.setSize(rect.size().expandedTo(QSize(minZoomSize, minZoomSize)));
517
rect.moveCenter(center);
520
pa[0] = rect.topLeft();
521
pa[1] = rect.bottomRight();
527
\brief Limit zooming by a minimum rectangle
529
\return zoomBase().width() / 10e4, zoomBase().height() / 10e4
531
QwtDoubleSize QwtPlotZoomer::minZoomSize() const
533
return QwtDoubleSize(
534
d_data->zoomStack[0].width() / 10e4,
535
d_data->zoomStack[0].height() / 10e4
540
Rejects selections, when the stack depth is too deep, or
541
the zoomed rectangle is minZoomSize().
543
\sa minZoomSize(), maxStackDepth()
545
void QwtPlotZoomer::begin()
547
if ( d_data->maxStackDepth >= 0 )
549
if ( d_data->zoomRectIndex >= uint(d_data->maxStackDepth) )
553
const QwtDoubleSize minSize = minZoomSize();
554
if ( minSize.isValid() )
556
const QwtDoubleSize sz =
557
d_data->zoomStack[d_data->zoomRectIndex].size() * 0.9999;
559
if ( minSize.width() >= sz.width() &&
560
minSize.height() >= sz.height() )
566
QwtPlotPicker::begin();
570
Expand the selected rectangle to minZoomSize() and zoom in
573
\sa QwtPlotZoomer::accept()a, QwtPlotZoomer::minZoomSize()
575
bool QwtPlotZoomer::end(bool ok)
577
ok = QwtPlotPicker::end(ok);
581
QwtPlot *plot = QwtPlotZoomer::plot();
585
const QwtPolygon &pa = selection();
586
if ( pa.count() < 2 )
589
QRect rect = QRect(pa[0], pa[int(pa.count() - 1)]);
590
#if QT_VERSION < 0x040000
591
rect = rect.normalize();
593
rect = rect.normalized();
597
QwtDoubleRect zoomRect = invTransform(rect).normalized();
599
const QwtDoublePoint center = zoomRect.center();
600
zoomRect.setSize(zoomRect.size().expandedTo(minZoomSize()));
601
zoomRect.moveCenter(center);
609
Set the selection flags
611
\param flags Or'd value of QwtPicker::RectSelectionType and
612
QwtPicker::SelectionMode. The default value is
613
QwtPicker::RectSelection & QwtPicker::ClickSelection.
615
\sa selectionFlags(), SelectionType, RectSelectionType, SelectionMode
616
\note QwtPicker::RectSelection will be auto added.
619
void QwtPlotZoomer::setSelectionFlags(int flags)
621
// we accept only rects
622
flags &= ~(PointSelection | PolygonSelection);
623
flags |= RectSelection;
625
QwtPlotPicker::setSelectionFlags(flags);