Dash Core  0.12.2.1
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(100000) // satoshis
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 }
AmountSpinBox(QWidget *parent)
bool event(QEvent *event)
void setEnabled(bool fEnabled)
static bool parse(int unit, const QString &value, CAmount *val_out)
Parse string to coin amount.
BitcoinAmountField(QWidget *parent=0)
void stepBy(int steps)
static CAmount maxMoney()
Return maximum number of base units (Satoshis)
int64_t CAmount
Definition: amount.h:14
void setSingleStep(const CAmount &step)
void setDisplayUnit(int unit)
void setDisplayUnit(int unit)
CAmount value(bool *valid_out=0) const
void setValue(const QVariant &value)
AmountSpinBox * amount
void setValue(const CAmount &value)
#define STYLE_INVALID
Definition: guiconstants.h:21
static QString format(int unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=separatorStandard)
Format as string.
void setValid(bool valid)
StepEnabled stepEnabled() const
bool eventFilter(QObject *object, QEvent *event)
void fixup(QString &input) const
void valueChanged()
QValidator::State validate(QString &text, int &pos) const
QSize minimumSizeHint() const
QValueComboBox * unit
QWidget * setupTabChain(QWidget *prev)
void setValue(const CAmount &value)
CAmount parse(const QString &text, bool *valid_out=0) const
void setReadOnly(bool fReadOnly)
void setSingleStep(const CAmount &step)