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