Skip to content
qwt_picker.cpp 38.3 KiB
Newer Older
pixhawk's avatar
pixhawk committed
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
 * Qwt Widget Library
 * Copyright (C) 1997   Josef Wilgen
 * Copyright (C) 2002   Uwe Rathmann
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the Qwt License, Version 1.0
 *****************************************************************************/

Bryant's avatar
Bryant committed
#include "qwt_picker.h"
#include "qwt_picker_machine.h"
#include "qwt_painter.h"
#include "qwt_math.h"
#include "qwt_widget_overlay.h"
pixhawk's avatar
pixhawk committed
#include <qapplication.h>
#include <qevent.h>
#include <qpainter.h>
#include <qframe.h>
#include <qcursor.h>
#include <qbitmap.h>
#include <qpointer.h>
#include <qpaintengine.h>
Bryant's avatar
Bryant committed
#include <qmath.h>
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
static inline QRegion qwtMaskRegion( const QRect &r, int penWidth )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    const int pw = qMax( penWidth, 1 );
    const int pw2 = penWidth / 2;

    int x1 = r.left() - pw2;
    int x2 = r.right() + 1 + pw2 + ( pw % 2 );

    int y1 = r.top() - pw2;
    int y2 = r.bottom() + 1 + pw2 + ( pw % 2 );

    QRegion region;

    region += QRect( x1, y1, x2 - x1, pw );
    region += QRect( x1, y1, pw, y2 - y1 );
    region += QRect( x1, y2 - pw, x2 - x1, pw );
    region += QRect( x2 - pw, y1, pw, y2 - y1 );

    return region;
}

static inline QRegion qwtMaskRegion( const QLine &l, int penWidth )
{
    const int pw = qMax( penWidth, 1 );
    const int pw2 = penWidth / 2;

    QRegion region;

    if ( l.x1() == l.x2() )
    {
        region += QRect( l.x1() - pw2, l.y1(), 
            pw, l.y2() ).normalized();
    }
    else if ( l.y1() == l.y2() )
    {
        region += QRect( l.x1(), l.y1() - pw2, 
            l.x2(), pw ).normalized();
    }
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    return region;
}
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
class QwtPickerRubberband: public QwtWidgetOverlay
{
public:
    QwtPickerRubberband( QwtPicker *, QWidget * );
pixhawk's avatar
pixhawk committed

protected:
Bryant's avatar
Bryant committed
    virtual void drawOverlay( QPainter * ) const;
    virtual QRegion maskHint() const;
pixhawk's avatar
pixhawk committed

    QwtPicker *d_picker;
};

Bryant's avatar
Bryant committed
class QwtPickerTracker: public QwtWidgetOverlay
{                                  
public:
    QwtPickerTracker( QwtPicker *, QWidget * );
    
protected:
    virtual void drawOverlay( QPainter * ) const;
    virtual QRegion maskHint() const;
    
    QwtPicker *d_picker;
};  


pixhawk's avatar
pixhawk committed
class QwtPicker::PrivateData
{
public:
Bryant's avatar
Bryant committed
    PrivateData():
        enabled( false ),
        stateMachine( NULL ),
        resizeMode( QwtPicker::Stretch ),
        rubberBand( QwtPicker::NoRubberBand ),
        trackerMode( QwtPicker::AlwaysOff ),
        isActive( false ),
        trackerPosition( -1, -1 ),
        mouseTracking( false ),
        openGL( false )
    {
    }
        
pixhawk's avatar
pixhawk committed
    bool enabled;

    QwtPickerMachine *stateMachine;

    QwtPicker::ResizeMode resizeMode;

    QwtPicker::RubberBand rubberBand;
    QPen rubberBandPen;

    QwtPicker::DisplayMode trackerMode;
    QPen trackerPen;
    QFont trackerFont;

Bryant's avatar
Bryant committed
    QPolygon pickedPoints;
pixhawk's avatar
pixhawk committed
    bool isActive;
    QPoint trackerPosition;

