| 1 | /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
 | 
|---|
| 2 |  * Qwt Widget Library
 | 
|---|
| 3 |  * Copyright (C) 1997   Josef Wilgen
 | 
|---|
| 4 |  * Copyright (C) 2002   Uwe Rathmann
 | 
|---|
| 5 |  *
 | 
|---|
| 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 |  *****************************************************************************/
 | 
|---|
| 9 | 
 | 
|---|
| 10 | #include "qwt_plot_zoomer.h"
 | 
|---|
| 11 | #include "qwt_plot.h"
 | 
|---|
| 12 | #include "qwt_plot_canvas.h"
 | 
|---|
| 13 | #include "qwt_scale_div.h"
 | 
|---|
| 14 | #include "qwt_picker_machine.h"
 | 
|---|
| 15 | #include <qalgorithms.h>
 | 
|---|
| 16 | 
 | 
|---|
| 17 | class QwtPlotZoomer::PrivateData
 | 
|---|
| 18 | {
 | 
|---|
| 19 | public:
 | 
|---|
| 20 |     uint zoomRectIndex;
 | 
|---|
| 21 |     QStack<QRectF> zoomStack;
 | 
|---|
| 22 | 
 | 
|---|
| 23 |     int maxStackDepth;
 | 
|---|
| 24 | };
 | 
|---|
| 25 | 
 | 
|---|
| 26 | /*!
 | 
|---|
| 27 |   \brief Create a zoomer for a plot canvas.
 | 
|---|
| 28 | 
 | 
|---|
| 29 |   The zoomer is set to those x- and y-axis of the parent plot of the
 | 
|---|
| 30 |   canvas that are enabled. If both or no x-axis are enabled, the picker
 | 
|---|
| 31 |   is set to QwtPlot::xBottom. If both or no y-axis are
 | 
|---|
| 32 |   enabled, it is set to QwtPlot::yLeft.
 | 
|---|
| 33 | 
 | 
|---|
| 34 |   The zoomer is initialized with a QwtPickerDragRectMachine,
 | 
|---|
| 35 |   the tracker mode is set to QwtPicker::ActiveOnly and the rubberband
 | 
|---|
| 36 |   is set to QwtPicker::RectRubberBand
 | 
|---|
| 37 | 
 | 
|---|
| 38 |   \param canvas Plot canvas to observe, also the parent object
 | 
|---|
| 39 |   \param doReplot Call replot for the attached plot before initializing
 | 
|---|
| 40 |                   the zoomer with its scales. This might be necessary,
 | 
|---|
| 41 |                   when the plot is in a state with pending scale changes.
 | 
|---|
| 42 | 
 | 
|---|
| 43 |   \sa QwtPlot::autoReplot(), QwtPlot::replot(), setZoomBase()
 | 
|---|
| 44 | */
 | 
|---|
| 45 | QwtPlotZoomer::QwtPlotZoomer( QwtPlotCanvas *canvas, bool doReplot ):
 | 
|---|
| 46 |     QwtPlotPicker( canvas )
 | 
|---|
| 47 | {
 | 
|---|
| 48 |     if ( canvas )
 | 
|---|
| 49 |         init( doReplot );
 | 
|---|
| 50 | }
 | 
|---|
| 51 | 
 | 
|---|
| 52 | /*!
 | 
|---|
| 53 |   \brief Create a zoomer for a plot canvas.
 | 
|---|
| 54 | 
 | 
|---|
| 55 |   The zoomer is initialized with a QwtPickerDragRectMachine,
 | 
|---|
| 56 |   the tracker mode is set to QwtPicker::ActiveOnly and the rubberband
 | 
|---|
| 57 |   is set to QwtPicker;;RectRubberBand
 | 
|---|
| 58 | 
 | 
|---|
| 59 |   \param xAxis X axis of the zoomer
 | 
|---|
| 60 |   \param yAxis Y axis of the zoomer
 | 
|---|
| 61 |   \param canvas Plot canvas to observe, also the parent object
 | 
|---|
| 62 |   \param doReplot Call replot for the attached plot before initializing
 | 
|---|
| 63 |                   the zoomer with its scales. This might be necessary,
 | 
|---|
| 64 |                   when the plot is in a state with pending scale changes.
 | 
|---|
| 65 | 
 | 
|---|
| 66 |   \sa QwtPlot::autoReplot(), QwtPlot::replot(), setZoomBase()
 | 
|---|
| 67 | */
 | 
|---|
| 68 | 
 | 
|---|
| 69 | QwtPlotZoomer::QwtPlotZoomer( int xAxis, int yAxis,
 | 
|---|
| 70 |         QwtPlotCanvas *canvas, bool doReplot ):
 | 
