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