    bool mouseTracking; // used to save previous value

Bryant's avatar
Bryant committed
    QPointer< QwtPickerRubberband > rubberBandOverlay;
    QPointer< QwtPickerTracker> trackerOverlay;

    bool openGL;
pixhawk's avatar
pixhawk committed
};

Bryant's avatar
Bryant committed
QwtPickerRubberband::QwtPickerRubberband(
        QwtPicker *picker, QWidget *parent ):
    QwtWidgetOverlay( parent ),
    d_picker( picker )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    setMaskMode( QwtWidgetOverlay::MaskHint );
pixhawk's avatar
pixhawk committed
}

Bryant's avatar
Bryant committed
QRegion QwtPickerRubberband::maskHint() const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    return d_picker->rubberBandMask();
}
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
void QwtPickerRubberband::drawOverlay( QPainter *painter ) const
{
    painter->setPen( d_picker->rubberBandPen() );
    d_picker->drawRubberBand( painter );
pixhawk's avatar
pixhawk committed
}

Bryant's avatar
Bryant committed
QwtPickerTracker::QwtPickerTracker(
        QwtPicker *picker, QWidget *parent ):
    QwtWidgetOverlay( parent ),
    d_picker( picker )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    setMaskMode( QwtWidgetOverlay::MaskHint );
}
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
QRegion QwtPickerTracker::maskHint() const
{
    return d_picker->trackerRect( font() );
}
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
void QwtPickerTracker::drawOverlay( QPainter *painter ) const
{
    painter->setPen( d_picker->trackerPen() );
    d_picker->drawTracker( painter );
pixhawk's avatar
pixhawk committed
}

/*!
  Constructor

Bryant's avatar
Bryant committed
  Creates an picker that is enabled, but without a state machine.
  rubber band and tracker are disabled.
pixhawk's avatar
pixhawk committed
  \param parent Parent widget, that will be observed
 */

Bryant's avatar
Bryant committed
QwtPicker::QwtPicker( QWidget *parent ):
    QObject( parent )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    init( parent, NoRubberBand, AlwaysOff );
pixhawk's avatar
pixhawk committed
}

/*!
  Constructor

Bryant's avatar
Bryant committed
  \param rubberBand Rubber band style
pixhawk's avatar
pixhawk committed
  \param trackerMode Tracker mode
  \param parent Parent widget, that will be observed
 */
Bryant's avatar
Bryant committed
QwtPicker::QwtPicker( RubberBand rubberBand,
        DisplayMode trackerMode, QWidget *parent ):
    QObject( parent )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    init( parent, rubberBand, trackerMode );
pixhawk's avatar
pixhawk committed
}

//! Destructor
QwtPicker::~QwtPicker()
{
Bryant's avatar
Bryant committed
    setMouseTracking( false );

pixhawk's avatar
pixhawk committed
    delete d_data->stateMachine;
Bryant's avatar
Bryant committed
    delete d_data->rubberBandOverlay;
    delete d_data->trackerOverlay;

pixhawk's avatar
pixhawk committed
    delete d_data;
}

Bryant's avatar
Bryant committed
//! Initialize the picker - used by the constructors
void QwtPicker::init( QWidget *parent,
    RubberBand rubberBand, DisplayMode trackerMode )
pixhawk's avatar
pixhawk committed
{
    d_data = new PrivateData;

    d_data->rubberBand = rubberBand;
Bryant's avatar
Bryant committed

    if ( parent )
    {
pixhawk's avatar
pixhawk committed
        if ( parent->focusPolicy() == Qt::NoFocus )
Bryant's avatar
Bryant committed
            parent->setFocusPolicy( Qt::WheelFocus );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        d_data->openGL = parent->inherits( "QGLWidget" );
pixhawk's avatar
pixhawk committed
        d_data->trackerFont = parent->font();
        d_data->mouseTracking = parent->hasMouseTracking();
Bryant's avatar
Bryant committed

        setEnabled( true );
pixhawk's avatar
pixhawk committed
    }
Bryant's avatar
Bryant committed

    setTrackerMode( trackerMode );
pixhawk's avatar
pixhawk committed
}

