Salome HOME
Unicode support: correct handling of unicode on GUI level
[modules/gui.git] / src / Qtx / Qtx.cxx
index 0dc508b54c2a4864c4659a58f67102f528ea6aa2..8d98abdb5f8977230b0d71880ec88eb592d24033 100755 (executable)
@@ -1,4 +1,4 @@
-// Copyright (C) 2007-2014  CEA/DEN, EDF R&D, OPEN CASCADE
+// Copyright (C) 2007-2016  CEA/DEN, EDF R&D, OPEN CASCADE
 //
 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
@@ -38,6 +38,9 @@
 #include <QApplication>
 #include <QDesktopWidget>
 #include <QtDebug>
+#if QT_VERSION > QT_VERSION_CHECK(5, 0, 0)
+#include <QSurfaceFormat>
+#endif
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -277,26 +280,31 @@ void Qtx::simplifySeparators( QWidget* wid, const bool recursive )
   if ( items.isEmpty() )
     return;
 
-  QList<QAction*> toRemove;
-  for ( int i = 1; i < items.count(); i++ )
+  bool action = false;
+  for ( int i = 0; i < items.count(); i++ )
   {
-    if ( items[i]->isSeparator() && items[i - 1]->isSeparator() )
-      toRemove.append( items[i] );
-
-    if ( recursive && items[i]->menu() )
-      simplifySeparators( items[i]->menu(), recursive );
+    QAction* a = items[i];
+    if ( a->isSeparator() ) {
+      a->setVisible(action);
+      action = false;
+    }
+    else if ( a->isVisible() ) {
+      action = true;
+      if ( recursive && a->menu() )
+       simplifySeparators( a->menu(), recursive );
+    }
   }
 