|---|
| 71 |     QwtPlotPicker( xAxis, yAxis, canvas )
 | 
|---|
| 72 | {
 | 
|---|
| 73 |     if ( canvas )
 | 
|---|
| 74 |         init( doReplot );
 | 
|---|
| 75 | }
 | 
|---|
| 76 | 
 | 
|---|
| 77 | //! Init the zoomer, used by the constructors
 | 
|---|
| 78 | void QwtPlotZoomer::init( bool doReplot )
 | 
|---|
| 79 | {
 | 
|---|
| 80 |     d_data = new PrivateData;
 | 
|---|
| 81 | 
 | 
|---|
| 82 |     d_data->maxStackDepth = -1;
 | 
|---|
| 83 | 
 | 
|---|
| 84 |     setTrackerMode( ActiveOnly );
 | 
|---|
| 85 |     setRubberBand( RectRubberBand );
 | 
|---|
| 86 |     setStateMachine( new QwtPickerDragRectMachine() );
 | 
|---|
| 87 | 
 | 
|---|
| 88 |     if ( doReplot && plot() )
 | 
|---|
| 89 |         plot()->replot();
 | 
|---|
| 90 | 
 | 
|---|
| 91 |     setZoomBase( scaleRect() );
 | 
|---|
| 92 | }
 | 
|---|
| 93 | 
 | 
|---|
| 94 | QwtPlotZoomer::~QwtPlotZoomer()
 | 
|---|
| 95 | {
 | 
|---|
| 96 |     delete d_data;
 | 
|---|
| 97 | }
 | 
|---|
| 98 | 
 | 
|---|
| 99 | /*!
 | 
|---|
| 100 |   \brief Limit the number of recursive zoom operations to depth.
 | 
|---|
| 101 | 
 | 
|---|
| 102 |   A value of -1 set the depth to unlimited, 0 disables zooming.
 | 
|---|
| 103 |   If the current zoom rectangle is below depth, the plot is unzoomed.
 | 
|---|
| 104 | 
 | 
|---|
| 105 |   \param depth Maximum for the stack depth
 | 
|---|
| 106 |   \sa maxStackDepth()
 | 
|---|
| 107 |   \note depth doesn't include the zoom base, so zoomStack().count() might be
 | 
|---|
| 108 |               maxStackDepth() + 1.
 | 
|---|
| 109 | */
 | 
|---|
| 110 | void QwtPlotZoomer::setMaxStackDepth( int depth )
 | 
|---|
| 111 | {
 | 
|---|
| 112 |     d_data->maxStackDepth = depth;
 | 
|---|
| 113 | 
 | 
|---|
| 114 |     if ( depth >= 0 )
 | 
|---|
| 115 |     {
 | 
|---|
| 116 |         // unzoom if the current depth is below d_data->maxStackDepth
 | 
|---|
| 117 | 
 | 
|---|
| 118 |         const int zoomOut =
 | 
|---|
| 119 |             int( d_data->zoomStack.count() ) - 1 - depth; // -1 for the zoom base
 | 
|---|
| 120 | 
 | 
|---|
| 121 |         if ( zoomOut > 0 )
 | 
|---|
| 122 |         {
 | 
|---|
| 123 |             zoom( -zoomOut );
 | 
|---|
| 124 |             for ( int i = int( d_data->zoomStack.count() ) - 1;
 | 
|---|
| 125 |                 i > int( d_data->zoomRectIndex ); i-- )
 | 
|---|
| 126 |             {
 | 
|---|
| 127 |                 ( void )d_data->zoomStack.pop(); // remove trailing rects
 | 
|---|
| 128 |             }
 | 
|---|
| 129 |         }
 | 
|---|
| 130 |     }
 | 
|---|
| 131 | }
 | 
|---|
| 132 | 
 | 
|---|
| 133 | /*!
 | 
|---|
| 134 |   \return Maximal depth of the zoom stack.
 | 
|---|
| 135 |   \sa setMaxStackDepth()
 | 
|---|
| 136 | */
 | 
|---|
| 137 | int QwtPlotZoomer::maxStackDepth() const
 | 
|---|
| 138 | {
 | 
|---|
| 139 |     return d_data->maxStackDepth;
 | 
|---|
| 140 | }
 | 
|---|
| 141 | 
 | 
|---|
| 142 | /*!
 | 
|---|
| 143 |   Return the zoom stack. zoomStack()[0] is the zoom base,
 | 
|---|
| 144 |   zoomStack()[1] the first zoomed rectangle.
 | 
|---|
| 145 | 
 | 
|---|
| 146 |   \sa setZoomStack(), zoomRectIndex()
 | 
|---|
| 147 | */
 | 
