Dogecoin Core  1.14.2
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 
5 #include "bitcoinamountfield.h"
6 
7 #include "bitcoinunits.h"
8 #include "guiconstants.h"
9 #include "qvaluecombobox.h"
10 
11 #include <QApplication>
12 #include <QAbstractSpinBox>
13 #include <QHBoxLayout>
14 #include <QKeyEvent>
15 #include <QLineEdit>
16 
20 class AmountSpinBox: public QAbstractSpinBox
21 {
22  Q_OBJECT
23 
24 public:
25  explicit AmountSpinBox(QWidget *parent):
26  QAbstractSpinBox(parent),
28  singleStep(100000000) // koinu
29  {
30  setAlignment(Qt::AlignRight);
31 
32  connect(lineEdit(), SIGNAL(textEdited(QString)), this, SIGNAL(valueChanged()));
33  }
34 
35  QValidator::State validate(QString &text, int &pos) const
36  {
37  if(text.isEmpty())
38  return QValidator::Intermediate;
39  bool valid = false;
40  parse(text, &valid);
41  /* Make sure we return Intermediate so that fixup() is called on defocus */
42  return valid ? QValidator::Intermediate : QValidator::Invalid;
43  }
44 
45  void fixup(QString &input) const
46  {
47  bool valid = false;
48  CAmount val = parse(input, &valid);
49  if(valid)
50  {
52  lineEdit()->setText(input);
53  }
54  }
55 
56  CAmount value(bool *valid_out=0) const
57  {
58  return parse(text(), valid_out);
59  }
60 
61  void setValue(const CAmount& value)
62  {
64  Q_EMIT valueChanged();
65  }
66 
67  void stepBy(int steps)
68  {
69  bool valid = false;
70  CAmount val = value(&valid);
71  val = val + steps * singleStep;
72  val = qMin(qMax(val, CAmount(0)), BitcoinUnits::maxMoney());
73  setValue(val);
74  }
75 
76  void setDisplayUnit(int unit)
77  {
78  bool valid = false;
79  CAmount val = value(&valid);
80 
81  currentUnit = unit;
82 
83  if(valid)
84  setValue(val);
85  else
86  clear();
87  }
88 
89  void setSingleStep(const CAmount& step)
90  {
91  singleStep = step;
92  }
93 
94  QSize minimumSizeHint() const
95  {
96  if(cachedMinimumSizeHint.isEmpty())
97  {
98  ensurePolished();
99 
100  const QFontMetrics fm(fontMetrics());
101  int h = lineEdit()->minimumSizeHint().height();
103  w += 2; // cursor blinking space
104 
105  QStyleOptionSpinBox opt;
106  initStyleOption(&opt);
107  QSize hint(w, h);
108  QSize extra(35, 6);
109  opt.rect.setSize(hint + extra);
110  extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &opt,
111  QStyle::SC_SpinBoxEditField, this).size();
112  // get closer to final result by repeating the calculation
113  opt.rect.setSize(hint + extra);
114  extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &opt,
115  QStyle::SC_SpinBoxEditField, this).size();
116  hint += extra;
117  hint.setHeight(h);
118 
119  opt.rect = rect();
120 
121  cachedMinimumSizeHint = style()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, this)
122  .expandedTo(QApplication::globalStrut());
123  }
124  return cachedMinimumSizeHint;
125  }
126 
127 private:
130  mutable QSize cachedMinimumSizeHint;
131 
137  CAmount parse(const QString &text, bool *valid_out=0) const
138  {
139  CAmount val = 0;
140  bool valid = BitcoinUnits::parse(currentUnit, text, &val);
141  if(valid)
142  {
143  if(val < 0 || val > BitcoinUnits::maxMoney())
144  valid = false;
145  }
146  if(valid_out)
147  *valid_out = valid;
148  return valid ? val : 0;
149  }
150 
151 protected:
152  bool event(QEvent *event)
153  {
154  if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease)
155  {
156  QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
157  if (keyEvent->key() == Qt::Key_Comma)
158  {
159  // Translate a comma into a period
160  QKeyEvent periodKeyEvent(event->type(), Qt::Key_Period, keyEvent->modifiers(), ".", keyEvent->isAutoRepeat(), keyEvent->count());
161  return QAbstractSpinBox::event(&periodKeyEvent);
162  }
163  }
164  return QAbstractSpinBox::event(event);
165  }
166 
167  StepEnabled stepEnabled() const
168  {
169  if (isReadOnly()) // Disable steps when AmountSpinBox is read-only
170  return StepNone;
171  if (text().isEmpty()) // Allow step-up with empty field
172  return StepUpEnabled;
173 
174  StepEnabled rv = 0;
175  bool valid = false;
176  CAmount val = value(&valid);
177  if(valid)
178  {
179  if(val > 0)
180  rv |= StepDownEnabled;
181  if(val < BitcoinUnits::maxMoney())
182  rv |= StepUpEnabled;
183  }
184  return rv;
185  }
186 
187 Q_SIGNALS:
188  void valueChanged();
189 };
190 
191 #include "bitcoinamountfield.moc"
192 
194  QWidget(parent),
195  amount(0)
196 {
197  amount = new AmountSpinBox(this);
198  amount->setLocale(QLocale::c());
199  amount->installEventFilter(this);
200  amount->setMaximumWidth(170);
201 
202  QHBoxLayout *layout = new QHBoxLayout(this);
203  layout->addWidget(amount);
204  unit = new QValueComboBox(this);
205  unit->setModel(new BitcoinUnits(this));
206  layout->addWidget(unit);
207  layout->addStretch(1);
208  layout->setContentsMargins(0,0,0,0);
209 
210  setLayout(layout);
211 
212  setFocusPolicy(Qt::TabFocus);
213  setFocusProxy(amount);
214 
215  // If one if the widgets changes, the combined content changes as well
216  connect(amount, SIGNAL(valueChanged()), this, SIGNAL(valueChanged()));
217  connect(unit, SIGNAL(currentIndexChanged(int)), this, SLOT(unitChanged(int)));
218 
219  // Set default based on configuration
220  unitChanged(unit->currentIndex());
221 }
222 
224 {
225  amount->clear();
226  unit->setCurrentIndex(0);
227 }
228 
230 {
231  amount->setEnabled(fEnabled);
232  unit->setEnabled(fEnabled);
233 }
234 
236 {
237  bool valid = false;
238  value(&valid);
239  setValid(valid);
240  return valid;
241 }
242 
244 {
245  if (valid)
246  amount->setStyleSheet("");
247  else
248  amount->setStyleSheet(STYLE_INVALID);
249 }
250 
251 bool BitcoinAmountField::eventFilter(QObject *object, QEvent *event)
252 {
253  if (event->type() == QEvent::FocusIn)
254  {
255  // Clear invalid flag on focus
256  setValid(true);
257  }
258  return QWidget::eventFilter(object, event);
259 }
260 
261 QWidget *BitcoinAmountField::setupTabChain(QWidget *prev)
262 {
263  QWidget::setTabOrder(prev, amount);
264  QWidget::setTabOrder(amount, unit);
265  return unit;
266 }
267 
268 CAmount BitcoinAmountField::value(bool *valid_out) const
269 {
270  return amount->value(valid_out);
271 }
272 
274 {
276 }
277 
279 {
280  amount->setReadOnly(fReadOnly);
281 }
282 
284 {
285  // Use description tooltip for current unit for the combobox
286  unit->setToolTip(unit->itemData(idx, Qt::ToolTipRole).toString());
287 
288  // Determine new unit ID
289  int newUnit = unit->itemData(idx, BitcoinUnits::UnitRole).toInt();
290 
291  amount->setDisplayUnit(newUnit);
292 }
293 
295 {
296  unit->setValue(newUnit);
297 }
298 
300 {
301  amount->setSingleStep(step);
302 }
int64_t CAmount
Amount in satoshis (Can be negative)
Definition: amount.h:15
QSpinBox that uses fixed-point numbers internally and uses our own formatting/parsing functions.
void stepBy(int steps)
void fixup(QString &input) const
void setSingleStep(const CAmount &step)
AmountSpinBox(QWidget *parent)
QSize minimumSizeHint() const
CAmount parse(const QString &text, bool *valid_out=0) const
Parse a string into a number of base monetary units and return validity.
void valueChanged()
StepEnabled stepEnabled() const
CAmount value(bool *valid_out=0) const
void setValue(const CAmount &value)
void setDisplayUnit(int unit)
bool event(QEvent *event)
QValidator::State validate(QString &text, int &pos) const
AmountSpinBox * amount
void setEnabled(bool fEnabled)
Enable/Disable.
QValueComboBox * unit
void setSingleStep(const CAmount &step)
Set single step in satoshis.
void setReadOnly(bool fReadOnly)
Make read-only.
void setDisplayUnit(int unit)
Change unit used to display amount.
void clear()
Make field empty and ready for new input.
bool validate()
Perform input validation, mark field as invalid if entered value is not valid.
BitcoinAmountField(QWidget *parent=0)
QWidget * setupTabChain(QWidget *prev)
Qt messes up the tab chain by default in some cases (issue https://bugreports.qt-project....
void setValid(bool valid)
Mark current value as invalid in UI.
bool eventFilter(QObject *object, QEvent *event)
Intercept focus-in event and ',' key presses.
void setValue(const CAmount &value)
Bitcoin unit definitions.
Definition: bitcoinunits.h:48
@ UnitRole
Unit identifier.
Definition: bitcoinunits.h:106
static bool parse(int unit, const QString &value, CAmount *val_out)
Parse string to coin amount.
static CAmount maxMoney()
Return maximum number of base units (Satoshis)
static QString format(int unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=separatorStandard)
Format as string.
void setValue(const QVariant &value)
#define STYLE_INVALID
Definition: guiconstants.h:20