Bitcoin ABC  0.24.7
P2P Digital Currency
bitcoinamountfield.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2015 The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 
6 
7 #include <qt/bitcoinunits.h>
8 #include <qt/guiconstants.h>
9 #include <qt/guiutil.h>
10 #include <qt/qvaluecombobox.h>
11 
12 #include <QAbstractSpinBox>
13 #include <QApplication>
14 #include <QHBoxLayout>
15 #include <QKeyEvent>
16 #include <QLineEdit>
17 
22 class AmountSpinBox : public QAbstractSpinBox {
23  Q_OBJECT
24 
25 public:
26  explicit AmountSpinBox(QWidget *parent) {
27  setAlignment(Qt::AlignRight);
28 
29  connect(lineEdit(), &QLineEdit::textEdited, this,
31  }
32 
33  QValidator::State validate(QString &text, int &pos) const override {
34  if (text.isEmpty()) {
35  return QValidator::Intermediate;
36  }
37  bool valid = false;
38  parse(text, &valid);
39  // Make sure we return Intermediate so that fixup() is called on
40  // defocus.
41  return valid ? QValidator::Intermediate : QValidator::Invalid;
42  }
43 
44  void fixup(QString &input) const override {
45  bool valid;
46  Amount val;
47 
48  if (input.isEmpty() && !m_allow_empty) {
49  valid = true;
50  val = m_min_amount;
51  } else {
52  valid = false;
53  val = parse(input, &valid);
54  }
55 
56  if (valid) {
57  val = qBound(m_min_amount, val, m_max_amount);
58  input = BitcoinUnits::format(currentUnit, val, false,
60  lineEdit()->setText(input);
61  }
62  }
63 
64  Amount value(bool *valid_out = nullptr) const {
65  return parse(text(), valid_out);
66  }
67 
68  void setValue(const Amount value) {
69  lineEdit()->setText(BitcoinUnits::format(
71  Q_EMIT valueChanged();
72  }
73 
74  void SetAllowEmpty(bool allow) { m_allow_empty = allow; }
75 
77 
79 
80  void stepBy(int steps) override {
81  bool valid = false;
82  Amount val = value(&valid);
83  val = val + steps * singleStep;
84  val = qBound(m_min_amount, val, m_max_amount);
85  setValue(val);
86  }
87 
88  void setDisplayUnit(int unit) {
89  bool valid = false;
90  Amount val(value(&valid));
91  currentUnit = unit;
92  lineEdit()->setPlaceholderText(
95  if (valid) {
96  setValue(val);
97  } else {
98  clear();
99  }
100  }
101 
102  void setSingleStep(const Amount step) { singleStep = step; }
103 
104  QSize minimumSizeHint() const override {
105  if (cachedMinimumSizeHint.isEmpty()) {
106  ensurePolished();
107 
108  const QFontMetrics fm(fontMetrics());
109  int h = lineEdit()->minimumSizeHint().height();
110  int w = GUIUtil::TextWidth(
112  BitcoinUnits::maxMoney(), false,
114  // Cursor blinking space.
115  w += 2;
116 
117  QStyleOptionSpinBox opt;
118  initStyleOption(&opt);
119  QSize hint(w, h);
120  QSize extra(35, 6);
121  opt.rect.setSize(hint + extra);
122  extra +=
123  hint - style()
124  ->subControlRect(QStyle::CC_SpinBox, &opt,
125  QStyle::SC_SpinBoxEditField, this)
126  .size();
127  // Get closer to final result by repeating the calculation.
128  opt.rect.setSize(hint + extra);
129  extra +=
130  hint - style()
131  ->subControlRect(QStyle::CC_SpinBox, &opt,
132  QStyle::SC_SpinBoxEditField, this)
133  .size();
134  hint += extra;
135  hint.setHeight(h);
136 
137  opt.rect = rect();
138 
140  style()
141  ->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, this)
142  .expandedTo(QApplication::globalStrut());
143  }
144  return cachedMinimumSizeHint;
145  }
146 
147 private:
150  mutable QSize cachedMinimumSizeHint;
151  bool m_allow_empty{true};
154 
160  Amount parse(const QString &text, bool *valid_out = nullptr) const {
161  Amount val = Amount::zero();
162  bool valid = BitcoinUnits::parse(currentUnit, text, &val);
163  if (valid) {
164  if (val < Amount::zero() || val > BitcoinUnits::maxMoney()) {
165  valid = false;
166  }
167  }
168  if (valid_out) {
169  *valid_out = valid;
170  }
171  return valid ? val : Amount::zero();
172  }
173 
174 protected:
175  bool event(QEvent *event) override {
176  if (event->type() == QEvent::KeyPress ||
177  event->type() == QEvent::KeyRelease) {
178  QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
179  if (keyEvent->key() == Qt::Key_Comma) {
180  // Translate a comma into a period.
181  QKeyEvent periodKeyEvent(
182  event->type(), Qt::Key_Period, keyEvent->modifiers(), ".",
183  keyEvent->isAutoRepeat(), keyEvent->count());
184  return QAbstractSpinBox::event(&periodKeyEvent);
185  }
186  }
187  return QAbstractSpinBox::event(event);
188  }
189 
190  StepEnabled stepEnabled() const override {
191  if (isReadOnly()) {
192  // Disable steps when AmountSpinBox is read-only.
193  return StepNone;
194  }
195  if (text().isEmpty()) {
196  // Allow step-up with empty field.
197  return StepUpEnabled;
198  }
199 
200  StepEnabled rv = StepNone;
201  bool valid = false;
202  Amount val = value(&valid);
203  if (valid) {
204  if (val > m_min_amount) {
205  rv |= StepDownEnabled;
206  }
207  if (val < m_max_amount) {
208  rv |= StepUpEnabled;
209  }
210  }
211  return rv;
212  }
213 
214 Q_SIGNALS:
215  void valueChanged();
216 };
217 
218 #include <qt/bitcoinamountfield.moc>
219 
221  : QWidget(parent), amount(nullptr) {
222  amount = new AmountSpinBox(this);
223  amount->setLocale(QLocale::c());
224  amount->installEventFilter(this);
225  amount->setMaximumWidth(240);
226 
227  QHBoxLayout *layout = new QHBoxLayout(this);
228  layout->addWidget(amount);
229  unit = new QValueComboBox(this);
230  unit->setModel(new BitcoinUnits(this));
231  layout->addWidget(unit);
232  layout->addStretch(1);
233  layout->setContentsMargins(0, 0, 0, 0);
234 
235  setLayout(layout);
236 
237  setFocusPolicy(Qt::TabFocus);
238  setFocusProxy(amount);
239 
240  // If one if the widgets changes, the combined content changes as well
241  connect(amount, &AmountSpinBox::valueChanged, this,
243  connect(
244  unit,
245  static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
247 
248  // Set default based on configuration
249  unitChanged(unit->currentIndex());
250 }
251 
253  amount->clear();
254  unit->setCurrentIndex(0);
255 }
256 
257 void BitcoinAmountField::setEnabled(bool fEnabled) {
258  amount->setEnabled(fEnabled);
259  unit->setEnabled(fEnabled);
260 }
261 
263  bool valid = false;
264  value(&valid);
265  setValid(valid);
266  return valid;
267 }
268 
270  if (valid) {
271  amount->setStyleSheet("");
272  } else {
273  amount->setStyleSheet(STYLE_INVALID);
274  }
275 }
276 
277 bool BitcoinAmountField::eventFilter(QObject *object, QEvent *event) {
278  if (event->type() == QEvent::FocusIn) {
279  // Clear invalid flag on focus
280  setValid(true);
281  }
282  return QWidget::eventFilter(object, event);
283 }
284 
285 QWidget *BitcoinAmountField::setupTabChain(QWidget *prev) {
286  QWidget::setTabOrder(prev, amount);
287  QWidget::setTabOrder(amount, unit);
288  return unit;
289 }
290 
291 Amount BitcoinAmountField::value(bool *valid_out) const {
292  return amount->value(valid_out);
293 }
294 
297 }
298 
300  amount->SetAllowEmpty(allow);
301 }
302 
305 }
306 
309 }
310 void BitcoinAmountField::setReadOnly(bool fReadOnly) {
311  amount->setReadOnly(fReadOnly);
312 }
313 
315  // Use description tooltip for current unit for the combobox
316  unit->setToolTip(unit->itemData(idx, Qt::ToolTipRole).toString());
317 
318  // Determine new unit ID
319  int newUnit = unit->itemData(idx, BitcoinUnits::UnitRole).toInt();
320 
321  amount->setDisplayUnit(newUnit);
322 }
323 
325  unit->setValue(newUnit);
326 }
327 
329  amount->setSingleStep(step);
330 }
AmountSpinBox::event
bool event(QEvent *event) override
Definition: bitcoinamountfield.cpp:175
BitcoinAmountField::SetAllowEmpty
void SetAllowEmpty(bool allow)
If allow empty is set to false the field will be set to the minimum allowed value if left empty.
Definition: bitcoinamountfield.cpp:299
BitcoinUnits::maxMoney
static Amount maxMoney()
Return maximum number of base units (Satoshis)
Definition: bitcoinunits.cpp:257
QValueComboBox::setValue
void setValue(const QVariant &value)
Definition: qvaluecombobox.cpp:19
BitcoinAmountField::unitChanged
void unitChanged(int idx)
Definition: bitcoinamountfield.cpp:314
BitcoinUnits::parse
static bool parse(int unit, const QString &value, Amount *val_out)
Parse string to coin amount.
Definition: bitcoinunits.cpp:189
BitcoinAmountField::valueChanged
void valueChanged()
AmountSpinBox::setValue
void setValue(const Amount value)
Definition: bitcoinamountfield.cpp:68
BitcoinAmountField::setReadOnly
void setReadOnly(bool fReadOnly)
Make read-only.
Definition: bitcoinamountfield.cpp:310
BitcoinAmountField::unit
QValueComboBox * unit
Definition: bitcoinamountfield.h:80
AmountSpinBox::value
Amount value(bool *valid_out=nullptr) const
Definition: bitcoinamountfield.cpp:64
BitcoinAmountField::setDisplayUnit
void setDisplayUnit(int unit)
Change unit used to display amount.
Definition: bitcoinamountfield.cpp:324
AmountSpinBox::m_allow_empty
bool m_allow_empty
Definition: bitcoinamountfield.cpp:151
AmountSpinBox::SetMaxValue
void SetMaxValue(const Amount &value)
Definition: bitcoinamountfield.cpp:78
AmountSpinBox::currentUnit
int currentUnit
Definition: bitcoinamountfield.cpp:148
BitcoinUnits::format
static QString format(int unit, const Amount amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD, bool justify=false)
Format as string.
Definition: bitcoinunits.cpp:108
Amount::zero
static constexpr Amount zero()
Definition: amount.h:42
QValueComboBox
Definition: qvaluecombobox.h:13
qvaluecombobox.h
SATOSHI
static constexpr Amount SATOSHI
Definition: amount.h:153
AmountSpinBox::parse
Amount parse(const QString &text, bool *valid_out=nullptr) const
Parse a string into a number of base monetary units and return validity.
Definition: bitcoinamountfield.cpp:160
AmountSpinBox::setSingleStep
void setSingleStep(const Amount step)
Definition: bitcoinamountfield.cpp:102
BitcoinAmountField::SetMaxValue
void SetMaxValue(const Amount &value)
Set the maximum value in satoshis.
Definition: bitcoinamountfield.cpp:307
AmountSpinBox::stepBy
void stepBy(int steps) override
Definition: bitcoinamountfield.cpp:80
AmountSpinBox::SetMinValue
void SetMinValue(const Amount &value)
Definition: bitcoinamountfield.cpp:76
BitcoinAmountField::value
Amount value
Definition: bitcoinamountfield.h:24
BitcoinAmountField::SetMinValue
void SetMinValue(const Amount &value)
Set the minimum value in satoshis.
Definition: bitcoinamountfield.cpp:303
BitcoinUnits
Bitcoin unit definitions.
Definition: bitcoinunits.h:32
BitcoinAmountField::setupTabChain
QWidget * setupTabChain(QWidget *prev)
Qt messes up the tab chain by default in some cases (issue https://bugreports.qt-project....
Definition: bitcoinamountfield.cpp:285
BitcoinAmountField::eventFilter
bool eventFilter(QObject *object, QEvent *event) override
Intercept focus-in event and ',' key presses.
Definition: bitcoinamountfield.cpp:277
guiutil.h
AmountSpinBox
QSpinBox that uses fixed-point numbers internally and uses our own formatting/parsing functions.
Definition: bitcoinamountfield.cpp:22
Amount
Definition: amount.h:19
AmountSpinBox::valueChanged
void valueChanged()
AmountSpinBox::SetAllowEmpty
void SetAllowEmpty(bool allow)
Definition: bitcoinamountfield.cpp:74
AmountSpinBox::m_max_amount
Amount m_max_amount
Definition: bitcoinamountfield.cpp:153
BitcoinUnits::SeparatorStyle::ALWAYS
@ ALWAYS
AmountSpinBox::minimumSizeHint
QSize minimumSizeHint() const override
Definition: bitcoinamountfield.cpp:104
bitcoinamountfield.h
AmountSpinBox::AmountSpinBox
AmountSpinBox(QWidget *parent)
Definition: bitcoinamountfield.cpp:26
BitcoinAmountField::clear
void clear()
Make field empty and ready for new input.
Definition: bitcoinamountfield.cpp:252
guiconstants.h
BitcoinAmountField::amount
AmountSpinBox * amount
Definition: bitcoinamountfield.h:79
BitcoinAmountField::setValid
void setValid(bool valid)
Mark current value as invalid in UI.
Definition: bitcoinamountfield.cpp:269
AmountSpinBox::setDisplayUnit
void setDisplayUnit(int unit)
Definition: bitcoinamountfield.cpp:88
AmountSpinBox::singleStep
Amount singleStep
Definition: bitcoinamountfield.cpp:149
bitcoinunits.h
AmountSpinBox::stepEnabled
StepEnabled stepEnabled() const override
Definition: bitcoinamountfield.cpp:190
GUIUtil::TextWidth
int TextWidth(const QFontMetrics &fm, const QString &text)
Returns the distance in pixels appropriate for drawing a subsequent character after text.
Definition: guiutil.cpp:911
BitcoinUnits::base
@ base
Definition: bitcoinunits.h:42
BitcoinAmountField::BitcoinAmountField
BitcoinAmountField(QWidget *parent=nullptr)
Definition: bitcoinamountfield.cpp:220
AmountSpinBox::fixup
void fixup(QString &input) const override
Definition: bitcoinamountfield.cpp:44
BitcoinAmountField::setSingleStep
void setSingleStep(const Amount step)
Set single step in satoshis.
Definition: bitcoinamountfield.cpp:328
STYLE_INVALID
#define STYLE_INVALID
Definition: guiconstants.h:22
AmountSpinBox::cachedMinimumSizeHint
QSize cachedMinimumSizeHint
Definition: bitcoinamountfield.cpp:150
AmountSpinBox::validate
QValidator::State validate(QString &text, int &pos) const override
Definition: bitcoinamountfield.cpp:33
BitcoinUnits::UnitRole
@ UnitRole
Unit identifier.
Definition: bitcoinunits.h:92
BitcoinAmountField::setValue
void setValue(const Amount value)
Definition: bitcoinamountfield.cpp:295
BitcoinAmountField::validate
bool validate()
Perform input validation, mark field as invalid if entered value is not valid.
Definition: bitcoinamountfield.cpp:262
BitcoinAmountField::setEnabled
void setEnabled(bool fEnabled)
Enable/Disable.
Definition: bitcoinamountfield.cpp:257
AmountSpinBox::m_min_amount
Amount m_min_amount
Definition: bitcoinamountfield.cpp:152