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