/*!
Bryant's avatar
Bryant committed
  Set a state machine and delete the previous one

  \param stateMachine State machine
  \sa stateMachine()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
void QwtPicker::setStateMachine( QwtPickerMachine *stateMachine )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( d_data->stateMachine != stateMachine )
    {
pixhawk's avatar
pixhawk committed
        reset();

        delete d_data->stateMachine;
        d_data->stateMachine = stateMachine;

        if ( d_data->stateMachine )
            d_data->stateMachine->reset();
    }
}

/*!
Bryant's avatar
Bryant committed
  \return Assigned state machine
  \sa setStateMachine()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
QwtPickerMachine *QwtPicker::stateMachine()
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    return d_data->stateMachine;
}

/*!
  \return Assigned state machine
  \sa setStateMachine()
*/
const QwtPickerMachine *QwtPicker::stateMachine() const
{
    return d_data->stateMachine;
pixhawk's avatar
pixhawk committed
}

//! Return the parent widget, where the selection happens
QWidget *QwtPicker::parentWidget()
{
    QObject *obj = parent();
    if ( obj && obj->isWidgetType() )
Bryant's avatar
Bryant committed
        return static_cast<QWidget *>( obj );
pixhawk's avatar
pixhawk committed

    return NULL;
}

//! Return the parent widget, where the selection happens
const QWidget *QwtPicker::parentWidget() const
{
    QObject *obj = parent();
    if ( obj && obj->isWidgetType() )
Bryant's avatar
Bryant committed
        return static_cast< const QWidget *>( obj );
pixhawk's avatar
pixhawk committed

    return NULL;
}

/*!
Bryant's avatar
Bryant committed
  Set the rubber band style
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  \param rubberBand Rubber band style
pixhawk's avatar
pixhawk committed
         The default value is NoRubberBand.

  \sa rubberBand(), RubberBand, setRubberBandPen()
*/
Bryant's avatar
Bryant committed
void QwtPicker::setRubberBand( RubberBand rubberBand )
pixhawk's avatar
pixhawk committed
{
    d_data->rubberBand = rubberBand;
}

/*!
Bryant's avatar
Bryant committed
  \return Rubber band style
pixhawk's avatar
pixhawk committed
  \sa setRubberBand(), RubberBand, rubberBandPen()
*/
QwtPicker::RubberBand QwtPicker::rubberBand() const
{
    return d_data->rubberBand;
}

/*!
  \brief Set the display mode of the tracker.

  A tracker displays information about current position of
  the cursor as a string. The display mode controls
  if the tracker has to be displayed whenever the observed
  widget has focus and cursor (AlwaysOn), never (AlwaysOff), or
  only when the selection is active (ActiveOnly).
pixhawk's avatar
pixhawk committed
  \param mode Tracker display mode

  \warning In case of AlwaysOn, mouseTracking will be enabled
           for the observed widget.
  \sa trackerMode(), DisplayMode
*/

Bryant's avatar
Bryant committed
void QwtPicker::setTrackerMode( DisplayMode mode )
Bryant's avatar
Bryant committed
    if ( d_data->trackerMode != mode )
    {
pixhawk's avatar
pixhawk committed
        d_data->trackerMode = mode;
Bryant's avatar
Bryant committed
        setMouseTracking( d_data->trackerMode == AlwaysOn );
pixhawk's avatar
pixhawk committed
    }
pixhawk's avatar
pixhawk committed

/*!
  \return Tracker display mode
  \sa setTrackerMode(), DisplayMode
*/
QwtPicker::DisplayMode QwtPicker::trackerMode() const
pixhawk's avatar
pixhawk committed
    return d_data->trackerMode;
pixhawk's avatar
pixhawk committed

