9
9
#include " qvaluecombobox.h"
10
10
11
11
#include < QApplication>
12
- #include < QDoubleSpinBox >
12
+ #include < QAbstractSpinBox >
13
13
#include < QHBoxLayout>
14
14
#include < QKeyEvent>
15
- #include < qmath.h > // for qPow()
15
+ #include < QLineEdit >
16
16
17
- // QDoubleSpinBox that shows SI-style thin space thousands separators
18
- class AmountSpinBox : public QDoubleSpinBox
17
+ /* * QSpinBox that uses fixed-point numbers internally and uses our own
18
+ * formatting/parsing functions.
19
+ */
20
+ class AmountSpinBox : public QAbstractSpinBox
19
21
{
22
+ Q_OBJECT
20
23
public:
21
24
explicit AmountSpinBox (QWidget *parent):
22
- QDoubleSpinBox(parent)
25
+ QAbstractSpinBox(parent),
26
+ currentUnit(BitcoinUnits::BTC),
27
+ singleStep(100000 ) // satoshis
23
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
+ qint64 val = parse (input, &valid);
48
+ if (valid)
49
+ {
50
+ input = BitcoinUnits::format (currentUnit, val, false , BitcoinUnits::separatorAlways);
51
+ lineEdit ()->setText (input);
52
+ }
24
53
}
25
- QString textFromValue (double value) const
54
+
55
+ qint64 value (bool *valid_out=0 ) const
26
56
{
27
- QStringList parts = QDoubleSpinBox::textFromValue (value).split (" ." );
28
- QString quotient_str = parts[0 ];
29
- QString remainder_str;
30
- if (parts.size () > 1 )
31
- remainder_str = parts[1 ];
32
-
33
- // Code duplication between here and BitcoinUnits::format
34
- // TODO: Figure out how to share this code
35
- QChar thin_sp (THIN_SP_CP);
36
- int q_size = quotient_str.size ();
37
- if (q_size > 4 )
38
- for (int i = 3 ; i < q_size; i += 3 )
39
- quotient_str.insert (q_size - i, thin_sp);
40
-
41
- int r_size = remainder_str.size ();
42
- if (r_size > 4 )
43
- for (int i = 3 , adj = 0 ; i < r_size; i += 3 , adj++)
44
- remainder_str.insert (i + adj, thin_sp);
45
-
46
- if (remainder_str.isEmpty ())
47
- return quotient_str;
57
+ return parse (text (), valid_out);
58
+ }
59
+
60
+ void setValue (qint64 value)
61
+ {
62
+ lineEdit ()->setText (BitcoinUnits::format (currentUnit, value, false , BitcoinUnits::separatorAlways));
63
+ emit valueChanged ();
64
+ }
65
+
66
+ void stepBy (int steps)
67
+ {
68
+ bool valid = false ;
69
+ qint64 val = value (&valid);
70
+ val = val + steps * singleStep;
71
+ val = qMin (qMax (val, Q_INT64_C (0 )), BitcoinUnits::maxMoney ());
72
+ setValue (val);
73
+ }
74
+
75
+ StepEnabled stepEnabled () const
76
+ {
77
+ StepEnabled rv = 0 ;
78
+ if (text ().isEmpty ()) // Allow step-up with empty field
79
+ return StepUpEnabled;
80
+ bool valid = false ;
81
+ qint64 val = value (&valid);
82
+ if (valid)
83
+ {
84
+ if (val > 0 )
85
+ rv |= StepDownEnabled;
86
+ if (val < BitcoinUnits::maxMoney ())
87
+ rv |= StepUpEnabled;
88
+ }
89
+ return rv;
90
+ }
91
+
92
+ void setDisplayUnit (int unit)
93
+ {
94
+ bool valid = false ;
95
+ qint64 val = value (&valid);
96
+
97
+ currentUnit = unit;
98
+
99
+ if (valid)
100
+ setValue (val);
48
101
else
49
- return quotient_str + QString ( " . " ) + remainder_str ;
102
+ clear () ;
50
103
}
51
- QValidator::State validate (QString &text, int &pos) const
104
+
105
+ void setSingleStep (qint64 step)
52
106
{
53
- QString s (BitcoinUnits::removeSpaces (text));
54
- return QDoubleSpinBox::validate (s, pos);
107
+ singleStep = step;
108
+ }
109
+
110
+ QSize minimumSizeHint () const
111
+ {
112
+ if (cachedMinimumSizeHint.isEmpty ())
113
+ {
114
+ ensurePolished ();
115
+
116
+ const QFontMetrics fm (fontMetrics ());
117
+ int h = lineEdit ()->minimumSizeHint ().height ();
118
+ int w = fm.width (BitcoinUnits::format (BitcoinUnits::BTC, BitcoinUnits::maxMoney (), false , BitcoinUnits::separatorAlways));
119
+ w += 2 ; // cursor blinking space
120
+
121
+ QStyleOptionSpinBox opt;
122
+ initStyleOption (&opt);
123
+ QSize hint (w, h);
124
+ QSize extra (35 , 6 );
125
+ opt.rect .setSize (hint + extra);
126
+ extra += hint - style ()->subControlRect (QStyle::CC_SpinBox, &opt,
127
+ QStyle::SC_SpinBoxEditField, this ).size ();
128
+ // get closer to final result by repeating the calculation
129
+ opt.rect .setSize (hint + extra);
130
+ extra += hint - style ()->subControlRect (QStyle::CC_SpinBox, &opt,
131
+ QStyle::SC_SpinBoxEditField, this ).size ();
132
+ hint += extra;
133
+
134
+ opt.rect = rect ();
135
+
136
+ cachedMinimumSizeHint = style ()->sizeFromContents (QStyle::CT_SpinBox, &opt, hint, this )
137
+ .expandedTo (QApplication::globalStrut ());
138
+ }
139
+ return cachedMinimumSizeHint;
140
+ }
141
+ private:
142
+ int currentUnit;
143
+ qint64 singleStep;
144
+ mutable QSize cachedMinimumSizeHint;
145
+
146
+ /* *
147
+ * Parse a string into a number of base monetary units and
148
+ * return validity.
149
+ * @note Must return 0 if !valid.
150
+ */
151
+ qint64 parse (const QString &text, bool *valid_out=0 ) const
152
+ {
153
+ qint64 val = 0 ;
154
+ bool valid = BitcoinUnits::parse (currentUnit, text, &val);
155
+ if (valid)
156
+ {
157
+ if (val < 0 || val > BitcoinUnits::maxMoney ())
158
+ valid = false ;
159
+ }
160
+ if (valid_out)
161
+ *valid_out = valid;
162
+ return valid ? val : 0 ;
55
163
}
56
- double valueFromText (const QString& text) const
164
+
165
+ protected:
166
+ bool event (QEvent *event)
57
167
{
58
- return QDoubleSpinBox::valueFromText (BitcoinUnits::removeSpaces (text));
168
+ if (event->type () == QEvent::KeyPress || event->type () == QEvent::KeyRelease)
169
+ {
170
+ QKeyEvent *keyEvent = static_cast <QKeyEvent *>(event);
171
+ if (keyEvent->key () == Qt::Key_Comma)
172
+ {
173
+ // Translate a comma into a period
174
+ QKeyEvent periodKeyEvent (event->type (), Qt::Key_Period, keyEvent->modifiers (), " ." , keyEvent->isAutoRepeat (), keyEvent->count ());
175
+ return QAbstractSpinBox::event (&periodKeyEvent);
176
+ }
177
+ }
178
+ return QAbstractSpinBox::event (event);
59
179
}
180
+
181
+ signals:
182
+ void valueChanged ();
60
183
};
61
184
185
+ #include " bitcoinamountfield.moc"
186
+
62
187
BitcoinAmountField::BitcoinAmountField (QWidget *parent) :
63
188
QWidget(parent),
64
- amount(0 ),
65
- currentUnit(-1 )
189
+ amount(0 )
66
190
{
67
- nSingleStep = 100000 ; // satoshis
68
-
69
191
amount = new AmountSpinBox (this );
70
192
amount->setLocale (QLocale::c ());
71
193
amount->installEventFilter (this );
@@ -85,21 +207,13 @@ BitcoinAmountField::BitcoinAmountField(QWidget *parent) :
85
207
setFocusProxy (amount);
86
208
87
209
// If one if the widgets changes, the combined content changes as well
88
- connect (amount, SIGNAL (valueChanged (QString )), this , SIGNAL (textChanged ()));
210
+ connect (amount, SIGNAL (valueChanged ()), this , SIGNAL (valueChanged ()));
89
211
connect (unit, SIGNAL (currentIndexChanged (int )), this , SLOT (unitChanged (int )));
90
212
91
213
// Set default based on configuration
92
214
unitChanged (unit->currentIndex ());
93
215
}
94
216
95
- void BitcoinAmountField::setText (const QString &text)
96
- {
97
- if (text.isEmpty ())
98
- amount->clear ();
99
- else
100
- amount->setValue (BitcoinUnits::removeSpaces (text).toDouble ());
101
- }
102
-
103
217
void BitcoinAmountField::clear ()
104
218
{
105
219
amount->clear ();
@@ -108,16 +222,9 @@ void BitcoinAmountField::clear()
108
222
109
223
bool BitcoinAmountField::validate ()
110
224
{
111
- bool valid = true ;
112
- if (amount->value () == 0.0 )
113
- valid = false ;
114
- else if (!BitcoinUnits::parse (currentUnit, text (), 0 ))
115
- valid = false ;
116
- else if (amount->value () > BitcoinUnits::maxAmount (currentUnit))
117
- valid = false ;
118
-
225
+ bool valid = false ;
226
+ value (&valid);
119
227
setValid (valid);
120
-
121
228
return valid;
122
229
}
123
230
@@ -129,32 +236,13 @@ void BitcoinAmountField::setValid(bool valid)
129
236
amount->setStyleSheet (STYLE_INVALID);
130
237
}
131
238
132
- QString BitcoinAmountField::text () const
133
- {
134
- if (amount->text ().isEmpty ())
135
- return QString ();
136
- else
137
- return amount->text ();
138
- }
139
-
140
239
bool BitcoinAmountField::eventFilter (QObject *object, QEvent *event)
141
240
{
142
241
if (event->type () == QEvent::FocusIn)
143
242
{
144
243
// Clear invalid flag on focus
145
244
setValid (true );
146
245
}
147
- else if (event->type () == QEvent::KeyPress || event->type () == QEvent::KeyRelease)
148
- {
149
- QKeyEvent *keyEvent = static_cast <QKeyEvent *>(event);
150
- if (keyEvent->key () == Qt::Key_Comma)
151
- {
152
- // Translate a comma into a period
153
- QKeyEvent periodKeyEvent (event->type (), Qt::Key_Period, keyEvent->modifiers (), " ." , keyEvent->isAutoRepeat (), keyEvent->count ());
154
- QApplication::sendEvent (object, &periodKeyEvent);
155
- return true ;
156
- }
157
- }
158
246
return QWidget::eventFilter (object, event);
159
247
}
160
248
@@ -167,18 +255,12 @@ QWidget *BitcoinAmountField::setupTabChain(QWidget *prev)
167
255
168
256
qint64 BitcoinAmountField::value (bool *valid_out) const
169
257
{
170
- qint64 val_out = 0 ;
171
- bool valid = BitcoinUnits::parse (currentUnit, text (), &val_out);
172
- if (valid_out)
173
- {
174
- *valid_out = valid;
175
- }
176
- return val_out;
258
+ return amount->value (valid_out);
177
259
}
178
260
179
261
void BitcoinAmountField::setValue (qint64 value)
180
262
{
181
- setText ( BitcoinUnits::format (currentUnit, value) );
263
+ amount-> setValue ( value);
182
264
}
183
265
184
266
void BitcoinAmountField::setReadOnly (bool fReadOnly )
@@ -195,28 +277,7 @@ void BitcoinAmountField::unitChanged(int idx)
195
277
// Determine new unit ID
196
278
int newUnit = unit->itemData (idx, BitcoinUnits::UnitRole).toInt ();
197
279
198
- // Parse current value and convert to new unit
199
- bool valid = false ;
200
- qint64 currentValue = value (&valid);
201
-
202
- currentUnit = newUnit;
203
-
204
- // Set max length after retrieving the value, to prevent truncation
205
- amount->setDecimals (BitcoinUnits::decimals (currentUnit));
206
- amount->setMaximum (qPow (10 , BitcoinUnits::amountDigits (currentUnit)) - qPow (10 , -amount->decimals ()));
207
- amount->setSingleStep ((double )nSingleStep / (double )BitcoinUnits::factor (currentUnit));
208
-
209
- if (valid)
210
- {
211
- // If value was valid, re-place it in the widget with the new unit
212
- setValue (currentValue);
213
- }
214
- else
215
- {
216
- // If current value is invalid, just clear field
217
- setText (" " );
218
- }
219
- setValid (true );
280
+ amount->setDisplayUnit (newUnit);
220
281
}
221
282
222
283
void BitcoinAmountField::setDisplayUnit (int newUnit)
@@ -226,6 +287,5 @@ void BitcoinAmountField::setDisplayUnit(int newUnit)
226
287
227
288
void BitcoinAmountField::setSingleStep (qint64 step)
228
289
{
229
- nSingleStep = step;
230
- unitChanged (unit->currentIndex ());
290
+ amount->setSingleStep (step);
231
291
}
0 commit comments