-  for ( QList<QAction*>::iterator it = toRemove.begin(); it != toRemove.end(); ++it )
-    wid->removeAction( *it );
-
-  items = wid->actions();
-  if ( !items.isEmpty() && items[0]->isSeparator() )
-    wid->removeAction( items[0] );
-
-  items = wid->actions();
-  if ( !items.isEmpty() && items[items.count() - 1]->isSeparator() )
-    wid->removeAction( items[items.count() - 1] );
+  action = false;
+  for ( int i = items.count() - 1; i > 0; i-- ) {
+    QAction* a = items[i];
+    if ( a->isSeparator() ) {
+      a->setVisible(action);
+      action = false;
+    }
+    else if ( a->isVisible() )
+      action = true;
+  }
 }
 
 /*!
@@ -429,13 +437,15 @@ QString Qtx::library( const QString& str )
     name = QString( "lib" ) + name;
 #endif
 
-#ifdef WIN32
+#if defined(WIN32)
   QString libExt( "dll" );
+#elif defined(__APPLE__)
+  QString libExt( "dylib" );
 #else
   QString libExt( "so" );
 #endif
 
-  if ( ext.toLower() != QString( "so" ) && ext.toLower() != QString( "dll" ) )
+  if ( ext.toLower() != QString( "so" ) && ext.toLower() != QString( "dll" ) && ext.toLower() != QString( "dylib" ) )
   {
     if ( !name.isEmpty() && !ext.isEmpty() )
       name += QString( "." );
@@ -533,14 +543,14 @@ QString Qtx::addSlash( const QString& path )
 */
 bool Qtx::dos2unix( const QString& absName )
 {
-  FILE* src = ::fopen( absName.toLatin1(), "rb" );
+  FILE* src = ::fopen( absName.toUtf8(), "rb" );
   if ( !src )
     return false;
 
   /* we'll use temporary file */
   char temp[512] = { '\0' };
   QString dir = Qtx::dir( absName );
-  FILE* tgt = ::fopen( strcpy( temp, ::tempnam( dir.toLatin1(), "__x" ) ), "wb" );
+  FILE* tgt = ::fopen( strcpy( temp, ::tempnam( dir.toUtf8(), "__x" ) ), "wb" );
   if ( !tgt )
     return false;
 
@@ -651,11 +661,17 @@ QCompleter* Qtx::pathCompleter( const PathType type, const QString& filter )
 /*!
   \brief Parse given string to retrieve environment variable.
 
-  Looks through the string for the patterns: ${name} or $(name) or %name%.
+  Looks through the string for the environment variable patterns.
   If string contains variable satisfying any pattern, the variable name
   is returned, start index of the variable is returned in the \a start parameter,
   and length of the variable is returned in the \a len parameter.
 
+  Supported environment variables definitions:
+  - ${name} or $name : Linux shell variable
+  - $(name)          : GNU make substitution
+  - %name%           : Windows shell variable
+  - %(name)s         : Python substitutions:
+
   \param str string being processed
   \param start if variable is found, this parameter contains its starting 
          position in the \a str
@@ -667,30 +683,22 @@ QString Qtx::findEnvVar( const QString& str, int& start, int& len )
   QString varName;
   len = 0;
 
-  QRegExp rx( "(^\\$\\{|[^\\$]\\$\\{)([a-zA-Z]+[a-zA-Z0-9_]*)(\\})|(^\\$\\(|[^\\$]\\$\\()([a-zA-Z]+[a-zA-Z0-9_]*)(\\))|(^\\$|[^\\$]\\$)([a-zA-Z]+[a-zA-Z0-9_]*)|(^%|[^%]%)([a-zA-Z]+[a-zA-Z0-9_]*)(%[^%]|%$)" );
-
-  int pos = rx.indexIn( str, start );
-  if ( pos != -1 )
+  QStringList rxList;
+  rxList << "\\$\\{([a-zA-Z][a-zA-Z_0-9]*)\\}"; // ${name}
+  rxList << "\\$([a-zA-Z][a-zA-Z_0-9]*)";       // $name
+  rxList << "\\$\\(([a-zA-Z][a-zA-Z_0-9]*)\\)"; // $(name)
+  rxList << "%([a-zA-Z][a-zA-Z0-9_]*)%";        // %name%
+  rxList << "%\\(([a-zA-Z][a-zA-Z_0-9]*)\\)s";  // %(name)s
+  
+  for ( int i = 0; i < rxList.count() && varName.isEmpty(); ++i ) 
   {
-    int i = 1;
-    while ( i <= rx.numCaptures() && varName.isEmpty() )
+    QRegExp rx(rxList[i]);
+    int pos = rx.indexIn( str, start );
+    if ( pos != -1 )
     {
-      QString capStr = rx.cap( i );
-      if ( !capStr.contains( "%" ) && !capStr.contains( "$" ) )
-        varName = capStr;
-      i++;
-    }
-
-    if ( !varName.isEmpty() )
-    {
-      int capIdx = i - 1;
-      start = rx.pos( capIdx );
-      int end = start + varName.length();
-      if ( capIdx > 1 && rx.cap( capIdx - 1 ).contains( QRegExp( "\\$|%" ) ) )
-        start = rx.pos( capIdx - 1 ) + rx.cap( capIdx - 1 ).indexOf( QRegExp( "\\$|%" ) );
-      if ( capIdx < rx.numCaptures() && !rx.cap( capIdx - 1 ).isEmpty() )
-        end++;
-      len = end - start;
+      varName = rx.cap( 1 );
+      start = pos;
+      len = rx.matchedLength();
     }
   }
   return varName;
@@ -719,8 +727,8 @@ QString Qtx::makeEnvVarSubst( const QString& str, const SubstMode mode )
         break;
 
       QString newStr;
-      if ( ::getenv( envName.toLatin1() ) || mode == Always )
-        newStr = QString( ::getenv( envName.toLatin1() ) );
+      if ( ::getenv( envName.toUtf8() ) || mode == Always )
+        newStr = QString( ::getenv( envName.toUtf8() ) );
 
       if ( newStr.isNull() )
       {
@@ -863,7 +871,7 @@ QImage Qtx::grayscale( const QImage& img )
 {
   QImage res = img;
 
-  int colNum = res.numColors();
+  int colNum = res.colorCount();
   if ( colNum )
   {
     for ( int i = 0; i < colNum; i++ )
@@ -1991,9 +1999,122 @@ void Qtx::BackgroundData::setGradient( const QGradient& grad )
   myGradient = grad;
   setMode( Qtx::CustomGradientBackground );
 }
+
+/*!
+  \brief Convert string representation of version identifier to the numerical value.
+  Resulting value can be used for comparison of different versions (lower, higher, equal).
+
+  String representation of the version consists of zero or more components:
+
+  [major[.minor[.release[patchid]]]]
+  where
+  - major is version major number
+  - minor is version minor number
+  - release is version release number
+  - patchid is a version dev identifier which is one of the following
+    * 1 letter optionally followed by 1 or 2 digits, e.g. "a" for "alpha", "b1" for "beta 1"
+    * "rc" optionally followed by 1 or 2 digits, e.g. "rc1" for "release candidate 1"
+    * "dev" for development version (note: 7.4.0dev > 7.4.0, 7.4.0dev < 7.4.1, 7.4.0dev < 7.4.0a1)
+
+  If version string does not include any component or has invalid format, the function returns 0.
+
+  Examples:
+    1.0      - version 1.0
+    1.2.3a   - version 1.2.3 alpha
+    3.3.3b1  - version 3.3.3 beta 1
+    7.4.0rc1 - version 7.4.0 release candidate 1
+    7.4.0dev - dev version, i.e. future version 7.4.1 (or 7.5.0)
+
+  \param version string representation of version
+  \return numerical identifier of the version
+*/
+long Qtx::versionToId( const QString& version )
+{
+  long id = 0;
+
+  QRegExp vers_exp( "^([0-9]+)([A-Z]|RC|DEV)?([0-9]{0,2})$", Qt::CaseInsensitive );
+  
+  QStringList vers = version.split( ".", QString::SkipEmptyParts );
+  int major=0, minor=0;
+  int release = 0, dev1 = 0, dev2 = 0;
+  if ( vers.count() > 0 ) major = vers[0].toInt();
+  if ( vers.count() > 1 ) minor = vers[1].toInt();
+  if ( vers.count() > 2 ) {
+    if ( vers_exp.indexIn( vers[2] ) != -1 ) {
+      release = vers_exp.cap( 1 ).toInt();
+      QString tag = vers_exp.cap( 2 ).toLower();
+      if ( !tag.isEmpty() ) {
+       // patchid is subtracted from version number
+       // a   = 55 --> -(55 * 100) + patch number --> 4500..4599, e.g. 7.4.1a1  -> 704004501
+       // b   = 54 --> -(54 * 100) + patch number --> 4600..4699, e.g. 7.4.1b1  -> 704004601
+       // c   = 53 --> -(53 * 100) + patch number --> 4700..4799, e.g. 7.4.1c1  -> 704004701
+       // ...
+       // z   = 30 --> -( 1 * 100) + patch number --> 7000..7099, e.g. 7.4.1z1  -> 704007001
+       // rc  =  1 --> -( 1 * 100) + patch number --> 9900..9999, e.g. 7.4.1rc1 -> 704009901
+       // dev = -1 --> +( 1 * 100) + patch number --> 0100..0199, e.g. 7.4.1dev -> 704010100
+       // ---
+       // i.e. "a" < "b" < ... < "z" < "rc" < [stable] < "dev"
+       if ( tag == "rc" )
+         dev1 = 1;
+       else if ( tag == "dev" )
+         dev1 = -1;
+       else
+         dev1 = (int)( QChar('z').toLatin1() ) - (int)( tag[ 0 ].toLatin1() ) + 30;
+      }
+      if ( !vers_exp.cap( 3 ).isEmpty() )
+       dev2 = vers_exp.cap( 3 ).toInt();
+    }
+  }
+  
+  int dev = dev1*100-dev2;
+  id = major;
+  id*=100; id+=minor;
+  id*=100; id+=release;
+  id*=10000;
+  id-=dev;
+
+  return id;
+}
+
+/*!
+  \brief Get Qt installation directory
   
+  The function tries to detect qt installation directory by analyzing the system variables in the following order:
+  - QT5_ROOT_DIR
+  - QT4_ROOT_DIR
+  - QT_ROOT_DIR
+  - QTDIR
 
-#ifndef WIN32
+  Optional parameter \a context allows obtaining subdirectory in the Qt installation directory.
+
+  \param context optional sub-directory
+  \return path to the Qt installation directory (or its sub-folder, if \a context is specified)
+*/
+
+QString Qtx::qtDir( const QString& context )
+{
+  const char* vars[] = { "QT5_ROOT_DIR", "QT4_ROOT_DIR", "QT_ROOT_DIR", "QTDIR" };
+  QString qtPath;
+  for (uint i = 0; i < sizeof(vars)/sizeof(vars[0]) && qtPath.isEmpty(); i++ )
+    qtPath = qgetenv( vars[i] );
+  if ( !qtPath.isEmpty() && !context.isEmpty() )
+    qtPath = QDir( qtPath ).absoluteFilePath( context );
+  return qtPath;
+}
+
+/*!
+  Creates font from string description
+*/
+QFont Qtx::stringToFont( const QString& fontDescription )
+{
+  QFont font;
+  if ( fontDescription.trimmed().isEmpty() || !font.fromString( fontDescription ) )
+    font = QFont( "Courier", 11 );
+  return font;
+}
+
+#if !defined WIN32 && !defined __APPLE__ 
 
 #include <X11/Xlib.h>
 #include <GL/glx.h>
@@ -2041,6 +2162,7 @@ Qt::HANDLE Qtx::getVisual()
     GLX_RGBA,           // Needs to support OpenGL
     GLX_DEPTH_SIZE, 16, // Needs to support a 16 bit depth buffer
     GLX_DOUBLEBUFFER,   // Needs to support double-buffering
+    GLX_STEREO,         // Needs to support stereo rendering
     GLX_STENCIL_SIZE, 1,
     None                // end of list
   };
