Salome HOME
Revamped Python interpreter logic in GUI.
[modules/gui.git] / src / SalomeApp / SalomeApp_DoubleSpinBox.cxx
1 // Copyright (C) 2007-2013  CEA/DEN, EDF R&D, OPEN CASCADE
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.
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 email : webmaster.salome@opencascade.com
18 //
19
20 // File:      SalomeApp_DoubleSpinBox.cxx
21 // Author:    Oleg UVAROV
22 //
23 #include <PyConsole_Interp.h> // this include must be first (see PyInterp_base.h)!
24 #include <PyConsole_Console.h>
25
26 #include "SalomeApp_DoubleSpinBox.h"
27 #include "SalomeApp_Application.h"
28 #include "SalomeApp_Study.h"
29
30 #include <SUIT_Session.h>
31
32 #include "SALOMEDSClient_ClientFactory.hxx" 
33 #include CORBA_SERVER_HEADER(SALOMEDS)
34
35 #include <QKeyEvent>
36 #include <QLineEdit>
37 #include <QToolTip>
38 #include <QRegExp>
39
40 #include <string>
41
42 /*!
43   \class SalomeApp_DoubleSpinBox
44 */
45
46 /*!
47   \brief Constructor.
48
49   Constructs a spin box with 0.0 as minimum value and 99.99 as maximum value,
50   a step value of 1.0 and a precision of 2 decimal places. 
51   The value is initially set to 0.00.
52
53   \param parent parent object
54 */
55 SalomeApp_DoubleSpinBox::SalomeApp_DoubleSpinBox( QWidget* parent )
56 : QtxDoubleSpinBox( parent ),
57   myDefaultValue( 0.0 ),
58   myIsRangeSet( false ),
59   myMinimum( 0.0 ),
60   myMaximum( 99.99 ),
61   myAcceptNames( true ),
62   myShowTip( true )
63 {
64   connectSignalsAndSlots();
65 }
66
67 /*!
68   \brief Constructor.
69
70   Constructs a spin box with specified minimum, maximum and step value.
71   The precision is set to 2 decimal places. 
72   The value is initially set to the minimum value.
73
74   \param min spin box minimum possible value
75   \param max spin box maximum possible value
76   \param step spin box increment/decrement value
77   \param parent parent object
78 */
79 SalomeApp_DoubleSpinBox::SalomeApp_DoubleSpinBox( double min, double max, double step, QWidget* parent )
80 : QtxDoubleSpinBox( min, max, step, parent ),
81   myDefaultValue( 0.0 ),
82   myIsRangeSet( false ),
83   myMinimum( min ),
84   myMaximum( max ),
85   myAcceptNames( true ),
86   myShowTip( true )
87 {
88   connectSignalsAndSlots();
89 }
90
91 /*!
92   \brief Constructor.
93
94   Constructs a spin box with specified minimum, maximum and step value.
95   The precision is set to 2 decimal places. 
96   The value is initially set to the minimum value.
97
98   \param min spin box minimum possible value
99   \param max spin box maximum possible value
100   \param step spin box increment/decrement value
101   \param parent parent object
102   \param acceptNames if true, enables variable names in the spin box
103   \param showTip if true, makes the widget show a tooltip when invalid text is entered by the user
104 */
105 SalomeApp_DoubleSpinBox::SalomeApp_DoubleSpinBox( double min, 
106                                                   double max, 
107                                                   double step, 
108                                                   int prec, 
109                                                   int dec, 
110                                                   QWidget* parent,
111                                                   bool acceptNames,
112                                                   bool showTip )
113 : QtxDoubleSpinBox( min, max, step, prec, dec, parent ),
114   myDefaultValue( 0.0 ),
115   myIsRangeSet( false ),
116   myMinimum( min ),
117   myMaximum( max ),
118   myAcceptNames( acceptNames ),
119   myShowTip( showTip )
120 {
121   connectSignalsAndSlots();
122 }
123
124 /*!
125   \brief Destructor.
126 */
127 SalomeApp_DoubleSpinBox::~SalomeApp_DoubleSpinBox()
128 {
129 }
130
131 /*!
132   \brief Perform \a steps increment/decrement steps.
133   
134   Re-implemented to handle cases when Notebook variable
135   name is specified by the user as the widget text.  
136   Otherwise, simply calls the base implementation.
137
138   \param steps number of increment/decrement steps
139 */
140 void SalomeApp_DoubleSpinBox::stepBy( int steps )
141 {
142   QString str  = text();
143   QString pref = prefix();
144   QString suff = suffix();
145   
146   if ( pref.length() && str.startsWith( pref ) )
147     str = str.right( str.length() - pref.length() );
148   if ( suff.length() && str.endsWith( suff ) )
149     str = str.left( str.length() - suff.length() );
150   
151   QRegExp varNameMask( "([a-z]|[A-Z]|_).*" );
152   if ( varNameMask.exactMatch( str ) )
153     return;
154
155   QtxDoubleSpinBox::stepBy( steps );
156 }
157
158 /*!
159   \brief Connect signals and slots.
160 */
161 void SalomeApp_DoubleSpinBox::connectSignalsAndSlots()
162 {
163   connect( this, SIGNAL( editingFinished() ),
164            this, SLOT( onEditingFinished() ) );
165
166   connect( this, SIGNAL( valueChanged( const QString& ) ),
167            this, SLOT( onTextChanged( const QString& ) ) );
168
169   connect( lineEdit(), SIGNAL( textChanged( const QString& ) ),
170            this, SLOT( onTextChanged( const QString& ) ) );
171
172   connect( lineEdit(), SIGNAL( textChanged( const QString& )),
173            this, SIGNAL( textChanged( const QString& ) ) );
174 }
175
176 /*!
177   \brief This function is called when editing is finished.
178 */
179 void SalomeApp_DoubleSpinBox::onEditingFinished()
180 {
181   if( myTextValue.isNull() )
182     myTextValue = text();
183
184   setText( myTextValue );
185 }
186
187 /*!
188   \brief This function is called when value is changed.
189 */
190 void SalomeApp_DoubleSpinBox::onTextChanged( const QString& text )
191 {
192   myTextValue = text;
193
194   double value = 0;
195   if( isValid( text, value ) == Acceptable )
196     myCorrectValue = text;
197 }
198
199 /*!
200   \brief Interpret text entered by the user as a value.
201   \param text text entered by the user
202   \return mapped value
203   \sa textFromValue()
204 */
205 double SalomeApp_DoubleSpinBox::valueFromText( const QString& text ) const
206 {
207   double value = 0;
208   if( isValid( text, value ) == Acceptable )
209     return value;
210
211   return defaultValue();
212 }
213
214 /*!
215   \brief This function is used by the spin box whenever it needs to display
216   the given value.
217
218   \param val spin box value
219   \return text representation of the value
220   \sa valueFromText()
221 */
222 QString SalomeApp_DoubleSpinBox::textFromValue( double val ) const
223 {
224   return QtxDoubleSpinBox::textFromValue( val );
225 }
226
227 /*!
228   \brief This function is used to determine whether input is valid.
229   \param str currently entered value
230   \param pos cursor position in the string
231   \return validating operation result
232 */
233 QValidator::State SalomeApp_DoubleSpinBox::validate( QString& str, int& pos ) const
234 {
235   QValidator::State res = QValidator::Invalid;
236
237   // Considering the input text as a variable name
238   // Applying Python identifier syntax:
239   // either a string starting with a letter, or a string starting with
240   // an underscore followed by at least one alphanumeric character
241   if ( isAcceptNames() ){
242     QRegExp varNameMask( "(([a-z]|[A-Z])([a-z]|[A-Z]|[0-9]|_)*)|(_([a-z]|[A-Z]|[0-9])+([a-z]|[A-Z]|[0-9]|_)*)" );
243     if ( varNameMask.exactMatch( str ) )
244       res = QValidator::Acceptable;
245   
246     if ( res == QValidator::Invalid ){
247       varNameMask.setPattern( "_" );
248       if ( varNameMask.exactMatch( str ) )  
249         res = QValidator::Intermediate;
250     }
251   }
252   
253   // Trying to interpret the current input text as a numeric value
254   if ( res == QValidator::Invalid )
255     res = QtxDoubleSpinBox::validate( str, pos );  
256   
257   // Show tooltip in case of invalid manual input
258   if ( isShowTipOnValidate() && lineEdit()->hasFocus() ){
259     if ( res != QValidator::Acceptable ){ // san: do we need to warn the user in Intermediate state???
260       SalomeApp_DoubleSpinBox* that = const_cast<SalomeApp_DoubleSpinBox*>( this );
261       QPoint pos( size().width(), 0. );
262       QPoint globalPos = mapToGlobal( pos );
263       QString minVal = textFromValue( minimum() );
264       QString maxVal = textFromValue( maximum() );
265       
266       // Same stuff as in QtxDoubleSpinBox::textFromValue()
267       int digits = getPrecision();
268       
269       // For 'g' format, max. number of digits after the decimal point is getPrecision() - 1
270       // See also QtxDoubleSpinBox::validate()
271       if ( digits < 0 )
272         digits = qAbs( digits ) - 1;
273       
274       QString templ( isAcceptNames() ? tr( "VALID_RANGE_VAR_MSG" ) : tr( "VALID_RANGE_NOVAR_MSG" ) );
275       QString msg( templ.arg( minVal ).arg( maxVal ).arg( digits ) );
276       
277       // Add extra hints to the message (if any passed through dynamic properties)
278       QVariant propVal = property( "validity_tune_hint" );
279       if ( propVal.isValid() ){
280         QString extraInfo = propVal.toString();
281         if ( !extraInfo.isEmpty() ){
282           msg += "\n";
283           msg += extraInfo;
284         }
285       }
286       
287       QToolTip::showText( globalPos, 
288                           msg, 
289                           that );
290     }
291     else
292       QToolTip::hideText();
293   }
294       
295   return res;
296 }
297
298 /*!
299   \brief This function is used to determine whether input is valid.
300   \return validating operation result
301 */
302 bool SalomeApp_DoubleSpinBox::isValid( QString& msg, bool toCorrect )
303 {
304   double value;
305   State aState = isValid( text(), value );
306
307   if( aState != Acceptable )
308   {
309     if( toCorrect )
310     {
311       if( aState == Incompatible )
312         msg += tr( "ERR_INCOMPATIBLE_TYPE" ).arg( text() ) + "\n";
313       else if( aState == NoVariable )
314         msg += tr( "ERR_NO_VARIABLE" ).arg( text() ) + "\n";
315       else if( aState == Invalid )
316         msg += tr( "ERR_INVALID_VALUE" ) + "\n";
317
318       setText( myCorrectValue );
319     }
320     return false;
321   }
322
323   return true;
324 }
325
326 /*!
327   \brief This function is used to set a default value for this spinbox.
328   \param value default value
329 */
330 void SalomeApp_DoubleSpinBox::setDefaultValue( const double value )
331 {
332   myDefaultValue = value;
333 }
334
335 /*!
336   \brief This function is used to set minimum and maximum values for this spinbox.
337   \param min minimum value
338   \param max maximum value
339 */
340 void SalomeApp_DoubleSpinBox::setRange( const double min, const double max )
341 {
342   QtxDoubleSpinBox::setRange( min, max );
343
344   myIsRangeSet = true;
345   myMinimum = min;
346   myMaximum = max;
347 }
348
349 /*!
350   \brief This function is used to set a current value for this spinbox.
351   \param value current value
352 */
353 void SalomeApp_DoubleSpinBox::setValue( const double value )
354 {
355   QtxDoubleSpinBox::setValue( value );
356
357   myCorrectValue = QtxDoubleSpinBox::textFromValue( value );
358   myTextValue = myCorrectValue;
359 }
360
361 /*!
362   \brief This function is used to set a text for this spinbox.
363   \param value current value
364 */
365 void SalomeApp_DoubleSpinBox::setText( const QString& value )
366 {
367   lineEdit()->setText(value);
368 }
369
370 /*!
371   \brief This function is used to determine whether input is valid.
372   \return validating operation result
373 */
374 SalomeApp_DoubleSpinBox::State SalomeApp_DoubleSpinBox::isValid( const QString& text, double& value ) const
375 {
376   SearchState aSearchState = findVariable( text, value );
377   if( aSearchState == NotFound )
378   {
379     bool ok = false;
380     value = locale().toDouble( text, &ok );
381     if ( !ok )
382       return NoVariable;
383   }
384   else if( aSearchState == IncorrectType )
385     return Incompatible;
386
387   if( !checkRange( value ) )
388     return Invalid;
389
390   return Acceptable;
391 }
392
393 /*!
394   \brief This function return a default acceptable value (commonly, 0.0).
395   \return default acceptable value
396 */
397 double SalomeApp_DoubleSpinBox::defaultValue() const
398 {
399   if( myMinimum > myDefaultValue || myMaximum < myDefaultValue )
400     return myMinimum;
401
402   return myDefaultValue;
403 }
404
405 /*!
406   \brief This function is used to check that string value lies within predefined range.
407   \return check status
408 */
409 bool SalomeApp_DoubleSpinBox::checkRange( const double value ) const
410 {
411   if( !myIsRangeSet )
412     return true;
413
414   return value >= myMinimum && value <= myMaximum;
415 }
416
417 /*!
418   \brief This function is used to determine whether input is a variable name and to get its value.
419   \return status of search operation
420 */
421 SalomeApp_DoubleSpinBox::SearchState SalomeApp_DoubleSpinBox::findVariable( const QString& name, double& value ) const
422 {
423   value = 0;
424   if( SalomeApp_Application* app = dynamic_cast<SalomeApp_Application*>( SUIT_Session::session()->activeApplication() ) )
425   {
426     if( SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>( app->activeStudy() ) )
427     {
428       _PTR(Study) studyDS = study->studyDS();
429
430       std::string aName = name.toStdString();
431       if( studyDS->IsVariable( aName ) )
432       {
433         if( studyDS->IsReal( aName ) || studyDS->IsInteger( aName ) || studyDS->IsString( aName ) )
434         {
435           if( studyDS->IsString( aName ) )
436             {
437               PyConsole_Console* pyConsole = app->pythonConsole();
438               PyConsole_Interp* pyInterp = pyConsole->getInterp();
439               PyLockWrapper aLock; // Acquire GIL
440               std::string command;
441               command  = "import salome_notebook ; ";
442               command += "salome_notebook.notebook.setAsReal(\"";
443               command += aName;
444               command += "\")";
445               bool aResult;
446               aResult = pyInterp->run(command.c_str());
447               if(aResult)
448                 {
449                   return IncorrectType;
450                 }
451             }
452           value = studyDS->GetReal( aName );
453           return Found;
454         }
455         return IncorrectType;
456       }
457     }
458   }
459   return NotFound;
460 }
461
462 /*!
463   \brief This function is called when the spinbox recieves key press event.
464 */
465 void SalomeApp_DoubleSpinBox::keyPressEvent( QKeyEvent* e )
466 {
467   if ( e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter )
468     QWidget::keyPressEvent( e );
469   else
470     QtxDoubleSpinBox::keyPressEvent( e );
471 }
472
473 /*!
474   \brief This function is called when the spinbox recieves show event.
475 */
476 void SalomeApp_DoubleSpinBox::showEvent( QShowEvent* )
477 {
478   setText( myTextValue );
479 }
480
481 /*!
482   \brief Enables or disables variable names in the spin box.
483          By default, variable names are enabled.
484   \param flag If true, variable names are enabled.
485 */
486 void SalomeApp_DoubleSpinBox::setAcceptNames( const bool flag )
487 {
488   myAcceptNames = flag;
489 }
490
491 /*!
492   \brief Returns true if the spin box accepts variable names.
493 */
494 bool SalomeApp_DoubleSpinBox::isAcceptNames() const
495 {
496   return myAcceptNames;
497 }
498
499 /*!
500   \brief Enables or disables  tooltips in case of invalid or intermediate-state input.
501          Tooltips are enabled by default.
502   \param flag If true, tooltips are enabled.
503 */
504 void SalomeApp_DoubleSpinBox::setShowTipOnValidate( const bool flag )
505 {
506   myShowTip = myShowTip;
507 }
508
509 /*!
510   \brief Returns true if tooltip should be shown in case of invalid or intermediate-state input.
511 */
512 bool SalomeApp_DoubleSpinBox::isShowTipOnValidate() const
513 {
514   return myShowTip;
515 }