File indexing completed on 2024-05-19 15:02:21

0001 #include "qxtspanslider.h"
0002 /****************************************************************************
0003 ** Copyright (c) 2006 - 2011, the LibQxt project.
0004 ** See the Qxt AUTHORS file for a list of authors and copyright holders.
0005 ** All rights reserved.
0006 **
0007 ** Redistribution and use in source and binary forms, with or without
0008 ** modification, are permitted provided that the following conditions are met:
0009 **     * Redistributions of source code must retain the above copyright
0010 **       notice, this list of conditions and the following disclaimer.
0011 **     * Redistributions in binary form must reproduce the above copyright
0012 **       notice, this list of conditions and the following disclaimer in the
0013 **       documentation and/or other materials provided with the distribution.
0014 **     * Neither the name of the LibQxt project nor the
0015 **       names of its contributors may be used to endorse or promote products
0016 **       derived from this software without specific prior written permission.
0017 **
0018 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
0019 ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
0020 ** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0021 ** DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
0022 ** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
0023 ** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
0024 ** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0025 ** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0026 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
0027 ** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0028 **
0029 ** <http://libqxt.org>  <foundation@libqxt.org>
0030 *****************************************************************************/
0031 
0032 #include "qxtspanslider_p.h"
0033 #include <QKeyEvent>
0034 #include <QMouseEvent>
0035 #include <QStylePainter>
0036 #include <QStyleOptionSlider>
0037 
0038 QxtSpanSliderPrivate::QxtSpanSliderPrivate() :
0039     lower(0),
0040     upper(0),
0041     lowerPos(0),
0042     upperPos(0),
0043     offset(0),
0044     position(0),
0045     lastPressed(QxtSpanSlider::NoHandle),
0046     mainControl(QxtSpanSlider::LowerHandle),
0047     lowerPressed(QStyle::SC_None),
0048     upperPressed(QStyle::SC_None),
0049     movement(QxtSpanSlider::FreeMovement),
0050     firstMovement(false),
0051     blockTracking(false) {
0052 }
0053 
0054 void QxtSpanSliderPrivate::initStyleOption(QStyleOptionSlider* option, QxtSpanSlider::SpanHandle handle) const {
0055     const QxtSpanSlider* p = &qxt_p();
0056     p->initStyleOption(option);
0057     option->sliderPosition = (handle == QxtSpanSlider::LowerHandle ? lowerPos : upperPos);
0058     option->sliderValue = (handle == QxtSpanSlider::LowerHandle ? lower : upper);
0059 }
0060 
0061 int QxtSpanSliderPrivate::pixelPosToRangeValue(int pos) const {
0062     QStyleOptionSlider opt;
0063     initStyleOption(&opt);
0064 
0065     int sliderMin = 0;
0066     int sliderMax = 0;
0067     int sliderLength = 0;
0068     const QSlider* p = &qxt_p();
0069     const QRect gr = p->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderGroove, p);
0070     const QRect sr = p->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, p);
0071     if (p->orientation() == Qt::Horizontal) {
0072         sliderLength = sr.width();
0073         sliderMin = gr.x();
0074         sliderMax = gr.right() - sliderLength + 1;
0075     } else {
0076         sliderLength = sr.height();
0077         sliderMin = gr.y();
0078         sliderMax = gr.bottom() - sliderLength + 1;
0079     }
0080     return QStyle::sliderValueFromPosition(p->minimum(), p->maximum(), pos - sliderMin,
0081                                            sliderMax - sliderMin, opt.upsideDown);
0082 }
0083 
0084 void QxtSpanSliderPrivate::handleMousePress(QPoint pos, QStyle::SubControl& control, int value, QxtSpanSlider::SpanHandle handle) {
0085     QStyleOptionSlider opt;
0086     initStyleOption(&opt, handle);
0087     QxtSpanSlider* p = &qxt_p();
0088     const QStyle::SubControl oldControl = control;
0089     control = p->style()->hitTestComplexControl(QStyle::CC_Slider, &opt, pos, p);
0090     const QRect sr = p->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, p);
0091     if (control == QStyle::SC_SliderHandle) {
0092         position = value;
0093         offset = pick(pos - sr.topLeft());
0094         lastPressed = handle;
0095         p->setSliderDown(true);
0096         emit p->sliderPressed(handle);
0097     }
0098     if (control != oldControl)
0099         p->update(sr);
0100 }
0101 
0102 void QxtSpanSliderPrivate::setupPainter(QPainter* painter, Qt::Orientation orientation, qreal x1, qreal y1, qreal x2, qreal y2) const {
0103     QColor highlight = qxt_p().palette().color(QPalette::Highlight);
0104     QLinearGradient gradient(x1, y1, x2, y2);
0105     gradient.setColorAt(0, highlight.darker(120));
0106     gradient.setColorAt(1, highlight.lighter(108));
0107     painter->setBrush(gradient);
0108 
0109     if (orientation == Qt::Horizontal)
0110         painter->setPen(QPen(highlight.darker(130), 0));
0111     else
0112         painter->setPen(QPen(highlight.darker(150), 0));
0113 }
0114 
0115 void QxtSpanSliderPrivate::drawSpan(QStylePainter* painter, QRect rect) const {
0116     QStyleOptionSlider opt;
0117     initStyleOption(&opt);
0118     const QSlider* p = &qxt_p();
0119 
0120     // area
0121     QRect groove = p->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderGroove, p);
0122     if (opt.orientation == Qt::Horizontal)
0123         groove.adjust(0, 0, -1, 0);
0124     else
0125         groove.adjust(0, 0, 0, -1);
0126 
0127     // pen & brush
0128     painter->setPen(QPen(p->palette().color(QPalette::Dark).lighter(110), 0));
0129     if (opt.orientation == Qt::Horizontal)
0130         setupPainter(painter, opt.orientation, groove.center().x(), groove.top(), groove.center().x(), groove.bottom());
0131     else
0132         setupPainter(painter, opt.orientation, groove.left(), groove.center().y(), groove.right(), groove.center().y());
0133 
0134     // draw groove
0135     painter->drawRect(rect.intersected(groove));
0136 }
0137 
0138 void QxtSpanSliderPrivate::drawHandle(QStylePainter* painter, QxtSpanSlider::SpanHandle handle) const {
0139     QStyleOptionSlider opt;
0140     initStyleOption(&opt, handle);
0141     opt.subControls = QStyle::SC_SliderHandle;
0142     QStyle::SubControl pressed = (handle == QxtSpanSlider::LowerHandle ? lowerPressed : upperPressed);
0143     if (pressed == QStyle::SC_SliderHandle) {
0144         opt.activeSubControls = pressed;
0145         opt.state |= QStyle::State_Sunken;
0146     }
0147     painter->drawComplexControl(QStyle::CC_Slider, opt);
0148 }
0149 
0150 void QxtSpanSliderPrivate::triggerAction(QAbstractSlider::SliderAction action, bool main) {
0151     int value = 0;
0152     bool update = true;
0153     bool isUpperHandle = false;
0154     const int min = qxt_p().minimum();
0155     const int max = qxt_p().maximum();
0156     const QxtSpanSlider::SpanHandle altControl = (mainControl == QxtSpanSlider::LowerHandle ? QxtSpanSlider::UpperHandle : QxtSpanSlider::LowerHandle);
0157 
0158     blockTracking = true;
0159 
0160     switch (action) {
0161     case QAbstractSlider::SliderSingleStepAdd:
0162         if ((main && mainControl == QxtSpanSlider::UpperHandle) || (!main && altControl == QxtSpanSlider::UpperHandle)) {
0163             value = qBound(min, upper + qxt_p().singleStep(), max);
0164             isUpperHandle = true;
0165         } else {
0166             value = qBound(min, lower + qxt_p().singleStep(), max);
0167         }
0168         break;
0169     case QAbstractSlider::SliderSingleStepSub:
0170         if ((main && mainControl == QxtSpanSlider::UpperHandle) || (!main && altControl == QxtSpanSlider::UpperHandle)) {
0171             value = qBound(min, upper - qxt_p().singleStep(), max);
0172             isUpperHandle = true;
0173         } else {
0174             value = qBound(min, lower - qxt_p().singleStep(), max);
0175         }
0176         break;
0177     case QAbstractSlider::SliderToMinimum:
0178         value = min;
0179         if ((main && mainControl == QxtSpanSlider::UpperHandle) || (!main && altControl == QxtSpanSlider::UpperHandle))
0180             isUpperHandle = true;
0181         break;
0182     case QAbstractSlider::SliderToMaximum:
0183         value = max;
0184         if ((main && mainControl == QxtSpanSlider::UpperHandle) || (!main && altControl == QxtSpanSlider::UpperHandle))
0185             isUpperHandle = true;
0186         break;
0187     case QAbstractSlider::SliderMove:
0188         // This is handled not here, but in QxtSpanSlider::mouseMoveEvent
0189         // so update not needed
0190         update = false;
0191         break;
0192     case QAbstractSlider::SliderNoAction:
0193         update = false;
0194         break;
0195     case QAbstractSlider::SliderPageStepAdd:
0196     case QAbstractSlider::SliderPageStepSub:
0197         break;
0198     }
0199 
0200     if (update) {
0201         if ( isUpperHandle ) {
0202             if (movement == QxtSpanSlider::NoCrossing)
0203                 value = qMin(value, upper);
0204             else if (movement == QxtSpanSlider::NoOverlapping)
0205                 value = qMin(value, upper - 1);
0206 
0207             if (movement == QxtSpanSlider::FreeMovement && value < lower) {
0208                 swapControls();
0209                 qxt_p().setLowerPosition(value);
0210             } else
0211                 qxt_p().setUpperPosition(value);
0212         } else {
0213             if (movement == QxtSpanSlider::NoCrossing)
0214                 value = qMax(value, lower);
0215             else if (movement == QxtSpanSlider::NoOverlapping)
0216                 value = qMax(value, lower + 1);
0217 
0218             if (movement == QxtSpanSlider::FreeMovement && value > upper) {
0219                 swapControls();
0220                 qxt_p().setUpperPosition(value);
0221             } else
0222                 qxt_p().setLowerPosition(value);
0223         }
0224     }
0225 
0226     blockTracking = false;
0227     qxt_p().setLowerValue(lowerPos);
0228     qxt_p().setUpperValue(upperPos);
0229 }
0230 
0231 void QxtSpanSliderPrivate::swapControls() {
0232     qSwap(lower, upper);
0233     qSwap(lowerPressed, upperPressed);
0234     lastPressed = (lastPressed == QxtSpanSlider::LowerHandle ? QxtSpanSlider::UpperHandle : QxtSpanSlider::LowerHandle);
0235     mainControl = (mainControl == QxtSpanSlider::LowerHandle ? QxtSpanSlider::UpperHandle : QxtSpanSlider::LowerHandle);
0236 }
0237 
0238 void QxtSpanSliderPrivate::updateRange(int min, int max) {
0239     Q_UNUSED(min);
0240     Q_UNUSED(max);
0241     // setSpan() takes care of keeping span in range
0242     qxt_p().setSpan(lower, upper);
0243 }
0244 
0245 void QxtSpanSliderPrivate::movePressedHandle() {
0246     switch (lastPressed) {
0247     case QxtSpanSlider::LowerHandle:
0248         if (lowerPos != lower) {
0249             bool main = (mainControl == QxtSpanSlider::LowerHandle);
0250             triggerAction(QAbstractSlider::SliderMove, main);
0251         }
0252         break;
0253     case QxtSpanSlider::UpperHandle:
0254         if (upperPos != upper) {
0255             bool main = (mainControl == QxtSpanSlider::UpperHandle);
0256             triggerAction(QAbstractSlider::SliderMove, main);
0257         }
0258         break;
0259     case QxtSpanSlider::NoHandle:
0260         break;
0261     }
0262 }
0263 
0264 /*!
0265     \class QxtSpanSlider
0266     \inmodule QxtWidgets
0267     \brief The QxtSpanSlider widget is a QSlider with two handles.
0268 
0269     QxtSpanSlider is a slider with two handles. QxtSpanSlider is
0270     handy for letting user to choose an span between min/max.
0271 
0272     The span color is calculated based on QPalette::Highlight.
0273 
0274     The keys are bound according to the following table:
0275     <table>
0276     <tr><th>Orientation     <th>Key            <th>Handle
0277     <tr><td>Qt::Horizontal  <td>Qt::Key_Left   <td>lower
0278     <tr><td>Qt::Horizontal  <td>Qt::Key_Right  <td>lower
0279     <tr><td>Qt::Horizontal  <td>Qt::Key_Up     <td>upper
0280     <tr><td>Qt::Horizontal  <td>Qt::Key_Down   <td>upper
0281     <tr><td>Qt::Vertical    <td>Qt::Key_Up     <td>lower
0282     <tr><td>Qt::Vertical    <td>Qt::Key_Down   <td>lower
0283     <tr><td>Qt::Vertical    <td>Qt::Key_Left   <td>upper
0284     <tr><td>Qt::Vertical    <td>Qt::Key_Right  <td>upper
0285     </table>
0286 
0287     Keys are bound by the time the slider is created. A key is bound
0288     to same handle for the lifetime of the slider. So even if the handle
0289     representation might change from lower to upper, the same key binding
0290     remains.
0291 
0292     \image qxtspanslider.png "QxtSpanSlider in Plastique style."
0293 
0294     <b>Note:</b> QxtSpanSlider inherits QSlider for implementation specific
0295     reasons. Adjusting any single handle specific properties like
0296     -# QAbstractSlider::sliderPosition
0297     -# QAbstractSlider::value
0298     has no effect. However, all slider specific properties like
0299     -# QAbstractSlider::invertedAppearance
0300     -# QAbstractSlider::invertedControls
0301     -# QAbstractSlider::minimum
0302     -# QAbstractSlider::maximum
0303     -# QAbstractSlider::orientation
0304     -# QAbstractSlider::pageStep
0305     -# QAbstractSlider::singleStep
0306     -# QSlider::tickInterval
0307     -# QSlider::tickPosition
0308     are taken into consideration.
0309  */
0310 
0311 /*!
0312     \enum QxtSpanSlider::HandleMovementMode
0313 
0314     This enum describes the available handle movement modes.
0315 
0316     \value FreeMovement The handles can be moved freely.
0317     \value NoCrossing The handles cannot cross, but they can still overlap each other. The lower and upper values can be the same.
0318     \value NoOverlapping The handles cannot overlap each other. The lower and upper values cannot be the same.
0319  */
0320 
0321 /*!
0322     \enum QxtSpanSlider::SpanHandle
0323 
0324     This enum describes the available span handles.
0325 
0326     \omitvalue NoHandle \omit Internal only (for now). \endomit
0327     \var LowerHandle The lower boundary handle.
0328     \var UpperHandle The upper boundary handle.
0329  */
0330 
0331 /*!
0332     \fn QxtSpanSlider::lowerValueChanged(int lower)
0333 
0334     This signal is emitted whenever the \a lower value has changed.
0335  */
0336 
0337 /*!
0338     \fn QxtSpanSlider::upperValueChanged(int upper)
0339 
0340     This signal is emitted whenever the \a upper value has changed.
0341  */
0342 
0343 /*!
0344     \fn QxtSpanSlider::spanChanged(int lower, int upper)
0345 
0346     This signal is emitted whenever both the \a lower and the \a upper
0347     values have changed ie. the span has changed.
0348  */
0349 
0350 /*!
0351     \fn QxtSpanSlider::lowerPositionChanged(int lower)
0352 
0353     This signal is emitted whenever the \a lower position has changed.
0354  */
0355 
0356 /*!
0357     \fn QxtSpanSlider::upperPositionChanged(int upper)
0358 
0359     This signal is emitted whenever the \a upper position has changed.
0360  */
0361 
0362 /*!
0363     \fn QxtSpanSlider::sliderPressed(SpanHandle handle)
0364 
0365     This signal is emitted whenever the \a handle has been pressed.
0366  */
0367 
0368 /*!
0369     Constructs a new QxtSpanSlider with \a parent.
0370  */
0371 QxtSpanSlider::QxtSpanSlider(QWidget* parent) : QSlider(parent) {
0372     QXT_INIT_PRIVATE(QxtSpanSlider);
0373     connect(this, SIGNAL(rangeChanged(int,int)), &qxt_d(), SLOT(updateRange(int,int)));
0374     connect(this, SIGNAL(sliderReleased()), &qxt_d(), SLOT(movePressedHandle()));
0375 }
0376 
0377 /*!
0378     Constructs a new QxtSpanSlider with \a orientation and \a parent.
0379  */
0380 QxtSpanSlider::QxtSpanSlider(Qt::Orientation orientation, QWidget* parent) : QSlider(orientation, parent) {
0381     QXT_INIT_PRIVATE(QxtSpanSlider);
0382     connect(this, SIGNAL(rangeChanged(int,int)), &qxt_d(), SLOT(updateRange(int,int)));
0383     connect(this, SIGNAL(sliderReleased()), &qxt_d(), SLOT(movePressedHandle()));
0384 }
0385 
0386 /*!
0387     Destructs the span slider.
0388  */
0389 QxtSpanSlider::~QxtSpanSlider() = default;
0390 
0391 /*!
0392     \property QxtSpanSlider::handleMovementMode
0393     \brief the handle movement mode
0394  */
0395 QxtSpanSlider::HandleMovementMode QxtSpanSlider::handleMovementMode() const {
0396     return qxt_d().movement;
0397 }
0398 
0399 void QxtSpanSlider::setHandleMovementMode(QxtSpanSlider::HandleMovementMode mode) {
0400     qxt_d().movement = mode;
0401 }
0402 
0403 /*!
0404     \property QxtSpanSlider::lowerValue
0405     \brief the lower value of the span
0406  */
0407 int QxtSpanSlider::lowerValue() const {
0408     return qMin(qxt_d().lower, qxt_d().upper);
0409 }
0410 
0411 void QxtSpanSlider::setLowerValue(int lower) {
0412     setSpan(lower, qxt_d().upper);
0413 }
0414 
0415 /*!
0416     \property QxtSpanSlider::upperValue
0417     \brief the upper value of the span
0418  */
0419 int QxtSpanSlider::upperValue() const {
0420     return qMax(qxt_d().lower, qxt_d().upper);
0421 }
0422 
0423 void QxtSpanSlider::setUpperValue(int upper) {
0424     setSpan(qxt_d().lower, upper);
0425 }
0426 
0427 /*!
0428     Sets the span from \a lower to \a upper.
0429  */
0430 void QxtSpanSlider::setSpan(int lower, int upper) {
0431     const int low = qBound(minimum(), qMin(lower, upper), maximum());
0432     const int upp = qBound(minimum(), qMax(lower, upper), maximum());
0433     if (low != qxt_d().lower || upp != qxt_d().upper) {
0434         if (low != qxt_d().lower) {
0435             qxt_d().lower = low;
0436             qxt_d().lowerPos = low;
0437             emit lowerValueChanged(low);
0438         }
0439         if (upp != qxt_d().upper) {
0440             qxt_d().upper = upp;
0441             qxt_d().upperPos = upp;
0442             emit upperValueChanged(upp);
0443         }
0444         emit spanChanged(qxt_d().lower, qxt_d().upper);
0445         update();
0446     }
0447 }
0448 
0449 /*!
0450     \property QxtSpanSlider::lowerPosition
0451     \brief the lower position of the span
0452  */
0453 int QxtSpanSlider::lowerPosition() const {
0454     return qxt_d().lowerPos;
0455 }
0456 
0457 void QxtSpanSlider::setLowerPosition(int lower) {
0458     if (qxt_d().lowerPos != lower) {
0459         qxt_d().lowerPos = lower;
0460         if (!hasTracking())
0461             update();
0462         if (isSliderDown())
0463             emit lowerPositionChanged(lower);
0464         if (hasTracking() && !qxt_d().blockTracking) {
0465             bool main = (qxt_d().mainControl == QxtSpanSlider::LowerHandle);
0466             qxt_d().triggerAction(SliderMove, main);
0467         }
0468     }
0469 }
0470 
0471 /*!
0472     \property QxtSpanSlider::upperPosition
0473     \brief the upper position of the span
0474  */
0475 int QxtSpanSlider::upperPosition() const {
0476     return qxt_d().upperPos;
0477 }
0478 
0479 void QxtSpanSlider::setUpperPosition(int upper) {
0480     if (qxt_d().upperPos != upper) {
0481         qxt_d().upperPos = upper;
0482         if (!hasTracking())
0483             update();
0484         if (isSliderDown())
0485             emit upperPositionChanged(upper);
0486         if (hasTracking() && !qxt_d().blockTracking) {
0487             bool main = (qxt_d().mainControl == QxtSpanSlider::UpperHandle);
0488             qxt_d().triggerAction(SliderMove, main);
0489         }
0490     }
0491 }
0492 
0493 /*!
0494     \reimp
0495  */
0496 void QxtSpanSlider::keyPressEvent(QKeyEvent* event) {
0497     QSlider::keyPressEvent(event);
0498 
0499     bool main = true;
0500     SliderAction action = SliderNoAction;
0501     switch (event->key()) {
0502     case Qt::Key_Left:
0503         main   = (orientation() == Qt::Horizontal);
0504         action = !invertedAppearance() ? SliderSingleStepSub : SliderSingleStepAdd;
0505         break;
0506     case Qt::Key_Right:
0507         main   = (orientation() == Qt::Horizontal);
0508         action = !invertedAppearance() ? SliderSingleStepAdd : SliderSingleStepSub;
0509         break;
0510     case Qt::Key_Up:
0511         main   = (orientation() == Qt::Vertical);
0512         action = invertedControls() ? SliderSingleStepSub : SliderSingleStepAdd;
0513         break;
0514     case Qt::Key_Down:
0515         main   = (orientation() == Qt::Vertical);
0516         action = invertedControls() ? SliderSingleStepAdd : SliderSingleStepSub;
0517         break;
0518     case Qt::Key_Home:
0519         main   = (qxt_d().mainControl == QxtSpanSlider::LowerHandle);
0520         action = SliderToMinimum;
0521         break;
0522     case Qt::Key_End:
0523         main   = (qxt_d().mainControl == QxtSpanSlider::UpperHandle);
0524         action = SliderToMaximum;
0525         break;
0526     default:
0527         event->ignore();
0528         break;
0529     }
0530 
0531     if (action)
0532         qxt_d().triggerAction(action, main);
0533 }
0534 
0535 /*!
0536     \reimp
0537  */
0538 void QxtSpanSlider::mousePressEvent(QMouseEvent* event) {
0539     if (minimum() == maximum() || (event->buttons() ^ event->button())) {
0540         event->ignore();
0541         return;
0542     }
0543 
0544     qxt_d().handleMousePress(event->pos(), qxt_d().upperPressed, qxt_d().upper, QxtSpanSlider::UpperHandle);
0545     if (qxt_d().upperPressed != QStyle::SC_SliderHandle)
0546         qxt_d().handleMousePress(event->pos(), qxt_d().lowerPressed, qxt_d().lower, QxtSpanSlider::LowerHandle);
0547 
0548     qxt_d().firstMovement = true;
0549     event->accept();
0550 }
0551 
0552 /*!
0553     \reimp
0554  */
0555 void QxtSpanSlider::mouseMoveEvent(QMouseEvent* event) {
0556     if (qxt_d().lowerPressed != QStyle::SC_SliderHandle && qxt_d().upperPressed != QStyle::SC_SliderHandle) {
0557         event->ignore();
0558         return;
0559     }
0560 
0561     QStyleOptionSlider opt;
0562     qxt_d().initStyleOption(&opt);
0563     const int m = style()->pixelMetric(QStyle::PM_MaximumDragDistance, &opt, this);
0564     int newPosition = qxt_d().pixelPosToRangeValue(qxt_d().pick(event->pos()) - qxt_d().offset);
0565     if (m >= 0) {
0566         const QRect r = rect().adjusted(-m, -m, m, m);
0567         if (!r.contains(event->pos()))
0568             newPosition = qxt_d().position;
0569     }
0570 
0571     // pick the preferred handle on the first movement
0572     if (qxt_d().firstMovement) {
0573         if (qxt_d().lower == qxt_d().upper) {
0574             if (newPosition < lowerValue()) {
0575                 qxt_d().swapControls();
0576                 qxt_d().firstMovement = false;
0577             }
0578         } else
0579             qxt_d().firstMovement = false;
0580     }
0581 
0582     if (qxt_d().lowerPressed == QStyle::SC_SliderHandle) {
0583         if (qxt_d().movement == NoCrossing)
0584             newPosition = qMin(newPosition, upperValue());
0585         else if (qxt_d().movement == NoOverlapping)
0586             newPosition = qMin(newPosition, upperValue() - 1);
0587 
0588         if (qxt_d().movement == FreeMovement && newPosition > qxt_d().upper) {
0589             qxt_d().swapControls();
0590             setUpperPosition(newPosition);
0591         } else
0592             setLowerPosition(newPosition);
0593     } else if (qxt_d().upperPressed == QStyle::SC_SliderHandle) {
0594         if (qxt_d().movement == NoCrossing)
0595             newPosition = qMax(newPosition, lowerValue());
0596         else if (qxt_d().movement == NoOverlapping)
0597             newPosition = qMax(newPosition, lowerValue() + 1);
0598 
0599         if (qxt_d().movement == FreeMovement && newPosition < qxt_d().lower) {
0600             qxt_d().swapControls();
0601             setLowerPosition(newPosition);
0602         } else
0603             setUpperPosition(newPosition);
0604     }
0605     event->accept();
0606 }
0607 
0608 /*!
0609     \reimp
0610  */
0611 void QxtSpanSlider::mouseReleaseEvent(QMouseEvent* event) {
0612     QSlider::mouseReleaseEvent(event);
0613     setSliderDown(false);
0614     qxt_d().lowerPressed = QStyle::SC_None;
0615     qxt_d().upperPressed = QStyle::SC_None;
0616     update();
0617 }
0618 
0619 /*!
0620     \reimp
0621  */
0622 void QxtSpanSlider::paintEvent(QPaintEvent* event) {
0623     Q_UNUSED(event);
0624     QStylePainter painter(this);
0625 
0626     // groove & ticks
0627     QStyleOptionSlider opt;
0628     qxt_d().initStyleOption(&opt);
0629     opt.sliderValue = 0;
0630     opt.sliderPosition = 0;
0631     opt.subControls = QStyle::SC_SliderGroove | QStyle::SC_SliderTickmarks;
0632     painter.drawComplexControl(QStyle::CC_Slider, opt);
0633 
0634     // handle rects
0635     opt.sliderPosition = qxt_d().lowerPos;
0636     const QRect lr = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
0637     const int lrv  = qxt_d().pick(lr.center());
0638     opt.sliderPosition = qxt_d().upperPos;
0639     const QRect ur = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
0640     const int urv  = qxt_d().pick(ur.center());
0641 
0642     // span
0643     const int minv = qMin(lrv, urv);
0644     const int maxv = qMax(lrv, urv);
0645     const QPoint c = QRect(lr.center(), ur.center()).center();
0646     QRect spanRect;
0647     if (orientation() == Qt::Horizontal)
0648         spanRect = QRect(QPoint(minv, c.y() - 2), QPoint(maxv, c.y() + 1));
0649     else
0650         spanRect = QRect(QPoint(c.x() - 2, minv), QPoint(c.x() + 1, maxv));
0651     qxt_d().drawSpan(&painter, spanRect);
0652 
0653     // handles
0654     qxt_d().drawHandle(&painter, QxtSpanSlider::UpperHandle);
0655     qxt_d().drawHandle(&painter, QxtSpanSlider::LowerHandle);
0656 }