qwt_arrow_button.cpp 8.13 KB
Newer Older
pixhawk's avatar
pixhawk committed
1 2 3 4
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
 * Qwt Widget Library
 * Copyright (C) 1997   Josef Wilgen
 * Copyright (C) 2002   Uwe Rathmann
5
 *
pixhawk's avatar
pixhawk committed
6 7 8 9
 * 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
10 11
#include "qwt_arrow_button.h"
#include "qwt_math.h"
pixhawk's avatar
pixhawk committed
12 13
#include <qpainter.h>
#include <qstyle.h>
Bryant's avatar
Bryant committed
14
#include <qstyleoption.h>
pixhawk's avatar
pixhawk committed
15
#include <qevent.h>
Bryant's avatar
Bryant committed
16
#include <qapplication.h>
pixhawk's avatar
pixhawk committed
17 18 19 20 21 22 23 24 25 26 27 28

static const int MaxNum = 3;
static const int Margin = 2;
static const int Spacing = 1;

class QwtArrowButton::PrivateData
{
public:
    int num;
    Qt::ArrowType arrowType;
};

Bryant's avatar
Bryant committed
29
static QStyleOptionButton styleOpt( const QwtArrowButton* btn )
pixhawk's avatar
pixhawk committed
30 31
{
    QStyleOptionButton option;
Bryant's avatar
Bryant committed
32
    option.init( btn );
pixhawk's avatar
pixhawk committed
33
    option.features = QStyleOptionButton::None;
Bryant's avatar
Bryant committed
34
    if ( btn->isFlat() )
pixhawk's avatar
pixhawk committed
35
        option.features |= QStyleOptionButton::Flat;
Bryant's avatar
Bryant committed
36
    if ( btn->menu() )
pixhawk's avatar
pixhawk committed
37
        option.features |= QStyleOptionButton::HasMenu;
Bryant's avatar
Bryant committed
38
    if ( btn->autoDefault() || btn->isDefault() )
pixhawk's avatar
pixhawk committed
39
        option.features |= QStyleOptionButton::AutoDefaultButton;
Bryant's avatar
Bryant committed
40
    if ( btn->isDefault() )
pixhawk's avatar
pixhawk committed
41
        option.features |= QStyleOptionButton::DefaultButton;
Bryant's avatar
Bryant committed
42
    if ( btn->isDown() )
pixhawk's avatar
pixhawk committed
43
        option.state |= QStyle::State_Sunken;
Bryant's avatar
Bryant committed
44
    if ( !btn->isFlat() && !btn->isDown() )
pixhawk's avatar
pixhawk committed
45 46 47 48 49 50 51
        option.state |= QStyle::State_Raised;

    return option;
}

/*!
  \param num Number of arrows
Bryant's avatar
Bryant committed
52
  \param arrowType see Qt::ArrowType in the Qt docs.
pixhawk's avatar
pixhawk committed
53 54
  \param parent Parent widget
*/
Bryant's avatar
Bryant committed
55 56 57
QwtArrowButton::QwtArrowButton( int num,
        Qt::ArrowType arrowType, QWidget *parent ):
    QPushButton( parent )
pixhawk's avatar
pixhawk committed
58 59
{
    d_data = new PrivateData;
Bryant's avatar
Bryant committed
60
    d_data->num = qBound( 1, num, MaxNum );
pixhawk's avatar
pixhawk committed
61 62
    d_data->arrowType = arrowType;

Bryant's avatar
Bryant committed
63 64 65 66 67 68 69 70 71 72 73 74 75
    setAutoRepeat( true );
    setAutoDefault( false );

    switch ( d_data->arrowType )
    {
        case Qt::LeftArrow:
        case Qt::RightArrow:
            setSizePolicy( QSizePolicy::Expanding,
                QSizePolicy::Fixed );
            break;
        default:
            setSizePolicy( QSizePolicy::Fixed,
                QSizePolicy::Expanding );
pixhawk's avatar
pixhawk committed
76 77 78 79 80 81 82 83 84 85 86 87 88
    }
}

//! Destructor
QwtArrowButton::~QwtArrowButton()
{
    delete d_data;
    d_data = NULL;
}

/*!
  \brief The direction of the arrows
*/
89 90 91
Qt::ArrowType QwtArrowButton::arrowType() const
{
    return d_data->arrowType;
pixhawk's avatar
pixhawk committed
92 93 94 95 96
}

/*!
  \brief The number of arrows
*/
97 98 99
int QwtArrowButton::num() const
{
    return d_data->num;
pixhawk's avatar
pixhawk committed
100 101 102
}