/*!
  \brief Set the resize mode.

  The resize mode controls what to do with the selected points of an active
  selection when the observed widget is resized.

  Stretch means the points are scaled according to the new
  size, KeepSize means the points remain unchanged.

  The default mode is Stretch.

  \param mode Resize mode
  \sa resizeMode(), ResizeMode
*/
Bryant's avatar
Bryant committed
void QwtPicker::setResizeMode( ResizeMode mode )
pixhawk's avatar
pixhawk committed
{
    d_data->resizeMode = mode;
pixhawk's avatar
pixhawk committed

/*!
  \return Resize mode
  \sa setResizeMode(), ResizeMode
*/

QwtPicker::ResizeMode QwtPicker::resizeMode() const
pixhawk's avatar
pixhawk committed
    return d_data->resizeMode;
}

/*!
  \brief En/disable the picker

  When enabled is true an event filter is installed for
  the observed widget, otherwise the event filter is removed.

  \param enabled true or false
  \sa isEnabled(), eventFilter()
*/
Bryant's avatar
Bryant committed
void QwtPicker::setEnabled( bool enabled )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( d_data->enabled != enabled )
    {
pixhawk's avatar
pixhawk committed
        d_data->enabled = enabled;

        QWidget *w = parentWidget();
Bryant's avatar
Bryant committed
        if ( w )
        {
pixhawk's avatar
pixhawk committed
            if ( enabled )
Bryant's avatar
Bryant committed
                w->installEventFilter( this );
pixhawk's avatar
pixhawk committed
            else
Bryant's avatar
Bryant committed
                w->removeEventFilter( this );
pixhawk's avatar
pixhawk committed
        }

        updateDisplay();
    }
}

/*!
  \return true when enabled, false otherwise
Bryant's avatar
Bryant committed
  \sa setEnabled(), eventFilter()
pixhawk's avatar
pixhawk committed
*/

bool QwtPicker::isEnabled() const
{
    return d_data->enabled;
}

/*!
  Set the font for the tracker

  \param font Tracker font
  \sa trackerFont(), setTrackerMode(), setTrackerPen()
*/
Bryant's avatar
Bryant committed
void QwtPicker::setTrackerFont( const QFont &font )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( font != d_data->trackerFont )
    {
pixhawk's avatar
pixhawk committed
        d_data->trackerFont = font;
        updateDisplay();
    }
}

/*!
  \return Tracker font
  \sa setTrackerFont(), trackerMode(), trackerPen()
*/

QFont QwtPicker::trackerFont() const
{
    return d_data->trackerFont;
}

/*!
  Set the pen for the tracker

  \param pen Tracker pen
  \sa trackerPen(), setTrackerMode(), setTrackerFont()
*/
Bryant's avatar
Bryant committed
void QwtPicker::setTrackerPen( const QPen &pen )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( pen != d_data->trackerPen )
    {
pixhawk's avatar
pixhawk committed
        d_data->trackerPen = pen;
        updateDisplay();
    }
}

/*!
  \return Tracker pen
  \sa setTrackerPen(), trackerMode(), trackerFont()
*/
QPen QwtPicker::trackerPen() const
{
    return d_data->trackerPen;
}

/*!
  Set the pen for the rubberband

Bryant's avatar
Bryant committed
  \param pen Rubber band pen
pixhawk's avatar
pixhawk committed
  \sa rubberBandPen(), setRubberBand()
*/
Bryant's avatar
Bryant committed
void QwtPicker::setRubberBandPen( const QPen &pen )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( pen != d_data->rubberBandPen )
    {
pixhawk's avatar
pixhawk committed
        d_data->rubberBandPen = pen;
        updateDisplay();
    }
}

/*!
Bryant's avatar
Bryant committed
  \return Rubber band pen
pixhawk's avatar
pixhawk committed
  \sa setRubberBandPen(), rubberBand()
*/
QPen QwtPicker::rubberBandPen() const
{
    return d_data->rubberBandPen;
}

