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