ctkRangeSlider.cpp

Go to the documentation of this file.
00001 /*=========================================================================
00002 
00003   Library:   CTK
00004  
00005   Copyright (c) 2010  Kitware Inc.
00006 
00007   Licensed under the Apache License, Version 2.0 (the "License");
00008   you may not use this file except in compliance with the License.
00009   You may obtain a copy of the License at
00010 
00011       http://www.commontk.org/LICENSE
00012 
00013   Unless required by applicable law or agreed to in writing, software
00014   distributed under the License is distributed on an "AS IS" BASIS,
00015   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00016   See the License for the specific language governing permissions and
00017   limitations under the License.
00018  
00019 =========================================================================*/
00020 
00021 // Qt includes
00022 #include <QDebug>
00023 #include <QMouseEvent>
00024 #include <QKeyEvent>
00025 #include <QStyleOptionSlider>
00026 #include <QApplication>
00027 #include <QStylePainter>
00028 #include <QStyle>
00029 
00030 // CTK includes
00031 #include "ctkRangeSlider.h"
00032 
00033 class ctkRangeSliderPrivate:public ctkPrivate<ctkRangeSlider>
00034 {
00035 public:
00036   ctkRangeSliderPrivate();
00037   void init();
00038   
00039   // Description:
00040   // Copied verbatim from QSliderPrivate class (see QSlider.cpp)
00041   int pixelPosToRangeValue(int pos) const;
00042 
00043   // Description:
00044   // Draw the bottom and top sliders.
00045   void drawMinimumSlider( QStylePainter* painter ) const;
00046   void drawMaximumSlider( QStylePainter* painter ) const;
00047     
00048   // Description:
00049   // End points of the range on the Model
00050   int m_MaximumValue;
00051   int m_MinimumValue;
00052 
00053   // Description:
00054   // End points of the range on the GUI. This is synced with the model.
00055   int m_MaximumPosition;
00056   int m_MinimumPosition;
00057 
00058   // Description:
00059   // Controls selected ?
00060   QStyle::SubControl m_MinimumSliderSelected;
00061   QStyle::SubControl m_MaximumSliderSelected;
00062 
00063   // Description:
00064   // See QSliderPrivate::clickOffset. 
00065   // Overrides this ivar
00066   int m_SubclassClickOffset;
00067     
00068   // Description:
00069   // See QSliderPrivate::position
00070   // Overrides this ivar.
00071   int m_SubclassPosition;
00072 
00073   // Description:
00074   // Boolean indicates the selected handle
00075   //   True for the minimum range handle, false for the maximum range handle
00076   bool m_SelectedSlider;
00077 };
00078 
00079 // --------------------------------------------------------------------------
00080 ctkRangeSliderPrivate::ctkRangeSliderPrivate()
00081 {
00082   this->m_MinimumValue = 0;
00083   this->m_MaximumValue = 100;
00084   this->m_MinimumPosition = 0;
00085   this->m_MaximumPosition = 100;
00086   this->m_MinimumSliderSelected = QStyle::SC_None;
00087   this->m_MaximumSliderSelected = QStyle::SC_None;
00088   this->m_SubclassClickOffset = 0;
00089   this->m_SubclassPosition = 0;
00090 }
00091 
00092 // --------------------------------------------------------------------------
00093 void ctkRangeSliderPrivate::init()
00094 {
00095   CTK_P(ctkRangeSlider);
00096   this->m_MinimumValue = p->minimum();
00097   this->m_MaximumValue = p->maximum();
00098   this->m_MinimumPosition = p->minimum();
00099   this->m_MaximumPosition = p->maximum();
00100   p->connect(p, SIGNAL(rangeChanged(int, int)), p, SLOT(onRangeChanged(int, int)));
00101 }
00102 
00103 // --------------------------------------------------------------------------
00104 // Copied verbatim from QSliderPrivate::pixelPosToRangeValue. See QSlider.cpp
00105 //
00106 int ctkRangeSliderPrivate::pixelPosToRangeValue( int pos ) const
00107 {
00108   CTK_P(const ctkRangeSlider);
00109   QStyleOptionSlider option;
00110   p->initStyleOption( &option );
00111 
00112   QRect gr = p->style()->subControlRect( QStyle::CC_Slider, 
00113                                             &option, 
00114                                             QStyle::SC_SliderGroove, 
00115                                             p );
00116   QRect sr = p->style()->subControlRect( QStyle::CC_Slider, 
00117                                             &option, 
00118                                             QStyle::SC_SliderHandle, 
00119                                             p );
00120 
00121   int sliderLength = sr.width();
00122   int sliderMin = gr.x();
00123   int sliderMax = gr.right() - sliderLength + 1;
00124 
00125   return QStyle::sliderValueFromPosition( p->minimum(), 
00126                                           p->maximum(), 
00127                                           pos - sliderMin,
00128                                           sliderMax - sliderMin, 
00129                                           option.upsideDown );
00130 }
00131 
00132 //---------------------------------------------------------------------------
00133 // Draw slider at the bottom end of the range
00134 void ctkRangeSliderPrivate::drawMinimumSlider( QStylePainter* painter ) const
00135 {
00136   CTK_P(const ctkRangeSlider);
00137   QStyleOptionSlider option;
00138   p->initStyleOption( &option );
00139 
00140   option.subControls = QStyle::SC_SliderHandle;
00141   option.sliderValue = m_MinimumValue;
00142   option.sliderPosition = m_MinimumPosition;
00143   if (m_MinimumSliderSelected == QStyle::SC_SliderHandle)
00144     {
00145     option.activeSubControls = m_MinimumSliderSelected;
00146     option.state |= QStyle::State_Sunken;
00147     }
00148 
00149   painter->drawComplexControl(QStyle::CC_Slider, option);
00150 }
00151 
00152 //---------------------------------------------------------------------------
00153 // Draw slider at the top end of the range
00154 void ctkRangeSliderPrivate::drawMaximumSlider( QStylePainter* painter ) const
00155 {
00156   CTK_P(const ctkRangeSlider);
00157   QStyleOptionSlider option;
00158   p->Superclass::initStyleOption( &option );
00159 
00160   option.subControls = QStyle::SC_SliderHandle;
00161   option.sliderValue = m_MaximumValue;
00162   option.sliderPosition = m_MaximumPosition;
00163   if (m_MaximumSliderSelected == QStyle::SC_SliderHandle)
00164     {
00165     option.activeSubControls = m_MaximumSliderSelected;
00166     option.state |= QStyle::State_Sunken;
00167     }
00168 
00169   painter->drawComplexControl(QStyle::CC_Slider, option);
00170 }
00171 
00172 // --------------------------------------------------------------------------
00173 ctkRangeSlider::ctkRangeSlider(QWidget* parent)
00174   : QSlider(parent)
00175 {
00176   CTK_INIT_PRIVATE(ctkRangeSlider);
00177   ctk_d()->init();
00178 }
00179 
00180 // --------------------------------------------------------------------------
00181 ctkRangeSlider::ctkRangeSlider( Qt::Orientation o,
00182                                   QWidget* parentObject )
00183   :QSlider(o, parentObject)
00184 {
00185   CTK_INIT_PRIVATE(ctkRangeSlider);
00186   ctk_d()->init();
00187 }
00188 
00189 // --------------------------------------------------------------------------
00190 ctkRangeSlider::~ctkRangeSlider()
00191 {
00192 }
00193 
00194 // --------------------------------------------------------------------------
00195 int ctkRangeSlider::minimumValue() const
00196 {
00197   CTK_D(const ctkRangeSlider);
00198   return d->m_MinimumValue;
00199 }
00200 
00201 // --------------------------------------------------------------------------
00202 void ctkRangeSlider::setMinimumValue( int min )
00203 {
00204   CTK_D(ctkRangeSlider);
00205   this->setValues( min, qMax(d->m_MaximumValue,min) );
00206 }
00207 
00208 // --------------------------------------------------------------------------
00209 int ctkRangeSlider::maximumValue() const
00210 {
00211   CTK_D(const ctkRangeSlider);
00212   return d->m_MaximumValue;
00213 }
00214 
00215 // --------------------------------------------------------------------------
00216 void ctkRangeSlider::setMaximumValue( int max )
00217 {
00218   CTK_D(ctkRangeSlider);
00219   this->setValues( qMin(d->m_MinimumValue, max), max );
00220 }
00221 
00222 // --------------------------------------------------------------------------
00223 void ctkRangeSlider::setValues(int l, int u)
00224 {
00225   CTK_D(ctkRangeSlider);
00226   const int minimumValue = 
00227     qBound(this->minimum(), qMin(l,u), this->maximum());
00228   const int maximumValue = 
00229     qBound(this->minimum(), qMax(l,u), this->maximum());
00230   bool emitMinValChanged = (minimumValue != d->m_MinimumValue);
00231   bool emitMaxValChanged = (maximumValue != d->m_MaximumValue);
00232   
00233   d->m_MinimumValue = minimumValue;
00234   d->m_MaximumValue = maximumValue;
00235   
00236   bool emitMinPosChanged = 
00237     (minimumValue != d->m_MinimumPosition);
00238   bool emitMaxPosChanged = 
00239     (maximumValue != d->m_MaximumPosition);
00240   d->m_MinimumPosition = minimumValue;
00241   d->m_MaximumPosition = maximumValue;
00242   
00243   if (isSliderDown())
00244     {
00245     if (emitMinPosChanged)
00246       {
00247       emit minimumPositionChanged(minimumValue);
00248       }
00249     if (emitMaxPosChanged)
00250       {
00251       emit maximumPositionChanged(maximumValue);
00252       }
00253     if (emitMinPosChanged || emitMaxPosChanged)
00254       {
00255       emit positionsChanged(minimumValue, maximumValue);
00256       }
00257     }
00258   if (emitMinValChanged)
00259     {
00260     emit minimumValueChanged(minimumValue);
00261     }
00262   if (emitMaxValChanged)
00263     {
00264     emit maximumValueChanged(maximumValue);
00265     }
00266   if (emitMinValChanged || emitMaxValChanged)
00267     {
00268     emit valuesChanged(d->m_MinimumValue, 
00269                        d->m_MaximumValue);
00270     }
00271   if (emitMinPosChanged || emitMaxPosChanged || 
00272       emitMinValChanged || emitMaxValChanged)
00273     {
00274     this->update();
00275     }
00276 }
00277 
00278 // --------------------------------------------------------------------------
00279 int ctkRangeSlider::minimumPosition() const
00280 {
00281   CTK_D(const ctkRangeSlider);
00282   return d->m_MinimumPosition;
00283 }
00284 
00285 // --------------------------------------------------------------------------
00286 int ctkRangeSlider::maximumPosition() const
00287 {
00288   CTK_D(const ctkRangeSlider);
00289   return d->m_MaximumPosition;
00290 }
00291 
00292 // --------------------------------------------------------------------------
00293 void ctkRangeSlider::setMinimumPosition(int l)
00294 {
00295   CTK_D(const ctkRangeSlider);
00296   this->setPositions(l, qMax(l, d->m_MaximumPosition));
00297 }
00298 
00299 // --------------------------------------------------------------------------
00300 void ctkRangeSlider::setMaximumPosition(int u)
00301 {
00302   CTK_D(const ctkRangeSlider);
00303   this->setPositions(qMin(d->m_MinimumPosition, u), u);
00304 }
00305 
00306 // --------------------------------------------------------------------------
00307 void ctkRangeSlider::setPositions(int min, int max)
00308 {
00309   CTK_D(ctkRangeSlider);
00310   const int minPosition = 
00311     qBound(this->minimum(), qMin(min, max), this->maximum());
00312   const int maxPosition = 
00313     qBound(this->minimum(), qMax(min, max), this->maximum());
00314 
00315   bool emitMinPosChanged = (minPosition != d->m_MinimumPosition);
00316   bool emitMaxPosChanged = (maxPosition != d->m_MaximumPosition);
00317   
00318   if (!emitMinPosChanged && !emitMaxPosChanged)
00319     {
00320     return;
00321     }
00322 
00323   d->m_MinimumPosition = minPosition;
00324   d->m_MaximumPosition = maxPosition;
00325 
00326   if (!this->hasTracking())
00327     {
00328     this->update();
00329     }
00330   if (isSliderDown())
00331     {
00332     if (emitMinPosChanged)
00333       {
00334       emit minimumPositionChanged(d->m_MinimumPosition);
00335       }
00336     if (emitMaxPosChanged)
00337       {
00338       emit maximumPositionChanged(d->m_MaximumPosition);
00339       }
00340     if (emitMinPosChanged || emitMaxPosChanged)
00341       {
00342       emit positionsChanged(d->m_MinimumPosition, d->m_MaximumPosition);
00343       }
00344     }
00345   if (this->hasTracking())
00346     {
00347     this->triggerAction(SliderMove);
00348     this->setValues(d->m_MinimumPosition, d->m_MaximumPosition);
00349     }
00350 }
00351 
00352 // --------------------------------------------------------------------------
00353 void ctkRangeSlider::onRangeChanged(int minimum, int maximum)
00354 {
00355   CTK_D(ctkRangeSlider);
00356   this->setValues(d->m_MinimumValue, d->m_MaximumValue);
00357 }
00358 
00359 // --------------------------------------------------------------------------
00360 // Render
00361 void ctkRangeSlider::paintEvent( QPaintEvent* )
00362 {
00363   CTK_D(ctkRangeSlider);
00364   QStyleOptionSlider option;
00365   this->initStyleOption(&option);
00366 
00367   QStylePainter painter(this);
00368   option.subControls = QStyle::SC_SliderGroove;
00369   painter.drawComplexControl(QStyle::CC_Slider, option);
00370 
00371   option.sliderPosition = d->m_MinimumPosition;
00372   const QRect lr = style()->subControlRect( QStyle::CC_Slider, 
00373                                             &option, 
00374                                             QStyle::SC_SliderHandle, 
00375                                             this);
00376   option.sliderPosition = d->m_MaximumPosition;
00377 
00378   const QRect ur = style()->subControlRect( QStyle::CC_Slider, 
00379                                             &option, 
00380                                             QStyle::SC_SliderHandle, 
00381                                             this);
00382 
00383   QRect sr = style()->subControlRect( QStyle::CC_Slider, 
00384                                       &option, 
00385                                       QStyle::SC_SliderGroove, 
00386                                       this);
00387 
00388   QRect rangeBox = QRect( 
00389       QPoint( qMin( lr.center().x(), ur.center().x() ), sr.center().y() - 2), 
00390       QPoint(qMax( lr.center().x(), ur.center().x() ), sr.center().y() + 1));
00391 
00392 
00393   // -----------------------------
00394   // Render the range
00395   //
00396   QRect groove = this->style()->subControlRect( QStyle::CC_Slider, 
00397                                                 &option, 
00398                                                 QStyle::SC_SliderGroove, 
00399                                                 this );
00400   groove.adjust(0, 0, -1, 0);
00401 
00402   painter.setPen( QPen( this->palette().color(QPalette::Dark).light(90), 0));
00403 
00404   // Create default colors based on the transfer function.
00405   //
00406   QColor highlight = this->palette().color(QPalette::Highlight);
00407   QLinearGradient gradient( groove.center().x(), groove.top(),
00408                             groove.center().x(), groove.bottom());
00409 
00410   // TODO: Set this based on the supplied transfer function
00411   QColor l = Qt::darkGray;
00412   QColor u = Qt::black;
00413 
00414   gradient.setColorAt(0, l);
00415   gradient.setColorAt(1, u);
00416 
00417   painter.setBrush(gradient);
00418   painter.setPen(QPen(highlight.dark(140), 0));
00419 
00420   painter.drawRect( rangeBox.intersected(groove) );
00421 
00422   //  -----------------------------------
00423   // Render the sliders
00424   //
00425   if (d->m_SelectedSlider)
00426     {
00427     d->drawMaximumSlider( &painter );
00428     d->drawMinimumSlider( &painter );
00429     }
00430   else
00431     {
00432     d->drawMinimumSlider( &painter );
00433     d->drawMaximumSlider( &painter );
00434     }
00435 }
00436 
00437 // --------------------------------------------------------------------------
00438 // Standard Qt UI events
00439 void ctkRangeSlider::mousePressEvent(QMouseEvent* mouseEvent)
00440 {
00441   CTK_D(ctkRangeSlider);
00442   if (minimum() == maximum() || (mouseEvent->buttons() ^ mouseEvent->button()))
00443     {
00444     mouseEvent->ignore();
00445     return;
00446     }
00447   
00448   QStyleOptionSlider option;
00449   this->initStyleOption( &option );
00450 
00451   // Check if the first slider is pressed
00452   
00453   if (!this->isSliderDown())
00454     {
00455     option.sliderPosition = d->m_MinimumPosition;
00456     option.sliderValue    = d->m_MinimumValue;
00457 
00458     QStyle::SubControl& control = d->m_MinimumSliderSelected;
00459     
00460     control = this->style()->hitTestComplexControl( QStyle::CC_Slider, 
00461                                                     &option, 
00462                                                     mouseEvent->pos(), 
00463                                                     this);
00464 
00465     if (control == QStyle::SC_SliderHandle)
00466       {
00467       d->m_SelectedSlider = true;
00468       d->m_SubclassPosition = d->m_MinimumValue;
00469 
00470       const QRect sr = this->style()->subControlRect( QStyle::CC_Slider, 
00471                                                       &option, 
00472                                                       QStyle::SC_SliderHandle, 
00473                                                       this);
00474       
00475       d->m_SubclassClickOffset = mouseEvent->pos().x() - sr.topLeft().x();
00476 
00477       this->setSliderDown(true);
00478 
00479       if (control != d->m_MinimumSliderSelected)
00480         {
00481         this->update(sr);
00482         }
00483       }
00484     }
00485 
00486 
00487   // Check if the other slider is pressed
00488 
00489   if (!this->isSliderDown())
00490     {
00491     option.sliderPosition = d->m_MaximumPosition;
00492     option.sliderValue    = d->m_MaximumValue;
00493 
00494     QStyle::SubControl& control = d->m_MaximumSliderSelected;
00495     
00496     control = this->style()->hitTestComplexControl( QStyle::CC_Slider, 
00497                                                     &option, 
00498                                                     mouseEvent->pos(), 
00499                                                     this);
00500 
00501     if (control == QStyle::SC_SliderHandle)
00502       {
00503       d->m_SelectedSlider = false;
00504       d->m_SubclassPosition = d->m_MaximumValue;
00505 
00506       const QRect sr = this->style()->subControlRect( QStyle::CC_Slider, 
00507                                                       &option, 
00508                                                       QStyle::SC_SliderHandle, 
00509                                                       this);
00510 
00511       d->m_SubclassClickOffset = mouseEvent->pos().x() - sr.topLeft().x();
00512 
00513       this->setSliderDown(true);
00514 
00515       if (d->m_MaximumSliderSelected != control)
00516         {
00517         this->update(sr);
00518         }
00519       }
00520     }
00521 
00522 
00523   // Accept the mouseEvent
00524   mouseEvent->accept();
00525 }
00526 
00527 // --------------------------------------------------------------------------
00528 // Standard Qt UI events
00529 void ctkRangeSlider::mouseMoveEvent(QMouseEvent* mouseEvent)
00530 {
00531   CTK_D(ctkRangeSlider);
00532   if (d->m_MinimumSliderSelected == QStyle::SC_SliderHandle ||
00533       d->m_MaximumSliderSelected == QStyle::SC_SliderHandle)
00534     {
00535 
00536     QStyleOptionSlider option;
00537     this->initStyleOption(&option);
00538 
00539     const int m = style()->pixelMetric( QStyle::PM_MaximumDragDistance, &option, this );
00540 
00541     int newPosition = d->pixelPosToRangeValue(
00542         mouseEvent->pos().x() - d->m_SubclassClickOffset);
00543 
00544     if (m >= 0)
00545       {
00546       const QRect r = rect().adjusted(-m, -m, m, m);
00547       if (!r.contains(mouseEvent->pos()))
00548         {
00549         newPosition = d->m_SubclassPosition;
00550         }
00551       }
00552 
00553     if (d->m_MinimumSliderSelected == QStyle::SC_SliderHandle)
00554       {
00555       this->setMinimumPosition(qMin(newPosition,d->m_MaximumPosition));
00556       }
00557     else if (d->m_MaximumSliderSelected == QStyle::SC_SliderHandle)
00558       {
00559       this->setMaximumPosition(qMax(d->m_MinimumPosition, newPosition));
00560       }
00561     mouseEvent->accept();
00562     }
00563 
00564   mouseEvent->ignore();  
00565 }
00566 
00567 // --------------------------------------------------------------------------
00568 // Standard Qt UI mouseEvents
00569 void ctkRangeSlider::mouseReleaseEvent(QMouseEvent* mouseEvent)
00570 {
00571   CTK_D(ctkRangeSlider);
00572   QSlider::mouseReleaseEvent(mouseEvent);
00573   setSliderDown(false);
00574   
00575   d->m_MinimumSliderSelected = QStyle::SC_None;
00576   d->m_MaximumSliderSelected = QStyle::SC_None;
00577 
00578   this->update();
00579 }
00580 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines

Generated on 21 May 2010 for CTK by  doxygen 1.6.1