|---|
| 148 | const QStack<QRectF> &QwtPlotZoomer::zoomStack() const
 | 
|---|
| 149 | {
 | 
|---|
| 150 |     return d_data->zoomStack;
 | 
|---|
| 151 | }
 | 
|---|
| 152 | 
 | 
|---|
| 153 | /*!
 | 
|---|
| 154 |   \return Initial rectangle of the zoomer
 | 
|---|
| 155 |   \sa setZoomBase(), zoomRect()
 | 
|---|
| 156 | */
 | 
|---|
| 157 | QRectF QwtPlotZoomer::zoomBase() const
 | 
|---|
| 158 | {
 | 
|---|
| 159 |     return d_data->zoomStack[0];
 | 
|---|
| 160 | }
 | 
|---|
| 161 | 
 | 
|---|
| 162 | /*!
 | 
|---|
| 163 |   Reinitialized the zoom stack with scaleRect() as base.
 | 
|---|
| 164 | 
 | 
|---|
| 165 |   \param doReplot Call replot for the attached plot before initializing
 | 
|---|
| 166 |                   the zoomer with its scales. This might be necessary,
 | 
|---|
| 167 |                   when the plot is in a state with pending scale changes.
 | 
|---|
| 168 | 
 | 
|---|
| 169 |   \sa zoomBase(), scaleRect() QwtPlot::autoReplot(), QwtPlot::replot().
 | 
|---|
| 170 | */
 | 
|---|
| 171 | void QwtPlotZoomer::setZoomBase( bool doReplot )
 | 
|---|
| 172 | {
 | 
|---|
| 173 |     QwtPlot *plt = plot();
 | 
|---|
| 174 |     if ( plt == NULL )
 | 
|---|
| 175 |         return;
 | 
|---|
| 176 | 
 | 
|---|
| 177 |     if ( doReplot )
 | 
|---|
| 178 |         plt->replot();
 | 
|---|
| 179 | 
 | 
|---|
| 180 |     d_data->zoomStack.clear();
 | 
|---|
| 181 |     d_data->zoomStack.push( scaleRect() );
 | 
|---|
| 182 |     d_data->zoomRectIndex = 0;
 | 
|---|
| 183 | 
 | 
|---|
| 184 |     rescale();
 | 
|---|
| 185 | }
 | 
|---|
| 186 | 
 | 
|---|
| 187 | /*!
 | 
|---|
| 188 |   \brief Set the initial size of the zoomer.
 | 
|---|
| 189 | 
 | 
|---|
| 190 |   base is united with the current scaleRect() and the zoom stack is
 | 
|---|
| 191 |   reinitalized with it as zoom base. plot is zoomed to scaleRect().
 | 
|---|
| 192 | 
 | 
|---|
| 193 |   \param base Zoom base
 | 
|---|
| 194 | 
 | 
|---|
| 195 |   \sa zoomBase(), scaleRect()
 | 
|---|
| 196 | */
 | 
|---|
| 197 | void QwtPlotZoomer::setZoomBase( const QRectF &base )
 | 
|---|
| 198 | {
 | 
|---|
| 199 |     const QwtPlot *plt = plot();
 | 
|---|
| 200 |     if ( !plt )
 | 
|---|
| 201 |         return;
 | 
|---|
| 202 | 
 | 
|---|
| 203 |     const QRectF sRect = scaleRect();
 | 
|---|
| 204 |     const QRectF bRect = base | sRect;
 | 
|---|
| 205 | 
 | 
|---|
| 206 |     d_data->zoomStack.clear();
 | 
|---|
| 207 |     d_data->zoomStack.push( bRect );
 | 
|---|
| 208 |     d_data->zoomRectIndex = 0;
 | 
|---|
| 209 | 
 | 
|---|
| 210 |     if ( base != sRect )
 | 
|---|
| 211 |     {
 | 
|---|
| 212 |         d_data->zoomStack.push( sRect );
 | 
|---|
| 213 |         d_data->zoomRectIndex++;
 | 
|---|
| 214 |     }
 | 
|---|
| 215 | 
 | 
|---|
| 216 |     rescale();
 | 
|---|
| 217 | }
 | 
|---|
| 218 | 
 | 
|---|
| 219 | /*!
 | 
|---|
| 220 |   Rectangle at the current position on the zoom stack.
 | 
|---|
| 221 | 
 | 
|---|
| 222 |   \sa zoomRectIndex(), scaleRect().
 | 
|---|
| 223 | */
 | 
|---|
| 224 | QRectF QwtPlotZoomer::zoomRect() const
 | 
|---|
| 225 | {
 | 
|---|
| 226 |     return d_data->zoomStack[d_data->zoomRectIndex];
 | 
|---|
| 227 | }
 | 
