Salome HOME
Unicode support: correct handling of unicode on GUI level
[modules/gui.git] / src / CAM / CAM_Application.cxx
index 354394bfb53e3efe7576061e6f1f8295667ba5c8..7721f9f62f1568d1bbb7af9bab8a360ec6a4d432 100755 (executable)
@@ -1,24 +1,25 @@
-//  Copyright (C) 2007-2008  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
+// Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
+// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
 //
-//  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 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, 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.
+// 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
-//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+// 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
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
 //
+
 #include "CAM_Application.h"
 
 #include "CAM_Study.h"
@@ -30,6 +31,9 @@
 #include <SUIT_MessageBox.h>
 #include <SUIT_ResourceMgr.h>
 
+#include <KERNEL_version.h>
+#include <GUI_version.h>
+
 #include <QApplication>
 #include <QRegExp>
 
 #include <dlfcn.h>
 #endif
 
+#include <cstdio>
+#include <iostream>
+
+namespace
+{
+class BusyLocker
+{
+public:
+  BusyLocker( bool& busy ) : myPrev( busy ), myBusy( busy ) { myBusy = true; }
+  ~BusyLocker() { myBusy = myPrev; }
+private:
+  bool  myPrev;
+  bool& myBusy;
+};
+}
+
 /*!
   \brief Create new instance of CAM_Application.
   \return new instance of CAM_Application class
@@ -63,6 +83,8 @@ extern "C" CAM_EXPORT SUIT_Application* createApplication()
   - etc
 */
 
+CAM_Application::ModuleInfoList CAM_Application::myInfoList;
+
 /*!
   \brief Constructor.
 
@@ -76,7 +98,8 @@ extern "C" CAM_EXPORT SUIT_Application* createApplication()
 CAM_Application::CAM_Application( const bool autoLoad )
 : STD_Application(),
   myModule( 0 ),
-  myAutoLoad( autoLoad )
+  myAutoLoad( autoLoad ),
+  myBlocked( false )
 {
   readModuleList();
 }
@@ -88,6 +111,9 @@ CAM_Application::CAM_Application( const bool autoLoad )
 */
 CAM_Application::~CAM_Application()
 {
+  for ( QList<CAM_Module*>::const_iterator it = myModules.begin(); it != myModules.end(); ++it )
+    delete *it;
+  myModules.clear();
 }
 
 /*! 
@@ -99,6 +125,15 @@ CAM_Application::~CAM_Application()
 */
 void CAM_Application::start()
 {
+  // check modules
+  for ( ModuleInfoList::iterator it = myInfoList.begin(); 
+        it != myInfoList.end(); ++it )
+  {
+    if ( (*it).status == stUnknown )
+      (*it).status = checkModule( (*it).title ) ? stReady : stInaccessible;
+  }
+  
+  // auto-load modules
   if ( myAutoLoad )
     loadModules();
 
@@ -122,7 +157,7 @@ CAM_Module* CAM_Application::module(  const QString& modName ) const
 {
   CAM_Module* mod = 0;
   for ( QList<CAM_Module*>::const_iterator it = myModules.begin(); 
-       it != myModules.end() && !mod; ++it )
+        it != myModules.end() && !mod; ++it )
     if ( (*it)->moduleName() == modName )
       mod = *it;
   return mod;
@@ -146,7 +181,7 @@ void CAM_Application::modules( CAM_Application::ModuleList& out ) const
   out.clear();
 
   for ( QList<CAM_Module*>::const_iterator it = myModules.begin(); 
-       it != myModules.end(); ++it )
+        it != myModules.end(); ++it )
     out.append( *it );
 }
 
@@ -166,13 +201,13 @@ void CAM_Application::modules( QStringList& lst, const bool loaded ) const
   if ( loaded )
   {
     for ( QList<CAM_Module*>::const_iterator it = myModules.begin(); 
-         it != myModules.end(); ++it )
+          it != myModules.end(); ++it )
       lst.append( (*it)->moduleName() );
   }
   else
   {
     for ( ModuleInfoList::const_iterator it = myInfoList.begin(); 
-         it != myInfoList.end(); ++it )
+          it != myInfoList.end(); ++it )
       lst.append( (*it).title );
   }
 }
