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>
49 #include <utilities.h>
56 BusyLocker( bool& busy ) : myPrev( busy ), myBusy( busy ) { myBusy = true; }
57 ~BusyLocker() { myBusy = myPrev; }
65 \brief Create new instance of CAM_Application.
66 \return new instance of CAM_Application class
68 extern "C" CAM_EXPORT SUIT_Application* createApplication()
70 return new CAM_Application();
74 \class CAM_Application
75 \brief Introduces an application class which provides modular architecture.
77 This class defines multi-modular application configuration and behaviour.
78 Each module (CAM_Module) can have own data model, document windows and
81 An application provides all necessary functionality for modules management,
84 - modules activation/deactivation
88 CAM_Application::ModuleInfoList CAM_Application::myInfoList;
93 Read modules list (from command line or from resource file).
94 If \a autoLoad parameter is \c true all the modules will be loaded
95 immediately after application starting, otherwise each module will
96 be loaded by demand (with activateModule()).
98 \param autoLoad auto loading flag
100 CAM_Application::CAM_Application( const bool autoLoad )
103 myAutoLoad( autoLoad ),
112 Does nothing currently.
114 CAM_Application::~CAM_Application()
116 for ( QList<CAM_Module*>::const_iterator it = myModules.begin(); it != myModules.end(); ++it )
122 \brief Start an application.
124 Load all modules, if "auto loading" flag has been set to \c true.
126 \sa CAM_Application()
128 void CAM_Application::start()
131 for ( ModuleInfoList::iterator it = myInfoList.begin();
132 it != myInfoList.end(); ++it )
134 if ( (*it).status == stUnknown )
135 (*it).status = checkModule( (*it).title ) ? stReady : stInaccessible;
142 STD_Application::start();
146 \brief Get active module.
147 \return active module or 0 if there are no any
149 CAM_Module* CAM_Application::activeModule() const
155 \brief Get the module with specified name.
156 \return module or 0 if not found
158 CAM_Module* CAM_Application::module( const QString& modName ) const
161 for ( QList<CAM_Module*>::const_iterator it = myModules.begin();
162 it != myModules.end() && !mod; ++it )
163 if ( (*it)->moduleName() == modName )
169 \brief Get all loaded modules.
170 \return list of modules
172 CAM_Application::ModuleList CAM_Application::modules() const
178 \brief Get all loaded modules.
179 \param returning list of modules
181 void CAM_Application::modules( CAM_Application::ModuleList& out ) const
185 for ( QList<CAM_Module*>::const_iterator it = myModules.begin();
186 it != myModules.end(); ++it )
191 \brief Get names of all modules.
193 Get loaded modules names if \a loaded is \c true,
194 otherwise get all avaiable modules names.
196 \param lst output list of modules names
197 \param loaded boolean flag, defines what modules names to return
199 void CAM_Application::modules( QStringList& lst, const bool loaded ) const
205 for ( QList<CAM_Module*>::const_iterator it = myModules.begin();
206 it != myModules.end(); ++it )
207 lst.append( (*it)->moduleName() );
211 for ( ModuleInfoList::const_iterator it = myInfoList.begin();
212 it != myInfoList.end(); ++it )
213 if ( (*it).status != stNoGui )
214 lst.append( (*it).title );
219 \brief Add module \a mod to the modules list.
221 Performes module initialization. Does nothing if the module
224 \param mod module being added
225 \sa CAM_Module::initialize()
227 void CAM_Application::addModule( CAM_Module* mod )
229 if ( !mod || myModules.contains( mod ) )
232 mod->initialize( this );
234 QMap<CAM_Module*, int> map;
237 for ( ModuleInfoList::const_iterator it = myInfoList.begin();
238 it != myInfoList.end(); ++it )
240 if ( (*it).title == mod->moduleName() )
241 newList.append( mod );
244 CAM_Module* curMod = module( (*it).title );
246 newList.append( curMod );
250 for ( QList<CAM_Module*>::const_iterator it = myModules.begin();
251 it != myModules.end(); ++it )
253 if ( !newList.contains( *it ) )
254 newList.append( *it );
257 if ( !newList.contains( mod ) )
258 newList.append( mod );
266 \brief Load modules from the modules information list.
268 If some module can not be loaded, an error message is shown.
270 void CAM_Application::loadModules()
272 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end(); ++it )
274 CAM_Module* mod = loadModule( (*it).title );
278 QString wrn = tr( "Can not load module %1" ).arg( (*it).title );
279 if ( desktop() && desktop()->isVisible() )
280 SUIT_MessageBox::critical( desktop(), tr( "Loading modules" ), wrn );
282 qWarning( qPrintable( wrn ) );
288 \brief Load module \a modName.
290 The function prints warning message if:
291 - modules information list is empty
292 - modules information list does not include specified module info
293 - module library can not be loaded by some reason
295 \param modName module name
296 \return module object pointer or 0 if module could not be loaded
298 CAM_Module* CAM_Application::loadModule( const QString& modName, const bool showMsg )
300 if ( myInfoList.isEmpty() )
302 qWarning( qPrintable( tr( "Modules configuration is not defined." ) ) );
306 QString libName = moduleLibrary( modName );
307 if ( libName.isEmpty() )
309 qWarning( qPrintable( tr( "Information about module \"%1\" doesn't exist." ).arg( modName ) ) );
314 GET_MODULE_FUNC crtInst = 0;
315 GET_VERSION_FUNC getVersion = 0;
319 LPTSTR str_libname = new TCHAR[libName.length() + 1];
320 str_libname[libName.toWCharArray(str_libname)] = '\0';
322 QByteArray arr = libName.toLatin1();
323 LPTSTR str_libname = arr.constData();
325 HINSTANCE modLib = ::LoadLibrary( str_libname );
332 ::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
333 FORMAT_MESSAGE_IGNORE_INSERTS, 0, ::GetLastError(), 0, (LPTSTR)&lpMsgBuf, 0, 0 );
335 QString out_err = QString::fromWCharArray((LPTSTR)lpMsgBuf);
337 QString out_err = (LPTSTR)lpMsgBuf;
339 err = QString( "Failed to load %1. %2" ).arg( libName ).arg(out_err);
340 ::LocalFree( lpMsgBuf );
344 crtInst = (GET_MODULE_FUNC)::GetProcAddress( modLib, GET_MODULE_NAME );
348 ::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
349 FORMAT_MESSAGE_IGNORE_INSERTS, 0, ::GetLastError(), 0, (LPTSTR)&lpMsgBuf, 0, 0 );
351 QString out_err = QString::fromWCharArray((LPTSTR)lpMsgBuf);
353 QString out_err = (LPTSTR)lpMsgBuf;
356 err = QString( "Failed to find %1 function. %2" ).arg( GET_MODULE_NAME ).arg( out_err );
357 ::LocalFree( lpMsgBuf );
360 getVersion = (GET_VERSION_FUNC)::GetProcAddress( modLib, GET_VERSION_NAME );
363 void* modLib = dlopen( libName.toUtf8(), RTLD_LAZY | RTLD_GLOBAL );
365 err = QString( "Can not load library %1. %2" ).arg( libName ).arg( dlerror() );
368 crtInst = (GET_MODULE_FUNC)dlsym( modLib, GET_MODULE_NAME );
370 err = QString( "Failed to find function %1. %2" ).arg( GET_MODULE_NAME ).arg( dlerror() );
372 getVersion = (GET_VERSION_FUNC)dlsym( modLib, GET_VERSION_NAME );
376 CAM_Module* module = crtInst ? crtInst() : 0;
379 module->setModuleName( modName );
380 module->setName( moduleName( modName ) );
383 if ( !err.isEmpty() && showMsg ) {
384 if ( desktop() && desktop()->isVisible() )
385 SUIT_MessageBox::warning( desktop(), tr( "Error" ), err );
387 qWarning( qPrintable( err ) );
390 char* version = getVersion ? getVersion() : 0;
393 for ( ModuleInfoList::iterator it = myInfoList.begin(); it != myInfoList.end(); ++it ) {
394 if ( (*it).title == modName ) {
395 if( (*it).version.isEmpty() ) {
396 (*it).version = QString(version);
407 \brief Activate module \a modName.
408 \param modName module name
409 \return \c true, if module is loaded and activated successfully and \c false otherwise
411 bool CAM_Application::activateModule( const QString& modName )
413 if ( (!modName.isEmpty() && !activeStudy()) || myBlocked )
416 // VSR 25/10/2011: prevent nested activation/deactivation
417 // See issues 0021307, 0021373
418 BusyLocker lock( myBlocked );
420 QString name = modName;
421 if ( !name.isEmpty() && !moduleTitle( modName ).isEmpty() )
422 name = moduleTitle( modName );
425 if ( !name.isEmpty() )
427 setProperty("activateModule", true);
428 CAM_Module* mod = module( name );
430 mod = loadModule( name );
434 res = activateModule( mod );
435 setProperty("activateModule", QVariant());
438 res = activateModule( 0 );
444 \brief Activate module \a mod.
446 Shows error message if module could not be activated in the current study.
448 \param mod module object pointer
449 \return \c true, if module is loaded and activated successfully and \c false otherwise
451 bool CAM_Application::activateModule( CAM_Module* mod )
453 if ( mod && !activeStudy() )
456 if ( myModule == mod )
461 if ( !myModule->deactivateModule( activeStudy() ) )
465 moduleDeactivated( myModule );
471 // Connect the module to the active study
472 myModule->connectToStudy( dynamic_cast<CAM_Study*>( activeStudy() ) );
473 if ( !myModule->activateModule( activeStudy() ) )
475 myModule->setMenuShown( false );
476 myModule->setToolShown( false );
477 QString wrn = tr( "ERROR_ACTIVATE_MODULE_MSG" ).arg( myModule->moduleName() );
478 if ( desktop() && desktop()->isVisible() )
479 SUIT_MessageBox::critical( desktop(), tr( "ERROR_TLT" ), wrn );
481 qWarning( qPrintable( wrn ) );
487 updateCommandsStatus();
493 \brief Load module \a modName and activate its operation, corresponding to \a actionId.
494 This method is dedicated to run operations of some module from any other module.
495 \param modName module name
496 \param actionId is a numerical unique operation identifier
497 \return \c true in case of success and \c false otherwise
499 bool CAM_Application::activateOperation( const QString& modName, int actionId )
501 CAM_Module* mod = loadModule(modName, false);
504 return mod->activateOperation(actionId);
510 \brief Load module \a modName and activate its operation, corresponding to \a actionId.
511 This method is dedicated to run operations of some module from any other module.
512 \param modName module name
513 \param actionId is a string unique operation identifier
514 \return \c true in case of success and \c false otherwise
516 bool CAM_Application::activateOperation( const QString& modName, const QString& actionId )
518 CAM_Module* mod = loadModule(modName, false);
521 return mod->activateOperation(actionId);
527 \brief Load module \a modName and activate its operation,
528 corresponding to \a actionId and \a pluginName.
529 This method is dedicated to run operations of some module from any other module.
530 \param modName module name
531 \param actionId is a string unique operation identifier
532 \param pluginName is a name of a plugin where the operation is implemented
533 \return \c true in case of success and \c false otherwise
535 bool CAM_Application::activateOperation( const QString& modName,
536 const QString& actionId,
537 const QString& pluginName )
539 CAM_Module* mod = loadModule(modName, false);
542 return mod->activateOperation(actionId, pluginName);
548 \brief Create new study.
549 \return study object pointer
551 SUIT_Study* CAM_Application::createNewStudy()
553 return new CAM_Study( this );
557 \brief Update menu commands status.
559 void CAM_Application::updateCommandsStatus()
561 STD_Application::updateCommandsStatus();
563 if ( activeModule() )
564 activeModule()->updateCommandsStatus();
568 \brief Prepare application to study closing.
570 Closes all modules in study \a theDoc.
574 void CAM_Application::beforeCloseDoc( SUIT_Study* theDoc )
576 for ( QList<CAM_Module*>::iterator it = myModules.begin(); it != myModules.end(); ++it )
577 (*it)->studyClosed( theDoc );
580 void CAM_Application::afterCloseDoc()
585 \brief Set active study.
586 \param study study to be made active
588 void CAM_Application::setActiveStudy( SUIT_Study* study )
590 STD_Application::setActiveStudy( study );
594 \brief Check module availability.
596 The method can be redefined in successors. Default implementation returns \c true.
598 \param title module title
599 \return \c true if module is accessible; \c false otherwise
601 bool CAM_Application::checkModule( const QString& )
607 \brief Callback function, called when the module is added to the application.
609 This virtual method can be re-implemented in the successors. Base implementation
612 \param mod module being added
614 void CAM_Application::moduleAdded( CAM_Module* /*mod*/ )
619 \brief Callback function, called when the module is just deactivated.
621 This virtual method can be re-implemented in the successors. Base implementation
624 \param mod module just deactivated
626 void CAM_Application::moduleDeactivated( CAM_Module* /*mod*/ )
631 \brief Get module name by its title (user name).
632 \param title module title (user name)
633 \return module name or null QString if module is not found
635 QString CAM_Application::moduleName( const QString& title )
638 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it )
640 if ( (*it).title == title )
647 \brief Get module title (user name) by its name.
648 \param name module name
649 \return module title (user name) or null QString if module is not found
651 QString CAM_Application::moduleTitle( const QString& name )
654 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it )
656 if ( (*it).name == name )
663 \brief Get module icon name.
664 \param name module name or title
665 \return module icon or null QString if module is not found
667 QString CAM_Application::moduleIcon( const QString& name )
670 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isNull(); ++it )
672 if ( (*it).name == name || (*it).title == name )
679 \brief Get module description.
680 \param name module name or title
681 \return module description or null QString if description is not provided in config file.
683 QString CAM_Application::moduleDescription( const QString& name )
686 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isNull(); ++it )
688 if ( (*it).name == name || (*it).title == name )
689 res = tr((*it).description.toUtf8());
695 \brief Get module library name by its title (user name).
696 \param title module name or title
697 \param full if \c true, return full library name, otherwise return its internal name
698 \return module library name or null QString if module is not found
700 QString CAM_Application::moduleLibrary( const QString& name, const bool full )
703 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it )
705 if ( (*it).name == name || (*it).title == name )
708 if ( !res.isEmpty() && full )
709 res = SUIT_Tools::library( res );
714 \brief Get displayer proxy for given module, by its title (user name).
715 \param name module name or title
716 \return name of module which provides displayer for requested module
718 QString CAM_Application::moduleDisplayer( const QString& name )
722 if ( !name.isEmpty() )
724 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it )
726 if ( (*it).title == name || (*it).name == name ) {
727 res = (*it).displayer;
738 \brief Read modules information list
740 This function first tries to get the modules names list by parsing
741 the application command line arguments, looking for the
742 "--modules ( <mod_name>[:<mod_name>...] )" option.
743 List of modules is separated by colon symbol (":").
745 If "--modules" command line option is not used, the list of modules
746 is retrieved from the application resource file: parameter "modules" of
747 the section "launch".
749 Then the information about each module (module title (user name),
750 library name) is retrieved from the corresponding section of resource
751 file with help of resources manager.
753 Shows the warning message, if module information list is empty.
757 void CAM_Application::readModuleList()
759 if ( !myInfoList.isEmpty() )
762 // we cannot use own resourceMgr() as this method can be called from constructor
763 SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
767 QString args = QApplication::arguments().join( " " );
769 QRegExp rx1("--modules=([\\w,]*)");
770 rx1.setMinimal( false );
771 QRegExp rx2("--modules\\s+\\(\\s*(.*)\\s*\\)");
772 rx2.setMinimal( true );
776 int pos1 = rx1.indexIn( args, pos );
777 int pos2 = rx2.indexIn( args, pos );
778 if ( pos1 != -1 && pos2 != -1 ) {
779 modules = pos1 < pos2 ? rx1.cap( 1 ) : rx2.cap(1);
780 pos = pos1 < pos2 ? pos1 + rx1.matchedLength() : pos2 + rx2.matchedLength();
782 else if ( pos1 != -1 ) {
783 modules = rx1.cap( 1 );
784 pos = pos1 + rx1.matchedLength();
786 else if ( pos2 != -1 ) {
787 modules = rx2.cap( 1 );
788 pos = pos2 + rx2.matchedLength();
795 QStringList mods = modules.split( QRegExp( "[:|,\\s]" ), QString::SkipEmptyParts );
796 for ( int i = 0; i < mods.count(); i++ ) {
797 if ( !mods[i].trimmed().isEmpty() )
798 modList.append( mods[i].trimmed() );
802 if ( modList.isEmpty() ) {
803 QString mods = resMgr->stringValue( "launch", "modules", QString() );
804 modList = mods.split( ",", QString::SkipEmptyParts );
807 // extra modules loaded manually on previous session
810 foreach ( QString modName, modList )
811 appendModuleInfo( modName.trimmed() );
813 if ( myInfoList.isEmpty() ) {
814 if ( desktop() && desktop()->isVisible() )
815 SUIT_MessageBox::warning( desktop(), tr( "Warning" ), tr( "Modules list is empty" ) );
818 printf( "****************************************************************\n" );
819 printf( "* Warning: modules list is empty.\n" );
820 printf( "****************************************************************\n" );
825 bool CAM_Application::appendModuleInfo( const QString& modName )
827 MESSAGE("Start to append module info for a given module name: ");
828 SCRUTE(modName.toStdString());
830 if ( modName.isEmpty() )
831 return false; // empty module name
833 if ( !moduleTitle( modName ).isEmpty() )
834 return false; // already added
836 if ( modName == "KERNEL" || modName == "GUI" )
837 return false; // skip KERNEL and GUI modules
839 // we cannot use own resourceMgr() as this method can be called from constructor
840 SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
844 // module internal name
847 inf.version = resMgr->stringValue( modName, "version", QString() ).trimmed();
848 // displayer, if module does not have GUI, displayer may be delegated to other module
849 inf.displayer = resMgr->stringValue( modName, "displayer", QString() ).trimmed();
851 // "gui" option explicitly says that module has GUI
852 // Now trying to get the "gui" option value, we always get a default one,
853 // then we can't rely on it.
854 bool hasGui = resMgr->booleanValue(modName, "gui", false);
856 // Additional check if the module actually has a title and icon.
857 // Module with GUI must explicitly specify title (GUI name).
858 inf.title = resMgr->stringValue(modName, "name", QString()).trimmed();
859 const bool hasTitle = !inf.title.isEmpty();
862 if (hasGui && !hasTitle)
864 MESSAGE("Invalid config! The module has gui option, but doesn't have a title.");
868 // While we can't rely on gui option, use a title to make a decision about gui.
871 // status; if module has GUI, availability will be checked on activation
872 inf.status = hasGui ? stUnknown : stNoGui;
877 inf.icon = resMgr->stringValue( modName, "icon", QString() ).trimmed();
878 // description, for Info panel
879 inf.description = resMgr->stringValue( modName, "description", QString() );
880 // library; if not specified, we use internal module name
881 inf.library = SUIT_Tools::libraryName( resMgr->stringValue( modName, "library", QString() ).trimmed() );
882 if ( inf.library.isEmpty() )
883 inf.library = modName;
886 // At this point we should have only valid inf object.
887 myInfoList.append(inf);
889 SCRUTE(inf.name.toStdString());
890 SCRUTE(inf.version.toStdString());
891 SCRUTE(inf.displayer.toStdString());
893 SCRUTE(inf.title.toStdString());
894 SCRUTE(inf.icon.toStdString());
895 SCRUTE(inf.description.toStdString());
896 SCRUTE(inf.library.toStdString());
901 void CAM_Application::removeModuleInfo( const QString& modName )
903 QMutableListIterator<ModuleInfo> it( myInfoList );
904 while ( it.hasNext() )
906 ModuleInfo info = it.next();
907 if ( info.name == modName )
916 \brief Add common menu items to the popup menu.
918 Menu items list is defined by the active module.
920 \param type popup menu context
921 \param menu popup menu
922 \param title popup menu title, which can be set by the module if required
924 void CAM_Application::contextMenuPopup( const QString& type, QMenu* menu, QString& title )
926 // to do : add common items for popup menu ( if they are exist )
927 if ( activeModule() )
928 activeModule()->contextMenuPopup( type, menu, title );
932 \brief Create new empty study.
934 void CAM_Application::createEmptyStudy()
936 /*SUIT_Study* study = */activeStudy();
937 STD_Application::createEmptyStudy();
941 \brief Return information about version of the each module.
943 CAM_Application::ModuleShortInfoList CAM_Application::getVersionInfo()
945 ModuleShortInfoList info;
947 ModuleShortInfo kernel;
948 kernel.name = "KERNEL";
949 kernel.version = KERNEL_VERSION_STR;
954 gui.version = GUI_VERSION_STR;
957 for(int i = 0; i < myInfoList.size(); i++) {
958 ModuleShortInfo infoItem;
959 infoItem.name = myInfoList.at(i).title.isEmpty() ? myInfoList.at(i).name : myInfoList.at(i).title;
960 infoItem.version = myInfoList.at(i).version;
961 info.append(infoItem);
967 \brief Abort active operations if there are any
969 Iterates through all modules and asks each of them if there are pending operations that cannot be aborted.
971 \return \c false if some operation cannot be aborted
973 bool CAM_Application::abortAllOperations()
976 for ( QList<CAM_Module*>::const_iterator it = myModules.begin(); it != myModules.end() && aborted; ++it )
978 aborted = (*it)->abortAllOperations();