Ok, the long awaited config wizard is here (at least in a very basic state). There...
[quassel.git] / src / contrib / libqxt-2007-10-24 / src / gui / qxtspanslider.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) Qxt Foundation. Some rights reserved.
4 **
5 ** This file is part of the QxtGui module of the Qt eXTension library
6 **
7 ** This library is free software; you can redistribute it and/or modify it
8 ** under the terms of th Common Public License, version 1.0, as published by
9 ** IBM.
10 **
11 ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY
12 ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
13 ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR
14 ** FITNESS FOR A PARTICULAR PURPOSE.
15 **
16 ** You should have received a copy of the CPL along with this file.
17 ** See the LICENSE file and the cpl1.0.txt file included with the source
18 ** distribution for more information. If you did not receive a copy of the
19 ** license, contact the Qxt Foundation.
20 **
21 ** <http://libqxt.sourceforge.net>  <foundation@libqxt.org>
22 **
23 ****************************************************************************/
24 #include "qxtspanslider.h"
25 #include "qxtspanslider_p.h"
26 #include <QKeyEvent>
27 #include <QMouseEvent>
28 #include <QApplication>
29 #include <QStylePainter>
30 #include <QStyleOptionSlider>
31
32 QxtSpanSliderPrivate::QxtSpanSliderPrivate()
33         : lower(0),
34         upper(0),
35         offset(0),
36         position(0),
37         lastPressed(NoHandle),
38         mainControl(LowerHandle),
39         lowerPressed(QStyle::SC_None),
40         upperPressed(QStyle::SC_None)
41 {}
42
43 // TODO: get rid of this in Qt 4.3
44 void QxtSpanSliderPrivate::initStyleOption(QStyleOptionSlider* option, SpanHandle handle) const
45 {
46     if (!option)
47         return;
48
49     const QSlider* p = &qxt_p();
50     option->initFrom(p);
51     option->subControls = QStyle::SC_None;
52     option->activeSubControls = QStyle::SC_None;
53     option->orientation = p->orientation();
54     option->maximum = p->maximum();
55     option->minimum = p->minimum();
56     option->tickPosition = p->tickPosition();
57     option->tickInterval = p->tickInterval();
58     option->upsideDown = (p->orientation() == Qt::Horizontal) ?
59                          (p->invertedAppearance() != (option->direction == Qt::RightToLeft)) : (!p->invertedAppearance());
60     option->direction = Qt::LeftToRight; // we use the upsideDown option instead
61     option->sliderPosition = (handle == LowerHandle ? lower : upper);
62     option->sliderValue = (handle == LowerHandle ? lower : upper);
63     option->singleStep = p->singleStep();
64     option->pageStep = p->pageStep();
65     if (p->orientation() == Qt::Horizontal)
66         option->state |= QStyle::State_Horizontal;
67 }
68
69 int QxtSpanSliderPrivate::pixelPosToRangeValue(int pos) const
70 {
71     QStyleOptionSlider opt;
72     initStyleOption(&opt);
73
74     int sliderMin = 0;
75     int sliderMax = 0;
76     int sliderLength = 0;
77     const QSlider* p = &qxt_p();
78     const QRect gr = p->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderGroove, p);
79     const QRect sr = p->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, p);
80     if (p->orientation() == Qt::Horizontal)
81     {
82         sliderLength = sr.width();
83         sliderMin = gr.x();
84         sliderMax = gr.right() - sliderLength + 1;
85     }
86     else
87     {
88         sliderLength = sr.height();
89         sliderMin = gr.y();
90         sliderMax = gr.bottom() - sliderLength + 1;
91     }
92     return QStyle::sliderValueFromPosition(p->minimum(), p->maximum(), pos - sliderMin,
93                                            sliderMax - sliderMin, opt.upsideDown);
94 }
95
96 void QxtSpanSliderPrivate::handleMousePress(const QPoint& pos, QStyle::SubControl& control, int value, SpanHandle handle)
97 {
98     QStyleOptionSlider opt;
99     initStyleOption(&opt, handle);
100     QSlider* p = &qxt_p();
101     const QStyle::SubControl oldControl = control;
102     control = p->style()->hitTestComplexControl(QStyle::CC_Slider, &opt, pos, p);
103     const QRect sr = p->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, p);
104     if (control == QStyle::SC_SliderHandle)
105     {
106         position = value;
107         offset = pick(pos - sr.topLeft());
108         lastPressed = handle;
109         p->setSliderDown(true);
110     }
111     if (control != oldControl)
112         p->update(sr);
113 }
114
115 void QxtSpanSliderPrivate::setupPainter(QPainter* painter, Qt::Orientation orientation, qreal x1, qreal y1, qreal x2, qreal y2) const
116 {
117     QColor highlight = qxt_p().palette().color(QPalette::Highlight);
118     QLinearGradient gradient(x1, y1, x2, y2);
119     gradient.setColorAt(0, highlight.dark(120));
120     gradient.setColorAt(1, highlight.light(108));
121     painter->setBrush(gradient);
122
123     if (orientation == Qt::Horizontal)
124         painter->setPen(QPen(highlight.dark(130), 0));
125     else
126         painter->setPen(QPen(highlight.dark(150), 0));
127 }
128
129 void QxtSpanSliderPrivate::drawSpan(QStylePainter* painter, const QRect& rect) const
130 {
131     QStyleOptionSlider opt;
132     initStyleOption(&opt);
133     const QSlider* p = &qxt_p();
134
135     // area
136     QRect groove = p->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderGroove, p);
137     if (opt.orientation == Qt::Horizontal)
138         groove.adjust(0, 0, -1, 0);
139     else
140         groove.adjust(0, 0, 0, -1);
141
142     // pen & brush
143     painter->setPen(QPen(p->palette().color(QPalette::Dark).light(110), 0));
144     if (opt.orientation == Qt::Horizontal)
145         setupPainter(painter, opt.orientation, groove.center().x(), groove.top(), groove.center().x(), groove.bottom());
146     else
147         setupPainter(painter, opt.orientation, groove.left(), groove.center().y(), groove.right(), groove.center().y());
148
149     // draw groove
150 #if QT_VERSION >= 0x040200
151     painter->drawRect(rect.intersected(groove));
152 #else // QT_VERSION < 0x040200
153     painter->drawRect(rect.intersect(groove));
154 #endif // QT_VERSION
155 }
156
157 void QxtSpanSliderPrivate::drawHandle(QStylePainter* painter, SpanHandle handle) const
158 {
159     QStyleOptionSlider opt;
160     initStyleOption(&opt, handle);
161     opt.subControls = QStyle::SC_SliderHandle;
162     QStyle::SubControl pressed = (handle == LowerHandle ? lowerPressed : upperPressed);
163     if (pressed == QStyle::SC_SliderHandle)
164     {
165         opt.activeSubControls = pressed;
166         opt.state |= QStyle::State_Sunken;
167     }
168     painter->drawComplexControl(QStyle::CC_Slider, opt);
169 }
170
171 void QxtSpanSliderPrivate::triggerAction(QAbstractSlider::SliderAction action, bool main)
172 {
173     int value = 0;
174     bool up = false;
175     const int min = qxt_p().minimum();
176     const int max = qxt_p().maximum();
177     const SpanHandle altControl = (mainControl == LowerHandle ? UpperHandle : LowerHandle);
178     switch (action)
179     {
180     case QAbstractSlider::SliderSingleStepAdd:
181         if ((main && mainControl == UpperHandle) || (!main && altControl == UpperHandle))
182         {
183             value = qBound(min, upper + qxt_p().singleStep(), max);
184             up = true;
185             break;
186         }
187         value = qBound(min, lower + qxt_p().singleStep(), max);
188         break;
189     case QAbstractSlider::SliderSingleStepSub:
190         if ((main && mainControl == UpperHandle) || (!main && altControl == UpperHandle))
191         {
192             value = qBound(min, upper - qxt_p().singleStep(), max);
193             up = true;
194             break;
195         }
196         value = qBound(min, lower - qxt_p().singleStep(), max);
197         break;
198     case QAbstractSlider::SliderToMinimum:
199         value = min;
200         if ((main && mainControl == UpperHandle) || (!main && altControl == UpperHandle))
201             up = true;
202         break;
203     case QAbstractSlider::SliderToMaximum:
204         value = max;
205         if ((main && mainControl == UpperHandle) || (!main && altControl == UpperHandle))
206             up = true;
207         break;
208     default:
209         qWarning("QxtSpanSliderPrivate::triggerAction: Unknown action");
210         break;
211     }
212
213     if (!up)
214     {
215         if (value > upper)
216         {
217             swapControls();
218             qxt_p().setUpperValue(value);
219         }
220         else
221         {
222             qxt_p().setLowerValue(value);
223         }
224     }
225     else
226     {
227         if (value < lower)
228         {
229             swapControls();
230             qxt_p().setLowerValue(value);
231         }
232         else
233         {
234             qxt_p().setUpperValue(value);
235         }
236     }
237 }
238
239 void QxtSpanSliderPrivate::swapControls()
240 {
241     qSwap(lower, upper);
242     qSwap(lowerPressed, upperPressed);
243     lastPressed = (lastPressed == LowerHandle ? UpperHandle : LowerHandle);
244     mainControl = (mainControl == LowerHandle ? UpperHandle : LowerHandle);
245 }
246
247 void QxtSpanSliderPrivate::updateRange(int min, int max)
248 {
249     Q_UNUSED(min);
250     Q_UNUSED(max);
251     // setSpan() takes care of keeping span in range
252     qxt_p().setSpan(lower, upper);
253 }
254
255 /*!
256     \class QxtSpanSlider QxtSpanSlider
257     \ingroup QxtGui
258     \brief A QSlider with two handles.
259
260     QxtSpanSlider is a slider with two handles. QxtSpanSlider is
261     handy for letting user to choose an span between min/max.
262
263     The span color is calculated based on \b QPalette::Highlight.
264
265     The keys are bound according to the following table:
266     <table>
267     <tr><td><b>Orientation</b></td><td><b>Key</b></td><td><b>Handle</b></td></tr>
268     <tr><td>Qt::Horizontal</td><td>Qt::Key_Left</td><td>lower</td></tr>
269     <tr><td>Qt::Horizontal</td><td>Qt::Key_Right</td><td>lower</td></tr>
270     <tr><td>Qt::Horizontal</td><td>Qt::Key_Up</td><td>upper</td></tr>
271     <tr><td>Qt::Horizontal</td><td>Qt::Key_Down</td><td>upper</td></tr>
272     <tr><td>Qt::Vertical</td><td>Qt::Key_Up</td><td>lower</td></tr>
273     <tr><td>Qt::Vertical</td><td>Qt::Key_Down</td><td>lower</td></tr>
274     <tr><td>Qt::Vertical</td><td>Qt::Key_Left</td><td>upper</td></tr>
275     <tr><td>Qt::Vertical</td><td>Qt::Key_Right</td><td>upper</td></tr>
276     </table>
277
278     Keys are bound by the time the slider is created. A key is bound
279     to same handle for the lifetime of the slider. So even if the handle
280     representation might change from lower to upper, the same key binding
281     remains.
282
283     \image html qxtspanslider.png "QxtSpanSlider in Plastique style."
284
285     \note QxtSpanSlider inherits \b QSlider for implementation specific
286     reasons. Adjusting any single handle specific properties like
287     <ul>
288     <li>\b QAbstractSlider::sliderPosition</li>
289     <li>\b QAbstractSlider::value</li>
290     </ul>
291     has no effect. However, all slider specific properties like
292     <ul>
293     <li>\b QAbstractSlider::invertedAppearance</li>
294     <li>\b QAbstractSlider::invertedControls</li>
295     <li>\b QAbstractSlider::minimum</li>
296     <li>\b QAbstractSlider::maximum</li>
297     <li>\b QAbstractSlider::orientation</li>
298     <li>\b QAbstractSlider::pageStep</li>
299     <li>\b QAbstractSlider::singleStep</li>
300     <li>\b QSlider::tickInterval</li>
301     <li>\b QSlider::tickPosition</li>
302     </ul>
303     are taken into consideration.
304  */
305
306 /*!
307     \fn QxtSpanSlider::lowerValueChanged(int lower)
308
309     This signal is emitted whenever the lower value has changed.
310  */
311
312 /*!
313     \fn QxtSpanSlider::upperValueChanged(int upper)
314
315     This signal is emitted whenever the upper value has changed.
316  */
317
318 /*!
319     \fn QxtSpanSlider::spanChanged(int lower, int upper)
320
321     This signal is emitted whenever the span has changed.
322  */
323
324 /*!
325     Constructs a new QxtSpanSlider with \a parent.
326  */
327 QxtSpanSlider::QxtSpanSlider(QWidget* parent) : QSlider(parent)
328 {
329     QXT_INIT_PRIVATE(QxtSpanSlider);
330     connect(this, SIGNAL(rangeChanged(int, int)), &qxt_d(), SLOT(updateRange(int, int)));
331 }
332
333 /*!
334     Constructs a new QxtSpanSlider with \a orientation and \a parent.
335  */
336 QxtSpanSlider::QxtSpanSlider(Qt::Orientation orientation, QWidget* parent) : QSlider(orientation, parent)
337 {
338     QXT_INIT_PRIVATE(QxtSpanSlider);
339     connect(this, SIGNAL(rangeChanged(int, int)), &qxt_d(), SLOT(updateRange(int, int)));
340 }
341
342 /*!
343     Destructs the slider.
344  */
345 QxtSpanSlider::~QxtSpanSlider()
346 {}
347
348 /*!
349     \property QxtSpanSlider::lowerValue
350     \brief This property holds the lower value of the span
351  */
352 int QxtSpanSlider::lowerValue() const
353 {
354     return qMin(qxt_d().lower, qxt_d().upper);
355 }
356
357 void QxtSpanSlider::setLowerValue(int lower)
358 {
359     setSpan(lower, qxt_d().upper);
360 }
361
362 /*!
363     \property QxtSpanSlider::upperValue
364     \brief This property holds the upper value of the span
365  */
366 int QxtSpanSlider::upperValue() const
367 {
368     return qMax(qxt_d().lower, qxt_d().upper);
369 }
370
371 void QxtSpanSlider::setUpperValue(int upper)
372 {
373     setSpan(qxt_d().lower, upper);
374 }
375
376 /*!
377     Sets the span from \a lower to \a upper.
378     \sa upperValue, lowerValue
379  */
380 void QxtSpanSlider::setSpan(int lower, int upper)
381 {
382     const int low = qBound(minimum(), qMin(lower, upper), maximum());
383     const int upp = qBound(minimum(), qMax(lower, upper), maximum());
384     if (low != qxt_d().lower || upp != qxt_d().upper)
385     {
386         if (low != qxt_d().lower)
387         {
388             qxt_d().lower = low;
389             emit lowerValueChanged(low);
390         }
391         if (upp != qxt_d().upper)
392         {
393             qxt_d().upper = upp;
394             emit upperValueChanged(upp);
395         }
396         emit spanChanged(qxt_d().lower, qxt_d().upper);
397         update();
398     }
399 }
400
401 void QxtSpanSlider::keyPressEvent(QKeyEvent* event)
402 {
403     QSlider::keyPressEvent(event);
404
405     bool main = true;
406     SliderAction action = SliderNoAction;
407     switch (event->key())
408     {
409     case Qt::Key_Left:
410         main   = (orientation() == Qt::Horizontal);
411         action = !invertedAppearance() ? SliderSingleStepSub : SliderSingleStepAdd;
412         break;
413     case Qt::Key_Right:
414         main   = (orientation() == Qt::Horizontal);
415         action = !invertedAppearance() ? SliderSingleStepAdd : SliderSingleStepSub;
416         break;
417     case Qt::Key_Up:
418         main   = (orientation() == Qt::Vertical);
419         action = invertedControls() ? SliderSingleStepSub : SliderSingleStepAdd;
420         break;
421     case Qt::Key_Down:
422         main   = (orientation() == Qt::Vertical);
423         action = invertedControls() ? SliderSingleStepAdd : SliderSingleStepSub;
424         break;
425     case Qt::Key_Home:
426         main   = (qxt_d().mainControl == QxtSpanSliderPrivate::LowerHandle);
427         action = SliderToMinimum;
428         break;
429     case Qt::Key_End:
430         main   = (qxt_d().mainControl == QxtSpanSliderPrivate::UpperHandle);
431         action = SliderToMaximum;
432         break;
433     default:
434         event->ignore();
435         break;
436     }
437
438     if (action)
439         qxt_d().triggerAction(action, main);
440 }
441
442 void QxtSpanSlider::mousePressEvent(QMouseEvent* event)
443 {
444     if (minimum() == maximum() || (event->buttons() ^ event->button()))
445     {
446         event->ignore();
447         return;
448     }
449
450     qxt_d().handleMousePress(event->pos(), qxt_d().upperPressed, qxt_d().upper, QxtSpanSliderPrivate::UpperHandle);
451     if (qxt_d().upperPressed != QStyle::SC_SliderHandle)
452         qxt_d().handleMousePress(event->pos(), qxt_d().lowerPressed, qxt_d().lower, QxtSpanSliderPrivate::LowerHandle);
453
454     event->accept();
455 }
456
457 void QxtSpanSlider::mouseMoveEvent(QMouseEvent* event)
458 {
459     if (qxt_d().lowerPressed != QStyle::SC_SliderHandle && qxt_d().upperPressed != QStyle::SC_SliderHandle)
460     {
461         event->ignore();
462         return;
463     }
464
465     QStyleOptionSlider opt;
466     qxt_d().initStyleOption(&opt);
467     const int m = style()->pixelMetric(QStyle::PM_MaximumDragDistance, &opt, this);
468     int newPosition = qxt_d().pixelPosToRangeValue(qxt_d().pick(event->pos()) - qxt_d().offset);
469     if (m >= 0)
470     {
471         const QRect r = rect().adjusted(-m, -m, m, m);
472         if (!r.contains(event->pos()))
473         {
474             newPosition = qxt_d().position;
475         }
476     }
477
478     if (qxt_d().lowerPressed == QStyle::SC_SliderHandle)
479     {
480         if (newPosition > qxt_d().upper)
481         {
482             qxt_d().swapControls();
483             setUpperValue(newPosition);
484         }
485         else
486         {
487             setLowerValue(newPosition);
488         }
489     }
490     else if (qxt_d().upperPressed == QStyle::SC_SliderHandle)
491     {
492         if (newPosition < qxt_d().lower)
493         {
494             qxt_d().swapControls();
495             setLowerValue(newPosition);
496         }
497         else
498         {
499             setUpperValue(newPosition);
500         }
501     }
502     event->accept();
503 }
504
505 void QxtSpanSlider::mouseReleaseEvent(QMouseEvent* event)
506 {
507     QSlider::mouseReleaseEvent(event);
508     qxt_d().lowerPressed = QStyle::SC_None;
509     qxt_d().upperPressed = QStyle::SC_None;
510     update();
511 }
512
513 void QxtSpanSlider::paintEvent(QPaintEvent* event)
514 {
515     Q_UNUSED(event);
516     QStylePainter painter(this);
517
518     // ticks
519     QStyleOptionSlider opt;
520     qxt_d().initStyleOption(&opt);
521     opt.subControls = QStyle::SC_SliderTickmarks;
522     painter.drawComplexControl(QStyle::CC_Slider, opt);
523
524     // groove
525     opt.sliderPosition = 0;
526     opt.subControls = QStyle::SC_SliderGroove;
527     painter.drawComplexControl(QStyle::CC_Slider, opt);
528
529     // handle rects
530     opt.sliderPosition = qxt_d().lower;
531     const QRect lr = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
532     const int lrv  = qxt_d().pick(lr.center());
533     opt.sliderPosition = qxt_d().upper;
534     const QRect ur = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
535     const int urv  = qxt_d().pick(ur.center());
536
537     // span
538     const int minv = qMin(lrv, urv);
539     const int maxv = qMax(lrv, urv);
540     const QPoint c = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderGroove, this).center();
541     QRect spanRect;
542     if (orientation() == Qt::Horizontal)
543         spanRect = QRect(QPoint(minv, c.y()-2), QPoint(maxv, c.y()+1));
544     else
545         spanRect = QRect(QPoint(c.x()-2, minv), QPoint(c.x()+1, maxv));
546     qxt_d().drawSpan(&painter, spanRect);
547
548     // handles
549     switch (qxt_d().lastPressed)
550     {
551     case QxtSpanSliderPrivate::LowerHandle:
552         qxt_d().drawHandle(&painter, QxtSpanSliderPrivate::UpperHandle);
553         qxt_d().drawHandle(&painter, QxtSpanSliderPrivate::LowerHandle);
554         break;
555     case QxtSpanSliderPrivate::UpperHandle:
556     default:
557         qxt_d().drawHandle(&painter, QxtSpanSliderPrivate::LowerHandle);
558         qxt_d().drawHandle(&painter, QxtSpanSliderPrivate::UpperHandle);
559         break;
560     }
561 }