@@ -197,7 +232,7 @@ void CAM_Application::addModule( CAM_Module* mod )
 
   ModuleList newList;
   for ( ModuleInfoList::const_iterator it = myInfoList.begin(); 
-       it != myInfoList.end(); ++it )
+        it != myInfoList.end(); ++it )
   {
     if ( (*it).title == mod->moduleName() )
       newList.append( mod );
@@ -210,7 +245,7 @@ void CAM_Application::addModule( CAM_Module* mod )
   }
 
   for ( QList<CAM_Module*>::const_iterator it = myModules.begin();
-       it != myModules.end(); ++it )
+        it != myModules.end(); ++it )
   {
     if ( !newList.contains( *it ) )
       newList.append( *it );
@@ -233,15 +268,18 @@ void CAM_Application::loadModules()
 {
   for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end(); ++it )
   {
+    if ( !isModuleAccessible( (*it).title ) ) {
+      continue;
+    }
     CAM_Module* mod = loadModule( (*it).title );
     if ( mod )
       addModule( mod );
     else {
+      QString wrn = tr( "Can not load module %1" ).arg( (*it).title );
       if ( desktop() && desktop()->isVisible() )
-       SUIT_MessageBox::critical( desktop(), tr( "Loading modules" ),
-                                  tr( "Can not load module %1" ).arg( (*it).title ) );
+        SUIT_MessageBox::critical( desktop(), tr( "Loading modules" ), wrn );
       else
-       qWarning( tr( "Can not load module %1" ).arg( (*it).title ).toLatin1().data() ); 
+        qWarning( qPrintable( wrn ) ); 
     }
   }
 }