@@ -2059,4 +2181,98 @@ Qt::HANDLE Qtx::getVisual()
  
   return res;
 }
+
 #endif // WIN32
+
+
+#if QT_VERSION > QT_VERSION_CHECK(5, 0, 0)
+/*!
+  \brief Set default QSurfaceFormat for an application.
+
+  This application property should be set before a creation of the QApplication.
+*/  
+void Qtx::initDefaultSurfaceFormat()
+{
+  // Settings from Paraview: 
+  // This piece of code was taken from QVTKOpenGLWidget::defaultFormat() method in
+  // order to avoid dependency of the SALOME_Session_Server on vtk libraries
+  QSurfaceFormat fmt;
+  fmt.setRenderableType(QSurfaceFormat::OpenGL);
+  fmt.setVersion(3, 2);
+  fmt.setProfile(QSurfaceFormat::CoreProfile);
+  fmt.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
+  fmt.setRedBufferSize(1);
+  fmt.setGreenBufferSize(1);
+  fmt.setBlueBufferSize(1);
+  fmt.setDepthBufferSize(1);
+  fmt.setStencilBufferSize(0);
+  fmt.setAlphaBufferSize(1);
+  fmt.setStereo(false);
+  fmt.setSamples(0);
+  
+  // Settings for OCCT viewer window:
+  fmt.setDepthBufferSize(16);
+  fmt.setStencilBufferSize(1);
+  //  fmt.setProfile(QSurfaceFormat::CompatibilityProfile);
+
+  QSurfaceFormat::setDefaultFormat(fmt);
+}
+#endif
+
+/*!
+  \class Qtx::CmdLineArgs
+  \brief Get access to the command line arguments in the C-like manner.
+
+  This class translates command line arguments stored in QApplication in form of QStrlingList
+  to the char* array, in the same way as they specified to main() function.
+
+  Constructor of class allocates required memory to store arguments; destructor deallocates it,
+  This allows using this class as a local variable:
+
+  \code
+  Qtx::CmdLineArgs args;
+  some_function(args.argc(), args.argv()); // function that has main()-like syntax.
+  \endcode
+*/
+
+/*!
+  \brief Default constructor.
+*/
+Qtx::CmdLineArgs::CmdLineArgs()
+{
+  QStringList args = QCoreApplication::arguments();
+  myArgc = args.size();
+  myArgv = new char*[myArgc];
+  for ( int i = 0; i < myArgc; i++ ) {
+    QByteArray ba = args[i].toUtf8();
+    myArgv[i] = qstrdup(ba.constData());
+  }
+}
+
+/*!
+  \brief Destructor. Deallocates the array with command line arguments
+*/
+Qtx::CmdLineArgs::~CmdLineArgs()
+{
+  for ( int i = 0; i < myArgc; i++ )
+    delete[] myArgv[i];
+  delete[] myArgv;
+}
+
+/*!
+  \brief Get number of command line arguments
+  \return number of arguments
+*/
+int Qtx::CmdLineArgs::argc() const
+{
+  return myArgc;
+}
+
+/*!
+  \brief Get command line arguments
+  \return command line arguments
+*/
+char** Qtx::CmdLineArgs::argv() const
+{
+  return myArgv;
+}