/*!
   \brief Return the label for a position

   In case of HLineRubberBand the label is the value of the
   y position, in case of VLineRubberBand the value of the x position.
   Otherwise the label contains x and y position separated by a ',' .

   The format for the string conversion is "%d".

   \param pos Position
   \return Converted position as string
*/

Bryant's avatar
Bryant committed
QwtText QwtPicker::trackerText( const QPoint &pos ) const
pixhawk's avatar
pixhawk committed
{
    QString label;

Bryant's avatar
Bryant committed
    switch ( rubberBand() )
    {
        case HLineRubberBand:
            label.sprintf( "%d", pos.y() );
            break;
        case VLineRubberBand:
            label.sprintf( "%d", pos.x() );
            break;
        default:
            label.sprintf( "%d, %d", pos.x(), pos.y() );
pixhawk's avatar
pixhawk committed
    }
    return label;
}

/*!
Bryant's avatar
Bryant committed
  Calculate the mask for the rubber band overlay
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  \return Region for the mask
  \sa QWidget::setMask()
 */
QRegion QwtPicker::rubberBandMask() const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    QRegion mask;

    if ( !isActive() || rubberBand() == NoRubberBand ||
Bryant's avatar
Bryant committed
        rubberBandPen().style() == Qt::NoPen )
    {
        return mask;
pixhawk's avatar
pixhawk committed
    }

Bryant's avatar
Bryant committed
    const QPolygon pa = adjustedPoints( d_data->pickedPoints );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    QwtPickerMachine::SelectionType selectionType =
        QwtPickerMachine::NoSelection;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    if ( d_data->stateMachine )
        selectionType = d_data->stateMachine->selectionType();
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    switch ( selectionType )
    {
        case QwtPickerMachine::NoSelection:
        case QwtPickerMachine::PointSelection:
        {
            if ( pa.count() < 1 )
                return mask;

            const QPoint pos = pa[0];
            const int pw = rubberBandPen().width();

            const QRect pRect = pickArea().boundingRect().toRect();
            switch ( rubberBand() )
            {
                case VLineRubberBand:
                {
                    mask += qwtMaskRegion( QLine( pos.x(), pRect.top(), 
                        pos.x(), pRect.bottom() ), pw );
                    break;
                }
                case HLineRubberBand:
                {
                    mask += qwtMaskRegion( QLine( pRect.left(), pos.y(), 
                        pRect.right(), pos.y() ), pw );
                    break;
                }
                case CrossRubberBand:
                {
                    mask += qwtMaskRegion( QLine( pos.x(), pRect.top(), 
                        pos.x(), pRect.bottom() ), pw );
                    mask += qwtMaskRegion( QLine( pRect.left(), pos.y(), 
                        pRect.right(), pos.y() ), pw );
                    break;
                }
                default:
                    break;
            }
Bryant's avatar
Bryant committed
        }
        case QwtPickerMachine::RectSelection:
        {
            if ( pa.count() < 2 )
                return mask;
Bryant's avatar
Bryant committed
            const int pw = rubberBandPen().width();

            switch ( rubberBand() )
            {
                case RectRubberBand:
                {
                    const QRect r = QRect( pa.first(), pa.last() );
                    mask = qwtMaskRegion( r.normalized(), pw );
                    break;
                }
                case EllipseRubberBand:
                {
                    const QRect r = QRect( pa.first(), pa.last() );
                    mask += r.adjusted( -pw, -pw, pw, pw );
                    break;
                }
                default:
                    break;
            }
Bryant's avatar
Bryant committed
        }
        case QwtPickerMachine::PolygonSelection:
        {
            const int pw = rubberBandPen().width();
            if ( pw <= 1 )
            {
                // because of the join style we better
                // return a mask for a pen width <= 1 only
Bryant's avatar
Bryant committed
                const int off = 2 * pw;
                const QRect r = pa.boundingRect();
                mask += r.adjusted( -off, -off, off, off );
            }
Bryant's avatar
Bryant committed
        }
pixhawk's avatar
pixhawk committed
    }

