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

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