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

Last change on this file since 8450 was 8127, checked in by stoecker, 8 years ago

update qwt and qwtpolar, many QT5 fixes (unfinished)

File size: 39.1 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 {
[8127]55 region += QRect( l.x1() - pw2, l.y1(),
56 pw, l.y2() ).normalized();
57 }
58 else if ( l.y1() == l.y2() )
59 {
60 region += QRect( l.x1(), l.y1() - pw2,
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
80{
81public:
82 QwtPickerTracker( QwtPicker *, QWidget * );
83
84protected:
85 virtual void drawOverlay( QPainter * ) const;
86 virtual QRegion maskHint() const;
87
88 QwtPicker *d_picker;
89};
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 }
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 {
560 mask += qwtMaskRegion( QLine( pos.x(), pRect.top(),
561 pos.x(), pRect.bottom() ), pw );
562 break;
563 }
564 case HLineRubberBand:
565 {
566 mask += qwtMaskRegion( QLine( pRect.left(), pos.y(),
567 pRect.right(), pos.y() ), pw );
568 break;
569 }
570 case CrossRubberBand:
571 {
572 mask += qwtMaskRegion( QLine( pos.x(), pRect.top(),
573 pos.x(), pRect.bottom() ), pw );
574 mask += qwtMaskRegion( QLine( pRect.left(), pos.y(),
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
754 - A rectangular selection might need to have a specific aspect ratio only.\n
755 - A selection could accept non intersecting polygons only.\n
756 - ...\n
757
758 The example below is for a rectangular selection, where the first
759 point is the center of the selected rectangle.
760 \par Example
761 \verbatim QPolygon MyPicker::adjustedPoints(const QPolygon &points) const
762{
763 QPolygon adjusted;
764 if ( points.size() == 2 )
765 {
766 const int width = qAbs(points[1].x() - points[0].x());
767 const int height = qAbs(points[1].y() - points[0].y());
768
769 QRect rect(0, 0, 2 * width, 2 * height);
770 rect.moveCenter(points[0]);
771
772 adjusted += rect.topLeft();
773 adjusted += rect.bottomRight();
774 }
775 return adjusted;
776}\endverbatim\n
[8127]777
778 \param points Selected points
779 \return Selected points unmodified
[4271]780*/
781QPolygon QwtPicker::adjustedPoints( const QPolygon &points ) const
782{
783 return points;
784}
785
786/*!
787 \return Selected points
788 \sa pickedPoints(), adjustedPoints()
789*/
790QPolygon QwtPicker::selection() const
791{
792 return adjustedPoints( d_data->pickedPoints );
793}
794
795//! \return Current position of the tracker
796QPoint QwtPicker::trackerPosition() const
797{
798 return d_data->trackerPosition;
799}
800
801/*!
802 Calculate the bounding rectangle for the tracker text
803 from the current position of the tracker
804
805 \param font Font of the tracker text
806 \return Bounding rectangle of the tracker text
807
808 \sa trackerPosition()
809*/
810QRect QwtPicker::trackerRect( const QFont &font ) const
811{
812 if ( trackerMode() == AlwaysOff ||
813 ( trackerMode() == ActiveOnly && !isActive() ) )
814 {
815 return QRect();
816 }
817
818 if ( d_data->trackerPosition.x() < 0 || d_data->trackerPosition.y() < 0 )
819 return QRect();
820
821 QwtText text = trackerText( d_data->trackerPosition );
822 if ( text.isEmpty() )
823 return QRect();
824
825 const QSizeF textSize = text.textSize( font );
826 QRect textRect( 0, 0, qCeil( textSize.width() ), qCeil( textSize.height() ) );
827
828 const QPoint &pos = d_data->trackerPosition;
829
830 int alignment = 0;
831 if ( isActive() && d_data->pickedPoints.count() > 1
832 && rubberBand() != NoRubberBand )
833 {
834 const QPoint last =
835 d_data->pickedPoints[int( d_data->pickedPoints.count() ) - 2];
836
837 alignment |= ( pos.x() >= last.x() ) ? Qt::AlignRight : Qt::AlignLeft;
838 alignment |= ( pos.y() > last.y() ) ? Qt::AlignBottom : Qt::AlignTop;
839 }
840 else
841 alignment = Qt::AlignTop | Qt::AlignRight;
842
843 const int margin = 5;
844
845 int x = pos.x();
846 if ( alignment & Qt::AlignLeft )
847 x -= textRect.width() + margin;
848 else if ( alignment & Qt::AlignRight )
849 x += margin;
850
851 int y = pos.y();
852 if ( alignment & Qt::AlignBottom )
853 y += margin;
854 else if ( alignment & Qt::AlignTop )
855 y -= textRect.height() + margin;
856
857 textRect.moveTopLeft( QPoint( x, y ) );
858
[8127]859 const QRect pickRect = pickArea().boundingRect().toRect();
860
861 int right = qMin( textRect.right(), pickRect.right() - margin );
862 int bottom = qMin( textRect.bottom(), pickRect.bottom() - margin );
[4271]863 textRect.moveBottomRight( QPoint( right, bottom ) );
864
[8127]865 int left = qMax( textRect.left(), pickRect.left() + margin );
866 int top = qMax( textRect.top(), pickRect.top() + margin );
[4271]867 textRect.moveTopLeft( QPoint( left, top ) );
868
869 return textRect;
870}
871
872/*!
873 \brief Event filter
874
[8127]875 When isEnabled() is true all events of the observed widget are filtered.
[4271]876 Mouse and keyboard events are translated into widgetMouse- and widgetKey-
877 and widgetWheel-events. Paint and Resize events are handled to keep
[8127]878 rubber band and tracker up to date.
[4271]879
880 \param object Object to be filtered
881 \param event Event
882
[8127]883 \return Always false.
884
[4271]885 \sa widgetEnterEvent(), widgetLeaveEvent(),
886 widgetMousePressEvent(), widgetMouseReleaseEvent(),
887 widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(),
888 widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent(),
889 QObject::installEventFilter(), QObject::event()
890*/
891bool QwtPicker::eventFilter( QObject *object, QEvent *event )
892{
893 if ( object && object == parentWidget() )
894 {
895 switch ( event->type() )
896 {
897 case QEvent::Resize:
898 {
[8127]899 const QResizeEvent *re = static_cast<QResizeEvent *>( event );
900
901 /*
902 Adding/deleting additional event filters inside of an event filter
903 is not safe dues to the implementation in Qt ( changing alist while iterating ).
904 So we create the overlays in a way, that they don't install en event filter
905 ( parent set to NULL ) and do the resizing here.
906 */
907 if ( d_data->trackerOverlay )
908 d_data->trackerOverlay->resize( re->size() );
909
910 if ( d_data->rubberBandOverlay )
911 d_data->rubberBandOverlay->resize( re->size() );
912
[4271]913 if ( d_data->resizeMode == Stretch )
914 stretchSelection( re->oldSize(), re->size() );
915
[8127]916 updateDisplay();
[4271]917 break;
918 }
919 case QEvent::Enter:
[8127]920 {
[4271]921 widgetEnterEvent( event );
922 break;
[8127]923 }
[4271]924 case QEvent::Leave:
[8127]925 {
[4271]926 widgetLeaveEvent( event );
927 break;
[8127]928 }
[4271]929 case QEvent::MouseButtonPress:
[8127]930 {
931 widgetMousePressEvent( static_cast<QMouseEvent *>( event ) );
[4271]932 break;
[8127]933 }
[4271]934 case QEvent::MouseButtonRelease:
[8127]935 {
936 widgetMouseReleaseEvent( static_cast<QMouseEvent *>( event ) );
[4271]937 break;
[8127]938 }
[4271]939 case QEvent::MouseButtonDblClick:
[8127]940 {
941 widgetMouseDoubleClickEvent( static_cast<QMouseEvent *>( event ) );
[4271]942 break;
[8127]943 }
[4271]944 case QEvent::MouseMove:
[8127]945 {
946 widgetMouseMoveEvent( static_cast<QMouseEvent *>( event ) );
[4271]947 break;
[8127]948 }
[4271]949 case QEvent::KeyPress:
[8127]950 {
951 widgetKeyPressEvent( static_cast<QKeyEvent *>( event ) );
[4271]952 break;
[8127]953 }
[4271]954 case QEvent::KeyRelease:
[8127]955 {
956 widgetKeyReleaseEvent( static_cast<QKeyEvent *>( event ) );
[4271]957 break;
[8127]958 }
[4271]959 case QEvent::Wheel:
[8127]960 {
961 widgetWheelEvent( static_cast<QWheelEvent *>( event ) );
[4271]962 break;
[8127]963 }
[4271]964 default:
965 break;
966 }
967 }
968 return false;
969}
970
971/*!
972 Handle a mouse press event for the observed widget.
973
974 \param mouseEvent Mouse event
975
976 \sa eventFilter(), widgetMouseReleaseEvent(),
977 widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(),
978 widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent()
979*/
980void QwtPicker::widgetMousePressEvent( QMouseEvent *mouseEvent )
981{
982 transition( mouseEvent );
983}
984
985/*!
986 Handle a mouse move event for the observed widget.
987
988 \param mouseEvent Mouse event
989
990 \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(),
991 widgetMouseDoubleClickEvent(),
992 widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent()
993*/
994void QwtPicker::widgetMouseMoveEvent( QMouseEvent *mouseEvent )
995{
[8127]996 if ( pickArea().contains( mouseEvent->pos() ) )
[4271]997 d_data->trackerPosition = mouseEvent->pos();
998 else
999 d_data->trackerPosition = QPoint( -1, -1 );
1000
1001 if ( !isActive() )
1002 updateDisplay();
1003
1004 transition( mouseEvent );
1005}
1006
1007/*!
1008 Handle a enter event for the observed widget.
1009
1010 \param event Qt event
1011
1012 \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(),
1013 widgetMouseDoubleClickEvent(),
1014 widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent()
1015*/
1016void QwtPicker::widgetEnterEvent( QEvent *event )
1017{
1018 transition( event );
1019}
1020
1021/*!
1022 Handle a leave event for the observed widget.
1023
1024 \param event Qt event
1025
1026 \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(),
1027 widgetMouseDoubleClickEvent(),
1028 widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent()
1029*/
1030void QwtPicker::widgetLeaveEvent( QEvent *event )
1031{
1032 transition( event );
1033
1034 d_data->trackerPosition = QPoint( -1, -1 );
1035 if ( !isActive() )
1036 updateDisplay();
1037}
1038
1039/*!
[8127]1040 Handle a mouse release event for the observed widget.
[4271]1041
1042 \param mouseEvent Mouse event
1043
1044 \sa eventFilter(), widgetMousePressEvent(),
1045 widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(),
1046 widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent()
1047*/
1048void QwtPicker::widgetMouseReleaseEvent( QMouseEvent *mouseEvent )
1049{
1050 transition( mouseEvent );
1051}
1052
1053/*!
1054 Handle mouse double click event for the observed widget.
1055
1056 \param mouseEvent Mouse event
1057
1058 \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(),
1059 widgetMouseMoveEvent(),
1060 widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent()
1061*/
1062void QwtPicker::widgetMouseDoubleClickEvent( QMouseEvent *mouseEvent )
1063{
1064 transition( mouseEvent );
1065}
1066
1067
1068/*!
1069 Handle a wheel event for the observed widget.
1070
1071 Move the last point of the selection in case of isActive() == true
1072
1073 \param wheelEvent Wheel event
1074
1075 \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(),
1076 widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(),
1077 widgetKeyPressEvent(), widgetKeyReleaseEvent()
1078*/
1079void QwtPicker::widgetWheelEvent( QWheelEvent *wheelEvent )
1080{
[8127]1081 if ( pickArea().contains( wheelEvent->pos() ) )
[4271]1082 d_data->trackerPosition = wheelEvent->pos();
1083 else
1084 d_data->trackerPosition = QPoint( -1, -1 );
1085
1086 updateDisplay();
1087
1088 transition( wheelEvent );
1089}
1090
1091/*!
1092 Handle a key press event for the observed widget.
1093
1094 Selections can be completely done by the keyboard. The arrow keys
1095 move the cursor, the abort key aborts a selection. All other keys
1096 are handled by the current state machine.
1097
1098 \param keyEvent Key event
1099
1100 \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(),
1101 widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(),
1102 widgetWheelEvent(), widgetKeyReleaseEvent(), stateMachine(),
1103 QwtEventPattern::KeyPatternCode
1104*/
1105void QwtPicker::widgetKeyPressEvent( QKeyEvent *keyEvent )
1106{
1107 int dx = 0;
1108 int dy = 0;
1109
1110 int offset = 1;
1111 if ( keyEvent->isAutoRepeat() )
1112 offset = 5;
1113
1114 if ( keyMatch( KeyLeft, keyEvent ) )
1115 dx = -offset;
1116 else if ( keyMatch( KeyRight, keyEvent ) )
1117 dx = offset;
1118 else if ( keyMatch( KeyUp, keyEvent ) )
1119 dy = -offset;
1120 else if ( keyMatch( KeyDown, keyEvent ) )
1121 dy = offset;
1122 else if ( keyMatch( KeyAbort, keyEvent ) )
1123 {
1124 reset();
1125 }
1126 else
1127 transition( keyEvent );
1128
1129 if ( dx != 0 || dy != 0 )
1130 {
[8127]1131 const QRect rect = pickArea().boundingRect().toRect();
[4271]1132 const QPoint pos = parentWidget()->mapFromGlobal( QCursor::pos() );
1133
1134 int x = pos.x() + dx;
1135 x = qMax( rect.left(), x );
1136 x = qMin( rect.right(), x );
1137
1138 int y = pos.y() + dy;
1139 y = qMax( rect.top(), y );
1140 y = qMin( rect.bottom(), y );
1141
1142 QCursor::setPos( parentWidget()->mapToGlobal( QPoint( x, y ) ) );
1143 }
1144}
1145
1146/*!
1147 Handle a key release event for the observed widget.
1148
1149 Passes the event to the state machine.
1150
1151 \param keyEvent Key event
1152
1153 \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(),
1154 widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(),
1155 widgetWheelEvent(), widgetKeyPressEvent(), stateMachine()
1156*/
1157void QwtPicker::widgetKeyReleaseEvent( QKeyEvent *keyEvent )
1158{
1159 transition( keyEvent );
1160}
1161
1162/*!
1163 Passes an event to the state machine and executes the resulting
1164 commands. Append and Move commands use the current position
[8127]1165 of the cursor ( QCursor::pos() ).
[4271]1166
1167 \param event Event
1168*/
1169void QwtPicker::transition( const QEvent *event )
1170{
1171 if ( !d_data->stateMachine )
1172 return;
1173
1174 const QList<QwtPickerMachine::Command> commandList =
1175 d_data->stateMachine->transition( *this, event );
1176
1177 QPoint pos;
1178 switch ( event->type() )
1179 {
1180 case QEvent::MouseButtonDblClick:
1181 case QEvent::MouseButtonPress:
1182 case QEvent::MouseButtonRelease:
1183 case QEvent::MouseMove:
1184 {
1185 const QMouseEvent *me =
1186 static_cast< const QMouseEvent * >( event );
1187 pos = me->pos();
1188 break;
1189 }
1190 default:
1191 pos = parentWidget()->mapFromGlobal( QCursor::pos() );
1192 }
1193
1194 for ( int i = 0; i < commandList.count(); i++ )
1195 {
1196 switch ( commandList[i] )
1197 {
1198 case QwtPickerMachine::Begin:
1199 {
1200 begin();
1201 break;
1202 }
1203 case QwtPickerMachine::Append:
1204 {
1205 append( pos );
1206 break;
1207 }
1208 case QwtPickerMachine::Move:
1209 {
1210 move( pos );
1211 break;
1212 }
1213 case QwtPickerMachine::Remove:
1214 {
1215 remove();
1216 break;
1217 }
1218 case QwtPickerMachine::End:
1219 {
1220 end();
1221 break;
1222 }
1223 }
1224 }
1225}
1226
1227/*!
1228 Open a selection setting the state to active
1229
1230 \sa isActive(), end(), append(), move()
1231*/
1232void QwtPicker::begin()
1233{
1234 if ( d_data->isActive )
1235 return;
1236
1237 d_data->pickedPoints.resize( 0 );
1238 d_data->isActive = true;
1239 Q_EMIT activated( true );
1240
1241 if ( trackerMode() != AlwaysOff )
1242 {
1243 if ( d_data->trackerPosition.x() < 0 || d_data->trackerPosition.y() < 0 )
1244 {
1245 QWidget *w = parentWidget();
1246 if ( w )
1247 d_data->trackerPosition = w->mapFromGlobal( QCursor::pos() );
1248 }
1249 }
1250
1251 updateDisplay();
1252 setMouseTracking( true );
1253}
1254
1255/*!
1256 \brief Close a selection setting the state to inactive.
1257
1258 The selection is validated and maybe fixed by accept().
1259
1260 \param ok If true, complete the selection and emit a selected signal
1261 otherwise discard the selection.
1262 \return true if the selection is accepted, false otherwise
1263 \sa isActive(), begin(), append(), move(), selected(), accept()
1264*/
1265bool QwtPicker::end( bool ok )
1266{
1267 if ( d_data->isActive )
1268 {
1269 setMouseTracking( false );
1270
1271 d_data->isActive = false;
1272 Q_EMIT activated( false );
1273
1274 if ( trackerMode() == ActiveOnly )
1275 d_data->trackerPosition = QPoint( -1, -1 );
1276
1277 if ( ok )
1278 ok = accept( d_data->pickedPoints );
1279
1280 if ( ok )
1281 Q_EMIT selected( d_data->pickedPoints );
1282 else
1283 d_data->pickedPoints.resize( 0 );
1284
1285 updateDisplay();
1286 }
1287 else
1288 ok = false;
1289
1290 return ok;
1291}
1292
1293/*!
[8127]1294 Reset the state machine and terminate ( end(false) ) the selection
[4271]1295*/
1296void QwtPicker::reset()
1297{
1298 if ( d_data->stateMachine )
1299 d_data->stateMachine->reset();
1300
1301 if ( isActive() )
1302 end( false );
1303}
1304
1305/*!
[8127]1306 Append a point to the selection and update rubber band and tracker.
[4271]1307 The appended() signal is emitted.
1308
1309 \param pos Additional point
1310
1311 \sa isActive(), begin(), end(), move(), appended()
1312*/
1313void QwtPicker::append( const QPoint &pos )
1314{
1315 if ( d_data->isActive )
1316 {
1317 const int idx = d_data->pickedPoints.count();
1318 d_data->pickedPoints.resize( idx + 1 );
1319 d_data->pickedPoints[idx] = pos;
1320
1321 updateDisplay();
1322 Q_EMIT appended( pos );
1323 }
1324}
1325
1326/*!
1327 Move the last point of the selection
1328 The moved() signal is emitted.
1329
1330 \param pos New position
1331 \sa isActive(), begin(), end(), append()
1332*/
1333void QwtPicker::move( const QPoint &pos )
1334{
1335 if ( d_data->isActive )
1336 {
1337 const int idx = d_data->pickedPoints.count() - 1;
1338 if ( idx >= 0 )
1339 {
1340 if ( d_data->pickedPoints[idx] != pos )
1341 {
1342 d_data->pickedPoints[idx] = pos;
1343
1344 updateDisplay();
1345 Q_EMIT moved( pos );
1346 }
1347 }
1348 }
1349}
1350
1351/*!
1352 Remove the last point of the selection
1353 The removed() signal is emitted.
1354
1355 \sa isActive(), begin(), end(), append(), move()
1356*/
1357void QwtPicker::remove()
1358{
1359 if ( d_data->isActive )
1360 {
1361 const int idx = d_data->pickedPoints.count() - 1;
1362 if ( idx > 0 )
1363 {
1364 const int idx = d_data->pickedPoints.count();
1365
1366 const QPoint pos = d_data->pickedPoints[idx - 1];
1367 d_data->pickedPoints.resize( idx - 1 );
1368
1369 updateDisplay();
1370 Q_EMIT removed( pos );
1371 }
1372 }
1373}
1374
1375/*!
[8127]1376 \brief Validate and fix up the selection
[4271]1377
1378 Accepts all selections unmodified
1379
[8127]1380 \param selection Selection to validate and fix up
[4271]1381 \return true, when accepted, false otherwise
1382*/
1383bool QwtPicker::accept( QPolygon &selection ) const
1384{
1385 Q_UNUSED( selection );
1386 return true;
1387}
1388
1389/*!
1390 A picker is active between begin() and end().
1391 \return true if the selection is active.
1392*/
1393bool QwtPicker::isActive() const
1394{
1395 return d_data->isActive;
1396}
1397
1398/*!
1399 Return the points, that have been collected so far. The selection()
1400 is calculated from the pickedPoints() in adjustedPoints().
1401 \return Picked points
1402*/
1403const QPolygon &QwtPicker::pickedPoints() const
1404{
1405 return d_data->pickedPoints;
1406}
1407
1408/*!
1409 Scale the selection by the ratios of oldSize and newSize
1410 The changed() signal is emitted.
1411
1412 \param oldSize Previous size
1413 \param newSize Current size
1414
1415 \sa ResizeMode, setResizeMode(), resizeMode()
1416*/
1417void QwtPicker::stretchSelection( const QSize &oldSize, const QSize &newSize )
1418{
1419 if ( oldSize.isEmpty() )
1420 {
1421 // avoid division by zero. But scaling for small sizes also
1422 // doesn't make much sense, because of rounding losses. TODO ...
1423 return;
1424 }
1425
1426 const double xRatio =
1427 double( newSize.width() ) / double( oldSize.width() );
1428 const double yRatio =
1429 double( newSize.height() ) / double( oldSize.height() );
1430
1431 for ( int i = 0; i < int( d_data->pickedPoints.count() ); i++ )
1432 {
1433 QPoint &p = d_data->pickedPoints[i];
1434 p.setX( qRound( p.x() * xRatio ) );
1435 p.setY( qRound( p.y() * yRatio ) );
1436
1437 Q_EMIT changed( d_data->pickedPoints );
1438 }
1439}
1440
1441/*!
1442 Set mouse tracking for the observed widget.
1443
1444 In case of enable is true, the previous value
1445 is saved, that is restored when enable is false.
1446
1447 \warning Even when enable is false, mouse tracking might be restored
1448 to true. When mouseTracking for the observed widget
1449 has been changed directly by QWidget::setMouseTracking
1450 while mouse tracking has been set to true, this value can't
1451 be restored.
1452*/
1453
1454void QwtPicker::setMouseTracking( bool enable )
1455{
1456 QWidget *widget = parentWidget();
1457 if ( !widget )
1458 return;
1459
1460 if ( enable )
1461 {
1462 d_data->mouseTracking = widget->hasMouseTracking();
1463 widget->setMouseTracking( true );
1464 }
1465 else
1466 {
1467 widget->setMouseTracking( d_data->mouseTracking );
1468 }
1469}
1470
1471/*!
1472 Find the area of the observed widget, where selection might happen.
1473
1474 \return parentWidget()->contentsRect()
1475*/
[8127]1476QPainterPath QwtPicker::pickArea() const
[4271]1477{
[8127]1478 QPainterPath path;
1479
[4271]1480 const QWidget *widget = parentWidget();
1481 if ( widget )
[8127]1482 path.addRect( widget->contentsRect() );
[4271]1483
[8127]1484 return path;
[4271]1485}
1486
[8127]1487//! Update the state of rubber band and tracker label
[4271]1488void QwtPicker::updateDisplay()
1489{
1490 QWidget *w = parentWidget();
1491
1492 bool showRubberband = false;
1493 bool showTracker = false;
[8127]1494
[4271]1495 if ( w && w->isVisible() && d_data->enabled )
1496 {
1497 if ( rubberBand() != NoRubberBand && isActive() &&
1498 rubberBandPen().style() != Qt::NoPen )
1499 {
1500 showRubberband = true;
1501 }
1502
1503 if ( trackerMode() == AlwaysOn ||
1504 ( trackerMode() == ActiveOnly && isActive() ) )
1505 {
[8127]1506 if ( trackerPen() != Qt::NoPen
1507 && !trackerRect( QFont() ).isEmpty() )
1508 {
[4271]1509 showTracker = true;
[8127]1510 }
[4271]1511 }
1512 }
1513
[8127]1514 QPointer< QwtPickerRubberband > &rw = d_data->rubberBandOverlay;
[4271]1515 if ( showRubberband )
1516 {
1517 if ( rw.isNull() )
1518 {
[8127]1519 rw = new QwtPickerRubberband( this, NULL ); // NULL -> no extra event filter
1520 rw->setObjectName( "PickerRubberBand" );
1521 rw->setParent( w );
[4271]1522 rw->resize( w->size() );
1523 }
[8127]1524
1525 if ( d_data->rubberBand <= RectRubberBand )
1526 rw->setMaskMode( QwtWidgetOverlay::MaskHint );
1527 else
1528 rw->setMaskMode( QwtWidgetOverlay::AlphaMask );
1529
1530 rw->updateOverlay();
[4271]1531 }
1532 else
[8127]1533 {
1534 if ( d_data->openGL )
1535 {
1536 // Qt 4.8 crashes for a delete
1537 if ( !rw.isNull() )
1538 {
1539 rw->hide();
1540 rw->deleteLater();
1541 rw = NULL;
1542 }
1543 }
1544 else
1545 {
1546 delete rw;
1547 }
1548 }
[4271]1549
[8127]1550 QPointer< QwtPickerTracker > &tw = d_data->trackerOverlay;
[4271]1551 if ( showTracker )
1552 {
1553 if ( tw.isNull() )
1554 {
[8127]1555 tw = new QwtPickerTracker( this, NULL ); // NULL -> no extra event filter
1556 tw->setObjectName( "PickerTracker" );
1557 tw->setParent( w );
[4271]1558 tw->resize( w->size() );
1559 }
1560 tw->setFont( d_data->trackerFont );
[8127]1561 tw->updateOverlay();
[4271]1562 }
1563 else
[8127]1564 {
1565 if ( d_data->openGL )
1566 {
1567 // Qt 4.8 crashes for a delete
1568 if ( !tw.isNull() )
1569 {
1570 tw->hide();
1571 tw->deleteLater();
1572 tw = NULL;
1573 }
1574 }
1575 else
1576 {
1577 delete tw;
1578 }
1579 }
[4271]1580}
1581
[8127]1582//! \return Overlay displaying the rubber band
1583const QwtWidgetOverlay *QwtPicker::rubberBandOverlay() const
[4271]1584{
[8127]1585 return d_data->rubberBandOverlay;
[4271]1586}
1587
[8127]1588//! \return Overlay displaying the tracker text
1589const QwtWidgetOverlay *QwtPicker::trackerOverlay() const
[4271]1590{
[8127]1591 return d_data->trackerOverlay;
[4271]1592}
1593
Note: See TracBrowser for help on using the repository browser.