/*!
Bryant's avatar
Bryant committed
103
  \return the bounding rectangle for the label
pixhawk's avatar
pixhawk committed
104 105 106 107 108 109
*/
QRect QwtArrowButton::labelRect() const
{
    const int m = Margin;

    QRect r = rect();
Bryant's avatar
Bryant committed
110 111 112 113 114 115 116 117 118 119 120 121
    r.setRect( r.x() + m, r.y() + m,
        r.width() - 2 * m, r.height() - 2 * m );

    if ( isDown() )
    {
        QStyleOptionButton option = styleOpt( this );
        const int ph = style()->pixelMetric(
            QStyle::PM_ButtonShiftHorizontal, &option, this );
        const int pv = style()->pixelMetric(
            QStyle::PM_ButtonShiftVertical, &option, this );

        r.translate( ph, pv );
pixhawk's avatar
pixhawk committed
122 123 124 125 126 127 128 129 130
    }

    return r;
}

/*!
   Paint event handler
   \param event Paint event
*/
Bryant's avatar
Bryant committed
131
void QwtArrowButton::paintEvent( QPaintEvent *event )
pixhawk's avatar
pixhawk committed
132
{
Bryant's avatar
Bryant committed
133 134 135
    QPushButton::paintEvent( event );
    QPainter painter( this );
    drawButtonLabel( &painter );
pixhawk's avatar
pixhawk committed
136 137 138 139 140 141
}

/*!
  \brief Draw the button label

  \param painter Painter
Bryant's avatar
Bryant committed
142
  \sa The Qt Manual for QPushButton
pixhawk's avatar
pixhawk committed
143
*/
Bryant's avatar
Bryant committed
144
void QwtArrowButton::drawButtonLabel( QPainter *painter )
pixhawk's avatar
pixhawk committed
145 146
{
    const bool isVertical = d_data->arrowType == Qt::UpArrow ||
Bryant's avatar
Bryant committed
147
        d_data->arrowType == Qt::DownArrow;
pixhawk's avatar
pixhawk committed
148 149 150 151 152

    const QRect r = labelRect();
    QSize boundingSize = labelRect().size();
    if ( isVertical )
        boundingSize.transpose();
153 154

    const int w =
Bryant's avatar
Bryant committed
155
        ( boundingSize.width() - ( MaxNum - 1 ) * Spacing ) / MaxNum;
pixhawk's avatar
pixhawk committed
156

Bryant's avatar
Bryant committed
157 158
    QSize arrow = arrowSize( Qt::RightArrow,
        QSize( w, boundingSize.height() ) );
pixhawk's avatar
pixhawk committed
159 160 161 162 163

    if ( isVertical )
        arrow.transpose();

    QRect contentsSize; // aligned rect where to paint all arrows
Bryant's avatar
Bryant committed
164 165 166 167 168 169 170 171 172 173 174
    if ( d_data->arrowType == Qt::LeftArrow || d_data->arrowType == Qt::RightArrow )
    {
        contentsSize.setWidth( d_data->num * arrow.width()
            + ( d_data->num - 1 ) * Spacing );
        contentsSize.setHeight( arrow.height() );
    }
    else
    {
        contentsSize.setWidth( arrow.width() );
        contentsSize.setHeight( d_data->num * arrow.height()
            + ( d_data->num - 1 ) * Spacing );
pixhawk's avatar
pixhawk committed
175 176
    }

Bryant's avatar
Bryant committed
177 178 179
    QRect arrowRect( contentsSize );
    arrowRect.moveCenter( r.center() );
    arrowRect.setSize( arrow );
pixhawk's avatar
pixhawk committed
180 181

    painter->save();
Bryant's avatar
Bryant committed
182 183 184
    for ( int i = 0; i < d_data->num; i++ )
    {
        drawArrow( painter, arrowRect, d_data->arrowType );
pixhawk's avatar
pixhawk committed
185 186 187 188 189 190 191 192 193

        int dx = 0;
        int dy = 0;

        if ( isVertical )
            dy = arrow.height() + Spacing;
        else
            dx = arrow.width() + Spacing;

Bryant's avatar
Bryant committed
194
        arrowRect.translate( dx, dy );
pixhawk's avatar
pixhawk committed
195 196 197
    }
    painter->restore();

Bryant's avatar
Bryant committed
198 199
    if ( hasFocus() )
    {
pixhawk's avatar
pixhawk committed
200
        QStyleOptionFocusRect option;
Bryant's avatar
Bryant committed
201 202 203 204 205
        option.init( this );
        option.backgroundColor = palette().color( QPalette::Window );

        style()->drawPrimitive( QStyle::PE_FrameFocusRect,
            &option, painter, this );
pixhawk's avatar
pixhawk committed
206 207 208 209
    }
}

/*!
Bryant's avatar
Bryant committed
210
    Draw an arrow int a bounding rectangle
pixhawk's avatar
pixhawk committed
211 212 213 214 215

    \param painter Painter
    \param r Rectangle where to paint the arrow
    \param arrowType Arrow type
*/
Bryant's avatar
Bryant committed
216 217
void QwtArrowButton::drawArrow( QPainter *painter,
    const QRect &r, Qt::ArrowType arrowType ) const
