Raven Core  3.0.0
P2P Digital Currency
ravenamountfield.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2015 The Bitcoin Core developers
2 // Copyright (c) 2017-2019 The Raven Core developers
3 // Distributed under the MIT software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 
6 #include "ravenamountfield.h"
7 
8 #include "ravenunits.h"
9 #include "guiconstants.h"
10 #include "qvaluecombobox.h"
11 #include "platformstyle.h"
12 
13 #include <QDebug>
14 #include <QApplication>
15 #include <QAbstractSpinBox>
16 #include <QHBoxLayout>
17 #include <QKeyEvent>
18 #include <QLineEdit>
19 
23 class AmountSpinBox: public QAbstractSpinBox
24 {
25  Q_OBJECT
26 
27 public:
28  explicit AmountSpinBox(QWidget *parent):
29  QAbstractSpinBox(parent),
30  currentUnit(RavenUnits::RVN),
31  singleStep(100000), // satoshis
32  assetUnit(-1)
33  {
34  setAlignment(Qt::AlignRight);
35 
36  connect(lineEdit(), SIGNAL(textEdited(QString)), this, SIGNAL(valueChanged()));
37  }
38 
39  QValidator::State validate(QString &text, int &pos) const
40  {
41  if(text.isEmpty())
42  return QValidator::Intermediate;
43  bool valid = false;
44  parse(text, &valid);
45  /* Make sure we return Intermediate so that fixup() is called on defocus */
46  return valid ? QValidator::Intermediate : QValidator::Invalid;
47  }
48 
49  void fixup(QString &input) const
50  {
51  bool valid = false;
52  CAmount val = parse(input, &valid);
53  if(valid)
54  {
56  lineEdit()->setText(input);
57  }
58  }
59 
60  CAmount value(bool *valid_out=0) const
61  {
62  return parse(text(), valid_out);
63  }
64 
65  void setValue(const CAmount& value)
66  {
67  lineEdit()->setText(RavenUnits::format(currentUnit, value, false, RavenUnits::separatorAlways, assetUnit));
68  Q_EMIT valueChanged();
69  }
70 
71  void stepBy(int steps)
72  {
73  bool valid = false;
74  CAmount val = value(&valid);
75  val = val + steps * singleStep;
76  val = qMin(qMax(val, CAmount(0)), RavenUnits::maxMoney());
77  setValue(val);
78  }
79 
80  void setDisplayUnit(int unit)
81  {
82  bool valid = false;
83  CAmount val = value(&valid);
84 
85  currentUnit = unit;
86 
87  if(valid)
88  setValue(val);
89  else
90  clear();
91  }
92 
93  void setSingleStep(const CAmount& step)
94  {
95  singleStep = step;
96  }
97 
98  void setAssetUnit(int unit)
99  {
100  if (unit > MAX_ASSET_UNITS)
101  unit = MAX_ASSET_UNITS;
102 
103  assetUnit = unit;
104 
105  bool valid = false;
106  CAmount val = value(&valid);
107 
108  if(valid)
109  setValue(val);
110  else
111  clear();
112  }
113 
114  QSize minimumSizeHint() const
115  {
116  if(cachedMinimumSizeHint.isEmpty())
117  {
118  ensurePolished();
119 
120  const QFontMetrics fm(fontMetrics());
121  int h = lineEdit()->minimumSizeHint().height();
123  w += 2; // cursor blinking space
124 
125  QStyleOptionSpinBox opt;
126  initStyleOption(&opt);
127  QSize hint(w, h);
128  QSize extra(35, 6);
129  opt.rect.setSize(hint + extra);
130  extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &opt,
131  QStyle::SC_SpinBoxEditField, this).size();
132  // get closer to final result by repeating the calculation
133  opt.rect.setSize(hint + extra);
134  extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &opt,
135  QStyle::SC_SpinBoxEditField, this).size();
136  hint += extra;
137  hint.setHeight(h);
138 
139  opt.rect = rect();
140 
141  cachedMinimumSizeHint = style()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, this)
142  .expandedTo(QApplication::globalStrut());
143  }
144  return cachedMinimumSizeHint;
145  }
146 
147 private:
150  mutable QSize cachedMinimumSizeHint;
152 
158  CAmount parse(const QString &text, bool *valid_out=0) const
159  {
160  CAmount val = 0;
161 
162  // Update parsing function to work with asset parsing units
163  bool valid = false;
164  if (assetUnit >= 0) {
165  valid = RavenUnits::assetParse(assetUnit, text, &val);
166  }
167  else
168  valid = RavenUnits::parse(currentUnit, text, &val);
169 
170  if(valid)
171  {
172  if(val < 0 || val > RavenUnits::maxMoney())
173  valid = false;
174  }
175  if(valid_out)
176  *valid_out = valid;
177  return valid ? val : 0;
178  }
179 
180 protected:
181  bool event(QEvent *event)
182  {
183  if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease)
184  {
185  QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
186  if (keyEvent->key() == Qt::Key_Comma)
187  {
188  // Translate a comma into a period
189  QKeyEvent periodKeyEvent(event->type(), Qt::Key_Period, keyEvent->modifiers(), ".", keyEvent->isAutoRepeat(), keyEvent->count());
190  return QAbstractSpinBox::event(&periodKeyEvent);
191  }
192  }
193  return QAbstractSpinBox::event(event);
194  }
195 
196  StepEnabled stepEnabled() const
197  {
198  if (isReadOnly()) // Disable steps when AmountSpinBox is read-only
199  return StepNone;
200  if (text().isEmpty()) // Allow step-up with empty field
201  return StepUpEnabled;
202 
203  StepEnabled rv = 0;
204  bool valid = false;
205  CAmount val = value(&valid);
206  if(valid)
207  {
208  if(val > 0)
209  rv |= StepDownEnabled;
210  if(val < RavenUnits::maxMoney())
211  rv |= StepUpEnabled;
212  }
213  return rv;
214  }
215 
216 Q_SIGNALS:
217  void valueChanged();
218 };
219 
220 #include "ravenamountfield.moc"
221 
223  QWidget(parent),
224  amount(0)
225 {
226  amount = new AmountSpinBox(this);
227  amount->setLocale(QLocale::c());
228  amount->installEventFilter(this);
229  amount->setMaximumWidth(170);
230 
231  QHBoxLayout *layout = new QHBoxLayout(this);
232  layout->addWidget(amount);
233  unit = new QValueComboBox();
234  unit->setModel(new RavenUnits(this));
235  layout->addWidget(unit);
236  layout->addStretch(1);
237  layout->setContentsMargins(0,0,0,0);
238 
239  setLayout(layout);
240 
241  setFocusPolicy(Qt::TabFocus);
242  setFocusProxy(amount);
243 
244  // If one if the widgets changes, the combined content changes as well
245  connect(amount, SIGNAL(valueChanged()), this, SIGNAL(valueChanged()));
246  connect(unit, SIGNAL(currentIndexChanged(int)), this, SLOT(unitChanged(int)));
247 
248  // Set default based on configuration
249  unitChanged(unit->currentIndex());
250 
251 }
252 
254 {
255  amount->clear();
256  unit->setCurrentIndex(0);
257 }
258 
259 void RavenAmountField::setEnabled(bool fEnabled)
260 {
261  amount->setEnabled(fEnabled);
262  unit->setEnabled(fEnabled);
263 }
264 
266 {
267  bool valid = false;
268  value(&valid);
269  setValid(valid);
270  return valid;
271 }
272 
274 {
275  if (valid) {
276  amount->setStyleSheet("");
277  } else {
278  amount->setStyleSheet(STYLE_INVALID);
279  }
280 }
281 
282 bool RavenAmountField::eventFilter(QObject *object, QEvent *event)
283 {
284  if (event->type() == QEvent::FocusIn)
285  {
286  // Clear invalid flag on focus
287  setValid(true);
288  }
289  return QWidget::eventFilter(object, event);
290 }
291 
292 QWidget *RavenAmountField::setupTabChain(QWidget *prev)
293 {
294  QWidget::setTabOrder(prev, amount);
295  QWidget::setTabOrder(amount, unit);
296  return unit;
297 }
298 
299 CAmount RavenAmountField::value(bool *valid_out) const
300 {
301  return amount->value(valid_out);
302 }
303 
305 {
306  amount->setValue(value);
307 }
308 
309 void RavenAmountField::setReadOnly(bool fReadOnly)
310 {
311  amount->setReadOnly(fReadOnly);
312 }
313 
315 {
316  // Use description tooltip for current unit for the combobox
317  unit->setToolTip(unit->itemData(idx, Qt::ToolTipRole).toString());
318 
319  // Determine new unit ID
320  int newUnit = unit->itemData(idx, RavenUnits::UnitRole).toInt();
321 
322  amount->setDisplayUnit(newUnit);
323 }
324 
326 {
327  unit->setValue(newUnit);
328 }
329 
331 {
332  amount->setSingleStep(step);
333 }
334 
336  QWidget(parent),
337  amount(0)
338 {
339  amount = new AmountSpinBox(this);
340  amount->setLocale(QLocale::c());
341  amount->installEventFilter(this);
342  amount->setMaximumWidth(170);
343 
344  QHBoxLayout *layout = new QHBoxLayout(this);
345  layout->addWidget(amount);
346  layout->addStretch(1);
347  layout->setContentsMargins(0,0,0,0);
348 
349  setLayout(layout);
350 
351  setFocusPolicy(Qt::TabFocus);
352  setFocusProxy(amount);
353 
354  // If one if the widgets changes, the combined content changes as well
355  connect(amount, SIGNAL(valueChanged()), this, SIGNAL(valueChanged()));
356 
357  // Set default based on configuration
359 }
360 
362 {
363  amount->clear();
365 }
366 
367 void AssetAmountField::setEnabled(bool fEnabled)
368 {
369  amount->setEnabled(fEnabled);
370 }
371 
373 {
374  bool valid = false;
375  value(&valid);
376  setValid(valid);
377  return valid;
378 }
379 
381 {
382  if (valid) {
383  amount->setStyleSheet("");
384  } else {
385  amount->setStyleSheet(STYLE_INVALID);
386  }
387 }
388 
389 bool AssetAmountField::eventFilter(QObject *object, QEvent *event)
390 {
391  if (event->type() == QEvent::FocusIn)
392  {
393  // Clear invalid flag on focus
394  setValid(true);
395  }
396  return QWidget::eventFilter(object, event);
397 }
398 
399 CAmount AssetAmountField::value(bool *valid_out) const
400 {
401  return amount->value(valid_out) * RavenUnits::factorAsset(8 - assetUnit);
402 }
403 
405 {
406  amount->setValue(value);
407 }
408 
409 void AssetAmountField::setReadOnly(bool fReadOnly)
410 {
411  amount->setReadOnly(fReadOnly);
412 }
413 
415 {
416  amount->setSingleStep(step);
417 }
418 
420 {
421  assetUnit = unit;
423 }
AmountSpinBox * amount
AmountSpinBox * amount
static QString format(int unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=separatorStandard, const int nAssetUnit=MIN_ASSET_UNITS - 1)
Format as string.
Definition: ravenunits.cpp:101
static bool assetParse(int assetUnit, const QString &value, CAmount *val_out)
Definition: ravenunits.cpp:203
bool validate()
Perform input validation, mark field as invalid if entered value is not valid.
void setEnabled(bool fEnabled)
Enable/Disable.
void unitChanged(int idx)
void setEnabled(bool fEnabled)
Enable/Disable.
QSpinBox that uses fixed-point numbers internally and uses our own formatting/parsing functions...
void valueChanged()
void setValid(bool valid)
Mark current value as invalid in UI.
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
RavenAmountField(QWidget *parent=0)
AssetAmountField(QWidget *parent=0)
void setSingleStep(const CAmount &step)
Set single step in satoshis.
static bool parse(int unit, const QString &value, CAmount *val_out)
Parse string to coin amount.
Definition: ravenunits.cpp:164
QValidator::State validate(QString &text, int &pos) const
void setValue(const CAmount &value)
void setSingleStep(const CAmount &step)
Set single step in satoshis.
int64_t CAmount
Amount in corbies (Can be negative)
Definition: amount.h:13
void setValue(const CAmount &value)
bool eventFilter(QObject *object, QEvent *event)
Intercept focus-in event and &#39;,&#39; key presses.
bool eventFilter(QObject *object, QEvent *event)
Intercept focus-in event and &#39;,&#39; key presses.
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.
void setDisplayUnit(int unit)
Change unit used to display amount.
void stepBy(int steps)
#define STYLE_INVALID
Definition: guiconstants.h:21
CAmount parse(const QString &text, bool *valid_out=0) const
Parse a string into a number of base monetary units and return validity.
void setReadOnly(bool fReadOnly)
Make read-only.
bool event(QEvent *event)
void setReadOnly(bool fReadOnly)
Make read-only.
void setValue(const QVariant &value)
void clear()
Make field empty and ready for new input.
static qint64 factorAsset(int unit)
Number of Satoshis (1e-8) per unit for assets.
Definition: ravenunits.cpp:73
void setUnit(int unit)
Unit identifier.
Definition: ravenunits.h:115
void setSingleStep(const CAmount &step)
void setValue(const CAmount &value)
QValueComboBox * unit
void fixup(QString &input) const
Raven unit definitions.
Definition: ravenunits.h:53
void setAssetUnit(int unit)
void setValid(bool valid)
Mark current value as invalid in UI.
#define MAX_ASSET_UNITS
Definition: ravenunits.h:15
static CAmount maxMoney()
Return maximum number of base units (Satoshis)
Definition: ravenunits.cpp:278