File: DSpinBox.cpp

package info (click to toggle)
bornagain 23.0-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 103,936 kB
  • sloc: cpp: 423,131; python: 40,997; javascript: 11,167; awk: 630; sh: 318; ruby: 173; xml: 130; makefile: 51; ansic: 24
file content (139 lines) | stat: -rw-r--r-- 4,299 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      GUI/View/Numeric/DSpinBox.cpp
//! @brief     Implements class DSpinBox.
//!
//! @homepage  http://www.bornagainproject.org
//! @license   GNU General Public License v3 or higher (see COPYING)
//! @copyright Forschungszentrum Jülich GmbH 2021
//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
//
//  ************************************************************************************************

#include "GUI/View/Numeric/DSpinBox.h"
#include "Base/Math/Numeric.h"
#include "Base/Util/Assert.h"
#include "Base/Util/StringUtil.h"
#include "GUI/Model/Descriptor/DoubleProperty.h"
#include "GUI/Model/Project/ProjectDocument.h"
#include <QLineEdit>
#include <QRegularExpression>
#include <QWheelEvent>
#include <iostream>

namespace {

static constexpr double step0 = 0.1;

QString toString(double val, int decimal_points)
{
    if (val == 0)
        return "0";

    QString result = (std::abs(val) >= 10000 || std::abs(val) < 0.1)
                         ? QString::number(val, 'e', decimal_points)
                         : QString::number(val, 'f', decimal_points);

    // suppress ".0" in mantissa; normalize exponent
    return result.replace(QRegularExpression("(\\.?0+)?((e)([\\+]?)([-]?)(0*)([1-9].*))?$"),
                          "\\3\\5\\7");
}

} // namespace


DSpinBox::DSpinBox(DoubleProperty* d)
{
    replaceProperty(d);

    connect(this, &QAbstractSpinBox::editingFinished, [this] {
        ASSERT(m_property);
        setValue(fromDisplay());
    });
}

void DSpinBox::replaceProperty(DoubleProperty* d)
{
    if (m_property)
        disconnect(m_property, &DoubleProperty::setAndNotifyCalled, this, &DSpinBox::updateValue);

    m_property = d;
    if (m_property) {
        setFocusPolicy(Qt::StrongFocus);
        setToolTip(d->tooltip());
        setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
        lineEdit()->setText(toString(m_property->dVal(), m_property->decimals()));
        connect(d, &DoubleProperty::setAndNotifyCalled, this, &DSpinBox::updateValue,
                Qt::UniqueConnection);
    }
    setReadOnly(!m_property);
    updateValue();
}

void DSpinBox::updateValue()
{
    if (m_property)
        lineEdit()->setText(toString(m_property->dVal(), m_property->decimals()));
    else
        lineEdit()->setText("");
}

QAbstractSpinBox::StepEnabled DSpinBox::stepEnabled() const
{
    return StepUpEnabled | StepDownEnabled;
}

double DSpinBox::fromDisplay() const
{
    double result;
    if (Base::String::to_double(lineEdit()->text().toStdString(), &result))
        return result;
    // invalid input => restore last valid value
    return m_property->dVal();
}

void DSpinBox::setValue(double val)
{
    ASSERT(m_property);
    const double oldval = m_property->dVal();
    val = m_property->limits().clamp(val);
    lineEdit()->setText(toString(val, m_property->decimals()));
    m_property->setDVal(fromDisplay());
    if (m_property->dVal() != oldval) {
        emit valueChanged(m_property->dVal());
        gDoc->setModified();
    }
}

void DSpinBox::wheelEvent(QWheelEvent* event)
{
    if (hasFocus())
        QAbstractSpinBox::wheelEvent(event);
    else
        event->ignore();
}

void DSpinBox::stepBy(int steps)
{
    ASSERT(m_property);
    const double val = m_property->dVal();

    if (m_property->useFixedStep()) {
        setValue(val + m_property->step() * steps);
        return;
    }

    // "step digit" is the orger of magnitude of the step.
    // If it is "1", then we increment/decrement the first digit of the value.
    // If it is "2", then we increment/decrement the second digit of the value and so on.
    const int step_digit = 2;
    const int order_of_mag = Numeric::orderOfMagnitude(val);
    // special cases: if the current value is 100 and step digit is 2, then the diminished value
    // should be not 90, but 99.
    const int correction = (val == std::pow(10, order_of_mag) && steps < 0) ? 1 : 0;
    const double adaptive_step = std::pow(10, order_of_mag - step_digit - correction + 1);
    const double step = (val == 0) ? ::step0 : adaptive_step;
    setValue(val + step * steps);
}