ctkDoubleSpinBox.cpp 29 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006
  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_p.h"
  16. #include "ctkUtils.h"
  17. #include "ctkValueProxy.h"
  18. #include "ctkPimpl.h"
  19. // Qt includes
  20. #include <QDebug>
  21. #include <QEvent>
  22. #include <QHBoxLayout>
  23. #include <QKeyEvent>
  24. #include <QLineEdit>
  25. #include <QShortcut>
  26. #include <QSizePolicy>
  27. #include <QVariant>
  28. //-----------------------------------------------------------------------------
  29. // ctkQDoubleSpinBox
  30. //----------------------------------------------------------------------------
  31. ctkQDoubleSpinBox::ctkQDoubleSpinBox(ctkDoubleSpinBoxPrivate* pimpl,
  32. QWidget* spinBoxParent)
  33. : QDoubleSpinBox(spinBoxParent)
  34. , d_ptr(pimpl)
  35. {
  36. this->InvertedControls = false;
  37. }
  38. //----------------------------------------------------------------------------
  39. QLineEdit* ctkQDoubleSpinBox::lineEdit()const
  40. {
  41. return this->QDoubleSpinBox::lineEdit();
  42. }
  43. //----------------------------------------------------------------------------
  44. void ctkQDoubleSpinBox::setInvertedControls(bool invertedControls)
  45. {
  46. this->InvertedControls = invertedControls;
  47. }
  48. //----------------------------------------------------------------------------
  49. bool ctkQDoubleSpinBox::invertedControls() const
  50. {
  51. return this->InvertedControls;
  52. }
  53. //----------------------------------------------------------------------------
  54. void ctkQDoubleSpinBox::stepBy(int steps)
  55. {
  56. if (this->InvertedControls)
  57. {
  58. steps = -steps;
  59. }
  60. this->Superclass::stepBy(steps);
  61. }
  62. //----------------------------------------------------------------------------
  63. QAbstractSpinBox::StepEnabled ctkQDoubleSpinBox::stepEnabled() const
  64. {
  65. if (!this->InvertedControls)
  66. {
  67. return this->Superclass::stepEnabled();
  68. }
  69. if (this->isReadOnly())
  70. {
  71. return StepNone;
  72. }
  73. if (this->wrapping())
  74. {
  75. return StepEnabled(StepUpEnabled | StepDownEnabled);
  76. }
  77. StepEnabled ret = StepNone;
  78. double value = this->value();
  79. if (value < this->maximum())
  80. {
  81. ret |= StepDownEnabled;
  82. }
  83. if (value > this->minimum())
  84. {
  85. ret |= StepUpEnabled;
  86. }
  87. return ret;
  88. }
  89. //-----------------------------------------------------------------------------
  90. double ctkQDoubleSpinBox::valueFromText(const QString &text) const
  91. {
  92. Q_D(const ctkDoubleSpinBox);
  93. QString copy = text;
  94. int pos = this->lineEdit()->cursorPosition();
  95. QValidator::State state = QValidator::Acceptable;
  96. int decimals = 0;
  97. double value = d->validateAndInterpret(copy, pos, state, decimals);
  98. return value;
  99. }
  100. //-----------------------------------------------------------------------------
  101. QString ctkQDoubleSpinBox::textFromValue(double value) const
  102. {
  103. Q_D(const ctkDoubleSpinBox);
  104. QString text = this->QDoubleSpinBox::textFromValue(value);
  105. if (text.isEmpty())
  106. {
  107. text = "0";
  108. }
  109. // If there is no decimal, it does not mean there won't be any.
  110. if (d->DOption & ctkDoubleSpinBox::DecimalPointAlwaysVisible &&
  111. text.indexOf(this->locale().decimalPoint()) == -1)
  112. {
  113. text += this->locale().decimalPoint();
  114. }
  115. return text;
  116. }
  117. //-----------------------------------------------------------------------------
  118. int ctkQDoubleSpinBox::decimalsFromText(const QString &text) const
  119. {
  120. Q_D(const ctkDoubleSpinBox);
  121. QString copy = text;
  122. int pos = this->lineEdit()->cursorPosition();
  123. int decimals = 0;
  124. QValidator::State state = QValidator::Acceptable;
  125. d->validateAndInterpret(copy, pos, state, decimals);
  126. return decimals;
  127. }
  128. //-----------------------------------------------------------------------------
  129. QValidator::State ctkQDoubleSpinBox::validate(QString &text, int &pos) const
  130. {
  131. Q_D(const ctkDoubleSpinBox);
  132. QValidator::State state = QValidator::Acceptable;
  133. int decimals = 0;
  134. d->validateAndInterpret(text, pos, state, decimals);
  135. return state;
  136. }
  137. //-----------------------------------------------------------------------------
  138. // ctkDoubleSpinBoxPrivate
  139. //-----------------------------------------------------------------------------
  140. ctkDoubleSpinBoxPrivate::ctkDoubleSpinBoxPrivate(ctkDoubleSpinBox& object)
  141. : q_ptr(&object)
  142. {
  143. qRegisterMetaType<ctkDoubleSpinBox::SetMode>("ctkDoubleSpinBox::SetMode");
  144. qRegisterMetaType<ctkDoubleSpinBox::DecimalsOptions>("ctkDoubleSpinBox::DecimalsOption");
  145. this->SpinBox = 0;
  146. this->Mode = ctkDoubleSpinBox::SetIfDifferent;
  147. this->DefaultDecimals = 2;
  148. // InsertDecimals is not a great default, but it is QDoubleSpinBox's default.
  149. this->DOption = ctkDoubleSpinBox::DecimalsByShortcuts
  150. | ctkDoubleSpinBox::InsertDecimals;
  151. this->InvertedControls = false;
  152. }
  153. //-----------------------------------------------------------------------------
  154. void ctkDoubleSpinBoxPrivate::init()
  155. {
  156. Q_Q(ctkDoubleSpinBox);
  157. this->SpinBox = new ctkQDoubleSpinBox(this, q);
  158. this->SpinBox->setInvertedControls(this->InvertedControls);
  159. // ctkQDoubleSpinBox needs to be first to receive textChanged() signals.
  160. QLineEdit* lineEdit = new QLineEdit(q);
  161. QObject::connect(lineEdit, SIGNAL(textChanged(QString)),
  162. this, SLOT(editorTextChanged(QString)));
  163. this->SpinBox->setLineEdit(lineEdit);
  164. lineEdit->setObjectName(QLatin1String("qt_spinbox_lineedit"));
  165. QObject::connect(this->SpinBox, SIGNAL(valueChanged(double)),
  166. this, SLOT(onValueChanged()));
  167. QObject::connect(this->SpinBox, SIGNAL(editingFinished()),
  168. q, SIGNAL(editingFinished()));
  169. QHBoxLayout* l = new QHBoxLayout(q);
  170. l->addWidget(this->SpinBox);
  171. l->setContentsMargins(0,0,0,0);
  172. q->setLayout(l);
  173. q->setSizePolicy(QSizePolicy(QSizePolicy::Minimum,
  174. QSizePolicy::Fixed, QSizePolicy::ButtonBox));
  175. this->SpinBox->installEventFilter(q);
  176. }
  177. //-----------------------------------------------------------------------------
  178. bool ctkDoubleSpinBoxPrivate::compare(double x1, double x2) const
  179. {
  180. Q_Q(const ctkDoubleSpinBox);
  181. return q->round(x1) == q->round(x2);
  182. }
  183. //-----------------------------------------------------------------------------
  184. double ctkDoubleSpinBoxPrivate::round(double value, int decimals) const
  185. {
  186. return QString::number(value, 'f', decimals).toDouble();
  187. }
  188. //-----------------------------------------------------------------------------
  189. QString ctkDoubleSpinBoxPrivate::stripped(const QString& text, int* pos) const
  190. {
  191. Q_Q(const ctkDoubleSpinBox);
  192. QString strip(text);
  193. if (strip.startsWith(q->prefix()))
  194. {
  195. strip.remove(0, q->prefix().size());
  196. }
  197. if (strip.endsWith(q->suffix()))
  198. {
  199. strip.chop(q->suffix().size());
  200. }
  201. strip = strip.trimmed();
  202. if (pos)
  203. {
  204. int stripInText = text.indexOf(strip);
  205. *pos = qBound(0, *pos - stripInText, strip.size());
  206. }
  207. return strip;
  208. }
  209. //-----------------------------------------------------------------------------
  210. int ctkDoubleSpinBoxPrivate::boundDecimals(int dec)const
  211. {
  212. Q_Q(const ctkDoubleSpinBox);
  213. if (dec == -1)
  214. {
  215. return q->decimals();
  216. }
  217. int min = (this->DOption & ctkDoubleSpinBox::DecimalsAsMin) ?
  218. this->DefaultDecimals : 0;
  219. int max = (this->DOption & ctkDoubleSpinBox::DecimalsAsMax) ?
  220. this->DefaultDecimals : 323; // see QDoubleSpinBox::decimals doc
  221. return qBound(min, dec, max);
  222. }
  223. //-----------------------------------------------------------------------------
  224. int ctkDoubleSpinBoxPrivate::decimalsForValue(double value) const
  225. {
  226. int decimals = this->DefaultDecimals;
  227. if (this->DOption & ctkDoubleSpinBox::DecimalsByValue)
  228. {
  229. decimals = ctk::significantDecimals(value, decimals);
  230. }
  231. return this->boundDecimals(decimals);
  232. }
  233. //-----------------------------------------------------------------------------
  234. void ctkDoubleSpinBoxPrivate::setValue(double value, int dec)
  235. {
  236. Q_Q(ctkDoubleSpinBox);
  237. dec = this->boundDecimals(dec);
  238. bool changeDecimals = dec != q->decimals();
  239. if (changeDecimals)
  240. {
  241. // don't fire decimalsChanged signal yet, wait for the value to be
  242. // up-to-date.
  243. this->SpinBox->setDecimals(dec);
  244. }
  245. this->SpinBox->setValue(value); // re-do the text (calls textFromValue())
  246. if (changeDecimals)
  247. {
  248. emit q->decimalsChanged(dec);
  249. }
  250. }
  251. //-----------------------------------------------------------------------------
  252. void ctkDoubleSpinBoxPrivate::setDecimals(int dec)
  253. {
  254. Q_Q(ctkDoubleSpinBox);
  255. dec = this->boundDecimals(dec);
  256. this->SpinBox->setDecimals(dec);
  257. emit q->decimalsChanged(dec);
  258. }
  259. //-----------------------------------------------------------------------------
  260. void ctkDoubleSpinBoxPrivate::editorTextChanged(const QString& text)
  261. {
  262. if (this->SpinBox->keyboardTracking())
  263. {
  264. QString tmp = text;
  265. int pos = this->SpinBox->lineEdit()->cursorPosition();
  266. QValidator::State state = QValidator::Invalid;
  267. int decimals = 0;
  268. this->validateAndInterpret(tmp, pos, state, decimals);
  269. if (state == QValidator::Acceptable)
  270. {
  271. double newValue = this->SpinBox->valueFromText(tmp);
  272. int decimals = this->boundDecimals(this->SpinBox->decimalsFromText(tmp));
  273. bool changeDecimals = this->DOption & ctkDoubleSpinBox::DecimalsByKey &&
  274. decimals != this->SpinBox->decimals();
  275. if (changeDecimals)
  276. {
  277. this->setValue(newValue, decimals);
  278. }
  279. // else, let QDoubleSpinBox process the validation.
  280. }
  281. }
  282. }
  283. //-----------------------------------------------------------------------------
  284. double ctkDoubleSpinBoxPrivate
  285. ::validateAndInterpret(QString &input, int &pos,
  286. QValidator::State &state, int &decimals) const
  287. {
  288. Q_Q(const ctkDoubleSpinBox);
  289. if (this->CachedText == input)
  290. {
  291. state = this->CachedState;
  292. decimals = this->CachedDecimals;
  293. return this->CachedValue;
  294. }
  295. const double max = this->SpinBox->maximum();
  296. const double min = this->SpinBox->minimum();
  297. int posInValue = pos;
  298. QString text = this->stripped(input, &posInValue);
  299. // posInValue can change, track the offset.
  300. const int oldPosInValue = posInValue;
  301. state = QValidator::Acceptable;
  302. decimals = 0;
  303. double value = min;
  304. const int dec = text.indexOf(q->locale().decimalPoint());
  305. bool ok = false;
  306. value = q->locale().toDouble(text, &ok);
  307. // could be in an intermediate state
  308. if (!ok && state == QValidator::Acceptable)
  309. {
  310. if (text.isEmpty() ||
  311. text == "." ||
  312. text == "-" ||
  313. text == "+" ||
  314. text == "-." ||
  315. text == "+.")
  316. {
  317. state = QValidator::Intermediate;
  318. }
  319. }
  320. // could be because of group separators:
  321. if (!ok && state == QValidator::Acceptable)
  322. {
  323. if (q->locale().groupSeparator().isPrint())
  324. {
  325. int start = (dec == -1 ? text.size() : dec)- 1;
  326. int lastGroupSeparator = start;
  327. for (int digit = start; digit >= 0; --digit)
  328. {
  329. if (text.at(digit) == q->locale().groupSeparator())
  330. {
  331. if (digit != lastGroupSeparator - 3)
  332. {
  333. state = QValidator::Invalid;
  334. break;
  335. }
  336. text.remove(digit, 1);
  337. lastGroupSeparator = digit;
  338. }
  339. }
  340. }
  341. // try again without the group separators
  342. value = q->locale().toDouble(text, &ok);
  343. }
  344. // test the decimalPoint
  345. if (!ok && state == QValidator::Acceptable)
  346. {
  347. // duplicate decimal points probably means the user typed another decimal points,
  348. // move the cursor pos to the right then
  349. if (dec + 1 < text.size() &&
  350. text.at(dec + 1) == q->locale().decimalPoint() &&
  351. posInValue == dec + 1)
  352. {
  353. text.remove(dec + 1, 1);
  354. value = q->locale().toDouble(text, &ok);
  355. }
  356. }
  357. if (ok && state != QValidator::Invalid)
  358. {
  359. if (dec != -1)
  360. {
  361. decimals = text.size() - (dec + 1);
  362. if (decimals > q->decimals())
  363. {
  364. // With ReplaceDecimals on, key strokes replace decimal digits
  365. if (posInValue > dec && posInValue < text.size())
  366. {
  367. const int extraDecimals = decimals - q->decimals();
  368. if (this->DOption & ctkDoubleSpinBox::ReplaceDecimals)
  369. {
  370. text.remove(posInValue, extraDecimals);
  371. decimals = q->decimals();
  372. value = q->locale().toDouble(text, &ok);
  373. }
  374. else if (!(this->DOption & ctkDoubleSpinBox::InsertDecimals))
  375. {
  376. text.remove(text.size() - extraDecimals, extraDecimals);
  377. decimals = q->decimals();
  378. value = q->locale().toDouble(text, &ok);
  379. }
  380. }
  381. }
  382. // When DecimalsByKey is set, it is possible to extend the number of decimals
  383. if (decimals > q->decimals() &&
  384. !(this->DOption & ctkDoubleSpinBox::DecimalsByKey) )
  385. {
  386. state = QValidator::Invalid;
  387. }
  388. }
  389. }
  390. if (state == QValidator::Acceptable)
  391. {
  392. if (!ok)
  393. {
  394. state = QValidator::Invalid;
  395. }
  396. else if (value >= min && value <= max)
  397. {
  398. state = QValidator::Acceptable;
  399. }
  400. else if (max == min)
  401. { // when max and min is the same the only non-Invalid input is max (or min)
  402. state = QValidator::Invalid;
  403. }
  404. else if ((value >= 0 && value > max) || (value < 0 && value < min))
  405. {
  406. state = QValidator::Invalid;
  407. }
  408. else
  409. {
  410. state = QValidator::Intermediate;
  411. }
  412. }
  413. if (state != QValidator::Acceptable)
  414. {
  415. value = max > 0 ? min : max;
  416. }
  417. pos += posInValue - oldPosInValue;
  418. input = q->prefix() + text + q->suffix();
  419. this->CachedText = input;
  420. this->CachedState = state;
  421. this->CachedValue = value;
  422. this->CachedDecimals = decimals;
  423. return value;
  424. }
  425. //-----------------------------------------------------------------------------
  426. void ctkDoubleSpinBoxPrivate::onValueChanged()
  427. {
  428. Q_Q(ctkDoubleSpinBox);
  429. double value = q->value();
  430. emit q->valueChanged(value);
  431. emit q->valueChanged(QString::number(value, 'f', this->SpinBox->decimals()));
  432. }
  433. //-----------------------------------------------------------------------------
  434. void ctkDoubleSpinBoxPrivate::onValueProxyAboutToBeModified()
  435. {
  436. Q_Q(ctkDoubleSpinBox);
  437. this->SpinBox->setProperty("inputValue", q->value());
  438. this->SpinBox->setProperty("inputMinimum", q->minimum());
  439. this->SpinBox->setProperty("inputMaximum", q->maximum());
  440. }
  441. //-----------------------------------------------------------------------------
  442. void ctkDoubleSpinBoxPrivate::onValueProxyModified()
  443. {
  444. Q_Q(ctkDoubleSpinBox);
  445. int decimals = q->decimals();
  446. bool wasBlocking = q->blockSignals(true);
  447. q->setRange(this->SpinBox->property("inputMinimum").toDouble(),
  448. this->SpinBox->property("inputMaximum").toDouble());
  449. q->setValue(this->SpinBox->property("inputValue").toDouble());
  450. q->value();
  451. q->blockSignals(wasBlocking);
  452. // only decimals can change when value proxy is modified.
  453. if (decimals != q->decimals())
  454. {
  455. emit q->decimalsChanged(q->decimals());
  456. }
  457. }
  458. //-----------------------------------------------------------------------------
  459. // ctkDoubleSpinBox
  460. //-----------------------------------------------------------------------------
  461. ctkDoubleSpinBox::ctkDoubleSpinBox(QWidget* newParent)
  462. : QWidget(newParent)
  463. , d_ptr(new ctkDoubleSpinBoxPrivate(*this))
  464. {
  465. Q_D(ctkDoubleSpinBox);
  466. d->init();
  467. }
  468. //-----------------------------------------------------------------------------
  469. ctkDoubleSpinBox::ctkDoubleSpinBox(ctkDoubleSpinBox::SetMode mode, QWidget* newParent)
  470. : QWidget(newParent)
  471. , d_ptr(new ctkDoubleSpinBoxPrivate(*this))
  472. {
  473. Q_D(ctkDoubleSpinBox);
  474. d->init();
  475. this->setSetMode(mode);
  476. }
  477. //-----------------------------------------------------------------------------
  478. double ctkDoubleSpinBox::value() const
  479. {
  480. Q_D(const ctkDoubleSpinBox);
  481. double val = d->SpinBox->value();
  482. if (d->Proxy)
  483. {
  484. val = d->Proxy.data()->valueFromProxyValue(val);
  485. }
  486. return val;
  487. }
  488. //-----------------------------------------------------------------------------
  489. double ctkDoubleSpinBox::displayedValue() const
  490. {
  491. Q_D(const ctkDoubleSpinBox);
  492. return d->SpinBox->value();
  493. }
  494. //----------------------------------------------------------------------------
  495. void ctkDoubleSpinBox::setDisplayedValue(double value)
  496. {
  497. Q_D(ctkDoubleSpinBox);
  498. d->SpinBox->setValue(value);
  499. }
  500. //-----------------------------------------------------------------------------
  501. QString ctkDoubleSpinBox::text() const
  502. {
  503. Q_D(const ctkDoubleSpinBox);
  504. return d->SpinBox->text();
  505. }
  506. //-----------------------------------------------------------------------------
  507. QString ctkDoubleSpinBox::cleanText() const
  508. {
  509. Q_D(const ctkDoubleSpinBox);
  510. return d->SpinBox->cleanText();
  511. }
  512. //-----------------------------------------------------------------------------
  513. Qt::Alignment ctkDoubleSpinBox::alignment() const
  514. {
  515. Q_D(const ctkDoubleSpinBox);
  516. return d->SpinBox->alignment();
  517. }
  518. //-----------------------------------------------------------------------------
  519. void ctkDoubleSpinBox::setAlignment(Qt::Alignment flag)
  520. {
  521. Q_D(const ctkDoubleSpinBox);
  522. if (d->Mode == ctkDoubleSpinBox::SetIfDifferent && flag == d->SpinBox->alignment())
  523. {
  524. return;
  525. }
  526. d->SpinBox->setAlignment(flag);
  527. }
  528. //-----------------------------------------------------------------------------
  529. void ctkDoubleSpinBox::setFrame(bool frame)
  530. {
  531. Q_D(const ctkDoubleSpinBox);
  532. if (d->Mode == ctkDoubleSpinBox::SetIfDifferent && frame == d->SpinBox->hasFrame())
  533. {
  534. return;
  535. }
  536. d->SpinBox->setFrame(frame);
  537. }
  538. //-----------------------------------------------------------------------------
  539. bool ctkDoubleSpinBox::hasFrame() const
  540. {
  541. Q_D(const ctkDoubleSpinBox);
  542. return d->SpinBox->hasFrame();
  543. }
  544. //-----------------------------------------------------------------------------
  545. QString ctkDoubleSpinBox::prefix() const
  546. {
  547. Q_D(const ctkDoubleSpinBox);
  548. return d->SpinBox->prefix();
  549. }
  550. //-----------------------------------------------------------------------------
  551. void ctkDoubleSpinBox::setPrefix(const QString &prefix)
  552. {
  553. Q_D(const ctkDoubleSpinBox);
  554. if (d->Mode == ctkDoubleSpinBox::SetIfDifferent && prefix == d->SpinBox->prefix())
  555. {
  556. return;
  557. }
  558. #if QT_VERSION < 0x040800
  559. /// Setting the prefix doesn't recompute the sizehint, do it manually here:
  560. /// See: http://bugreports.qt.nokia.com/browse/QTBUG-9530
  561. d->SpinBox->setRange(d->SpinBox->minimum(), d->SpinBox->maximum());
  562. #endif
  563. d->SpinBox->setPrefix(prefix);
  564. }
  565. //-----------------------------------------------------------------------------
  566. QString ctkDoubleSpinBox::suffix() const
  567. {
  568. Q_D(const ctkDoubleSpinBox);
  569. return d->SpinBox->suffix();
  570. }
  571. //-----------------------------------------------------------------------------
  572. void ctkDoubleSpinBox::setSuffix(const QString &suffix)
  573. {
  574. Q_D(const ctkDoubleSpinBox);
  575. if (d->Mode == ctkDoubleSpinBox::SetIfDifferent && suffix == d->SpinBox->suffix())
  576. {
  577. return;
  578. }
  579. #if QT_VERSION < 0x040800
  580. /// Setting the suffix doesn't recompute the sizehint, do it manually here:
  581. /// See: http://bugreports.qt.nokia.com/browse/QTBUG-9530
  582. d->SpinBox->setRange(d->SpinBox->minimum(), d->SpinBox->maximum());
  583. #endif
  584. d->SpinBox->setSuffix(suffix);
  585. }
  586. //-----------------------------------------------------------------------------
  587. double ctkDoubleSpinBox::singleStep() const
  588. {
  589. Q_D(const ctkDoubleSpinBox);
  590. double step = d->SpinBox->singleStep();
  591. return step;
  592. }
  593. //-----------------------------------------------------------------------------
  594. void ctkDoubleSpinBox::setSingleStep(double newStep)
  595. {
  596. Q_D(ctkDoubleSpinBox);
  597. if (d->Mode == ctkDoubleSpinBox::SetIfDifferent
  598. && d->compare(newStep, this->singleStep()))
  599. {
  600. return;
  601. }
  602. d->SpinBox->setSingleStep(newStep);
  603. }
  604. //-----------------------------------------------------------------------------
  605. double ctkDoubleSpinBox::minimum() const
  606. {
  607. Q_D(const ctkDoubleSpinBox);
  608. double min = d->SpinBox->minimum();
  609. double max = d->SpinBox->maximum();
  610. if (d->Proxy)
  611. {
  612. min = d->Proxy.data()->valueFromProxyValue(min);
  613. max = d->Proxy.data()->valueFromProxyValue(max);
  614. }
  615. return qMin(min, max);
  616. }
  617. //-----------------------------------------------------------------------------
  618. void ctkDoubleSpinBox::setMinimum(double newMin)
  619. {
  620. Q_D(ctkDoubleSpinBox);
  621. if (d->Proxy)
  622. {
  623. newMin = d->Proxy.data()->proxyValueFromValue(newMin);
  624. }
  625. if (d->Mode == ctkDoubleSpinBox::SetIfDifferent
  626. && d->compare(newMin, d->SpinBox->minimum()))
  627. {
  628. return;
  629. }
  630. d->SpinBox->setMinimum(newMin);
  631. }
  632. //-----------------------------------------------------------------------------
  633. double ctkDoubleSpinBox::maximum() const
  634. {
  635. Q_D(const ctkDoubleSpinBox);
  636. double min = d->SpinBox->minimum();
  637. double max = d->SpinBox->maximum();
  638. if (d->Proxy)
  639. {
  640. min = d->Proxy.data()->valueFromProxyValue(min);
  641. max = d->Proxy.data()->valueFromProxyValue(max);
  642. }
  643. return qMax(min, max);
  644. }
  645. //-----------------------------------------------------------------------------
  646. void ctkDoubleSpinBox::setMaximum(double newMax)
  647. {
  648. Q_D(ctkDoubleSpinBox);
  649. if (d->Proxy)
  650. {
  651. newMax = d->Proxy.data()->proxyValueFromValue(newMax);
  652. }
  653. if (d->Mode == ctkDoubleSpinBox::SetIfDifferent
  654. && d->compare(newMax, d->SpinBox->maximum()))
  655. {
  656. return;
  657. }
  658. d->SpinBox->setMaximum(newMax);
  659. }
  660. //-----------------------------------------------------------------------------
  661. void ctkDoubleSpinBox::setRange(double newMin, double newMax)
  662. {
  663. Q_D(ctkDoubleSpinBox);
  664. if (d->Proxy)
  665. {
  666. newMin = d->Proxy.data()->proxyValueFromValue(newMin);
  667. newMax = d->Proxy.data()->proxyValueFromValue(newMax);
  668. }
  669. if (newMin > newMax)
  670. {
  671. qSwap(newMin, newMax);
  672. }
  673. if (d->Mode == ctkDoubleSpinBox::SetIfDifferent
  674. && d->compare(newMax, d->SpinBox->maximum())
  675. && d->compare(newMin, d->SpinBox->minimum()))
  676. {
  677. return;
  678. }
  679. d->SpinBox->setRange(newMin, newMax);
  680. }
  681. //-----------------------------------------------------------------------------
  682. int ctkDoubleSpinBox::decimals() const
  683. {
  684. Q_D(const ctkDoubleSpinBox);
  685. return d->SpinBox->decimals();
  686. }
  687. //-----------------------------------------------------------------------------
  688. void ctkDoubleSpinBox::setDecimals(int dec)
  689. {
  690. Q_D(ctkDoubleSpinBox);
  691. if (d->Mode == ctkDoubleSpinBox::SetIfDifferent
  692. && dec == this->decimals()
  693. && dec == d->DefaultDecimals)
  694. {
  695. return;
  696. }
  697. d->DefaultDecimals = dec;
  698. // The number of decimals may or may not depend on the value. Recompute the
  699. // new number of decimals.
  700. double value = this->value();
  701. if (d->Proxy)
  702. {
  703. value = d->Proxy.data()->proxyValueFromValue(value);
  704. }
  705. int newDecimals = d->decimalsForValue(value);
  706. d->setDecimals(newDecimals);
  707. }
  708. //-----------------------------------------------------------------------------
  709. double ctkDoubleSpinBox::round(double value) const
  710. {
  711. Q_D(const ctkDoubleSpinBox);
  712. return QString::number(value, 'f', d->SpinBox->decimals()).toDouble();
  713. }
  714. //-----------------------------------------------------------------------------
  715. QDoubleSpinBox* ctkDoubleSpinBox::spinBox() const
  716. {
  717. Q_D(const ctkDoubleSpinBox);
  718. return d->SpinBox;
  719. }
  720. //-----------------------------------------------------------------------------
  721. QLineEdit* ctkDoubleSpinBox::lineEdit() const
  722. {
  723. Q_D(const ctkDoubleSpinBox);
  724. return d->SpinBox->lineEdit();
  725. }
  726. //-----------------------------------------------------------------------------
  727. void ctkDoubleSpinBox::setValue(double value)
  728. {
  729. Q_D(ctkDoubleSpinBox);
  730. if (d->Mode == ctkDoubleSpinBox::SetIfDifferent)
  731. {
  732. this->setValueIfDifferent(value);
  733. }
  734. else
  735. {
  736. this->setValueAlways(value);
  737. }
  738. }
  739. //-----------------------------------------------------------------------------
  740. void ctkDoubleSpinBox::setValueIfDifferent(double newValue)
  741. {
  742. Q_D(ctkDoubleSpinBox);
  743. double newValueToDisplay = newValue;
  744. if (d->Proxy)
  745. {
  746. newValueToDisplay = d->Proxy.data()->proxyValueFromValue(newValueToDisplay);
  747. }
  748. int newValueDecimals = d->decimalsForValue(newValueToDisplay);
  749. if (d->SpinBox->value() != d->round(newValueToDisplay, newValueDecimals)
  750. || d->SpinBox->decimals() != newValueDecimals)
  751. {
  752. // Pass newValue and not newValueToDisplay as setValueAlways also computes the
  753. // proxyValue from the given value
  754. this->setValueAlways(newValue);
  755. }
  756. }
  757. //-----------------------------------------------------------------------------
  758. void ctkDoubleSpinBox::setValueAlways(double value)
  759. {
  760. Q_D(ctkDoubleSpinBox);
  761. if (d->Proxy)
  762. {
  763. value = d->Proxy.data()->proxyValueFromValue(value);
  764. }
  765. int decimals = d->decimalsForValue(value);
  766. d->setValue(value, decimals);
  767. }
  768. //-----------------------------------------------------------------------------
  769. void ctkDoubleSpinBox::stepUp()
  770. {
  771. Q_D(const ctkDoubleSpinBox);
  772. d->SpinBox->stepUp();
  773. }
  774. //-----------------------------------------------------------------------------
  775. void ctkDoubleSpinBox::stepDown()
  776. {
  777. Q_D(const ctkDoubleSpinBox);
  778. d->SpinBox->stepDown();
  779. }
  780. //-----------------------------------------------------------------------------
  781. ctkDoubleSpinBox::SetMode ctkDoubleSpinBox::setMode() const
  782. {
  783. Q_D(const ctkDoubleSpinBox);
  784. return d->Mode;
  785. }
  786. //-----------------------------------------------------------------------------
  787. void ctkDoubleSpinBox::setSetMode(ctkDoubleSpinBox::SetMode newMode)
  788. {
  789. Q_D(ctkDoubleSpinBox);
  790. d->Mode = newMode;
  791. }
  792. //-----------------------------------------------------------------------------
  793. ctkDoubleSpinBox::DecimalsOptions ctkDoubleSpinBox::decimalsOption()
  794. {
  795. Q_D(const ctkDoubleSpinBox);
  796. return d->DOption;
  797. }
  798. //-----------------------------------------------------------------------------
  799. void ctkDoubleSpinBox::setDecimalsOption(ctkDoubleSpinBox::DecimalsOptions option)
  800. {
  801. Q_D(ctkDoubleSpinBox);
  802. if (d->Mode == ctkDoubleSpinBox::SetIfDifferent && option == d->DOption)
  803. {
  804. return;
  805. }
  806. d->DOption = option;
  807. this->setValueAlways(this->value());
  808. }
  809. //----------------------------------------------------------------------------
  810. void ctkDoubleSpinBox::setInvertedControls(bool invertedControls)
  811. {
  812. Q_D(ctkDoubleSpinBox);
  813. d->InvertedControls = invertedControls;
  814. d->SpinBox->setInvertedControls(d->InvertedControls);
  815. }
  816. //----------------------------------------------------------------------------
  817. bool ctkDoubleSpinBox::invertedControls() const
  818. {
  819. Q_D(const ctkDoubleSpinBox);
  820. return d->InvertedControls;
  821. }
  822. //----------------------------------------------------------------------------
  823. void ctkDoubleSpinBox::setValueProxy(ctkValueProxy* proxy)
  824. {
  825. Q_D(ctkDoubleSpinBox);
  826. if (proxy == d->Proxy.data())
  827. {
  828. return;
  829. }
  830. d->onValueProxyAboutToBeModified();
  831. if (d->Proxy)
  832. {
  833. disconnect(d->Proxy.data(), SIGNAL(proxyAboutToBeModified()),
  834. d, SLOT(onValueProxyAboutToBeModified()));
  835. disconnect(d->Proxy.data(), SIGNAL(proxyModified()),
  836. d, SLOT(onValueProxyModified()));
  837. }
  838. d->Proxy = proxy;
  839. if (d->Proxy)
  840. {
  841. connect(d->Proxy.data(), SIGNAL(proxyAboutToBeModified()),
  842. d, SLOT(onValueProxyAboutToBeModified()));
  843. connect(d->Proxy.data(), SIGNAL(proxyModified()),
  844. d, SLOT(onValueProxyModified()));
  845. }
  846. d->onValueProxyModified();
  847. }
  848. //----------------------------------------------------------------------------
  849. ctkValueProxy* ctkDoubleSpinBox::valueProxy() const
  850. {
  851. Q_D(const ctkDoubleSpinBox);
  852. return d->Proxy.data();
  853. }
  854. //-----------------------------------------------------------------------------
  855. void ctkDoubleSpinBox::keyPressEvent(QKeyEvent* event)
  856. {
  857. Q_D(ctkDoubleSpinBox);
  858. const bool accept = this->eventFilter(d->SpinBox, event);
  859. event->setAccepted(accept);
  860. }
  861. //-----------------------------------------------------------------------------
  862. bool ctkDoubleSpinBox::eventFilter(QObject* obj, QEvent* event)
  863. {
  864. Q_D(ctkDoubleSpinBox);
  865. if (d->DOption & ctkDoubleSpinBox::DecimalsByShortcuts &&
  866. obj == d->SpinBox && event->type() == QEvent::KeyPress)
  867. {
  868. QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
  869. Q_ASSERT(keyEvent);
  870. if (keyEvent->modifiers() & Qt::ControlModifier)
  871. {
  872. if (keyEvent->key() == Qt::Key_Plus
  873. || keyEvent->key() == Qt::Key_Equal)
  874. {
  875. d->setDecimals(this->decimals() + 1);
  876. return true;
  877. }
  878. else if (keyEvent->key() == Qt::Key_Minus)
  879. {
  880. d->setDecimals(this->decimals() - 1);
  881. return true;
  882. }
  883. else if (keyEvent->key() == Qt::Key_0)
  884. {
  885. d->setDecimals(d->DefaultDecimals);
  886. return true;
  887. }
  888. }
  889. return QWidget::eventFilter(obj, event);
  890. }
  891. else
  892. {
  893. // pass the event on to the parent class
  894. return QWidget::eventFilter(obj, event);
  895. }
  896. }