ctkDoubleSpinBox.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  1. /*=========================================================================
  2. Library: CTK
  3. Copyright (c) Kitware Inc.
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.apache.org/licenses/LICENSE-2.0.txt
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. =========================================================================*/
  14. // CTK includes
  15. #include "ctkDoubleSpinBox.h"
  16. #include "ctkUtils.h"
  17. #include "ctkPimpl.h"
  18. #include <QDebug>
  19. // Qt includes
  20. #include <QDoubleSpinBox>
  21. #include <QEvent>
  22. #include <QHBoxLayout>
  23. #include <QKeyEvent>
  24. #include <QShortcut>
  25. #include <QSizePolicy>
  26. #include <QVariant>
  27. //-----------------------------------------------------------------------------
  28. class ctkDoubleSpinBoxPrivate
  29. {
  30. Q_DECLARE_PUBLIC(ctkDoubleSpinBox);
  31. protected:
  32. ctkDoubleSpinBox* const q_ptr;
  33. public:
  34. ctkDoubleSpinBoxPrivate(ctkDoubleSpinBox& object);
  35. QDoubleSpinBox* SpinBox;
  36. ctkDoubleSpinBox::SetMode Mode;
  37. int DefaultDecimals;
  38. ctkDoubleSpinBox::DecimalsOptions DOption;
  39. void init();
  40. // Compare two double previously rounded according to the number of decimals
  41. bool compare(double x1, double x2) const;
  42. // Set the number of decimals of the spinbox and emit the signal
  43. // No check if they are the same.
  44. void setDecimals(int dec);
  45. };
  46. //-----------------------------------------------------------------------------
  47. ctkDoubleSpinBoxPrivate::ctkDoubleSpinBoxPrivate(ctkDoubleSpinBox& object) : q_ptr(&object)
  48. {
  49. qRegisterMetaType<ctkDoubleSpinBox::SetMode>("ctkDoubleSpinBox::SetMode");
  50. qRegisterMetaType<ctkDoubleSpinBox::DecimalsOptions>("ctkDoubleSpinBox::DecimalsOption");
  51. this->SpinBox = 0;
  52. this->Mode = ctkDoubleSpinBox::SetIfDifferent;
  53. this->DefaultDecimals = 2;
  54. this->DOption = ctkDoubleSpinBox::UseShortcuts;
  55. }
  56. //-----------------------------------------------------------------------------
  57. void ctkDoubleSpinBoxPrivate::init()
  58. {
  59. Q_Q(ctkDoubleSpinBox);
  60. this->SpinBox = new QDoubleSpinBox(q);
  61. QObject::connect(this->SpinBox, SIGNAL(valueChanged(double)),
  62. q, SIGNAL(valueChanged(double)));
  63. QObject::connect(this->SpinBox, SIGNAL(valueChanged(const QString&)),
  64. q, SIGNAL(valueChanged(const QString &)));
  65. QObject::connect(this->SpinBox, SIGNAL(editingFinished()),
  66. q, SIGNAL(editingFinished()));
  67. QHBoxLayout* l = new QHBoxLayout(q);
  68. l->addWidget(this->SpinBox);
  69. l->setContentsMargins(0,0,0,0);
  70. q->setLayout(l);
  71. q->setSizePolicy(QSizePolicy(QSizePolicy::Minimum,
  72. QSizePolicy::Fixed, QSizePolicy::ButtonBox));
  73. this->SpinBox->installEventFilter(q);
  74. }
  75. //-----------------------------------------------------------------------------
  76. bool ctkDoubleSpinBoxPrivate::compare(double x1, double x2) const
  77. {
  78. Q_Q(const ctkDoubleSpinBox);
  79. return q->round(x1) == q->round(x2);
  80. }
  81. //-----------------------------------------------------------------------------
  82. void ctkDoubleSpinBoxPrivate::setDecimals(int dec)
  83. {
  84. Q_Q(ctkDoubleSpinBox);
  85. this->SpinBox->setDecimals(dec);
  86. emit q->decimalsChanged(dec);
  87. }
  88. //-----------------------------------------------------------------------------
  89. ctkDoubleSpinBox::ctkDoubleSpinBox(QWidget* newParent)
  90. : QWidget(newParent)
  91. , d_ptr(new ctkDoubleSpinBoxPrivate(*this))
  92. {
  93. Q_D(ctkDoubleSpinBox);
  94. d->init();
  95. }
  96. //-----------------------------------------------------------------------------
  97. ctkDoubleSpinBox::ctkDoubleSpinBox(ctkDoubleSpinBox::SetMode mode, QWidget* newParent)
  98. : QWidget(newParent)
  99. , d_ptr(new ctkDoubleSpinBoxPrivate(*this))
  100. {
  101. Q_D(ctkDoubleSpinBox);
  102. d->init();
  103. this->setSetMode(mode);
  104. }
  105. //-----------------------------------------------------------------------------
  106. double ctkDoubleSpinBox::value() const
  107. {
  108. Q_D(const ctkDoubleSpinBox);
  109. return d->SpinBox->value();
  110. }
  111. //-----------------------------------------------------------------------------
  112. double ctkDoubleSpinBox::displayedValue() const
  113. {
  114. return this->round(this->value());
  115. }
  116. //-----------------------------------------------------------------------------
  117. QString ctkDoubleSpinBox::text() const
  118. {
  119. Q_D(const ctkDoubleSpinBox);
  120. return d->SpinBox->text();
  121. }
  122. //-----------------------------------------------------------------------------
  123. QString ctkDoubleSpinBox::cleanText() const
  124. {
  125. Q_D(const ctkDoubleSpinBox);
  126. return d->SpinBox->cleanText();
  127. }
  128. //-----------------------------------------------------------------------------
  129. Qt::Alignment ctkDoubleSpinBox::alignment() const
  130. {
  131. Q_D(const ctkDoubleSpinBox);
  132. return d->SpinBox->alignment();
  133. }
  134. //-----------------------------------------------------------------------------
  135. void ctkDoubleSpinBox::setAlignment(Qt::Alignment flag)
  136. {
  137. Q_D(const ctkDoubleSpinBox);
  138. if (d->Mode == ctkDoubleSpinBox::SetIfDifferent && flag == d->SpinBox->alignment())
  139. {
  140. return;
  141. }
  142. d->SpinBox->setAlignment(flag);
  143. }
  144. //-----------------------------------------------------------------------------
  145. void ctkDoubleSpinBox::setFrame(bool frame)
  146. {
  147. Q_D(const ctkDoubleSpinBox);
  148. if (d->Mode == ctkDoubleSpinBox::SetIfDifferent && frame == d->SpinBox->hasFrame())
  149. {
  150. return;
  151. }
  152. d->SpinBox->setFrame(frame);
  153. }
  154. //-----------------------------------------------------------------------------
  155. bool ctkDoubleSpinBox::hasFrame() const
  156. {
  157. Q_D(const ctkDoubleSpinBox);
  158. return d->SpinBox->hasFrame();
  159. }
  160. //-----------------------------------------------------------------------------
  161. QString ctkDoubleSpinBox::prefix() const
  162. {
  163. Q_D(const ctkDoubleSpinBox);
  164. return d->SpinBox->prefix();
  165. }
  166. //-----------------------------------------------------------------------------
  167. void ctkDoubleSpinBox::setPrefix(const QString &prefix)
  168. {
  169. Q_D(const ctkDoubleSpinBox);
  170. if (d->Mode == ctkDoubleSpinBox::SetIfDifferent && prefix == d->SpinBox->prefix())
  171. {
  172. return;
  173. }
  174. #if QT_VERSION < 0x040800
  175. /// Setting the prefix doesn't recompute the sizehint, do it manually here:
  176. /// See: http://bugreports.qt.nokia.com/browse/QTBUG-9530
  177. d->SpinBox->setRange(d->SpinBox->minimum(), d->SpinBox->maximum());
  178. #endif
  179. d->SpinBox->setPrefix(prefix);
  180. }
  181. //-----------------------------------------------------------------------------
  182. QString ctkDoubleSpinBox::suffix() const
  183. {
  184. Q_D(const ctkDoubleSpinBox);
  185. return d->SpinBox->suffix();
  186. }
  187. //-----------------------------------------------------------------------------
  188. void ctkDoubleSpinBox::setSuffix(const QString &suffix)
  189. {
  190. Q_D(const ctkDoubleSpinBox);
  191. if (d->Mode == ctkDoubleSpinBox::SetIfDifferent && suffix == d->SpinBox->suffix())
  192. {
  193. return;
  194. }
  195. #if QT_VERSION < 0x040800
  196. /// Setting the suffix doesn't recompute the sizehint, do it manually here:
  197. /// See: http://bugreports.qt.nokia.com/browse/QTBUG-9530
  198. d->SpinBox->setRange(d->SpinBox->minimum(), d->SpinBox->maximum());
  199. #endif
  200. d->SpinBox->setSuffix(suffix);
  201. }
  202. //-----------------------------------------------------------------------------
  203. double ctkDoubleSpinBox::singleStep() const
  204. {
  205. Q_D(const ctkDoubleSpinBox);
  206. return d->SpinBox->singleStep();
  207. }
  208. //-----------------------------------------------------------------------------
  209. void ctkDoubleSpinBox::setSingleStep(double step)
  210. {
  211. Q_D(ctkDoubleSpinBox);
  212. if (d->Mode == ctkDoubleSpinBox::SetIfDifferent
  213. && d->compare(step, this->singleStep()))
  214. {
  215. return;
  216. }
  217. d->SpinBox->setSingleStep(step);
  218. }
  219. //-----------------------------------------------------------------------------
  220. double ctkDoubleSpinBox::minimum() const
  221. {
  222. Q_D(const ctkDoubleSpinBox);
  223. return d->SpinBox->minimum();
  224. }
  225. //-----------------------------------------------------------------------------
  226. void ctkDoubleSpinBox::setMinimum(double min)
  227. {
  228. Q_D(ctkDoubleSpinBox);
  229. if (d->Mode == ctkDoubleSpinBox::SetIfDifferent
  230. && d->compare(min, this->minimum()))
  231. {
  232. return;
  233. }
  234. d->SpinBox->setMinimum(min);
  235. }
  236. //-----------------------------------------------------------------------------
  237. double ctkDoubleSpinBox::maximum() const
  238. {
  239. Q_D(const ctkDoubleSpinBox);
  240. return d->SpinBox->maximum();
  241. }
  242. //-----------------------------------------------------------------------------
  243. void ctkDoubleSpinBox::setMaximum(double max)
  244. {
  245. Q_D(ctkDoubleSpinBox);
  246. if (d->Mode == ctkDoubleSpinBox::SetIfDifferent
  247. && d->compare(max, this->maximum()))
  248. {
  249. return;
  250. }
  251. d->SpinBox->setMaximum(max);
  252. }
  253. //-----------------------------------------------------------------------------
  254. void ctkDoubleSpinBox::setRange(double min, double max)
  255. {
  256. Q_D(ctkDoubleSpinBox);
  257. if (d->Mode == ctkDoubleSpinBox::SetIfDifferent
  258. && d->compare(max, this->maximum()) && d->compare(min, this->minimum()))
  259. {
  260. return;
  261. }
  262. d->SpinBox->setRange(min, max);
  263. }
  264. //-----------------------------------------------------------------------------
  265. int ctkDoubleSpinBox::decimals() const
  266. {
  267. Q_D(const ctkDoubleSpinBox);
  268. return d->SpinBox->decimals();
  269. }
  270. //-----------------------------------------------------------------------------
  271. void ctkDoubleSpinBox::setDecimals(int dec)
  272. {
  273. Q_D(ctkDoubleSpinBox);
  274. dec = qMax(0, dec);
  275. if (d->Mode == ctkDoubleSpinBox::SetIfDifferent && dec == this->decimals())
  276. {
  277. return;
  278. }
  279. d->DefaultDecimals = dec;
  280. d->setDecimals(d->DefaultDecimals);
  281. }
  282. //-----------------------------------------------------------------------------
  283. double ctkDoubleSpinBox::round(double value) const
  284. {
  285. Q_D(const ctkDoubleSpinBox);
  286. return QString::number(value, 'f', d->SpinBox->decimals()).toDouble();
  287. }
  288. //-----------------------------------------------------------------------------
  289. QDoubleSpinBox* ctkDoubleSpinBox::spinBox() const
  290. {
  291. Q_D(const ctkDoubleSpinBox);
  292. return d->SpinBox;
  293. }
  294. //-----------------------------------------------------------------------------
  295. void ctkDoubleSpinBox::setValue(double value)
  296. {
  297. Q_D(ctkDoubleSpinBox);
  298. if (d->Mode == ctkDoubleSpinBox::SetIfDifferent)
  299. {
  300. this->setValueIfDifferent(value);
  301. }
  302. else
  303. {
  304. this->setValueAlways(value);
  305. }
  306. }
  307. //-----------------------------------------------------------------------------
  308. void ctkDoubleSpinBox::setValueIfDifferent(double value)
  309. {
  310. Q_D(ctkDoubleSpinBox);
  311. if (! d->compare(this->value(), value))
  312. {
  313. d->SpinBox->setValue(value);
  314. }
  315. }
  316. //-----------------------------------------------------------------------------
  317. void ctkDoubleSpinBox::setValueAlways(double value)
  318. {
  319. Q_D(const ctkDoubleSpinBox);
  320. d->SpinBox->setValue(value);
  321. }
  322. //-----------------------------------------------------------------------------
  323. void ctkDoubleSpinBox::stepUp()
  324. {
  325. Q_D(const ctkDoubleSpinBox);
  326. d->SpinBox->stepUp();
  327. }
  328. //-----------------------------------------------------------------------------
  329. void ctkDoubleSpinBox::stepDown()
  330. {
  331. Q_D(const ctkDoubleSpinBox);
  332. d->SpinBox->stepDown();
  333. }
  334. //-----------------------------------------------------------------------------
  335. ctkDoubleSpinBox::SetMode ctkDoubleSpinBox::setMode() const
  336. {
  337. Q_D(const ctkDoubleSpinBox);
  338. return d->Mode;
  339. }
  340. //-----------------------------------------------------------------------------
  341. void ctkDoubleSpinBox::setSetMode(ctkDoubleSpinBox::SetMode newMode)
  342. {
  343. Q_D(ctkDoubleSpinBox);
  344. d->Mode = newMode;
  345. }
  346. //-----------------------------------------------------------------------------
  347. ctkDoubleSpinBox::DecimalsOptions ctkDoubleSpinBox::decimalsOption()
  348. {
  349. Q_D(const ctkDoubleSpinBox);
  350. return d->DOption;
  351. }
  352. //-----------------------------------------------------------------------------
  353. void ctkDoubleSpinBox::setDecimalsOption(ctkDoubleSpinBox::DecimalsOptions option)
  354. {
  355. Q_D(ctkDoubleSpinBox);
  356. if (d->Mode == ctkDoubleSpinBox::SetIfDifferent && option == d->DOption)
  357. {
  358. return;
  359. }
  360. d->DOption = option;
  361. }
  362. //-----------------------------------------------------------------------------
  363. bool ctkDoubleSpinBox::eventFilter(QObject* obj, QEvent* event)
  364. {
  365. Q_D(ctkDoubleSpinBox);
  366. if (d->DOption & ctkDoubleSpinBox::UseShortcuts &&
  367. obj == d->SpinBox && event->type() == QEvent::KeyPress)
  368. {
  369. QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
  370. Q_ASSERT(keyEvent);
  371. if (keyEvent->modifiers() & Qt::ControlModifier)
  372. {
  373. if (keyEvent->key() == Qt::Key_Plus
  374. || keyEvent->key() == Qt::Key_Equal)
  375. {
  376. d->setDecimals(this->decimals() + 1);
  377. return true;
  378. }
  379. else if (keyEvent->key() == Qt::Key_Minus)
  380. {
  381. d->setDecimals(this->decimals() - 1);
  382. return true;
  383. }
  384. else if (keyEvent->key() == Qt::Key_0)
  385. {
  386. d->setDecimals(d->DefaultDecimals);
  387. return true;
  388. }
  389. }
  390. return QWidget::eventFilter(obj, event);
  391. }
  392. else
  393. {
  394. // pass the event on to the parent class
  395. return QWidget::eventFilter(obj, event);
  396. }
  397. }