ctkDoubleSpinBox.cpp 31 KB

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