Bryant's avatar
Bryant committed
    return mask;
}

/*!
   Draw a rubber band, depending on rubberBand()

   \param painter Painter, initialized with a clip region

   \sa rubberBand(), RubberBand
*/

void QwtPicker::drawRubberBand( QPainter *painter ) const
{
    if ( !isActive() || rubberBand() == NoRubberBand ||
        rubberBandPen().style() == Qt::NoPen )
    {
        return;
    }

    const QPolygon pa = adjustedPoints( d_data->pickedPoints );

    QwtPickerMachine::SelectionType selectionType =
        QwtPickerMachine::NoSelection;

    if ( d_data->stateMachine )
        selectionType = d_data->stateMachine->selectionType();

    switch ( selectionType )
    {
        case QwtPickerMachine::NoSelection:
        case QwtPickerMachine::PointSelection:
        {
            if ( pa.count() < 1 )
                return;

            const QPoint pos = pa[0];

            const QRect pRect = pickArea().boundingRect().toRect();
            switch ( rubberBand() )
            {
                case VLineRubberBand:
                {
                    QwtPainter::drawLine( painter, pos.x(),
                        pRect.top(), pos.x(), pRect.bottom() );
                    break;
                }
                case HLineRubberBand:
                {
                    QwtPainter::drawLine( painter, pRect.left(),
                        pos.y(), pRect.right(), pos.y() );
                    break;
                }
                case CrossRubberBand:
                {
                    QwtPainter::drawLine( painter, pos.x(),
                        pRect.top(), pos.x(), pRect.bottom() );
                    QwtPainter::drawLine( painter, pRect.left(),
                        pos.y(), pRect.right(), pos.y() );
                    break;
                }
                default:
                    break;
            }
            break;
pixhawk's avatar
pixhawk committed
        }
Bryant's avatar
Bryant committed
        case QwtPickerMachine::RectSelection:
        {
            if ( pa.count() < 2 )
                return;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
            const QRect rect = QRect( pa.first(), pa.last() ).normalized();
            switch ( rubberBand() )
            {
                case EllipseRubberBand:
                {
                    QwtPainter::drawEllipse( painter, rect );
                    break;
                }
                case RectRubberBand:
                {
                    QwtPainter::drawRect( painter, rect );
                    break;
                }
                default:
                    break;
            }
Bryant's avatar
Bryant committed
        }
        case QwtPickerMachine::PolygonSelection:
        {
            if ( rubberBand() == PolygonRubberBand )
                painter->drawPolyline( pa );
Bryant's avatar
Bryant committed
        }
pixhawk's avatar
pixhawk committed
    }
}

/*!
   Draw the tracker

   \param painter Painter
   \sa trackerRect(), trackerText()
*/

Bryant's avatar
Bryant committed
void QwtPicker::drawTracker( QPainter *painter ) const
{
    const QRect textRect = trackerRect( painter->font() );
    if ( !textRect.isEmpty() )
    {
        const QwtText label = trackerText( d_data->trackerPosition );
        if ( !label.isEmpty() )
            label.draw( painter, textRect );
pixhawk's avatar
pixhawk committed
    }
}

Bryant's avatar
Bryant committed
/*!
   \brief Map the pickedPoints() into a selection()

   adjustedPoints() maps the points, that have been collected on
   the parentWidget() into a selection(). The default implementation
   simply returns the points unmodified.

   The reason, why a selection() differs from the picked points
   depends on the application requirements. F.e. :

   - A rectangular selection might need to have a specific aspect ratio only.\n
   - A selection could accept non intersecting polygons only.\n
   - ...\n

   The example below is for a rectangular selection, where the first
   point is the center of the selected rectangle.
  \par Example
  \verbatim QPolygon MyPicker::adjustedPoints(const QPolygon &points) const
{
    QPolygon adjusted;
    if ( points.size() == 2 )
    {
        const int width = qAbs(points[1].x() - points[0].x());
        const int height = qAbs(points[1].y() - points[0].y());

        QRect rect(0, 0, 2 * width, 2 * height);
        rect.moveCenter(points[0]);

        adjusted += rect.topLeft();
        adjusted += rect.bottomRight();
    }
    return adjusted;
}\endverbatim\n

  \param points Selected points
  \return Selected points unmodified
*/
QPolygon QwtPicker::adjustedPoints( const QPolygon &points ) const
{
    return points;
}

