File indexing completed on 2024-04-14 14:08:53

0001 /*
0002     SPDX-FileCopyrightText: Kitware Inc.
0003     SPDX-License-Identifier: Apache-2.0
0004 */
0005 
0006 // Qt includes
0007 #include <QDebug>
0008 #include <QMouseEvent>
0009 #include <QKeyEvent>
0010 #include <QStyleOptionSlider>
0011 #include <QApplication>
0012 #include <QStylePainter>
0013 #include <QStyle>
0014 #include <QToolTip>
0015 
0016 // CTK includes
0017 #include "ctkrangeslider.h"
0018 
0019 class ctkRangeSliderPrivate
0020 {
0021   Q_DECLARE_PUBLIC(ctkRangeSlider)
0022 protected:
0023   ctkRangeSlider* const q_ptr;
0024 public:
0025   /// Boolean indicates the selected handle
0026   ///   True for the minimum range handle, false for the maximum range handle
0027   enum Handle {
0028     NoHandle = 0x0,
0029     MinimumHandle = 0x1,
0030     MaximumHandle = 0x2
0031   };
0032   Q_DECLARE_FLAGS(Handles, Handle)
0033 
0034   ctkRangeSliderPrivate(ctkRangeSlider& object);
0035   void init();
0036 
0037   /// Return the handle at the given pos, or none if no handle is at the pos.
0038   /// If a handle is selected, handleRect is set to the handle rect.
0039   /// otherwise return NoHandle and handleRect is set to the combined rect of
0040   /// the min and max handles
0041   Handle handleAtPos(const QPoint& pos, QRect& handleRect)const;
0042 
0043   /// Copied verbatim from QSliderPrivate class (see QSlider.cpp)
0044   int pixelPosToRangeValue(int pos) const;
0045   int pixelPosFromRangeValue(int val) const;
0046 
0047   /// Draw the bottom and top sliders.
0048   void drawMinimumSlider( QStylePainter* painter ) const;
0049   void drawMaximumSlider( QStylePainter* painter ) const;
0050 
0051   /// End points of the range on the Model
0052   int m_MaximumValue;
0053   int m_MinimumValue;
0054 
0055   /// End points of the range on the GUI. This is synced with the model.
0056   int m_MaximumPosition;
0057   int m_MinimumPosition;
0058 
0059   /// Controls selected ?
0060   QStyle::SubControl m_MinimumSliderSelected;
0061   QStyle::SubControl m_MaximumSliderSelected;
0062 
0063   /// See QSliderPrivate::clickOffset.
0064   /// Overrides this ivar
0065   int m_SubclassClickOffset;
0066 
0067   /// See QSliderPrivate::position
0068   /// Overrides this ivar.
0069   int m_SubclassPosition;
0070 
0071   /// Original width between the 2 bounds before any moves
0072   int m_SubclassWidth;
0073 
0074   ctkRangeSliderPrivate::Handles m_SelectedHandles;
0075 
0076   /// When symmetricMoves is true, moving a handle will move the other handle
0077   /// symmetrically, otherwise the handles are independent.
0078   bool m_SymmetricMoves;
0079 
0080   QString m_HandleToolTip;
0081 
0082 private:
0083   Q_DISABLE_COPY(ctkRangeSliderPrivate)
0084 };
0085 
0086 // --------------------------------------------------------------------------
0087 ctkRangeSliderPrivate::ctkRangeSliderPrivate(ctkRangeSlider& object)
0088   :q_ptr(&object)
0089 {
0090   this->m_MinimumValue = 0;
0091   this->m_MaximumValue = 100;
0092   this->m_MinimumPosition = 0;
0093   this->m_MaximumPosition = 100;
0094   this->m_MinimumSliderSelected = QStyle::SC_None;
0095   this->m_MaximumSliderSelected = QStyle::SC_None;
0096   this->m_SubclassClickOffset = 0;
0097   this->m_SubclassPosition = 0;
0098   this->m_SubclassWidth = 0;
0099   this->m_SelectedHandles = ctkRangeSliderPrivate::NoHandle;
0100   this->m_SymmetricMoves = false;
0101 }
0102 
0103 // --------------------------------------------------------------------------
0104 void ctkRangeSliderPrivate::init()
0105 {
0106   Q_Q(ctkRangeSlider);
0107   this->m_MinimumValue = q->minimum();
0108   this->m_MaximumValue = q->maximum();
0109   this->m_MinimumPosition = q->minimum();
0110   this->m_MaximumPosition = q->maximum();
0111   q->connect(q, SIGNAL(rangeChanged(int,int)), q, SLOT(onRangeChanged(int,int)));
0112 }
0113 
0114 // --------------------------------------------------------------------------
0115 ctkRangeSliderPrivate::Handle ctkRangeSliderPrivate::handleAtPos(const QPoint& pos, QRect& handleRect)const
0116 {
0117   Q_Q(const ctkRangeSlider);
0118 
0119   QStyleOptionSlider option;
0120   q->initStyleOption( &option );
0121 
0122   // The functions hitTestComplexControl only know about 1 handle. As we have
0123   // 2, we change the position of the handle and test if the pos correspond to
0124   // any of the 2 positions.
0125 
0126   // Test the MinimumHandle
0127   option.sliderPosition = this->m_MinimumPosition;
0128   option.sliderValue    = this->m_MinimumValue;
0129 
0130   QStyle::SubControl minimumControl = q->style()->hitTestComplexControl(
0131     QStyle::CC_Slider, &option, pos, q);
0132   QRect minimumHandleRect = q->style()->subControlRect(
0133       QStyle::CC_Slider, &option, QStyle::SC_SliderHandle, q);
0134 
0135   // Test if the pos is under the Maximum handle
0136   option.sliderPosition = this->m_MaximumPosition;
0137   option.sliderValue    = this->m_MaximumValue;
0138 
0139   QStyle::SubControl maximumControl = q->style()->hitTestComplexControl(
0140     QStyle::CC_Slider, &option, pos, q);
0141   QRect maximumHandleRect = q->style()->subControlRect(
0142       QStyle::CC_Slider, &option, QStyle::SC_SliderHandle, q);
0143 
0144   // The pos is above both handles, select the closest handle
0145   if (minimumControl == QStyle::SC_SliderHandle &&
0146       maximumControl == QStyle::SC_SliderHandle)
0147     {
0148     int minDist = 0;
0149     int maxDist = 0;
0150     if (q->orientation() == Qt::Horizontal)
0151       {
0152       minDist = pos.x() - minimumHandleRect.left();
0153       maxDist = maximumHandleRect.right() - pos.x();
0154       }
0155     else //if (q->orientation() == Qt::Vertical)
0156       {
0157       minDist = minimumHandleRect.bottom() - pos.y();
0158       maxDist = pos.y() - maximumHandleRect.top();
0159       }
0160     Q_ASSERT( minDist >= 0 && maxDist >= 0);
0161     minimumControl = minDist < maxDist ? minimumControl : QStyle::SC_None;
0162     }
0163 
0164   if (minimumControl == QStyle::SC_SliderHandle)
0165     {
0166     handleRect = minimumHandleRect;
0167     return MinimumHandle;
0168     }
0169   else if (maximumControl == QStyle::SC_SliderHandle)
0170     {
0171     handleRect = maximumHandleRect;
0172     return MaximumHandle;
0173     }
0174   handleRect = minimumHandleRect.united(maximumHandleRect);
0175   return NoHandle;
0176 }
0177 
0178 // --------------------------------------------------------------------------
0179 // Copied verbatim from QSliderPrivate::pixelPosToRangeValue. See QSlider.cpp
0180 //
0181 int ctkRangeSliderPrivate::pixelPosToRangeValue( int pos ) const
0182 {
0183   Q_Q(const ctkRangeSlider);
0184   QStyleOptionSlider option;
0185   q->initStyleOption( &option );
0186 
0187   QRect gr = q->style()->subControlRect( QStyle::CC_Slider,
0188                                             &option,
0189                                             QStyle::SC_SliderGroove,
0190                                             q );
0191   QRect sr = q->style()->subControlRect( QStyle::CC_Slider,
0192                                             &option,
0193                                             QStyle::SC_SliderHandle,
0194                                             q );
0195   int sliderMin, sliderMax, sliderLength;
0196   if (option.orientation == Qt::Horizontal)
0197     {
0198     sliderLength = sr.width();
0199     sliderMin = gr.x();
0200     sliderMax = gr.right() - sliderLength + 1;
0201     }
0202   else
0203     {
0204     sliderLength = sr.height();
0205     sliderMin = gr.y();
0206     sliderMax = gr.bottom() - sliderLength + 1;
0207     }
0208 
0209   return QStyle::sliderValueFromPosition( q->minimum(),
0210                                           q->maximum(),
0211                                           pos - sliderMin,
0212                                           sliderMax - sliderMin,
0213                                           option.upsideDown );
0214 }
0215 
0216 //---------------------------------------------------------------------------
0217 int ctkRangeSliderPrivate::pixelPosFromRangeValue( int val ) const
0218 {
0219   Q_Q(const ctkRangeSlider);
0220   QStyleOptionSlider option;
0221   q->initStyleOption( &option );
0222 
0223   QRect gr = q->style()->subControlRect( QStyle::CC_Slider,
0224                                             &option,
0225                                             QStyle::SC_SliderGroove,
0226                                             q );
0227   QRect sr = q->style()->subControlRect( QStyle::CC_Slider,
0228                                             &option,
0229                                             QStyle::SC_SliderHandle,
0230                                             q );
0231   int sliderMin, sliderMax, sliderLength;
0232   if (option.orientation == Qt::Horizontal)
0233     {
0234     sliderLength = sr.width();
0235     sliderMin = gr.x();
0236     sliderMax = gr.right() - sliderLength + 1;
0237     }
0238   else
0239     {
0240     sliderLength = sr.height();
0241     sliderMin = gr.y();
0242     sliderMax = gr.bottom() - sliderLength + 1;
0243     }
0244 
0245   return QStyle::sliderPositionFromValue( q->minimum(),
0246                                           q->maximum(),
0247                                           val,
0248                                           sliderMax - sliderMin,
0249                                           option.upsideDown ) + sliderMin;
0250 }
0251 
0252 //---------------------------------------------------------------------------
0253 // Draw slider at the bottom end of the range
0254 void ctkRangeSliderPrivate::drawMinimumSlider( QStylePainter* painter ) const
0255 {
0256   Q_Q(const ctkRangeSlider);
0257   QStyleOptionSlider option;
0258   q->initMinimumSliderStyleOption( &option );
0259 
0260   option.subControls = QStyle::SC_SliderHandle;
0261   option.sliderValue = m_MinimumValue;
0262   option.sliderPosition = m_MinimumPosition;
0263   if (q->isMinimumSliderDown())
0264     {
0265     option.activeSubControls = QStyle::SC_SliderHandle;
0266     option.state |= QStyle::State_Sunken;
0267     }
0268 #ifdef Q_OS_MAC
0269   // On mac style, drawing just the handle actually draws also the groove.
0270   QRect clip = q->style()->subControlRect(QStyle::CC_Slider, &option,
0271                                           QStyle::SC_SliderHandle, q);
0272   painter->setClipRect(clip);
0273 #endif
0274   painter->drawComplexControl(QStyle::CC_Slider, option);
0275 }
0276 
0277 //---------------------------------------------------------------------------
0278 // Draw slider at the top end of the range
0279 void ctkRangeSliderPrivate::drawMaximumSlider( QStylePainter* painter ) const
0280 {
0281   Q_Q(const ctkRangeSlider);
0282   QStyleOptionSlider option;
0283   q->initMaximumSliderStyleOption( &option );
0284 
0285   option.subControls = QStyle::SC_SliderHandle;
0286   option.sliderValue = m_MaximumValue;
0287   option.sliderPosition = m_MaximumPosition;
0288   if (q->isMaximumSliderDown())
0289     {
0290     option.activeSubControls = QStyle::SC_SliderHandle;
0291     option.state |= QStyle::State_Sunken;
0292     }
0293 #ifdef Q_OS_MAC
0294   // On mac style, drawing just the handle actually draws also the groove.
0295   QRect clip = q->style()->subControlRect(QStyle::CC_Slider, &option,
0296                                           QStyle::SC_SliderHandle, q);
0297   painter->setClipRect(clip);
0298 #endif
0299   painter->drawComplexControl(QStyle::CC_Slider, option);
0300 }
0301 
0302 // --------------------------------------------------------------------------
0303 ctkRangeSlider::ctkRangeSlider(QWidget* _parent)
0304   : QSlider(_parent)
0305   , d_ptr(new ctkRangeSliderPrivate(*this))
0306 {
0307   Q_D(ctkRangeSlider);
0308   d->init();
0309 }
0310 
0311 // --------------------------------------------------------------------------
0312 ctkRangeSlider::ctkRangeSlider( Qt::Orientation o,
0313                                   QWidget* parentObject )
0314   :QSlider(o, parentObject)
0315   , d_ptr(new ctkRangeSliderPrivate(*this))
0316 {
0317   Q_D(ctkRangeSlider);
0318   d->init();
0319 }
0320 
0321 // --------------------------------------------------------------------------
0322 ctkRangeSlider::ctkRangeSlider(ctkRangeSliderPrivate* impl, QWidget* _parent)
0323   : QSlider(_parent)
0324   , d_ptr(impl)
0325 {
0326   Q_D(ctkRangeSlider);
0327   d->init();
0328 }
0329 
0330 // --------------------------------------------------------------------------
0331 ctkRangeSlider::ctkRangeSlider( ctkRangeSliderPrivate* impl, Qt::Orientation o,
0332                                 QWidget* parentObject )
0333   :QSlider(o, parentObject)
0334   , d_ptr(impl)
0335 {
0336   Q_D(ctkRangeSlider);
0337   d->init();
0338 }
0339 
0340 // --------------------------------------------------------------------------
0341 ctkRangeSlider::~ctkRangeSlider()
0342 {
0343 }
0344 
0345 // --------------------------------------------------------------------------
0346 int ctkRangeSlider::minimumValue() const
0347 {
0348   Q_D(const ctkRangeSlider);
0349   return d->m_MinimumValue;
0350 }
0351 
0352 // --------------------------------------------------------------------------
0353 void ctkRangeSlider::setMinimumValue( int min )
0354 {
0355   Q_D(ctkRangeSlider);
0356   this->setValues( min, qMax(d->m_MaximumValue,min) );
0357 }
0358 
0359 // --------------------------------------------------------------------------
0360 int ctkRangeSlider::maximumValue() const
0361 {
0362   Q_D(const ctkRangeSlider);
0363   return d->m_MaximumValue;
0364 }
0365 
0366 // --------------------------------------------------------------------------
0367 void ctkRangeSlider::setMaximumValue( int max )
0368 {
0369   Q_D(ctkRangeSlider);
0370   this->setValues( qMin(d->m_MinimumValue, max), max );
0371 }
0372 
0373 // --------------------------------------------------------------------------
0374 void ctkRangeSlider::setValues(int l, int u)
0375 {
0376   Q_D(ctkRangeSlider);
0377   const int minValue =
0378     qBound(this->minimum(), qMin(l,u), this->maximum());
0379   const int maxValue =
0380     qBound(this->minimum(), qMax(l,u), this->maximum());
0381   bool emitMinValChanged = (minValue != d->m_MinimumValue);
0382   bool emitMaxValChanged = (maxValue != d->m_MaximumValue);
0383 
0384   d->m_MinimumValue = minValue;
0385   d->m_MaximumValue = maxValue;
0386 
0387   bool emitMinPosChanged =
0388     (minValue != d->m_MinimumPosition);
0389   bool emitMaxPosChanged =
0390     (maxValue != d->m_MaximumPosition);
0391   d->m_MinimumPosition = minValue;
0392   d->m_MaximumPosition = maxValue;
0393 
0394   if (isSliderDown())
0395     {
0396     if (emitMinPosChanged || emitMaxPosChanged)
0397       {
0398       emit positionsChanged(d->m_MinimumPosition, d->m_MaximumPosition);
0399       }
0400     if (emitMinPosChanged)
0401       {
0402       emit minimumPositionChanged(d->m_MinimumPosition);
0403       }
0404     if (emitMaxPosChanged)
0405       {
0406       emit maximumPositionChanged(d->m_MaximumPosition);
0407       }
0408     }
0409   if (emitMinValChanged || emitMaxValChanged)
0410     {
0411     emit valuesChanged(d->m_MinimumValue,
0412                        d->m_MaximumValue);
0413     }
0414   if (emitMinValChanged)
0415     {
0416     emit minimumValueChanged(d->m_MinimumValue);
0417     }
0418   if (emitMaxValChanged)
0419     {
0420     emit maximumValueChanged(d->m_MaximumValue);
0421     }
0422   if (emitMinPosChanged || emitMaxPosChanged ||
0423       emitMinValChanged || emitMaxValChanged)
0424     {
0425     this->update();
0426     }
0427 }
0428 
0429 // --------------------------------------------------------------------------
0430 int ctkRangeSlider::minimumPosition() const
0431 {
0432   Q_D(const ctkRangeSlider);
0433   return d->m_MinimumPosition;
0434 }
0435 
0436 // --------------------------------------------------------------------------
0437 int ctkRangeSlider::maximumPosition() const
0438 {
0439   Q_D(const ctkRangeSlider);
0440   return d->m_MaximumPosition;
0441 }
0442 
0443 // --------------------------------------------------------------------------
0444 void ctkRangeSlider::setMinimumPosition(int l)
0445 {
0446   Q_D(const ctkRangeSlider);
0447   this->setPositions(l, qMax(l, d->m_MaximumPosition));
0448 }
0449 
0450 // --------------------------------------------------------------------------
0451 void ctkRangeSlider::setMaximumPosition(int u)
0452 {
0453   Q_D(const ctkRangeSlider);
0454   this->setPositions(qMin(d->m_MinimumPosition, u), u);
0455 }
0456 
0457 // --------------------------------------------------------------------------
0458 void ctkRangeSlider::setPositions(int min, int max)
0459 {
0460   Q_D(ctkRangeSlider);
0461   const int minPosition =
0462     qBound(this->minimum(), qMin(min, max), this->maximum());
0463   const int maxPosition =
0464     qBound(this->minimum(), qMax(min, max), this->maximum());
0465 
0466   bool emitMinPosChanged = (minPosition != d->m_MinimumPosition);
0467   bool emitMaxPosChanged = (maxPosition != d->m_MaximumPosition);
0468 
0469   if (!emitMinPosChanged && !emitMaxPosChanged)
0470     {
0471     return;
0472     }
0473 
0474   d->m_MinimumPosition = minPosition;
0475   d->m_MaximumPosition = maxPosition;
0476 
0477   if (!this->hasTracking())
0478     {
0479     this->update();
0480     }
0481   if (isSliderDown())
0482     {
0483     if (emitMinPosChanged)
0484       {
0485       emit minimumPositionChanged(d->m_MinimumPosition);
0486       }
0487     if (emitMaxPosChanged)
0488       {
0489       emit maximumPositionChanged(d->m_MaximumPosition);
0490       }
0491     if (emitMinPosChanged || emitMaxPosChanged)
0492       {
0493       emit positionsChanged(d->m_MinimumPosition, d->m_MaximumPosition);
0494       }
0495     }
0496   if (this->hasTracking())
0497     {
0498     this->triggerAction(SliderMove);
0499     this->setValues(d->m_MinimumPosition, d->m_MaximumPosition);
0500     }
0501 }
0502 
0503 // --------------------------------------------------------------------------
0504 void ctkRangeSlider::setSymmetricMoves(bool symmetry)
0505 {
0506   Q_D(ctkRangeSlider);
0507   d->m_SymmetricMoves = symmetry;
0508 }
0509 
0510 // --------------------------------------------------------------------------
0511 bool ctkRangeSlider::symmetricMoves()const
0512 {
0513   Q_D(const ctkRangeSlider);
0514   return d->m_SymmetricMoves;
0515 }
0516 
0517 // --------------------------------------------------------------------------
0518 void ctkRangeSlider::onRangeChanged(int _minimum, int _maximum)
0519 {
0520   Q_UNUSED(_minimum);
0521   Q_UNUSED(_maximum);
0522   Q_D(ctkRangeSlider);
0523   this->setValues(d->m_MinimumValue, d->m_MaximumValue);
0524 }
0525 
0526 // --------------------------------------------------------------------------
0527 // Render
0528 void ctkRangeSlider::paintEvent( QPaintEvent* )
0529 {
0530   Q_D(ctkRangeSlider);
0531   QStyleOptionSlider option;
0532   this->initStyleOption(&option);
0533 
0534   QStylePainter painter(this);
0535   option.subControls = QStyle::SC_SliderGroove;
0536   // Move to minimum to not highlight the SliderGroove.
0537   // On mac style, drawing just the slider groove also draws the handles,
0538   // therefore we give a negative (outside of view) position.
0539   option.sliderValue = this->minimum() - this->maximum();
0540   option.sliderPosition = this->minimum() - this->maximum();
0541   painter.drawComplexControl(QStyle::CC_Slider, option);
0542 
0543   option.sliderPosition = d->m_MinimumPosition;
0544   const QRect lr = style()->subControlRect( QStyle::CC_Slider,
0545                                             &option,
0546                                             QStyle::SC_SliderHandle,
0547                                             this);
0548   option.sliderPosition = d->m_MaximumPosition;
0549 
0550   const QRect ur = style()->subControlRect( QStyle::CC_Slider,
0551                                             &option,
0552                                             QStyle::SC_SliderHandle,
0553                                             this);
0554 
0555   QRect sr = style()->subControlRect( QStyle::CC_Slider,
0556                                       &option,
0557                                       QStyle::SC_SliderGroove,
0558                                       this);
0559   QRect rangeBox;
0560   if (option.orientation == Qt::Horizontal)
0561     {
0562     rangeBox = QRect(
0563       QPoint(qMin( lr.center().x(), ur.center().x() ), sr.center().y() - 2),
0564       QPoint(qMax( lr.center().x(), ur.center().x() ), sr.center().y() + 1));
0565     }
0566   else
0567     {
0568     rangeBox = QRect(
0569       QPoint(sr.center().x() - 2, qMin( lr.center().y(), ur.center().y() )),
0570       QPoint(sr.center().x() + 1, qMax( lr.center().y(), ur.center().y() )));
0571     }
0572 
0573   // -----------------------------
0574   // Render the range
0575   //
0576   QRect groove = this->style()->subControlRect( QStyle::CC_Slider,
0577                                                 &option,
0578                                                 QStyle::SC_SliderGroove,
0579                                                 this );
0580   groove.adjust(0, 0, -1, 0);
0581 
0582   // Create default colors based on the transfer function.
0583   //
0584   QColor highlight = this->palette().color(QPalette::Normal, QPalette::Highlight);
0585   QLinearGradient gradient;
0586   if (option.orientation == Qt::Horizontal)
0587     {
0588     gradient = QLinearGradient( groove.center().x(), groove.top(),
0589                                 groove.center().x(), groove.bottom());
0590     }
0591   else
0592     {
0593     gradient = QLinearGradient( groove.left(), groove.center().y(),
0594                                 groove.right(), groove.center().y());
0595     }
0596 
0597   // TODO: Set this based on the supplied transfer function
0598   //QColor l = Qt::darkGray;
0599   //QColor u = Qt::black;
0600 
0601   gradient.setColorAt(0, highlight.darker(120));
0602   gradient.setColorAt(1, highlight.lighter(160));
0603 
0604   painter.setPen(QPen(highlight.darker(150), 0));
0605   painter.setBrush(gradient);
0606   painter.drawRect( rangeBox.intersected(groove) );
0607 
0608   //  -----------------------------------
0609   // Render the sliders
0610   //
0611   if (this->isMinimumSliderDown())
0612     {
0613     d->drawMaximumSlider( &painter );
0614     d->drawMinimumSlider( &painter );
0615     }
0616   else
0617     {
0618     d->drawMinimumSlider( &painter );
0619     d->drawMaximumSlider( &painter );
0620     }
0621 }
0622 
0623 // --------------------------------------------------------------------------
0624 // Standard Qt UI events
0625 void ctkRangeSlider::mousePressEvent(QMouseEvent* mouseEvent)
0626 {
0627   Q_D(ctkRangeSlider);
0628   if (minimum() == maximum() || (mouseEvent->buttons() ^ mouseEvent->button()))
0629     {
0630     mouseEvent->ignore();
0631     return;
0632     }
0633   int mepos = this->orientation() == Qt::Horizontal ?
0634     mouseEvent->pos().x() : mouseEvent->pos().y();
0635 
0636   QStyleOptionSlider option;
0637   this->initStyleOption( &option );
0638 
0639   QRect handleRect;
0640   ctkRangeSliderPrivate::Handle handle_ = d->handleAtPos(mouseEvent->pos(), handleRect);
0641 
0642   if (handle_ != ctkRangeSliderPrivate::NoHandle)
0643     {
0644     d->m_SubclassPosition = (handle_ == ctkRangeSliderPrivate::MinimumHandle)?
0645       d->m_MinimumPosition : d->m_MaximumPosition;
0646 
0647     // save the position of the mouse inside the handle for later
0648     d->m_SubclassClickOffset = mepos - (this->orientation() == Qt::Horizontal ?
0649       handleRect.left() : handleRect.top());
0650 
0651     this->setSliderDown(true);
0652 
0653     if (d->m_SelectedHandles != handle_)
0654       {
0655       d->m_SelectedHandles = handle_;
0656       this->update(handleRect);
0657       }
0658     // Accept the mouseEvent
0659     mouseEvent->accept();
0660     return;
0661     }
0662 
0663   // if we are here, no handles have been pressed
0664   // Check if we pressed on the groove between the 2 handles
0665 
0666   QStyle::SubControl control = this->style()->hitTestComplexControl(
0667     QStyle::CC_Slider, &option, mouseEvent->pos(), this);
0668   QRect sr = style()->subControlRect(
0669     QStyle::CC_Slider, &option, QStyle::SC_SliderGroove, this);
0670   int minCenter = (this->orientation() == Qt::Horizontal ?
0671     handleRect.left() : handleRect.top());
0672   int maxCenter = (this->orientation() == Qt::Horizontal ?
0673     handleRect.right() : handleRect.bottom());
0674   if (control == QStyle::SC_SliderGroove &&
0675       mepos > minCenter && mepos < maxCenter)
0676     {
0677     // warning lost of precision it might be fatal
0678     d->m_SubclassPosition = (d->m_MinimumPosition + d->m_MaximumPosition) / 2.;
0679     d->m_SubclassClickOffset = mepos - d->pixelPosFromRangeValue(d->m_SubclassPosition);
0680     d->m_SubclassWidth = (d->m_MaximumPosition - d->m_MinimumPosition) / 2;
0681     qMax(d->m_SubclassPosition - d->m_MinimumPosition, d->m_MaximumPosition - d->m_SubclassPosition);
0682     this->setSliderDown(true);
0683     if (!this->isMinimumSliderDown() || !this->isMaximumSliderDown())
0684       {
0685       d->m_SelectedHandles =
0686         QFlags<ctkRangeSliderPrivate::Handle>(ctkRangeSliderPrivate::MinimumHandle) |
0687         QFlags<ctkRangeSliderPrivate::Handle>(ctkRangeSliderPrivate::MaximumHandle);
0688       this->update(handleRect.united(sr));
0689       }
0690     mouseEvent->accept();
0691     return;
0692     }
0693   mouseEvent->ignore();
0694 }
0695 
0696 // --------------------------------------------------------------------------
0697 // Standard Qt UI events
0698 void ctkRangeSlider::mouseMoveEvent(QMouseEvent* mouseEvent)
0699 {
0700   Q_D(ctkRangeSlider);
0701   if (!d->m_SelectedHandles)
0702     {
0703     mouseEvent->ignore();
0704     return;
0705     }
0706   int mepos = this->orientation() == Qt::Horizontal ?
0707     mouseEvent->pos().x() : mouseEvent->pos().y();
0708 
0709   QStyleOptionSlider option;
0710   this->initStyleOption(&option);
0711 
0712   const int m = style()->pixelMetric( QStyle::PM_MaximumDragDistance, &option, this );
0713 
0714   int newPosition = d->pixelPosToRangeValue(mepos - d->m_SubclassClickOffset);
0715 
0716   if (m >= 0)
0717     {
0718     const QRect r = rect().adjusted(-m, -m, m, m);
0719     if (!r.contains(mouseEvent->pos()))
0720       {
0721       newPosition = d->m_SubclassPosition;
0722       }
0723     }
0724 
0725   // Only the lower/left slider is down
0726   if (this->isMinimumSliderDown() && !this->isMaximumSliderDown())
0727     {
0728     double newMinPos = qMin(newPosition,d->m_MaximumPosition);
0729     this->setPositions(newMinPos, d->m_MaximumPosition +
0730       (d->m_SymmetricMoves ? d->m_MinimumPosition - newMinPos : 0));
0731     }
0732   // Only the upper/right slider is down
0733   else if (this->isMaximumSliderDown() && !this->isMinimumSliderDown())
0734     {
0735     double newMaxPos = qMax(d->m_MinimumPosition, newPosition);
0736     this->setPositions(d->m_MinimumPosition -
0737       (d->m_SymmetricMoves ? newMaxPos - d->m_MaximumPosition: 0),
0738       newMaxPos);
0739     }
0740   // Both handles are down (the user clicked in between the handles)
0741   else if (this->isMinimumSliderDown() && this->isMaximumSliderDown())
0742     {
0743     this->setPositions(newPosition - d->m_SubclassWidth,
0744                        newPosition + d->m_SubclassWidth );
0745     }
0746   mouseEvent->accept();
0747 }
0748 
0749 // --------------------------------------------------------------------------
0750 // Standard Qt UI mouseEvents
0751 void ctkRangeSlider::mouseReleaseEvent(QMouseEvent* mouseEvent)
0752 {
0753   Q_D(ctkRangeSlider);
0754   this->QSlider::mouseReleaseEvent(mouseEvent);
0755 
0756   setSliderDown(false);
0757   d->m_SelectedHandles = ctkRangeSliderPrivate::NoHandle;
0758 
0759   this->update();
0760 }
0761 
0762 // --------------------------------------------------------------------------
0763 bool ctkRangeSlider::isMinimumSliderDown()const
0764 {
0765   Q_D(const ctkRangeSlider);
0766   return d->m_SelectedHandles & ctkRangeSliderPrivate::MinimumHandle;
0767 }
0768 
0769 // --------------------------------------------------------------------------
0770 bool ctkRangeSlider::isMaximumSliderDown()const
0771 {
0772   Q_D(const ctkRangeSlider);
0773   return d->m_SelectedHandles & ctkRangeSliderPrivate::MaximumHandle;
0774 }
0775 
0776 // --------------------------------------------------------------------------
0777 void ctkRangeSlider::initMinimumSliderStyleOption(QStyleOptionSlider* option) const
0778 {
0779   this->initStyleOption(option);
0780 }
0781 
0782 // --------------------------------------------------------------------------
0783 void ctkRangeSlider::initMaximumSliderStyleOption(QStyleOptionSlider* option) const
0784 {
0785   this->initStyleOption(option);
0786 }
0787 
0788 // --------------------------------------------------------------------------
0789 QString ctkRangeSlider::handleToolTip()const
0790 {
0791   Q_D(const ctkRangeSlider);
0792   return d->m_HandleToolTip;
0793 }
0794 
0795 // --------------------------------------------------------------------------
0796 void ctkRangeSlider::setHandleToolTip(const QString& _toolTip)
0797 {
0798   Q_D(ctkRangeSlider);
0799   d->m_HandleToolTip = _toolTip;
0800 }
0801 
0802 // --------------------------------------------------------------------------
0803 bool ctkRangeSlider::event(QEvent* _event)
0804 {
0805   Q_D(ctkRangeSlider);
0806   switch(_event->type())
0807     {
0808     case QEvent::ToolTip:
0809       {
0810       QHelpEvent* helpEvent = static_cast<QHelpEvent*>(_event);
0811       QStyleOptionSlider opt;
0812       // Test the MinimumHandle
0813       opt.sliderPosition = d->m_MinimumPosition;
0814       opt.sliderValue = d->m_MinimumValue;
0815       this->initStyleOption(&opt);
0816       QStyle::SubControl hoveredControl =
0817         this->style()->hitTestComplexControl(
0818           QStyle::CC_Slider, &opt, helpEvent->pos(), this);
0819       if (!d->m_HandleToolTip.isEmpty() &&
0820           hoveredControl == QStyle::SC_SliderHandle)
0821         {
0822         QToolTip::showText(helpEvent->globalPos(), d->m_HandleToolTip.arg(this->minimumValue()));
0823         _event->accept();
0824         return true;
0825         }
0826       // Test the MaximumHandle
0827       opt.sliderPosition = d->m_MaximumPosition;
0828       opt.sliderValue = d->m_MaximumValue;
0829       this->initStyleOption(&opt);
0830       hoveredControl = this->style()->hitTestComplexControl(
0831         QStyle::CC_Slider, &opt, helpEvent->pos(), this);
0832       if (!d->m_HandleToolTip.isEmpty() &&
0833           hoveredControl == QStyle::SC_SliderHandle)
0834         {
0835         QToolTip::showText(helpEvent->globalPos(), d->m_HandleToolTip.arg(this->maximumValue()));
0836         _event->accept();
0837         return true;
0838         }
0839       }
0840     default:
0841       break;
0842     }
0843   return this->Superclass::event(_event);
0844 }