BSHA3  0.17.99
P2P Blockchain, based on Bitcoin
bitcoinamountfield.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2018 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/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(100000) // satoshis
29  {
30  setAlignment(Qt::AlignRight);
31 
32  connect(lineEdit(), &QLineEdit::textEdited, this, &AmountSpinBox::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 <qt/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(240);
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
217  connect(unit, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &BitcoinAmountField::unitChanged);
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 }
bool validate()
Perform input validation, mark field as invalid if entered value is not valid.
Bitcoin unit definitions.
Definition: bitcoinunits.h:47
void setReadOnly(bool fReadOnly)
Make read-only.
QSpinBox that uses fixed-point numbers internally and uses our own formatting/parsing functions...
void valueChanged()
AmountSpinBox(QWidget *parent)
CAmount value(bool *valid_out=0) const
StepEnabled stepEnabled() const
QWidget * setupTabChain(QWidget *prev)
Qt messes up the tab chain by default in some cases (issue https://bugreports.qt-project.org/browse/QTBUG-10907), in these cases we have to set it up manually.
void setDisplayUnit(int unit)
QSize minimumSizeHint() const
bool eventFilter(QObject *object, QEvent *event)
Intercept focus-in event and &#39;,&#39; key presses.
static bool parse(int unit, const QString &value, CAmount *val_out)
Parse string to coin amount.
QValidator::State validate(QString &text, int &pos) const
void setValue(const CAmount &value)
int64_t CAmount
Amount in satoshis (Can be negative)
Definition: amount.h:12
BitcoinAmountField(QWidget *parent=0)
AmountSpinBox * amount
void setEnabled(bool fEnabled)
Enable/Disable.
void stepBy(int steps)
#define STYLE_INVALID
Definition: guiconstants.h:20
CAmount parse(const QString &text, bool *valid_out=0) const
Parse a string into a number of base monetary units and return validity.
Unit identifier.
Definition: bitcoinunits.h:107
void setSingleStep(const CAmount &step)
Set single step in satoshis.
bool event(QEvent *event)
QValueComboBox * unit
void setValue(const QVariant &value)
void clear()
Make field empty and ready for new input.
void setValid(bool valid)
Mark current value as invalid in UI.
void setSingleStep(const CAmount &step)
void setDisplayUnit(int unit)
Change unit used to display amount.
void fixup(QString &input) const
void setValue(const CAmount &value)
static QString format(int unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=separatorStandard)
Format as string.
static CAmount maxMoney()
Return maximum number of base units (Satoshis)