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.
5 #include "bitcoinamountfield.h"
7 #include "bitcoinunits.h"
8 #include "guiconstants.h"
9 #include "qvaluecombobox.h"
11 #include <QApplication>
12 #include <QAbstractSpinBox>
13 #include <QHBoxLayout>
17 /** QSpinBox that uses fixed-point numbers internally and uses our own
18 * formatting/parsing functions.
20 class AmountSpinBox
: public QAbstractSpinBox
25 explicit AmountSpinBox(QWidget
*parent
):
26 QAbstractSpinBox(parent
),
27 currentUnit(BitcoinUnits::BTC
),
28 singleStep(100000) // satoshis
30 setAlignment(Qt::AlignRight
);
32 connect(lineEdit(), SIGNAL(textEdited(QString
)), this, SIGNAL(valueChanged()));
35 QValidator::State
validate(QString
&text
, int &pos
) const
38 return QValidator::Intermediate
;
41 /* Make sure we return Intermediate so that fixup() is called on defocus */
42 return valid
? QValidator::Intermediate
: QValidator::Invalid
;
45 void fixup(QString
&input
) const
48 CAmount val
= parse(input
, &valid
);
51 input
= BitcoinUnits::format(currentUnit
, val
, false, BitcoinUnits::separatorAlways
);
52 lineEdit()->setText(input
);
56 CAmount
value(bool *valid_out
=0) const
58 return parse(text(), valid_out
);
61 void setValue(const CAmount
& value
)
63 lineEdit()->setText(BitcoinUnits::format(currentUnit
, value
, false, BitcoinUnits::separatorAlways
));
64 Q_EMIT
valueChanged();
67 void stepBy(int steps
)
70 CAmount val
= value(&valid
);
71 val
= val
+ steps
* singleStep
;
72 val
= qMin(qMax(val
, CAmount(0)), BitcoinUnits::maxMoney());
76 void setDisplayUnit(int unit
)
79 CAmount val
= value(&valid
);
89 void setSingleStep(const CAmount
& step
)
94 QSize
minimumSizeHint() const
96 if(cachedMinimumSizeHint
.isEmpty())
100 const QFontMetrics
fm(fontMetrics());
101 int h
= lineEdit()->minimumSizeHint().height();
102 int w
= fm
.width(BitcoinUnits::format(BitcoinUnits::BTC
, BitcoinUnits::maxMoney(), false, BitcoinUnits::separatorAlways
));
103 w
+= 2; // cursor blinking space
105 QStyleOptionSpinBox opt
;
106 initStyleOption(&opt
);
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();
121 cachedMinimumSizeHint
= style()->sizeFromContents(QStyle::CT_SpinBox
, &opt
, hint
, this)
122 .expandedTo(QApplication::globalStrut());
124 return cachedMinimumSizeHint
;
130 mutable QSize cachedMinimumSizeHint
;
133 * Parse a string into a number of base monetary units and
135 * @note Must return 0 if !valid.
137 CAmount
parse(const QString
&text
, bool *valid_out
=0) const
140 bool valid
= BitcoinUnits::parse(currentUnit
, text
, &val
);
143 if(val
< 0 || val
> BitcoinUnits::maxMoney())
148 return valid
? val
: 0;
152 bool event(QEvent
*event
)
154 if (event
->type() == QEvent::KeyPress
|| event
->type() == QEvent::KeyRelease
)
156 QKeyEvent
*keyEvent
= static_cast<QKeyEvent
*>(event
);
157 if (keyEvent
->key() == Qt::Key_Comma
)
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
);
164 return QAbstractSpinBox::event(event
);
167 StepEnabled
stepEnabled() const
169 if (isReadOnly()) // Disable steps when AmountSpinBox is read-only
171 if (text().isEmpty()) // Allow step-up with empty field
172 return StepUpEnabled
;
176 CAmount val
= value(&valid
);
180 rv
|= StepDownEnabled
;
181 if(val
< BitcoinUnits::maxMoney())
191 #include "bitcoinamountfield.moc"
193 BitcoinAmountField::BitcoinAmountField(QWidget
*parent
) :
197 amount
= new AmountSpinBox(this);
198 amount
->setLocale(QLocale::c());
199 amount
->installEventFilter(this);
200 amount
->setMaximumWidth(170);
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);
212 setFocusPolicy(Qt::TabFocus
);
213 setFocusProxy(amount
);
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)));
219 // Set default based on configuration
220 unitChanged(unit
->currentIndex());
223 void BitcoinAmountField::clear()
226 unit
->setCurrentIndex(0);
229 void BitcoinAmountField::setEnabled(bool fEnabled
)
231 amount
->setEnabled(fEnabled
);
232 unit
->setEnabled(fEnabled
);
235 bool BitcoinAmountField::validate()
243 void BitcoinAmountField::setValid(bool valid
)
246 amount
->setStyleSheet("");
248 amount
->setStyleSheet(STYLE_INVALID
);
251 bool BitcoinAmountField::eventFilter(QObject
*object
, QEvent
*event
)
253 if (event
->type() == QEvent::FocusIn
)
255 // Clear invalid flag on focus
258 return QWidget::eventFilter(object
, event
);
261 QWidget
*BitcoinAmountField::setupTabChain(QWidget
*prev
)
263 QWidget::setTabOrder(prev
, amount
);
264 QWidget::setTabOrder(amount
, unit
);
268 CAmount
BitcoinAmountField::value(bool *valid_out
) const
270 return amount
->value(valid_out
);
273 void BitcoinAmountField::setValue(const CAmount
& value
)
275 amount
->setValue(value
);
278 void BitcoinAmountField::setReadOnly(bool fReadOnly
)
280 amount
->setReadOnly(fReadOnly
);
283 void BitcoinAmountField::unitChanged(int idx
)
285 // Use description tooltip for current unit for the combobox
286 unit
->setToolTip(unit
->itemData(idx
, Qt::ToolTipRole
).toString());
288 // Determine new unit ID
289 int newUnit
= unit
->itemData(idx
, BitcoinUnits::UnitRole
).toInt();
291 amount
->setDisplayUnit(newUnit
);
294 void BitcoinAmountField::setDisplayUnit(int newUnit
)
296 unit
->setValue(newUnit
);
299 void BitcoinAmountField::setSingleStep(const CAmount
& step
)
301 amount
->setSingleStep(step
);