1 // Copyright (C) 2007-2016 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()
131 STD_Application::start();
135 \brief Get active module.
136 \return active module or 0 if there are no any
138 CAM_Module* CAM_Application::activeModule() const
144 \brief Get the module with specified name.
145 \return module or 0 if not found
147 CAM_Module* CAM_Application::module( const QString& modName ) const
150 for ( QList<CAM_Module*>::const_iterator it = myModules.begin();
151 it != myModules.end() && !mod; ++it )
152 if ( (*it)->moduleName() == modName )
158 \brief Get all loaded modules.
159 \return list of modules
161 CAM_Application::ModuleList CAM_Application::modules() const
167 \brief Get all loaded modules.
168 \param returning list of modules
170 void CAM_Application::modules( CAM_Application::ModuleList& out ) const
174 for ( QList<CAM_Module*>::const_iterator it = myModules.begin();
175 it != myModules.end(); ++it )
180 \brief Get names of all modules.
182 Get loaded modules names if \a loaded is \c true,
183 otherwise get all avaiable modules names.
185 \param lst output list of modules names
186 \param loaded boolean flag, defines what modules names to return
188 void CAM_Application::modules( QStringList& lst, const bool loaded ) const
194 for ( QList<CAM_Module*>::const_iterator it = myModules.begin();
195 it != myModules.end(); ++it )
196 lst.append( (*it)->moduleName() );
200 for ( ModuleInfoList::const_iterator it = myInfoList.begin();
201 it != myInfoList.end(); ++it )
202 lst.append( (*it).title );
207 \brief Add module \a mod to the modules list.
209 Performes module initialization. Does nothing if the module
212 \param mod module being added
213 \sa CAM_Module::initialize()
215 void CAM_Application::addModule( CAM_Module* mod )
217 if ( !mod || myModules.contains( mod ) )
220 mod->initialize( this );
222 QMap<CAM_Module*, int> map;
225 for ( ModuleInfoList::const_iterator it = myInfoList.begin();
226 it != myInfoList.end(); ++it )
228 if ( (*it).title == mod->moduleName() )
229 newList.append( mod );
232 CAM_Module* curMod = module( (*it).title );
234 newList.append( curMod );
238 for ( QList<CAM_Module*>::const_iterator it = myModules.begin();
239 it != myModules.end(); ++it )
241 if ( !newList.contains( *it ) )
242 newList.append( *it );
245 if ( !newList.contains( mod ) )
246 newList.append( mod );
254 \brief Load modules from the modules information list.
256 If some module can not be loaded, an error message is shown.
258 void CAM_Application::loadModules()
260 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end(); ++it )
262 if ( !isModuleAccessible( (*it).title ) ) {
265 CAM_Module* mod = loadModule( (*it).title );
269 QString wrn = tr( "Can not load module %1" ).arg( (*it).title );
270 if ( desktop() && desktop()->isVisible() )
271 SUIT_MessageBox::critical( desktop(), tr( "Loading modules" ), wrn );
273 qWarning( qPrintable( wrn ) );
279 \brief Load module \a modName.
281 The function prints warning message if:
282 - modules information list is empty
283 - modules information list does not include specified module info
284 - module library can not be loaded by some reason
286 \param modName module name
287 \return module object pointer or 0 if module could not be loaded
289 CAM_Module* CAM_Application::loadModule( const QString& modName, const bool showMsg )
291 if ( myInfoList.isEmpty() )
293 qWarning( qPrintable( tr( "Modules configuration is not defined." ) ) );
297 if ( !isModuleAccessible( modName ) ) {
298 qWarning( qPrintable( tr( "Module \"%1\" cannot be loaded in this application." ).arg( modName ) ) );
302 QString libName = moduleLibrary( modName );
303 if ( libName.isEmpty() )
305 qWarning( qPrintable( tr( "Information about module \"%1\" doesn't exist." ).arg( modName ) ) );
310 GET_MODULE_FUNC crtInst = 0;
311 GET_VERSION_FUNC getVersion = 0;
314 HINSTANCE modLib = ::LoadLibrary( libName.toLatin1() );
318 ::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
319 FORMAT_MESSAGE_IGNORE_INSERTS, 0, ::GetLastError(), 0, (LPTSTR)&lpMsgBuf, 0, 0 );
320 err = QString( "Failed to load %1. %2" ).arg( libName ).arg( (LPTSTR)lpMsgBuf );
321 ::LocalFree( lpMsgBuf );
325 crtInst = (GET_MODULE_FUNC)::GetProcAddress( modLib, GET_MODULE_NAME );
329 ::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
330 FORMAT_MESSAGE_IGNORE_INSERTS, 0, ::GetLastError(), 0, (LPTSTR)&lpMsgBuf, 0, 0 );
331 err = QString( "Failed to find %1 function. %2" ).arg( GET_MODULE_NAME ).arg( (LPTSTR)lpMsgBuf );
332 ::LocalFree( lpMsgBuf );
335 getVersion = (GET_VERSION_FUNC)::GetProcAddress( modLib, GET_VERSION_NAME );
338 void* modLib = dlopen( libName.toLatin1(), RTLD_LAZY | RTLD_GLOBAL );
340 err = QString( "Can not load library %1. %2" ).arg( libName ).arg( dlerror() );
343 crtInst = (GET_MODULE_FUNC)dlsym( modLib, GET_MODULE_NAME );
345 err = QString( "Failed to find function %1. %2" ).arg( GET_MODULE_NAME ).arg( dlerror() );
347 getVersion = (GET_VERSION_FUNC)dlsym( modLib, GET_VERSION_NAME );
351 CAM_Module* module = crtInst ? crtInst() : 0;
354 module->setModuleName( modName );
355 module->setName( moduleName( modName ) );
358 if ( !err.isEmpty() && showMsg ) {
359 if ( desktop() && desktop()->isVisible() )
360 SUIT_MessageBox::warning( desktop(), tr( "Error" ), err );
362 qWarning( qPrintable( err ) );
365 char* version = getVersion ? getVersion() : 0;
368 for ( ModuleInfoList::iterator it = myInfoList.begin(); it != myInfoList.end(); ++it ) {
369 if ( (*it).title == modName ) {
370 if( (*it).version.isEmpty() ) {
371 (*it).version = QString(version);
382 \brief Activate module \a modName.
383 \param modName module name
384 \return \c true, if module is loaded and activated successfully and \c false otherwise
386 bool CAM_Application::activateModule( const QString& modName )
388 if ( (!modName.isEmpty() && !activeStudy()) || myBlocked )
391 // VSR 25/10/2011: prevent nested activation/deactivation
392 // See issues 0021307, 0021373
393 BusyLocker lock( myBlocked );
396 if ( !modName.isEmpty() )
398 CAM_Module* mod = module( modName );
399 if ( !mod && !moduleLibrary( modName ).isEmpty() )
401 mod = loadModule( modName );
406 res = activateModule( mod );
409 res = activateModule( 0 );
415 \brief Activate module \a mod.
417 Shows error message if module could not be activated in the current study.
419 \param mod module object pointer
420 \return \c true, if module is loaded and activated successfully and \c false otherwise
422 bool CAM_Application::activateModule( CAM_Module* mod )
424 if ( mod && !activeStudy() )
427 if ( myModule == mod )
432 if ( !myModule->deactivateModule( activeStudy() ) )
440 // Connect the module to the active study
441 myModule->connectToStudy( dynamic_cast<CAM_Study*>( activeStudy() ) );
442 if ( !myModule->activateModule( activeStudy() ) )
444 myModule->setMenuShown( false );
445 myModule->setToolShown( false );
446 QString wrn = tr( "ERROR_ACTIVATE_MODULE_MSG" ).arg( myModule->moduleName() );
447 if ( desktop() && desktop()->isVisible() )
448 SUIT_MessageBox::critical( desktop(), tr( "ERROR_TLT" ), wrn );
450 qWarning( qPrintable( wrn ) );
456 updateCommandsStatus();
462 \brief Load module \a modName and activate its operation, corresponding to \a actionId.
463 This method is dedicated to run operations of some module from any other module.
464 \param modName module name
465 \param actionId is a numerical unique operation identifier
466 \return \c true in case of success and \c false otherwise
468 bool CAM_Application::activateOperation( const QString& modName, int actionId )
470 if (isModuleAccessible(modName)) {
471 CAM_Module* mod = loadModule(modName, false);
474 return mod->activateOperation(actionId);
481 \brief Load module \a modName and activate its operation, corresponding to \a actionId.
482 This method is dedicated to run operations of some module from any other module.
483 \param modName module name
484 \param actionId is a string unique operation identifier
485 \return \c true in case of success and \c false otherwise
487 bool CAM_Application::activateOperation( const QString& modName, const QString& actionId )
489 if (isModuleAccessible(modName)) {
490 CAM_Module* mod = loadModule(modName, false);
493 return mod->activateOperation(actionId);
500 \brief Load module \a modName and activate its operation,
501 corresponding to \a actionId and \a pluginName.
502 This method is dedicated to run operations of some module from any other module.
503 \param modName module name
504 \param actionId is a string unique operation identifier
505 \param pluginName is a name of a plugin where the operation is implemented
506 \return \c true in case of success and \c false otherwise
508 bool CAM_Application::activateOperation( const QString& modName,
509 const QString& actionId,
510 const QString& pluginName )
512 if (isModuleAccessible(modName)) {
513 CAM_Module* mod = loadModule(modName, false);
516 return mod->activateOperation(actionId, pluginName);
523 \brief Create new study.
524 \return study object pointer
526 SUIT_Study* CAM_Application::createNewStudy()
528 return new CAM_Study( this );
532 \brief Update menu commands status.
534 void CAM_Application::updateCommandsStatus()
536 STD_Application::updateCommandsStatus();
538 if ( activeModule() )
539 activeModule()->updateCommandsStatus();
543 \brief Prepare application to study closing.
545 Closes all modules in study \a theDoc.
549 void CAM_Application::beforeCloseDoc( SUIT_Study* theDoc )
551 for ( QList<CAM_Module*>::iterator it = myModules.begin(); it != myModules.end(); ++it )
552 (*it)->studyClosed( theDoc );
555 void CAM_Application::afterCloseDoc()
560 \brief Set active study.
561 \param study study to be made active
563 void CAM_Application::setActiveStudy( SUIT_Study* study )
565 STD_Application::setActiveStudy( study );
569 \brief Callback function, called when the module is added to the application.
571 This virtual method can be re-implemented in the successors. Base implementation
574 \param mod module being added
576 void CAM_Application::moduleAdded( CAM_Module* /*mod*/ )
581 \brief Get module name by its title (user name).
582 \param title module title (user name)
583 \return module name or null QString if module is not found
585 QString CAM_Application::moduleName( const QString& title )
588 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it )
590 if ( (*it).title == title )
597 \brief Get module title (user name) by its name.
598 \param name module name
599 \return module title (user name) or null QString if module is not found
601 QString CAM_Application::moduleTitle( const QString& name )
604 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it )
606 if ( (*it).name == name )
613 \brief Get module icon name.
614 \param name module name
615 \return module icon or null QString if module is not found
617 QString CAM_Application::moduleIcon( const QString& name )
620 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isNull(); ++it )
622 if ( (*it).name == name )
629 \brief Returns \c true if module is accessible for the current application.
630 Singleton module can be loaded only in one application object. In other application
631 objects this module will be unavailable.
632 \param title module title (user name)
633 \return \c true if module is accessible (can be loaded) or \c false otherwise
635 bool CAM_Application::isModuleAccessible( const QString& title )
638 bool blocked = false;
640 QStringList somewhereLoaded;
641 QList<SUIT_Application*> apps = SUIT_Session::session()->applications();
642 foreach( SUIT_Application* app, apps ) {
643 CAM_Application* camApp = dynamic_cast<CAM_Application*>( app );
644 if ( !camApp ) continue;
646 camApp->modules( loaded, true );
647 foreach( QString lm, loaded ) {
648 if ( !somewhereLoaded.contains( lm ) ) somewhereLoaded << lm;
652 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && !found; ++it )
654 found = (*it).title == title;
655 blocked = (*it).isSingleton && somewhereLoaded.contains((*it).title);
657 return found && !blocked;
661 \brief Get module library name by its title (user name).
662 \param title module title (user name)
663 \param full if \c true, return full library name, otherwise return its internal name
664 \return module library name or null QString if module is not found
666 QString CAM_Application::moduleLibrary( const QString& title, const bool full )
669 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it )
671 if ( (*it).title == title )
672 res = (*it).internal;
674 if ( !res.isEmpty() && full )
675 res = SUIT_Tools::library( res );
680 \brief Read modules information list
682 This function first tries to get the modules names list by parsing
683 the application command line arguments, looking for the
684 "--modules ( <mod_name>[:<mod_name>...] )" option.
685 List of modules is separated by colon symbol (":").
687 If "--modules" command line option is not used, the list of modules
688 is retrieved from the application resource file: parameter "modules" of
689 the section "launch".
691 Then the information about each module (module title (user name),
692 library name) is retrieved from the corresponding section of resource
693 file with help of resources manager.
695 Shows the warning message, if module information list is empty.
699 void CAM_Application::readModuleList()
701 if ( !myInfoList.isEmpty() )
704 SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
708 QString args = QApplication::arguments().join( " " );
710 QRegExp rx1("--modules=([\\w,]*)");
711 rx1.setMinimal( false );
712 QRegExp rx2("--modules\\s+\\(\\s*(.*)\\s*\\)");
713 rx2.setMinimal( true );
717 int pos1 = rx1.indexIn( args, pos );
718 int pos2 = rx2.indexIn( args, pos );
719 if ( pos1 != -1 && pos2 != -1 ) {
720 modules = pos1 < pos2 ? rx1.cap( 1 ) : rx2.cap(1);
721 pos = pos1 < pos2 ? pos1 + rx1.matchedLength() : pos2 + rx2.matchedLength();
723 else if ( pos1 != -1 ) {
724 modules = rx1.cap( 1 );
725 pos = pos1 + rx1.matchedLength();
727 else if ( pos2 != -1 ) {
728 modules = rx2.cap( 1 );
729 pos = pos2 + rx2.matchedLength();
736 QStringList mods = modules.split( QRegExp( "[:|,\\s]" ), QString::SkipEmptyParts );
737 for ( int i = 0; i < mods.count(); i++ ) {
738 if ( !mods[i].trimmed().isEmpty() )
739 modList.append( mods[i].trimmed() );
743 if ( modList.isEmpty() ) {
744 QString mods = resMgr->stringValue( "launch", "modules", QString() );
745 modList = mods.split( ",", QString::SkipEmptyParts );
748 for ( QStringList::const_iterator it = modList.begin(); it != modList.end(); ++it )
750 QString modName = (*it).trimmed();
752 if ( modName.isEmpty() )
753 continue; // empty module name
755 if ( !moduleTitle( modName ).isEmpty() )
756 continue; // already added
758 QString modTitle = resMgr->stringValue( *it, "name", QString() );
759 if ( modTitle.isEmpty() )
761 printf( "****************************************************************\n" );
762 printf( "* Warning: %s GUI resources are not found.\n", qPrintable(*it) );
763 printf( "* %s GUI will not be available.\n", qPrintable(*it) );
764 printf( "****************************************************************\n" );
768 QString modIcon = resMgr->stringValue( *it, "icon", QString() );
770 QString modLibrary = resMgr->stringValue( *it, "library", QString() ).trimmed();
771 if ( !modLibrary.isEmpty() )
773 modLibrary = SUIT_Tools::file( modLibrary.trimmed() );
775 QString libExt = QString( "dll" );
776 #elif defined(__APPLE__)
777 QString libExt = QString( "dylib" );
779 QString libExt = QString( "so" );
781 if ( SUIT_Tools::extension( modLibrary ).toLower() == libExt )
782 modLibrary.truncate( modLibrary.length() - libExt.length() - 1 );
784 QString prefix = QString( "lib" );
785 if ( modLibrary.startsWith( prefix ) )
786 modLibrary.remove( 0, prefix.length() );
790 modLibrary = modName;
792 bool aIsSingleton = resMgr->booleanValue(*it, "singleton", false);
794 QString ver = resMgr->stringValue(*it, "version", QString());
798 inf.title = modTitle;
799 inf.internal = modLibrary;
801 inf.isSingleton = aIsSingleton;
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();