ctkSpinBox.cpp 12 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 "ctkSpinBox.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 ctkSpinBoxPrivate
  29. {
  30. Q_DECLARE_PUBLIC(ctkSpinBox);
  31. protected:
  32. ctkSpinBox* const q_ptr;
  33. public:
  34. ctkSpinBoxPrivate(ctkSpinBox& object);
  35. QDoubleSpinBox* SpinBox;
  36. ctkSpinBox::SetMode Mode;
  37. int DefaultDecimals;
  38. ctkSpinBox::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. ctkSpinBoxPrivate::ctkSpinBoxPrivate(ctkSpinBox& object) : q_ptr(&object)
  48. {
  49. qRegisterMetaType<ctkSpinBox::SetMode>("ctkSpinBox::SetMode");
  50. qRegisterMetaType<ctkSpinBox::DecimalsOptions>("ctkSpinBox::DecimalsOption");
  51. this->SpinBox = 0;
  52. this->Mode = ctkSpinBox::SetIfDifferent;
  53. this->DefaultDecimals = 2;
  54. this->DOption = ctkSpinBox::UseShortcuts;
  55. }
  56. //-----------------------------------------------------------------------------
  57. void ctkSpinBoxPrivate::init()
  58. {
  59. Q_Q(ctkSpinBox);
  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 ctkSpinBoxPrivate::compare(double x1, double x2) const
  77. {
  78. Q_Q(const ctkSpinBox);
  79. return q->round(x1) == q->round(x2);
  80. }
  81. //-----------------------------------------------------------------------------
  82. void ctkSpinBoxPrivate::setDecimals(int dec)
  83. {
  84. Q_Q(ctkSpinBox);
  85. this->SpinBox->setDecimals(dec);
  86. emit q->decimalsChanged(dec);
  87. }
  88. //-----------------------------------------------------------------------------
  89. ctkSpinBox::ctkSpinBox(QWidget* newParent)
  90. : QWidget(newParent)
  91. , d_ptr(new ctkSpinBoxPrivate(*this))
  92. {
  93. Q_D(ctkSpinBox);
  94. d->init();
  95. }
  96. //-----------------------------------------------------------------------------
  97. ctkSpinBox::ctkSpinBox(ctkSpinBox::SetMode mode, QWidget* newParent)
  98. : QWidget(newParent)
  99. , d_ptr(new ctkSpinBoxPrivate(*this))
  100. {
  101. Q_D(ctkSpinBox);
  102. d->init();
  103. this->setSetMode(mode);
  104. }
  105. //-----------------------------------------------------------------------------
  106. double ctkSpinBox::value() const
  107. {
  108. Q_D(const ctkSpinBox);
  109. return d->SpinBox->value();
  110. }
  111. //-----------------------------------------------------------------------------
  112. double ctkSpinBox::displayedValue() const
  113. {
  114. return this->round(this->value());
  115. }
  116. //-----------------------------------------------------------------------------
  117. QString ctkSpinBox::text() const
  118. {
  119. Q_D(const ctkSpinBox);
  120. return d->SpinBox->text();
  121. }
  122. //-----------------------------------------------------------------------------
  123. QString ctkSpinBox::cleanText() const
  124. {
  125. Q_D(const ctkSpinBox);
  126. return d->SpinBox->cleanText();
  127. }
  128. //-----------------------------------------------------------------------------
  129. Qt::Alignment ctkSpinBox::alignment() const
  130. {
  131. Q_D(const ctkSpinBox);
  132. return d->SpinBox->alignment();
  133. }
  134. //-----------------------------------------------------------------------------
  135. void ctkSpinBox::setAlignment(Qt::Alignment flag)
  136. {
  137. Q_D(const ctkSpinBox);
  138. if (d->Mode == ctkSpinBox::SetIfDifferent && flag == d->SpinBox->alignment())
  139. {
  140. return;
  141. }
  142. d->SpinBox->setAlignment(flag);
  143. }
  144. //-----------------------------------------------------------------------------
  145. void ctkSpinBox::setFrame(bool frame)
  146. {
  147. Q_D(const ctkSpinBox);
  148. if (d->Mode == ctkSpinBox::SetIfDifferent && frame == d->SpinBox->hasFrame())
  149. {
  150. return;
  151. }
  152. d->SpinBox->setFrame(frame);
  153. }
  154. //-----------------------------------------------------------------------------
  155. bool ctkSpinBox::hasFrame() const
  156. {
  157. Q_D(const ctkSpinBox);
  158. return d->SpinBox->hasFrame();
  159. }
  160. //-----------------------------------------------------------------------------
  161. QString ctkSpinBox::prefix() const
  162. {
  163. Q_D(const ctkSpinBox);
  164. return d->SpinBox->prefix();
  165. }
  166. //-----------------------------------------------------------------------------
  167. void ctkSpinBox::setPrefix(const QString &prefix)
  168. {
  169. Q_D(const ctkSpinBox);
  170. if (d->Mode == ctkSpinBox::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 ctkSpinBox::suffix() const
  183. {
  184. Q_D(const ctkSpinBox);
  185. return d->SpinBox->suffix();
  186. }
  187. //-----------------------------------------------------------------------------
  188. void ctkSpinBox::setSuffix(const QString &suffix)
  189. {
  190. Q_D(const ctkSpinBox);
  191. if (d->Mode == ctkSpinBox::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 ctkSpinBox::singleStep() const
  204. {
  205. Q_D(const ctkSpinBox);
  206. return d->SpinBox->singleStep();
  207. }
  208. //-----------------------------------------------------------------------------
  209. void ctkSpinBox::setSingleStep(double step)
  210. {
  211. Q_D(ctkSpinBox);
  212. if (d->Mode == ctkSpinBox::SetIfDifferent
  213. && d->compare(step, this->singleStep()))
  214. {
  215. return;
  216. }
  217. d->SpinBox->setSingleStep(step);
  218. }
  219. //-----------------------------------------------------------------------------
  220. double ctkSpinBox::minimum() const
  221. {
  222. Q_D(const ctkSpinBox);
  223. return d->SpinBox->minimum();
  224. }
  225. //-----------------------------------------------------------------------------
  226. void ctkSpinBox::setMinimum(double min)
  227. {
  228. Q_D(ctkSpinBox);
  229. if (d->Mode == ctkSpinBox::SetIfDifferent
  230. && d->compare(min, this->minimum()))
  231. {
  232. return;
  233. }
  234. d->SpinBox->setMinimum(min);
  235. }
  236. //-----------------------------------------------------------------------------
  237. double ctkSpinBox::maximum() const
  238. {
  239. Q_D(const ctkSpinBox);
  240. return d->SpinBox->maximum();
  241. }
  242. //-----------------------------------------------------------------------------
  243. void ctkSpinBox::setMaximum(double max)
  244. {
  245. Q_D(ctkSpinBox);
  246. if (d->Mode == ctkSpinBox::SetIfDifferent
  247. && d->compare(max, this->maximum()))
  248. {
  249. return;
  250. }
  251. d->SpinBox->setMaximum(max);
  252. }
  253. //-----------------------------------------------------------------------------
  254. void ctkSpinBox::setRange(double min, double max)
  255. {
  256. Q_D(ctkSpinBox);
  257. if (d->Mode == ctkSpinBox::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 ctkSpinBox::decimals() const
  266. {
  267. Q_D(const ctkSpinBox);
  268. return d->SpinBox->decimals();
  269. }
  270. //-----------------------------------------------------------------------------
  271. void ctkSpinBox::setDecimals(int dec)
  272. {
  273. Q_D(ctkSpinBox);
  274. dec = qMax(0, dec);
  275. if (d->Mode == ctkSpinBox::SetIfDifferent && dec == this->decimals())
  276. {
  277. return;
  278. }
  279. d->DefaultDecimals = dec;
  280. d->setDecimals(d->DefaultDecimals);
  281. }
  282. //-----------------------------------------------------------------------------
  283. double ctkSpinBox::round(double value) const
  284. {
  285. Q_D(const ctkSpinBox);
  286. return QString::number(value, 'f', d->SpinBox->decimals()).toDouble();
  287. }
  288. //-----------------------------------------------------------------------------
  289. QDoubleSpinBox* ctkSpinBox::spinBox() const
  290. {
  291. Q_D(const ctkSpinBox);
  292. return d->SpinBox;
  293. }
  294. //-----------------------------------------------------------------------------
  295. void ctkSpinBox::setValue(double value)
  296. {
  297. Q_D(ctkSpinBox);
  298. if (d->Mode == ctkSpinBox::SetIfDifferent)
  299. {
  300. this->setValueIfDifferent(value);
  301. }
  302. else
  303. {
  304. this->setValueAlways(value);
  305. }
  306. }
  307. //-----------------------------------------------------------------------------
  308. void ctkSpinBox::setValueIfDifferent(double value)
  309. {
  310. Q_D(ctkSpinBox);
  311. if (! d->compare(this->value(), value))
  312. {
  313. d->SpinBox->setValue(value);
  314. }
  315. }
  316. //-----------------------------------------------------------------------------
  317. void ctkSpinBox::setValueAlways(double value)
  318. {
  319. Q_D(const ctkSpinBox);
  320. d->SpinBox->setValue(value);
  321. }
  322. //-----------------------------------------------------------------------------
  323. void ctkSpinBox::stepUp()
  324. {
  325. Q_D(const ctkSpinBox);
  326. d->SpinBox->stepUp();
  327. }
  328. //-----------------------------------------------------------------------------
  329. void ctkSpinBox::stepDown()
  330. {
  331. Q_D(const ctkSpinBox);
  332. d->SpinBox->stepDown();
  333. }
  334. //-----------------------------------------------------------------------------
  335. ctkSpinBox::SetMode ctkSpinBox::setMode() const
  336. {
  337. Q_D(const ctkSpinBox);
  338. return d->Mode;
  339. }
  340. //-----------------------------------------------------------------------------
  341. void ctkSpinBox::setSetMode(ctkSpinBox::SetMode newMode)
  342. {
  343. Q_D(ctkSpinBox);
  344. d->Mode = newMode;
  345. }
  346. //-----------------------------------------------------------------------------
  347. ctkSpinBox::DecimalsOptions ctkSpinBox::decimalsOption()
  348. {
  349. Q_D(const ctkSpinBox);
  350. return d->DOption;
  351. }
  352. //-----------------------------------------------------------------------------
  353. void ctkSpinBox::setDecimalsOption(ctkSpinBox::DecimalsOptions option)
  354. {
  355. Q_D(ctkSpinBox);
  356. if (d->Mode == ctkSpinBox::SetIfDifferent && option == d->DOption)
  357. {
  358. return;
  359. }
  360. d->DOption = option;
  361. }
  362. //-----------------------------------------------------------------------------
  363. bool ctkSpinBox::eventFilter(QObject* obj, QEvent* event)
  364. {
  365. Q_D(ctkSpinBox);
  366. if (d->DOption & ctkSpinBox::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. }