|---|
| 228 | 
 | 
|---|
| 229 | /*!
 | 
|---|
| 230 |   \return Index of current position of zoom stack.
 | 
|---|
| 231 | */
 | 
|---|
| 232 | uint QwtPlotZoomer::zoomRectIndex() const
 | 
|---|
| 233 | {
 | 
|---|
| 234 |     return d_data->zoomRectIndex;
 | 
|---|
| 235 | }
 | 
|---|
| 236 | 
 | 
|---|
| 237 | /*!
 | 
|---|
| 238 |   \brief Zoom in
 | 
|---|
| 239 | 
 | 
|---|
| 240 |   Clears all rectangles above the current position of the
 | 
|---|
| 241 |   zoom stack and pushs the normalized rect on it.
 | 
|---|
| 242 | 
 | 
|---|
| 243 |   \note If the maximal stack depth is reached, zoom is ignored.
 | 
|---|
| 244 |   \note The zoomed signal is emitted.
 | 
|---|
| 245 | */
 | 
|---|
| 246 | 
 | 
|---|
| 247 | void QwtPlotZoomer::zoom( const QRectF &rect )
 | 
|---|
| 248 | {
 | 
|---|
| 249 |     if ( d_data->maxStackDepth >= 0 &&
 | 
|---|
| 250 |             int( d_data->zoomRectIndex ) >= d_data->maxStackDepth )
 | 
|---|
| 251 |     {
 | 
|---|
| 252 |         return;
 | 
|---|
| 253 |     }
 | 
|---|
| 254 | 
 | 
|---|
| 255 |     const QRectF zoomRect = rect.normalized();
 | 
|---|
| 256 |     if ( zoomRect != d_data->zoomStack[d_data->zoomRectIndex] )
 | 
|---|
| 257 |     {
 | 
|---|
| 258 |         for ( uint i = int( d_data->zoomStack.count() ) - 1;
 | 
|---|
| 259 |            i > d_data->zoomRectIndex; i-- )
 | 
|---|
| 260 |         {
 | 
|---|
| 261 |             ( void )d_data->zoomStack.pop();
 | 
|---|
| 262 |         }
 | 
|---|
| 263 | 
 | 
|---|
| 264 |         d_data->zoomStack.push( zoomRect );
 | 
|---|
| 265 |         d_data->zoomRectIndex++;
 | 
|---|
| 266 | 
 | 
|---|
| 267 |         rescale();
 | 
|---|
| 268 | 
 | 
|---|
| 269 |         Q_EMIT zoomed( zoomRect );
 | 
|---|
| 270 |     }
 | 
|---|
| 271 | }
 | 
|---|
| 272 | 
 | 
|---|
| 273 | /*!
 | 
|---|
| 274 |   \brief Zoom in or out
 | 
|---|
| 275 | 
 | 
|---|
| 276 |   Activate a rectangle on the zoom stack with an offset relative
 | 
|---|
| 277 |   to the current position. Negative values of offest will zoom out,
 | 
|---|
| 278 |   positive zoom in. A value of 0 zooms out to the zoom base.
 | 
|---|
| 279 | 
 | 
|---|
| 280 |   \param offset Offset relative to the current position of the zoom stack.
 | 
|---|
| 281 |   \note The zoomed signal is emitted.
 | 
|---|
| 282 |   \sa zoomRectIndex()
 | 
|---|
| 283 | */
 | 
|---|
| 284 | void QwtPlotZoomer::zoom( int offset )
 | 
|---|
| 285 | {
 | 
|---|
| 286 |     if ( offset == 0 )
 | 
|---|
| 287 |         d_data->zoomRectIndex = 0;
 | 
|---|
| 288 |     else
 | 
|---|
| 289 |     {
 | 
|---|
| 290 |         int newIndex = d_data->zoomRectIndex + offset;
 | 
|---|
| 291 |         newIndex = qMax( 0, newIndex );
 | 
|---|
| 292 |         newIndex = qMin( int( d_data->zoomStack.count() ) - 1, newIndex );
 | 
|---|
| 293 | 
 | 
|---|
| 294 |         d_data->zoomRectIndex = uint( newIndex );
 | 
|---|
| 295 |     }
 | 
|---|
| 296 | 
 | 
|---|
| 297 |     rescale();
 | 
|---|
| 298 | 
 | 
|---|
| 299 |     Q_EMIT zoomed( zoomRect() );
 | 
|---|
| 300 | }
 | 
|---|
| 301 | 
 | 
