qwt_wheel.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618
  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_wheel.h"
  10. #include "qwt_math.h"
  11. #include "qwt_painter.h"
  12. #include <qevent.h>
  13. #include <qdrawutil.h>
  14. #include <qpainter.h>
  15. #include <qstyle.h>
  16. #define NUM_COLORS 30
  17. class QwtWheel::PrivateData
  18. {
  19. public:
  20. PrivateData()
  21. {
  22. viewAngle = 175.0;
  23. totalAngle = 360.0;
  24. tickCnt = 10;
  25. intBorder = 2;
  26. borderWidth = 2;
  27. wheelWidth = 20;
  28. };
  29. QRect sliderRect;
  30. double viewAngle;
  31. double totalAngle;
  32. int tickCnt;
  33. int intBorder;
  34. int borderWidth;
  35. int wheelWidth;
  36. QColor colors[NUM_COLORS];
  37. };
  38. //! Constructor
  39. QwtWheel::QwtWheel( QWidget *parent ):
  40. QwtAbstractSlider( Qt::Horizontal, parent )
  41. {
  42. initWheel();
  43. }
  44. void QwtWheel::initWheel()
  45. {
  46. d_data = new PrivateData;
  47. setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
  48. setAttribute( Qt::WA_WState_OwnSizePolicy, false );
  49. setUpdateTime( 50 );
  50. }
  51. //! Destructor
  52. QwtWheel::~QwtWheel()
  53. {
  54. delete d_data;
  55. }
  56. //! Set up the color array for the background pixmap.
  57. void QwtWheel::setColorArray()
  58. {
  59. if ( !d_data->colors )
  60. return;
  61. const QColor light = palette().color( QPalette::Light );
  62. const QColor dark = palette().color( QPalette::Dark );
  63. if ( !d_data->colors[0].isValid() ||
  64. d_data->colors[0] != light ||
  65. d_data->colors[NUM_COLORS - 1] != dark )
  66. {
  67. d_data->colors[0] = light;
  68. d_data->colors[NUM_COLORS - 1] = dark;
  69. int dh, ds, dv, lh, ls, lv;
  70. d_data->colors[0].getRgb( &lh, &ls, &lv );
  71. d_data->colors[NUM_COLORS - 1].getRgb( &dh, &ds, &dv );
  72. for ( int i = 1; i < NUM_COLORS - 1; ++i )
  73. {
  74. const double factor = double( i ) / double( NUM_COLORS );
  75. d_data->colors[i].setRgb( lh + int( double( dh - lh ) * factor ),
  76. ls + int( double( ds - ls ) * factor ),
  77. lv + int( double( dv - lv ) * factor ) );
  78. }
  79. }
  80. }
  81. /*!
  82. \brief Adjust the number of grooves in the wheel's surface.
  83. The number of grooves is limited to 6 <= cnt <= 50.
  84. Values outside this range will be clipped.
  85. The default value is 10.
  86. \param cnt Number of grooves per 360 degrees
  87. \sa tickCnt()
  88. */
  89. void QwtWheel::setTickCnt( int cnt )
  90. {
  91. d_data->tickCnt = qwtLim( cnt, 6, 50 );
  92. update();
  93. }
  94. /*!
  95. \return Number of grooves in the wheel's surface.
  96. \sa setTickCnt()
  97. */
  98. int QwtWheel::tickCnt() const
  99. {
  100. return d_data->tickCnt;
  101. }
  102. /*!
  103. \return mass
  104. */
  105. double QwtWheel::mass() const
  106. {
  107. return QwtAbstractSlider::mass();
  108. }
  109. /*!
  110. \brief Set the internal border width of the wheel.
  111. The internal border must not be smaller than 1
  112. and is limited in dependence on the wheel's size.
  113. Values outside the allowed range will be clipped.
  114. The internal border defaults to 2.
  115. \param w border width
  116. \sa internalBorder()
  117. */
  118. void QwtWheel::setInternalBorder( int w )
  119. {
  120. const int d = qMin( width(), height() ) / 3;
  121. w = qMin( w, d );
  122. d_data->intBorder = qMax( w, 1 );
  123. layoutWheel();
  124. }
  125. /*!
  126. \return Internal border width of the wheel.
  127. \sa setInternalBorder()
  128. */
  129. int QwtWheel::internalBorder() const
  130. {
  131. return d_data->intBorder;
  132. }
  133. /*!
  134. Draw the Wheel's background gradient
  135. \param painter Painter
  136. \param r Bounding rectangle
  137. */
  138. void QwtWheel::drawWheelBackground( QPainter *painter, const QRect &r )
  139. {
  140. painter->save();
  141. //
  142. // initialize pens
  143. //
  144. const QColor light = palette().color( QPalette::Light );
  145. const QColor dark = palette().color( QPalette::Dark );
  146. QPen lightPen;
  147. lightPen.setColor( light );
  148. lightPen.setWidth( d_data->intBorder );
  149. QPen darkPen;
  150. darkPen.setColor( dark );
  151. darkPen.setWidth( d_data->intBorder );
  152. setColorArray();
  153. //
  154. // initialize auxiliary variables
  155. //
  156. const int nFields = NUM_COLORS * 13 / 10;
  157. const int hiPos = nFields - NUM_COLORS + 1;
  158. if ( orientation() == Qt::Horizontal )
  159. {
  160. const int rx = r.x();
  161. int ry = r.y() + d_data->intBorder;
  162. const int rh = r.height() - 2 * d_data->intBorder;
  163. const int rw = r.width();
  164. //
  165. // draw shaded background
  166. //
  167. int x1 = rx;
  168. for ( int i = 1; i < nFields; i++ )
  169. {
  170. const int x2 = rx + ( rw * i ) / nFields;
  171. painter->fillRect( x1, ry, x2 - x1 + 1 , rh,
  172. d_data->colors[qAbs( i-hiPos )] );
  173. x1 = x2 + 1;
  174. }
  175. painter->fillRect( x1, ry, rw - ( x1 - rx ), rh,
  176. d_data->colors[NUM_COLORS - 1] );
  177. //
  178. // draw internal border
  179. //
  180. painter->setPen( lightPen );
  181. ry = r.y() + d_data->intBorder / 2;
  182. painter->drawLine( r.x(), ry, r.x() + r.width() , ry );
  183. painter->setPen( darkPen );
  184. ry = r.y() + r.height() - ( d_data->intBorder - d_data->intBorder / 2 );
  185. painter->drawLine( r.x(), ry , r.x() + r.width(), ry );
  186. }
  187. else // Qt::Vertical
  188. {
  189. int rx = r.x() + d_data->intBorder;
  190. const int ry = r.y();
  191. const int rh = r.height();
  192. const int rw = r.width() - 2 * d_data->intBorder;
  193. //
  194. // draw shaded background
  195. //
  196. int y1 = ry;
  197. for ( int i = 1; i < nFields; i++ )
  198. {
  199. const int y2 = ry + ( rh * i ) / nFields;
  200. painter->fillRect( rx, y1, rw, y2 - y1 + 1,
  201. d_data->colors[qAbs( i-hiPos )] );
  202. y1 = y2 + 1;
  203. }
  204. painter->fillRect( rx, y1, rw, rh - ( y1 - ry ),
  205. d_data->colors[NUM_COLORS - 1] );
  206. //
  207. // draw internal borders
  208. //
  209. painter->setPen( lightPen );
  210. rx = r.x() + d_data->intBorder / 2;
  211. painter->drawLine( rx, r.y(), rx, r.y() + r.height() );
  212. painter->setPen( darkPen );
  213. rx = r.x() + r.width() - ( d_data->intBorder - d_data->intBorder / 2 );
  214. painter->drawLine( rx, r.y(), rx , r.y() + r.height() );
  215. }
  216. painter->restore();
  217. }
  218. /*!
  219. \brief Set the total angle which the wheel can be turned.
  220. One full turn of the wheel corresponds to an angle of
  221. 360 degrees. A total angle of n*360 degrees means
  222. that the wheel has to be turned n times around its axis
  223. to get from the minimum value to the maximum value.
  224. The default setting of the total angle is 360 degrees.
  225. \param angle total angle in degrees
  226. \sa totalAngle()
  227. */
  228. void QwtWheel::setTotalAngle( double angle )
  229. {
  230. if ( angle < 0.0 )
  231. angle = 0.0;
  232. d_data->totalAngle = angle;
  233. update();
  234. }
  235. /*!
  236. \return Total angle which the wheel can be turned.
  237. \sa setTotalAngle()
  238. */
  239. double QwtWheel::totalAngle() const
  240. {
  241. return d_data->totalAngle;
  242. }
  243. /*!
  244. \brief Set the wheel's orientation.
  245. \param o Orientation. Allowed values are
  246. Qt::Horizontal and Qt::Vertical.
  247. Defaults to Qt::Horizontal.
  248. \sa QwtAbstractSlider::orientation()
  249. */
  250. void QwtWheel::setOrientation( Qt::Orientation o )
  251. {
  252. if ( orientation() == o )
  253. return;
  254. if ( !testAttribute( Qt::WA_WState_OwnSizePolicy ) )
  255. {
  256. QSizePolicy sp = sizePolicy();
  257. sp.transpose();
  258. setSizePolicy( sp );
  259. setAttribute( Qt::WA_WState_OwnSizePolicy, false );
  260. }
  261. QwtAbstractSlider::setOrientation( o );
  262. layoutWheel();
  263. }
  264. /*!
  265. \brief Specify the visible portion of the wheel.
  266. You may use this function for fine-tuning the appearance of
  267. the wheel. The default value is 175 degrees. The value is
  268. limited from 10 to 175 degrees.
  269. \param angle Visible angle in degrees
  270. \sa viewAngle(), setTotalAngle()
  271. */
  272. void QwtWheel::setViewAngle( double angle )
  273. {
  274. d_data->viewAngle = qwtLim( angle, 10.0, 175.0 );
  275. update();
  276. }
  277. /*!
  278. \return Visible portion of the wheel
  279. \sa setViewAngle(), totalAngle()
  280. */
  281. double QwtWheel::viewAngle() const
  282. {
  283. return d_data->viewAngle;
  284. }
  285. /*!
  286. \brief Redraw the wheel
  287. \param painter painter
  288. \param r contents rectangle
  289. */
  290. void QwtWheel::drawWheel( QPainter *painter, const QRect &r )
  291. {
  292. //
  293. // draw background gradient
  294. //
  295. drawWheelBackground( painter, r );
  296. if ( maxValue() == minValue() || d_data->totalAngle == 0.0 )
  297. return;
  298. const QColor light = palette().color( QPalette::Light );
  299. const QColor dark = palette().color( QPalette::Dark );
  300. const double sign = ( minValue() < maxValue() ) ? 1.0 : -1.0;
  301. double cnvFactor = qAbs( d_data->totalAngle / ( maxValue() - minValue() ) );
  302. const double halfIntv = 0.5 * d_data->viewAngle / cnvFactor;
  303. const double loValue = value() - halfIntv;
  304. const double hiValue = value() + halfIntv;
  305. const double tickWidth = 360.0 / double( d_data->tickCnt ) / cnvFactor;
  306. const double sinArc = qSin( d_data->viewAngle * M_PI / 360.0 );
  307. cnvFactor *= M_PI / 180.0;
  308. //
  309. // draw grooves
  310. //
  311. if ( orientation() == Qt::Horizontal )
  312. {
  313. const double halfSize = double( r.width() ) * 0.5;
  314. int l1 = r.y() + d_data->intBorder;
  315. int l2 = r.y() + r.height() - d_data->intBorder - 1;
  316. // draw one point over the border if border > 1
  317. if ( d_data->intBorder > 1 )
  318. {
  319. l1 --;
  320. l2 ++;
  321. }
  322. const int maxpos = r.x() + r.width() - 2;
  323. const int minpos = r.x() + 2;
  324. //
  325. // draw tick marks
  326. //
  327. for ( double tickValue = qCeil( loValue / tickWidth ) * tickWidth;
  328. tickValue < hiValue; tickValue += tickWidth )
  329. {
  330. //
  331. // calculate position
  332. //
  333. const int tickPos = r.x() + r.width()
  334. - int( halfSize
  335. * ( sinArc + sign * qSin( ( tickValue - value() ) * cnvFactor ) )
  336. / sinArc );
  337. //
  338. // draw vertical line
  339. //
  340. if ( ( tickPos <= maxpos ) && ( tickPos > minpos ) )
  341. {
  342. painter->setPen( dark );
  343. painter->drawLine( tickPos - 1 , l1, tickPos - 1, l2 );
  344. painter->setPen( light );
  345. painter->drawLine( tickPos, l1, tickPos, l2 );
  346. }
  347. }
  348. }
  349. else if ( orientation() == Qt::Vertical )
  350. {
  351. const double halfSize = double( r.height() ) * 0.5;
  352. int l1 = r.x() + d_data->intBorder;
  353. int l2 = r.x() + r.width() - d_data->intBorder - 1;
  354. if ( d_data->intBorder > 1 )
  355. {
  356. l1--;
  357. l2++;
  358. }
  359. const int maxpos = r.y() + r.height() - 2;
  360. const int minpos = r.y() + 2;
  361. //
  362. // draw tick marks
  363. //
  364. for ( double tickValue = qCeil( loValue / tickWidth ) * tickWidth;
  365. tickValue < hiValue; tickValue += tickWidth )
  366. {
  367. //
  368. // calculate position
  369. //
  370. const int tickPos = r.y() + int( halfSize *
  371. ( sinArc + sign * qSin( ( tickValue - value() ) * cnvFactor ) )
  372. / sinArc );
  373. //
  374. // draw horizontal line
  375. //
  376. if ( ( tickPos <= maxpos ) && ( tickPos > minpos ) )
  377. {
  378. painter->setPen( dark );
  379. painter->drawLine( l1, tickPos - 1 , l2, tickPos - 1 );
  380. painter->setPen( light );
  381. painter->drawLine( l1, tickPos, l2, tickPos );
  382. }
  383. }
  384. }
  385. }
  386. //! Determine the value corresponding to a specified point
  387. double QwtWheel::getValue( const QPoint &p )
  388. {
  389. // The reference position is arbitrary, but the
  390. // sign of the offset is important
  391. int w, dx;
  392. if ( orientation() == Qt::Vertical )
  393. {
  394. w = d_data->sliderRect.height();
  395. dx = d_data->sliderRect.y() - p.y();
  396. }
  397. else
  398. {
  399. w = d_data->sliderRect.width();
  400. dx = p.x() - d_data->sliderRect.x();
  401. }
  402. // w pixels is an arc of viewAngle degrees,
  403. // so we convert change in pixels to change in angle
  404. const double ang = dx * d_data->viewAngle / w;
  405. // value range maps to totalAngle degrees,
  406. // so convert the change in angle to a change in value
  407. const double val = ang * ( maxValue() - minValue() ) / d_data->totalAngle;
  408. // Note, range clamping and rasterizing to step is automatically
  409. // handled by QwtAbstractSlider, so we simply return the change in value
  410. return val;
  411. }
  412. //! Qt Resize Event
  413. void QwtWheel::resizeEvent( QResizeEvent * )
  414. {
  415. layoutWheel( false );
  416. }
  417. //! Recalculate the slider's geometry and layout based on
  418. // the current rect and fonts.
  419. // \param update_geometry notify the layout system and call update
  420. // to redraw the scale
  421. void QwtWheel::layoutWheel( bool update_geometry )
  422. {
  423. const QRect r = this->rect();
  424. d_data->sliderRect.setRect( r.x() + d_data->borderWidth, r.y() + d_data->borderWidth,
  425. r.width() - 2*d_data->borderWidth, r.height() - 2*d_data->borderWidth );
  426. if ( update_geometry )
  427. {
  428. updateGeometry();
  429. update();
  430. }
  431. }
  432. //! Qt Paint Event
  433. void QwtWheel::paintEvent( QPaintEvent *e )
  434. {
  435. // Use double-buffering
  436. const QRect &ur = e->rect();
  437. if ( ur.isValid() )
  438. {
  439. QPainter painter( this );
  440. draw( &painter, ur );
  441. }
  442. }
  443. /*!
  444. Redraw panel and wheel
  445. \param painter Painter
  446. */
  447. void QwtWheel::draw( QPainter *painter, const QRect& )
  448. {
  449. qDrawShadePanel( painter, rect().x(), rect().y(),
  450. rect().width(), rect().height(),
  451. palette(), true, d_data->borderWidth );
  452. drawWheel( painter, d_data->sliderRect );
  453. if ( hasFocus() )
  454. QwtPainter::drawFocusRect( painter, this );
  455. }
  456. //! Notify value change
  457. void QwtWheel::valueChange()
  458. {
  459. QwtAbstractSlider::valueChange();
  460. update();
  461. }
  462. /*!
  463. \brief Determine the scrolling mode and direction corresponding
  464. to a specified point
  465. \param p point
  466. \param scrollMode scrolling mode
  467. \param direction direction
  468. */
  469. void QwtWheel::getScrollMode( const QPoint &p, int &scrollMode, int &direction )
  470. {
  471. if ( d_data->sliderRect.contains( p ) )
  472. scrollMode = ScrMouse;
  473. else
  474. scrollMode = ScrNone;
  475. direction = 0;
  476. }
  477. /*!
  478. \brief Set the mass of the wheel
  479. Assigning a mass turns the wheel into a flywheel.
  480. \param val the wheel's mass
  481. */
  482. void QwtWheel::setMass( double val )
  483. {
  484. QwtAbstractSlider::setMass( val );
  485. }
  486. /*!
  487. \brief Set the width of the wheel
  488. Corresponds to the wheel height for horizontal orientation,
  489. and the wheel width for vertical orientation.
  490. \param w the wheel's width
  491. */
  492. void QwtWheel::setWheelWidth( int w )
  493. {
  494. d_data->wheelWidth = w;
  495. layoutWheel();
  496. }
  497. /*!
  498. \return a size hint
  499. */
  500. QSize QwtWheel::sizeHint() const
  501. {
  502. return minimumSizeHint();
  503. }
  504. /*!
  505. \brief Return a minimum size hint
  506. \warning The return value is based on the wheel width.
  507. */
  508. QSize QwtWheel::minimumSizeHint() const
  509. {
  510. QSize sz( 3*d_data->wheelWidth + 2*d_data->borderWidth,
  511. d_data->wheelWidth + 2*d_data->borderWidth );
  512. if ( orientation() != Qt::Horizontal )
  513. sz.transpose();
  514. return sz;
  515. }
  516. /*!
  517. \brief Call update() when the palette changes
  518. */
  519. void QwtWheel::paletteChange( const QPalette& )
  520. {
  521. update();
  522. }