source: trunk/src/gui/widgets/qslider.cpp@ 112

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

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

File size: 20.4 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 "qslider.h"
43#ifndef QT_NO_SLIDER
44#ifndef QT_NO_ACCESSIBILITY
45#include "qaccessible.h"
46#endif
47#include "qapplication.h"
48#include "qevent.h"
49#include "qpainter.h"
50#include "qstyle.h"
51#include "qstyleoption.h"
52#include "private/qabstractslider_p.h"
53#include "qdebug.h"
54
55QT_BEGIN_NAMESPACE
56
57class QSliderPrivate : public QAbstractSliderPrivate
58{
59 Q_DECLARE_PUBLIC(QSlider)
60public:
61 QStyle::SubControl pressedControl;
62 int tickInterval;
63 QSlider::TickPosition tickPosition;
64 int clickOffset;
65 int snapBackPosition;
66 void init();
67 void resetLayoutItemMargins();
68 int pixelPosToRangeValue(int pos) const;
69 inline int pick(const QPoint &pt) const;
70
71 QStyle::SubControl newHoverControl(const QPoint &pos);
72 bool updateHoverControl(const QPoint &pos);
73 QStyle::SubControl hoverControl;
74 QRect hoverRect;
75};
76
77void QSliderPrivate::init()
78{
79 Q_Q(QSlider);
80 pressedControl = QStyle::SC_None;
81 tickInterval = 0;
82 tickPosition = QSlider::NoTicks;
83 hoverControl = QStyle::SC_None;
84 q->setFocusPolicy(Qt::FocusPolicy(q->style()->styleHint(QStyle::SH_Button_FocusPolicy)));
85 QSizePolicy sp(QSizePolicy::Expanding, QSizePolicy::Fixed, QSizePolicy::Slider);
86 if (orientation == Qt::Vertical)
87 sp.transpose();
88 q->setSizePolicy(sp);
89 q->setAttribute(Qt::WA_WState_OwnSizePolicy, false);
90 resetLayoutItemMargins();
91}
92
93void QSliderPrivate::resetLayoutItemMargins()
94{
95 Q_Q(QSlider);
96 QStyleOptionSlider opt;
97 q->initStyleOption(&opt);
98 setLayoutItemMargins(QStyle::SE_SliderLayoutItem, &opt);
99}
100
101int QSliderPrivate::pixelPosToRangeValue(int pos) const
102{
103 Q_Q(const QSlider);
104 QStyleOptionSlider opt;
105 q->initStyleOption(&opt);
106 QRect gr = q->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderGroove, q);
107 QRect sr = q->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, q);
108 int sliderMin, sliderMax, sliderLength;
109
110 if (orientation == Qt::Horizontal) {
111 sliderLength = sr.width();
112 sliderMin = gr.x();
113 sliderMax = gr.right() - sliderLength + 1;
114 } else {
115 sliderLength = sr.height();
116 sliderMin = gr.y();
117 sliderMax = gr.bottom() - sliderLength + 1;
118 }
119 return QStyle::sliderValueFromPosition(minimum, maximum, pos - sliderMin,
120 sliderMax - sliderMin, opt.upsideDown);
121}
122
123inline int QSliderPrivate::pick(const QPoint &pt) const
124{
125 return orientation == Qt::Horizontal ? pt.x() : pt.y();
126}
127
128/*!
129 Initialize \a option with the values from this QSlider. This method
130 is useful for subclasses when they need a QStyleOptionSlider, but don't want
131 to fill in all the information themselves.
132
133 \sa QStyleOption::initFrom()
134*/
135void QSlider::initStyleOption(QStyleOptionSlider *option) const
136{
137 if (!option)
138 return;
139
140 Q_D(const QSlider);
141 option->initFrom(this);
142 option->subControls = QStyle::SC_None;
143 option->activeSubControls = QStyle::SC_None;
144 option->orientation = d->orientation;
145 option->maximum = d->maximum;
146 option->minimum = d->minimum;
147 option->tickPosition = (QSlider::TickPosition)d->tickPosition;
148 option->tickInterval = d->tickInterval;
149 option->upsideDown = (d->orientation == Qt::Horizontal) ?
150 (d->invertedAppearance != (option->direction == Qt::RightToLeft))
151 : (!d->invertedAppearance);
152 option->direction = Qt::LeftToRight; // we use the upsideDown option instead
153 option->sliderPosition = d->position;
154 option->sliderValue = d->value;
155 option->singleStep = d->singleStep;
156 option->pageStep = d->pageStep;
157 if (d->orientation == Qt::Horizontal)
158 option->state |= QStyle::State_Horizontal;
159}
160
161bool QSliderPrivate::updateHoverControl(const QPoint &pos)
162{
163 Q_Q(QSlider);
164 QRect lastHoverRect = hoverRect;
165 QStyle::SubControl lastHoverControl = hoverControl;
166 bool doesHover = q->testAttribute(Qt::WA_Hover);
167 if (lastHoverControl != newHoverControl(pos) && doesHover) {
168 q->update(lastHoverRect);
169 q->update(hoverRect);
170 return true;
171 }
172 return !doesHover;
173}
174
175QStyle::SubControl QSliderPrivate::newHoverControl(const QPoint &pos)
176{
177 Q_Q(QSlider);
178 QStyleOptionSlider opt;
179 q->initStyleOption(&opt);
180 opt.subControls = QStyle::SC_All;
181 QRect handleRect = q->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, q);
182 QRect grooveRect = q->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderGroove, q);
183 QRect tickmarksRect = q->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderTickmarks, q);
184
185 if (handleRect.contains(pos)) {
186 hoverRect = handleRect;
187 hoverControl = QStyle::SC_SliderHandle;
188 } else if (grooveRect.contains(pos)) {
189 hoverRect = grooveRect;
190 hoverControl = QStyle::SC_SliderGroove;
191 } else if (tickmarksRect.contains(pos)) {
192 hoverRect = tickmarksRect;
193 hoverControl = QStyle::SC_SliderTickmarks;
194 } else {
195 hoverRect = QRect();
196 hoverControl = QStyle::SC_None;
197 }
198
199 return hoverControl;
200}
201
202/*!
203 \class QSlider
204 \brief The QSlider widget provides a vertical or horizontal slider.
205
206 \ingroup basicwidgets
207 \mainclass
208
209 The slider is the classic widget for controlling a bounded value.
210 It lets the user move a slider handle along a horizontal or vertical
211 groove and translates the handle's position into an integer value
212 within the legal range.
213
214 QSlider has very few of its own functions; most of the functionality is in
215 QAbstractSlider. The most useful functions are setValue() to set
216 the slider directly to some value; triggerAction() to simulate
217 the effects of clicking (useful for shortcut keys);
218 setSingleStep(), setPageStep() to set the steps; and setMinimum()
219 and setMaximum() to define the range of the scroll bar.
220
221 QSlider provides methods for controlling tickmarks. You can use
222 setTickPosition() to indicate where you want the tickmarks to be,
223 setTickInterval() to indicate how many of them you want. the
224 currently set tick position and interval can be queried using the
225 tickPosition() and tickInterval() functions, respectively.
226
227 QSlider inherits a comprehensive set of signals:
228 \table
229 \header \o Signal \o Description
230 \row \o \l valueChanged()
231 \o Emitted when the slider's value has changed. The tracking()
232 determines whether this signal is emitted during user
233 interaction.
234 \row \o \l sliderPressed()
235 \o Emitted when the user starts to drag the slider.
236 \row \o \l sliderMoved()
237 \o Emitted when the user drags the slider.
238 \row \o \l sliderReleased()
239 \o Emitted when the user releases the slider.
240 \endtable
241
242 QSlider only provides integer ranges. Note that although
243 QSlider handles very large numbers, it becomes difficult for users
244 to use a slider accurately for very large ranges.
245
246 A slider accepts focus on Tab and provides both a mouse wheel and a
247 keyboard interface. The keyboard interface is the following:
248
249 \list
250 \o Left/Right move a horizontal slider by one single step.
251 \o Up/Down move a vertical slider by one single step.
252 \o PageUp moves up one page.
253 \o PageDown moves down one page.
254 \o Home moves to the start (mininum).
255 \o End moves to the end (maximum).
256 \endlist
257
258 \table 100%
259 \row \o \inlineimage macintosh-slider.png Screenshot of a Macintosh slider
260 \o A slider shown in the \l{Macintosh Style Widget Gallery}{Macintosh widget style}.
261 \row \o \inlineimage windows-slider.png Screenshot of a Windows XP slider
262 \o A slider shown in the \l{Windows XP Style Widget Gallery}{Windows XP widget style}.
263 \row \o \inlineimage plastique-slider.png Screenshot of a Plastique slider
264 \o A slider shown in the \l{Plastique Style Widget Gallery}{Plastique widget style}.
265 \endtable
266
267 \sa QScrollBar, QSpinBox, QDial, {fowler}{GUI Design Handbook: Slider}, {Sliders Example}
268*/
269
270
271/*!
272 \enum QSlider::TickPosition
273
274 This enum specifies where the tick marks are to be drawn relative
275 to the slider's groove and the handle the user moves.
276
277 \value NoTicks Do not draw any tick marks.
278 \value TicksBothSides Draw tick marks on both sides of the groove.
279 \value TicksAbove Draw tick marks above the (horizontal) slider
280 \value TicksBelow Draw tick marks below the (horizontal) slider
281 \value TicksLeft Draw tick marks to the left of the (vertical) slider
282 \value TicksRight Draw tick marks to the right of the (vertical) slider
283
284 \omitvalue NoMarks
285 \omitvalue Above
286 \omitvalue Left
287 \omitvalue Below
288 \omitvalue Right
289 \omitvalue Both
290*/
291
292
293/*!
294 Constructs a vertical slider with the given \a parent.
295*/
296QSlider::QSlider(QWidget *parent)
297 : QAbstractSlider(*new QSliderPrivate, parent)
298{
299 d_func()->orientation = Qt::Vertical;
300 d_func()->init();
301}
302
303/*!
304 Constructs a slider with the given \a parent. The \a orientation
305 parameter determines whether the slider is horizontal or vertical;
306 the valid values are Qt::Vertical and Qt::Horizontal.
307*/
308
309QSlider::QSlider(Qt::Orientation orientation, QWidget *parent)
310 : QAbstractSlider(*new QSliderPrivate, parent)
311{
312 d_func()->orientation = orientation;
313 d_func()->init();
314}
315
316#ifdef QT3_SUPPORT
317/*!
318 Use QSlider() and QObject::setObjectName() instead.
319
320 \oldcode
321 QSlider *mySlider = new QSlider(parent, name);
322 \newcode
323 QSlider *mySlider = new QSlider(parent);
324 mySlider->setObjectName(name);
325 \endcode
326*/
327QSlider::QSlider(QWidget *parent, const char *name)
328 : QAbstractSlider(*new QSliderPrivate, parent)
329{
330 setObjectName(QString::fromAscii(name));
331 d_func()->orientation = Qt::Vertical;
332 d_func()->init();
333}
334
335/*!
336 Use QSlider() and QObject::setObjectName() instead.
337
338 \oldcode
339 QSlider *mySlider = new QSlider(orientation, parent, name);
340 \newcode
341 QSlider *mySlider = new QSlider(orientation, parent);
342 mySlider->setObjectName(name);
343 \endcode
344*/
345QSlider::QSlider(Qt::Orientation orientation, QWidget *parent, const char *name)
346 : QAbstractSlider(*new QSliderPrivate, parent)
347{
348 setObjectName(QString::fromAscii(name));
349 d_func()->orientation = orientation;
350 d_func()->init();
351}
352
353/*!
354 Use QSlider(), QObject::setObjectName() and the functionality
355 inherited from QAbstractSlider instead.
356
357 \oldcode
358 QSlider *mySlider = new QSlider(minValue, maxValue, pageStep,
359 value, orientation, parent, name);
360 \newcode
361 QSlider *mySlider = new QSlider(orientation, parent);
362 mySlider->setObjectName(name);
363 mySlider->setMinimum(minValue);
364 mySlider->setMaximum(maxValue);
365 mySlider->setPageStep(pageStep);
366 mySlider->setValue(value);
367 \endcode
368*/
369QSlider::QSlider(int minValue, int maxValue, int pageStep, int value, Qt::Orientation orientation,
370 QWidget *parent, const char *name)
371 : QAbstractSlider(*new QSliderPrivate, parent)
372{
373 Q_D(QSlider);
374 setObjectName(QString::fromAscii(name));
375 d->minimum = minValue;
376 d->maximum = maxValue;
377 d->pageStep = pageStep;
378 d->position = d->value = value;
379 d->orientation = orientation;
380 d->init();
381}
382#endif
383
384/*!
385 Destroys this slider.
386*/
387QSlider::~QSlider()
388{
389}
390
391/*!
392 \reimp
393*/
394void QSlider::paintEvent(QPaintEvent *)
395{
396 Q_D(QSlider);
397 QPainter p(this);
398 QStyleOptionSlider opt;
399 initStyleOption(&opt);
400
401 opt.subControls = QStyle::SC_SliderGroove | QStyle::SC_SliderHandle;
402 if (d->tickPosition != NoTicks)
403 opt.subControls |= QStyle::SC_SliderTickmarks;
404 if (d->pressedControl) {
405 opt.activeSubControls = d->pressedControl;
406 opt.state |= QStyle::State_Sunken;
407 } else {
408 opt.activeSubControls = d->hoverControl;
409 }
410
411 style()->drawComplexControl(QStyle::CC_Slider, &opt, &p, this);
412}
413
414/*!
415 \reimp
416*/
417
418bool QSlider::event(QEvent *event)
419{
420 Q_D(QSlider);
421
422 switch(event->type()) {
423 case QEvent::HoverEnter:
424 case QEvent::HoverLeave:
425 case QEvent::HoverMove:
426 if (const QHoverEvent *he = static_cast<const QHoverEvent *>(event))
427 d->updateHoverControl(he->pos());
428 break;
429 case QEvent::StyleChange:
430 case QEvent::MacSizeChange:
431 d->resetLayoutItemMargins();
432 break;
433 default:
434 break;
435 }
436 return QAbstractSlider::event(event);
437}
438
439/*!
440 \reimp
441*/
442void QSlider::mousePressEvent(QMouseEvent *ev)
443{
444 Q_D(QSlider);
445 if (d->maximum == d->minimum || (ev->buttons() ^ ev->button())) {
446 ev->ignore();
447 return;
448 }
449#ifdef QT_KEYPAD_NAVIGATION
450 if (QApplication::keypadNavigationEnabled())
451 setEditFocus(true);
452#endif
453 ev->accept();
454 if ((ev->button() & style()->styleHint(QStyle::SH_Slider_AbsoluteSetButtons)) == ev->button()) {
455 QStyleOptionSlider opt;
456 initStyleOption(&opt);
457 const QRect sliderRect = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
458 const QPoint center = sliderRect.center() - sliderRect.topLeft();
459 // to take half of the slider off for the setSliderPosition call we use the center - topLeft
460
461 setSliderPosition(d->pixelPosToRangeValue(d->pick(ev->pos() - center)));
462 triggerAction(SliderMove);
463 setRepeatAction(SliderNoAction);
464 d->pressedControl = QStyle::SC_SliderHandle;
465 update();
466 } else if ((ev->button() & style()->styleHint(QStyle::SH_Slider_PageSetButtons)) == ev->button()) {
467 QStyleOptionSlider opt;
468 initStyleOption(&opt);
469 d->pressedControl = style()->hitTestComplexControl(QStyle::CC_Slider,
470 &opt, ev->pos(), this);
471 SliderAction action = SliderNoAction;
472 if (d->pressedControl == QStyle::SC_SliderGroove) {
473 const QRect sliderRect = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
474 int pressValue = d->pixelPosToRangeValue(d->pick(ev->pos() - sliderRect.center() + sliderRect.topLeft()));
475 d->pressValue = pressValue;
476 if (pressValue > d->value)
477 action = SliderPageStepAdd;
478 else if (pressValue < d->value)
479 action = SliderPageStepSub;
480 if (action) {
481 triggerAction(action);
482 setRepeatAction(action);
483 }
484 }
485 } else {
486 ev->ignore();
487 return;
488 }
489
490 if (d->pressedControl == QStyle::SC_SliderHandle) {
491 QStyleOptionSlider opt;
492 initStyleOption(&opt);
493 setRepeatAction(SliderNoAction);
494 QRect sr = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
495 d->clickOffset = d->pick(ev->pos() - sr.topLeft());
496 d->snapBackPosition = d->position;
497 update(sr);
498 setSliderDown(true);
499 }
500}
501
502/*!
503 \reimp
504*/
505void QSlider::mouseMoveEvent(QMouseEvent *ev)
506{
507 Q_D(QSlider);
508 if (d->pressedControl != QStyle::SC_SliderHandle) {
509 ev->ignore();
510 return;
511 }
512 ev->accept();
513 int newPosition = d->pixelPosToRangeValue(d->pick(ev->pos()) - d->clickOffset);
514 QStyleOptionSlider opt;
515 initStyleOption(&opt);
516 int m = style()->pixelMetric(QStyle::PM_MaximumDragDistance, &opt, this);
517 if (m >= 0) {
518 QRect r = rect();
519 r.adjust(-m, -m, m, m);
520 if (!r.contains(ev->pos())) {
521 newPosition = d->snapBackPosition;
522 }
523 }
524 setSliderPosition(newPosition);
525}
526
527
528/*!
529 \reimp
530*/
531void QSlider::mouseReleaseEvent(QMouseEvent *ev)
532{
533 Q_D(QSlider);
534 if (d->pressedControl == QStyle::SC_None || ev->buttons()) {
535 ev->ignore();
536 return;
537 }
538 ev->accept();
539 QStyle::SubControl oldPressed = QStyle::SubControl(d->pressedControl);
540 d->pressedControl = QStyle::SC_None;
541 setRepeatAction(SliderNoAction);
542 if (oldPressed == QStyle::SC_SliderHandle)
543 setSliderDown(false);
544 QStyleOptionSlider opt;
545 initStyleOption(&opt);
546 opt.subControls = oldPressed;
547 update(style()->subControlRect(QStyle::CC_Slider, &opt, oldPressed, this));
548}
549
550/*!
551 \reimp
552*/
553QSize QSlider::sizeHint() const
554{
555 Q_D(const QSlider);
556 ensurePolished();
557 const int SliderLength = 84, TickSpace = 5;
558 QStyleOptionSlider opt;
559 initStyleOption(&opt);
560 int thick = style()->pixelMetric(QStyle::PM_SliderThickness, &opt, this);
561 if (d->tickPosition & TicksAbove)
562 thick += TickSpace;
563 if (d->tickPosition & TicksBelow)
564 thick += TickSpace;
565 int w = thick, h = SliderLength;
566 if (d->orientation == Qt::Horizontal) {
567 w = SliderLength;
568 h = thick;
569 }
570 return style()->sizeFromContents(QStyle::CT_Slider, &opt, QSize(w, h), this).expandedTo(QApplication::globalStrut());
571}
572
573/*!
574 \reimp
575*/
576QSize QSlider::minimumSizeHint() const
577{
578 Q_D(const QSlider);
579 QSize s = sizeHint();
580 QStyleOptionSlider opt;
581 initStyleOption(&opt);
582 int length = style()->pixelMetric(QStyle::PM_SliderLength, &opt, this);
583 if (d->orientation == Qt::Horizontal)
584 s.setWidth(length);
585 else
586 s.setHeight(length);
587 return s;
588}
589
590/*!
591 \property QSlider::tickPosition
592 \brief the tickmark position for this slider
593
594 The valid values are described by the QSlider::TickPosition enum.
595
596 The default value is \l QSlider::NoTicks.
597
598 \sa tickInterval
599*/
600
601void QSlider::setTickPosition(TickPosition position)
602{
603 Q_D(QSlider);
604 d->tickPosition = position;
605 d->resetLayoutItemMargins();
606 update();
607 updateGeometry();
608}
609
610QSlider::TickPosition QSlider::tickPosition() const
611{
612 return d_func()->tickPosition;
613}
614
615/*!
616 \fn TickPosition QSlider::tickmarks() const
617 \compat
618
619 Use tickPosition() instead.
620*/
621
622/*!
623 \fn QSlider::setTickmarks(TickPosition position)
624 \compat
625
626 Use setTickPosition() instead.
627*/
628
629/*!
630 \property QSlider::tickInterval
631 \brief the interval between tickmarks
632
633 This is a value interval, not a pixel interval. If it is 0, the
634 slider will choose between lineStep() and pageStep().
635
636 The default value is 0.
637
638 \sa tickPosition, lineStep(), pageStep()
639*/
640
641void QSlider::setTickInterval(int ts)
642{
643 d_func()->tickInterval = qMax(0, ts);
644 update();
645}
646
647int QSlider::tickInterval() const
648{
649 return d_func()->tickInterval;
650}
651
652/*!
653 \fn void QSlider::addStep()
654
655 Use setValue() instead.
656*/
657
658/*!
659 \fn void QSlider::subtractStep()
660
661 Use setValue() instead.
662*/
663
664/*! \internal
665 Returns the style option for slider.
666*/
667Q_GUI_EXPORT QStyleOptionSlider qt_qsliderStyleOption(QSlider *slider)
668{
669 QStyleOptionSlider sliderOption;
670 slider->initStyleOption(&sliderOption);
671 return sliderOption;
672}
673
674#endif
675
676QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.