Salome HOME
Issue #910 - Impossible to set start point , end points for line manually
[modules/shaper.git] / src / ModuleBase / ModuleBase_DoubleSpinBox.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
2
3 // File:      ModuleBase_DoubleSpinBox.cxx
4 // Author:    Sergey TELKOV
5 //
6 #include "ModuleBase_DoubleSpinBox.h"
7
8 #include <QLineEdit>
9 #include <QDoubleValidator>
10 #include <QVariant>
11
12 #include <limits>
13
14 const double PSEUDO_ZERO = 1.e-20;
15
16 /*!
17  \class ModuleBase_DoubleSpinBox
18  \brief Enhanced version of the Qt's double spin box.
19
20  The ModuleBase_DoubleSpinBox class represents the widget for entering the
21  floating point values. In addition to the functionality provided by
22  QDoubleSpinBox, this class supports "cleared" state - this is the
23  state corresponding to "None" (or empty) entered value.
24
25  To set "cleared" state use setCleared() method. To check if the spin
26  box stores "cleared" state, use isCleared() method.
27  For example:
28  \code
29  if (myDblSpinBox->isCleared()) {
30  ... // process "None" state
31  }
32  else {
33  double value = myDblSpinBox->value();
34  ... // process entered value
35  }
36  \endcode
37
38  Another useful feature is possibility to use scientific notation (e.g. 1.234e+18)
39  for the widget text. To enable this, negative precision should be specified either
40  through a constructor or using setPrecision() method.
41
42  Note that "decimals" property of QDoubleSpinBox is almost completely substituted
43  by "myPrecision" field of ModuleBase_DoubleSpinBox class. "decimals" is still used
44  for proper size hint calculation and for rounding minimum and maximum bounds of
45  the spin box range.
46  */
47
48 /*!
49  \brief Constructor.
50
51  Constructs a spin box with 0.0 as minimum value and 99.99 as maximum value,
52  a step value of 1.0 and a precision of 2 decimal places.
53  The value is initially set to 0.00.
54
55  \param theParent parent object
56  \param thePrecision precision of values input
57  */
58 ModuleBase_DoubleSpinBox::ModuleBase_DoubleSpinBox(QWidget* theParent, int thePrecision)
59     : QDoubleSpinBox(theParent),
60       myCleared(false)
61 {
62   // VSR 01/07/2010: Disable thousands separator for spin box
63   // (to avoid incosistency of double-2-string and string-2-double conversion)
64   QLocale loc;
65   loc.setNumberOptions(loc.numberOptions() | QLocale::OmitGroupSeparator | QLocale::RejectGroupSeparator);
66   setLocale(loc);
67
68   // MPV 15/09/2014: this must be set before setDecimals; otherwise in release mode setDecimals may crash
69   myPrecision = thePrecision;
70
71   // Use precision equal to default Qt decimals
72   // it's necessary to set decimals before the range setting,
73   // by default Qt rounds boundaries to 2 decimals at setRange
74   setDecimals(qAbs(myPrecision));
75
76   connect(lineEdit(), SIGNAL(textChanged( const QString& )), this,
77           SLOT(onTextChanged( const QString& )));
78 }
79
80 /*!
81  \brief Destructor.
82  */
83 ModuleBase_DoubleSpinBox::~ModuleBase_DoubleSpinBox()
84 {
85 }
86
87 /*!
88  \brief Check if spin box is in the "cleared" state.
89  \return \c true if spin box is cleared
90  \sa setCleared()
91  */
92 bool ModuleBase_DoubleSpinBox::isCleared() const
93 {
94   return myCleared;
95 }
96
97 /*!
98  \brief Change "cleared" status of the spin box.
99  \param on new "cleared" status
100  \sa isCleared()
101  */
102 void ModuleBase_DoubleSpinBox::setCleared(const bool on)
103 {
104   if (myCleared == on)
105     return;
106
107   myCleared = on;
108   setSpecialValueText(specialValueText());
109 }
110
111 /*!
112  \brief Set precision of the spin box
113
114  If precision value is less than 0, the 'g' format is used for value output,
115  otherwise 'f' format is used.
116
117  \param prec new precision value.
118  \sa precision()
119  */
120 void ModuleBase_DoubleSpinBox::setPrecision(const int prec)
121 {
122   int newPrec = qAbs(prec);
123   int oldPrec = qAbs(myPrecision);
124   myPrecision = prec;
125   if (newPrec != oldPrec)
126     update();
127 }
128
129 /*!
130  \brief Get precision value of the spin box
131  \return current precision value
132  \sa setPrecision()
133  */
134 int ModuleBase_DoubleSpinBox::getPrecision() const
135 {
136   return myPrecision;
137 }
138
139 /*!
140  \brief Interpret text entered by the user as a value.
141  \param text text entered by the user
142  \return mapped value
143  \sa textFromValue()
144  */
145 double ModuleBase_DoubleSpinBox::valueFromText(const QString& text) const
146 {
147   if (myPrecision < 0)
148     return text.toDouble();
149
150   return QDoubleSpinBox::valueFromText(text);
151 }
152
153 /*!
154  \brief This function is used by the spin box whenever it needs to display
155  the given value.
156
157  \param val spin box value
158  \return text representation of the value
159  \sa valueFromText()
160  */
161 QString ModuleBase_DoubleSpinBox::textFromValue(double val) const
162 {
163   QString s = locale().toString(val, myPrecision >= 0 ? 'f' : 'g', qAbs(myPrecision));
164   return removeTrailingZeroes(s);
165 }
166
167 /*!
168  \brief Return source string with removed leading and trailing zeros.
169  \param src source string
170  \return resulting string
171  */
172 QString ModuleBase_DoubleSpinBox::removeTrailingZeroes(const QString& src) const
173 {
174   QString delim(locale().decimalPoint());
175
176   int idx = src.lastIndexOf(delim);
177   if (idx == -1)
178     return src;
179
180   QString iPart = src.left(idx);
181   QString fPart = src.mid(idx + 1);
182   QString ePart = "";
183   int idx1 = fPart.lastIndexOf(QRegExp("e[+|-]?[0-9]+"));
184   if (idx1 >= 0) {
185     ePart = fPart.mid(idx1);
186     fPart = fPart.left(idx1);
187   }
188
189   fPart.remove(QRegExp("0+$"));
190
191   QString res = iPart;
192   if (!fPart.isEmpty())
193     res += delim + fPart;
194   res += ePart;
195
196   return res;
197 }
198
199 /*!
200  \brief Perform \a steps increment/decrement steps.
201
202  The \a steps value can be any integer number. If it is > 0,
203  the value incrementing is done, otherwise value is decremented
204  \a steps times.
205
206  \param steps number of increment/decrement steps
207  */
208 void ModuleBase_DoubleSpinBox::stepBy(int steps)
209 {
210   myCleared = false;
211
212   QDoubleSpinBox::stepBy(steps);
213   double tmpval = value();
214   if (qAbs(tmpval) < PSEUDO_ZERO)
215     tmpval = 0.;
216   if (tmpval < minimum())
217     tmpval = minimum();
218   else if (tmpval > maximum())
219     tmpval = maximum();
220   setValue(tmpval);
221 }
222
223 /*!
224  \brief This function is used to determine whether input is valid.
225  \param str currently entered value
226  \param pos cursor position in the string
227  \return validating operation result
228  */
229 QValidator::State ModuleBase_DoubleSpinBox::validate(QString& str, int& pos) const
230 {
231   QString pref = this->prefix();
232   QString suff = this->suffix();
233   uint overhead = pref.length() + suff.length();
234   QValidator::State state = QValidator::Invalid;
235
236   QDoubleValidator v(NULL);
237
238   // If 'g' format is used (myPrecision < 0), then
239   // myPrecision - 1 digits are allowed after the decimal point.
240   // Otherwise, expect myPrecision digits after the decimal point.
241   int decs = myPrecision < 0 ? qAbs(myPrecision) - 1 : myPrecision;
242
243   v.setLocale(this->locale());
244   v.setDecimals(decs);
245   v.setBottom(minimum());
246   v.setTop(maximum());
247   v.setNotation(
248       myPrecision >= 0 ? QDoubleValidator::StandardNotation : QDoubleValidator::ScientificNotation);
249
250   if (overhead == 0)
251     state = v.validate(str, pos);
252   else {
253     if ((uint)(str.length()) >= overhead && str.startsWith(pref) && str.right(suff.length()) == suff) {
254       QString core = str.mid(pref.length(), str.length() - overhead);
255       int corePos = pos - pref.length();
256       state = v.validate(core, corePos);
257       pos = corePos + pref.length();
258       str.replace(pref.length(), str.length() - overhead, core);
259     } else {
260       state = v.validate(str, pos);
261       if (state == QValidator::Invalid) {
262         QString special = this->specialValueText().trimmed();
263         QString candidate = str.trimmed();
264         if (special.startsWith(candidate)) {
265           if (candidate.length() == special.length())
266             state = QValidator::Acceptable;
267           else
268             state = QValidator::Intermediate;
269         }
270       }
271     }
272   }
273
274   // Treat values ouside (min; max) range as Invalid
275   // This check is enabled by assigning "strict_validity_check" dynamic property
276   // with value "true" to the spin box instance.
277   if (state == QValidator::Intermediate) {
278     bool isOk;
279     double val = str.toDouble(&isOk);
280     if (isOk) {
281       QVariant propVal = property("strict_validity_check");
282       if (propVal.isValid() && propVal.canConvert(QVariant::Bool) && propVal.toBool()) {
283         if (val < minimum() || val > maximum())
284           state = QValidator::Invalid;
285       }
286     } else if (myPrecision < 0) {
287       // Consider too large negative exponent as Invalid
288       QChar e(locale().exponential());
289       int epos = str.indexOf(e, 0, Qt::CaseInsensitive);
290       if (epos != -1) {
291         epos++;  // Skip exponential symbol itself
292         QString exponent = str.right(str.length() - epos);
293         int expValue = exponent.toInt(&isOk);
294         if (isOk && expValue < std::numeric_limits<double>::min_exponent10)
295           state = QValidator::Invalid;
296       }
297     }
298   }
299
300   return state;
301 }
302
303 /*!
304  \brief Called when user enters the text in the spin box.
305  */
306 void ModuleBase_DoubleSpinBox::onTextChanged(const QString& )
307 {
308   myCleared = false;
309 }