File indexing completed on 2024-05-12 03:48:30

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