5f37240d67eec67d5667fd87070e7276939f0bbc
[profile/ivi/qtbase.git] / src / widgets / widgets / qslider.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
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
55 QT_BEGIN_NAMESPACE
56
57 class QSliderPrivate : public QAbstractSliderPrivate
58 {
59     Q_DECLARE_PUBLIC(QSlider)
60 public:
61     QStyle::SubControl pressedControl;
62     int tickInterval;
63     QSlider::TickPosition tickPosition;
64     int clickOffset;
65     void init();
66         void resetLayoutItemMargins();
67     int pixelPosToRangeValue(int pos) const;
68     inline int pick(const QPoint &pt) const;
69
70     QStyle::SubControl newHoverControl(const QPoint &pos);
71     bool updateHoverControl(const QPoint &pos);
72     QStyle::SubControl hoverControl;
73     QRect hoverRect;
74 };
75
76 void QSliderPrivate::init()
77 {
78     Q_Q(QSlider);
79     pressedControl = QStyle::SC_None;
80     tickInterval = 0;
81     tickPosition = QSlider::NoTicks;
82     hoverControl = QStyle::SC_None;
83     q->setFocusPolicy(Qt::FocusPolicy(q->style()->styleHint(QStyle::SH_Button_FocusPolicy)));
84     QSizePolicy sp(QSizePolicy::Expanding, QSizePolicy::Fixed, QSizePolicy::Slider);
85     if (orientation == Qt::Vertical)
86         sp.transpose();
87     q->setSizePolicy(sp);
88     q->setAttribute(Qt::WA_WState_OwnSizePolicy, false);
89         resetLayoutItemMargins();
90 }
91
92 void QSliderPrivate::resetLayoutItemMargins()
93 {
94     Q_Q(QSlider);
95     QStyleOptionSlider opt;
96     q->initStyleOption(&opt);
97     setLayoutItemMargins(QStyle::SE_SliderLayoutItem, &opt);
98 }
99
100 int QSliderPrivate::pixelPosToRangeValue(int pos) const
101 {
102     Q_Q(const QSlider);
103     QStyleOptionSlider opt;
104     q->initStyleOption(&opt);
105     QRect gr = q->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderGroove, q);
106     QRect sr = q->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, q);
107     int sliderMin, sliderMax, sliderLength;
108
109     if (orientation == Qt::Horizontal) {
110         sliderLength = sr.width();
111         sliderMin = gr.x();
112         sliderMax = gr.right() - sliderLength + 1;
113     } else {
114         sliderLength = sr.height();
115         sliderMin = gr.y();
116         sliderMax = gr.bottom() - sliderLength + 1;
117     }
118     return QStyle::sliderValueFromPosition(minimum, maximum, pos - sliderMin,
119                                            sliderMax - sliderMin, opt.upsideDown);
120 }
121
122 inline int QSliderPrivate::pick(const QPoint &pt) const
123 {
124     return orientation == Qt::Horizontal ? pt.x() : pt.y();
125 }
126
127 /*!
128     Initialize \a option with the values from this QSlider. This method
129     is useful for subclasses when they need a QStyleOptionSlider, but don't want
130     to fill in all the information themselves.
131
132     \sa QStyleOption::initFrom()
133 */
134 void QSlider::initStyleOption(QStyleOptionSlider *option) const
135 {
136     if (!option)
137         return;
138
139     Q_D(const QSlider);
140     option->initFrom(this);
141     option->subControls = QStyle::SC_None;
142     option->activeSubControls = QStyle::SC_None;
143     option->orientation = d->orientation;
144     option->maximum = d->maximum;
145     option->minimum = d->minimum;
146     option->tickPosition = (QSlider::TickPosition)d->tickPosition;
147     option->tickInterval = d->tickInterval;
148     option->upsideDown = (d->orientation == Qt::Horizontal) ?
149                      (d->invertedAppearance != (option->direction == Qt::RightToLeft))
150                      : (!d->invertedAppearance);
151     option->direction = Qt::LeftToRight; // we use the upsideDown option instead
152     option->sliderPosition = d->position;
153     option->sliderValue = d->value;
154     option->singleStep = d->singleStep;
155     option->pageStep = d->pageStep;
156     if (d->orientation == Qt::Horizontal)
157         option->state |= QStyle::State_Horizontal;
158 }
159
160 bool QSliderPrivate::updateHoverControl(const QPoint &pos)
161 {
162     Q_Q(QSlider);
163     QRect lastHoverRect = hoverRect;
164     QStyle::SubControl lastHoverControl = hoverControl;
165     bool doesHover = q->testAttribute(Qt::WA_Hover);
166     if (lastHoverControl != newHoverControl(pos) && doesHover) {
167         q->update(lastHoverRect);
168         q->update(hoverRect);
169         return true;
170     }
171     return !doesHover;
172 }
173
174 QStyle::SubControl QSliderPrivate::newHoverControl(const QPoint &pos)
175 {
176     Q_Q(QSlider);
177     QStyleOptionSlider opt;
178     q->initStyleOption(&opt);
179     opt.subControls = QStyle::SC_All;
180     QRect handleRect = q->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, q);
181     QRect grooveRect = q->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderGroove, q);
182     QRect tickmarksRect = q->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderTickmarks, q);
183
184     if (handleRect.contains(pos)) {
185         hoverRect = handleRect;
186         hoverControl = QStyle::SC_SliderHandle;
187     } else if (grooveRect.contains(pos)) {
188         hoverRect = grooveRect;
189         hoverControl = QStyle::SC_SliderGroove;
190     } else if (tickmarksRect.contains(pos)) {
191         hoverRect = tickmarksRect;
192         hoverControl = QStyle::SC_SliderTickmarks;
193     } else {
194         hoverRect = QRect();
195         hoverControl = QStyle::SC_None;
196     }
197
198     return hoverControl;
199 }
200
201 /*!
202     \class QSlider
203     \brief The QSlider widget provides a vertical or horizontal slider.
204
205     \ingroup basicwidgets
206     \inmodule QtWidgets
207
208     The slider is the classic widget for controlling a bounded value.
209     It lets the user move a slider handle along a horizontal or vertical
210     groove and translates the handle's position into an integer value
211     within the legal range.
212
213     QSlider has very few of its own functions; most of the functionality is in
214     QAbstractSlider. The most useful functions are setValue() to set
215     the slider directly to some value; triggerAction() to simulate
216     the effects of clicking (useful for shortcut keys);
217     setSingleStep(), setPageStep() to set the steps; and setMinimum()
218     and setMaximum() to define the range of the scroll bar.
219
220     QSlider provides methods for controlling tickmarks.  You can use
221     setTickPosition() to indicate where you want the tickmarks to be,
222     setTickInterval() to indicate how many of them you want. the
223     currently set tick position and interval can be queried using the
224     tickPosition() and tickInterval() functions, respectively.
225
226     QSlider inherits a comprehensive set of signals:
227     \table
228     \header \li Signal \li Description
229     \row \li \l valueChanged()
230     \li Emitted when the slider's value has changed. The tracking()
231        determines whether this signal is emitted during user
232        interaction.
233     \row \li \l sliderPressed()
234     \li Emitted when the user starts to drag the slider.
235     \row \li \l sliderMoved()
236     \li Emitted when the user drags the slider.
237     \row \li \l sliderReleased()
238     \li Emitted when the user releases the slider.
239     \endtable
240
241     QSlider only provides integer ranges. Note that although
242     QSlider handles very large numbers, it becomes difficult for users
243     to use a slider accurately for very large ranges.
244
245     A slider accepts focus on Tab and provides both a mouse wheel and a
246     keyboard interface. The keyboard interface is the following:
247
248     \list
249         \li Left/Right move a horizontal slider by one single step.
250         \li Up/Down move a vertical slider by one single step.
251         \li PageUp moves up one page.
252         \li PageDown moves down one page.
253         \li Home moves to the start (mininum).
254         \li End moves to the end (maximum).
255     \endlist
256
257     \table 100%
258     \row \li \inlineimage macintosh-slider.png Screenshot of a Macintosh slider
259          \li A slider shown in the \l{Macintosh Style Widget Gallery}{Macintosh widget style}.
260     \row \li \inlineimage windows-slider.png Screenshot of a Windows XP slider
261          \li A slider shown in the \l{Windows XP Style Widget Gallery}{Windows XP widget style}.
262     \row \li \inlineimage plastique-slider.png Screenshot of a Plastique slider
263          \li A slider shown in the \l{Plastique Style Widget Gallery}{Plastique widget style}.
264     \endtable
265
266     \sa QScrollBar, QSpinBox, QDial, {fowler}{GUI Design Handbook: Slider}, {Sliders Example}
267 */
268
269
270 /*!
271     \enum QSlider::TickPosition
272
273     This enum specifies where the tick marks are to be drawn relative
274     to the slider's groove and the handle the user moves.
275
276     \value NoTicks Do not draw any tick marks.
277     \value TicksBothSides Draw tick marks on both sides of the groove.
278     \value TicksAbove Draw tick marks above the (horizontal) slider
279     \value TicksBelow Draw tick marks below the (horizontal) slider
280     \value TicksLeft Draw tick marks to the left of the (vertical) slider
281     \value TicksRight Draw tick marks to the right of the (vertical) slider
282
283     \omitvalue NoMarks
284     \omitvalue Above
285     \omitvalue Left
286     \omitvalue Below
287     \omitvalue Right
288     \omitvalue Both
289 */
290
291
292 /*!
293     Constructs a vertical slider with the given \a parent.
294 */
295 QSlider::QSlider(QWidget *parent)
296     : QAbstractSlider(*new QSliderPrivate, parent)
297 {
298     d_func()->orientation = Qt::Vertical;
299     d_func()->init();
300 }
301
302 /*!
303     Constructs a slider with the given \a parent. The \a orientation
304     parameter determines whether the slider is horizontal or vertical;
305     the valid values are Qt::Vertical and Qt::Horizontal.
306 */
307
308 QSlider::QSlider(Qt::Orientation orientation, QWidget *parent)
309     : QAbstractSlider(*new QSliderPrivate, parent)
310 {
311     d_func()->orientation = orientation;
312     d_func()->init();
313 }
314
315
316 /*!
317     Destroys this slider.
318 */
319 QSlider::~QSlider()
320 {
321 }
322
323 /*!
324     \reimp
325 */
326 void QSlider::paintEvent(QPaintEvent *)
327 {
328     Q_D(QSlider);
329     QPainter p(this);
330     QStyleOptionSlider opt;
331     initStyleOption(&opt);
332
333     opt.subControls = QStyle::SC_SliderGroove | QStyle::SC_SliderHandle;
334     if (d->tickPosition != NoTicks)
335         opt.subControls |= QStyle::SC_SliderTickmarks;
336     if (d->pressedControl) {
337         opt.activeSubControls = d->pressedControl;
338         opt.state |= QStyle::State_Sunken;
339     } else {
340         opt.activeSubControls = d->hoverControl;
341     }
342
343     style()->drawComplexControl(QStyle::CC_Slider, &opt, &p, this);
344 }
345
346 /*!
347     \reimp
348 */
349
350 bool QSlider::event(QEvent *event)
351 {
352     Q_D(QSlider);
353
354     switch(event->type()) {
355     case QEvent::HoverEnter:
356     case QEvent::HoverLeave:
357     case QEvent::HoverMove:
358         if (const QHoverEvent *he = static_cast<const QHoverEvent *>(event))
359             d->updateHoverControl(he->pos());
360         break;
361     case QEvent::StyleChange:
362     case QEvent::MacSizeChange:
363         d->resetLayoutItemMargins();
364         break;
365     default:
366         break;
367     }
368     return QAbstractSlider::event(event);
369 }
370
371 /*!
372     \reimp
373 */
374 void QSlider::mousePressEvent(QMouseEvent *ev)
375 {
376     Q_D(QSlider);
377     if (d->maximum == d->minimum || (ev->buttons() ^ ev->button())) {
378         ev->ignore();
379         return;
380     }
381 #ifdef QT_KEYPAD_NAVIGATION
382     if (QApplication::keypadNavigationEnabled())
383         setEditFocus(true);
384 #endif
385     ev->accept();
386     if ((ev->button() & style()->styleHint(QStyle::SH_Slider_AbsoluteSetButtons)) == ev->button()) {
387         QStyleOptionSlider opt;
388         initStyleOption(&opt);
389         const QRect sliderRect = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
390         const QPoint center = sliderRect.center() - sliderRect.topLeft();
391         // to take half of the slider off for the setSliderPosition call we use the center - topLeft
392
393         setSliderPosition(d->pixelPosToRangeValue(d->pick(ev->pos() - center)));
394         triggerAction(SliderMove);
395         setRepeatAction(SliderNoAction);
396         d->pressedControl = QStyle::SC_SliderHandle;
397         update();
398     } else if ((ev->button() & style()->styleHint(QStyle::SH_Slider_PageSetButtons)) == ev->button()) {
399         QStyleOptionSlider opt;
400         initStyleOption(&opt);
401         d->pressedControl = style()->hitTestComplexControl(QStyle::CC_Slider,
402                                                            &opt, ev->pos(), this);
403         SliderAction action = SliderNoAction;
404         if (d->pressedControl == QStyle::SC_SliderGroove) {
405             const QRect sliderRect = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
406             int pressValue = d->pixelPosToRangeValue(d->pick(ev->pos() - sliderRect.center() + sliderRect.topLeft()));
407             d->pressValue = pressValue;
408             if (pressValue > d->value)
409                 action = SliderPageStepAdd;
410             else if (pressValue < d->value)
411                 action = SliderPageStepSub;
412             if (action) {
413                 triggerAction(action);
414                 setRepeatAction(action);
415             }
416         }
417     } else {
418         ev->ignore();
419         return;
420     }
421
422     if (d->pressedControl == QStyle::SC_SliderHandle) {
423         QStyleOptionSlider opt;
424         initStyleOption(&opt);
425         setRepeatAction(SliderNoAction);
426         QRect sr = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
427         d->clickOffset = d->pick(ev->pos() - sr.topLeft());
428         update(sr);
429         setSliderDown(true);
430     }
431 }
432
433 /*!
434     \reimp
435 */
436 void QSlider::mouseMoveEvent(QMouseEvent *ev)
437 {
438     Q_D(QSlider);
439     if (d->pressedControl != QStyle::SC_SliderHandle) {
440         ev->ignore();
441         return;
442     }
443     ev->accept();
444     int newPosition = d->pixelPosToRangeValue(d->pick(ev->pos()) - d->clickOffset);
445     QStyleOptionSlider opt;
446     initStyleOption(&opt);
447     setSliderPosition(newPosition);
448 }
449
450
451 /*!
452     \reimp
453 */
454 void QSlider::mouseReleaseEvent(QMouseEvent *ev)
455 {
456     Q_D(QSlider);
457     if (d->pressedControl == QStyle::SC_None || ev->buttons()) {
458         ev->ignore();
459         return;
460     }
461     ev->accept();
462     QStyle::SubControl oldPressed = QStyle::SubControl(d->pressedControl);
463     d->pressedControl = QStyle::SC_None;
464     setRepeatAction(SliderNoAction);
465     if (oldPressed == QStyle::SC_SliderHandle)
466         setSliderDown(false);
467     QStyleOptionSlider opt;
468     initStyleOption(&opt);
469     opt.subControls = oldPressed;
470     update(style()->subControlRect(QStyle::CC_Slider, &opt, oldPressed, this));
471 }
472
473 /*!
474     \reimp
475 */
476 QSize QSlider::sizeHint() const
477 {
478     Q_D(const QSlider);
479     ensurePolished();
480     const int SliderLength = 84, TickSpace = 5;
481     QStyleOptionSlider opt;
482     initStyleOption(&opt);
483     int thick = style()->pixelMetric(QStyle::PM_SliderThickness, &opt, this);
484     if (d->tickPosition & TicksAbove)
485         thick += TickSpace;
486     if (d->tickPosition & TicksBelow)
487         thick += TickSpace;
488     int w = thick, h = SliderLength;
489     if (d->orientation == Qt::Horizontal) {
490         w = SliderLength;
491         h = thick;
492     }
493     return style()->sizeFromContents(QStyle::CT_Slider, &opt, QSize(w, h), this).expandedTo(QApplication::globalStrut());
494 }
495
496 /*!
497     \reimp
498 */
499 QSize QSlider::minimumSizeHint() const
500 {
501     Q_D(const QSlider);
502     QSize s = sizeHint();
503     QStyleOptionSlider opt;
504     initStyleOption(&opt);
505     int length = style()->pixelMetric(QStyle::PM_SliderLength, &opt, this);
506     if (d->orientation == Qt::Horizontal)
507         s.setWidth(length);
508     else
509         s.setHeight(length);
510     return s;
511 }
512
513 /*!
514     \property QSlider::tickPosition
515     \brief the tickmark position for this slider
516
517     The valid values are described by the QSlider::TickPosition enum.
518
519     The default value is \l QSlider::NoTicks.
520
521     \sa tickInterval
522 */
523
524 void QSlider::setTickPosition(TickPosition position)
525 {
526     Q_D(QSlider);
527     d->tickPosition = position;
528     d->resetLayoutItemMargins();
529     update();
530     updateGeometry();
531 }
532
533 QSlider::TickPosition QSlider::tickPosition() const
534 {
535     return d_func()->tickPosition;
536 }
537
538 /*!
539     \property QSlider::tickInterval
540     \brief the interval between tickmarks
541
542     This is a value interval, not a pixel interval. If it is 0, the
543     slider will choose between singleStep() and pageStep().
544
545     The default value is 0.
546
547     \sa tickPosition, lineStep(), pageStep()
548 */
549
550 void QSlider::setTickInterval(int ts)
551 {
552     d_func()->tickInterval = qMax(0, ts);
553     update();
554 }
555
556 int QSlider::tickInterval() const
557 {
558     return d_func()->tickInterval;
559 }
560
561 /*!
562     \fn void QSlider::addStep()
563
564     Use setValue() instead.
565 */
566
567 /*!
568     \fn void QSlider::subtractStep()
569
570     Use setValue() instead.
571 */
572
573 /*! \internal
574     Returns the style option for slider.
575 */
576 Q_WIDGETS_EXPORT QStyleOptionSlider qt_qsliderStyleOption(QSlider *slider)
577 {
578     QStyleOptionSlider sliderOption;
579     slider->initStyleOption(&sliderOption);
580     return sliderOption;
581 }
582
583 #endif
584
585 QT_END_NAMESPACE