pixhawk's avatar
pixhawk committed
218
{
Bryant's avatar
Bryant committed
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
    QPolygon pa( 3 );

    switch ( arrowType )
    {
        case Qt::UpArrow:
            pa.setPoint( 0, r.bottomLeft() );
            pa.setPoint( 1, r.bottomRight() );
            pa.setPoint( 2, r.center().x(), r.top() );
            break;
        case Qt::DownArrow:
            pa.setPoint( 0, r.topLeft() );
            pa.setPoint( 1, r.topRight() );
            pa.setPoint( 2, r.center().x(), r.bottom() );
            break;
        case Qt::RightArrow:
            pa.setPoint( 0, r.topLeft() );
            pa.setPoint( 1, r.bottomLeft() );
            pa.setPoint( 2, r.right(), r.center().y() );
            break;
        case Qt::LeftArrow:
            pa.setPoint( 0, r.topRight() );
            pa.setPoint( 1, r.bottomRight() );
            pa.setPoint( 2, r.left(), r.center().y() );
            break;
        default:
            break;
pixhawk's avatar
pixhawk committed
245 246 247
    }

    painter->save();
Bryant's avatar
Bryant committed
248 249 250 251 252 253

    painter->setRenderHint( QPainter::Antialiasing, true );
    painter->setPen( Qt::NoPen );
    painter->setBrush( palette().brush( QPalette::ButtonText ) );
    painter->drawPolygon( pa );

pixhawk's avatar
pixhawk committed
254 255 256 257 258 259 260 261
    painter->restore();
}

/*!
  \return a size hint
*/
QSize QwtArrowButton::sizeHint() const
{
Bryant's avatar
Bryant committed
262 263
    const QSize hint = minimumSizeHint();
    return hint.expandedTo( QApplication::globalStrut() );
pixhawk's avatar
pixhawk committed
264 265 266 267 268 269 270
}

/*!
  \brief Return a minimum size hint
*/
QSize QwtArrowButton::minimumSizeHint() const
{
Bryant's avatar
Bryant committed
271
    const QSize asz = arrowSize( Qt::RightArrow, QSize() );
pixhawk's avatar
pixhawk committed
272 273

    QSize sz(
Bryant's avatar
Bryant committed
274
        2 * Margin + ( MaxNum - 1 ) * Spacing + MaxNum * asz.width(),
pixhawk's avatar
pixhawk committed
275 276 277 278 279 280 281
        2 * Margin + asz.height()
    );

    if ( d_data->arrowType == Qt::UpArrow || d_data->arrowType == Qt::DownArrow )
        sz.transpose();

    QStyleOption styleOption;
Bryant's avatar
Bryant committed
282
    styleOption.init( this );
pixhawk's avatar
pixhawk committed
283

Bryant's avatar
Bryant committed
284 285
    sz = style()->sizeFromContents( QStyle::CT_PushButton,
        &styleOption, sz, this );
pixhawk's avatar
pixhawk committed
286 287 288 289 290

    return sz;
}

/*!
Bryant's avatar
Bryant committed
291
   Calculate the size for a arrow that fits into a rectangle of a given size
pixhawk's avatar
pixhawk committed
292 293 294 295 296

   \param arrowType Arrow type
   \param boundingSize Bounding size
   \return Size of the arrow
*/
Bryant's avatar
Bryant committed
297 298
QSize QwtArrowButton::arrowSize( Qt::ArrowType arrowType,
    const QSize &boundingSize ) const
pixhawk's avatar
pixhawk committed
299 300 301 302
{
    QSize bs = boundingSize;
    if ( arrowType == Qt::UpArrow || arrowType == Qt::DownArrow )
        bs.transpose();
303

pixhawk's avatar
pixhawk committed
304 305
    const int MinLen = 2;
    const QSize sz = bs.expandedTo(
Bryant's avatar
Bryant committed
306
        QSize( MinLen, 2 * MinLen - 1 ) ); // minimum
pixhawk's avatar
pixhawk committed
307 308 309 310

    int w = sz.width();
    int h = 2 * w - 1;

Bryant's avatar
Bryant committed
311 312
    if ( h > sz.height() )
    {
pixhawk's avatar
pixhawk committed
313
        h = sz.height();
Bryant's avatar
Bryant committed
314
        w = ( h + 1 ) / 2;
pixhawk's avatar
pixhawk committed
315 316
    }

Bryant's avatar
Bryant committed
317
    QSize arrSize( w, h );
pixhawk's avatar
pixhawk committed
318 319 320 321 322 323 324 325 326
    if ( arrowType == Qt::UpArrow || arrowType == Qt::DownArrow )
        arrSize.transpose();

    return arrSize;
}

/*!
  \brief autoRepeat for the space keys
*/
Bryant's avatar
Bryant committed
327
void QwtArrowButton::keyPressEvent( QKeyEvent *event )
pixhawk's avatar
pixhawk committed
328
{
Bryant's avatar
Bryant committed
329 330
    if ( event->isAutoRepeat() && event->key() == Qt::Key_Space )
        Q_EMIT clicked();
pixhawk's avatar
pixhawk committed
331

Bryant's avatar
Bryant committed
332
    QPushButton::keyPressEvent( event );
pixhawk's avatar
pixhawk committed
333
}