source: trunk/src/gui/widgets/qdial.cpp@ 460

Last change on this file since 460 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 14.0 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
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
63QT_BEGIN_NAMESPACE
64
65class QDialPrivate : public QAbstractSliderPrivate
66{
67 Q_DECLARE_PUBLIC(QDial)
68public:
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
87void 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*/
106void 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
133int 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 \mainclass
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*/
239QDial::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*/
251QDial::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*/
263QDial::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*/
279QDial::~QDial()
280{
281}
282
283/*! \reimp */
284void QDial::resizeEvent(QResizeEvent *e)
285{
286 QWidget::resizeEvent(e);
287}
288
289/*!
290 \reimp
291*/
292
293void 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
305void 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
328void 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
346void 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
364void QDial::sliderChange(SliderChange change)
365{
366 QAbstractSlider::sliderChange(change);
367}
368
369void 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
394bool 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
414int 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
436void 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*/
454qreal QDial::notchTarget() const
455{
456 Q_D(const QDial);
457 return d->target;
458}
459
460
461void 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*/
478bool QDial::notchesVisible() const
479{
480 Q_D(const QDial);
481 return d->showNotches;
482}
483
484/*!
485 \reimp
486*/
487
488QSize QDial::minimumSizeHint() const
489{
490 return QSize(50, 50);
491}
492
493/*!
494 \reimp
495*/
496
497QSize QDial::sizeHint() const
498{
499 return QSize(100, 100).expandedTo(QApplication::globalStrut());
500}
501
502/*!
503 \reimp
504*/
505bool 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
528QT_END_NAMESPACE
529
530#endif // QT_NO_DIAL
Note: See TracBrowser for help on using the repository browser.