|---|
| 302 | /*!
 | 
|---|
| 303 |   \brief Assign a zoom stack
 | 
|---|
| 304 | 
 | 
|---|
| 305 |   In combination with other types of navigation it might be useful to
 | 
|---|
| 306 |   modify to manipulate the complete zoom stack.
 | 
|---|
| 307 | 
 | 
|---|
| 308 |   \param zoomStack New zoom stack
 | 
|---|
| 309 |   \param zoomRectIndex Index of the current position of zoom stack.
 | 
|---|
| 310 |                        In case of -1 the current position is at the top
 | 
|---|
| 311 |                        of the stack.
 | 
|---|
| 312 | 
 | 
|---|
| 313 |   \note The zoomed signal might be emitted.
 | 
|---|
| 314 |   \sa zoomStack(), zoomRectIndex()
 | 
|---|
| 315 | */
 | 
|---|
| 316 | void QwtPlotZoomer::setZoomStack(
 | 
|---|
| 317 |     const QStack<QRectF> &zoomStack, int zoomRectIndex )
 | 
|---|
| 318 | {
 | 
|---|
| 319 |     if ( zoomStack.isEmpty() )
 | 
|---|
| 320 |         return;
 | 
|---|
| 321 | 
 | 
|---|
| 322 |     if ( d_data->maxStackDepth >= 0 &&
 | 
|---|
| 323 |         int( zoomStack.count() ) > d_data->maxStackDepth )
 | 
|---|
| 324 |     {
 | 
|---|
| 325 |         return;
 | 
|---|
| 326 |     }
 | 
|---|
| 327 | 
 | 
|---|
| 328 |     if ( zoomRectIndex < 0 || zoomRectIndex > int( zoomStack.count() ) )
 | 
|---|
| 329 |         zoomRectIndex = zoomStack.count() - 1;
 | 
|---|
| 330 | 
 | 
|---|
| 331 |     const bool doRescale = zoomStack[zoomRectIndex] != zoomRect();
 | 
|---|
| 332 | 
 | 
|---|
| 333 |     d_data->zoomStack = zoomStack;
 | 
|---|
| 334 |     d_data->zoomRectIndex = uint( zoomRectIndex );
 | 
|---|
| 335 | 
 | 
|---|
| 336 |     if ( doRescale )
 | 
|---|
| 337 |     {
 | 
|---|
| 338 |         rescale();
 | 
|---|
| 339 |         Q_EMIT zoomed( zoomRect() );
 | 
|---|
| 340 |     }
 | 
|---|
| 341 | }
 | 
|---|
| 342 | 
 | 
|---|
| 343 | /*!
 | 
|---|
| 344 |   Adjust the observed plot to zoomRect()
 | 
|---|
| 345 | 
 | 
|---|
| 346 |   \note Initiates QwtPlot::replot
 | 
|---|
| 347 | */
 | 
|---|
| 348 | 
 | 
|---|
| 349 | void QwtPlotZoomer::rescale()
 | 
|---|
| 350 | {
 | 
|---|
| 351 |     QwtPlot *plt = plot();
 | 
|---|
| 352 |     if ( !plt )
 | 
|---|
| 353 |         return;
 | 
|---|
| 354 | 
 | 
|---|
| 355 |     const QRectF &rect = d_data->zoomStack[d_data->zoomRectIndex];
 | 
|---|
| 356 |     if ( rect != scaleRect() )
 | 
|---|
| 357 |     {
 | 
|---|
| 358 |         const bool doReplot = plt->autoReplot();
 | 
|---|
| 359 |         plt->setAutoReplot( false );
 | 
|---|
| 360 | 
 | 
|---|
| 361 |         double x1 = rect.left();
 | 
|---|
| 362 |         double x2 = rect.right();
 | 
|---|
| 363 |         if ( plt->axisScaleDiv( xAxis() )->lowerBound() >
 | 
|---|
| 364 |             plt->axisScaleDiv( xAxis() )->upperBound() )
 | 
|---|
| 365 |         {
 | 
|---|
| 366 |             qSwap( x1, x2 );
 | 
|---|
| 367 |         }
 | 
|---|
| 368 | 
 | 
|---|
| 369 |         plt->setAxisScale( xAxis(), x1, x2 );
 | 
|---|
| 370 | 
 | 
|---|
| 371 |         double y1 = rect.top();
 | 
|---|
| 372 |         double y2 = rect.bottom();
 | 
|---|
| 373 |         if ( plt->axisScaleDiv( yAxis() )->lowerBound() >
 | 
|---|
| 374 |             plt->axisScaleDiv( yAxis() )->upperBound() )
 | 
|---|
| 375 |         {
 | 
|---|
| 376 |             qSwap( y1, y2 );
 | 
|---|
| 377 |         }
 | 
|---|
| 378 |         plt->setAxisScale( yAxis(), y1, y2 );
 | 
|---|
| 379 | 
 | 
|---|
| 380 |         plt->setAutoReplot( doReplot );
 | 
|---|
| 381 | 
 | 
|---|
| 382 |         plt->replot();
 | 
|---|
| 383 |     }
 | 
|---|
| 384 | }
 | 