/*!
  \return Selected points
  \sa pickedPoints(), adjustedPoints()
*/
QPolygon QwtPicker::selection() const
{
    return adjustedPoints( d_data->pickedPoints );
}

//! \return Current position of the tracker
QPoint QwtPicker::trackerPosition() const
pixhawk's avatar
pixhawk committed
{
    return d_data->trackerPosition;
}

Bryant's avatar
Bryant committed
/*!
   Calculate the bounding rectangle for the tracker text
   from the current position of the tracker

   \param font Font of the tracker text
   \return Bounding rectangle of the tracker text

   \sa trackerPosition()
*/
QRect QwtPicker::trackerRect( const QFont &font ) const
pixhawk's avatar
pixhawk committed
{
    if ( trackerMode() == AlwaysOff ||
Bryant's avatar
Bryant committed
        ( trackerMode() == ActiveOnly && !isActive() ) )
    {
pixhawk's avatar
pixhawk committed
        return QRect();
    }

    if ( d_data->trackerPosition.x() < 0 || d_data->trackerPosition.y() < 0 )
        return QRect();

Bryant's avatar
Bryant committed
    QwtText text = trackerText( d_data->trackerPosition );
pixhawk's avatar
pixhawk committed
    if ( text.isEmpty() )
        return QRect();

Bryant's avatar
Bryant committed
    const QSizeF textSize = text.textSize( font );
    QRect textRect( 0, 0, qCeil( textSize.width() ), qCeil( textSize.height() ) );
pixhawk's avatar
pixhawk committed

    const QPoint &pos = d_data->trackerPosition;

    int alignment = 0;
Bryant's avatar
Bryant committed
    if ( isActive() && d_data->pickedPoints.count() > 1
        && rubberBand() != NoRubberBand )
    {
        const QPoint last =
Bryant's avatar
Bryant committed
            d_data->pickedPoints[int( d_data->pickedPoints.count() ) - 2];
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        alignment |= ( pos.x() >= last.x() ) ? Qt::AlignRight : Qt::AlignLeft;
        alignment |= ( pos.y() > last.y() ) ? Qt::AlignBottom : Qt::AlignTop;
    }
    else
pixhawk's avatar
pixhawk committed
        alignment = Qt::AlignTop | Qt::AlignRight;

    const int margin = 5;

    int x = pos.x();
    if ( alignment & Qt::AlignLeft )
        x -= textRect.width() + margin;
    else if ( alignment & Qt::AlignRight )
        x += margin;

    int y = pos.y();
    if ( alignment & Qt::AlignBottom )
        y += margin;
    else if ( alignment & Qt::AlignTop )
        y -= textRect.height() + margin;
Bryant's avatar
Bryant committed
    textRect.moveTopLeft( QPoint( x, y ) );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    const QRect pickRect = pickArea().boundingRect().toRect();
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    int right = qMin( textRect.right(), pickRect.right() - margin );
    int bottom = qMin( textRect.bottom(), pickRect.bottom() - margin );
    textRect.moveBottomRight( QPoint( right, bottom ) );

    int left = qMax( textRect.left(), pickRect.left() + margin );
    int top = qMax( textRect.top(), pickRect.top() + margin );
    textRect.moveTopLeft( QPoint( left, top ) );
pixhawk's avatar
pixhawk committed

    return textRect;
}