@@ -261,22 +299,28 @@ CAM_Module* CAM_Application::loadModule( const QString& modName, const bool show
 {
   if ( myInfoList.isEmpty() )
   {
-    qWarning( tr( "Modules configuration is not defined." ).toLatin1().data() );
+    qWarning( qPrintable( tr( "Modules configuration is not defined." ) ) );
+    return 0;
+  }
+
+  if ( !isModuleAccessible( modName ) ) {
+    qWarning( qPrintable( tr( "Module \"%1\" cannot be loaded in this application." ).arg( modName ) ) );
     return 0;
   }
 
   QString libName = moduleLibrary( modName );
   if ( libName.isEmpty() )
   {
-    qWarning( tr( "Information about module \"%1\" doesn't exist." ).arg( modName ).toLatin1().data() );
+    qWarning( qPrintable( tr( "Information about module \"%1\" doesn't exist." ).arg( modName ) ) );
     return 0;
   }
 
   QString err;
   GET_MODULE_FUNC crtInst = 0;
+  GET_VERSION_FUNC getVersion = 0;
 
 #ifdef WIN32
-  HINSTANCE modLib = ::LoadLibrary( libName.toLatin1() ); 
+  HINSTANCE modLib = ::LoadLibrary( libName.toUtf8() ); 
   if ( !modLib )
   {
     LPVOID lpMsgBuf;
@@ -296,9 +340,11 @@ CAM_Module* CAM_Application::loadModule( const QString& modName, const bool show
     err = QString( "Failed to find  %1 function. %2" ).arg( GET_MODULE_NAME ).arg( (LPTSTR)lpMsgBuf );
     ::LocalFree( lpMsgBuf );
     }
+
+    getVersion = (GET_VERSION_FUNC)::GetProcAddress( modLib, GET_VERSION_NAME );
   }
 #else
-  void* modLib = dlopen( libName.toLatin1(), RTLD_LAZY );
+  void* modLib = dlopen( libName.toUtf8(), RTLD_LAZY | RTLD_GLOBAL );
   if ( !modLib )
     err = QString( "Can not load library %1. %2" ).arg( libName ).arg( dlerror() );
   else
@@ -306,6 +352,8 @@ CAM_Module* CAM_Application::loadModule( const QString& modName, const bool show
     crtInst = (GET_MODULE_FUNC)dlsym( modLib, GET_MODULE_NAME );
     if ( !crtInst )
       err = QString( "Failed to find function %1. %2" ).arg( GET_MODULE_NAME ).arg( dlerror() );
+
+    getVersion = (GET_VERSION_FUNC)dlsym( modLib, GET_VERSION_NAME );
   }
 #endif
 
@@ -320,9 +368,22 @@ CAM_Module* CAM_Application::loadModule( const QString& modName, const bool show
     if ( desktop() && desktop()->isVisible() )
       SUIT_MessageBox::warning( desktop(), tr( "Error" ), err );
     else
-      qWarning( err.toLatin1().data() ); 
+      qWarning( qPrintable( err ) ); 
   }
 
+  char* version = getVersion ? getVersion() : 0;
+
+  if ( version ) {    
+    for ( ModuleInfoList::iterator it = myInfoList.begin(); it != myInfoList.end(); ++it ) {
+      if ( (*it).title == modName ) {
+        if( (*it).version.isEmpty() ) {
+          (*it).version = QString(version);
+        }
+        break;
+      }
+    }
+  }
+  
   return module;
 }
 
@@ -333,18 +394,20 @@ CAM_Module* CAM_Application::loadModule( const QString& modName, const bool show
 */
 bool CAM_Application::activateModule( const QString& modName )
 {
-  if ( !modName.isEmpty() && !activeStudy() )
+  if ( (!modName.isEmpty() && !activeStudy()) || myBlocked )
     return false;
 
+  // VSR 25/10/2011: prevent nested activation/deactivation
+  // See issues 0021307, 0021373
+  BusyLocker lock( myBlocked );
+
   bool res = false;
   if ( !modName.isEmpty() )
   {
     CAM_Module* mod = module( modName );
-    if ( !mod && !moduleLibrary( modName ).isEmpty() )
-    {
+    if ( !mod )
       mod = loadModule( modName );
-      addModule( mod );
-    }
+    addModule( mod );
 
     if ( mod )
       res = activateModule( mod );
@@ -377,7 +440,7 @@ bool CAM_Application::activateModule( CAM_Module* mod )
     {
       // ....      
     }    
-  }    
+  }     
   myModule = mod;
 
   if ( myModule ){
@@ -387,10 +450,11 @@ bool CAM_Application::activateModule( CAM_Module* mod )
     {
       myModule->setMenuShown( false );
       myModule->setToolShown( false );
+      QString wrn = tr( "ERROR_ACTIVATE_MODULE_MSG" ).arg( myModule->moduleName() );
       if ( desktop() && desktop()->isVisible() )
-       SUIT_MessageBox::critical( desktop(), tr( "ERROR_TLT" ), tr( "ERROR_ACTIVATE_MODULE_MSG" ).arg( myModule->moduleName() ) );
+        SUIT_MessageBox::critical( desktop(), tr( "ERROR_TLT" ), wrn );
       else
-       qWarning( tr( "ERROR_ACTIVATE_MODULE_MSG" ).arg( myModule->moduleName() ).toLatin1().data() ); 
+        qWarning( qPrintable( wrn ) ); 
       myModule = 0;
       return false;
     }
@@ -401,6 +465,67 @@ bool CAM_Application::activateModule( CAM_Module* mod )
   return true;
 }
 
+/*!
+  \brief Load module \a modName and activate its operation, corresponding to \a actionId.
+  This method is dedicated to run operations of some module from any other module.
+  \param modName module name
+  \param actionId is a numerical unique operation identifier
+  \return \c true in case of success and \c false otherwise
+*/
+bool CAM_Application::activateOperation( const QString& modName, int actionId )
+{
+  if (isModuleAccessible(modName)) {
+    CAM_Module* mod = loadModule(modName, false);
+    if (mod) {
+      addModule(mod);
+      return mod->activateOperation(actionId);
+    }
+  }
+  return false;
+}
+
+/*!
+  \brief Load module \a modName and activate its operation, corresponding to \a actionId.
+  This method is dedicated to run operations of some module from any other module.
+  \param modName module name
+  \param actionId is a string unique operation identifier
+  \return \c true in case of success and \c false otherwise
+*/
+bool CAM_Application::activateOperation( const QString& modName, const QString& actionId )
+{
+  if (isModuleAccessible(modName)) {
+    CAM_Module* mod = loadModule(modName, false);
+    if (mod) {
+      addModule(mod);
+      return mod->activateOperation(actionId);
+    }
+  }
+  return false;
+}
+
+/*!
+  \brief Load module \a modName and activate its operation,
+         corresponding to \a actionId and \a pluginName.
+  This method is dedicated to run operations of some module from any other module.
+  \param modName module name
+  \param actionId is a string unique operation identifier
+  \param pluginName is a name of a plugin where the operation is implemented
+  \return \c true in case of success and \c false otherwise
+*/
+bool CAM_Application::activateOperation( const QString& modName,
+                                         const QString& actionId,
+                                         const QString& pluginName )
+{
+  if (isModuleAccessible(modName)) {
+    CAM_Module* mod = loadModule(modName, false);
+    if (mod) {
+      addModule(mod);
+      return mod->activateOperation(actionId, pluginName);
+    }
+  }
+  return false;
+}
+
 /*!
   \brief Create new study.
   \return study object pointer
@@ -434,6 +559,10 @@ void CAM_Application::beforeCloseDoc( SUIT_Study* theDoc )
     (*it)->studyClosed( theDoc );
 }
 
+void CAM_Application::afterCloseDoc()
+{
+}
+
 /*!
   \brief Set active study.
   \param study study to be made active
@@ -443,6 +572,19 @@ void CAM_Application::setActiveStudy( SUIT_Study* study )
   STD_Application::setActiveStudy( study );
 }
 
+/*!
+  \brief Check module availability.
+
+  The method can be redefined in successors. Default implementation returns \c true.
+
+  \param title module title
+  \return \c true if module is accessible; \c false otherwise
+*/
+bool CAM_Application::checkModule( const QString& )
+{
+  return true;
+}
+
 /*!
   \brief Callback function, called when the module is added to the application.
   
@@ -460,7 +602,7 @@ void CAM_Application::moduleAdded( CAM_Module* /*mod*/ )
   \param title module title (user name)
   \return module name or null QString if module is not found
 */
-QString CAM_Application::moduleName( const QString& title ) const
+QString CAM_Application::moduleName( const QString& title )
 {
   QString res;
   for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it )
@@ -476,7 +618,7 @@ QString CAM_Application::moduleName( const QString& title ) const
   \param name module name
   \return module title (user name) or null QString if module is not found
 */
-QString CAM_Application::moduleTitle( const QString& name ) const
+QString CAM_Application::moduleTitle( const QString& name )
 {
   QString res;
   for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it )
@@ -492,7 +634,7 @@ QString CAM_Application::moduleTitle( const QString& name ) const
   \param name module name
   \return module icon or null QString if module is not found
 */
-QString CAM_Application::moduleIcon( const QString& name ) const
+QString CAM_Application::moduleIcon( const QString& name )
 {
   QString res;
   for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isNull(); ++it )
@@ -503,19 +645,53 @@ QString CAM_Application::moduleIcon( const QString& name ) const
   return res;
 }
 
