]> SALOME platform Git repositories - modules/shaper.git/blob - src/ModuleBase/ModuleBase_ParamSpinBox.cpp
Salome HOME
Merge branch 'BR_EDF_2018_Lot1'
[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_MIN),
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 }
181
182 /*!
183  \brief Enables or disables variable names in the spin box.
184  By default, variable names are enabled.
185  \param flag If true, variable names are enabled.
186  */
187 void ModuleBase_ParamSpinBox::setAcceptVariables(const bool flag)
188 {
189   myAcceptVariables = flag;
190   if ((!myAcceptVariables) && myIsEquation) {
191     setValue(0);
192   }
193 }
194
195 /*!
196  \brief Returns true if the spin box accepts variable names.
197  */
198 bool ModuleBase_ParamSpinBox::isAcceptVariables() const
199 {
200   return myAcceptVariables;
201 }
202
203 bool ModuleBase_ParamSpinBox::hasVariable() const
204 {
205   return myIsEquation;
206 }
207
208 bool ModuleBase_ParamSpinBox::hasVariable(const QString& theText) const
209 {
210   bool isDouble = false;
211   QLocale::c().toDouble(theText, &isDouble);
212   return !isDouble;
213 }
214
215 void ModuleBase_ParamSpinBox::showCompletion(bool checkPrefix)
216 {
217   myCompletePos = lineEdit()->cursorPosition();
218   int aStart, aEnd;
219   QString aPrefix;
220   aPrefix = getPrefix(aStart, aEnd);
221   if (checkPrefix) {
222     if (aPrefix.length() > 0) {
223       myCompleter->setCompletionPrefix(aPrefix);
224       myCompleter->complete();
225     }
226   } else {
227     myCompleter->setCompletionPrefix(aPrefix);
228     myCompleter->complete();
229   }
230 }
231
232 void ModuleBase_ParamSpinBox::keyReleaseEvent(QKeyEvent* e)
233 {
234   QString aText;
235
236   switch (e->key()) {
237   case Qt::Key_Backspace:
238     if (myCompleter->popup()->isVisible()) {
239       myCompleter->popup()->hide();
240     }
241     showCompletion(true);
242     break;
243   case Qt::Key_Return:
244   case Qt::Key_Enter:
245     if (myCompleter->popup()->isVisible()) {
246       myCompleter->popup()->hide();
247       myIsEquation = true;
248     }
249     emit textChanged(lineEdit()->text());
250     break;
251   case Qt::Key_Space:
252     if (e->modifiers() & Qt::ControlModifier) {
253       showCompletion(false);
254     }
255     break;  default:
256     aText = e->text();
257     if (aText.length() == 1) {
258       QChar aChar = aText.at(0);
259       if (isVariableSymbol(aChar)) {
260         showCompletion(true);
261       }
262     }
263   }
264   QAbstractSpinBox::keyReleaseEvent(e);
265 }
266
267 bool ModuleBase_ParamSpinBox::eventFilter(QObject* theObj, QEvent* theEvent)
268 {
269   if (theEvent->type() == QEvent::KeyRelease) {
270     keyReleaseEvent((QKeyEvent*)theEvent);
271   }
272   return QAbstractSpinBox::eventFilter(theObj, theEvent);
273 }
274
275
276 QString ModuleBase_ParamSpinBox::getPrefix(int& theStart, int& theEnd) const
277 {
278   QString aPrefix;
279   QString aText = lineEdit()->text();
280   theStart = theEnd = myCompletePos;
281   const int aLen = aText.length();
282   if (aLen > 0) {
283     if (myCompletePos > 0) {
284       int aLastChar = myCompletePos - 1;
285       QChar aChar = aText.at(aLastChar);
286       while (isVariableSymbol(aChar)) {
287         aPrefix.prepend(aText.at(aLastChar));
288         aLastChar--;
289         if (aLastChar < 0)
290           break;
291         aChar = aText.at(aLastChar);
292       }
293       theStart = aLastChar + 1;
294     }
295     if (myCompletePos < aLen) {
296       int aLastChar = myCompletePos;
297       QChar aChar = aText.at(aLastChar);
298       while (isVariableSymbol(aChar)) {
299         aPrefix.append(aText.at(aLastChar));
300         aLastChar++;
301         if (aLastChar >= aLen)
302           break;
303         aChar = aText.at(aLastChar);
304       }
305       theEnd = aLastChar;
306     }
307   }
308   return aPrefix;
309 }
310
311
312 void ModuleBase_ParamSpinBox::insertCompletion(const QString& theText)
313 {
314   QString aText = lineEdit()->text();
315   int aStart, aEnd;
316   QString aPrefix = getPrefix(aStart, aEnd);
317
318   QString aResult;
319   int aPrefLen = aPrefix.length();
320   if (aPrefLen == 0)
321     aResult = aText.insert(myCompletePos, theText);
322   else {
323     aResult = aText.left(aStart) + theText + aText.right(aText.length() - aEnd);
324   }
325   lineEdit()->setText(aResult);
326   myIsEquation = true;
327 }
328
329
330 void ModuleBase_ParamSpinBox::setValueEnabled(bool theEnable)
331 {
332   setReadOnly(!theEnable);
333
334   QPalette aPal = palette();
335   aPal.setColor(QPalette::All, QPalette::Base,
336     theEnable ? myEnabledBaseColor : aPal.color(QPalette::Disabled, QPalette::Base));
337   setPalette(aPal);
338 }