|---|
| 385 | 
 | 
|---|
| 386 | /*!
 | 
|---|
| 387 |   Reinitialize the axes, and set the zoom base to their scales.
 | 
|---|
| 388 | 
 | 
|---|
| 389 |   \param xAxis X axis
 | 
|---|
| 390 |   \param yAxis Y axis
 | 
|---|
| 391 | */
 | 
|---|
| 392 | 
 | 
|---|
| 393 | void QwtPlotZoomer::setAxis( int xAxis, int yAxis )
 | 
|---|
| 394 | {
 | 
|---|
| 395 |     if ( xAxis != QwtPlotPicker::xAxis() || yAxis != QwtPlotPicker::yAxis() )
 | 
|---|
| 396 |     {
 | 
|---|
| 397 |         QwtPlotPicker::setAxis( xAxis, yAxis );
 | 
|---|
| 398 |         setZoomBase( scaleRect() );
 | 
|---|
| 399 |     }
 | 
|---|
| 400 | }
 | 
|---|
| 401 | 
 | 
|---|
| 402 | /*!
 | 
|---|
| 403 |    Qt::MidButton zooms out one position on the zoom stack,
 | 
|---|
| 404 |    Qt::RightButton to the zoom base.
 | 
|---|
| 405 | 
 | 
|---|
| 406 |    Changes the current position on the stack, but doesn't pop
 | 
|---|
| 407 |    any rectangle.
 | 
|---|
| 408 | 
 | 
|---|
| 409 |    \note The mouse events can be changed, using
 | 
|---|
| 410 |          QwtEventPattern::setMousePattern: 2, 1
 | 
|---|
| 411 | */
 | 
|---|
| 412 | void QwtPlotZoomer::widgetMouseReleaseEvent( QMouseEvent *me )
 | 
|---|
| 413 | {
 | 
|---|
| 414 |     if ( mouseMatch( MouseSelect2, me ) )
 | 
|---|
| 415 |         zoom( 0 );
 | 
|---|
| 416 |     else if ( mouseMatch( MouseSelect3, me ) )
 | 
|---|
| 417 |         zoom( -1 );
 | 
|---|
| 418 |     else if ( mouseMatch( MouseSelect6, me ) )
 | 
|---|
| 419 |         zoom( +1 );
 | 
|---|
| 420 |     else
 | 
|---|
| 421 |         QwtPlotPicker::widgetMouseReleaseEvent( me );
 | 
|---|
| 422 | }
 | 
|---|
| 423 | 
 | 
|---|
| 424 | /*!
 | 
|---|
| 425 |    Qt::Key_Plus zooms in, Qt::Key_Minus zooms out one position on the
 | 
|---|
| 426 |    zoom stack, Qt::Key_Escape zooms out to the zoom base.
 | 
|---|
| 427 | 
 | 
|---|
| 428 |    Changes the current position on the stack, but doesn't pop
 | 
|---|
| 429 |    any rectangle.
 | 
|---|
| 430 | 
 | 
|---|
| 431 |    \note The keys codes can be changed, using
 | 
|---|
| 432 |          QwtEventPattern::setKeyPattern: 3, 4, 5
 | 
|---|
| 433 | */
 | 
|---|
| 434 | 
 | 
|---|
| 435 | void QwtPlotZoomer::widgetKeyPressEvent( QKeyEvent *ke )
 | 
|---|
| 436 | {
 | 
|---|
| 437 |     if ( !isActive() )
 | 
|---|
| 438 |     {
 | 
|---|
| 439 |         if ( keyMatch( KeyUndo, ke ) )
 | 
|---|
| 440 |             zoom( -1 );
 | 
|---|
| 441 |         else if ( keyMatch( KeyRedo, ke ) )
 | 
|---|
| 442 |             zoom( +1 );
 | 
|---|
| 443 |         else if ( keyMatch( KeyHome, ke ) )
 | 
|---|
| 444 |             zoom( 0 );
 | 
|---|
| 445 |     }
 | 
|---|
| 446 | 
 | 
|---|
| 447 |     QwtPlotPicker::widgetKeyPressEvent( ke );
 | 
|---|
| 448 | }
 | 
|---|
| 449 | 
 | 
|---|
| 450 | /*!
 | 
|---|
| 451 |   Move the current zoom rectangle.
 | 
|---|
| 452 | 
 | 
|---|
| 453 |   \param dx X offset
 | 
|---|
| 454 |   \param dy Y offset
 | 
|---|
| 455 | 
 | 
|---|
| 456 |   \note The changed rectangle is limited by the zoom base
 | 
|---|
| 457 | */
 | 
