]> SALOME platform Git repositories - modules/shaper.git/blob - src/ModuleBase/ModuleBase_ParamSpinBox.cpp
Salome HOME
Issue #3086: Avoid crash when FeatureInfo is null.
[modules/shaper.git] / src / ModuleBase / ModuleBase_ParamSpinBox.cpp
1 // Copyright (C) 2014-2019  CEA/DEN, EDF R&D
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 #include "ModuleBase_ParamSpinBox.h"
21
22 #include <QKeyEvent>
23 #include <QLocale>
24 #include <QRegExp>
25 #include <QToolTip>
26 #include <QApplication>
27
28 #include <QStringListModel>
29 #include <QCompleter>
30 #include <QAbstractItemView>
31 #include <QShortcut>
32
33 #include <string>
34 #include <iostream>
35 #include <cfloat>
36
37
38 bool isVariableSymbol(const QChar& theChar) {
39   if (theChar.isLetterOrNumber())
40     return true;
41   if (theChar == '_')
42     return true;
43   return false;
44 }
45
46 ModuleBase_ParamSpinBox::ModuleBase_ParamSpinBox(QWidget* theParent, int thePrecision)
47   : QAbstractSpinBox(theParent),
48   myIsEquation(false),
49   myAcceptVariables(true),
50   mySingleStep(1),
51   myMinimum(-DBL_MAX),
52   myMaximum(DBL_MAX)
53 {
54   myCompleter = new QCompleter(this);
55   myCompleter->setWidget(lineEdit());
56   myCompleter->setCompletionMode(QCompleter::PopupCompletion);
57
58   myCompleterModel = new QStringListModel(this);
59   myCompleter->setModel(myCompleterModel);
60   connect(myCompleter, SIGNAL(highlighted(const QString&)),
61     this, SLOT(insertCompletion(const QString&)));
62
63   QAbstractItemView* aPopup = myCompleter->popup();
64   aPopup->installEventFilter(this);
65
66   //  connectSignalsAndSlots();
67   myEnabledBaseColor = palette().color(QPalette::Active, QPalette::Base);
68   connect(lineEdit(), SIGNAL(textChanged(const QString&)),
69     this, SLOT(onTextChanged(const QString&)));
70
71   setLocale(QLocale::c());
72
73   myValidator = new QDoubleValidator(this);
74   myValidator->setLocale(locale());
75   myValidator->setRange(myMinimum, myMaximum);
76   myValidator->setDecimals(thePrecision);
77 }
78
79 void ModuleBase_ParamSpinBox::setCompletionList(QStringList& theList)
80 {
81   theList.removeDuplicates();
82   theList.sort();
83   myCompleterModel->setStringList(theList);
84 }
85
86 /*!
87  \brief Destructor.
88  */
89 ModuleBase_ParamSpinBox::~ModuleBase_ParamSpinBox()
90 {
91 }
92
93
94 /*!
95  \brief Perform \a steps increment/decrement steps.
96
97  Re-implemented to handle cases when Notebook variable
98  name is specified by the user as the widget text.
99  Otherwise, simply calls the base implementation.
100
101  \param steps number of increment/decrement steps
102  */
103 void ModuleBase_ParamSpinBox::stepBy(int steps)
104 {
105   if (hasVariable())
106     return;
107
108   double aVal = lineEdit()->text().toDouble();
109   aVal += steps * mySingleStep;
110   setValue(aVal);
111   //QAbstractSpinBox::stepBy(steps);
112 }
113
114 void ModuleBase_ParamSpinBox::onTextChanged(const QString& theText)
115 {
116   myIsEquation = hasVariable(theText);
117   emit textChanged(theText);
118 }
119
120
121 /*!
122  \brief This function is used to determine whether input is valid.
123  \param str currently entered value
124  \param pos cursor position in the string
125  \return validating operation result
126  */
127 QValidator::State ModuleBase_ParamSpinBox::validate(QString& str, int& pos) const
128 {
129   // Trying to interpret the current input text as a numeric value
130   if (!hasVariable(str)) {
131     /// If decimals = 0 do not accept '.' (interpret as int)
132     if ((myValidator->decimals() == 0) && str.endsWith('.'))
133       return QValidator::Invalid;
134     return myValidator->validate(str, pos);
135   }
136
137   return isAcceptVariables() ? QValidator::Acceptable : QValidator::Invalid;
138 }
139
140 /*!
141  \brief This function is used to set a current value for this spinbox.
142  \param value current value
143
144  The new value is ignored if the spinbox has a variable.
145  */
146 void ModuleBase_ParamSpinBox::setValue(double value)
147 {
148   myIsEquation = false;
149   double aVal = value;
150   if (aVal < myMinimum)
151     aVal = myMinimum;
152   else if (aVal > myMaximum)
153     aVal = myMaximum;
154   QString aText = (myValidator->decimals() == 0) ? QString::number((int)aVal) :
155     QString::number(aVal, 'g', decimals());
156   lineEdit()->blockSignals(true);
157   lineEdit()->setText(aText);
158   lineEdit()->blockSignals(false);
159   emit textChanged(aText);
160 }
161
162 double ModuleBase_ParamSpinBox::value() const
163 {
164   return lineEdit()->text().toDouble();
165 }
166
167 /*!
168  \brief This function is used to set a text for this spinbox.
169  \param value current value
170  */
171 void ModuleBase_ParamSpinBox::setText(const QString& value)
172 {
173   myIsEquation = hasVariable(value);
174   if (myAcceptVariables && myIsEquation) {
175     lineEdit()->setText(value);
176     emit textChanged(value);
177   }
178   else {
179     bool isConv = false;
180     double aVal = value.toDouble(&isConv);
181     if (isConv)
182       setValue(aVal);
183   }
184 }
185
186 /*!
187  \brief Enables or disables variable names in the spin box.
188  By default, variable names are enabled.
189  \param flag If true, variable names are enabled.
190  */
191 void ModuleBase_ParamSpinBox::setAcceptVariables(const bool flag)
192 {
193   myAcceptVariables = flag;
194   if ((!myAcceptVariables) && myIsEquation) {
195     setValue(0);
196   }
197 }
198
199 /*!
200  \brief Returns true if the spin box accepts variable names.
201  */
202 bool ModuleBase_ParamSpinBox::isAcceptVariables() const
203 {
204   return myAcceptVariables;
205 }
206
207 bool ModuleBase_ParamSpinBox::hasVariable() const
208 {
209   return myIsEquation;
210 }
211
212 bool ModuleBase_ParamSpinBox::hasVariable(const QString& theText) const
213 {
214   bool isDouble = false;
215   QLocale::c().toDouble(theText, &isDouble);
216   return !isDouble;
217 }
218
219 void ModuleBase_ParamSpinBox::showCompletion(bool checkPrefix)
220 {
221   myCompletePos = lineEdit()->cursorPosition();
222   int aStart, aEnd;
223   QString aPrefix;
224   aPrefix = getPrefix(aStart, aEnd);
225   if (checkPrefix) {
226     if (aPrefix.length() > 0) {
227       myCompleter->setCompletionPrefix(aPrefix);
228       myCompleter->complete();
229     }
230   } else {
231     myCompleter->setCompletionPrefix(aPrefix);
232     myCompleter->complete();
233   }
234 }
235
236 void ModuleBase_ParamSpinBox::keyReleaseEvent(QKeyEvent* e)
237 {
238   QString aText;
239
240   switch (e->key()) {
241   case Qt::Key_Backspace:
242     if (myCompleter->popup()->isVisible()) {
243       myCompleter->popup()->hide();
244     }
245     showCompletion(true);
246     break;
247   case Qt::Key_Return:
248   case Qt::Key_Enter:
249     if (myCompleter->popup()->isVisible()) {
250       myCompleter->popup()->hide();
251       myIsEquation = true;
252     }
253     emit textChanged(lineEdit()->text());
254     break;
255   case Qt::Key_Space:
256     if (e->modifiers() & Qt::ControlModifier) {
257       showCompletion(false);
258     }
259     break;  default:
260     aText = e->text();
261     if (aText.length() == 1) {
262       QChar aChar = aText.at(0);
263       if (isVariableSymbol(aChar)) {
264         showCompletion(true);
265       }
266     }
267   }
268   QAbstractSpinBox::keyReleaseEvent(e);
269 }
270
271 bool ModuleBase_ParamSpinBox::eventFilter(QObject* theObj, QEvent* theEvent)
272 {
273   if (theEvent->type() == QEvent::KeyRelease) {
274     keyReleaseEvent((QKeyEvent*)theEvent);
275   }
276   return QAbstractSpinBox::eventFilter(theObj, theEvent);
277 }
278
279
280 QString ModuleBase_ParamSpinBox::getPrefix(int& theStart, int& theEnd) const
281 {
282   QString aPrefix;
283   QString aText = lineEdit()->text();
284   theStart = theEnd = myCompletePos;
285   const int aLen = aText.length();
286   if (aLen > 0) {
287     if (myCompletePos > 0) {
288       int aLastChar = myCompletePos - 1;
289       QChar aChar = aText.at(aLastChar);
290       while (isVariableSymbol(aChar)) {
291         aPrefix.prepend(aText.at(aLastChar));
292         aLastChar--;
293         if (aLastChar < 0)
294           break;
295         aChar = aText.at(aLastChar);
296       }
297       theStart = aLastChar + 1;
298     }
299     if (myCompletePos < aLen) {
300       int aLastChar = myCompletePos;
301       QChar aChar = aText.at(aLastChar);
302       while (isVariableSymbol(aChar)) {
303         aPrefix.append(aText.at(aLastChar));
304         aLastChar++;
305         if (aLastChar >= aLen)
306           break;
307         aChar = aText.at(aLastChar);
308       }
309       theEnd = aLastChar;
310     }
311   }
312   return aPrefix;
313 }
314
315
316 void ModuleBase_ParamSpinBox::insertCompletion(const QString& theText)
317 {
318   QString aText = lineEdit()->text();
319   int aStart, aEnd;
320   QString aPrefix = getPrefix(aStart, aEnd);
321
322   QString aResult;
323   int aPrefLen = aPrefix.length();
324   if (aPrefLen == 0)
325     aResult = aText.insert(myCompletePos, theText);
326   else {
327     aResult = aText.left(aStart) + theText + aText.right(aText.length() - aEnd);
328   }
329   lineEdit()->setText(aResult);
330   myIsEquation = true;
331 }
332
333
334 void ModuleBase_ParamSpinBox::setValueEnabled(bool theEnable)
335 {
336   setReadOnly(!theEnable);
337
338   QPalette aPal = palette();
339   aPal.setColor(QPalette::All, QPalette::Base,
340     theEnable ? myEnabledBaseColor : aPal.color(QPalette::Disabled, QPalette::Base));
341   setPalette(aPal);
342 }