src/gui/widgets/qdial.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtGui module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "qdial.h"
       
    43 
       
    44 #ifndef QT_NO_DIAL
       
    45 
       
    46 #include <qapplication.h>
       
    47 #include <qbitmap.h>
       
    48 #include <qcolor.h>
       
    49 #include <qevent.h>
       
    50 #include <qpainter.h>
       
    51 #include <qpolygon.h>
       
    52 #include <qregion.h>
       
    53 #include <qstyle.h>
       
    54 #include <qstylepainter.h>
       
    55 #include <qstyleoption.h>
       
    56 #include <qslider.h>
       
    57 #include <private/qabstractslider_p.h>
       
    58 #include <private/qmath_p.h>
       
    59 #ifndef QT_NO_ACCESSIBILITY
       
    60 #include "qaccessible.h"
       
    61 #endif
       
    62 
       
    63 QT_BEGIN_NAMESPACE
       
    64 
       
    65 class QDialPrivate : public QAbstractSliderPrivate
       
    66 {
       
    67     Q_DECLARE_PUBLIC(QDial)
       
    68 public:
       
    69     QDialPrivate()
       
    70     {
       
    71         wrapping = false;
       
    72         tracking = true;
       
    73         doNotEmit = false;
       
    74         target = qreal(3.7);
       
    75     }
       
    76 
       
    77     qreal target;
       
    78     uint showNotches : 1;
       
    79     uint wrapping : 1;
       
    80     uint doNotEmit : 1;
       
    81 
       
    82     int valueFromPoint(const QPoint &) const;
       
    83     double angle(const QPoint &, const QPoint &) const;
       
    84     void init();
       
    85 };
       
    86 
       
    87 void QDialPrivate::init()
       
    88 {
       
    89     Q_Q(QDial);
       
    90     showNotches = false;
       
    91     q->setFocusPolicy(Qt::WheelFocus);
       
    92 #ifdef QT3_SUPPORT
       
    93     QObject::connect(q, SIGNAL(sliderPressed()), q, SIGNAL(dialPressed()));
       
    94     QObject::connect(q, SIGNAL(sliderMoved(int)), q, SIGNAL(dialMoved(int)));
       
    95     QObject::connect(q, SIGNAL(sliderReleased()), q, SIGNAL(dialReleased()));
       
    96 #endif
       
    97 }
       
    98 
       
    99 /*!
       
   100     Initialize \a option with the values from this QDial. This method
       
   101     is useful for subclasses when they need a QStyleOptionSlider, but don't want
       
   102     to fill in all the information themselves.
       
   103 
       
   104     \sa QStyleOption::initFrom()
       
   105 */
       
   106 void QDial::initStyleOption(QStyleOptionSlider *option) const
       
   107 {
       
   108     if (!option)
       
   109         return;
       
   110 
       
   111     Q_D(const QDial);
       
   112     option->initFrom(this);
       
   113     option->minimum = d->minimum;
       
   114     option->maximum = d->maximum;
       
   115     option->sliderPosition = d->position;
       
   116     option->sliderValue = d->value;
       
   117     option->singleStep = d->singleStep;
       
   118     option->pageStep = d->pageStep;
       
   119     option->upsideDown = !d->invertedAppearance;
       
   120     option->notchTarget = d->target;
       
   121     option->dialWrapping = d->wrapping;
       
   122     option->subControls = QStyle::SC_All;
       
   123     option->activeSubControls = QStyle::SC_None;
       
   124     if (!d->showNotches) {
       
   125         option->subControls &= ~QStyle::SC_DialTickmarks;
       
   126         option->tickPosition = QSlider::TicksAbove;
       
   127     } else {
       
   128         option->tickPosition = QSlider::NoTicks;
       
   129     }
       
   130     option->tickInterval = notchSize();
       
   131 }
       
   132 
       
   133 int QDialPrivate::valueFromPoint(const QPoint &p) const
       
   134 {
       
   135     Q_Q(const QDial);
       
   136     double yy = (double)q->height()/2.0 - p.y();
       
   137     double xx = (double)p.x() - q->width()/2.0;
       
   138     double a = (xx || yy) ? atan2(yy, xx) : 0;
       
   139 
       
   140     if (a < Q_PI / -2)
       
   141         a = a + Q_PI * 2;
       
   142 
       
   143     int dist = 0;
       
   144     int minv = minimum, maxv = maximum;
       
   145 
       
   146     if (minimum < 0) {
       
   147         dist = -minimum;
       
   148         minv = 0;
       
   149         maxv = maximum + dist;
       
   150     }
       
   151 
       
   152     int r = maxv - minv;
       
   153     int v;
       
   154     if (wrapping)
       
   155         v =  (int)(0.5 + minv + r * (Q_PI * 3 / 2 - a) / (2 * Q_PI));
       
   156     else
       
   157         v =  (int)(0.5 + minv + r* (Q_PI * 4 / 3 - a) / (Q_PI * 10 / 6));
       
   158 
       
   159     if (dist > 0)
       
   160         v -= dist;
       
   161 
       
   162     return !invertedAppearance ? bound(v) : maximum - bound(v);
       
   163 }
       
   164 
       
   165 /*!
       
   166     \class QDial
       
   167 
       
   168     \brief The QDial class provides a rounded range control (like a speedometer or potentiometer).
       
   169 
       
   170     \ingroup basicwidgets
       
   171 
       
   172 
       
   173     QDial is used when the user needs to control a value within a
       
   174     program-definable range, and the range either wraps around
       
   175     (for example, with angles measured from 0 to 359 degrees) or the
       
   176     dialog layout needs a square widget.
       
   177 
       
   178     Since QDial inherits from QAbstractSlider, the dial behaves in
       
   179     a similar way to a \l{QSlider}{slider}. When wrapping() is false
       
   180     (the default setting) there is no real difference between a slider
       
   181     and a dial. They both share the same signals, slots and member
       
   182     functions. Which one you use depends on the expectations of
       
   183     your users and on the type of application.
       
   184 
       
   185     The dial initially emits valueChanged() signals continuously while
       
   186     the slider is being moved; you can make it emit the signal less
       
   187     often by disabling the \l{QAbstractSlider::tracking} {tracking}
       
   188     property. The sliderMoved() signal is emitted continuously even
       
   189     when tracking is disabled.
       
   190 
       
   191     The dial also emits sliderPressed() and sliderReleased() signals
       
   192     when the mouse button is pressed and released. Note that the
       
   193     dial's value can change without these signals being emitted since
       
   194     the keyboard and wheel can also be used to change the value.
       
   195 
       
   196     Unlike the slider, QDial attempts to draw a "nice" number of
       
   197     notches rather than one per line step. If possible, the number of
       
   198     notches drawn is one per line step, but if there aren't enough pixels
       
   199     to draw every one, QDial will skip notches to try and draw a uniform
       
   200     set (e.g. by drawing every second or third notch).
       
   201 
       
   202     Like the slider, the dial makes the QAbstractSlider functions
       
   203     setValue(), addLine(), subtractLine(), addPage() and
       
   204     subtractPage() available as slots.
       
   205 
       
   206     The dial's keyboard interface is fairly simple: The
       
   207     \key{left}/\key{up} and \key{right}/\key{down} arrow keys adjust
       
   208     the dial's \l {QAbstractSlider::value} {value} by the defined
       
   209     \l {QAbstractSlider::singleStep} {singleStep}, \key{Page Up} and
       
   210     \key{Page Down} by the defined \l {QAbstractSlider::pageStep}
       
   211     {pageStep}, and the \key Home and \key End keys set the value to
       
   212     the defined \l {QAbstractSlider::minimum} {minimum} and
       
   213     \l {QAbstractSlider::maximum} {maximum} values.
       
   214 
       
   215     If you are using the mouse wheel to adjust the dial, the increment
       
   216     value is determined by the lesser value of
       
   217     \l{QApplication::wheelScrollLines()} {wheelScrollLines} multipled
       
   218     by \l {QAbstractSlider::singleStep} {singleStep}, and
       
   219     \l {QAbstractSlider::pageStep} {pageStep}.
       
   220 
       
   221     \table
       
   222     \row \o \inlineimage plastique-dial.png Screenshot of a dial in the Plastique widget style
       
   223     \o \inlineimage windowsxp-dial.png Screenshot of a dial in the Windows XP widget style
       
   224     \o \inlineimage macintosh-dial.png Screenshot of a dial in the Macintosh widget style
       
   225     \row \o {3,1} Dials shown in various widget styles (from left to right):
       
   226          \l{Plastique Style Widget Gallery}{Plastique},
       
   227          \l{Windows XP Style Widget Gallery}{Windows XP},
       
   228          \l{Macintosh Style Widget Gallery}{Macintosh}.
       
   229     \endtable
       
   230 
       
   231     \sa QScrollBar, QSpinBox, QSlider, {fowler}{GUI Design Handbook: Slider}, {Sliders Example}
       
   232 */
       
   233 
       
   234 /*!
       
   235     Constructs a dial.
       
   236 
       
   237     The \a parent argument is sent to the QAbstractSlider constructor.
       
   238 */
       
   239 QDial::QDial(QWidget *parent)
       
   240     : QAbstractSlider(*new QDialPrivate, parent)
       
   241 {
       
   242     Q_D(QDial);
       
   243     d->init();
       
   244 }
       
   245 
       
   246 #ifdef QT3_SUPPORT
       
   247 /*!
       
   248     Use one of the constructors that doesn't take the \a name
       
   249     argument and then use setObjectName() instead.
       
   250 */
       
   251 QDial::QDial(QWidget *parent, const char *name)
       
   252     : QAbstractSlider(*new QDialPrivate, parent)
       
   253 {
       
   254     Q_D(QDial);
       
   255     setObjectName(QString::fromAscii(name));
       
   256     d->init();
       
   257 }
       
   258 
       
   259 /*!
       
   260     Use one of the constructors that doesn't take the \a name
       
   261     argument and then use setObjectName() instead.
       
   262 */
       
   263 QDial::QDial(int minValue, int maxValue, int pageStep, int value,
       
   264               QWidget *parent, const char *name)
       
   265     : QAbstractSlider(*new QDialPrivate, parent)
       
   266 {
       
   267     Q_D(QDial);
       
   268     setObjectName(QString::fromAscii(name));
       
   269     d->minimum = minValue;
       
   270     d->maximum = maxValue;
       
   271     d->pageStep = pageStep;
       
   272     d->position = d->value = value;
       
   273     d->init();
       
   274 }
       
   275 #endif
       
   276 /*!
       
   277     Destroys the dial.
       
   278 */
       
   279 QDial::~QDial()
       
   280 {
       
   281 }
       
   282 
       
   283 /*! \reimp */
       
   284 void QDial::resizeEvent(QResizeEvent *e)
       
   285 {
       
   286     QWidget::resizeEvent(e);
       
   287 }
       
   288 
       
   289 /*!
       
   290   \reimp
       
   291 */
       
   292 
       
   293 void QDial::paintEvent(QPaintEvent *)
       
   294 {
       
   295     QStylePainter p(this);
       
   296     QStyleOptionSlider option;
       
   297     initStyleOption(&option);
       
   298     p.drawComplexControl(QStyle::CC_Dial, option);
       
   299 }
       
   300 
       
   301 /*!
       
   302   \reimp
       
   303 */
       
   304 
       
   305 void QDial::mousePressEvent(QMouseEvent *e)
       
   306 {
       
   307     Q_D(QDial);
       
   308     if (d->maximum == d->minimum ||
       
   309         (e->button() != Qt::LeftButton)  ||
       
   310         (e->buttons() ^ e->button())) {
       
   311         e->ignore();
       
   312         return;
       
   313     }
       
   314     e->accept();
       
   315     setSliderPosition(d->valueFromPoint(e->pos()));
       
   316     // ### This isn't quite right,
       
   317     // we should be doing a hit test and only setting this if it's
       
   318     // the actual dial thingie (similar to what QSlider does), but we have no
       
   319     // subControls for QDial.
       
   320     setSliderDown(true);
       
   321 }
       
   322 
       
   323 
       
   324 /*!
       
   325   \reimp
       
   326 */
       
   327 
       
   328 void QDial::mouseReleaseEvent(QMouseEvent * e)
       
   329 {
       
   330     Q_D(QDial);
       
   331     if (e->buttons() & (~e->button()) ||
       
   332        (e->button() != Qt::LeftButton)) {
       
   333         e->ignore();
       
   334         return;
       
   335     }
       
   336     e->accept();
       
   337     setValue(d->valueFromPoint(e->pos()));
       
   338     setSliderDown(false);
       
   339 }
       
   340 
       
   341 
       
   342 /*!
       
   343   \reimp
       
   344 */
       
   345 
       
   346 void QDial::mouseMoveEvent(QMouseEvent * e)
       
   347 {
       
   348     Q_D(QDial);
       
   349     if (!(e->buttons() & Qt::LeftButton)) {
       
   350         e->ignore();
       
   351         return;
       
   352     }
       
   353     e->accept();
       
   354     d->doNotEmit = true;
       
   355     setSliderPosition(d->valueFromPoint(e->pos()));
       
   356     d->doNotEmit = false;
       
   357 }
       
   358 
       
   359 
       
   360 /*!
       
   361     \reimp
       
   362 */
       
   363 
       
   364 void QDial::sliderChange(SliderChange change)
       
   365 {
       
   366     QAbstractSlider::sliderChange(change);
       
   367 }
       
   368 
       
   369 void QDial::setWrapping(bool enable)
       
   370 {
       
   371     Q_D(QDial);
       
   372     if (d->wrapping == enable)
       
   373         return;
       
   374     d->wrapping = enable;
       
   375     update();
       
   376 }
       
   377 
       
   378 
       
   379 /*!
       
   380     \property QDial::wrapping
       
   381     \brief whether wrapping is enabled
       
   382 
       
   383     If true, wrapping is enabled; otherwise some space is inserted at the bottom
       
   384     of the dial to separate the ends of the range of valid values.
       
   385 
       
   386     If enabled, the arrow can be oriented at any angle on the dial. If disabled,
       
   387     the arrow will be restricted to the upper part of the dial; if it is rotated
       
   388     into the space at the bottom of the dial, it will be clamped to the closest
       
   389     end of the valid range of values.
       
   390 
       
   391     By default this property is false.
       
   392 */
       
   393 
       
   394 bool QDial::wrapping() const
       
   395 {
       
   396     Q_D(const QDial);
       
   397     return d->wrapping;
       
   398 }
       
   399 
       
   400 
       
   401 /*!
       
   402     \property QDial::notchSize
       
   403     \brief the current notch size
       
   404 
       
   405     The notch size is in range control units, not pixels, and if
       
   406     possible it is a multiple of singleStep() that results in an
       
   407     on-screen notch size near notchTarget().
       
   408 
       
   409     By default, this property has a value of 1.
       
   410 
       
   411     \sa notchTarget(), singleStep()
       
   412 */
       
   413 
       
   414 int QDial::notchSize() const
       
   415 {
       
   416     Q_D(const QDial);
       
   417     // radius of the arc
       
   418     int r = qMin(width(), height())/2;
       
   419     // length of the whole arc
       
   420     int l = (int)(r * (d->wrapping ? 6 : 5) * Q_PI / 6);
       
   421     // length of the arc from minValue() to minValue()+pageStep()
       
   422     if (d->maximum > d->minimum + d->pageStep)
       
   423         l = (int)(0.5 + l * d->pageStep / (d->maximum - d->minimum));
       
   424     // length of a singleStep arc
       
   425     l = l * d->singleStep / (d->pageStep ? d->pageStep : 1);
       
   426     if (l < 1)
       
   427         l = 1;
       
   428     // how many times singleStep can be draw in d->target pixels
       
   429     l = (int)(0.5 + d->target / l);
       
   430     // we want notchSize() to be a non-zero multiple of lineStep()
       
   431     if (!l)
       
   432         l = 1;
       
   433     return d->singleStep * l;
       
   434 }
       
   435 
       
   436 void QDial::setNotchTarget(double target)
       
   437 {
       
   438     Q_D(QDial);
       
   439     d->target = target;
       
   440     update();
       
   441 }
       
   442 
       
   443 /*!
       
   444     \property QDial::notchTarget
       
   445     \brief the target number of pixels between notches
       
   446 
       
   447     The notch target is the number of pixels QDial attempts to put
       
   448     between each notch.
       
   449 
       
   450     The actual size may differ from the target size.
       
   451 
       
   452     The default notch target is 3.7 pixels.
       
   453 */
       
   454 qreal QDial::notchTarget() const
       
   455 {
       
   456     Q_D(const QDial);
       
   457     return d->target;
       
   458 }
       
   459 
       
   460 
       
   461 void QDial::setNotchesVisible(bool visible)
       
   462 {
       
   463     Q_D(QDial);
       
   464     d->showNotches = visible;
       
   465     update();
       
   466 }
       
   467 
       
   468 /*!
       
   469     \property QDial::notchesVisible
       
   470     \brief whether the notches are shown
       
   471 
       
   472     If the property is true, a series of notches are drawn around the dial
       
   473     to indicate the range of values available; otherwise no notches are
       
   474     shown.
       
   475 
       
   476     By default, this property is disabled.
       
   477 */
       
   478 bool QDial::notchesVisible() const
       
   479 {
       
   480     Q_D(const QDial);
       
   481     return d->showNotches;
       
   482 }
       
   483 
       
   484 /*!
       
   485   \reimp
       
   486 */
       
   487 
       
   488 QSize QDial::minimumSizeHint() const
       
   489 {
       
   490     return QSize(50, 50);
       
   491 }
       
   492 
       
   493 /*!
       
   494   \reimp
       
   495 */
       
   496 
       
   497 QSize QDial::sizeHint() const
       
   498 {
       
   499     return QSize(100, 100).expandedTo(QApplication::globalStrut());
       
   500 }
       
   501 
       
   502 /*!
       
   503   \reimp
       
   504 */
       
   505 bool QDial::event(QEvent *e)
       
   506 {
       
   507     return QAbstractSlider::event(e);
       
   508 }
       
   509 
       
   510 /*!
       
   511     \fn void QDial::dialPressed();
       
   512 
       
   513     Use QAbstractSlider::sliderPressed() instead.
       
   514 */
       
   515 
       
   516 /*!
       
   517     \fn void QDial::dialMoved(int value);
       
   518 
       
   519     Use QAbstractSlider::sliderMoved() instead.
       
   520 */
       
   521 
       
   522 /*!
       
   523     \fn void QDial::dialReleased();
       
   524 
       
   525     Use QAbstractSlider::sliderReleased() instead.
       
   526 */
       
   527 
       
   528 QT_END_NAMESPACE
       
   529 
       
   530 #endif // QT_NO_DIAL