+/*!
+  \brief Returns \c true if module is accessible for the current application.
+  Singleton module can be loaded only in one application object. In other application
+  objects this module will be unavailable.
+  \param title module title (user name)
+  \return \c true if module is accessible (can be loaded) or \c false otherwise
+ */
+bool CAM_Application::isModuleAccessible( const QString& title )
+{
+  bool found   = false;
+  bool blocked = false;
+  bool statusOK = false;
+  
+  QStringList somewhereLoaded;
+  QList<SUIT_Application*> apps = SUIT_Session::session()->applications();
+  foreach( SUIT_Application* app, apps ) {
+    CAM_Application* camApp = dynamic_cast<CAM_Application*>( app );
+    if ( !camApp ) continue;
+    QStringList loaded;
+    camApp->modules( loaded, true );
+    foreach( QString lm, loaded ) {
+      if ( !somewhereLoaded.contains( lm ) ) somewhereLoaded << lm;
+    }
+  }
+
+  for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && !found; ++it )
+  {
+    found = (*it).title == title;
+    blocked = (*it).isSingleton && somewhereLoaded.contains((*it).title);
+    statusOK = (*it).status == stReady;
+  }
+  return found && statusOK && !blocked;
+}
+
 /*!
   \brief Get module library name by its title (user name).
   \param title module title (user name)
   \param full if \c true, return full library name, otherwise return its internal name
   \return module library name or null QString if module is not found
  */
-QString CAM_Application::moduleLibrary( const QString& title, const bool full ) const
+QString CAM_Application::moduleLibrary( const QString& title, const bool full )
 {
   QString res;
   for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it )
   {
     if ( (*it).title == title )
-      res = (*it).internal;
+      res = (*it).library;
   }
   if ( !res.isEmpty() && full )
     res = SUIT_Tools::library( res );
