1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2020 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtWidgets module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include <private/qabstractspinbox_p.h> |
41 | #include <qspinbox.h> |
42 | |
43 | #include <qlineedit.h> |
44 | #include <qlocale.h> |
45 | #include <qvalidator.h> |
46 | #include <qdebug.h> |
47 | |
48 | #include <algorithm> |
49 | #include <cmath> |
50 | #include <float.h> |
51 | |
52 | QT_BEGIN_NAMESPACE |
53 | |
54 | //#define QSPINBOX_QSBDEBUG |
55 | #ifdef QSPINBOX_QSBDEBUG |
56 | # define QSBDEBUG qDebug |
57 | #else |
58 | # define QSBDEBUG if (false) qDebug |
59 | #endif |
60 | |
61 | class QSpinBoxPrivate : public QAbstractSpinBoxPrivate |
62 | { |
63 | Q_DECLARE_PUBLIC(QSpinBox) |
64 | public: |
65 | QSpinBoxPrivate(); |
66 | void emitSignals(EmitPolicy ep, const QVariant &) override; |
67 | |
68 | virtual QVariant valueFromText(const QString &n) const override; |
69 | virtual QString textFromValue(const QVariant &n) const override; |
70 | QVariant validateAndInterpret(QString &input, int &pos, |
71 | QValidator::State &state) const; |
72 | |
73 | inline void init() { |
74 | Q_Q(QSpinBox); |
75 | q->setInputMethodHints(Qt::ImhDigitsOnly); |
76 | setLayoutItemMargins(QStyle::SE_SpinBoxLayoutItem); |
77 | } |
78 | |
79 | int displayIntegerBase; |
80 | |
81 | QVariant calculateAdaptiveDecimalStep(int steps) const override; |
82 | }; |
83 | |
84 | class QDoubleSpinBoxPrivate : public QAbstractSpinBoxPrivate |
85 | { |
86 | Q_DECLARE_PUBLIC(QDoubleSpinBox) |
87 | public: |
88 | QDoubleSpinBoxPrivate(); |
89 | void emitSignals(EmitPolicy ep, const QVariant &) override; |
90 | |
91 | virtual QVariant valueFromText(const QString &n) const override; |
92 | virtual QString textFromValue(const QVariant &n) const override; |
93 | QVariant validateAndInterpret(QString &input, int &pos, |
94 | QValidator::State &state) const; |
95 | double round(double input) const; |
96 | // variables |
97 | int decimals; |
98 | |
99 | inline void init() { |
100 | Q_Q(QDoubleSpinBox); |
101 | q->setInputMethodHints(Qt::ImhFormattedNumbersOnly); |
102 | } |
103 | |
104 | // When fiddling with the decimals property, we may lose precision in these properties. |
105 | double actualMin; |
106 | double actualMax; |
107 | |
108 | QVariant calculateAdaptiveDecimalStep(int steps) const override; |
109 | }; |
110 | |
111 | |
112 | /*! |
113 | \class QSpinBox |
114 | \brief The QSpinBox class provides a spin box widget. |
115 | |
116 | \ingroup basicwidgets |
117 | \inmodule QtWidgets |
118 | |
119 | \image windows-spinbox.png |
120 | |
121 | QSpinBox is designed to handle integers and discrete sets of |
122 | values (e.g., month names); use QDoubleSpinBox for floating point |
123 | values. |
124 | |
125 | QSpinBox allows the user to choose a value by clicking the up/down |
126 | buttons or pressing up/down on the keyboard to increase/decrease |
127 | the value currently displayed. The user can also type the value in |
128 | manually. The spin box supports integer values but can be extended to |
129 | use different strings with validate(), textFromValue() and valueFromText(). |
130 | |
131 | Every time the value changes QSpinBox emits valueChanged() and |
132 | textChanged() signals, the former providing a int and the latter |
133 | a QString. The textChanged() signal provides the value with both |
134 | prefix() and suffix(). |
135 | The current value can be fetched with value() and set with setValue(). |
136 | |
137 | Clicking the up/down buttons or using the keyboard accelerator's |
138 | up and down arrows will increase or decrease the current value in |
139 | steps of size singleStep(). If you want to change this behaviour you |
140 | can reimplement the virtual function stepBy(). The minimum and |
141 | maximum value and the step size can be set using one of the |
142 | constructors, and can be changed later with setMinimum(), |
143 | setMaximum() and setSingleStep(). |
144 | |
145 | Most spin boxes are directional, but QSpinBox can also operate as |
146 | a circular spin box, i.e. if the range is 0-99 and the current |
147 | value is 99, clicking "up" will give 0 if wrapping() is set to |
148 | true. Use setWrapping() if you want circular behavior. |
149 | |
150 | The displayed value can be prepended and appended with arbitrary |
151 | strings indicating, for example, currency or the unit of |
152 | measurement. See setPrefix() and setSuffix(). The text in the spin |
153 | box is retrieved with text() (which includes any prefix() and |
154 | suffix()), or with cleanText() (which has no prefix(), no suffix() |
155 | and no leading or trailing whitespace). |
156 | |
157 | It is often desirable to give the user a special (often default) |
158 | choice in addition to the range of numeric values. See |
159 | setSpecialValueText() for how to do this with QSpinBox. |
160 | |
161 | \section1 Subclassing QSpinBox |
162 | |
163 | If using prefix(), suffix(), and specialValueText() don't provide |
164 | enough control, you subclass QSpinBox and reimplement |
165 | valueFromText() and textFromValue(). For example, here's the code |
166 | for a custom spin box that allows the user to enter icon sizes |
167 | (e.g., "32 x 32"): |
168 | |
169 | \snippet widgets/icons/iconsizespinbox.cpp 1 |
170 | \codeline |
171 | \snippet widgets/icons/iconsizespinbox.cpp 2 |
172 | |
173 | See the \l{widgets/icons}{Icons} example for the full source |
174 | code. |
175 | |
176 | \sa QDoubleSpinBox, QDateTimeEdit, QSlider, {Spin Boxes Example} |
177 | */ |
178 | |
179 | /*! |
180 | \fn void QSpinBox::valueChanged(int i) |
181 | |
182 | This signal is emitted whenever the spin box's value is changed. |
183 | The new value's integer value is passed in \a i. |
184 | */ |
185 | |
186 | /*! |
187 | \fn void QSpinBox::textChanged(const QString &text) |
188 | \since 5.14 |
189 | |
190 | This signal is emitted whenever the spin box's text is changed. |
191 | The new text is passed in \a text with prefix() and suffix(). |
192 | */ |
193 | |
194 | /*! |
195 | Constructs a spin box with 0 as minimum value and 99 as maximum value, a |
196 | step value of 1. The value is initially set to 0. It is parented to \a |
197 | parent. |
198 | |
199 | \sa setMinimum(), setMaximum(), setSingleStep() |
200 | */ |
201 | |
202 | QSpinBox::QSpinBox(QWidget *parent) |
203 | : QAbstractSpinBox(*new QSpinBoxPrivate, parent) |
204 | { |
205 | Q_D(QSpinBox); |
206 | d->init(); |
207 | } |
208 | |
209 | /*! |
210 | Destructor. |
211 | */ |
212 | QSpinBox::~QSpinBox() {} |
213 | |
214 | /*! |
215 | \property QSpinBox::value |
216 | \brief the value of the spin box |
217 | |
218 | setValue() will emit valueChanged() if the new value is different |
219 | from the old one. The value property has a second notifier |
220 | signal which includes the spin box's prefix and suffix. |
221 | */ |
222 | |
223 | int QSpinBox::value() const |
224 | { |
225 | Q_D(const QSpinBox); |
226 | return d->value.toInt(); |
227 | } |
228 | |
229 | void QSpinBox::setValue(int value) |
230 | { |
231 | Q_D(QSpinBox); |
232 | d->setValue(QVariant(value), EmitIfChanged); |
233 | } |
234 | |
235 | /*! |
236 | \property QSpinBox::prefix |
237 | \brief the spin box's prefix |
238 | |
239 | The prefix is prepended to the start of the displayed value. |
240 | Typical use is to display a unit of measurement or a currency |
241 | symbol. For example: |
242 | |
243 | \snippet code/src_gui_widgets_qspinbox.cpp 0 |
244 | |
245 | To turn off the prefix display, set this property to an empty |
246 | string. The default is no prefix. The prefix is not displayed when |
247 | value() == minimum() and specialValueText() is set. |
248 | |
249 | If no prefix is set, prefix() returns an empty string. |
250 | |
251 | \sa suffix(), setSuffix(), specialValueText(), setSpecialValueText() |
252 | */ |
253 | |
254 | QString QSpinBox::prefix() const |
255 | { |
256 | Q_D(const QSpinBox); |
257 | return d->prefix; |
258 | } |
259 | |
260 | void QSpinBox::setPrefix(const QString &prefix) |
261 | { |
262 | Q_D(QSpinBox); |
263 | |
264 | d->prefix = prefix; |
265 | d->updateEdit(); |
266 | |
267 | d->cachedSizeHint = QSize(); |
268 | d->cachedMinimumSizeHint = QSize(); // minimumSizeHint cares about the prefix |
269 | updateGeometry(); |
270 | } |
271 | |
272 | /*! |
273 | \property QSpinBox::suffix |
274 | \brief the suffix of the spin box |
275 | |
276 | The suffix is appended to the end of the displayed value. Typical |
277 | use is to display a unit of measurement or a currency symbol. For |
278 | example: |
279 | |
280 | \snippet code/src_gui_widgets_qspinbox.cpp 1 |
281 | |
282 | To turn off the suffix display, set this property to an empty |
283 | string. The default is no suffix. The suffix is not displayed for |
284 | the minimum() if specialValueText() is set. |
285 | |
286 | If no suffix is set, suffix() returns an empty string. |
287 | |
288 | \sa prefix(), setPrefix(), specialValueText(), setSpecialValueText() |
289 | */ |
290 | |
291 | QString QSpinBox::suffix() const |
292 | { |
293 | Q_D(const QSpinBox); |
294 | |
295 | return d->suffix; |
296 | } |
297 | |
298 | void QSpinBox::setSuffix(const QString &suffix) |
299 | { |
300 | Q_D(QSpinBox); |
301 | |
302 | d->suffix = suffix; |
303 | d->updateEdit(); |
304 | |
305 | d->cachedSizeHint = QSize(); |
306 | updateGeometry(); |
307 | } |
308 | |
309 | /*! |
310 | \property QSpinBox::cleanText |
311 | |
312 | \brief the text of the spin box excluding any prefix, suffix, |
313 | or leading or trailing whitespace. |
314 | |
315 | \sa text, QSpinBox::prefix, QSpinBox::suffix |
316 | */ |
317 | |
318 | QString QSpinBox::cleanText() const |
319 | { |
320 | Q_D(const QSpinBox); |
321 | |
322 | return d->stripped(d->edit->displayText()); |
323 | } |
324 | |
325 | |
326 | /*! |
327 | \property QSpinBox::singleStep |
328 | \brief the step value |
329 | |
330 | When the user uses the arrows to change the spin box's value the |
331 | value will be incremented/decremented by the amount of the |
332 | singleStep. The default value is 1. Setting a singleStep value of |
333 | less than 0 does nothing. |
334 | */ |
335 | |
336 | int QSpinBox::singleStep() const |
337 | { |
338 | Q_D(const QSpinBox); |
339 | |
340 | return d->singleStep.toInt(); |
341 | } |
342 | |
343 | void QSpinBox::setSingleStep(int value) |
344 | { |
345 | Q_D(QSpinBox); |
346 | if (value >= 0) { |
347 | d->singleStep = QVariant(value); |
348 | d->updateEdit(); |
349 | } |
350 | } |
351 | |
352 | /*! |
353 | \property QSpinBox::minimum |
354 | |
355 | \brief the minimum value of the spin box |
356 | |
357 | When setting this property the \l maximum is adjusted |
358 | if necessary to ensure that the range remains valid. |
359 | |
360 | The default minimum value is 0. |
361 | |
362 | \sa setRange(), specialValueText |
363 | */ |
364 | |
365 | int QSpinBox::minimum() const |
366 | { |
367 | Q_D(const QSpinBox); |
368 | |
369 | return d->minimum.toInt(); |
370 | } |
371 | |
372 | void QSpinBox::setMinimum(int minimum) |
373 | { |
374 | Q_D(QSpinBox); |
375 | const QVariant m(minimum); |
376 | d->setRange(m, (QSpinBoxPrivate::variantCompare(d->maximum, m) > 0 ? d->maximum : m)); |
377 | } |
378 | |
379 | /*! |
380 | \property QSpinBox::maximum |
381 | |
382 | \brief the maximum value of the spin box |
383 | |
384 | When setting this property the minimum is adjusted |
385 | if necessary, to ensure that the range remains valid. |
386 | |
387 | The default maximum value is 99. |
388 | |
389 | \sa setRange(), specialValueText |
390 | |
391 | */ |
392 | |
393 | int QSpinBox::maximum() const |
394 | { |
395 | Q_D(const QSpinBox); |
396 | |
397 | return d->maximum.toInt(); |
398 | } |
399 | |
400 | void QSpinBox::setMaximum(int maximum) |
401 | { |
402 | Q_D(QSpinBox); |
403 | const QVariant m(maximum); |
404 | d->setRange((QSpinBoxPrivate::variantCompare(d->minimum, m) < 0 ? d->minimum : m), m); |
405 | } |
406 | |
407 | /*! |
408 | Convenience function to set the \a minimum, and \a maximum values |
409 | with a single function call. |
410 | |
411 | \snippet code/src_gui_widgets_qspinbox.cpp 2 |
412 | is equivalent to: |
413 | \snippet code/src_gui_widgets_qspinbox.cpp 3 |
414 | |
415 | \sa minimum, maximum |
416 | */ |
417 | |
418 | void QSpinBox::setRange(int minimum, int maximum) |
419 | { |
420 | Q_D(QSpinBox); |
421 | d->setRange(QVariant(minimum), QVariant(maximum)); |
422 | } |
423 | |
424 | /*! |
425 | Sets the step type for the spin box to \a stepType, which is single |
426 | step or adaptive decimal step. |
427 | |
428 | Adaptive decimal step means that the step size will continuously be |
429 | adjusted to one power of ten below the current \l value. So when |
430 | the value is 1100, the step is set to 100, so stepping up once |
431 | increases it to 1200. For 1200 stepping up takes it to 1300. For |
432 | negative values, stepping down from -1100 goes to -1200. |
433 | |
434 | Step direction is taken into account to handle edges cases, so |
435 | that stepping down from 100 takes the value to 99 instead of 90. |
436 | Thus a step up followed by a step down -- or vice versa -- always |
437 | lands on the starting value; 99 -> 100 -> 99. |
438 | |
439 | Setting this will cause the spin box to disregard the value of |
440 | \l singleStep, although it is preserved so that \l singleStep |
441 | comes into effect if adaptive decimal step is later turned off. |
442 | |
443 | \since 5.12 |
444 | */ |
445 | |
446 | void QSpinBox::setStepType(QAbstractSpinBox::StepType stepType) |
447 | { |
448 | Q_D(QSpinBox); |
449 | d->stepType = stepType; |
450 | } |
451 | |
452 | /*! |
453 | \property QSpinBox::stepType |
454 | \brief The step type. |
455 | |
456 | The step type can be single step or adaptive decimal step. |
457 | */ |
458 | |
459 | QAbstractSpinBox::StepType QSpinBox::stepType() const |
460 | { |
461 | Q_D(const QSpinBox); |
462 | return d->stepType; |
463 | } |
464 | |
465 | /*! |
466 | \property QSpinBox::displayIntegerBase |
467 | |
468 | \brief the base used to display the value of the spin box |
469 | |
470 | The default displayIntegerBase value is 10. |
471 | |
472 | \sa textFromValue(), valueFromText() |
473 | \since 5.2 |
474 | */ |
475 | |
476 | int QSpinBox::displayIntegerBase() const |
477 | { |
478 | Q_D(const QSpinBox); |
479 | return d->displayIntegerBase; |
480 | } |
481 | |
482 | void QSpinBox::setDisplayIntegerBase(int base) |
483 | { |
484 | Q_D(QSpinBox); |
485 | // Falls back to base 10 on invalid bases (like QString) |
486 | if (Q_UNLIKELY(base < 2 || base > 36)) { |
487 | qWarning("QSpinBox::setDisplayIntegerBase: Invalid base (%d)" , base); |
488 | base = 10; |
489 | } |
490 | |
491 | if (base != d->displayIntegerBase) { |
492 | d->displayIntegerBase = base; |
493 | d->updateEdit(); |
494 | } |
495 | } |
496 | |
497 | /*! |
498 | This virtual function is used by the spin box whenever it needs to |
499 | display the given \a value. The default implementation returns a |
500 | string containing \a value printed in the standard way using |
501 | QWidget::locale().toString(), but with the thousand separator |
502 | removed unless setGroupSeparatorShown() is set. Reimplementations may |
503 | return anything. (See the example in the detailed description.) |
504 | |
505 | Note: QSpinBox does not call this function for specialValueText() |
506 | and that neither prefix() nor suffix() should be included in the |
507 | return value. |
508 | |
509 | If you reimplement this, you may also need to reimplement |
510 | valueFromText() and validate() |
511 | |
512 | \sa valueFromText(), validate(), QLocale::groupSeparator() |
513 | */ |
514 | |
515 | QString QSpinBox::textFromValue(int value) const |
516 | { |
517 | Q_D(const QSpinBox); |
518 | QString str; |
519 | |
520 | if (d->displayIntegerBase != 10) { |
521 | const QLatin1String prefix = value < 0 ? QLatin1String("-" ) : QLatin1String(); |
522 | str = prefix + QString::number(qAbs(value), d->displayIntegerBase); |
523 | } else { |
524 | str = locale().toString(value); |
525 | if (!d->showGroupSeparator && (qAbs(value) >= 1000 || value == INT_MIN)) { |
526 | str.remove(locale().groupSeparator()); |
527 | } |
528 | } |
529 | |
530 | return str; |
531 | } |
532 | |
533 | /*! |
534 | \fn int QSpinBox::valueFromText(const QString &text) const |
535 | |
536 | This virtual function is used by the spin box whenever it needs to |
537 | interpret \a text entered by the user as a value. |
538 | |
539 | Subclasses that need to display spin box values in a non-numeric |
540 | way need to reimplement this function. |
541 | |
542 | Note: QSpinBox handles specialValueText() separately; this |
543 | function is only concerned with the other values. |
544 | |
545 | \sa textFromValue(), validate() |
546 | */ |
547 | |
548 | int QSpinBox::valueFromText(const QString &text) const |
549 | { |
550 | Q_D(const QSpinBox); |
551 | |
552 | QString copy = text; |
553 | int pos = d->edit->cursorPosition(); |
554 | QValidator::State state = QValidator::Acceptable; |
555 | return d->validateAndInterpret(copy, pos, state).toInt(); |
556 | } |
557 | |
558 | /*! |
559 | \reimp |
560 | */ |
561 | QValidator::State QSpinBox::validate(QString &text, int &pos) const |
562 | { |
563 | Q_D(const QSpinBox); |
564 | |
565 | QValidator::State state; |
566 | d->validateAndInterpret(text, pos, state); |
567 | return state; |
568 | } |
569 | |
570 | |
571 | /*! |
572 | \reimp |
573 | */ |
574 | void QSpinBox::fixup(QString &input) const |
575 | { |
576 | if (!isGroupSeparatorShown()) |
577 | input.remove(locale().groupSeparator()); |
578 | } |
579 | |
580 | |
581 | // --- QDoubleSpinBox --- |
582 | |
583 | /*! |
584 | \class QDoubleSpinBox |
585 | \brief The QDoubleSpinBox class provides a spin box widget that |
586 | takes doubles. |
587 | |
588 | \ingroup basicwidgets |
589 | \inmodule QtWidgets |
590 | |
591 | QDoubleSpinBox allows the user to choose a value by clicking the |
592 | up and down buttons or by pressing Up or Down on the keyboard to |
593 | increase or decrease the value currently displayed. The user can |
594 | also type the value in manually. The spin box supports double |
595 | values but can be extended to use different strings with |
596 | validate(), textFromValue() and valueFromText(). |
597 | |
598 | Every time the value changes QDoubleSpinBox emits valueChanged() and |
599 | textChanged() signals, the former providing a double and the latter |
600 | a QString. The textChanged() signal provides the value with both |
601 | prefix() and suffix(). The current value can be fetched with |
602 | value() and set with setValue(). |
603 | |
604 | Note: QDoubleSpinBox will round numbers so they can be displayed |
605 | with the current precision. In a QDoubleSpinBox with decimals set |
606 | to 2, calling setValue(2.555) will cause value() to return 2.56. |
607 | |
608 | Clicking the up and down buttons or using the keyboard accelerator's |
609 | Up and Down arrows will increase or decrease the current value in |
610 | steps of size singleStep(). If you want to change this behavior you |
611 | can reimplement the virtual function stepBy(). The minimum and |
612 | maximum value and the step size can be set using one of the |
613 | constructors, and can be changed later with setMinimum(), |
614 | setMaximum() and setSingleStep(). The spinbox has a default |
615 | precision of 2 decimal places but this can be changed using |
616 | setDecimals(). |
617 | |
618 | Most spin boxes are directional, but QDoubleSpinBox can also |
619 | operate as a circular spin box, i.e. if the range is 0.0-99.9 and |
620 | the current value is 99.9, clicking "up" will give 0 if wrapping() |
621 | is set to true. Use setWrapping() if you want circular behavior. |
622 | |
623 | The displayed value can be prepended and appended with arbitrary |
624 | strings indicating, for example, currency or the unit of |
625 | measurement. See setPrefix() and setSuffix(). The text in the spin |
626 | box is retrieved with text() (which includes any prefix() and |
627 | suffix()), or with cleanText() (which has no prefix(), no suffix() |
628 | and no leading or trailing whitespace). |
629 | |
630 | It is often desirable to give the user a special (often default) |
631 | choice in addition to the range of numeric values. See |
632 | setSpecialValueText() for how to do this with QDoubleSpinBox. |
633 | |
634 | \note The displayed value of the QDoubleSpinBox is limited to 18 characters |
635 | in addition to eventual prefix and suffix content. This limitation is used |
636 | to keep the double spin box usable even with extremely large values. |
637 | \sa QSpinBox, QDateTimeEdit, QSlider, {Spin Boxes Example} |
638 | */ |
639 | |
640 | /*! |
641 | \fn void QDoubleSpinBox::valueChanged(double d); |
642 | |
643 | This signal is emitted whenever the spin box's value is changed. |
644 | The new value is passed in \a d. |
645 | */ |
646 | |
647 | /*! |
648 | \fn void QDoubleSpinBox::textChanged(const QString &text) |
649 | \since 5.14 |
650 | |
651 | This signal is emitted whenever the spin box's text is changed. |
652 | The new text is passed in \a text with prefix() and suffix(). |
653 | */ |
654 | |
655 | /*! |
656 | Constructs a spin box with 0.0 as minimum value and 99.99 as maximum value, |
657 | a step value of 1.0 and a precision of 2 decimal places. The value is |
658 | initially set to 0.00. The spin box has the given \a parent. |
659 | |
660 | \sa setMinimum(), setMaximum(), setSingleStep() |
661 | */ |
662 | QDoubleSpinBox::QDoubleSpinBox(QWidget *parent) |
663 | : QAbstractSpinBox(*new QDoubleSpinBoxPrivate, parent) |
664 | { |
665 | Q_D(QDoubleSpinBox); |
666 | d->init(); |
667 | } |
668 | |
669 | /*! |
670 | Destructor. |
671 | */ |
672 | QDoubleSpinBox::~QDoubleSpinBox() {} |
673 | |
674 | /*! |
675 | \property QDoubleSpinBox::value |
676 | \brief the value of the spin box |
677 | |
678 | setValue() will emit valueChanged() if the new value is different |
679 | from the old one. The value property has a second notifier |
680 | signal which includes the spin box's prefix and suffix. |
681 | |
682 | Note: The value will be rounded so it can be displayed with the |
683 | current setting of decimals. |
684 | |
685 | \sa decimals |
686 | */ |
687 | double QDoubleSpinBox::value() const |
688 | { |
689 | Q_D(const QDoubleSpinBox); |
690 | |
691 | return d->value.toDouble(); |
692 | } |
693 | |
694 | void QDoubleSpinBox::setValue(double value) |
695 | { |
696 | Q_D(QDoubleSpinBox); |
697 | QVariant v(d->round(value)); |
698 | d->setValue(v, EmitIfChanged); |
699 | } |
700 | /*! |
701 | \property QDoubleSpinBox::prefix |
702 | \brief the spin box's prefix |
703 | |
704 | The prefix is prepended to the start of the displayed value. |
705 | Typical use is to display a unit of measurement or a currency |
706 | symbol. For example: |
707 | |
708 | \snippet code/src_gui_widgets_qspinbox.cpp 4 |
709 | |
710 | To turn off the prefix display, set this property to an empty |
711 | string. The default is no prefix. The prefix is not displayed when |
712 | value() == minimum() and specialValueText() is set. |
713 | |
714 | If no prefix is set, prefix() returns an empty string. |
715 | |
716 | \sa suffix(), setSuffix(), specialValueText(), setSpecialValueText() |
717 | */ |
718 | |
719 | QString QDoubleSpinBox::prefix() const |
720 | { |
721 | Q_D(const QDoubleSpinBox); |
722 | |
723 | return d->prefix; |
724 | } |
725 | |
726 | void QDoubleSpinBox::setPrefix(const QString &prefix) |
727 | { |
728 | Q_D(QDoubleSpinBox); |
729 | |
730 | d->prefix = prefix; |
731 | d->updateEdit(); |
732 | |
733 | d->cachedSizeHint = QSize(); |
734 | d->cachedMinimumSizeHint = QSize(); // minimumSizeHint cares about the prefix |
735 | updateGeometry(); |
736 | } |
737 | |
738 | /*! |
739 | \property QDoubleSpinBox::suffix |
740 | \brief the suffix of the spin box |
741 | |
742 | The suffix is appended to the end of the displayed value. Typical |
743 | use is to display a unit of measurement or a currency symbol. For |
744 | example: |
745 | |
746 | \snippet code/src_gui_widgets_qspinbox.cpp 5 |
747 | |
748 | To turn off the suffix display, set this property to an empty |
749 | string. The default is no suffix. The suffix is not displayed for |
750 | the minimum() if specialValueText() is set. |
751 | |
752 | If no suffix is set, suffix() returns an empty string. |
753 | |
754 | \sa prefix(), setPrefix(), specialValueText(), setSpecialValueText() |
755 | */ |
756 | |
757 | QString QDoubleSpinBox::suffix() const |
758 | { |
759 | Q_D(const QDoubleSpinBox); |
760 | |
761 | return d->suffix; |
762 | } |
763 | |
764 | void QDoubleSpinBox::setSuffix(const QString &suffix) |
765 | { |
766 | Q_D(QDoubleSpinBox); |
767 | |
768 | d->suffix = suffix; |
769 | d->updateEdit(); |
770 | |
771 | d->cachedSizeHint = QSize(); |
772 | updateGeometry(); |
773 | } |
774 | |
775 | /*! |
776 | \property QDoubleSpinBox::cleanText |
777 | |
778 | \brief the text of the spin box excluding any prefix, suffix, |
779 | or leading or trailing whitespace. |
780 | |
781 | \sa text, QDoubleSpinBox::prefix, QDoubleSpinBox::suffix |
782 | */ |
783 | |
784 | QString QDoubleSpinBox::cleanText() const |
785 | { |
786 | Q_D(const QDoubleSpinBox); |
787 | |
788 | return d->stripped(d->edit->displayText()); |
789 | } |
790 | |
791 | /*! |
792 | \property QDoubleSpinBox::singleStep |
793 | \brief the step value |
794 | |
795 | When the user uses the arrows to change the spin box's value the |
796 | value will be incremented/decremented by the amount of the |
797 | singleStep. The default value is 1.0. Setting a singleStep value |
798 | of less than 0 does nothing. |
799 | */ |
800 | double QDoubleSpinBox::singleStep() const |
801 | { |
802 | Q_D(const QDoubleSpinBox); |
803 | |
804 | return d->singleStep.toDouble(); |
805 | } |
806 | |
807 | void QDoubleSpinBox::setSingleStep(double value) |
808 | { |
809 | Q_D(QDoubleSpinBox); |
810 | |
811 | if (value >= 0) { |
812 | d->singleStep = value; |
813 | d->updateEdit(); |
814 | } |
815 | } |
816 | |
817 | /*! |
818 | \property QDoubleSpinBox::minimum |
819 | |
820 | \brief the minimum value of the spin box |
821 | |
822 | When setting this property the \l maximum is adjusted |
823 | if necessary to ensure that the range remains valid. |
824 | |
825 | The default minimum value is 0.0. |
826 | |
827 | Note: The minimum value will be rounded to match the decimals |
828 | property. |
829 | |
830 | \sa decimals, setRange(), specialValueText |
831 | */ |
832 | |
833 | double QDoubleSpinBox::minimum() const |
834 | { |
835 | Q_D(const QDoubleSpinBox); |
836 | |
837 | return d->minimum.toDouble(); |
838 | } |
839 | |
840 | void QDoubleSpinBox::setMinimum(double minimum) |
841 | { |
842 | Q_D(QDoubleSpinBox); |
843 | d->actualMin = minimum; |
844 | const QVariant m(d->round(minimum)); |
845 | d->setRange(m, (QDoubleSpinBoxPrivate::variantCompare(d->maximum, m) > 0 ? d->maximum : m)); |
846 | } |
847 | |
848 | /*! |
849 | \property QDoubleSpinBox::maximum |
850 | |
851 | \brief the maximum value of the spin box |
852 | |
853 | When setting this property the \l minimum is adjusted |
854 | if necessary, to ensure that the range remains valid. |
855 | |
856 | The default maximum value is 99.99. |
857 | |
858 | Note: The maximum value will be rounded to match the decimals |
859 | property. |
860 | |
861 | \sa decimals, setRange() |
862 | */ |
863 | |
864 | double QDoubleSpinBox::maximum() const |
865 | { |
866 | Q_D(const QDoubleSpinBox); |
867 | |
868 | return d->maximum.toDouble(); |
869 | } |
870 | |
871 | void QDoubleSpinBox::setMaximum(double maximum) |
872 | { |
873 | Q_D(QDoubleSpinBox); |
874 | d->actualMax = maximum; |
875 | const QVariant m(d->round(maximum)); |
876 | d->setRange((QDoubleSpinBoxPrivate::variantCompare(d->minimum, m) < 0 ? d->minimum : m), m); |
877 | } |
878 | |
879 | /*! |
880 | Convenience function to set the \a minimum and \a maximum values |
881 | with a single function call. |
882 | |
883 | Note: The maximum and minimum values will be rounded to match the |
884 | decimals property. |
885 | |
886 | \snippet code/src_gui_widgets_qspinbox.cpp 6 |
887 | is equivalent to: |
888 | \snippet code/src_gui_widgets_qspinbox.cpp 7 |
889 | |
890 | \sa minimum, maximum |
891 | */ |
892 | |
893 | void QDoubleSpinBox::setRange(double minimum, double maximum) |
894 | { |
895 | Q_D(QDoubleSpinBox); |
896 | d->actualMin = minimum; |
897 | d->actualMax = maximum; |
898 | d->setRange(QVariant(d->round(minimum)), QVariant(d->round(maximum))); |
899 | } |
900 | |
901 | /*! |
902 | Sets the step type for the spin box to \a stepType, which is single |
903 | step or adaptive decimal step. |
904 | |
905 | Adaptive decimal step means that the step size will continuously be |
906 | adjusted to one power of ten below the current \l value. So when |
907 | the value is 1100, the step is set to 100, so stepping up once |
908 | increases it to 1200. For 1200 stepping up takes it to 1300. For |
909 | negative values, stepping down from -1100 goes to -1200. |
910 | |
911 | It also works for any decimal values, 0.041 is increased to 0.042 |
912 | by stepping once. |
913 | |
914 | Step direction is taken into account to handle edges cases, so |
915 | that stepping down from 100 takes the value to 99 instead of 90. |
916 | Thus a step up followed by a step down -- or vice versa -- always |
917 | lands on the starting value; 99 -> 100 -> 99. |
918 | |
919 | Setting this will cause the spin box to disregard the value of |
920 | \l singleStep, although it is preserved so that \l singleStep |
921 | comes into effect if adaptive decimal step is later turned off. |
922 | |
923 | \since 5.12 |
924 | */ |
925 | |
926 | void QDoubleSpinBox::setStepType(StepType stepType) |
927 | { |
928 | Q_D(QDoubleSpinBox); |
929 | d->stepType = stepType; |
930 | } |
931 | |
932 | /*! |
933 | \property QDoubleSpinBox::stepType |
934 | \brief The step type. |
935 | |
936 | The step type can be single step or adaptive decimal step. |
937 | */ |
938 | |
939 | QAbstractSpinBox::StepType QDoubleSpinBox::stepType() const |
940 | { |
941 | Q_D(const QDoubleSpinBox); |
942 | return d->stepType; |
943 | } |
944 | |
945 | /*! |
946 | \property QDoubleSpinBox::decimals |
947 | |
948 | \brief the precision of the spin box, in decimals |
949 | |
950 | Sets how many decimals the spinbox will use for displaying and |
951 | interpreting doubles. |
952 | |
953 | \warning The maximum value for \a decimals is DBL_MAX_10_EXP + |
954 | DBL_DIG (ie. 323) because of the limitations of the double type. |
955 | |
956 | Note: The maximum, minimum and value might change as a result of |
957 | changing this property. |
958 | */ |
959 | |
960 | int QDoubleSpinBox::decimals() const |
961 | { |
962 | Q_D(const QDoubleSpinBox); |
963 | |
964 | return d->decimals; |
965 | } |
966 | |
967 | void QDoubleSpinBox::setDecimals(int decimals) |
968 | { |
969 | Q_D(QDoubleSpinBox); |
970 | d->decimals = qBound(0, decimals, DBL_MAX_10_EXP + DBL_DIG); |
971 | |
972 | setRange(d->actualMin, d->actualMax); // make sure values are rounded |
973 | setValue(value()); |
974 | } |
975 | |
976 | /*! |
977 | This virtual function is used by the spin box whenever it needs to |
978 | display the given \a value. The default implementation returns a string |
979 | containing \a value printed using QWidget::locale().toString(\a value, |
980 | QLatin1Char('f'), decimals()) and will remove the thousand |
981 | separator unless setGroupSeparatorShown() is set. Reimplementations may |
982 | return anything. |
983 | |
984 | Note: QDoubleSpinBox does not call this function for |
985 | specialValueText() and that neither prefix() nor suffix() should |
986 | be included in the return value. |
987 | |
988 | If you reimplement this, you may also need to reimplement |
989 | valueFromText(). |
990 | |
991 | \sa valueFromText(), QLocale::groupSeparator() |
992 | */ |
993 | |
994 | |
995 | QString QDoubleSpinBox::textFromValue(double value) const |
996 | { |
997 | Q_D(const QDoubleSpinBox); |
998 | QString str = locale().toString(value, 'f', d->decimals); |
999 | if (!d->showGroupSeparator && qAbs(value) >= 1000.0) |
1000 | str.remove(locale().groupSeparator()); |
1001 | |
1002 | return str; |
1003 | } |
1004 | |
1005 | /*! |
1006 | This virtual function is used by the spin box whenever it needs to |
1007 | interpret \a text entered by the user as a value. |
1008 | |
1009 | Subclasses that need to display spin box values in a non-numeric |
1010 | way need to reimplement this function. |
1011 | |
1012 | Note: QDoubleSpinBox handles specialValueText() separately; this |
1013 | function is only concerned with the other values. |
1014 | |
1015 | \sa textFromValue(), validate() |
1016 | */ |
1017 | double QDoubleSpinBox::valueFromText(const QString &text) const |
1018 | { |
1019 | Q_D(const QDoubleSpinBox); |
1020 | |
1021 | QString copy = text; |
1022 | int pos = d->edit->cursorPosition(); |
1023 | QValidator::State state = QValidator::Acceptable; |
1024 | return d->validateAndInterpret(copy, pos, state).toDouble(); |
1025 | } |
1026 | |
1027 | /*! |
1028 | \reimp |
1029 | */ |
1030 | QValidator::State QDoubleSpinBox::validate(QString &text, int &pos) const |
1031 | { |
1032 | Q_D(const QDoubleSpinBox); |
1033 | |
1034 | QValidator::State state; |
1035 | d->validateAndInterpret(text, pos, state); |
1036 | return state; |
1037 | } |
1038 | |
1039 | |
1040 | /*! |
1041 | \reimp |
1042 | */ |
1043 | void QDoubleSpinBox::fixup(QString &input) const |
1044 | { |
1045 | input.remove(locale().groupSeparator()); |
1046 | } |
1047 | |
1048 | // --- QSpinBoxPrivate --- |
1049 | |
1050 | /*! |
1051 | \internal |
1052 | Constructs a QSpinBoxPrivate object |
1053 | */ |
1054 | |
1055 | QSpinBoxPrivate::QSpinBoxPrivate() |
1056 | { |
1057 | minimum = QVariant((int)0); |
1058 | maximum = QVariant((int)99); |
1059 | value = minimum; |
1060 | displayIntegerBase = 10; |
1061 | singleStep = QVariant((int)1); |
1062 | type = QMetaType::Int; |
1063 | } |
1064 | |
1065 | /*! |
1066 | \internal |
1067 | \reimp |
1068 | */ |
1069 | |
1070 | void QSpinBoxPrivate::emitSignals(EmitPolicy ep, const QVariant &old) |
1071 | { |
1072 | Q_Q(QSpinBox); |
1073 | if (ep != NeverEmit) { |
1074 | pendingEmit = false; |
1075 | if (ep == AlwaysEmit || value != old) { |
1076 | emit q->textChanged(edit->displayText()); |
1077 | emit q->valueChanged(value.toInt()); |
1078 | } |
1079 | } |
1080 | } |
1081 | |
1082 | /*! |
1083 | \internal |
1084 | \reimp |
1085 | */ |
1086 | |
1087 | QString QSpinBoxPrivate::textFromValue(const QVariant &value) const |
1088 | { |
1089 | Q_Q(const QSpinBox); |
1090 | return q->textFromValue(value.toInt()); |
1091 | } |
1092 | /*! |
1093 | \internal |
1094 | \reimp |
1095 | */ |
1096 | |
1097 | QVariant QSpinBoxPrivate::valueFromText(const QString &text) const |
1098 | { |
1099 | Q_Q(const QSpinBox); |
1100 | |
1101 | return QVariant(q->valueFromText(text)); |
1102 | } |
1103 | |
1104 | |
1105 | /*! |
1106 | \internal Multi purpose function that parses input, sets state to |
1107 | the appropriate state and returns the value it will be interpreted |
1108 | as. |
1109 | */ |
1110 | |
1111 | QVariant QSpinBoxPrivate::validateAndInterpret(QString &input, int &pos, |
1112 | QValidator::State &state) const |
1113 | { |
1114 | if (cachedText == input && !input.isEmpty()) { |
1115 | state = cachedState; |
1116 | QSBDEBUG() << "cachedText was '" << cachedText << "' state was " |
1117 | << state << " and value was " << cachedValue; |
1118 | |
1119 | return cachedValue; |
1120 | } |
1121 | const int max = maximum.toInt(); |
1122 | const int min = minimum.toInt(); |
1123 | |
1124 | QString copy = stripped(input, &pos); |
1125 | QSBDEBUG() << "input" << input << "copy" << copy; |
1126 | state = QValidator::Acceptable; |
1127 | int num = min; |
1128 | |
1129 | if (max != min && (copy.isEmpty() |
1130 | || (min < 0 && copy == QLatin1String("-" )) |
1131 | || (max >= 0 && copy == QLatin1String("+" )))) { |
1132 | state = QValidator::Intermediate; |
1133 | QSBDEBUG() << __FILE__ << __LINE__<< "num is set to" << num; |
1134 | } else if (copy.startsWith(QLatin1Char('-')) && min >= 0) { |
1135 | state = QValidator::Invalid; // special-case -0 will be interpreted as 0 and thus not be invalid with a range from 0-100 |
1136 | } else { |
1137 | bool ok = false; |
1138 | if (displayIntegerBase != 10) { |
1139 | num = copy.toInt(&ok, displayIntegerBase); |
1140 | } else { |
1141 | num = locale.toInt(copy, &ok); |
1142 | if (!ok && (max >= 1000 || min <= -1000)) { |
1143 | const QString sep(locale.groupSeparator()); |
1144 | const QString doubleSep = sep + sep; |
1145 | if (copy.contains(sep) && !copy.contains(doubleSep)) { |
1146 | QString copy2 = copy; |
1147 | copy2.remove(sep); |
1148 | num = locale.toInt(copy2, &ok); |
1149 | } |
1150 | } |
1151 | } |
1152 | QSBDEBUG() << __FILE__ << __LINE__<< "num is set to" << num; |
1153 | if (!ok) { |
1154 | state = QValidator::Invalid; |
1155 | } else if (num >= min && num <= max) { |
1156 | state = QValidator::Acceptable; |
1157 | } else if (max == min) { |
1158 | state = QValidator::Invalid; |
1159 | } else { |
1160 | if ((num >= 0 && num > max) || (num < 0 && num < min)) { |
1161 | state = QValidator::Invalid; |
1162 | QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid" ; |
1163 | } else { |
1164 | state = QValidator::Intermediate; |
1165 | QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Intermediate" ; |
1166 | } |
1167 | } |
1168 | } |
1169 | if (state != QValidator::Acceptable) |
1170 | num = max > 0 ? min : max; |
1171 | input = prefix + copy + suffix; |
1172 | cachedText = input; |
1173 | cachedState = state; |
1174 | cachedValue = QVariant((int)num); |
1175 | |
1176 | QSBDEBUG() << "cachedText is set to '" << cachedText << "' state is set to " |
1177 | << state << " and value is set to " << cachedValue; |
1178 | return cachedValue; |
1179 | } |
1180 | |
1181 | QVariant QSpinBoxPrivate::calculateAdaptiveDecimalStep(int steps) const |
1182 | { |
1183 | const int intValue = value.toInt(); |
1184 | const int absValue = qAbs(intValue); |
1185 | |
1186 | if (absValue < 100) |
1187 | return 1; |
1188 | |
1189 | const bool valueNegative = intValue < 0; |
1190 | const bool stepsNegative = steps < 0; |
1191 | const int signCompensation = (valueNegative == stepsNegative) ? 0 : 1; |
1192 | |
1193 | const int log = static_cast<int>(std::log10(absValue - signCompensation)) - 1; |
1194 | return static_cast<int>(std::pow(10, log)); |
1195 | } |
1196 | |
1197 | // --- QDoubleSpinBoxPrivate --- |
1198 | |
1199 | /*! |
1200 | \internal |
1201 | Constructs a QSpinBoxPrivate object |
1202 | */ |
1203 | |
1204 | QDoubleSpinBoxPrivate::QDoubleSpinBoxPrivate() |
1205 | { |
1206 | actualMin = 0.0; |
1207 | actualMax = 99.99; |
1208 | minimum = QVariant(actualMin); |
1209 | maximum = QVariant(actualMax); |
1210 | value = minimum; |
1211 | singleStep = QVariant(1.0); |
1212 | decimals = 2; |
1213 | type = QMetaType::Double; |
1214 | } |
1215 | |
1216 | /*! |
1217 | \internal |
1218 | \reimp |
1219 | */ |
1220 | |
1221 | void QDoubleSpinBoxPrivate::emitSignals(EmitPolicy ep, const QVariant &old) |
1222 | { |
1223 | Q_Q(QDoubleSpinBox); |
1224 | if (ep != NeverEmit) { |
1225 | pendingEmit = false; |
1226 | if (ep == AlwaysEmit || value != old) { |
1227 | emit q->textChanged(edit->displayText()); |
1228 | emit q->valueChanged(value.toDouble()); |
1229 | } |
1230 | } |
1231 | } |
1232 | |
1233 | |
1234 | /*! |
1235 | \internal |
1236 | \reimp |
1237 | */ |
1238 | QVariant QDoubleSpinBoxPrivate::valueFromText(const QString &f) const |
1239 | { |
1240 | Q_Q(const QDoubleSpinBox); |
1241 | return QVariant(q->valueFromText(f)); |
1242 | } |
1243 | |
1244 | /*! |
1245 | \internal |
1246 | Rounds to a double value that is restricted to decimals. |
1247 | E.g. // decimals = 2 |
1248 | |
1249 | round(5.555) => 5.56 |
1250 | */ |
1251 | |
1252 | double QDoubleSpinBoxPrivate::round(double value) const |
1253 | { |
1254 | return QString::number(value, 'f', decimals).toDouble(); |
1255 | } |
1256 | |
1257 | |
1258 | /*! |
1259 | \internal Multi purpose function that parses input, sets state to |
1260 | the appropriate state and returns the value it will be interpreted |
1261 | as. |
1262 | */ |
1263 | |
1264 | QVariant QDoubleSpinBoxPrivate::validateAndInterpret(QString &input, int &pos, |
1265 | QValidator::State &state) const |
1266 | { |
1267 | if (cachedText == input && !input.isEmpty()) { |
1268 | state = cachedState; |
1269 | QSBDEBUG() << "cachedText was '" << cachedText << "' state was " |
1270 | << state << " and value was " << cachedValue; |
1271 | return cachedValue; |
1272 | } |
1273 | const double max = maximum.toDouble(); |
1274 | const double min = minimum.toDouble(); |
1275 | |
1276 | QString copy = stripped(input, &pos); |
1277 | QSBDEBUG() << "input" << input << "copy" << copy; |
1278 | int len = copy.size(); |
1279 | double num = min; |
1280 | const bool plus = max >= 0; |
1281 | const bool minus = min <= 0; |
1282 | |
1283 | const QString group(locale.groupSeparator()); |
1284 | const uint groupUcs = (group.size() > 1 && group.at(0).isHighSurrogate() |
1285 | ? QChar::surrogateToUcs4(group.at(0), group.at(1)) |
1286 | : group.at(0).unicode()); |
1287 | switch (len) { |
1288 | case 0: |
1289 | state = max != min ? QValidator::Intermediate : QValidator::Invalid; |
1290 | goto end; |
1291 | case 1: |
1292 | if (copy.at(0) == locale.decimalPoint() |
1293 | || (plus && copy.at(0) == QLatin1Char('+')) |
1294 | || (minus && copy.at(0) == QLatin1Char('-'))) { |
1295 | state = QValidator::Intermediate; |
1296 | goto end; |
1297 | } |
1298 | break; |
1299 | case 2: |
1300 | if (copy.at(1) == locale.decimalPoint() |
1301 | && ((plus && copy.at(0) == QLatin1Char('+')) || (minus && copy.at(0) == QLatin1Char('-')))) { |
1302 | state = QValidator::Intermediate; |
1303 | goto end; |
1304 | } |
1305 | break; |
1306 | default: break; |
1307 | } |
1308 | |
1309 | if (copy.at(0) == locale.groupSeparator()) { |
1310 | QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid" ; |
1311 | state = QValidator::Invalid; |
1312 | goto end; |
1313 | } else if (len > 1) { |
1314 | const int dec = copy.indexOf(locale.decimalPoint()); |
1315 | if (dec != -1) { |
1316 | if (dec + 1 < copy.size() && copy.at(dec + 1) == locale.decimalPoint() && pos == dec + 1) { |
1317 | copy.remove(dec + 1, 1); // typing a delimiter when you are on the delimiter |
1318 | } // should be treated as typing right arrow |
1319 | |
1320 | if (copy.size() - dec > decimals + 1) { |
1321 | QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid" ; |
1322 | state = QValidator::Invalid; |
1323 | goto end; |
1324 | } |
1325 | for (int i=dec + 1; i<copy.size(); ++i) { |
1326 | if (copy.at(i).isSpace() || copy.at(i) == locale.groupSeparator()) { |
1327 | QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid" ; |
1328 | state = QValidator::Invalid; |
1329 | goto end; |
1330 | } |
1331 | } |
1332 | } else { |
1333 | const QChar last = copy.back(); |
1334 | const bool groupEnd = copy.endsWith(group); |
1335 | const QStringView head(copy.constData(), groupEnd ? len - group.size() : len - 1); |
1336 | const QChar secondLast = head.back(); |
1337 | if ((groupEnd || last.isSpace()) && (head.endsWith(group) || secondLast.isSpace())) { |
1338 | state = QValidator::Invalid; |
1339 | QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid" ; |
1340 | goto end; |
1341 | } else if (last.isSpace() && (!QChar::isSpace(groupUcs) || secondLast.isSpace())) { |
1342 | state = QValidator::Invalid; |
1343 | QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid" ; |
1344 | goto end; |
1345 | } |
1346 | } |
1347 | } |
1348 | |
1349 | { |
1350 | bool ok = false; |
1351 | num = locale.toDouble(copy, &ok); |
1352 | QSBDEBUG() << __FILE__ << __LINE__ << locale << copy << num << ok; |
1353 | |
1354 | if (!ok) { |
1355 | if (QChar::isPrint(groupUcs)) { |
1356 | if (max < 1000 && min > -1000 && copy.contains(group)) { |
1357 | state = QValidator::Invalid; |
1358 | QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid" ; |
1359 | goto end; |
1360 | } |
1361 | |
1362 | const int len = copy.size(); |
1363 | for (int i = 0; i < len - 1;) { |
1364 | if (QStringView(copy).mid(i).startsWith(group)) { |
1365 | if (QStringView(copy).mid(i + group.size()).startsWith(group)) { |
1366 | QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid" ; |
1367 | state = QValidator::Invalid; |
1368 | goto end; |
1369 | } |
1370 | i += group.size(); |
1371 | } else { |
1372 | i++; |
1373 | } |
1374 | } |
1375 | |
1376 | QString copy2 = copy; |
1377 | copy2.remove(group); |
1378 | num = locale.toDouble(copy2, &ok); |
1379 | QSBDEBUG() << group << num << copy2 << ok; |
1380 | |
1381 | if (!ok) { |
1382 | state = QValidator::Invalid; |
1383 | QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid" ; |
1384 | goto end; |
1385 | } |
1386 | } |
1387 | } |
1388 | |
1389 | if (!ok) { |
1390 | state = QValidator::Invalid; |
1391 | QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid" ; |
1392 | } else if (num >= min && num <= max) { |
1393 | state = QValidator::Acceptable; |
1394 | QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Acceptable" ; |
1395 | } else if (max == min) { // when max and min is the same the only non-Invalid input is max (or min) |
1396 | state = QValidator::Invalid; |
1397 | QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid" ; |
1398 | } else { |
1399 | if ((num >= 0 && num > max) || (num < 0 && num < min)) { |
1400 | state = QValidator::Invalid; |
1401 | QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid" ; |
1402 | } else { |
1403 | state = QValidator::Intermediate; |
1404 | QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Intermediate" ; |
1405 | } |
1406 | } |
1407 | } |
1408 | |
1409 | end: |
1410 | if (state != QValidator::Acceptable) { |
1411 | num = max > 0 ? min : max; |
1412 | } |
1413 | |
1414 | input = prefix + copy + suffix; |
1415 | cachedText = input; |
1416 | cachedState = state; |
1417 | cachedValue = QVariant(num); |
1418 | return QVariant(num); |
1419 | } |
1420 | |
1421 | /* |
1422 | \internal |
1423 | \reimp |
1424 | */ |
1425 | |
1426 | QString QDoubleSpinBoxPrivate::textFromValue(const QVariant &f) const |
1427 | { |
1428 | Q_Q(const QDoubleSpinBox); |
1429 | return q->textFromValue(f.toDouble()); |
1430 | } |
1431 | |
1432 | QVariant QDoubleSpinBoxPrivate::calculateAdaptiveDecimalStep(int steps) const |
1433 | { |
1434 | const double doubleValue = value.toDouble(); |
1435 | const double minStep = std::pow(10, -decimals); |
1436 | double absValue = qAbs(doubleValue); |
1437 | |
1438 | if (absValue < minStep) |
1439 | return minStep; |
1440 | |
1441 | const bool valueNegative = doubleValue < 0; |
1442 | const bool stepsNegative = steps < 0; |
1443 | if (valueNegative != stepsNegative) |
1444 | absValue /= 1.01; |
1445 | |
1446 | const double shift = std::pow(10, 1 - std::floor(std::log10(absValue))); |
1447 | const double absRounded = round(absValue * shift) / shift; |
1448 | const double log = floorf(std::log10(absRounded)) - 1; |
1449 | |
1450 | return std::max(minStep, std::pow(10, log)); |
1451 | } |
1452 | |
1453 | /*! \reimp */ |
1454 | bool QSpinBox::event(QEvent *event) |
1455 | { |
1456 | Q_D(QSpinBox); |
1457 | if (event->type() == QEvent::StyleChange |
1458 | #ifdef Q_OS_MAC |
1459 | || event->type() == QEvent::MacSizeChange |
1460 | #endif |
1461 | ) |
1462 | d->setLayoutItemMargins(QStyle::SE_SpinBoxLayoutItem); |
1463 | return QAbstractSpinBox::event(event); |
1464 | } |
1465 | |
1466 | QT_END_NAMESPACE |
1467 | |
1468 | #include "moc_qspinbox.cpp" |
1469 | |