1 // Copyright (C) 2007-2019 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 #include "CAM_Application.h"
25 #include "CAM_Study.h"
26 #include "CAM_Module.h"
28 #include <SUIT_Tools.h>
29 #include <SUIT_Desktop.h>
30 #include <SUIT_Session.h>
31 #include <SUIT_MessageBox.h>
32 #include <SUIT_ResourceMgr.h>
34 #include <KERNEL_version.h>
35 #include <GUI_version.h>
37 #include <QApplication>
54 BusyLocker( bool& busy ) : myPrev( busy ), myBusy( busy ) { myBusy = true; }
55 ~BusyLocker() { myBusy = myPrev; }
63 \brief Create new instance of CAM_Application.
64 \return new instance of CAM_Application class
66 extern "C" CAM_EXPORT SUIT_Application* createApplication()
68 return new CAM_Application();
72 \class CAM_Application
73 \brief Introduces an application class which provides modular architecture.
75 This class defines multi-modular application configuration and behaviour.
76 Each module (CAM_Module) can have own data model, document windows and
79 An application provides all necessary functionality for modules management,
82 - modules activation/deactivation
86 CAM_Application::ModuleInfoList CAM_Application::myInfoList;
91 Read modules list (from command line or from resource file).
92 If \a autoLoad parameter is \c true all the modules will be loaded
93 immediately after application starting, otherwise each module will
94 be loaded by demand (with activateModule()).
96 \param autoLoad auto loading flag
98 CAM_Application::CAM_Application( const bool autoLoad )
101 myAutoLoad( autoLoad ),
110 Does nothing currently.
112 CAM_Application::~CAM_Application()
114 for ( QList<CAM_Module*>::const_iterator it = myModules.begin(); it != myModules.end(); ++it )
120 \brief Start an application.
122 Load all modules, if "auto loading" flag has been set to \c true.
124 \sa CAM_Application()
126 void CAM_Application::start()
129 for ( ModuleInfoList::iterator it = myInfoList.begin();
130 it != myInfoList.end(); ++it )
132 if ( (*it).status == stUnknown )
133 (*it).status = checkModule( (*it).title ) ? stReady : stInaccessible;
140 STD_Application::start();
144 \brief Get active module.
145 \return active module or 0 if there are no any
147 CAM_Module* CAM_Application::activeModule() const
153 \brief Get the module with specified name.
154 \return module or 0 if not found
156 CAM_Module* CAM_Application::module( const QString& modName ) const
159 for ( QList<CAM_Module*>::const_iterator it = myModules.begin();
160 it != myModules.end() && !mod; ++it )
161 if ( (*it)->moduleName() == modName )
167 \brief Get all loaded modules.
168 \return list of modules
170 CAM_Application::ModuleList CAM_Application::modules() const
176 \brief Get all loaded modules.
177 \param returning list of modules
179 void CAM_Application::modules( CAM_Application::ModuleList& out ) const
183 for ( QList<CAM_Module*>::const_iterator it = myModules.begin();
184 it != myModules.end(); ++it )
189 \brief Get names of all modules.
191 Get loaded modules names if \a loaded is \c true,
192 otherwise get all avaiable modules names.
194 \param lst output list of modules names
195 \param loaded boolean flag, defines what modules names to return
197 void CAM_Application::modules( QStringList& lst, const bool loaded ) const
203 for ( QList<CAM_Module*>::const_iterator it = myModules.begin();
204 it != myModules.end(); ++it )
205 lst.append( (*it)->moduleName() );
209 for ( ModuleInfoList::const_iterator it = myInfoList.begin();
210 it != myInfoList.end(); ++it )
211 lst.append( (*it).title );
216 \brief Add module \a mod to the modules list.
218 Performes module initialization. Does nothing if the module
221 \param mod module being added
222 \sa CAM_Module::initialize()
224 void CAM_Application::addModule( CAM_Module* mod )
226 if ( !mod || myModules.contains( mod ) )
229 mod->initialize( this );
231 QMap<CAM_Module*, int> map;
234 for ( ModuleInfoList::const_iterator it = myInfoList.begin();
235 it != myInfoList.end(); ++it )
237 if ( (*it).title == mod->moduleName() )
238 newList.append( mod );
241 CAM_Module* curMod = module( (*it).title );
243 newList.append( curMod );
247 for ( QList<CAM_Module*>::const_iterator it = myModules.begin();
248 it != myModules.end(); ++it )
250 if ( !newList.contains( *it ) )
251 newList.append( *it );
254 if ( !newList.contains( mod ) )
255 newList.append( mod );
263 \brief Load modules from the modules information list.
265 If some module can not be loaded, an error message is shown.
267 void CAM_Application::loadModules()
269 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end(); ++it )
271 CAM_Module* mod = loadModule( (*it).title );
275 QString wrn = tr( "Can not load module %1" ).arg( (*it).title );
276 if ( desktop() && desktop()->isVisible() )
277 SUIT_MessageBox::critical( desktop(), tr( "Loading modules" ), wrn );
279 qWarning( qPrintable( wrn ) );
285 \brief Load module \a modName.
287 The function prints warning message if:
288 - modules information list is empty
289 - modules information list does not include specified module info
290 - module library can not be loaded by some reason
292 \param modName module name
293 \return module object pointer or 0 if module could not be loaded
295 CAM_Module* CAM_Application::loadModule( const QString& modName, const bool showMsg )
297 if ( myInfoList.isEmpty() )
299 qWarning( qPrintable( tr( "Modules configuration is not defined." ) ) );
303 QString libName = moduleLibrary( modName );
304 if ( libName.isEmpty() )
306 qWarning( qPrintable( tr( "Information about module \"%1\" doesn't exist." ).arg( modName ) ) );
311 GET_MODULE_FUNC crtInst = 0;
312 GET_VERSION_FUNC getVersion = 0;
316 LPTSTR str_libname = new TCHAR[libName.length() + 1];
317 str_libname[libName.toWCharArray(str_libname)] = '\0';
319 QByteArray arr = libName.toLatin1();
320 LPTSTR str_libname = arr.constData();
322 HINSTANCE modLib = ::LoadLibrary( str_libname );
329 ::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
330 FORMAT_MESSAGE_IGNORE_INSERTS, 0, ::GetLastError(), 0, (LPTSTR)&lpMsgBuf, 0, 0 );
332 QString out_err = QString::fromWCharArray((LPTSTR)lpMsgBuf);
334 QString out_err = (LPTSTR)lpMsgBuf;
336 err = QString( "Failed to load %1. %2" ).arg( libName ).arg(out_err);
337 ::LocalFree( lpMsgBuf );
341 crtInst = (GET_MODULE_FUNC)::GetProcAddress( modLib, GET_MODULE_NAME );
345 ::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
346 FORMAT_MESSAGE_IGNORE_INSERTS, 0, ::GetLastError(), 0, (LPTSTR)&lpMsgBuf, 0, 0 );
348 QString out_err = QString::fromWCharArray((LPTSTR)lpMsgBuf);
350 QString out_err = (LPTSTR)lpMsgBuf;
353 err = QString( "Failed to find %1 function. %2" ).arg( GET_MODULE_NAME ).arg( out_err );
354 ::LocalFree( lpMsgBuf );
357 getVersion = (GET_VERSION_FUNC)::GetProcAddress( modLib, GET_VERSION_NAME );
360 void* modLib = dlopen( libName.toUtf8(), RTLD_LAZY | RTLD_GLOBAL );
362 err = QString( "Can not load library %1. %2" ).arg( libName ).arg( dlerror() );
365 crtInst = (GET_MODULE_FUNC)dlsym( modLib, GET_MODULE_NAME );
367 err = QString( "Failed to find function %1. %2" ).arg( GET_MODULE_NAME ).arg( dlerror() );
369 getVersion = (GET_VERSION_FUNC)dlsym( modLib, GET_VERSION_NAME );
373 CAM_Module* module = crtInst ? crtInst() : 0;
376 module->setModuleName( modName );
377 module->setName( moduleName( modName ) );
380 if ( !err.isEmpty() && showMsg ) {
381 if ( desktop() && desktop()->isVisible() )
382 SUIT_MessageBox::warning( desktop(), tr( "Error" ), err );
384 qWarning( qPrintable( err ) );
387 char* version = getVersion ? getVersion() : 0;
390 for ( ModuleInfoList::iterator it = myInfoList.begin(); it != myInfoList.end(); ++it ) {
391 if ( (*it).title == modName ) {
392 if( (*it).version.isEmpty() ) {
393 (*it).version = QString(version);
404 \brief Activate module \a modName.
405 \param modName module name
406 \return \c true, if module is loaded and activated successfully and \c false otherwise
408 bool CAM_Application::activateModule( const QString& modName )
410 if ( (!modName.isEmpty() && !activeStudy()) || myBlocked )
413 // VSR 25/10/2011: prevent nested activation/deactivation
414 // See issues 0021307, 0021373
415 BusyLocker lock( myBlocked );
418 if ( !modName.isEmpty() )
420 CAM_Module* mod = module( modName );
422 mod = loadModule( modName );
426 res = activateModule( mod );
429 res = activateModule( 0 );
435 \brief Activate module \a mod.
437 Shows error message if module could not be activated in the current study.
439 \param mod module object pointer
440 \return \c true, if module is loaded and activated successfully and \c false otherwise
442 bool CAM_Application::activateModule( CAM_Module* mod )
444 if ( mod && !activeStudy() )
447 if ( myModule == mod )
452 if ( !myModule->deactivateModule( activeStudy() ) )
460 // Connect the module to the active study
461 myModule->connectToStudy( dynamic_cast<CAM_Study*>( activeStudy() ) );
462 if ( !myModule->activateModule( activeStudy() ) )
464 myModule->setMenuShown( false );
465 myModule->setToolShown( false );
466 QString wrn = tr( "ERROR_ACTIVATE_MODULE_MSG" ).arg( myModule->moduleName() );
467 if ( desktop() && desktop()->isVisible() )
468 SUIT_MessageBox::critical( desktop(), tr( "ERROR_TLT" ), wrn );
470 qWarning( qPrintable( wrn ) );
476 updateCommandsStatus();
482 \brief Load module \a modName and activate its operation, corresponding to \a actionId.
483 This method is dedicated to run operations of some module from any other module.
484 \param modName module name
485 \param actionId is a numerical unique operation identifier
486 \return \c true in case of success and \c false otherwise
488 bool CAM_Application::activateOperation( const QString& modName, int actionId )
490 CAM_Module* mod = loadModule(modName, false);
493 return mod->activateOperation(actionId);
499 \brief Load module \a modName and activate its operation, corresponding to \a actionId.
500 This method is dedicated to run operations of some module from any other module.
501 \param modName module name
502 \param actionId is a string unique operation identifier
503 \return \c true in case of success and \c false otherwise
505 bool CAM_Application::activateOperation( const QString& modName, const QString& actionId )
507 CAM_Module* mod = loadModule(modName, false);
510 return mod->activateOperation(actionId);
516 \brief Load module \a modName and activate its operation,
517 corresponding to \a actionId and \a pluginName.
518 This method is dedicated to run operations of some module from any other module.
519 \param modName module name
520 \param actionId is a string unique operation identifier
521 \param pluginName is a name of a plugin where the operation is implemented
522 \return \c true in case of success and \c false otherwise
524 bool CAM_Application::activateOperation( const QString& modName,
525 const QString& actionId,
526 const QString& pluginName )
528 CAM_Module* mod = loadModule(modName, false);
531 return mod->activateOperation(actionId, pluginName);
537 \brief Create new study.
538 \return study object pointer
540 SUIT_Study* CAM_Application::createNewStudy()
542 return new CAM_Study( this );
546 \brief Update menu commands status.
548 void CAM_Application::updateCommandsStatus()
550 STD_Application::updateCommandsStatus();
552 if ( activeModule() )
553 activeModule()->updateCommandsStatus();
557 \brief Prepare application to study closing.
559 Closes all modules in study \a theDoc.
563 void CAM_Application::beforeCloseDoc( SUIT_Study* theDoc )
565 for ( QList<CAM_Module*>::iterator it = myModules.begin(); it != myModules.end(); ++it )
566 (*it)->studyClosed( theDoc );
569 void CAM_Application::afterCloseDoc()
574 \brief Set active study.
575 \param study study to be made active
577 void CAM_Application::setActiveStudy( SUIT_Study* study )
579 STD_Application::setActiveStudy( study );
583 \brief Check module availability.
585 The method can be redefined in successors. Default implementation returns \c true.
587 \param title module title
588 \return \c true if module is accessible; \c false otherwise
590 bool CAM_Application::checkModule( const QString& )
596 \brief Callback function, called when the module is added to the application.
598 This virtual method can be re-implemented in the successors. Base implementation
601 \param mod module being added
603 void CAM_Application::moduleAdded( CAM_Module* /*mod*/ )
608 \brief Get module name by its title (user name).
609 \param title module title (user name)
610 \return module name or null QString if module is not found
612 QString CAM_Application::moduleName( const QString& title )
615 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it )
617 if ( (*it).title == title )
624 \brief Get module title (user name) by its name.
625 \param name module name
626 \return module title (user name) or null QString if module is not found
628 QString CAM_Application::moduleTitle( const QString& name )
631 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it )
633 if ( (*it).name == name )
640 \brief Get module icon name.
641 \param name module name
642 \return module icon or null QString if module is not found
644 QString CAM_Application::moduleIcon( const QString& name )
647 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isNull(); ++it )
649 if ( (*it).name == name )
656 \brief Get module library name by its title (user name).
657 \param title module title (user name)
658 \param full if \c true, return full library name, otherwise return its internal name
659 \return module library name or null QString if module is not found
661 QString CAM_Application::moduleLibrary( const QString& title, const bool full )
664 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it )
666 if ( (*it).title == title )
669 if ( !res.isEmpty() && full )
670 res = SUIT_Tools::library( res );
675 \brief Read modules information list
677 This function first tries to get the modules names list by parsing
678 the application command line arguments, looking for the
679 "--modules ( <mod_name>[:<mod_name>...] )" option.
680 List of modules is separated by colon symbol (":").
682 If "--modules" command line option is not used, the list of modules
683 is retrieved from the application resource file: parameter "modules" of
684 the section "launch".
686 Then the information about each module (module title (user name),
687 library name) is retrieved from the corresponding section of resource
688 file with help of resources manager.
690 Shows the warning message, if module information list is empty.
694 void CAM_Application::readModuleList()
696 if ( !myInfoList.isEmpty() )
699 SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
703 QString args = QApplication::arguments().join( " " );
705 QRegExp rx1("--modules=([\\w,]*)");
706 rx1.setMinimal( false );
707 QRegExp rx2("--modules\\s+\\(\\s*(.*)\\s*\\)");
708 rx2.setMinimal( true );
712 int pos1 = rx1.indexIn( args, pos );
713 int pos2 = rx2.indexIn( args, pos );
714 if ( pos1 != -1 && pos2 != -1 ) {
715 modules = pos1 < pos2 ? rx1.cap( 1 ) : rx2.cap(1);
716 pos = pos1 < pos2 ? pos1 + rx1.matchedLength() : pos2 + rx2.matchedLength();
718 else if ( pos1 != -1 ) {
719 modules = rx1.cap( 1 );
720 pos = pos1 + rx1.matchedLength();
722 else if ( pos2 != -1 ) {
723 modules = rx2.cap( 1 );
724 pos = pos2 + rx2.matchedLength();
731 QStringList mods = modules.split( QRegExp( "[:|,\\s]" ), QString::SkipEmptyParts );
732 for ( int i = 0; i < mods.count(); i++ ) {
733 if ( !mods[i].trimmed().isEmpty() )
734 modList.append( mods[i].trimmed() );
738 if ( modList.isEmpty() ) {
739 QString mods = resMgr->stringValue( "launch", "modules", QString() );
740 modList = mods.split( ",", QString::SkipEmptyParts );
743 for ( QStringList::const_iterator it = modList.begin(); it != modList.end(); ++it )
745 QString modName = (*it).trimmed();
747 if ( modName.isEmpty() )
748 continue; // empty module name
750 if ( !moduleTitle( modName ).isEmpty() )
751 continue; // already added
753 if ( modName == "KERNEL" || modName == "GUI" )
754 continue; // omit KERNEL and GUI modules
756 bool hasGui = resMgr->booleanValue( *it, "gui", true );
758 continue; // omit if module is explicitly declared as not having GUI
760 QString modTitle = resMgr->stringValue( *it, "name", QString() );
761 if ( modTitle.isEmpty() )
763 printf( "****************************************************************\n" );
764 printf( " Warning: module %s is improperly configured!\n", qPrintable(*it) );
765 printf( " Module %s will not be available in GUI mode!\n", qPrintable(*it) );
766 printf( "****************************************************************\n" );
770 QString modIcon = resMgr->stringValue( *it, "icon", QString() );
772 QString modLibrary = resMgr->stringValue( *it, "library", QString() ).trimmed();
773 if ( !modLibrary.isEmpty() )
775 modLibrary = SUIT_Tools::file( modLibrary.trimmed() );
777 QString libExt = QString( "dll" );
778 #elif defined(__APPLE__)
779 QString libExt = QString( "dylib" );
781 QString libExt = QString( "so" );
783 if ( SUIT_Tools::extension( modLibrary ).toLower() == libExt )
784 modLibrary.truncate( modLibrary.length() - libExt.length() - 1 );
786 QString prefix = QString( "lib" );
787 if ( modLibrary.startsWith( prefix ) )
788 modLibrary.remove( 0, prefix.length() );
792 modLibrary = modName;
794 QString version = resMgr->stringValue( *it, "version", QString() );
798 inf.title = modTitle;
799 inf.status = hasGui ? stUnknown : stNoGui;
800 if ( hasGui ) inf.library = modLibrary;
802 inf.version = version;
803 myInfoList.append( inf );
806 if ( myInfoList.isEmpty() ) {
807 if ( desktop() && desktop()->isVisible() )
808 SUIT_MessageBox::warning( desktop(), tr( "Warning" ), tr( "Modules list is empty" ) );
811 printf( "****************************************************************\n" );
812 printf( "* Warning: modules list is empty.\n" );
813 printf( "****************************************************************\n" );
819 \brief Add common menu items to the popup menu.
821 Menu items list is defined by the active module.
823 \param type popup menu context
824 \param menu popup menu
825 \param title popup menu title, which can be set by the module if required
827 void CAM_Application::contextMenuPopup( const QString& type, QMenu* menu, QString& title )
829 // to do : add common items for popup menu ( if they are exist )
830 if ( activeModule() )
831 activeModule()->contextMenuPopup( type, menu, title );
835 \brief Create new empty study.
837 void CAM_Application::createEmptyStudy()
839 /*SUIT_Study* study = */activeStudy();
840 STD_Application::createEmptyStudy();
844 \brief Return information about version of the each module.
846 CAM_Application::ModuleShortInfoList CAM_Application::getVersionInfo()
848 ModuleShortInfoList info;
850 ModuleShortInfo kernel;
851 kernel.name = "KERNEL";
852 kernel.version = KERNEL_VERSION_STR;
857 gui.version = GUI_VERSION_STR;
860 for(int i = 0; i < myInfoList.size(); i++) {
861 ModuleShortInfo infoItem;
862 infoItem.name = myInfoList.at(i).title;
863 infoItem.version = myInfoList.at(i).version;
864 info.append(infoItem);
870 \brief Abort active operations if there are any
872 Iterates through all modules and asks each of them if there are pending operations that cannot be aborted.
874 \return \c false if some operation cannot be aborted
876 bool CAM_Application::abortAllOperations()
879 for ( QList<CAM_Module*>::const_iterator it = myModules.begin(); it != myModules.end() && aborted; ++it )
881 aborted = (*it)->abortAllOperations();