@@ -582,7 +758,7 @@ void CAM_Application::readModuleList()
     QStringList mods = modules.split( QRegExp( "[:|,\\s]" ), QString::SkipEmptyParts );
     for ( int i = 0; i < mods.count(); i++ ) {
       if ( !mods[i].trimmed().isEmpty() )
-       modList.append( mods[i].trimmed() );
+        modList.append( mods[i].trimmed() );
     }
   }
 
@@ -601,12 +777,15 @@ void CAM_Application::readModuleList()
     if ( !moduleTitle( modName ).isEmpty() )
       continue;  // already added
 
+    if ( modName == "KERNEL" || modName == "GUI" )
+      continue; // omit KERNEL and GUI modules
+
     QString modTitle = resMgr->stringValue( *it, "name", QString() );
     if ( modTitle.isEmpty() )
     {
       printf( "****************************************************************\n" );
-      printf( "*    Warning: %s not found in resources.\n", (*it).toLatin1().data() );
-      printf( "*    Module will not be available\n" );
+      printf( "     Warning: module %s is improperly configured!\n", qPrintable(*it) );
+      printf( "     Module %s will not be available in GUI mode!\n", qPrintable(*it) );
       printf( "****************************************************************\n" );
       continue;
     }
@@ -617,8 +796,10 @@ void CAM_Application::readModuleList()
     if ( !modLibrary.isEmpty() )
     {
       modLibrary = SUIT_Tools::file( modLibrary.trimmed() );
-#ifdef WIN32
+#if defined(WIN32)
       QString libExt = QString( "dll" );
+#elif defined(__APPLE__)
+      QString libExt = QString( "dylib" );
 #else
       QString libExt = QString( "so" );
 #endif
@@ -633,11 +814,18 @@ void CAM_Application::readModuleList()
     else
       modLibrary = modName;
 
+    bool aIsSingleton = resMgr->booleanValue( *it, "singleton", false );
+    bool hasGui = resMgr->booleanValue( *it, "gui", true );
+    QString version = resMgr->stringValue( *it, "version", QString() );
+
     ModuleInfo inf;
     inf.name = modName;
     inf.title = modTitle;
-    inf.internal = modLibrary;
+    inf.status = hasGui ? stUnknown : stNoGui;
+    if ( hasGui ) inf.library = modLibrary;
     inf.icon = modIcon;
+    inf.isSingleton = aIsSingleton;
+    inf.version = version;
     myInfoList.append( inf );
   }
 
@@ -646,9 +834,9 @@ void CAM_Application::readModuleList()
       SUIT_MessageBox::warning( desktop(), tr( "Warning" ), tr( "Modules list is empty" ) );
     else
       {
-       printf( "****************************************************************\n" );
-       printf( "*    Warning: modules list is empty.\n" );
-       printf( "****************************************************************\n" );
+        printf( "****************************************************************\n" );
+        printf( "*    Warning: modules list is empty.\n" );
+        printf( "****************************************************************\n" );
       }
   }
 }
@@ -677,3 +865,46 @@ void CAM_Application::createEmptyStudy()
   /*SUIT_Study* study = */activeStudy();
   STD_Application::createEmptyStudy();
 }
+
+/*!
+  \brief Return information about version of the each module.
+*/
+CAM_Application::ModuleShortInfoList CAM_Application::getVersionInfo()
+{
+  ModuleShortInfoList info;
+
+  ModuleShortInfo kernel;
+  kernel.name = "KERNEL";
+  kernel.version = KERNEL_VERSION_STR;
+  info.append(kernel);
+
+  ModuleShortInfo gui;
+  gui.name = "GUI";
+  gui.version = GUI_VERSION_STR;
+  info.append(gui);
+
+  for(int i = 0; i < myInfoList.size(); i++) {
+    ModuleShortInfo infoItem;
+    infoItem.name = myInfoList.at(i).title;
+    infoItem.version = myInfoList.at(i).version;
+    info.append(infoItem);
+  }  
+  return info;
+}
+
+/*!
+  \brief Abort active operations if there are any
+  Iterates through all modules and asks each of them if there are pending operations that cannot be aborted.
+  \return \c false if some operation cannot be aborted
+*/
+bool CAM_Application::abortAllOperations()
+{
+  bool aborted = true;
+  for ( QList<CAM_Module*>::const_iterator it = myModules.begin(); it != myModules.end() && aborted; ++it )
+  {
+    aborted = (*it)->abortAllOperations();
+  }
+  return aborted;
+}