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 if ( !isModuleAccessible( (*it).title ) ) {
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 if ( !isModuleAccessible( modName ) ) {
307 qWarning( qPrintable( tr( "Module \"%1\" cannot be loaded in this application." ).arg( modName ) ) );
311 QString libName = moduleLibrary( modName );
312 if ( libName.isEmpty() )
314 qWarning( qPrintable( tr( "Information about module \"%1\" doesn't exist." ).arg( modName ) ) );
319 GET_MODULE_FUNC crtInst = 0;
320 GET_VERSION_FUNC getVersion = 0;
324 LPTSTR str_libname = new TCHAR[libName.length() + 1];
325 str_libname[libName.toWCharArray(str_libname)] = '\0';
327 QByteArray arr = libName.toLatin1();
328 LPTSTR str_libname = arr.constData();
330 HINSTANCE modLib = ::LoadLibrary( str_libname );
337 ::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
338 FORMAT_MESSAGE_IGNORE_INSERTS, 0, ::GetLastError(), 0, (LPTSTR)&lpMsgBuf, 0, 0 );
340 QString out_err = QString::fromWCharArray((LPTSTR)lpMsgBuf);
342 QString out_err = (LPTSTR)lpMsgBuf;
344 err = QString( "Failed to load %1. %2" ).arg( libName ).arg(out_err);
345 ::LocalFree( lpMsgBuf );
349 crtInst = (GET_MODULE_FUNC)::GetProcAddress( modLib, GET_MODULE_NAME );
353 ::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
354 FORMAT_MESSAGE_IGNORE_INSERTS, 0, ::GetLastError(), 0, (LPTSTR)&lpMsgBuf, 0, 0 );
356 QString out_err = QString::fromWCharArray((LPTSTR)lpMsgBuf);
358 QString out_err = (LPTSTR)lpMsgBuf;
361 err = QString( "Failed to find %1 function. %2" ).arg( GET_MODULE_NAME ).arg( out_err );
362 ::LocalFree( lpMsgBuf );
365 getVersion = (GET_VERSION_FUNC)::GetProcAddress( modLib, GET_VERSION_NAME );
368 void* modLib = dlopen( libName.toUtf8(), RTLD_LAZY | RTLD_GLOBAL );
370 err = QString( "Can not load library %1. %2" ).arg( libName ).arg( dlerror() );
373 crtInst = (GET_MODULE_FUNC)dlsym( modLib, GET_MODULE_NAME );
375 err = QString( "Failed to find function %1. %2" ).arg( GET_MODULE_NAME ).arg( dlerror() );
377 getVersion = (GET_VERSION_FUNC)dlsym( modLib, GET_VERSION_NAME );
381 CAM_Module* module = crtInst ? crtInst() : 0;
384 module->setModuleName( modName );
385 module->setName( moduleName( modName ) );
388 if ( !err.isEmpty() && showMsg ) {
389 if ( desktop() && desktop()->isVisible() )
390 SUIT_MessageBox::warning( desktop(), tr( "Error" ), err );
392 qWarning( qPrintable( err ) );
395 char* version = getVersion ? getVersion() : 0;
398 for ( ModuleInfoList::iterator it = myInfoList.begin(); it != myInfoList.end(); ++it ) {
399 if ( (*it).title == modName ) {
400 if( (*it).version.isEmpty() ) {
401 (*it).version = QString(version);
412 \brief Activate module \a modName.
413 \param modName module name
414 \return \c true, if module is loaded and activated successfully and \c false otherwise
416 bool CAM_Application::activateModule( const QString& modName )
418 if ( (!modName.isEmpty() && !activeStudy()) || myBlocked )
421 // VSR 25/10/2011: prevent nested activation/deactivation
422 // See issues 0021307, 0021373
423 BusyLocker lock( myBlocked );
426 if ( !modName.isEmpty() )
428 CAM_Module* mod = module( modName );
430 mod = loadModule( modName );
434 res = activateModule( mod );
437 res = activateModule( 0 );
443 \brief Activate module \a mod.
445 Shows error message if module could not be activated in the current study.
447 \param mod module object pointer
448 \return \c true, if module is loaded and activated successfully and \c false otherwise
450 bool CAM_Application::activateModule( CAM_Module* mod )
452 if ( mod && !activeStudy() )
455 if ( myModule == mod )
460 if ( !myModule->deactivateModule( activeStudy() ) )
468 // Connect the module to the active study
469 myModule->connectToStudy( dynamic_cast<CAM_Study*>( activeStudy() ) );
470 if ( !myModule->activateModule( activeStudy() ) )
472 myModule->setMenuShown( false );
473 myModule->setToolShown( false );
474 QString wrn = tr( "ERROR_ACTIVATE_MODULE_MSG" ).arg( myModule->moduleName() );
475 if ( desktop() && desktop()->isVisible() )
476 SUIT_MessageBox::critical( desktop(), tr( "ERROR_TLT" ), wrn );
478 qWarning( qPrintable( wrn ) );
484 updateCommandsStatus();
490 \brief Load module \a modName and activate its operation, corresponding to \a actionId.
491 This method is dedicated to run operations of some module from any other module.
492 \param modName module name
493 \param actionId is a numerical unique operation identifier
494 \return \c true in case of success and \c false otherwise
496 bool CAM_Application::activateOperation( const QString& modName, int actionId )
498 if (isModuleAccessible(modName)) {
499 CAM_Module* mod = loadModule(modName, false);
502 return mod->activateOperation(actionId);
509 \brief Load module \a modName and activate its operation, corresponding to \a actionId.
510 This method is dedicated to run operations of some module from any other module.
511 \param modName module name
512 \param actionId is a string unique operation identifier
513 \return \c true in case of success and \c false otherwise
515 bool CAM_Application::activateOperation( const QString& modName, const QString& actionId )
517 if (isModuleAccessible(modName)) {
518 CAM_Module* mod = loadModule(modName, false);
521 return mod->activateOperation(actionId);
528 \brief Load module \a modName and activate its operation,
529 corresponding to \a actionId and \a pluginName.
530 This method is dedicated to run operations of some module from any other module.
531 \param modName module name
532 \param actionId is a string unique operation identifier
533 \param pluginName is a name of a plugin where the operation is implemented
534 \return \c true in case of success and \c false otherwise
536 bool CAM_Application::activateOperation( const QString& modName,
537 const QString& actionId,
538 const QString& pluginName )
540 if (isModuleAccessible(modName)) {
541 CAM_Module* mod = loadModule(modName, false);
544 return mod->activateOperation(actionId, pluginName);
551 \brief Create new study.
552 \return study object pointer
554 SUIT_Study* CAM_Application::createNewStudy()
556 return new CAM_Study( this );
560 \brief Update menu commands status.
562 void CAM_Application::updateCommandsStatus()
564 STD_Application::updateCommandsStatus();
566 if ( activeModule() )
567 activeModule()->updateCommandsStatus();
571 \brief Prepare application to study closing.
573 Closes all modules in study \a theDoc.
577 void CAM_Application::beforeCloseDoc( SUIT_Study* theDoc )
579 for ( QList<CAM_Module*>::iterator it = myModules.begin(); it != myModules.end(); ++it )
580 (*it)->studyClosed( theDoc );
583 void CAM_Application::afterCloseDoc()
588 \brief Set active study.
589 \param study study to be made active
591 void CAM_Application::setActiveStudy( SUIT_Study* study )
593 STD_Application::setActiveStudy( study );
597 \brief Check module availability.
599 The method can be redefined in successors. Default implementation returns \c true.
601 \param title module title
602 \return \c true if module is accessible; \c false otherwise
604 bool CAM_Application::checkModule( const QString& )
610 \brief Callback function, called when the module is added to the application.
612 This virtual method can be re-implemented in the successors. Base implementation
615 \param mod module being added
617 void CAM_Application::moduleAdded( CAM_Module* /*mod*/ )
622 \brief Get module name by its title (user name).
623 \param title module title (user name)
624 \return module name or null QString if module is not found
626 QString CAM_Application::moduleName( const QString& title )
629 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it )
631 if ( (*it).title == title )
638 \brief Get module title (user name) by its name.
639 \param name module name
640 \return module title (user name) or null QString if module is not found
642 QString CAM_Application::moduleTitle( const QString& name )
645 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it )
647 if ( (*it).name == name )
654 \brief Get module icon name.
655 \param name module name
656 \return module icon or null QString if module is not found
658 QString CAM_Application::moduleIcon( const QString& name )
661 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isNull(); ++it )
663 if ( (*it).name == name )
670 \brief Returns \c true if module is accessible for the current application.
671 Singleton module can be loaded only in one application object. In other application
672 objects this module will be unavailable.
673 \param title module title (user name)
674 \return \c true if module is accessible (can be loaded) or \c false otherwise
676 bool CAM_Application::isModuleAccessible( const QString& title )
679 bool blocked = false;
680 bool statusOK = false;
682 QStringList somewhereLoaded;
683 QList<SUIT_Application*> apps = SUIT_Session::session()->applications();
684 foreach( SUIT_Application* app, apps ) {
685 CAM_Application* camApp = dynamic_cast<CAM_Application*>( app );
686 if ( !camApp ) continue;
688 camApp->modules( loaded, true );
689 foreach( QString lm, loaded ) {
690 if ( !somewhereLoaded.contains( lm ) ) somewhereLoaded << lm;
694 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && !found; ++it )
696 found = (*it).title == title;
697 blocked = (*it).isSingleton && somewhereLoaded.contains((*it).title);
698 statusOK = (*it).status == stReady;
700 return found && statusOK && !blocked;
704 \brief Get module library name by its title (user name).
705 \param title module title (user name)
706 \param full if \c true, return full library name, otherwise return its internal name
707 \return module library name or null QString if module is not found
709 QString CAM_Application::moduleLibrary( const QString& title, const bool full )
712 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it )
714 if ( (*it).title == title )
717 if ( !res.isEmpty() && full )
718 res = SUIT_Tools::library( res );
723 \brief Read modules information list
725 This function first tries to get the modules names list by parsing
726 the application command line arguments, looking for the
727 "--modules ( <mod_name>[:<mod_name>...] )" option.
728 List of modules is separated by colon symbol (":").
730 If "--modules" command line option is not used, the list of modules
731 is retrieved from the application resource file: parameter "modules" of
732 the section "launch".
734 Then the information about each module (module title (user name),
735 library name) is retrieved from the corresponding section of resource
736 file with help of resources manager.
738 Shows the warning message, if module information list is empty.
742 void CAM_Application::readModuleList()
744 if ( !myInfoList.isEmpty() )
747 SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
751 QString args = QApplication::arguments().join( " " );
753 QRegExp rx1("--modules=([\\w,]*)");
754 rx1.setMinimal( false );
755 QRegExp rx2("--modules\\s+\\(\\s*(.*)\\s*\\)");
756 rx2.setMinimal( true );
760 int pos1 = rx1.indexIn( args, pos );
761 int pos2 = rx2.indexIn( args, pos );
762 if ( pos1 != -1 && pos2 != -1 ) {
763 modules = pos1 < pos2 ? rx1.cap( 1 ) : rx2.cap(1);
764 pos = pos1 < pos2 ? pos1 + rx1.matchedLength() : pos2 + rx2.matchedLength();
766 else if ( pos1 != -1 ) {
767 modules = rx1.cap( 1 );
768 pos = pos1 + rx1.matchedLength();
770 else if ( pos2 != -1 ) {
771 modules = rx2.cap( 1 );
772 pos = pos2 + rx2.matchedLength();
779 QStringList mods = modules.split( QRegExp( "[:|,\\s]" ), QString::SkipEmptyParts );
780 for ( int i = 0; i < mods.count(); i++ ) {
781 if ( !mods[i].trimmed().isEmpty() )
782 modList.append( mods[i].trimmed() );
786 if ( modList.isEmpty() ) {
787 QString mods = resMgr->stringValue( "launch", "modules", QString() );
788 modList = mods.split( ",", QString::SkipEmptyParts );
791 for ( QStringList::const_iterator it = modList.begin(); it != modList.end(); ++it )
793 QString modName = (*it).trimmed();
795 if ( modName.isEmpty() )
796 continue; // empty module name
798 if ( !moduleTitle( modName ).isEmpty() )
799 continue; // already added
801 if ( modName == "KERNEL" || modName == "GUI" )
802 continue; // omit KERNEL and GUI modules
804 QString modTitle = resMgr->stringValue( *it, "name", QString() );
805 if ( modTitle.isEmpty() )
807 printf( "****************************************************************\n" );
808 printf( " Warning: module %s is improperly configured!\n", qPrintable(*it) );
809 printf( " Module %s will not be available in GUI mode!\n", qPrintable(*it) );
810 printf( "****************************************************************\n" );
814 QString modIcon = resMgr->stringValue( *it, "icon", QString() );
816 QString modLibrary = resMgr->stringValue( *it, "library", QString() ).trimmed();
817 if ( !modLibrary.isEmpty() )
819 modLibrary = SUIT_Tools::file( modLibrary.trimmed() );
821 QString libExt = QString( "dll" );
822 #elif defined(__APPLE__)
823 QString libExt = QString( "dylib" );
825 QString libExt = QString( "so" );
827 if ( SUIT_Tools::extension( modLibrary ).toLower() == libExt )
828 modLibrary.truncate( modLibrary.length() - libExt.length() - 1 );
830 QString prefix = QString( "lib" );
831 if ( modLibrary.startsWith( prefix ) )
832 modLibrary.remove( 0, prefix.length() );
836 modLibrary = modName;
838 bool aIsSingleton = resMgr->booleanValue( *it, "singleton", false );
839 bool hasGui = resMgr->booleanValue( *it, "gui", true );
840 QString version = resMgr->stringValue( *it, "version", QString() );
844 inf.title = modTitle;
845 inf.status = hasGui ? stUnknown : stNoGui;
846 if ( hasGui ) inf.library = modLibrary;
848 inf.isSingleton = aIsSingleton;
849 inf.version = version;
850 myInfoList.append( inf );
853 if ( myInfoList.isEmpty() ) {
854 if ( desktop() && desktop()->isVisible() )
855 SUIT_MessageBox::warning( desktop(), tr( "Warning" ), tr( "Modules list is empty" ) );
858 printf( "****************************************************************\n" );
859 printf( "* Warning: modules list is empty.\n" );
860 printf( "****************************************************************\n" );
866 \brief Add common menu items to the popup menu.
868 Menu items list is defined by the active module.
870 \param type popup menu context
871 \param menu popup menu
872 \param title popup menu title, which can be set by the module if required
874 void CAM_Application::contextMenuPopup( const QString& type, QMenu* menu, QString& title )
876 // to do : add common items for popup menu ( if they are exist )
877 if ( activeModule() )
878 activeModule()->contextMenuPopup( type, menu, title );
882 \brief Create new empty study.
884 void CAM_Application::createEmptyStudy()
886 /*SUIT_Study* study = */activeStudy();
887 STD_Application::createEmptyStudy();
891 \brief Return information about version of the each module.
893 CAM_Application::ModuleShortInfoList CAM_Application::getVersionInfo()
895 ModuleShortInfoList info;
897 ModuleShortInfo kernel;
898 kernel.name = "KERNEL";
899 kernel.version = KERNEL_VERSION_STR;
904 gui.version = GUI_VERSION_STR;
907 for(int i = 0; i < myInfoList.size(); i++) {
908 ModuleShortInfo infoItem;
909 infoItem.name = myInfoList.at(i).title;
910 infoItem.version = myInfoList.at(i).version;
911 info.append(infoItem);
917 \brief Abort active operations if there are any
919 Iterates through all modules and asks each of them if there are pending operations that cannot be aborted.
921 \return \c false if some operation cannot be aborted
923 bool CAM_Application::abortAllOperations()
926 for ( QList<CAM_Module*>::const_iterator it = myModules.begin(); it != myModules.end() && aborted; ++it )
928 aborted = (*it)->abortAllOperations();