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

Last change on this file since 846 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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