Salome HOME
Merge V8_4_BR branch.
[modules/gui.git] / src / SalomeApp / SalomeApp_DoubleSpinBox.cxx
index 42cf9e48de5fcc704deb3696e4c31c62e461e277..9bdda7525743fd458d2b7a8d1ffc8637f7723a7f 100644 (file)
@@ -1,23 +1,29 @@
-// Copyright (C) 2005  OPEN CASCADE, CEA/DEN, EDF R&D, PRINCIPIA R&D
-// 
+// Copyright (C) 2007-2016  CEA/DEN, EDF R&D, OPEN CASCADE
+//
 // This library is free software; you can redistribute it and/or
 // modify it under the terms of the GNU Lesser General Public
-// License as published by the Free Software Foundation; either 
-// version 2.1 of the License.
-// 
-// This library is distributed in the hope that it will be useful 
-// but WITHOUT ANY WARRANTY; without even the implied warranty of 
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 // Lesser General Public License for more details.
 //
-// You should have received a copy of the GNU Lesser General Public  
-// License along with this library; if not, write to the Free Software 
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 //
 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
 //
+
 // File:      SalomeApp_DoubleSpinBox.cxx
 // Author:    Oleg UVAROV
+//
+
+#ifndef DISABLE_PYCONSOLE
+  #include <PyConsole_Interp.h> // this include must be first (see PyInterp_base.h)!
+#endif
 
 #include "SalomeApp_DoubleSpinBox.h"
 #include "SalomeApp_Application.h"
 
 #include <QKeyEvent>
 #include <QLineEdit>
+#include <QToolTip>
+#include <QRegExp>
+
+#include <string>
 
 /*!
   \class SalomeApp_DoubleSpinBox
@@ -49,7 +59,9 @@ SalomeApp_DoubleSpinBox::SalomeApp_DoubleSpinBox( QWidget* parent )
   myDefaultValue( 0.0 ),
   myIsRangeSet( false ),
   myMinimum( 0.0 ),
-  myMaximum( 99.99 )
+  myMaximum( 99.99 ),
+  myAcceptNames( true ),
+  myShowTip( true )
 {
   connectSignalsAndSlots();
 }
@@ -71,7 +83,9 @@ SalomeApp_DoubleSpinBox::SalomeApp_DoubleSpinBox( double min, double max, double
   myDefaultValue( 0.0 ),
   myIsRangeSet( false ),
   myMinimum( min ),
-  myMaximum( max )
+  myMaximum( max ),
+  myAcceptNames( true ),
+  myShowTip( true )
 {
   connectSignalsAndSlots();
 }
@@ -87,13 +101,24 @@ SalomeApp_DoubleSpinBox::SalomeApp_DoubleSpinBox( double min, double max, double
   \param max spin box maximum possible value
   \param step spin box increment/decrement value
   \param parent parent object
+  \param acceptNames if true, enables variable names in the spin box
+  \param showTip if true, makes the widget show a tooltip when invalid text is entered by the user
 */
-SalomeApp_DoubleSpinBox::SalomeApp_DoubleSpinBox( double min, double max, double step, int prec, int dec, QWidget* parent )
+SalomeApp_DoubleSpinBox::SalomeApp_DoubleSpinBox( double min, 
+                                                  double max, 
+                                                  double step, 
+                                                  int prec, 
+                                                  int dec, 
+                                                  QWidget* parent,
+                                                  bool acceptNames,
+                                                  bool showTip )
 : QtxDoubleSpinBox( min, max, step, prec, dec, parent ),
   myDefaultValue( 0.0 ),
   myIsRangeSet( false ),
   myMinimum( min ),
-  myMaximum( max )
+  myMaximum( max ),
+  myAcceptNames( acceptNames ),
+  myShowTip( showTip )
 {
   connectSignalsAndSlots();
 }
@@ -105,22 +130,49 @@ SalomeApp_DoubleSpinBox::~SalomeApp_DoubleSpinBox()
 {
 }
 