|---|
| 458 | void QwtPlotZoomer::moveBy( double dx, double dy )
 | 
|---|
| 459 | {
 | 
|---|
| 460 |     const QRectF &rect = d_data->zoomStack[d_data->zoomRectIndex];
 | 
|---|
| 461 |     moveTo( QPointF( rect.left() + dx, rect.top() + dy ) );
 | 
|---|
| 462 | }
 | 
|---|
| 463 | 
 | 
|---|
| 464 | /*!
 | 
|---|
| 465 |   Move the the current zoom rectangle.
 | 
|---|
| 466 | 
 | 
|---|
| 467 |   \param pos New position
 | 
|---|
| 468 | 
 | 
|---|
| 469 |   \sa QRectF::moveTo()
 | 
|---|
| 470 |   \note The changed rectangle is limited by the zoom base
 | 
|---|
| 471 | */
 | 
|---|
| 472 | void QwtPlotZoomer::moveTo( const QPointF &pos )
 | 
|---|
| 473 | {
 | 
|---|
| 474 |     double x = pos.x();
 | 
|---|
| 475 |     double y = pos.y();
 | 
|---|
| 476 | 
 | 
|---|
| 477 |     if ( x < zoomBase().left() )
 | 
|---|
| 478 |         x = zoomBase().left();
 | 
|---|
| 479 |     if ( x > zoomBase().right() - zoomRect().width() )
 | 
|---|
| 480 |         x = zoomBase().right() - zoomRect().width();
 | 
|---|
| 481 | 
 | 
|---|
| 482 |     if ( y < zoomBase().top() )
 | 
|---|
| 483 |         y = zoomBase().top();
 | 
|---|
| 484 |     if ( y > zoomBase().bottom() - zoomRect().height() )
 | 
|---|
| 485 |         y = zoomBase().bottom() - zoomRect().height();
 | 
|---|
| 486 | 
 | 
|---|
| 487 |     if ( x != zoomRect().left() || y != zoomRect().top() )
 | 
|---|
| 488 |     {
 | 
|---|
| 489 |         d_data->zoomStack[d_data->zoomRectIndex].moveTo( x, y );
 | 
|---|
| 490 |         rescale();
 | 
|---|
| 491 |     }
 | 
|---|
| 492 | }
 | 
|---|
| 493 | 
 | 
|---|
| 494 | /*!
 | 
|---|
| 495 |   \brief Check and correct a selected rectangle
 | 
|---|
| 496 | 
 | 
|---|
| 497 |   Reject rectangles with a hight or width < 2, otherwise
 | 
|---|
| 498 |   expand the selected rectangle to a minimum size of 11x11
 | 
|---|
| 499 |   and accept it.
 | 
|---|
| 500 | 
 | 
|---|
| 501 |   \return true If rect is accepted, or has been changed
 | 
|---|
| 502 |           to a accepted rectangle.
 | 
|---|
| 503 | */
 | 
|---|
| 504 | 
 | 
|---|
| 505 | bool QwtPlotZoomer::accept( QPolygon &pa ) const
 | 
|---|
| 506 | {
 | 
|---|
| 507 |     if ( pa.count() < 2 )
 | 
|---|
| 508 |         return false;
 | 
|---|
| 509 | 
 | 
|---|
| 510 |     QRect rect = QRect( pa[0], pa[int( pa.count() ) - 1] );
 | 
|---|
| 511 |     rect = rect.normalized();
 | 
|---|
| 512 | 
 | 
|---|
| 513 |     const int minSize = 2;
 | 
|---|
| 514 |     if ( rect.width() < minSize && rect.height() < minSize )
 | 
|---|
| 515 |         return false;
 | 
|---|
| 516 | 
 | 
|---|
| 517 |     const int minZoomSize = 11;
 | 
|---|
| 518 | 
 | 
|---|
| 519 |     const QPoint center = rect.center();
 | 
|---|
| 520 |     rect.setSize( rect.size().expandedTo( QSize( minZoomSize, minZoomSize ) ) );
 | 
|---|
| 521 |     rect.moveCenter( center );
 | 
|---|
| 522 | 
 | 
|---|
| 523 |     pa.resize( 2 );
 | 
|---|
| 524 |     pa[0] = rect.topLeft();
 | 
|---|
| 525 |     pa[1] = rect.bottomRight();
 | 
|---|
| 526 | 
 | 
|---|
| 527 |     return true;
 | 
|---|
| 528 | }
 | 
|---|
| 529 | 
 | 
|---|
| 530 | /*!
 | 
|---|
| 531 |   \brief Limit zooming by a minimum rectangle
 | 
|---|
| 532 | 
 | 
|---|
| 533 |   \return zoomBase().width() / 10e4, zoomBase().height() / 10e4
 | 
|---|
| 534 | */
 | 
|---|
| 535 | QSizeF QwtPlotZoomer::minZoomSize() const
 | 
|---|
| 536 | {
 | 
|---|
| 537 |     return QSizeF( d_data->zoomStack[0].width() / 10e4,
 | 
|---|
| 538 |         d_data->zoomStack[0].height() / 10e4 );
 | 
|---|
| 539 | }
 | 
|---|
| 540 | 
 | 
|---|
| 541 | /*!
 | 
|---|
| 542 |   Rejects selections, when the stack depth is too deep, or
 | 
|---|
| 543 |   the zoomed rectangle is minZoomSize().
 | 
|---|
| 544 | 
 | 
|---|
| 545 |   \sa minZoomSize(), maxStackDepth()
 | 
|---|
| 546 | */
 | 
|---|
| 547 | void QwtPlotZoomer::begin()
 | 
|---|
| 548 | {
 | 
|---|
| 549 |     if ( d_data->maxStackDepth >= 0 )
 | 
|---|
| 550 |     {
 | 
|---|
| 551 |         if ( d_data->zoomRectIndex >= uint( d_data->maxStackDepth ) )
 | 
|---|
| 552 |             return;
 | 
|---|
| 553 |     }
 | 
|---|
| 554 | 
 | 
|---|
| 555 |     const QSizeF minSize = minZoomSize();
 | 
|---|
| 556 |     if ( minSize.isValid() )
 | 
|---|
| 557 |     {
 | 
|---|
| 558 |         const QSizeF sz =
 | 
|---|
| 559 |             d_data->zoomStack[d_data->zoomRectIndex].size() * 0.9999;
 | 
|---|
| 560 | 
 | 
|---|
| 561 |         if ( minSize.width() >= sz.width() &&
 | 
|---|
| 562 |             minSize.height() >= sz.height() )
 | 
|---|
| 563 |         {
 | 
|---|
| 564 |             return;
 | 
|---|
| 565 |         }
 | 
|---|
| 566 |     }
 | 
|---|
| 567 | 
 | 
|---|
| 568 |     QwtPlotPicker::begin();
 | 
|---|
| 569 | }
 | 
|---|
| 570 | 
 | 
|---|
| 571 | /*!
 | 
|---|
| 572 |   Expand the selected rectangle to minZoomSize() and zoom in
 | 
|---|
| 573 |   if accepted.
 | 
|---|
| 574 | 
 | 
|---|
| 575 |   \sa accept(), minZoomSize()
 | 
|---|
| 576 | */
 | 
|---|
| 577 | bool QwtPlotZoomer::end( bool ok )
 | 
|---|
| 578 | {
 | 
|---|
| 579 |     ok = QwtPlotPicker::end( ok );
 | 
|---|
| 580 |     if ( !ok )
 | 
|---|
| 581 |         return false;
 | 
|---|
| 582 | 
 | 
|---|
| 583 |     QwtPlot *plot = QwtPlotZoomer::plot();
 | 
|---|
| 584 |     if ( !plot )
 | 
|---|
| 585 |         return false;
 | 
|---|
| 586 | 
 | 
|---|
| 587 |     const QPolygon &pa = selection();
 | 
|---|
| 588 |     if ( pa.count() < 2 )
 | 
|---|
| 589 |         return false;
 | 
|---|
| 590 | 
 | 
|---|
| 591 |     QRect rect = QRect( pa[0], pa[int( pa.count() - 1 )] );
 | 
|---|
| 592 |     rect = rect.normalized();
 | 
|---|
| 593 | 
 | 
|---|
| 594 |     QRectF zoomRect = invTransform( rect ).normalized();
 | 
|---|
| 595 | 
 | 
|---|
| 596 |     const QSizeF minSize = minZoomSize();
 | 
|---|
| 597 |     if ( minSize.isValid() )
 | 
|---|
| 598 |     {
 | 
|---|
| 599 |         const QPointF center = zoomRect.center();
 | 
|---|
| 600 |         zoomRect.setSize( zoomRect.size().expandedTo( minZoomSize() ) );
 | 
|---|
| 601 |         zoomRect.moveCenter( center );
 | 
|---|
| 602 |     }
 | 
|---|
| 603 | 
 | 
|---|
| 604 |     zoom( zoomRect );
 | 
|---|
| 605 | 
 | 
|---|
| 606 |     return true;
 | 
|---|
| 607 | }
 | 
|---|