Improved Mac experience; QDoubleSpinBox for BitcoinAmountField
[novacoin.git] / src / qt / bitcoinamountfield.cpp
1 #include "bitcoinamountfield.h"
2 #include "qvaluecombobox.h"
3 #include "bitcoinunits.h"
4
5 #include "guiconstants.h"
6
7 #include <QLabel>
8 #include <QLineEdit>
9 #include <QRegExpValidator>
10 #include <QHBoxLayout>
11 #include <QKeyEvent>
12 #include <QDoubleSpinBox>
13 #include <QComboBox>
14 #include <QApplication>
15 #include <qmath.h>
16
17 BitcoinAmountField::BitcoinAmountField(QWidget *parent):
18         QWidget(parent), amount(0), currentUnit(-1)
19 {
20     amount = new QDoubleSpinBox(this);
21     amount->setLocale(QLocale::c());
22     amount->setDecimals(8);
23     amount->installEventFilter(this);
24     amount->setMaximumWidth(170);
25
26     QHBoxLayout *layout = new QHBoxLayout(this);
27     layout->addWidget(amount);
28     unit = new QValueComboBox(this);
29     unit->setModel(new BitcoinUnits(this));
30     layout->addWidget(unit);
31     layout->addStretch(1);
32     layout->setContentsMargins(0,0,0,0);
33
34     setLayout(layout);
35
36     setFocusPolicy(Qt::TabFocus);
37     setFocusProxy(amount);
38
39     // If one if the widgets changes, the combined content changes as well
40     connect(amount, SIGNAL(valueChanged(QString)), this, SIGNAL(textChanged()));
41     connect(unit, SIGNAL(currentIndexChanged(int)), this, SLOT(unitChanged(int)));
42
43     // Set default based on configuration
44     unitChanged(unit->currentIndex());
45 }
46
47 void BitcoinAmountField::setText(const QString &text)
48 {
49     if (text.isEmpty())
50         amount->clear();
51     else
52         amount->setValue(text.toDouble());
53 }
54
55 void BitcoinAmountField::clear()
56 {
57     amount->clear();
58     unit->setCurrentIndex(0);
59 }
60
61 bool BitcoinAmountField::validate()
62 {
63     bool valid = true;
64     if (amount->value() == 0.0)
65         valid = false;
66     if (valid && !BitcoinUnits::parse(currentUnit, text(), 0))
67         valid = false;
68
69     setValid(valid);
70
71     return valid;
72 }
73
74 void BitcoinAmountField::setValid(bool valid)
75 {
76     if (valid)
77         amount->setStyleSheet("");
78     else
79         amount->setStyleSheet(STYLE_INVALID);
80 }
81
82 QString BitcoinAmountField::text() const
83 {
84     if (amount->text().isEmpty())
85         return QString();
86     else
87         return amount->text();
88 }
89
90 bool BitcoinAmountField::eventFilter(QObject *object, QEvent *event)
91 {
92     if (event->type() == QEvent::FocusIn)
93     {
94         // Clear invalid flag on focus
95         setValid(true);
96     }
97     else if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease)
98     {
99         QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
100         if (keyEvent->key() == Qt::Key_Comma)
101         {
102             // Translate a comma into a period
103             QKeyEvent periodKeyEvent(event->type(), Qt::Key_Period, keyEvent->modifiers(), ".", keyEvent->isAutoRepeat(), keyEvent->count());
104             qApp->sendEvent(object, &periodKeyEvent);
105             return true;
106         }
107     }
108     return QWidget::eventFilter(object, event);
109 }
110
111 QWidget *BitcoinAmountField::setupTabChain(QWidget *prev)
112 {
113     QWidget::setTabOrder(prev, amount);
114     return amount;
115 }
116
117 qint64 BitcoinAmountField::value(bool *valid_out) const
118 {
119     qint64 val_out = 0;
120     bool valid = BitcoinUnits::parse(currentUnit, text(), &val_out);
121     if(valid_out)
122     {
123         *valid_out = valid;
124     }
125     return val_out;
126 }
127
128 void BitcoinAmountField::setValue(qint64 value)
129 {
130     setText(BitcoinUnits::format(currentUnit, value));
131 }
132
133 void BitcoinAmountField::unitChanged(int idx)
134 {
135     // Use description tooltip for current unit for the combobox
136     unit->setToolTip(unit->itemData(idx, Qt::ToolTipRole).toString());
137
138     // Determine new unit ID
139     int newUnit = unit->itemData(idx, BitcoinUnits::UnitRole).toInt();
140
141     // Parse current value and convert to new unit
142     bool valid = false;
143     qint64 currentValue = value(&valid);
144
145     currentUnit = newUnit;
146
147     // Set max length after retrieving the value, to prevent truncation
148     amount->setDecimals(BitcoinUnits::decimals(currentUnit));
149     amount->setMaximum(qPow(10, BitcoinUnits::amountDigits(currentUnit)) - qPow(10, -amount->decimals()));
150
151     if(valid)
152     {
153         // If value was valid, re-place it in the widget with the new unit
154         setValue(currentValue);
155     }
156     else
157     {
158         // If current value is invalid, just clear field
159         setText("");
160     }
161     setValid(true);
162 }
163
164 void BitcoinAmountField::setDisplayUnit(int newUnit)
165 {
166     unit->setValue(newUnit);
167 }