source: ntrip/trunk/BNC/qwt/qwt_picker.cpp@ 10575

Last change on this file since 10575 was 9383, checked in by stoecker, 4 years ago

update to qwt verion 6.1.1 to fix build with newer Qt5

File size: 38.9 KB
RevLine 
[4271]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_picker.h"
11#include "qwt_picker_machine.h"
12#include "qwt_painter.h"
13#include "qwt_math.h"
[8127]14#include "qwt_widget_overlay.h"
[4271]15#include <qapplication.h>
16#include <qevent.h>
17#include <qpainter.h>
18#include <qframe.h>
19#include <qcursor.h>
20#include <qbitmap.h>
21#include <qpointer.h>
22#include <qpaintengine.h>
23#include <qmath.h>
24
[8127]25static inline QRegion qwtMaskRegion( const QRect &r, int penWidth )
[4271]26{
[8127]27 const int pw = qMax( penWidth, 1 );
28 const int pw2 = penWidth / 2;
29
30 int x1 = r.left() - pw2;
31 int x2 = r.right() + 1 + pw2 + ( pw % 2 );
32
33 int y1 = r.top() - pw2;
34 int y2 = r.bottom() + 1 + pw2 + ( pw % 2 );
35
36 QRegion region;
37
38 region += QRect( x1, y1, x2 - x1, pw );
39 region += QRect( x1, y1, pw, y2 - y1 );
40 region += QRect( x1, y2 - pw, x2 - x1, pw );
41 region += QRect( x2 - pw, y1, pw, y2 - y1 );
42
43 return region;
44}
45
46static inline QRegion qwtMaskRegion( const QLine &l, int penWidth )
47{
48 const int pw = qMax( penWidth, 1 );
49 const int pw2 = penWidth / 2;
50
51 QRegion region;
52
53 if ( l.x1() == l.x2() )
[4271]54 {
[9383]55 region += QRect( l.x1() - pw2, l.y1(),
[8127]56 pw, l.y2() ).normalized();
57 }
58 else if ( l.y1() == l.y2() )
59 {
[9383]60 region += QRect( l.x1(), l.y1() - pw2,
[8127]61 l.x2(), pw ).normalized();
62 }
[4271]63
[8127]64 return region;
65}
[4271]66
[8127]67class QwtPickerRubberband: public QwtWidgetOverlay
68{
69public:
70 QwtPickerRubberband( QwtPicker *, QWidget * );
[4271]71
72protected:
[8127]73 virtual void drawOverlay( QPainter * ) const;
74 virtual QRegion maskHint() const;
[4271]75
76 QwtPicker *d_picker;
77};
78
[8127]79class QwtPickerTracker: public QwtWidgetOverlay
[9383]80{
[8127]81public:
82 QwtPickerTracker( QwtPicker *, QWidget * );
[9383]83
[8127]84protected:
85 virtual void drawOverlay( QPainter * ) const;
86 virtual QRegion maskHint() const;
[9383]87
[8127]88 QwtPicker *d_picker;
[9383]89};
[8127]90
91
[4271]92class QwtPicker::PrivateData
93{
94public:
[8127]95 PrivateData():
96 enabled( false ),
97 stateMachine( NULL ),
98 resizeMode( QwtPicker::Stretch ),
99 rubberBand( QwtPicker::NoRubberBand ),
100 trackerMode( QwtPicker::AlwaysOff ),
101 isActive( false ),
102 trackerPosition( -1, -1 ),
103 mouseTracking( false ),
104 openGL( false )
105 {
106 }
[9383]107
[4271]108 bool enabled;
109
110 QwtPickerMachine *stateMachine;
111
112 QwtPicker::ResizeMode resizeMode;
113
114 QwtPicker::RubberBand rubberBand;
115 QPen rubberBandPen;
116
117 QwtPicker::DisplayMode trackerMode;
118 QPen trackerPen;
119 QFont trackerFont;
120
121 QPolygon pickedPoints;
122 bool isActive;
123 QPoint trackerPosition;
124
125 bool mouseTracking; // used to save previous value
126
[8127]127 QPointer< QwtPickerRubberband > rubberBandOverlay;
128 QPointer< QwtPickerTracker> trackerOverlay;
[4271]129
[8127]130 bool openGL;
[4271]131};
132
[8127]133QwtPickerRubberband::QwtPickerRubberband(
134 QwtPicker *picker, QWidget *parent ):
135 QwtWidgetOverlay( parent ),
136 d_picker( picker )
[4271]137{
[8127]138 setMaskMode( QwtWidgetOverlay::MaskHint );
[4271]139}
140
[8127]141QRegion QwtPickerRubberband::maskHint() const
[4271]142{
[8127]143 return d_picker->rubberBandMask();
144}
[4271]145
[8127]146void QwtPickerRubberband::drawOverlay( QPainter *painter ) const
147{
148 painter->setPen( d_picker->rubberBandPen() );
149 d_picker->drawRubberBand( painter );
150}
[4271]151
[8127]152QwtPickerTracker::QwtPickerTracker(
153 QwtPicker *picker, QWidget *parent ):
154 QwtWidgetOverlay( parent ),
155 d_picker( picker )
156{
157 setMaskMode( QwtWidgetOverlay::MaskHint );
158}
[4271]159
[8127]160QRegion QwtPickerTracker::maskHint() const
161{
162 return d_picker->trackerRect( font() );
[4271]163}
164
[8127]165void QwtPickerTracker::drawOverlay( QPainter *painter ) const
[4271]166{
[8127]167 painter->setPen( d_picker->trackerPen() );
168 d_picker->drawTracker( painter );
[4271]169}
170
171/*!
172 Constructor
173
174 Creates an picker that is enabled, but without a state machine.
[8127]175 rubber band and tracker are disabled.
[4271]176
177 \param parent Parent widget, that will be observed
178 */
179
180QwtPicker::QwtPicker( QWidget *parent ):
181 QObject( parent )
182{
183 init( parent, NoRubberBand, AlwaysOff );
184}
185
186/*!
187 Constructor
188
[8127]189 \param rubberBand Rubber band style
[4271]190 \param trackerMode Tracker mode
191 \param parent Parent widget, that will be observed
192 */
193QwtPicker::QwtPicker( RubberBand rubberBand,
194 DisplayMode trackerMode, QWidget *parent ):
195 QObject( parent )
196{
197 init( parent, rubberBand, trackerMode );
198}
199
200//! Destructor
201QwtPicker::~QwtPicker()
202{
203 setMouseTracking( false );
[8127]204
[4271]205 delete d_data->stateMachine;
[8127]206 delete d_data->rubberBandOverlay;
207 delete d_data->trackerOverlay;
208
[4271]209 delete d_data;
210}
211
[8127]212//! Initialize the picker - used by the constructors
[4271]213void QwtPicker::init( QWidget *parent,
214 RubberBand rubberBand, DisplayMode trackerMode )
215{
216 d_data = new PrivateData;
217
218 d_data->rubberBand = rubberBand;
219
220 if ( parent )
221 {
222 if ( parent->focusPolicy() == Qt::NoFocus )
223 parent->setFocusPolicy( Qt::WheelFocus );
224
[8127]225 d_data->openGL = parent->inherits( "QGLWidget" );
[4271]226 d_data->trackerFont = parent->font();
227 d_data->mouseTracking = parent->hasMouseTracking();
[8127]228
[4271]229 setEnabled( true );
230 }
[8127]231
[4271]232 setTrackerMode( trackerMode );
233}
234
235/*!
236 Set a state machine and delete the previous one
237
238 \param stateMachine State machine
239 \sa stateMachine()
240*/
241void QwtPicker::setStateMachine( QwtPickerMachine *stateMachine )
242{
243 if ( d_data->stateMachine != stateMachine )
244 {
245 reset();
246
247 delete d_data->stateMachine;
248 d_data->stateMachine = stateMachine;
249
250 if ( d_data->stateMachine )
251 d_data->stateMachine->reset();
252 }
253}
254
255/*!
256 \return Assigned state machine
257 \sa setStateMachine()
258*/
259QwtPickerMachine *QwtPicker::stateMachine()
260{
261 return d_data->stateMachine;
262}
263
264/*!
265 \return Assigned state machine
266 \sa setStateMachine()
267*/
268const QwtPickerMachine *QwtPicker::stateMachine() const
269{
270 return d_data->stateMachine;
271}
272
273//! Return the parent widget, where the selection happens
274QWidget *QwtPicker::parentWidget()
275{
276 QObject *obj = parent();
277 if ( obj && obj->isWidgetType() )
278 return static_cast<QWidget *>( obj );
279
280 return NULL;
281}
282
283//! Return the parent widget, where the selection happens
284const QWidget *QwtPicker::parentWidget() const
285{
286 QObject *obj = parent();
287 if ( obj && obj->isWidgetType() )
288 return static_cast< const QWidget *>( obj );
289
290 return NULL;
291}
292
293/*!
[8127]294 Set the rubber band style
[4271]295
[8127]296 \param rubberBand Rubber band style
[4271]297 The default value is NoRubberBand.
298
299 \sa rubberBand(), RubberBand, setRubberBandPen()
300*/
301void QwtPicker::setRubberBand( RubberBand rubberBand )
302{
303 d_data->rubberBand = rubberBand;
304}
305
306/*!
[8127]307 \return Rubber band style
[4271]308 \sa setRubberBand(), RubberBand, rubberBandPen()
309*/
310QwtPicker::RubberBand QwtPicker::rubberBand() const
311{
312 return d_data->rubberBand;
313}
314
315/*!
316 \brief Set the display mode of the tracker.
317
318 A tracker displays information about current position of
319 the cursor as a string. The display mode controls
320 if the tracker has to be displayed whenever the observed
321 widget has focus and cursor (AlwaysOn), never (AlwaysOff), or
322 only when the selection is active (ActiveOnly).
323
324 \param mode Tracker display mode
325
326 \warning In case of AlwaysOn, mouseTracking will be enabled
327 for the observed widget.
328 \sa trackerMode(), DisplayMode
329*/
330
331void QwtPicker::setTrackerMode( DisplayMode mode )
332{
333 if ( d_data->trackerMode != mode )
334 {
335 d_data->trackerMode = mode;
336 setMouseTracking( d_data->trackerMode == AlwaysOn );
337 }
338}
339
340/*!
341 \return Tracker display mode
342 \sa setTrackerMode(), DisplayMode
343*/
344QwtPicker::DisplayMode QwtPicker::trackerMode() const
345{
346 return d_data->trackerMode;
347}
348
349/*!
350 \brief Set the resize mode.
351
352 The resize mode controls what to do with the selected points of an active
353 selection when the observed widget is resized.
354
355 Stretch means the points are scaled according to the new
356 size, KeepSize means the points remain unchanged.
357
358 The default mode is Stretch.
359
360 \param mode Resize mode
361 \sa resizeMode(), ResizeMode
362*/
363void QwtPicker::setResizeMode( ResizeMode mode )
364{
365 d_data->resizeMode = mode;
366}
367
368/*!
369 \return Resize mode
370 \sa setResizeMode(), ResizeMode
371*/
372
373QwtPicker::ResizeMode QwtPicker::resizeMode() const
374{
375 return d_data->resizeMode;
376}
377
378/*!
379 \brief En/disable the picker
380
381 When enabled is true an event filter is installed for
382 the observed widget, otherwise the event filter is removed.
383
384 \param enabled true or false
385 \sa isEnabled(), eventFilter()
386*/
387void QwtPicker::setEnabled( bool enabled )
388{
389 if ( d_data->enabled != enabled )
390 {
391 d_data->enabled = enabled;
392
393 QWidget *w = parentWidget();
394 if ( w )
395 {
396 if ( enabled )
397 w->installEventFilter( this );
398 else
399 w->removeEventFilter( this );
400 }
401
402 updateDisplay();
403 }
404}
405
406/*!
407 \return true when enabled, false otherwise
408 \sa setEnabled(), eventFilter()
409*/
410
411bool QwtPicker::isEnabled() const
412{
413 return d_data->enabled;
414}
415
416/*!
417 Set the font for the tracker
418
419 \param font Tracker font
420 \sa trackerFont(), setTrackerMode(), setTrackerPen()
421*/
422void QwtPicker::setTrackerFont( const QFont &font )
423{
424 if ( font != d_data->trackerFont )
425 {
426 d_data->trackerFont = font;
427 updateDisplay();
428 }
429}
430
431/*!
432 \return Tracker font
433 \sa setTrackerFont(), trackerMode(), trackerPen()
434*/
435
436QFont QwtPicker::trackerFont() const
437{
438 return d_data->trackerFont;
439}
440
441/*!
442 Set the pen for the tracker
443
444 \param pen Tracker pen
445 \sa trackerPen(), setTrackerMode(), setTrackerFont()
446*/
447void QwtPicker::setTrackerPen( const QPen &pen )
448{
449 if ( pen != d_data->trackerPen )
450 {
451 d_data->trackerPen = pen;
452 updateDisplay();
453 }
454}
455
456/*!
457 \return Tracker pen
458 \sa setTrackerPen(), trackerMode(), trackerFont()
459*/
460QPen QwtPicker::trackerPen() const
461{
462 return d_data->trackerPen;
463}
464
465/*!
466 Set the pen for the rubberband
467
[8127]468 \param pen Rubber band pen
[4271]469 \sa rubberBandPen(), setRubberBand()
470*/
471void QwtPicker::setRubberBandPen( const QPen &pen )
472{
473 if ( pen != d_data->rubberBandPen )
474 {
475 d_data->rubberBandPen = pen;
476 updateDisplay();
477 }
478}
479
480/*!
[8127]481 \return Rubber band pen
[4271]482 \sa setRubberBandPen(), rubberBand()
483*/
484QPen QwtPicker::rubberBandPen() const
485{
486 return d_data->rubberBandPen;
487}
488
489/*!
490 \brief Return the label for a position
491
492 In case of HLineRubberBand the label is the value of the
493 y position, in case of VLineRubberBand the value of the x position.
494 Otherwise the label contains x and y position separated by a ',' .
495
496 The format for the string conversion is "%d".
497
498 \param pos Position
499 \return Converted position as string
500*/
501
502QwtText QwtPicker::trackerText( const QPoint &pos ) const
503{
504 QString label;
505
506 switch ( rubberBand() )
507 {
508 case HLineRubberBand:
509 label.sprintf( "%d", pos.y() );
510 break;
511 case VLineRubberBand:
512 label.sprintf( "%d", pos.x() );
513 break;
514 default:
515 label.sprintf( "%d, %d", pos.x(), pos.y() );
516 }
517 return label;
518}
519
520/*!
[8127]521 Calculate the mask for the rubber band overlay
[4271]522
[8127]523 \return Region for the mask
524 \sa QWidget::setMask()
525 */
526QRegion QwtPicker::rubberBandMask() const
527{
528 QRegion mask;
[4271]529
[8127]530 if ( !isActive() || rubberBand() == NoRubberBand ||
531 rubberBandPen().style() == Qt::NoPen )
532 {
533 return mask;
534 }
535
536 const QPolygon pa = adjustedPoints( d_data->pickedPoints );
537
538 QwtPickerMachine::SelectionType selectionType =
539 QwtPickerMachine::NoSelection;
540
541 if ( d_data->stateMachine )
542 selectionType = d_data->stateMachine->selectionType();
543
544 switch ( selectionType )
545 {
546 case QwtPickerMachine::NoSelection:
547 case QwtPickerMachine::PointSelection:
548 {
549 if ( pa.count() < 1 )
550 return mask;
551
552 const QPoint pos = pa[0];
553 const int pw = rubberBandPen().width();
554
555 const QRect pRect = pickArea().boundingRect().toRect();
556 switch ( rubberBand() )
557 {
558 case VLineRubberBand:
559 {
[9383]560 mask += qwtMaskRegion( QLine( pos.x(), pRect.top(),
[8127]561 pos.x(), pRect.bottom() ), pw );
562 break;
563 }
564 case HLineRubberBand:
565 {
[9383]566 mask += qwtMaskRegion( QLine( pRect.left(), pos.y(),
[8127]567 pRect.right(), pos.y() ), pw );
568 break;
569 }
570 case CrossRubberBand:
571 {
[9383]572 mask += qwtMaskRegion( QLine( pos.x(), pRect.top(),
[8127]573 pos.x(), pRect.bottom() ), pw );
[9383]574 mask += qwtMaskRegion( QLine( pRect.left(), pos.y(),
[8127]575 pRect.right(), pos.y() ), pw );
576 break;
577 }
578 default:
579 break;
580 }
581 break;
582 }
583 case QwtPickerMachine::RectSelection:
584 {
585 if ( pa.count() < 2 )
586 return mask;
587
588 const int pw = rubberBandPen().width();
589
590 switch ( rubberBand() )
591 {
592 case RectRubberBand:
593 {
594 const QRect r = QRect( pa.first(), pa.last() );
595 mask = qwtMaskRegion( r.normalized(), pw );
596 break;
597 }
598 case EllipseRubberBand:
599 {
600 const QRect r = QRect( pa.first(), pa.last() );
601 mask += r.adjusted( -pw, -pw, pw, pw );
602 break;
603 }
604 default:
605 break;
606 }
607 break;
608 }
609 case QwtPickerMachine::PolygonSelection:
610 {
611 const int pw = rubberBandPen().width();
612 if ( pw <= 1 )
613 {
614 // because of the join style we better
615 // return a mask for a pen width <= 1 only
616
617 const int off = 2 * pw;
618 const QRect r = pa.boundingRect();
619 mask += r.adjusted( -off, -off, off, off );
620 }
621 break;
622 }
623 default:
624 break;
625 }
626
627 return mask;
628}
629
630/*!
631 Draw a rubber band, depending on rubberBand()
632
633 \param painter Painter, initialized with a clip region
634
[4271]635 \sa rubberBand(), RubberBand
636*/
637
638void QwtPicker::drawRubberBand( QPainter *painter ) const
639{
640 if ( !isActive() || rubberBand() == NoRubberBand ||
641 rubberBandPen().style() == Qt::NoPen )
642 {
643 return;
644 }
645
646 const QPolygon pa = adjustedPoints( d_data->pickedPoints );
647
648 QwtPickerMachine::SelectionType selectionType =
649 QwtPickerMachine::NoSelection;
650
651 if ( d_data->stateMachine )
652 selectionType = d_data->stateMachine->selectionType();
653
654 switch ( selectionType )
655 {
656 case QwtPickerMachine::NoSelection:
657 case QwtPickerMachine::PointSelection:
658 {
659 if ( pa.count() < 1 )
660 return;
661
662 const QPoint pos = pa[0];
663
[8127]664 const QRect pRect = pickArea().boundingRect().toRect();
[4271]665 switch ( rubberBand() )
666 {
667 case VLineRubberBand:
[8127]668 {
[4271]669 QwtPainter::drawLine( painter, pos.x(),
670 pRect.top(), pos.x(), pRect.bottom() );
671 break;
[8127]672 }
[4271]673 case HLineRubberBand:
[8127]674 {
[4271]675 QwtPainter::drawLine( painter, pRect.left(),
676 pos.y(), pRect.right(), pos.y() );
677 break;
[8127]678 }
[4271]679 case CrossRubberBand:
[8127]680 {
[4271]681 QwtPainter::drawLine( painter, pos.x(),
682 pRect.top(), pos.x(), pRect.bottom() );
683 QwtPainter::drawLine( painter, pRect.left(),
684 pos.y(), pRect.right(), pos.y() );
685 break;
[8127]686 }
[4271]687 default:
688 break;
689 }
690 break;
691 }
692 case QwtPickerMachine::RectSelection:
693 {
694 if ( pa.count() < 2 )
695 return;
696
[8127]697 const QRect rect = QRect( pa.first(), pa.last() ).normalized();
[4271]698 switch ( rubberBand() )
699 {
700 case EllipseRubberBand:
[8127]701 {
[4271]702 QwtPainter::drawEllipse( painter, rect );
703 break;
[8127]704 }
[4271]705 case RectRubberBand:
[8127]706 {
[4271]707 QwtPainter::drawRect( painter, rect );
708 break;
[8127]709 }
[4271]710 default:
711 break;
712 }
713 break;
714 }
715 case QwtPickerMachine::PolygonSelection:
716 {
717 if ( rubberBand() == PolygonRubberBand )
718 painter->drawPolyline( pa );
719 break;
720 }
721 default:
722 break;
723 }
724}
725
726/*!
727 Draw the tracker
728
729 \param painter Painter
730 \sa trackerRect(), trackerText()
731*/
732
733void QwtPicker::drawTracker( QPainter *painter ) const
734{
735 const QRect textRect = trackerRect( painter->font() );
736 if ( !textRect.isEmpty() )
737 {
738 const QwtText label = trackerText( d_data->trackerPosition );
739 if ( !label.isEmpty() )
740 label.draw( painter, textRect );
741 }
742}
743
744/*!
745 \brief Map the pickedPoints() into a selection()
746
747 adjustedPoints() maps the points, that have been collected on
748 the parentWidget() into a selection(). The default implementation
749 simply returns the points unmodified.
750
751 The reason, why a selection() differs from the picked points
752 depends on the application requirements. F.e. :
753
[9383]754 - A rectangular selection might need to have a specific aspect ratio only.
755 - A selection could accept non intersecting polygons only.
756 - ...
[4271]757
758 The example below is for a rectangular selection, where the first
759 point is the center of the selected rectangle.
[9383]760
[4271]761 \par Example
[9383]762 \code
763 QPolygon MyPicker::adjustedPoints( const QPolygon &points ) const
[4271]764 {
[9383]765 QPolygon adjusted;
766 if ( points.size() == 2 )
767 {
768 const int width = qAbs( points[1].x() - points[0].x() );
769 const int height = qAbs( points[1].y() - points[0].y() );
[4271]770
[9383]771 QRect rect( 0, 0, 2 * width, 2 * height );
772 rect.moveCenter( points[0] );
[4271]773
[9383]774 adjusted += rect.topLeft();
775 adjusted += rect.bottomRight();
776 }
777 return adjusted;
[4271]778 }
[9383]779 \endcode
780 \endpar
[8127]781
782 \param points Selected points
783 \return Selected points unmodified
[4271]784*/
785QPolygon QwtPicker::adjustedPoints( const QPolygon &points ) const
786{
787 return points;
788}
789
790/*!
791 \return Selected points
792 \sa pickedPoints(), adjustedPoints()
793*/
794QPolygon QwtPicker::selection() const
795{
796 return adjustedPoints( d_data->pickedPoints );
797}
798
799//! \return Current position of the tracker
800QPoint QwtPicker::trackerPosition() const
801{
802 return d_data->trackerPosition;
803}
804
805/*!
806 Calculate the bounding rectangle for the tracker text
807 from the current position of the tracker
808
809 \param font Font of the tracker text
810 \return Bounding rectangle of the tracker text
811
812 \sa trackerPosition()
813*/
814QRect QwtPicker::trackerRect( const QFont &font ) const
815{
816 if ( trackerMode() == AlwaysOff ||
817 ( trackerMode() == ActiveOnly && !isActive() ) )
818 {
819 return QRect();
820 }
821
822 if ( d_data->trackerPosition.x() < 0 || d_data->trackerPosition.y() < 0 )
823 return QRect();
824
825 QwtText text = trackerText( d_data->trackerPosition );
826 if ( text.isEmpty() )
827 return QRect();
828
829 const QSizeF textSize = text.textSize( font );
830 QRect textRect( 0, 0, qCeil( textSize.width() ), qCeil( textSize.height() ) );
831
832 const QPoint &pos = d_data->trackerPosition;
833
834 int alignment = 0;
835 if ( isActive() && d_data->pickedPoints.count() > 1
836 && rubberBand() != NoRubberBand )
837 {
838 const QPoint last =
[9383]839 d_data->pickedPoints[ d_data->pickedPoints.count() - 2 ];
[4271]840
841 alignment |= ( pos.x() >= last.x() ) ? Qt::AlignRight : Qt::AlignLeft;
842 alignment |= ( pos.y() > last.y() ) ? Qt::AlignBottom : Qt::AlignTop;
843 }
844 else
845 alignment = Qt::AlignTop | Qt::AlignRight;
846
847 const int margin = 5;
848
849 int x = pos.x();
850 if ( alignment & Qt::AlignLeft )
851 x -= textRect.width() + margin;
852 else if ( alignment & Qt::AlignRight )
853 x += margin;
854
855 int y = pos.y();
856 if ( alignment & Qt::AlignBottom )
857 y += margin;
858 else if ( alignment & Qt::AlignTop )
859 y -= textRect.height() + margin;
860
861 textRect.moveTopLeft( QPoint( x, y ) );
862
[8127]863 const QRect pickRect = pickArea().boundingRect().toRect();
864
865 int right = qMin( textRect.right(), pickRect.right() - margin );
866 int bottom = qMin( textRect.bottom(), pickRect.bottom() - margin );
[4271]867 textRect.moveBottomRight( QPoint( right, bottom ) );
868
[8127]869 int left = qMax( textRect.left(), pickRect.left() + margin );
870 int top = qMax( textRect.top(), pickRect.top() + margin );
[4271]871 textRect.moveTopLeft( QPoint( left, top ) );
872
873 return textRect;
874}
875
876/*!
877 \brief Event filter
878
[8127]879 When isEnabled() is true all events of the observed widget are filtered.
[4271]880 Mouse and keyboard events are translated into widgetMouse- and widgetKey-
881 and widgetWheel-events. Paint and Resize events are handled to keep
[8127]882 rubber band and tracker up to date.
[4271]883
884 \param object Object to be filtered
885 \param event Event
886
[8127]887 \return Always false.
888
[4271]889 \sa widgetEnterEvent(), widgetLeaveEvent(),
890 widgetMousePressEvent(), widgetMouseReleaseEvent(),
891 widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(),
892 widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent(),
893 QObject::installEventFilter(), QObject::event()
894*/
895bool QwtPicker::eventFilter( QObject *object, QEvent *event )
896{
897 if ( object && object == parentWidget() )
898 {
899 switch ( event->type() )
900 {
901 case QEvent::Resize:
902 {
[8127]903 const QResizeEvent *re = static_cast<QResizeEvent *>( event );
904
905 /*
906 Adding/deleting additional event filters inside of an event filter
907 is not safe dues to the implementation in Qt ( changing alist while iterating ).
908 So we create the overlays in a way, that they don't install en event filter
909 ( parent set to NULL ) and do the resizing here.
910 */
911 if ( d_data->trackerOverlay )
912 d_data->trackerOverlay->resize( re->size() );
913
914 if ( d_data->rubberBandOverlay )
915 d_data->rubberBandOverlay->resize( re->size() );
916
[4271]917 if ( d_data->resizeMode == Stretch )
918 stretchSelection( re->oldSize(), re->size() );
919
[8127]920 updateDisplay();
[4271]921 break;
922 }
923 case QEvent::Enter:
[8127]924 {
[4271]925 widgetEnterEvent( event );
926 break;
[8127]927 }
[4271]928 case QEvent::Leave:
[8127]929 {
[4271]930 widgetLeaveEvent( event );
931 break;
[8127]932 }
[4271]933 case QEvent::MouseButtonPress:
[8127]934 {
935 widgetMousePressEvent( static_cast<QMouseEvent *>( event ) );
[4271]936 break;
[8127]937 }
[4271]938 case QEvent::MouseButtonRelease:
[8127]939 {
940 widgetMouseReleaseEvent( static_cast<QMouseEvent *>( event ) );
[4271]941 break;
[8127]942 }
[4271]943 case QEvent::MouseButtonDblClick:
[8127]944 {
945 widgetMouseDoubleClickEvent( static_cast<QMouseEvent *>( event ) );
[4271]946 break;
[8127]947 }
[4271]948 case QEvent::MouseMove:
[8127]949 {
950 widgetMouseMoveEvent( static_cast<QMouseEvent *>( event ) );
[4271]951 break;
[8127]952 }
[4271]953 case QEvent::KeyPress:
[8127]954 {
955 widgetKeyPressEvent( static_cast<QKeyEvent *>( event ) );
[4271]956 break;
[8127]957 }
[4271]958 case QEvent::KeyRelease:
[8127]959 {
960 widgetKeyReleaseEvent( static_cast<QKeyEvent *>( event ) );
[4271]961 break;
[8127]962 }
[4271]963 case QEvent::Wheel:
[8127]964 {
965 widgetWheelEvent( static_cast<QWheelEvent *>( event ) );
[4271]966 break;
[8127]967 }
[4271]968 default:
969 break;
970 }
971 }
972 return false;
973}
974
975/*!
976 Handle a mouse press event for the observed widget.
977
978 \param mouseEvent Mouse event
979
980 \sa eventFilter(), widgetMouseReleaseEvent(),
981 widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(),
982 widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent()
983*/
984void QwtPicker::widgetMousePressEvent( QMouseEvent *mouseEvent )
985{
986 transition( mouseEvent );
987}
988
989/*!
990 Handle a mouse move event for the observed widget.
991
992 \param mouseEvent Mouse event
993
994 \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(),
995 widgetMouseDoubleClickEvent(),
996 widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent()
997*/
998void QwtPicker::widgetMouseMoveEvent( QMouseEvent *mouseEvent )
999{
[8127]1000 if ( pickArea().contains( mouseEvent->pos() ) )
[4271]1001 d_data->trackerPosition = mouseEvent->pos();
1002 else
1003 d_data->trackerPosition = QPoint( -1, -1 );
1004
1005 if ( !isActive() )
1006 updateDisplay();
1007
1008 transition( mouseEvent );
1009}
1010
1011/*!
1012 Handle a enter event for the observed widget.
1013
1014 \param event Qt event
1015
1016 \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(),
1017 widgetMouseDoubleClickEvent(),
1018 widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent()
1019*/
1020void QwtPicker::widgetEnterEvent( QEvent *event )
1021{
1022 transition( event );
1023}
1024
1025/*!
1026 Handle a leave event for the observed widget.
1027
1028 \param event Qt event
1029
1030 \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(),
1031 widgetMouseDoubleClickEvent(),
1032 widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent()
1033*/
1034void QwtPicker::widgetLeaveEvent( QEvent *event )
1035{
1036 transition( event );
1037
1038 d_data->trackerPosition = QPoint( -1, -1 );
1039 if ( !isActive() )
1040 updateDisplay();
1041}
1042
1043/*!
[8127]1044 Handle a mouse release event for the observed widget.
[4271]1045
1046 \param mouseEvent Mouse event
1047
1048 \sa eventFilter(), widgetMousePressEvent(),
1049 widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(),
1050 widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent()
1051*/
1052void QwtPicker::widgetMouseReleaseEvent( QMouseEvent *mouseEvent )
1053{
1054 transition( mouseEvent );
1055}
1056
1057/*!
1058 Handle mouse double click event for the observed widget.
1059
1060 \param mouseEvent Mouse event
1061
1062 \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(),
1063 widgetMouseMoveEvent(),
1064 widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent()
1065*/
1066void QwtPicker::widgetMouseDoubleClickEvent( QMouseEvent *mouseEvent )
1067{
1068 transition( mouseEvent );
1069}
1070
1071
1072/*!
1073 Handle a wheel event for the observed widget.
1074
1075 Move the last point of the selection in case of isActive() == true
1076
1077 \param wheelEvent Wheel event
1078
1079 \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(),
1080 widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(),
1081 widgetKeyPressEvent(), widgetKeyReleaseEvent()
1082*/
1083void QwtPicker::widgetWheelEvent( QWheelEvent *wheelEvent )
1084{
[8127]1085 if ( pickArea().contains( wheelEvent->pos() ) )
[4271]1086 d_data->trackerPosition = wheelEvent->pos();
1087 else
1088 d_data->trackerPosition = QPoint( -1, -1 );
1089
1090 updateDisplay();
1091
1092 transition( wheelEvent );
1093}
1094
1095/*!
1096 Handle a key press event for the observed widget.
1097
1098 Selections can be completely done by the keyboard. The arrow keys
1099 move the cursor, the abort key aborts a selection. All other keys
1100 are handled by the current state machine.
1101
1102 \param keyEvent Key event
1103
1104 \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(),
1105 widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(),
1106 widgetWheelEvent(), widgetKeyReleaseEvent(), stateMachine(),
1107 QwtEventPattern::KeyPatternCode
1108*/
1109void QwtPicker::widgetKeyPressEvent( QKeyEvent *keyEvent )
1110{
1111 int dx = 0;
1112 int dy = 0;
1113
1114 int offset = 1;
1115 if ( keyEvent->isAutoRepeat() )
1116 offset = 5;
1117
1118 if ( keyMatch( KeyLeft, keyEvent ) )
1119 dx = -offset;
1120 else if ( keyMatch( KeyRight, keyEvent ) )
1121 dx = offset;
1122 else if ( keyMatch( KeyUp, keyEvent ) )
1123 dy = -offset;
1124 else if ( keyMatch( KeyDown, keyEvent ) )
1125 dy = offset;
1126 else if ( keyMatch( KeyAbort, keyEvent ) )
1127 {
1128 reset();
1129 }
1130 else
1131 transition( keyEvent );
1132
1133 if ( dx != 0 || dy != 0 )
1134 {
[8127]1135 const QRect rect = pickArea().boundingRect().toRect();
[4271]1136 const QPoint pos = parentWidget()->mapFromGlobal( QCursor::pos() );
1137
1138 int x = pos.x() + dx;
1139 x = qMax( rect.left(), x );
1140 x = qMin( rect.right(), x );
1141
1142 int y = pos.y() + dy;
1143 y = qMax( rect.top(), y );
1144 y = qMin( rect.bottom(), y );
1145
1146 QCursor::setPos( parentWidget()->mapToGlobal( QPoint( x, y ) ) );
1147 }
1148}
1149
1150/*!
1151 Handle a key release event for the observed widget.
1152
1153 Passes the event to the state machine.
1154
1155 \param keyEvent Key event
1156
1157 \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(),
1158 widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(),
1159 widgetWheelEvent(), widgetKeyPressEvent(), stateMachine()
1160*/
1161void QwtPicker::widgetKeyReleaseEvent( QKeyEvent *keyEvent )
1162{
1163 transition( keyEvent );
1164}
1165
1166/*!
1167 Passes an event to the state machine and executes the resulting
1168 commands. Append and Move commands use the current position
[8127]1169 of the cursor ( QCursor::pos() ).
[4271]1170
1171 \param event Event
1172*/
1173void QwtPicker::transition( const QEvent *event )
1174{
1175 if ( !d_data->stateMachine )
1176 return;
1177
1178 const QList<QwtPickerMachine::Command> commandList =
1179 d_data->stateMachine->transition( *this, event );
1180
1181 QPoint pos;
1182 switch ( event->type() )
1183 {
1184 case QEvent::MouseButtonDblClick:
1185 case QEvent::MouseButtonPress:
1186 case QEvent::MouseButtonRelease:
1187 case QEvent::MouseMove:
1188 {
[9383]1189 const QMouseEvent *me =
[4271]1190 static_cast< const QMouseEvent * >( event );
1191 pos = me->pos();
1192 break;
1193 }
1194 default:
1195 pos = parentWidget()->mapFromGlobal( QCursor::pos() );
1196 }
1197
1198 for ( int i = 0; i < commandList.count(); i++ )
1199 {
1200 switch ( commandList[i] )
1201 {
1202 case QwtPickerMachine::Begin:
1203 {
1204 begin();
1205 break;
1206 }
1207 case QwtPickerMachine::Append:
1208 {
1209 append( pos );
1210 break;
1211 }
1212 case QwtPickerMachine::Move:
1213 {
1214 move( pos );
1215 break;
1216 }
1217 case QwtPickerMachine::Remove:
1218 {
1219 remove();
1220 break;
1221 }
1222 case QwtPickerMachine::End:
1223 {
1224 end();
1225 break;
1226 }
1227 }
1228 }
1229}
1230
1231/*!
1232 Open a selection setting the state to active
1233
1234 \sa isActive(), end(), append(), move()
1235*/
1236void QwtPicker::begin()
1237{
1238 if ( d_data->isActive )
1239 return;
1240
[9383]1241 d_data->pickedPoints.clear();
[4271]1242 d_data->isActive = true;
1243 Q_EMIT activated( true );
1244
1245 if ( trackerMode() != AlwaysOff )
1246 {
1247 if ( d_data->trackerPosition.x() < 0 || d_data->trackerPosition.y() < 0 )
1248 {
1249 QWidget *w = parentWidget();
1250 if ( w )
1251 d_data->trackerPosition = w->mapFromGlobal( QCursor::pos() );
1252 }
1253 }
1254
1255 updateDisplay();
1256 setMouseTracking( true );
1257}
1258
1259/*!
1260 \brief Close a selection setting the state to inactive.
1261
1262 The selection is validated and maybe fixed by accept().
1263
1264 \param ok If true, complete the selection and emit a selected signal
1265 otherwise discard the selection.
1266 \return true if the selection is accepted, false otherwise
1267 \sa isActive(), begin(), append(), move(), selected(), accept()
1268*/
1269bool QwtPicker::end( bool ok )
1270{
1271 if ( d_data->isActive )
1272 {
1273 setMouseTracking( false );
1274
1275 d_data->isActive = false;
1276 Q_EMIT activated( false );
1277
1278 if ( trackerMode() == ActiveOnly )
1279 d_data->trackerPosition = QPoint( -1, -1 );
1280
1281 if ( ok )
1282 ok = accept( d_data->pickedPoints );
1283
1284 if ( ok )
1285 Q_EMIT selected( d_data->pickedPoints );
1286 else
[9383]1287 d_data->pickedPoints.clear();
[4271]1288
1289 updateDisplay();
1290 }
1291 else
1292 ok = false;
1293
1294 return ok;
1295}
1296
1297/*!
[8127]1298 Reset the state machine and terminate ( end(false) ) the selection
[4271]1299*/
1300void QwtPicker::reset()
1301{
1302 if ( d_data->stateMachine )
1303 d_data->stateMachine->reset();
1304
1305 if ( isActive() )
1306 end( false );
1307}
1308
1309/*!
[8127]1310 Append a point to the selection and update rubber band and tracker.
[4271]1311 The appended() signal is emitted.
1312
1313 \param pos Additional point
1314
1315 \sa isActive(), begin(), end(), move(), appended()
1316*/
1317void QwtPicker::append( const QPoint &pos )
1318{
1319 if ( d_data->isActive )
1320 {
[9383]1321 d_data->pickedPoints += pos;
[4271]1322
1323 updateDisplay();
1324 Q_EMIT appended( pos );
1325 }
1326}
1327
1328/*!
1329 Move the last point of the selection
1330 The moved() signal is emitted.
1331
1332 \param pos New position
1333 \sa isActive(), begin(), end(), append()
1334*/
1335void QwtPicker::move( const QPoint &pos )
1336{
[9383]1337 if ( d_data->isActive && !d_data->pickedPoints.isEmpty() )
[4271]1338 {
[9383]1339 QPoint &point = d_data->pickedPoints.last();
1340 if ( point != pos )
[4271]1341 {
[9383]1342 point = pos;
[4271]1343
[9383]1344 updateDisplay();
1345 Q_EMIT moved( pos );
[4271]1346 }
1347 }
1348}
1349
1350/*!
1351 Remove the last point of the selection
1352 The removed() signal is emitted.
1353
1354 \sa isActive(), begin(), end(), append(), move()
1355*/
1356void QwtPicker::remove()
1357{
[9383]1358 if ( d_data->isActive && !d_data->pickedPoints.isEmpty() )
[4271]1359 {
[9383]1360#if QT_VERSION >= 0x050100
1361 const QPoint pos = d_data->pickedPoints.takeLast();
1362#else
1363 const QPoint pos = d_data->pickedPoints.last();
1364 d_data->pickedPoints.resize( d_data->pickedPoints.count() - 1 );
1365#endif
[4271]1366
[9383]1367 updateDisplay();
1368 Q_EMIT removed( pos );
[4271]1369 }
1370}
1371
1372/*!
[8127]1373 \brief Validate and fix up the selection
[4271]1374
1375 Accepts all selections unmodified
1376
[8127]1377 \param selection Selection to validate and fix up
[4271]1378 \return true, when accepted, false otherwise
1379*/
1380bool QwtPicker::accept( QPolygon &selection ) const
1381{
1382 Q_UNUSED( selection );
1383 return true;
1384}
1385
1386/*!
1387 A picker is active between begin() and end().
1388 \return true if the selection is active.
1389*/
1390bool QwtPicker::isActive() const
1391{
1392 return d_data->isActive;
1393}
1394
1395/*!
1396 Return the points, that have been collected so far. The selection()
1397 is calculated from the pickedPoints() in adjustedPoints().
1398 \return Picked points
1399*/
1400const QPolygon &QwtPicker::pickedPoints() const
1401{
1402 return d_data->pickedPoints;
1403}
1404
1405/*!
1406 Scale the selection by the ratios of oldSize and newSize
1407 The changed() signal is emitted.
1408
1409 \param oldSize Previous size
1410 \param newSize Current size
1411
1412 \sa ResizeMode, setResizeMode(), resizeMode()
1413*/
1414void QwtPicker::stretchSelection( const QSize &oldSize, const QSize &newSize )
1415{
1416 if ( oldSize.isEmpty() )
1417 {
1418 // avoid division by zero. But scaling for small sizes also
1419 // doesn't make much sense, because of rounding losses. TODO ...
1420 return;
1421 }
1422
[9383]1423 const double xRatio = double( newSize.width() ) / double( oldSize.width() );
1424 const double yRatio = double( newSize.height() ) / double( oldSize.height() );
[4271]1425
[9383]1426 for ( int i = 0; i < d_data->pickedPoints.count(); i++ )
[4271]1427 {
1428 QPoint &p = d_data->pickedPoints[i];
1429 p.setX( qRound( p.x() * xRatio ) );
1430 p.setY( qRound( p.y() * yRatio ) );
1431
1432 Q_EMIT changed( d_data->pickedPoints );
1433 }
1434}
1435
1436/*!
1437 Set mouse tracking for the observed widget.
1438
1439 In case of enable is true, the previous value
1440 is saved, that is restored when enable is false.
1441
1442 \warning Even when enable is false, mouse tracking might be restored
1443 to true. When mouseTracking for the observed widget
1444 has been changed directly by QWidget::setMouseTracking
1445 while mouse tracking has been set to true, this value can't
1446 be restored.
1447*/
1448
1449void QwtPicker::setMouseTracking( bool enable )
1450{
1451 QWidget *widget = parentWidget();
1452 if ( !widget )
1453 return;
1454
1455 if ( enable )
1456 {
1457 d_data->mouseTracking = widget->hasMouseTracking();
1458 widget->setMouseTracking( true );
1459 }
1460 else
1461 {
1462 widget->setMouseTracking( d_data->mouseTracking );
1463 }
1464}
1465
1466/*!
1467 Find the area of the observed widget, where selection might happen.
1468
[9383]1469 \return parentWidget()->contentsRect()
[4271]1470*/
[8127]1471QPainterPath QwtPicker::pickArea() const
[4271]1472{
[8127]1473 QPainterPath path;
1474
[4271]1475 const QWidget *widget = parentWidget();
1476 if ( widget )
[8127]1477 path.addRect( widget->contentsRect() );
[4271]1478
[8127]1479 return path;
[4271]1480}
1481
[8127]1482//! Update the state of rubber band and tracker label
[4271]1483void QwtPicker::updateDisplay()
1484{
1485 QWidget *w = parentWidget();
1486
1487 bool showRubberband = false;
1488 bool showTracker = false;
[8127]1489
[4271]1490 if ( w && w->isVisible() && d_data->enabled )
1491 {
1492 if ( rubberBand() != NoRubberBand && isActive() &&
1493 rubberBandPen().style() != Qt::NoPen )
1494 {
1495 showRubberband = true;
1496 }
1497
1498 if ( trackerMode() == AlwaysOn ||
1499 ( trackerMode() == ActiveOnly && isActive() ) )
1500 {
[9383]1501 if ( trackerPen() != Qt::NoPen
[8127]1502 && !trackerRect( QFont() ).isEmpty() )
1503 {
[4271]1504 showTracker = true;
[8127]1505 }
[4271]1506 }
1507 }
1508
[8127]1509 QPointer< QwtPickerRubberband > &rw = d_data->rubberBandOverlay;
[4271]1510 if ( showRubberband )
1511 {
1512 if ( rw.isNull() )
1513 {
[8127]1514 rw = new QwtPickerRubberband( this, NULL ); // NULL -> no extra event filter
1515 rw->setObjectName( "PickerRubberBand" );
1516 rw->setParent( w );
[4271]1517 rw->resize( w->size() );
1518 }
[8127]1519
1520 if ( d_data->rubberBand <= RectRubberBand )
1521 rw->setMaskMode( QwtWidgetOverlay::MaskHint );
1522 else
1523 rw->setMaskMode( QwtWidgetOverlay::AlphaMask );
1524
1525 rw->updateOverlay();
[4271]1526 }
1527 else
[8127]1528 {
1529 if ( d_data->openGL )
1530 {
1531 // Qt 4.8 crashes for a delete
1532 if ( !rw.isNull() )
1533 {
1534 rw->hide();
1535 rw->deleteLater();
1536 rw = NULL;
1537 }
1538 }
1539 else
1540 {
1541 delete rw;
1542 }
1543 }
[4271]1544
[8127]1545 QPointer< QwtPickerTracker > &tw = d_data->trackerOverlay;
[4271]1546 if ( showTracker )
1547 {
1548 if ( tw.isNull() )
1549 {
[8127]1550 tw = new QwtPickerTracker( this, NULL ); // NULL -> no extra event filter
1551 tw->setObjectName( "PickerTracker" );
1552 tw->setParent( w );
[4271]1553 tw->resize( w->size() );
1554 }
1555 tw->setFont( d_data->trackerFont );
[8127]1556 tw->updateOverlay();
[4271]1557 }
1558 else
[8127]1559 {
1560 if ( d_data->openGL )
1561 {
1562 // Qt 4.8 crashes for a delete
1563 if ( !tw.isNull() )
1564 {
1565 tw->hide();
1566 tw->deleteLater();
1567 tw = NULL;
1568 }
1569 }
1570 else
1571 {
1572 delete tw;
1573 }
1574 }
[4271]1575}
1576
[8127]1577//! \return Overlay displaying the rubber band
1578const QwtWidgetOverlay *QwtPicker::rubberBandOverlay() const
[4271]1579{
[8127]1580 return d_data->rubberBandOverlay;
[4271]1581}
1582
[8127]1583//! \return Overlay displaying the tracker text
1584const QwtWidgetOverlay *QwtPicker::trackerOverlay() const
[4271]1585{
[8127]1586 return d_data->trackerOverlay;
[4271]1587}
1588
Note: See TracBrowser for help on using the repository browser.