/*!
  \brief Event filter

Bryant's avatar
Bryant committed
  When isEnabled() is true all events of the observed widget are filtered.
pixhawk's avatar
pixhawk committed
  Mouse and keyboard events are translated into widgetMouse- and widgetKey-
  and widgetWheel-events. Paint and Resize events are handled to keep
Bryant's avatar
Bryant committed
  rubber band and tracker up to date.

  \param object Object to be filtered
  \param event Event
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  \return Always false.

  \sa widgetEnterEvent(), widgetLeaveEvent(),
      widgetMousePressEvent(), widgetMouseReleaseEvent(),
pixhawk's avatar
pixhawk committed
      widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(),
Bryant's avatar
Bryant committed
      widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent(),
      QObject::installEventFilter(), QObject::event()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
bool QwtPicker::eventFilter( QObject *object, QEvent *event )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( object && object == parentWidget() )
    {
        switch ( event->type() )
        {
            case QEvent::Resize:
            {
                const QResizeEvent *re = static_cast<QResizeEvent *>( event );
                if ( d_data->resizeMode == Stretch )
                    stretchSelection( re->oldSize(), re->size() );
Bryant's avatar
Bryant committed
                break;
            }
            case QEvent::Enter:
            {
                widgetEnterEvent( event );
                break;
            }
            case QEvent::Leave:
            {
                widgetLeaveEvent( event );
                break;
            }
            case QEvent::MouseButtonPress:
            {
                widgetMousePressEvent( static_cast<QMouseEvent *>( event ) );
                break;
            }
            case QEvent::MouseButtonRelease:
            {
                widgetMouseReleaseEvent( static_cast<QMouseEvent *>( event ) );
                break;
            }
            case QEvent::MouseButtonDblClick:
            {
                widgetMouseDoubleClickEvent( static_cast<QMouseEvent *>( event ) );
                break;
            }
            case QEvent::MouseMove:
            {
                widgetMouseMoveEvent( static_cast<QMouseEvent *>( event ) );
                break;
            }
            case QEvent::KeyPress:
            {
                widgetKeyPressEvent( static_cast<QKeyEvent *>( event ) );
                break;
            }
            case QEvent::KeyRelease:
            {
                widgetKeyReleaseEvent( static_cast<QKeyEvent *>( event ) );
                break;
            }
            case QEvent::Wheel:
            {
                widgetWheelEvent( static_cast<QWheelEvent *>( event ) );
                break;
            }
            default:
                break;
pixhawk's avatar
pixhawk committed
        }
    }
    return false;
}

/*!
  Handle a mouse press event for the observed widget.

Bryant's avatar
Bryant committed
  \param mouseEvent Mouse event
pixhawk's avatar
pixhawk committed

  \sa eventFilter(), widgetMouseReleaseEvent(),
      widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(),
      widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent()
*/
Bryant's avatar
Bryant committed
void QwtPicker::widgetMousePressEvent( QMouseEvent *mouseEvent )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    transition( mouseEvent );
pixhawk's avatar
pixhawk committed
}

/*!
  Handle a mouse move event for the observed widget.

Bryant's avatar
Bryant committed
  \param mouseEvent Mouse event
pixhawk's avatar
pixhawk committed

  \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(),
      widgetMouseDoubleClickEvent(),
      widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent()
*/
Bryant's avatar
Bryant committed
void QwtPicker::widgetMouseMoveEvent( QMouseEvent *mouseEvent )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( pickArea().contains( mouseEvent->pos() ) )
        d_data->trackerPosition = mouseEvent->pos();
pixhawk's avatar
pixhawk committed
    else
Bryant's avatar
Bryant committed
        d_data->trackerPosition = QPoint( -1, -1 );
pixhawk's avatar
pixhawk committed

    if ( !isActive() )
        updateDisplay();

Bryant's avatar
Bryant committed
    transition( mouseEvent );
}

/*!
  Handle a enter event for the observed widget.

  \param event Qt event

  \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(),
      widgetMouseDoubleClickEvent(),
      widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent()