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