+/*!
+  \brief Perform \a steps increment/decrement steps.
+  
+  Re-implemented to handle cases when Notebook variable
+  name is specified by the user as the widget text.  
+  Otherwise, simply calls the base implementation.
+
+  \param steps number of increment/decrement steps
+*/
+void SalomeApp_DoubleSpinBox::stepBy( int steps )
+{
+  QString str  = text();
+  QString pref = prefix();
+  QString suff = suffix();
+  
+  if ( pref.length() && str.startsWith( pref ) )
+    str = str.right( str.length() - pref.length() );
+  if ( suff.length() && str.endsWith( suff ) )
+    str = str.left( str.length() - suff.length() );
+  
+  QRegExp varNameMask( "([a-z]|[A-Z]|_).*" );
+  if ( varNameMask.exactMatch( str ) )
+    return;
+
+  QtxDoubleSpinBox::stepBy( steps );
+}
+
 /*!
   \brief Connect signals and slots.
 */
 void SalomeApp_DoubleSpinBox::connectSignalsAndSlots()
 {
   connect( this, SIGNAL( editingFinished() ),
-          this, SLOT( onEditingFinished() ) );
+           this, SLOT( onEditingFinished() ) );
 
   connect( this, SIGNAL( valueChanged( const QString& ) ),
-          this, SLOT( onTextChanged( const QString& ) ) );
+           this, SLOT( onTextChanged( const QString& ) ) );
 
   connect( lineEdit(), SIGNAL( textChanged( const QString& ) ),
-          this, SLOT( onTextChanged( const QString& ) ) );
+           this, SLOT( onTextChanged( const QString& ) ) );
 
   connect( lineEdit(), SIGNAL( textChanged( const QString& )),
-          this, SIGNAL( textChanged( const QString& ) ) );
+           this, SIGNAL( textChanged( const QString& ) ) );
 }
 
 /*!
@@ -182,7 +234,67 @@ QString SalomeApp_DoubleSpinBox::textFromValue( double val ) const
 */
 QValidator::State SalomeApp_DoubleSpinBox::validate( QString& str, int& pos ) const
 {
-  return QValidator::Acceptable;
+  QValidator::State res = QValidator::Invalid;
+
+  // Considering the input text as a variable name
+  // Applying Python identifier syntax:
+  // either a string starting with a letter, or a string starting with
+  // an underscore followed by at least one alphanumeric character
+  if ( isAcceptNames() ){
+    QRegExp varNameMask( "(([a-z]|[A-Z])([a-z]|[A-Z]|[0-9]|_)*)|(_([a-z]|[A-Z]|[0-9])+([a-z]|[A-Z]|[0-9]|_)*)" );
+    if ( varNameMask.exactMatch( str ) )
+      res = QValidator::Acceptable;
+  
+    if ( res == QValidator::Invalid ){
+      varNameMask.setPattern( "_" );
+      if ( varNameMask.exactMatch( str ) )  
+        res = QValidator::Intermediate;
+    }
+  }
+  
+  // Trying to interpret the current input text as a numeric value
+  if ( res == QValidator::Invalid )
+    res = QtxDoubleSpinBox::validate( str, pos );  
+  
+  // Show tooltip in case of invalid manual input
+  if ( isShowTipOnValidate() && lineEdit()->hasFocus() ){
+    if ( res != QValidator::Acceptable ){ // san: do we need to warn the user in Intermediate state???
+      SalomeApp_DoubleSpinBox* that = const_cast<SalomeApp_DoubleSpinBox*>( this );
+      QPoint pos( size().width(), 0. );
+      QPoint globalPos = mapToGlobal( pos );
+      QString minVal = textFromValue( minimum() );
+      QString maxVal = textFromValue( maximum() );
+      
+      // Same stuff as in QtxDoubleSpinBox::textFromValue()
+      int digits = getPrecision();
+      
+      // For 'g' format, max. number of digits after the decimal point is getPrecision() - 1
+      // See also QtxDoubleSpinBox::validate()
+      if ( digits < 0 )
+        digits = qAbs( digits ) - 1;
+      
+      QString templ( isAcceptNames() ? tr( "VALID_RANGE_VAR_MSG" ) : tr( "VALID_RANGE_NOVAR_MSG" ) );
+      QString msg( templ.arg( minVal ).arg( maxVal ).arg( digits ) );
+      
+      // Add extra hints to the message (if any passed through dynamic properties)
+      QVariant propVal = property( "validity_tune_hint" );
+      if ( propVal.isValid() ){
+        QString extraInfo = propVal.toString();
+        if ( !extraInfo.isEmpty() ){
+          msg += "\n";
+          msg += extraInfo;
+        }
+      }
+      
+      QToolTip::showText( globalPos, 
+                          msg, 
+                          that );
+    }
+    else
+      QToolTip::hideText();
+  }
+      
+  return res;
 }
 
 /*!
@@ -199,11 +311,11 @@ bool SalomeApp_DoubleSpinBox::isValid( QString& msg, bool toCorrect )
     if( toCorrect )
     {
       if( aState == Incompatible )
-       msg += tr( "ERR_INCOMPATIBLE_TYPE" ).arg( text() ) + "\n";
+        msg += tr( "ERR_INCOMPATIBLE_TYPE" ).arg( text() ) + "\n";
       else if( aState == NoVariable )
-       msg += tr( "ERR_NO_VARIABLE" ).arg( text() ) + "\n";
+        msg += tr( "ERR_NO_VARIABLE" ).arg( text() ) + "\n";
       else if( aState == Invalid )
-       msg += tr( "ERR_INVALID_VALUE" ) + "\n";
+        msg += tr( "ERR_INVALID_VALUE" ) + "\n";
 
       setText( myCorrectValue );
     }
@@ -244,7 +356,7 @@ void SalomeApp_DoubleSpinBox::setValue( const double value )
 {
   QtxDoubleSpinBox::setValue( value );
 
-  myCorrectValue = QString::number( value );
+  myCorrectValue = QtxDoubleSpinBox::textFromValue( value );
   myTextValue = myCorrectValue;
 }
 
@@ -267,8 +379,8 @@ SalomeApp_DoubleSpinBox::State SalomeApp_DoubleSpinBox::isValid( const QString&
   if( aSearchState == NotFound )
   {
     bool ok = false;
-    value = text.toDouble( &ok );
-    if( !ok )
+    value = locale().toDouble( text, &ok );
+    if ( !ok )
       return NoVariable;
   }
   else if( aSearchState == IncorrectType )
@@ -320,12 +432,30 @@ SalomeApp_DoubleSpinBox::SearchState SalomeApp_DoubleSpinBox::findVariable( cons
       std::string aName = name.toStdString();
       if( studyDS->IsVariable( aName ) )
       {
-       if( studyDS->IsReal( aName ) || studyDS->IsInteger( aName ) )
-       {
-         value = studyDS->GetReal( aName );
-         return Found;
-       }
-       return IncorrectType;
+        if( studyDS->IsReal( aName ) || studyDS->IsInteger( aName ) || studyDS->IsString( aName ) )
+        {
+          if( studyDS->IsString( aName ) )
+            {
+#ifndef DISABLE_PYCONSOLE
+              PyConsole_Interp* pyInterp = app->getPyInterp();
+              PyLockWrapper aLock; // Acquire GIL
+              std::string command;
+              command  = "import salome_notebook ; ";
+              command += "salome_notebook.notebook.setAsReal(\"";
+              command += aName;
+              command += "\")";
+              bool aResult;
+              aResult = pyInterp->run(command.c_str());
+              if(aResult)
+                {
+                  return IncorrectType;
+                }
+#endif
+            }
+          value = studyDS->GetReal( aName );
+          return Found;
+        }
+        return IncorrectType;
       }
     }
   }
@@ -350,3 +480,39 @@ void SalomeApp_DoubleSpinBox::showEvent( QShowEvent* )
 {
   setText( myTextValue );
 }
+
+/*!
+  \brief Enables or disables variable names in the spin box.
+         By default, variable names are enabled.
+  \param flag If true, variable names are enabled.
+*/
+void SalomeApp_DoubleSpinBox::setAcceptNames( const bool flag )
+{
+  myAcceptNames = flag;
+}
+
+/*!
+  \brief Returns true if the spin box accepts variable names.
+*/
+bool SalomeApp_DoubleSpinBox::isAcceptNames() const
+{
+  return myAcceptNames;
+}
+
+/*!
+  \brief Enables or disables  tooltips in case of invalid or intermediate-state input.
+         Tooltips are enabled by default.
+  \param flag If true, tooltips are enabled.
+*/
+void SalomeApp_DoubleSpinBox::setShowTipOnValidate( const bool flag )
+{
+  myShowTip = flag;
+}
+
+/*!
+  \brief Returns true if tooltip should be shown in case of invalid or intermediate-state input.
+*/
+bool SalomeApp_DoubleSpinBox::isShowTipOnValidate() const
+{
+  return myShowTip;
+}