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