qwt_abstract_slider.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597
  1. /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
  2. * Qwt Widget Library
  3. * Copyright (C) 1997 Josef Wilgen
  4. * Copyright (C) 2002 Uwe Rathmann
  5. *
  6. * This library is free software; you can redistribute it and/or
  7. * modify it under the terms of the Qwt License, Version 1.0
  8. *****************************************************************************/
  9. #include "qwt_abstract_slider.h"
  10. #include "qwt_math.h"
  11. #include <qevent.h>
  12. #include <qdatetime.h>
  13. #if QT_VERSION < 0x040601
  14. #define qFabs(x) ::fabs(x)
  15. #define qExp(x) ::exp(x)
  16. #endif
  17. class QwtAbstractSlider::PrivateData
  18. {
  19. public:
  20. PrivateData():
  21. scrollMode( ScrNone ),
  22. mouseOffset( 0.0 ),
  23. tracking( true ),
  24. tmrID( 0 ),
  25. updTime( 150 ),
  26. mass( 0.0 ),
  27. readOnly( false )
  28. {
  29. }
  30. int scrollMode;
  31. double mouseOffset;
  32. int direction;
  33. int tracking;
  34. int tmrID;
  35. int updTime;
  36. int timerTick;
  37. QTime time;
  38. double speed;
  39. double mass;
  40. Qt::Orientation orientation;
  41. bool readOnly;
  42. };
  43. /*!
  44. \brief Constructor
  45. \param orientation Orientation
  46. \param parent Parent widget
  47. */
  48. QwtAbstractSlider::QwtAbstractSlider(
  49. Qt::Orientation orientation, QWidget *parent ):
  50. QWidget( parent, NULL )
  51. {
  52. d_data = new QwtAbstractSlider::PrivateData;
  53. d_data->orientation = orientation;
  54. setFocusPolicy( Qt::TabFocus );
  55. }
  56. //! Destructor
  57. QwtAbstractSlider::~QwtAbstractSlider()
  58. {
  59. if ( d_data->tmrID )
  60. killTimer( d_data->tmrID );
  61. delete d_data;
  62. }
  63. /*!
  64. En/Disable read only mode
  65. In read only mode the slider can't be controlled by mouse
  66. or keyboard.
  67. \param readOnly Enables in case of true
  68. \sa isReadOnly()
  69. */
  70. void QwtAbstractSlider::setReadOnly( bool readOnly )
  71. {
  72. d_data->readOnly = readOnly;
  73. update();
  74. }
  75. /*!
  76. In read only mode the slider can't be controlled by mouse
  77. or keyboard.
  78. \return true if read only
  79. \sa setReadOnly()
  80. */
  81. bool QwtAbstractSlider::isReadOnly() const
  82. {
  83. return d_data->readOnly;
  84. }
  85. /*!
  86. \brief Set the orientation.
  87. \param o Orientation. Allowed values are
  88. Qt::Horizontal and Qt::Vertical.
  89. */
  90. void QwtAbstractSlider::setOrientation( Qt::Orientation o )
  91. {
  92. d_data->orientation = o;
  93. }
  94. /*!
  95. \return Orientation
  96. \sa setOrientation()
  97. */
  98. Qt::Orientation QwtAbstractSlider::orientation() const
  99. {
  100. return d_data->orientation;
  101. }
  102. //! Stop updating if automatic scrolling is active
  103. void QwtAbstractSlider::stopMoving()
  104. {
  105. if ( d_data->tmrID )
  106. {
  107. killTimer( d_data->tmrID );
  108. d_data->tmrID = 0;
  109. }
  110. }
  111. /*!
  112. \brief Specify the update interval for automatic scrolling
  113. \param t update interval in milliseconds
  114. \sa getScrollMode()
  115. */
  116. void QwtAbstractSlider::setUpdateTime( int t )
  117. {
  118. if ( t < 50 )
  119. t = 50;
  120. d_data->updTime = t;
  121. }
  122. /*!
  123. Mouse press event handler
  124. \param e Mouse event
  125. */
  126. void QwtAbstractSlider::mousePressEvent( QMouseEvent *e )
  127. {
  128. if ( isReadOnly() )
  129. {
  130. e->ignore();
  131. return;
  132. }
  133. if ( !isValid() )
  134. return;
  135. const QPoint &p = e->pos();
  136. d_data->timerTick = 0;
  137. getScrollMode( p, d_data->scrollMode, d_data->direction );
  138. stopMoving();
  139. switch ( d_data->scrollMode )
  140. {
  141. case ScrPage:
  142. case ScrTimer:
  143. d_data->mouseOffset = 0;
  144. d_data->tmrID = startTimer( qMax( 250, 2 * d_data->updTime ) );
  145. break;
  146. case ScrMouse:
  147. d_data->time.start();
  148. d_data->speed = 0;
  149. d_data->mouseOffset = getValue( p ) - value();
  150. Q_EMIT sliderPressed();
  151. break;
  152. default:
  153. d_data->mouseOffset = 0;
  154. d_data->direction = 0;
  155. break;
  156. }
  157. }
  158. //! Emits a valueChanged() signal if necessary
  159. void QwtAbstractSlider::buttonReleased()
  160. {
  161. if ( ( !d_data->tracking ) || ( value() != prevValue() ) )
  162. Q_EMIT valueChanged( value() );
  163. }
  164. /*!
  165. Mouse Release Event handler
  166. \param e Mouse event
  167. */
  168. void QwtAbstractSlider::mouseReleaseEvent( QMouseEvent *e )
  169. {
  170. if ( isReadOnly() )
  171. {
  172. e->ignore();
  173. return;
  174. }
  175. if ( !isValid() )
  176. return;
  177. const double inc = step();
  178. switch ( d_data->scrollMode )
  179. {
  180. case ScrMouse:
  181. {
  182. setPosition( e->pos() );
  183. d_data->direction = 0;
  184. d_data->mouseOffset = 0;
  185. if ( d_data->mass > 0.0 )
  186. {
  187. const int ms = d_data->time.elapsed();
  188. if ( ( qFabs( d_data->speed ) > 0.0 ) && ( ms < 50 ) )
  189. d_data->tmrID = startTimer( d_data->updTime );
  190. }
  191. else
  192. {
  193. d_data->scrollMode = ScrNone;
  194. buttonReleased();
  195. }
  196. Q_EMIT sliderReleased();
  197. break;
  198. }
  199. case ScrDirect:
  200. {
  201. setPosition( e->pos() );
  202. d_data->direction = 0;
  203. d_data->mouseOffset = 0;
  204. d_data->scrollMode = ScrNone;
  205. buttonReleased();
  206. break;
  207. }
  208. case ScrPage:
  209. {
  210. stopMoving();
  211. if ( !d_data->timerTick )
  212. QwtDoubleRange::incPages( d_data->direction );
  213. d_data->timerTick = 0;
  214. buttonReleased();
  215. d_data->scrollMode = ScrNone;
  216. break;
  217. }
  218. case ScrTimer:
  219. {
  220. stopMoving();
  221. if ( !d_data->timerTick )
  222. QwtDoubleRange::fitValue( value() + double( d_data->direction ) * inc );
  223. d_data->timerTick = 0;
  224. buttonReleased();
  225. d_data->scrollMode = ScrNone;
  226. break;
  227. }
  228. default:
  229. {
  230. d_data->scrollMode = ScrNone;
  231. buttonReleased();
  232. }
  233. }
  234. }
  235. /*!
  236. Move the slider to a specified point, adjust the value
  237. and emit signals if necessary.
  238. */
  239. void QwtAbstractSlider::setPosition( const QPoint &p )
  240. {
  241. QwtDoubleRange::fitValue( getValue( p ) - d_data->mouseOffset );
  242. }
  243. /*!
  244. \brief Enables or disables tracking.
  245. If tracking is enabled, the slider emits a
  246. valueChanged() signal whenever its value
  247. changes (the default behaviour). If tracking
  248. is disabled, the value changed() signal will only
  249. be emitted if:<ul>
  250. <li>the user releases the mouse
  251. button and the value has changed or
  252. <li>at the end of automatic scrolling.</ul>
  253. Tracking is enabled by default.
  254. \param enable \c true (enable) or \c false (disable) tracking.
  255. */
  256. void QwtAbstractSlider::setTracking( bool enable )
  257. {
  258. d_data->tracking = enable;
  259. }
  260. /*!
  261. Mouse Move Event handler
  262. \param e Mouse event
  263. */
  264. void QwtAbstractSlider::mouseMoveEvent( QMouseEvent *e )
  265. {
  266. if ( isReadOnly() )
  267. {
  268. e->ignore();
  269. return;
  270. }
  271. if ( !isValid() )
  272. return;
  273. if ( d_data->scrollMode == ScrMouse )
  274. {
  275. setPosition( e->pos() );
  276. if ( d_data->mass > 0.0 )
  277. {
  278. double ms = double( d_data->time.elapsed() );
  279. if ( ms < 1.0 )
  280. ms = 1.0;
  281. d_data->speed = ( exactValue() - exactPrevValue() ) / ms;
  282. d_data->time.start();
  283. }
  284. if ( value() != prevValue() )
  285. Q_EMIT sliderMoved( value() );
  286. }
  287. }
  288. /*!
  289. Wheel Event handler
  290. \param e Whell event
  291. */
  292. void QwtAbstractSlider::wheelEvent( QWheelEvent *e )
  293. {
  294. if ( isReadOnly() )
  295. {
  296. e->ignore();
  297. return;
  298. }
  299. if ( !isValid() )
  300. return;
  301. int mode = ScrNone, direction = 0;
  302. // Give derived classes a chance to say ScrNone
  303. getScrollMode( e->pos(), mode, direction );
  304. if ( mode != ScrNone )
  305. {
  306. // Most mouse types work in steps of 15 degrees, in which case
  307. // the delta value is a multiple of 120
  308. const int inc = e->delta() / 120;
  309. QwtDoubleRange::incPages( inc );
  310. if ( value() != prevValue() )
  311. Q_EMIT sliderMoved( value() );
  312. }
  313. }
  314. /*!
  315. Handles key events
  316. - Key_Down, KeyLeft\n
  317. Decrement by 1
  318. - Key_Up, Key_Right\n
  319. Increment by 1
  320. \param e Key event
  321. \sa isReadOnly()
  322. */
  323. void QwtAbstractSlider::keyPressEvent( QKeyEvent *e )
  324. {
  325. if ( isReadOnly() )
  326. {
  327. e->ignore();
  328. return;
  329. }
  330. if ( !isValid() )
  331. return;
  332. int increment = 0;
  333. switch ( e->key() )
  334. {
  335. case Qt::Key_Down:
  336. if ( orientation() == Qt::Vertical )
  337. increment = -1;
  338. break;
  339. case Qt::Key_Up:
  340. if ( orientation() == Qt::Vertical )
  341. increment = 1;
  342. break;
  343. case Qt::Key_Left:
  344. if ( orientation() == Qt::Horizontal )
  345. increment = -1;
  346. break;
  347. case Qt::Key_Right:
  348. if ( orientation() == Qt::Horizontal )
  349. increment = 1;
  350. break;
  351. default:;
  352. e->ignore();
  353. }
  354. if ( increment != 0 )
  355. {
  356. QwtDoubleRange::incValue( increment );
  357. if ( value() != prevValue() )
  358. Q_EMIT sliderMoved( value() );
  359. }
  360. }
  361. /*!
  362. Qt timer event
  363. \param e Timer event
  364. */
  365. void QwtAbstractSlider::timerEvent( QTimerEvent * )
  366. {
  367. const double inc = step();
  368. switch ( d_data->scrollMode )
  369. {
  370. case ScrMouse:
  371. {
  372. if ( d_data->mass > 0.0 )
  373. {
  374. d_data->speed *= qExp( - double( d_data->updTime ) * 0.001 / d_data->mass );
  375. const double newval =
  376. exactValue() + d_data->speed * double( d_data->updTime );
  377. QwtDoubleRange::fitValue( newval );
  378. // stop if d_data->speed < one step per second
  379. if ( qFabs( d_data->speed ) < 0.001 * qFabs( step() ) )
  380. {
  381. d_data->speed = 0;
  382. stopMoving();
  383. buttonReleased();
  384. }
  385. }
  386. else
  387. stopMoving();
  388. break;
  389. }
  390. case ScrPage:
  391. {
  392. QwtDoubleRange::incPages( d_data->direction );
  393. if ( !d_data->timerTick )
  394. {
  395. killTimer( d_data->tmrID );
  396. d_data->tmrID = startTimer( d_data->updTime );
  397. }
  398. break;
  399. }
  400. case ScrTimer:
  401. {
  402. QwtDoubleRange::fitValue( value() + double( d_data->direction ) * inc );
  403. if ( !d_data->timerTick )
  404. {
  405. killTimer( d_data->tmrID );
  406. d_data->tmrID = startTimer( d_data->updTime );
  407. }
  408. break;
  409. }
  410. default:
  411. {
  412. stopMoving();
  413. break;
  414. }
  415. }
  416. d_data->timerTick = 1;
  417. }
  418. /*!
  419. Notify change of value
  420. This function can be reimplemented by derived classes
  421. in order to keep track of changes, i.e. repaint the widget.
  422. The default implementation emits a valueChanged() signal
  423. if tracking is enabled.
  424. */
  425. void QwtAbstractSlider::valueChange()
  426. {
  427. if ( d_data->tracking )
  428. Q_EMIT valueChanged( value() );
  429. }
  430. /*!
  431. \brief Set the slider's mass for flywheel effect.
  432. If the slider's mass is greater then 0, it will continue
  433. to move after the mouse button has been released. Its speed
  434. decreases with time at a rate depending on the slider's mass.
  435. A large mass means that it will continue to move for a
  436. long time.
  437. Derived widgets may overload this function to make it public.
  438. \param val New mass in kg
  439. \bug If the mass is smaller than 1g, it is set to zero.
  440. The maximal mass is limited to 100kg.
  441. \sa mass()
  442. */
  443. void QwtAbstractSlider::setMass( double val )
  444. {
  445. if ( val < 0.001 )
  446. d_data->mass = 0.0;
  447. else if ( val > 100.0 )
  448. d_data->mass = 100.0;
  449. else
  450. d_data->mass = val;
  451. }
  452. /*!
  453. \return mass
  454. \sa setMass()
  455. */
  456. double QwtAbstractSlider::mass() const
  457. {
  458. return d_data->mass;
  459. }
  460. /*!
  461. \brief Move the slider to a specified value
  462. This function can be used to move the slider to a value
  463. which is not an integer multiple of the step size.
  464. \param val new value
  465. \sa fitValue()
  466. */
  467. void QwtAbstractSlider::setValue( double val )
  468. {
  469. if ( d_data->scrollMode == ScrMouse )
  470. stopMoving();
  471. QwtDoubleRange::setValue( val );
  472. }
  473. /*!
  474. \brief Set the slider's value to the nearest integer multiple
  475. of the step size.
  476. \param value Value
  477. \sa setValue(), incValue()
  478. */
  479. void QwtAbstractSlider::fitValue( double value )
  480. {
  481. if ( d_data->scrollMode == ScrMouse )
  482. stopMoving();
  483. QwtDoubleRange::fitValue( value );
  484. }
  485. /*!
  486. \brief Increment the value by a specified number of steps
  487. \param steps number of steps
  488. \sa setValue()
  489. */
  490. void QwtAbstractSlider::incValue( int steps )
  491. {
  492. if ( d_data->scrollMode == ScrMouse )
  493. stopMoving();
  494. QwtDoubleRange::incValue( steps );
  495. }
  496. /*!
  497. \sa mouseOffset()
  498. */
  499. void QwtAbstractSlider::setMouseOffset( double offset )
  500. {
  501. d_data->mouseOffset = offset;
  502. }
  503. /*!
  504. \sa setMouseOffset()
  505. */
  506. double QwtAbstractSlider::mouseOffset() const
  507. {
  508. return d_data->mouseOffset;
  509. }
  510. //! sa ScrollMode
  511. int QwtAbstractSlider::scrollMode() const
  512. {
  513. return d_data->scrollMode;
  514. }