]> SALOME platform Git repositories - modules/shaper.git/blob - src/ModuleBase/ModuleBase_ParamSpinBox.cpp
Salome HOME
Task 2.7: Code completion
[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
37 ModuleBase_ParamSpinBox::ModuleBase_ParamSpinBox(QWidget* theParent, int thePrecision)
38   : QAbstractSpinBox(theParent),
39   myPrecision(thePrecision),
40   myIsEquation(false),
41   myAcceptVariables(true),
42   myDecimals(3),
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   myValidator = new QDoubleValidator(this);
65   myValidator->setLocale(locale());
66   myValidator->setRange(myMinimum, myMaximum);
67 }
68
69 void ModuleBase_ParamSpinBox::setCompletionList(QStringList& theList)
70 {
71   theList.sort();
72   theList.removeDuplicates();
73   myCompleterModel->setStringList(theList);
74 }
75
76 /*!
77  \brief Destructor.
78  */
79 ModuleBase_ParamSpinBox::~ModuleBase_ParamSpinBox()
80 {
81 }
82
83 /*!
84  \brief Perform \a steps increment/decrement steps.
85
86  Re-implemented to handle cases when Notebook variable
87  name is specified by the user as the widget text.
88  Otherwise, simply calls the base implementation.
89
90  \param steps number of increment/decrement steps
91  */
92 void ModuleBase_ParamSpinBox::stepBy(int steps)
93 {
94   if (hasVariable())
95     return;
96
97   double aVal = lineEdit()->text().toDouble();
98   aVal += steps * mySingleStep;
99   setValue(aVal);
100   QAbstractSpinBox::stepBy(steps);
101 }
102
103 void ModuleBase_ParamSpinBox::onTextChanged(const QString& theText)
104 {
105   myIsEquation = hasVariable(theText);
106   emit textChanged(theText);
107 }
108
109
110 ///*!
111 // \brief Connect signals and slots.
112 // */
113 //void ModuleBase_ParamSpinBox::connectSignalsAndSlots()
114 //{
115 //  connect(this, SIGNAL(valueChanged(const QString&)),
116 //          this, SLOT(onTextChanged(const QString&)));
117 //}
118 //
119 //void ModuleBase_ParamSpinBox::onTextChanged(const QString& text)
120 //{
121 //  myTextValue = text;
122 //  emit textChanged(text);
123 //}
124 //
125 //double ModuleBase_ParamSpinBox::valueFromText(const QString& theText) const
126 //{
127 //  if (!hasVariable(theText))
128 //    return ModuleBase_DoubleSpinBox::valueFromText(theText);
129 //
130 //  // small hack: return hash of the string to initiate valuesChanged signal
131 //  return qHash(theText);
132 //}
133 //
134 //QString ModuleBase_ParamSpinBox::textFromValue (double theValue) const
135 //{
136 //  if ((!myTextValue.isEmpty()) && hasVariable(myTextValue)){
137 //    return myTextValue;
138 //  }
139 //  return ModuleBase_DoubleSpinBox::textFromValue(theValue);
140 //}
141
142 /*!
143  \brief This function is used to determine whether input is valid.
144  \param str currently entered value
145  \param pos cursor position in the string
146  \return validating operation result
147  */
148 QValidator::State ModuleBase_ParamSpinBox::validate(QString& str, int& pos) const
149 {
150   // Trying to interpret the current input text as a numeric value
151   if (!hasVariable(str))
152     return myValidator->validate(str, pos);
153
154   return isAcceptVariables() ? QValidator::Acceptable : QValidator::Invalid;
155 }
156
157 /*!
158  \brief This function is used to set a current value for this spinbox.
159  \param value current value
160
161  The new value is ignored if the spinbox has a variable.
162  */
163 void ModuleBase_ParamSpinBox::setValue(double value)
164 {
165   myIsEquation = false;
166   double aVal = value;
167   if (aVal < myMinimum)
168     aVal = myMinimum;
169   else if (aVal > myMaximum)
170     aVal = myMaximum;
171   QString aText = QString::number(aVal, 'g', myDecimals);
172   lineEdit()->setText(aText);
173   emit textChanged(aText);
174 }
175
176 double ModuleBase_ParamSpinBox::value() const
177 {
178   //if (myIsEquation) {
179
180   //}
181   return lineEdit()->text().toDouble();
182 }
183
184 /*!
185  \brief This function is used to set a text for this spinbox.
186  \param value current value
187  */
188 void ModuleBase_ParamSpinBox::setText(const QString& value)
189 {
190   myIsEquation = hasVariable(value);
191   if (myAcceptVariables && myIsEquation) {
192     lineEdit()->setText(value);
193     emit textChanged(value);
194   }
195 }
196
197 /*!
198  \brief Enables or disables variable names in the spin box.
199  By default, variable names are enabled.
200  \param flag If true, variable names are enabled.
201  */
202 void ModuleBase_ParamSpinBox::setAcceptVariables(const bool flag)
203 {
204   myAcceptVariables = flag;
205   if ((!myAcceptVariables) && myIsEquation) {
206     setValue(0);
207   }
208 }
209
210 /*!
211  \brief Returns true if the spin box accepts variable names.
212  */
213 bool ModuleBase_ParamSpinBox::isAcceptVariables() const
214 {
215   return myAcceptVariables;
216 }
217
218 bool ModuleBase_ParamSpinBox::hasVariable() const
219 {
220   return myIsEquation;
221 }
222
223 bool ModuleBase_ParamSpinBox::hasVariable(const QString& theText) const
224 {
225   bool isDouble = false;
226   QLocale::c().toDouble(theText, &isDouble);
227   return !isDouble;
228 }
229
230 ///*!
231 // \brief This function is used to determine whether input is valid.
232 // \return validating operation result
233 // */
234 //ModuleBase_ParamSpinBox::State ModuleBase_ParamSpinBox::isValid(const QString& theText,
235 //                                                                double& theValue) const
236 //{
237 //  if (hasVariable() && !findVariable(theText, theValue)) {
238 //    bool ok = false;
239 //    theValue = locale().toDouble(theText, &ok);
240 //    if (!ok) {
241 //      return NoVariable;
242 //    }
243 //  }
244 //  if (!checkRange(theValue)) {
245 //    return Invalid;
246 //  }
247 //
248 //  return Acceptable;
249 //}
250 //
251 ///*!
252 // \brief This function is used to check that string value lies within predefined range.
253 // \return check status
254 // */
255 //bool ModuleBase_ParamSpinBox::checkRange(const double theValue) const
256 //{
257 //  return theValue >= minimum() && theValue <= maximum();
258 //}
259 //
260 ///*!
261 // \brief This function is used to determine whether input is a variable name and to get its value.
262 // \return status of search operation
263 // */
264 //bool ModuleBase_ParamSpinBox::findVariable(const QString& theName,
265 //                                           double& outValue) const
266 //{
267 //  ResultParameterPtr aParam;
268 //  return ModelAPI_Tools::findVariable(FeaturePtr(), theName.toStdString(), outValue, aParam);
269 //}
270
271 void ModuleBase_ParamSpinBox::keyReleaseEvent(QKeyEvent* e)
272 {
273   switch (e->key()) {
274   case Qt::Key_Return:
275   case Qt::Key_Enter:
276   case Qt::Key_Tab:
277   {
278     if (myCompleter->popup()->isVisible()) {
279       myCompleter->popup()->hide();
280       myIsEquation = true;
281     }
282     emit textChanged(lineEdit()->text());
283     return;
284   }
285   case Qt::Key_Space:
286     if (e->modifiers() & Qt::ControlModifier) {
287       myCompletePos = lineEdit()->cursorPosition();
288       int aStart, aEnd;
289       QString aPrefix = getPrefix(aStart, aEnd);
290       myCompleter->setCompletionPrefix(aPrefix);
291       myCompleter->complete();
292     }
293     break;
294   default:
295     QAbstractSpinBox::keyReleaseEvent(e);
296   }
297 }
298
299
300 QString ModuleBase_ParamSpinBox::getPrefix(int& theStart, int& theEnd) const
301 {
302   QString aPrefix;
303   QString aText = lineEdit()->text();
304   theStart = theEnd = myCompletePos;
305   const int aLen = aText.length();
306   if (aLen > 0) {
307     if (myCompletePos > 0) {
308       int aLastChar = myCompletePos - 1;
309       QChar aChar = aText.at(aLastChar);
310       while (aChar.isLetter() || aChar.isDigit()) {
311         aPrefix.prepend(aText.at(aLastChar));
312         aLastChar--;
313         if (aLastChar < 0)
314           break;
315         aChar = aText.at(aLastChar);
316       }
317       theStart = aLastChar + 1;
318     }
319     if (myCompletePos < aLen) {
320       int aLastChar = myCompletePos;
321       QChar aChar = aText.at(aLastChar);
322       while (aChar.isLetter() || aChar.isDigit()) {
323         aPrefix.append(aText.at(aLastChar));
324         aLastChar++;
325         if (aLastChar >= aLen)
326           break;
327         aChar = aText.at(aLastChar);
328       }
329       theEnd = aLastChar;
330     }
331   }
332   return aPrefix;
333 }
334
335
336 void ModuleBase_ParamSpinBox::insertCompletion(const QString& theText)
337 {
338   QString aText = lineEdit()->text();
339   int aStart, aEnd;
340   QString aPrefix = getPrefix(aStart, aEnd);
341
342   QString aResult;
343   int aPrefLen = aPrefix.length();
344   if (aPrefLen == 0)
345     aResult = aText.insert(myCompletePos, theText);
346   else {
347     aResult = aText.left(aStart) + theText + aText.right(aText.length() - aEnd);
348   }
349   lineEdit()->setText(aResult);
350   myIsEquation = true;
351 }
352
353
354 ///*!
355 // \brief This function is called when the spinbox receives show event.
356 // */
357 //void ModuleBase_ParamSpinBox::showEvent(QShowEvent* theEvent)
358 //{
359 //  ModuleBase_DoubleSpinBox::showEvent(theEvent);
360 //  if ((!myTextValue.isEmpty()) && hasVariable(myTextValue)) {
361 //    setText(myTextValue);
362 //  }
363 //}
364
365 void ModuleBase_ParamSpinBox::setValueEnabled(bool theEnable)
366 {
367   setReadOnly(!theEnable);
368
369   QPalette aPal = palette();
370   aPal.setColor(QPalette::All, QPalette::Base,
371     theEnable ? myEnabledBaseColor : aPal.color(QPalette::Disabled, QPalette::Base));
372   setPalette(aPal);
373 }