X-Git-Url: http://git.salome-platform.org/gitweb/?a=blobdiff_plain;ds=sidebyside;f=src%2FCAM%2FCAM_Application.cxx;h=7a9273171212677bbde07bd7cb421edb6b70176f;hb=be4807b0b329645b6e91c0419bd1c7f63a91077c;hp=eb7060abdc573fa8996afa0f0dcf9ce609affbbd;hpb=232b55569765049c7d1cceb5096a45f8584c9369;p=modules%2Fgui.git diff --git a/src/CAM/CAM_Application.cxx b/src/CAM/CAM_Application.cxx old mode 100755 new mode 100644 index eb7060abd..7a9273171 --- a/src/CAM/CAM_Application.cxx +++ b/src/CAM/CAM_Application.cxx @@ -1,36 +1,48 @@ -// Copyright (C) 2005 OPEN CASCADE, CEA/DEN, EDF R&D, PRINCIPIA R&D -// +// Copyright (C) 2007-2023 CEA, EDF, OPEN CASCADE +// +// 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 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/ +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + #include "CAM_Application.h" #include "CAM_Study.h" #include "CAM_Module.h" #include +#include #include #include +#include -#include -#include -#include -#include -#include -#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include #ifdef WIN32 #include @@ -38,98 +50,187 @@ #include #endif -/*!Create new instance of CAM_Application*/ +#include +#include + +#include + +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 +*/ extern "C" CAM_EXPORT SUIT_Application* createApplication() { return new CAM_Application(); } -/*!Constructor. read module list. - * \param autoLoad - auto load flag. - */ +/*! + \class CAM_Application + \brief Introduces an application class which provides modular architecture. + + This class defines multi-modular application configuration and behaviour. + Each module (CAM_Module) can have own data model, document windows and + viewers, etc. + + An application provides all necessary functionality for modules management, + like + - loading of modules + - modules activation/deactivation + - etc +*/ + +CAM_Application::ModuleInfoList CAM_Application::myInfoList; + +/*! + \brief Constructor. + + Read modules list (from command line or from resource file). + If \a autoLoad parameter is \c true all the modules will be loaded + immediately after application starting, otherwise each module will + be loaded by demand (with activateModule()). + + \param autoLoad auto loading flag +*/ CAM_Application::CAM_Application( const bool autoLoad ) : STD_Application(), -myModule( 0 ), -myAutoLoad( autoLoad ) + myModule( 0 ), + myAutoLoad( autoLoad ), + myBlocked( false ) { readModuleList(); } -/*!Destructor. Do nothing.*/ +/*! + \brief Destructor. + + Does nothing currently. +*/ CAM_Application::~CAM_Application() { + for ( QList::const_iterator it = myModules.begin(); it != myModules.end(); ++it ) + delete *it; + myModules.clear(); } -/*! Load modules, if \a myAutoLoad flag is true.\n - * Start application - call start() method from parent class. - */ +/*! + \brief Start an application. + + Load all modules, if "auto loading" flag has been set to \c true. + + \sa 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(); STD_Application::start(); } -/*!Get active module. - * \retval CAM_Module - active module. - */ +/*! + \brief Get active module. + \return active module or 0 if there are no any +*/ CAM_Module* CAM_Application::activeModule() const { return myModule; } -/*!Get module with name \a modName from modules list. - * \retval CAM_Module pointer - module. - */ +/*! + \brief Get the module with specified name. + \return module or 0 if not found +*/ CAM_Module* CAM_Application::module( const QString& modName ) const { CAM_Module* mod = 0; - for ( ModuleListIterator it( myModules ); it.current() && !mod; ++it ) - if ( it.current()->moduleName() == modName ) - mod = it.current(); + for ( QList::const_iterator it = myModules.begin(); + it != myModules.end() && !mod; ++it ) + if ( (*it)->moduleName() == modName ) + mod = *it; return mod; } -/*!Gets modules iterator.*/ -CAM_Application::ModuleListIterator CAM_Application::modules() const +/*! + \brief Get all loaded modules. + \return list of modules +*/ +CAM_Application::ModuleList CAM_Application::modules() const { - return ModuleListIterator( myModules ); + return myModules; } -/*!Gets modules list. - * \param out - output list of modules. - */ +/*! + \brief Get all loaded modules. + \param returning list of modules +*/ void CAM_Application::modules( CAM_Application::ModuleList& out ) const { - out.setAutoDelete( false ); out.clear(); - for ( ModuleListIterator it( myModules ); it.current(); ++it ) - out.append( it.current() ); + for ( QList::const_iterator it = myModules.begin(); + it != myModules.end(); ++it ) + out.append( *it ); } -/*!Gets list of names for modules.\n - * Get loaded modules names, if \a loaded is true, else \n - * get names from information list. - * \param lst - output list of names. - * \param loaded - boolean flag. - */ +/*! + \brief Get names of all modules. + + Get loaded modules names if \a loaded is \c true, + otherwise get all avaiable modules names. + + \param lst output list of modules names + \param loaded boolean flag, defines what modules names to return +*/ void CAM_Application::modules( QStringList& lst, const bool loaded ) const { lst.clear(); if ( loaded ) - for ( ModuleListIterator it( myModules ); it.current(); ++it ) - lst.append( it.current()->moduleName() ); + { + for ( QList::const_iterator it = myModules.begin(); + it != myModules.end(); ++it ) + lst.append( (*it)->moduleName() ); + } else - for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end(); ++it ) - lst.append( (*it).title ); + { + for ( ModuleInfoList::const_iterator it = myInfoList.begin(); + it != myInfoList.end(); ++it ) + if ( (*it).status != stNoGui ) + lst.append( (*it).title ); + } } -/*!Adding module \a mod to list. - *\param mod - module. - */ +/*! + \brief Add module \a mod to the modules list. + + Performes module initialization. Does nothing if the module + is already added. + + \param mod module being added + \sa CAM_Module::initialize() +*/ void CAM_Application::addModule( CAM_Module* mod ) { if ( !mod || myModules.contains( mod ) ) @@ -140,7 +241,8 @@ void CAM_Application::addModule( CAM_Module* mod ) QMap map; ModuleList newList; - for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end(); ++it ) + for ( ModuleInfoList::const_iterator it = myInfoList.begin(); + it != myInfoList.end(); ++it ) { if ( (*it).title == mod->moduleName() ) newList.append( mod ); @@ -150,17 +252,16 @@ void CAM_Application::addModule( CAM_Module* mod ) if ( curMod ) newList.append( curMod ); } - if ( !newList.isEmpty() ) - map.insert( newList.getLast(), 0 ); } - for ( ModuleListIterator itr( myModules ); itr.current(); ++itr ) + for ( QList::const_iterator it = myModules.begin(); + it != myModules.end(); ++it ) { - if ( !map.contains( itr.current() ) ) - newList.append( itr.current() ); + if ( !newList.contains( *it ) ) + newList.append( *it ); } - if ( !map.contains( mod ) ) + if ( !newList.contains( mod ) ) newList.append( mod ); myModules = newList; @@ -168,9 +269,11 @@ void CAM_Application::addModule( CAM_Module* mod ) moduleAdded( mod ); } -/*!Load modules from information list. - * \warning If some of modules not loaded, error message appear on desktop. - */ +/*! + \brief Load modules from the modules information list. + + If some module can not be loaded, an error message is shown. +*/ void CAM_Application::loadModules() { for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end(); ++it ) @@ -178,44 +281,69 @@ void CAM_Application::loadModules() CAM_Module* mod = loadModule( (*it).title ); if ( mod ) addModule( mod ); - else - SUIT_MessageBox::error1( desktop(), tr( "Loading modules" ), - tr( "Can not load module %1" ).arg( (*it).title ), tr( "Ok" ) ); + else { + QString wrn = tr( "Can not load module %1" ).arg( (*it).title ); + if ( desktop() && desktop()->isVisible() ) + SUIT_MessageBox::critical( desktop(), tr( "Loading modules" ), wrn ); + else + qWarning( qPrintable( wrn ) ); + } } } -/*!Load module with name \a modName. - *\param modName - module name for loading. - *\warning If information list is empty. - *\warning If module library (for module with \a modName) is empty. - *\warning If module library is not loaded. - */ -CAM_Module* CAM_Application::loadModule( const QString& modName ) +/*! + \brief Load module \a modName. + + The function prints warning message if: + - modules information list is empty + - modules information list does not include specified module info + - module library can not be loaded by some reason + + \param modName module name + \return module object pointer or 0 if module could not be loaded +*/ +CAM_Module* CAM_Application::loadModule( const QString& modName, const bool showMsg ) { if ( myInfoList.isEmpty() ) { - qWarning( tr( "Modules configuration is not defined." ) ); + qWarning( qPrintable( tr( "Modules configuration is not defined." ) ) ); return 0; } QString libName = moduleLibrary( modName ); if ( libName.isEmpty() ) { - qWarning( tr( "Information about module \"%1\" doesn't exist." ).arg( modName ) ); + 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 ); +#ifdef UNICODE + LPTSTR str_libname = new TCHAR[libName.length() + 1]; + str_libname[libName.toWCharArray(str_libname)] = '\0'; +#else + QByteArray arr = libName.toLatin1(); + LPTSTR str_libname = arr.constData(); +#endif + HINSTANCE modLib = ::LoadLibrary( str_libname ); +#ifdef UNICODE + delete str_libname; +#endif if ( !modLib ) { LPVOID lpMsgBuf; ::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, ::GetLastError(), 0, (LPTSTR)&lpMsgBuf, 0, 0 ); - err = QString( "Failed to load %1. %2" ).arg( libName ).arg( (LPTSTR)lpMsgBuf ); +#ifdef UNICODE + QString out_err = QString::fromWCharArray((LPTSTR)lpMsgBuf); +#else + QString out_err = (LPTSTR)lpMsgBuf; +#endif + err = QString( "Failed to load %1. %2" ).arg( libName ).arg(out_err); ::LocalFree( lpMsgBuf ); } else @@ -226,12 +354,20 @@ CAM_Module* CAM_Application::loadModule( const QString& modName ) LPVOID lpMsgBuf; ::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, ::GetLastError(), 0, (LPTSTR)&lpMsgBuf, 0, 0 ); - err = QString( "Failed to find %1 function. %2" ).arg( GET_MODULE_NAME ).arg( (LPTSTR)lpMsgBuf ); - ::LocalFree( lpMsgBuf ); +#ifdef UNICODE + QString out_err = QString::fromWCharArray((LPTSTR)lpMsgBuf); +#else + QString out_err = (LPTSTR)lpMsgBuf; +#endif + + err = QString( "Failed to find %1 function. %2" ).arg( GET_MODULE_NAME ).arg( out_err ); + ::LocalFree( lpMsgBuf ); } + + getVersion = (GET_VERSION_FUNC)::GetProcAddress( modLib, GET_VERSION_NAME ); } #else - void* modLib = dlopen( (char*)libName.latin1(), 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 @@ -239,6 +375,8 @@ CAM_Module* CAM_Application::loadModule( const QString& modName ) 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 @@ -249,35 +387,59 @@ CAM_Module* CAM_Application::loadModule( const QString& modName ) module->setName( moduleName( modName ) ); } - if ( !err.isEmpty() ) - SUIT_MessageBox::warn1( desktop(), tr( "Error" ), err, tr( "Ok" ) ); + if ( !err.isEmpty() && showMsg ) { + if ( desktop() && desktop()->isVisible() ) + SUIT_MessageBox::warning( desktop(), tr( "Error" ), err ); + else + 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; } -/**@name Activate module group.*/ -//@{ -/*!Activate module with name \a modName. - *\param modName - module name. - *\ratval true, if module loaded and activated successful, else false. - */ +/*! + \brief Activate module \a modName. + \param modName module name + \return \c true, if module is loaded and activated successfully and \c false otherwise +*/ 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 ); + + QString name = modName; + if ( !name.isEmpty() && !moduleTitle( modName ).isEmpty() ) + name = moduleTitle( modName ); + bool res = false; - if ( !modName.isEmpty() ) + if ( !name.isEmpty() ) { - CAM_Module* mod = module( modName ); - if ( !mod && !moduleLibrary( modName ).isEmpty() ) - { - mod = loadModule( modName ); - addModule( mod ); - } + setProperty("activateModule", true); + CAM_Module* mod = module( name ); + if ( !mod ) + mod = loadModule( name ); + addModule( mod ); if ( mod ) res = activateModule( mod ); + setProperty("activateModule", QVariant()); } else res = activateModule( 0 ); @@ -285,11 +447,14 @@ bool CAM_Application::activateModule( const QString& modName ) return res; } -/*!Activate module \a mod - *\param mod - module for activation. - *\retval true - if all sucessful. - *\warning Error message if module not activated in active study. - */ +/*! + \brief Activate module \a mod. + + Shows error message if module could not be activated in the current study. + + \param mod module object pointer + \return \c true, if module is loaded and activated successfully and \c false otherwise +*/ bool CAM_Application::activateModule( CAM_Module* mod ) { if ( mod && !activeStudy() ) @@ -300,21 +465,37 @@ bool CAM_Application::activateModule( CAM_Module* mod ) if ( myModule ) { - if ( !myModule->deactivateModule( activeStudy() ) ) + if ( myModule->deactivateModule( activeStudy() ) ) { - // .... - } - } + logStructuredUserEvent( myModule->moduleName(), + "", + "", + "deactivated" ); + } + moduleDeactivated( myModule ); + } myModule = mod; - if ( myModule ){ + if ( myModule ) + { // Connect the module to the active study myModule->connectToStudy( dynamic_cast( activeStudy() ) ); - if ( !myModule->activateModule( activeStudy() ) ) + if ( myModule->activateModule( activeStudy() ) ) + { + logStructuredUserEvent( myModule->moduleName(), + "", + "", + "activated" ); + } + else { myModule->setMenuShown( false ); myModule->setToolShown( false ); - SUIT_MessageBox::error1( desktop(), tr( "ERROR_TLT" ), tr( "ERROR_ACTIVATE_MODULE_MSG" ).arg( myModule->moduleName() ), tr( "BUT_OK" ) ); + QString wrn = tr( "ERROR_ACTIVATE_MODULE_MSG" ).arg( myModule->moduleName() ); + if ( desktop() && desktop()->isVisible() ) + SUIT_MessageBox::critical( desktop(), tr( "ERROR_TLT" ), wrn ); + else + qWarning( qPrintable( wrn ) ); myModule = 0; return false; } @@ -324,17 +505,76 @@ bool CAM_Application::activateModule( CAM_Module* mod ) return true; } -//@} -/*!Create new study for current application. - *\retval study pointer. - */ -SUIT_Study* CAM_Application::createNewStudy() -{ +/*! + \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, + const int actionId ) +{ + 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 ) +{ + 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 ) +{ + 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 +*/ +SUIT_Study* CAM_Application::createNewStudy() +{ return new CAM_Study( this ); } -/*!Update commands status for parent class and for current class(if module is active)*/ +/*! + \brief Update menu commands status. +*/ void CAM_Application::updateCommandsStatus() { STD_Application::updateCommandsStatus(); @@ -343,38 +583,75 @@ void CAM_Application::updateCommandsStatus() activeModule()->updateCommandsStatus(); } -/*!Close all modules in study \a theDoc. - *\param theDoc - study - */ +/*! + \brief Prepare application to study closing. + + Closes all modules in study \a theDoc. + + \param theDoc study +*/ void CAM_Application::beforeCloseDoc( SUIT_Study* theDoc ) { - for ( ModuleListIterator it( myModules ); it.current(); ++it ) - it.current()->studyClosed( theDoc ); + for ( QList::iterator it = myModules.begin(); it != myModules.end(); ++it ) + (*it)->studyClosed( theDoc ); } -/*!Sets active study for parent class. - *\param study - study. - */ +void CAM_Application::afterCloseDoc() +{ +} + +/*! + \brief Set active study. + \param study study to be made active +*/ void CAM_Application::setActiveStudy( SUIT_Study* study ) { STD_Application::setActiveStudy( study ); } -/*!Do nothing.*/ -void CAM_Application::moduleAdded( CAM_Module* mod ) +/*! + \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. + + This virtual method can be re-implemented in the successors. Base implementation + does nothing. + + \param mod module being added +*/ +void CAM_Application::moduleAdded( CAM_Module* /*mod*/ ) { -// CAM_Study* study = dynamic_cast( activeStudy() ); -// if ( !study ) -// return; +} + +/*! + \brief Callback function, called when the module is just deactivated. + + This virtual method can be re-implemented in the successors. Base implementation + does nothing. -// study->insertDataModel( mod->dataModel() ); + \param mod module just deactivated +*/ +void CAM_Application::moduleDeactivated( CAM_Module* /*mod*/ ) +{ } -/*!Gets module name by title \a title - *\param title - title name - *\retval QString module name. - */ -QString CAM_Application::moduleName( const QString& title ) const +/*! + \brief Get module name by its title (user name). + \param title module title (user name) + \return module name or null QString if module is not found +*/ +QString CAM_Application::moduleName( const QString& title ) { QString res; for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it ) @@ -385,11 +662,12 @@ QString CAM_Application::moduleName( const QString& title ) const return res; } -/*!Gets module title by module name \a name - *\param name - module name - *\retval QString module title. - */ -QString CAM_Application::moduleTitle( const QString& name ) const +/*! + \brief Get module title (user name) by its name. + \param name module name + \return module title (user name) or null QString if module is not found +*/ +QString CAM_Application::moduleTitle( const QString& name ) { QString res; for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it ) @@ -400,117 +678,421 @@ QString CAM_Application::moduleTitle( const QString& name ) const return res; } -/*!Get library name for module with title \a title. - *\param title - module title name. - *\param full - boolean flag (if true - return full library name, else internal name) - *\retval QString - library name. +/*! + \brief Get module icon name. + \param name module name or title + \return module icon or null QString if module is not found +*/ +QString CAM_Application::moduleIcon( const QString& name ) +{ + QString res; + for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isNull(); ++it ) + { + if ( (*it).name == name || (*it).title == name ) + res = (*it).icon; + } + return res; +} + +/*! + \brief Get module description. + \param name module name or title + \return module description or null QString if description is not provided in config file. +*/ +QString CAM_Application::moduleDescription( const QString& name ) +{ + QString res; + for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isNull(); ++it ) + { + if ( (*it).name == name || (*it).title == name ) + res = tr((*it).description.toUtf8()); + } + return res; +} + +/*! + \brief Get module library name by its title (user name). + \param title module name or title + \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& name, 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; + if ( (*it).name == name || (*it).title == name ) + res = (*it).library; } if ( !res.isEmpty() && full ) res = SUIT_Tools::library( res ); return res; } -/*!Read modules list*/ +/*! + \brief Get displayer proxy for given module, by its title (user name). + \param name module name or title + \return name of module which provides displayer for requested module + */ +QString CAM_Application::moduleDisplayer( const QString& name ) +{ + QString res; + + if ( !name.isEmpty() ) + { + for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it ) + { + if ( (*it).title == name || (*it).name == name ) { + res = (*it).displayer; + if ( res.isEmpty() ) + res = (*it).title; + } + } + } + + return res; +} + +/*! + \brief Read modules information list + + This function first tries to get the modules names list by parsing + the application command line arguments, looking for the + "--modules ( [:...] )" option. + List of modules is separated by colon symbol (":"). + + If "--modules" command line option is not used, the list of modules + is retrieved from the application resource file: parameter "modules" of + the section "launch". + + Then the information about each module (module title (user name), + library name) is retrieved from the corresponding section of resource + file with help of resources manager. + + Shows the warning message, if module information list is empty. + + \sa SUIT_ResourceMgr +*/ void CAM_Application::readModuleList() { if ( !myInfoList.isEmpty() ) return; + // we cannot use own resourceMgr() as this method can be called from constructor SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr(); QStringList modList; - // parse command line arguments - int nbArgs = qApp->argc(); - char** CmdLine = qApp->argv(); - QString CmdStr; - for ( int i = 0; i < nbArgs; i++ ) - { - CmdStr.append(CmdLine[i]); - CmdStr.append(" "); - } - int startId = CmdStr.find("--modules ("); - if ( startId != -1 ) { // application launch with --modules option - startId = CmdStr.find("(", startId); - int stopId = CmdStr.find(" )", startId); - QString ModStr = CmdStr.mid( startId+1, stopId - (startId+1) ).stripWhiteSpace(); - int i = 0; - while ( i < ModStr.length() ) - { - int nextId = ModStr.find( ":", i ); - modList.append( ModStr.mid( i, nextId - i ).stripWhiteSpace() ); - i = nextId + 1; + QString args = QApplication::arguments().join( " " ); + + QRegExp rx1("--modules=([\\w,]*)"); + rx1.setMinimal( false ); + QRegExp rx2("--modules\\s+\\(\\s*(.*)\\s*\\)"); + rx2.setMinimal( true ); + int pos = 0; + while ( 1 ) { + QString modules; + int pos1 = rx1.indexIn( args, pos ); + int pos2 = rx2.indexIn( args, pos ); + if ( pos1 != -1 && pos2 != -1 ) { + modules = pos1 < pos2 ? rx1.cap( 1 ) : rx2.cap(1); + pos = pos1 < pos2 ? pos1 + rx1.matchedLength() : pos2 + rx2.matchedLength(); + } + else if ( pos1 != -1 ) { + modules = rx1.cap( 1 ); + pos = pos1 + rx1.matchedLength(); + } + else if ( pos2 != -1 ) { + modules = rx2.cap( 1 ); + pos = pos2 + rx2.matchedLength(); + } + else { + break; + } + + modList.clear(); + 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() ); } } - else { - QString modStr = resMgr->stringValue( "launch", "modules", QString::null ); - modList = QStringList::split( ",", modStr ); + + if ( modList.isEmpty() ) { + QString mods = resMgr->stringValue( "launch", "modules", QString() ); + modList = mods.split( ",", QString::SkipEmptyParts ); } - for ( QStringList::const_iterator it = modList.begin(); it != modList.end(); ++it ) - { - QString modName = (*it).stripWhiteSpace(); - if ( modName.isEmpty() ) - continue; + // extra modules loaded manually on previous session + // ... - QString modTitle = resMgr->stringValue( *it, QString( "name" ), QString::null ); - if ( modTitle.isEmpty() ) - continue; + foreach ( QString modName, modList ) + appendModuleInfo( modName.trimmed() ); - QString modLibrary = resMgr->stringValue( *it, QString( "library" ), QString::null ).stripWhiteSpace(); - if ( !modLibrary.isEmpty() ) - { - QString libExt; - modLibrary = SUIT_Tools::file( modLibrary.stripWhiteSpace() ); - libExt = QString( "so" ); - if ( SUIT_Tools::extension( modLibrary ).lower() == libExt ) - modLibrary = modLibrary.mid( 0, modLibrary.length() - libExt.length() - 1 ); - libExt = QString( "dll" ); - if ( SUIT_Tools::extension( modLibrary ).lower() == libExt ) - modLibrary = modLibrary.mid( 0, modLibrary.length() - libExt.length() - 1 ); -#ifndef WIN32 - if ( modLibrary.startsWith( "lib" ) ) - modLibrary = modLibrary.mid( 3 ); -#endif - } + if ( myInfoList.isEmpty() ) { + if ( desktop() && desktop()->isVisible() ) + SUIT_MessageBox::warning( desktop(), tr( "Warning" ), tr( "Modules list is empty" ) ); else - modLibrary = modName; + { + printf( "****************************************************************\n" ); + printf( "* Warning: modules list is empty.\n" ); + printf( "****************************************************************\n" ); + } + } +} + +bool CAM_Application::appendModuleInfo( const QString& modName ) +{ + MESSAGE("Start to append module info for a given module name: "); + SCRUTE(modName.toStdString()); + + if ( modName.isEmpty() ) + return false; // empty module name + + if ( !moduleTitle( modName ).isEmpty() ) + return false; // already added - ModuleInfo inf; - inf.name = modName; - inf.title = modTitle; - inf.internal = modLibrary; - myInfoList.append( inf ); + if ( modName == "KERNEL" || modName == "GUI" ) + return false; // skip KERNEL and GUI modules + + // we cannot use own resourceMgr() as this method can be called from constructor + SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr(); + + ModuleInfo inf; + + // module internal name + inf.name = modName; + // module version + inf.version = resMgr->stringValue( modName, "version", QString() ).trimmed(); + // displayer, if module does not have GUI, displayer may be delegated to other module + inf.displayer = resMgr->stringValue( modName, "displayer", QString() ).trimmed(); + + // "gui" option explicitly says that module has GUI + // Now trying to get the "gui" option value, we always get a default one, + // then we can't rely on it. + bool hasGui = resMgr->booleanValue(modName, "gui", false); + + // Additional check if the module actually has a title and icon. + // Module with GUI must explicitly specify title (GUI name). + inf.title = resMgr->stringValue(modName, "name", QString()).trimmed(); + const bool hasTitle = !inf.title.isEmpty(); + SCRUTE(hasGui); + SCRUTE(hasTitle); + if (hasGui && !hasTitle) + { + MESSAGE("Invalid config! The module has gui option, but doesn't have a title."); + return false; } - if ( myInfoList.isEmpty() ) - SUIT_MessageBox::error1( 0, tr( "Error" ), tr( "Can not load modules configuration file " ), tr( "Ok" ) ); + // While we can't rely on gui option, use a title to make a decision about gui. + hasGui = hasTitle; + + // status; if module has GUI, availability will be checked on activation + inf.status = hasGui ? stUnknown : stNoGui; + + if ( hasGui ) + { + // icon + inf.icon = resMgr->stringValue( modName, "icon", QString() ).trimmed(); + // description, for Info panel + inf.description = resMgr->stringValue( modName, "description", QString() ); + // library; if not specified, we use internal module name + inf.library = SUIT_Tools::libraryName( resMgr->stringValue( modName, "library", QString() ).trimmed() ); + if ( inf.library.isEmpty() ) + inf.library = modName; + } + + // At this point we should have only valid inf object. + myInfoList.append(inf); + + SCRUTE(inf.name.toStdString()); + SCRUTE(inf.version.toStdString()); + SCRUTE(inf.displayer.toStdString()); + SCRUTE(inf.status); + SCRUTE(inf.title.toStdString()); + SCRUTE(inf.icon.toStdString()); + SCRUTE(inf.description.toStdString()); + SCRUTE(inf.library.toStdString()); + + return true; } -/*!Add common items for popup menu ( if they are exist ) - *\param type - type of popup menu - *\param thePopup - popup menu - *\param title - title of popup menu - */ -void CAM_Application::contextMenuPopup( const QString& type, QPopupMenu* thePopup, QString& title ) +void CAM_Application::removeModuleInfo( const QString& modName ) +{ + QMutableListIterator it( myInfoList ); + while ( it.hasNext() ) + { + ModuleInfo info = it.next(); + if ( info.name == modName ) + { + it.remove(); + break; + } + } +} + +/*! + \brief Add common menu items to the popup menu. + + Menu items list is defined by the active module. + + \param type popup menu context + \param menu popup menu + \param title popup menu title, which can be set by the module if required +*/ +void CAM_Application::contextMenuPopup( const QString& type, QMenu* menu, QString& title ) { // to do : add common items for popup menu ( if they are exist ) - if ( activeModule() ) - activeModule()->contextMenuPopup( type, thePopup, title ); + if ( activeModule() ) + activeModule()->contextMenuPopup( type, menu, title ); } -/*!Create empty study.*/ +/*! + \brief Create new empty study. +*/ void CAM_Application::createEmptyStudy() { - SUIT_Study* study = activeStudy(); - + /*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.isEmpty() ? myInfoList.at(i).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::const_iterator it = myModules.begin(); it != myModules.end() && aborted; ++it ) + { + aborted = (*it)->abortAllOperations(); + } + return aborted; +} + +/*! + \brief Log GUI event. + \param eventDescription GUI event description. +*/ +void CAM_Application::logUserEvent( const QString& eventDescription ) +{ + static QString guiLogFile; // null string means log file was not initialized yet + static QMutex aGUILogMutex; + + if ( guiLogFile.isNull() ) + { + // log file was not initialized yet, try to do that by parsing command line arguments + guiLogFile = ""; // empty string means initialization was done but log file was not set + QStringList args = QApplication::arguments(); + for ( int i = 1; i < args.count(); i++ ) + { + QRegExp rxs ( "--gui-log-file=(.+)" ); + if ( rxs.indexIn( args[i] ) >= 0 && rxs.capturedTexts().count() > 1 ) + { + QString file = rxs.capturedTexts()[1]; + QFileInfo fi ( file ); + if ( !fi.isDir() && fi.dir().exists() ) + { + guiLogFile = fi.absoluteFilePath(); + if ( fi.exists() ) { + QFile file ( guiLogFile ); + file.remove(); // remove probably existing log file, to start with empty one + } + } + break; + } + } + } + if ( !guiLogFile.isEmpty() ) // non-empty string means log file was already initialized + { + QMutexLocker aLocker( &aGUILogMutex ); + QFile file ( guiLogFile ); + if ( file.open( QFile::Append ) ) // append to log file + { + QDateTime current = QDateTime::currentDateTime(); + QTextStream stream( &file ); + stream << current.toString("yyyyMMdd-hhmmss") + << "," << eventDescription + << endl; + file.close(); + } + } +} + +void CAM_Application::logStructuredUserEvent( const QString& module, + const QString& section, + const QString& action, + const QString& event, + const QString& message ) +{ + const QStringList mes = (QStringList() << module + << section + << action + << event + << message); + + logUserEvent( mes.join( "," ) ); +} + +/*! + \brief Log given action. + \param action GUI action being logged. + \param moduleName optional name of module, owning an action +*/ +void CAM_Application::logAction( QAction* action, const QString& moduleName ) +{ + QString text = action->toolTip(); + if ( text.isEmpty() ) + text = action->text(); + if ( text.isEmpty() ) + text = action->iconText(); + + if ( !text.isEmpty() ) + { + if ( action->isCheckable() ) + { + logStructuredUserEvent ( moduleName, + "", + "toggled", + action->isChecked() ? "on" : "off", + text ); + } + else + { + logStructuredUserEvent ( moduleName, + "", + "triggered", + "", + text ); + } + } +}