1 // Copyright (C) 2007-2022 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 if ( (*it).status != stNoGui )
212 lst.append( (*it).title );
217 \brief Add module \a mod to the modules list.
219 Performes module initialization. Does nothing if the module
222 \param mod module being added
223 \sa CAM_Module::initialize()
225 void CAM_Application::addModule( CAM_Module* mod )
227 if ( !mod || myModules.contains( mod ) )
230 mod->initialize( this );
232 QMap<CAM_Module*, int> map;
235 for ( ModuleInfoList::const_iterator it = myInfoList.begin();
236 it != myInfoList.end(); ++it )
238 if ( (*it).title == mod->moduleName() )
239 newList.append( mod );
242 CAM_Module* curMod = module( (*it).title );
244 newList.append( curMod );
248 for ( QList<CAM_Module*>::const_iterator it = myModules.begin();
249 it != myModules.end(); ++it )
251 if ( !newList.contains( *it ) )
252 newList.append( *it );
255 if ( !newList.contains( mod ) )
256 newList.append( mod );
264 \brief Load modules from the modules information list.
266 If some module can not be loaded, an error message is shown.
268 void CAM_Application::loadModules()
270 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end(); ++it )
272 CAM_Module* mod = loadModule( (*it).title );
276 QString wrn = tr( "Can not load module %1" ).arg( (*it).title );
277 if ( desktop() && desktop()->isVisible() )
278 SUIT_MessageBox::critical( desktop(), tr( "Loading modules" ), wrn );
280 qWarning( qPrintable( wrn ) );
286 \brief Load module \a modName.
288 The function prints warning message if:
289 - modules information list is empty
290 - modules information list does not include specified module info
291 - module library can not be loaded by some reason
293 \param modName module name
294 \return module object pointer or 0 if module could not be loaded
296 CAM_Module* CAM_Application::loadModule( const QString& modName, const bool showMsg )
298 if ( myInfoList.isEmpty() )
300 qWarning( qPrintable( tr( "Modules configuration is not defined." ) ) );
304 QString libName = moduleLibrary( modName );
305 if ( libName.isEmpty() )
307 qWarning( qPrintable( tr( "Information about module \"%1\" doesn't exist." ).arg( modName ) ) );
312 GET_MODULE_FUNC crtInst = 0;
313 GET_VERSION_FUNC getVersion = 0;
317 LPTSTR str_libname = new TCHAR[libName.length() + 1];
318 str_libname[libName.toWCharArray(str_libname)] = '\0';
320 QByteArray arr = libName.toLatin1();
321 LPTSTR str_libname = arr.constData();
323 HINSTANCE modLib = ::LoadLibrary( str_libname );
330 ::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
331 FORMAT_MESSAGE_IGNORE_INSERTS, 0, ::GetLastError(), 0, (LPTSTR)&lpMsgBuf, 0, 0 );
333 QString out_err = QString::fromWCharArray((LPTSTR)lpMsgBuf);
335 QString out_err = (LPTSTR)lpMsgBuf;
337 err = QString( "Failed to load %1. %2" ).arg( libName ).arg(out_err);
338 ::LocalFree( lpMsgBuf );
342 crtInst = (GET_MODULE_FUNC)::GetProcAddress( modLib, GET_MODULE_NAME );
346 ::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
347 FORMAT_MESSAGE_IGNORE_INSERTS, 0, ::GetLastError(), 0, (LPTSTR)&lpMsgBuf, 0, 0 );
349 QString out_err = QString::fromWCharArray((LPTSTR)lpMsgBuf);
351 QString out_err = (LPTSTR)lpMsgBuf;
354 err = QString( "Failed to find %1 function. %2" ).arg( GET_MODULE_NAME ).arg( out_err );
355 ::LocalFree( lpMsgBuf );
358 getVersion = (GET_VERSION_FUNC)::GetProcAddress( modLib, GET_VERSION_NAME );
361 void* modLib = dlopen( libName.toUtf8(), RTLD_LAZY | RTLD_GLOBAL );
363 err = QString( "Can not load library %1. %2" ).arg( libName ).arg( dlerror() );
366 crtInst = (GET_MODULE_FUNC)dlsym( modLib, GET_MODULE_NAME );
368 err = QString( "Failed to find function %1. %2" ).arg( GET_MODULE_NAME ).arg( dlerror() );
370 getVersion = (GET_VERSION_FUNC)dlsym( modLib, GET_VERSION_NAME );
374 CAM_Module* module = crtInst ? crtInst() : 0;
377 module->setModuleName( modName );
378 module->setName( moduleName( modName ) );
381 if ( !err.isEmpty() && showMsg ) {
382 if ( desktop() && desktop()->isVisible() )
383 SUIT_MessageBox::warning( desktop(), tr( "Error" ), err );
385 qWarning( qPrintable( err ) );
388 char* version = getVersion ? getVersion() : 0;
391 for ( ModuleInfoList::iterator it = myInfoList.begin(); it != myInfoList.end(); ++it ) {
392 if ( (*it).title == modName ) {
393 if( (*it).version.isEmpty() ) {
394 (*it).version = QString(version);
405 \brief Activate module \a modName.
406 \param modName module name
407 \return \c true, if module is loaded and activated successfully and \c false otherwise
409 bool CAM_Application::activateModule( const QString& modName )
411 if ( (!modName.isEmpty() && !activeStudy()) || myBlocked )
414 // VSR 25/10/2011: prevent nested activation/deactivation
415 // See issues 0021307, 0021373
416 BusyLocker lock( myBlocked );
418 QString name = modName;
419 if ( !name.isEmpty() && !moduleTitle( modName ).isEmpty() )
420 name = moduleTitle( modName );
423 if ( !name.isEmpty() )
425 setProperty("activateModule", true);
426 CAM_Module* mod = module( name );
428 mod = loadModule( name );
432 res = activateModule( mod );
433 setProperty("activateModule", QVariant());
436 res = activateModule( 0 );
442 \brief Activate module \a mod.
444 Shows error message if module could not be activated in the current study.
446 \param mod module object pointer
447 \return \c true, if module is loaded and activated successfully and \c false otherwise
449 bool CAM_Application::activateModule( CAM_Module* mod )
451 if ( mod && !activeStudy() )
454 if ( myModule == mod )
459 if ( !myModule->deactivateModule( activeStudy() ) )
463 moduleDeactivated( myModule );
469 // Connect the module to the active study
470 myModule->connectToStudy( dynamic_cast<CAM_Study*>( activeStudy() ) );
471 if ( !myModule->activateModule( activeStudy() ) )
473 myModule->setMenuShown( false );
474 myModule->setToolShown( false );
475 QString wrn = tr( "ERROR_ACTIVATE_MODULE_MSG" ).arg( myModule->moduleName() );
476 if ( desktop() && desktop()->isVisible() )
477 SUIT_MessageBox::critical( desktop(), tr( "ERROR_TLT" ), wrn );
479 qWarning( qPrintable( wrn ) );
485 updateCommandsStatus();
491 \brief Load module \a modName and activate its operation, corresponding to \a actionId.
492 This method is dedicated to run operations of some module from any other module.
493 \param modName module name
494 \param actionId is a numerical unique operation identifier
495 \return \c true in case of success and \c false otherwise
497 bool CAM_Application::activateOperation( const QString& modName, int actionId )
499 CAM_Module* mod = loadModule(modName, false);
502 return mod->activateOperation(actionId);
508 \brief Load module \a modName and activate its operation, corresponding to \a actionId.
509 This method is dedicated to run operations of some module from any other module.
510 \param modName module name
511 \param actionId is a string unique operation identifier
512 \return \c true in case of success and \c false otherwise
514 bool CAM_Application::activateOperation( const QString& modName, const QString& actionId )
516 CAM_Module* mod = loadModule(modName, false);
519 return mod->activateOperation(actionId);
525 \brief Load module \a modName and activate its operation,
526 corresponding to \a actionId and \a pluginName.
527 This method is dedicated to run operations of some module from any other module.
528 \param modName module name
529 \param actionId is a string unique operation identifier
530 \param pluginName is a name of a plugin where the operation is implemented
531 \return \c true in case of success and \c false otherwise
533 bool CAM_Application::activateOperation( const QString& modName,
534 const QString& actionId,
535 const QString& pluginName )
537 CAM_Module* mod = loadModule(modName, false);
540 return mod->activateOperation(actionId, pluginName);
546 \brief Create new study.
547 \return study object pointer
549 SUIT_Study* CAM_Application::createNewStudy()
551 return new CAM_Study( this );
555 \brief Update menu commands status.
557 void CAM_Application::updateCommandsStatus()
559 STD_Application::updateCommandsStatus();
561 if ( activeModule() )
562 activeModule()->updateCommandsStatus();
566 \brief Prepare application to study closing.
568 Closes all modules in study \a theDoc.
572 void CAM_Application::beforeCloseDoc( SUIT_Study* theDoc )
574 for ( QList<CAM_Module*>::iterator it = myModules.begin(); it != myModules.end(); ++it )
575 (*it)->studyClosed( theDoc );
578 void CAM_Application::afterCloseDoc()
583 \brief Set active study.
584 \param study study to be made active
586 void CAM_Application::setActiveStudy( SUIT_Study* study )
588 STD_Application::setActiveStudy( study );
592 \brief Check module availability.
594 The method can be redefined in successors. Default implementation returns \c true.
596 \param title module title
597 \return \c true if module is accessible; \c false otherwise
599 bool CAM_Application::checkModule( const QString& )
605 \brief Callback function, called when the module is added to the application.
607 This virtual method can be re-implemented in the successors. Base implementation
610 \param mod module being added
612 void CAM_Application::moduleAdded( CAM_Module* /*mod*/ )
617 \brief Callback function, called when the module is just deactivated.
619 This virtual method can be re-implemented in the successors. Base implementation
622 \param mod module just deactivated
624 void CAM_Application::moduleDeactivated( CAM_Module* /*mod*/ )
629 \brief Get module name by its title (user name).
630 \param title module title (user name)
631 \return module name or null QString if module is not found
633 QString CAM_Application::moduleName( const QString& title )
636 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it )
638 if ( (*it).title == title )
645 \brief Get module title (user name) by its name.
646 \param name module name
647 \return module title (user name) or null QString if module is not found
649 QString CAM_Application::moduleTitle( const QString& name )
652 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it )
654 if ( (*it).name == name )
661 \brief Get module icon name.
662 \param name module name or title
663 \return module icon or null QString if module is not found
665 QString CAM_Application::moduleIcon( const QString& name )
668 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isNull(); ++it )
670 if ( (*it).name == name || (*it).title == name )
677 \brief Get module description.
678 \param name module name or title
679 \return module description or null QString if description is not provided in config file.
681 QString CAM_Application::moduleDescription( const QString& name )
684 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isNull(); ++it )
686 if ( (*it).name == name || (*it).title == name )
687 res = tr((*it).description.toUtf8());
693 \brief Get module library name by its title (user name).
694 \param title module name or title
695 \param full if \c true, return full library name, otherwise return its internal name
696 \return module library name or null QString if module is not found
698 QString CAM_Application::moduleLibrary( const QString& name, const bool full )
701 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it )
703 if ( (*it).name == name || (*it).title == name )
706 if ( !res.isEmpty() && full )
707 res = SUIT_Tools::library( res );
712 \brief Get displayer proxy for given module, by its title (user name).
713 \param name module name or title
714 \return name of module which provides displayer for requested module
716 QString CAM_Application::moduleDisplayer( const QString& name )
720 if ( !name.isEmpty() )
722 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it )
724 if ( (*it).title == name || (*it).name == name ) {
725 res = (*it).displayer;
736 \brief Read modules information list
738 This function first tries to get the modules names list by parsing
739 the application command line arguments, looking for the
740 "--modules ( <mod_name>[:<mod_name>...] )" option.
741 List of modules is separated by colon symbol (":").
743 If "--modules" command line option is not used, the list of modules
744 is retrieved from the application resource file: parameter "modules" of
745 the section "launch".
747 Then the information about each module (module title (user name),
748 library name) is retrieved from the corresponding section of resource
749 file with help of resources manager.
751 Shows the warning message, if module information list is empty.
755 void CAM_Application::readModuleList()
757 if ( !myInfoList.isEmpty() )
760 // we cannot use own resourceMgr() as this method can be called from constructor
761 SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
765 QString args = QApplication::arguments().join( " " );
767 QRegExp rx1("--modules=([\\w,]*)");
768 rx1.setMinimal( false );
769 QRegExp rx2("--modules\\s+\\(\\s*(.*)\\s*\\)");
770 rx2.setMinimal( true );
774 int pos1 = rx1.indexIn( args, pos );
775 int pos2 = rx2.indexIn( args, pos );
776 if ( pos1 != -1 && pos2 != -1 ) {
777 modules = pos1 < pos2 ? rx1.cap( 1 ) : rx2.cap(1);
778 pos = pos1 < pos2 ? pos1 + rx1.matchedLength() : pos2 + rx2.matchedLength();
780 else if ( pos1 != -1 ) {
781 modules = rx1.cap( 1 );
782 pos = pos1 + rx1.matchedLength();
784 else if ( pos2 != -1 ) {
785 modules = rx2.cap( 1 );
786 pos = pos2 + rx2.matchedLength();
793 QStringList mods = modules.split( QRegExp( "[:|,\\s]" ), QString::SkipEmptyParts );
794 for ( int i = 0; i < mods.count(); i++ ) {
795 if ( !mods[i].trimmed().isEmpty() )
796 modList.append( mods[i].trimmed() );
800 if ( modList.isEmpty() ) {
801 QString mods = resMgr->stringValue( "launch", "modules", QString() );
802 modList = mods.split( ",", QString::SkipEmptyParts );
805 // extra modules loaded manually on previous session
808 foreach ( QString modName, modList )
809 appendModuleInfo( modName.trimmed() );
811 if ( myInfoList.isEmpty() ) {
812 if ( desktop() && desktop()->isVisible() )
813 SUIT_MessageBox::warning( desktop(), tr( "Warning" ), tr( "Modules list is empty" ) );
816 printf( "****************************************************************\n" );
817 printf( "* Warning: modules list is empty.\n" );
818 printf( "****************************************************************\n" );
823 bool CAM_Application::appendModuleInfo( const QString& modName )
825 if ( modName.isEmpty() )
826 return false; // empty module name
828 if ( !moduleTitle( modName ).isEmpty() )
829 return false; // already added
831 if ( modName == "KERNEL" || modName == "GUI" )
832 return false; // skip KERNEL and GUI modules
834 // we cannot use own resourceMgr() as this method can be called from constructor
835 SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
837 // "gui" option explicitly says that module has GUI
838 bool hasGui = resMgr->booleanValue( modName, "gui", true );
842 // module internal name
845 inf.version = resMgr->stringValue( modName, "version", QString() ).trimmed();
846 // displayer, if module does not have GUI, displayer may be delegated to other module
847 inf.displayer = resMgr->stringValue( modName, "displayer", QString() ).trimmed();
848 // status; if module has GUI, availability will be checked on activation
849 inf.status = hasGui ? stUnknown : stNoGui;
853 // module with GUI must explicitly specify title (GUI name)
854 inf.title = resMgr->stringValue( modName, "name", QString() ).trimmed();
855 if ( inf.title.isEmpty() )
856 inf.status = stInvalid;
858 inf.icon = resMgr->stringValue( modName, "icon", QString() ).trimmed();
859 // description, for Info panel
860 inf.description = resMgr->stringValue( modName, "description", QString() );
861 // library; if not specified, we use internal module name
862 inf.library = SUIT_Tools::libraryName( resMgr->stringValue( modName, "library", QString() ).trimmed() );
863 if ( inf.library.isEmpty() )
864 inf.library = modName;
867 if ( inf.status != stInvalid )
868 myInfoList.append( inf );
873 void CAM_Application::removeModuleInfo( const QString& modName )
875 QMutableListIterator<ModuleInfo> it( myInfoList );
876 while ( it.hasNext() )
878 ModuleInfo info = it.next();
879 if ( info.name == modName )
888 \brief Add common menu items to the popup menu.
890 Menu items list is defined by the active module.
892 \param type popup menu context
893 \param menu popup menu
894 \param title popup menu title, which can be set by the module if required
896 void CAM_Application::contextMenuPopup( const QString& type, QMenu* menu, QString& title )
898 // to do : add common items for popup menu ( if they are exist )
899 if ( activeModule() )
900 activeModule()->contextMenuPopup( type, menu, title );
904 \brief Create new empty study.
906 void CAM_Application::createEmptyStudy()
908 /*SUIT_Study* study = */activeStudy();
909 STD_Application::createEmptyStudy();
913 \brief Return information about version of the each module.
915 CAM_Application::ModuleShortInfoList CAM_Application::getVersionInfo()
917 ModuleShortInfoList info;
919 ModuleShortInfo kernel;
920 kernel.name = "KERNEL";
921 kernel.version = KERNEL_VERSION_STR;
926 gui.version = GUI_VERSION_STR;
929 for(int i = 0; i < myInfoList.size(); i++) {
930 ModuleShortInfo infoItem;
931 infoItem.name = myInfoList.at(i).title.isEmpty() ? myInfoList.at(i).name : myInfoList.at(i).title;
932 infoItem.version = myInfoList.at(i).version;
933 info.append(infoItem);
939 \brief Abort active operations if there are any
941 Iterates through all modules and asks each of them if there are pending operations that cannot be aborted.
943 \return \c false if some operation cannot be aborted
945 bool CAM_Application::abortAllOperations()
948 for ( QList<CAM_Module*>::const_iterator it = myModules.begin(); it != myModules.end() && aborted; ++it )
950 aborted = (*it)->abortAllOperations();