You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

starwelsamountfield.cpp 8.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. // Copyright (c) 2011-2015 The Starwels developers
  2. // Distributed under the MIT software license, see the accompanying
  3. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  4. #include "starwelsamountfield.h"
  5. #include "starwelsunits.h"
  6. #include "guiconstants.h"
  7. #include "qvaluecombobox.h"
  8. #include <QApplication>
  9. #include <QAbstractSpinBox>
  10. #include <QHBoxLayout>
  11. #include <QKeyEvent>
  12. #include <QLineEdit>
  13. /** QSpinBox that uses fixed-point numbers internally and uses our own
  14. * formatting/parsing functions.
  15. */
  16. class AmountSpinBox: public QAbstractSpinBox
  17. {
  18. Q_OBJECT
  19. public:
  20. explicit AmountSpinBox(QWidget *parent):
  21. QAbstractSpinBox(parent),
  22. currentUnit(StarwelsUnits::MAI),
  23. singleStep(100000) // satoshis
  24. {
  25. setAlignment(Qt::AlignRight);
  26. connect(lineEdit(), SIGNAL(textEdited(QString)), this, SIGNAL(valueChanged()));
  27. }
  28. QValidator::State validate(QString &text, int &pos) const
  29. {
  30. if(text.isEmpty())
  31. return QValidator::Intermediate;
  32. bool valid = false;
  33. parse(text, &valid);
  34. /* Make sure we return Intermediate so that fixup() is called on defocus */
  35. return valid ? QValidator::Intermediate : QValidator::Invalid;
  36. }
  37. void fixup(QString &input) const
  38. {
  39. bool valid = false;
  40. CAmount val = parse(input, &valid);
  41. if(valid)
  42. {
  43. input = StarwelsUnits::format(currentUnit, val, false, StarwelsUnits::separatorAlways);
  44. lineEdit()->setText(input);
  45. }
  46. }
  47. CAmount value(bool *valid_out=0) const
  48. {
  49. return parse(text(), valid_out);
  50. }
  51. void setValue(const CAmount& value)
  52. {
  53. lineEdit()->setText(StarwelsUnits::format(currentUnit, value, false, StarwelsUnits::separatorAlways));
  54. Q_EMIT valueChanged();
  55. }
  56. void stepBy(int steps)
  57. {
  58. bool valid = false;
  59. CAmount val = value(&valid);
  60. val = val + steps * singleStep;
  61. val = qMin(qMax(val, CAmount(0)), StarwelsUnits::maxMoney());
  62. setValue(val);
  63. }
  64. void setDisplayUnit(int unit)
  65. {
  66. bool valid = false;
  67. CAmount val = value(&valid);
  68. currentUnit = unit;
  69. if(valid)
  70. setValue(val);
  71. else
  72. clear();
  73. }
  74. void setSingleStep(const CAmount& step)
  75. {
  76. singleStep = step;
  77. }
  78. QSize minimumSizeHint() const
  79. {
  80. if(cachedMinimumSizeHint.isEmpty())
  81. {
  82. ensurePolished();
  83. const QFontMetrics fm(fontMetrics());
  84. int h = lineEdit()->minimumSizeHint().height();
  85. int w = fm.width(StarwelsUnits::format(StarwelsUnits::MAI, StarwelsUnits::maxMoney(), false, StarwelsUnits::separatorAlways));
  86. w += 2; // cursor blinking space
  87. QStyleOptionSpinBox opt;
  88. initStyleOption(&opt);
  89. QSize hint(w, h);
  90. QSize extra(35, 6);
  91. opt.rect.setSize(hint + extra);
  92. extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &opt,
  93. QStyle::SC_SpinBoxEditField, this).size();
  94. // get closer to final result by repeating the calculation
  95. opt.rect.setSize(hint + extra);
  96. extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &opt,
  97. QStyle::SC_SpinBoxEditField, this).size();
  98. hint += extra;
  99. hint.setHeight(h);
  100. opt.rect = rect();
  101. cachedMinimumSizeHint = style()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, this)
  102. .expandedTo(QApplication::globalStrut());
  103. }
  104. return cachedMinimumSizeHint;
  105. }
  106. private:
  107. int currentUnit;
  108. CAmount singleStep;
  109. mutable QSize cachedMinimumSizeHint;
  110. /**
  111. * Parse a string into a number of base monetary units and
  112. * return validity.
  113. * @note Must return 0 if !valid.
  114. */
  115. CAmount parse(const QString &text, bool *valid_out=0) const
  116. {
  117. CAmount val = 0;
  118. bool valid = StarwelsUnits::parse(currentUnit, text, &val);
  119. if(valid)
  120. {
  121. if(val < 0 || val > StarwelsUnits::maxMoney())
  122. valid = false;
  123. }
  124. if(valid_out)
  125. *valid_out = valid;
  126. return valid ? val : 0;
  127. }
  128. protected:
  129. bool event(QEvent *event)
  130. {
  131. if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease)
  132. {
  133. QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
  134. if (keyEvent->key() == Qt::Key_Comma)
  135. {
  136. // Translate a comma into a period
  137. QKeyEvent periodKeyEvent(event->type(), Qt::Key_Period, keyEvent->modifiers(), ".", keyEvent->isAutoRepeat(), keyEvent->count());
  138. return QAbstractSpinBox::event(&periodKeyEvent);
  139. }
  140. }
  141. return QAbstractSpinBox::event(event);
  142. }
  143. StepEnabled stepEnabled() const
  144. {
  145. if (isReadOnly()) // Disable steps when AmountSpinBox is read-only
  146. return StepNone;
  147. if (text().isEmpty()) // Allow step-up with empty field
  148. return StepUpEnabled;
  149. StepEnabled rv = 0;
  150. bool valid = false;
  151. CAmount val = value(&valid);
  152. if(valid)
  153. {
  154. if(val > 0)
  155. rv |= StepDownEnabled;
  156. if(val < StarwelsUnits::maxMoney())
  157. rv |= StepUpEnabled;
  158. }
  159. return rv;
  160. }
  161. Q_SIGNALS:
  162. void valueChanged();
  163. };
  164. #include "starwelsamountfield.moc"
  165. StarwelsAmountField::StarwelsAmountField(QWidget *parent) :
  166. QWidget(parent),
  167. amount(0)
  168. {
  169. amount = new AmountSpinBox(this);
  170. amount->setLocale(QLocale::c());
  171. amount->installEventFilter(this);
  172. amount->setMaximumWidth(170);
  173. QHBoxLayout *layout = new QHBoxLayout(this);
  174. layout->addWidget(amount);
  175. unit = new QValueComboBox(this);
  176. unit->setModel(new StarwelsUnits(this));
  177. layout->addWidget(unit);
  178. layout->addStretch(1);
  179. layout->setContentsMargins(0,0,0,0);
  180. setLayout(layout);
  181. setFocusPolicy(Qt::TabFocus);
  182. setFocusProxy(amount);
  183. // If one if the widgets changes, the combined content changes as well
  184. connect(amount, SIGNAL(valueChanged()), this, SIGNAL(valueChanged()));
  185. connect(unit, SIGNAL(currentIndexChanged(int)), this, SLOT(unitChanged(int)));
  186. // Set default based on configuration
  187. unitChanged(unit->currentIndex());
  188. }
  189. void StarwelsAmountField::clear()
  190. {
  191. amount->clear();
  192. unit->setCurrentIndex(0);
  193. }
  194. void StarwelsAmountField::setEnabled(bool fEnabled)
  195. {
  196. amount->setEnabled(fEnabled);
  197. unit->setEnabled(fEnabled);
  198. }
  199. bool StarwelsAmountField::validate()
  200. {
  201. bool valid = false;
  202. value(&valid);
  203. setValid(valid);
  204. return valid;
  205. }
  206. void StarwelsAmountField::setValid(bool valid)
  207. {
  208. if (valid)
  209. amount->setStyleSheet("");
  210. else
  211. amount->setStyleSheet(STYLE_INVALID);
  212. }
  213. bool StarwelsAmountField::eventFilter(QObject *object, QEvent *event)
  214. {
  215. if (event->type() == QEvent::FocusIn)
  216. {
  217. // Clear invalid flag on focus
  218. setValid(true);
  219. }
  220. return QWidget::eventFilter(object, event);
  221. }
  222. QWidget *StarwelsAmountField::setupTabChain(QWidget *prev)
  223. {
  224. QWidget::setTabOrder(prev, amount);
  225. QWidget::setTabOrder(amount, unit);
  226. return unit;
  227. }
  228. CAmount StarwelsAmountField::value(bool *valid_out) const
  229. {
  230. return amount->value(valid_out);
  231. }
  232. void StarwelsAmountField::setValue(const CAmount& value)
  233. {
  234. amount->setValue(value);
  235. }
  236. void StarwelsAmountField::setReadOnly(bool fReadOnly)
  237. {
  238. amount->setReadOnly(fReadOnly);
  239. }
  240. void StarwelsAmountField::unitChanged(int idx)
  241. {
  242. // Use description tooltip for current unit for the combobox
  243. unit->setToolTip(unit->itemData(idx, Qt::ToolTipRole).toString());
  244. // Determine new unit ID
  245. int newUnit = unit->itemData(idx, StarwelsUnits::UnitRole).toInt();
  246. amount->setDisplayUnit(newUnit);
  247. }
  248. void StarwelsAmountField::setDisplayUnit(int newUnit)
  249. {
  250. unit->setValue(newUnit);
  251. }
  252. void StarwelsAmountField::setSingleStep(const CAmount& step)
  253. {
  254. amount->setSingleStep(step);
  255. }