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()
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;
325 LPTSTR str_libname = new TCHAR[libName.length() + 1];
326 str_libname[libName.toWCharArray(str_libname)] = '\0';
328 LPTSTR str_libname = libName.toLatin1().constData();
330 HINSTANCE modLib = ::LoadLibrary( str_libname );
334 ::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
335 FORMAT_MESSAGE_IGNORE_INSERTS, 0, ::GetLastError(), 0, (LPTSTR)&lpMsgBuf, 0, 0 );
337 QString out_err = QString::fromWCharArray((LPTSTR)lpMsgBuf);
339 QString out_err = (LPTSTR)lpMsgBuf;
341 err = QString( "Failed to load %1. %2" ).arg( libName ).arg(out_err);
342 ::LocalFree( lpMsgBuf );
346 crtInst = (GET_MODULE_FUNC)::GetProcAddress( modLib, GET_MODULE_NAME );
350 ::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
351 FORMAT_MESSAGE_IGNORE_INSERTS, 0, ::GetLastError(), 0, (LPTSTR)&lpMsgBuf, 0, 0 );
353 QString out_err = QString::fromWCharArray((LPTSTR)lpMsgBuf);
355 QString out_err = (LPTSTR)lpMsgBuf;
358 err = QString( "Failed to find %1 function. %2" ).arg( GET_MODULE_NAME ).arg( out_err );
359 ::LocalFree( lpMsgBuf );
362 getVersion = (GET_VERSION_FUNC)::GetProcAddress( modLib, GET_VERSION_NAME );
365 void* modLib = dlopen( libName.toUtf8(), RTLD_LAZY | RTLD_GLOBAL );
367 err = QString( "Can not load library %1. %2" ).arg( libName ).arg( dlerror() );
370 crtInst = (GET_MODULE_FUNC)dlsym( modLib, GET_MODULE_NAME );
372 err = QString( "Failed to find function %1. %2" ).arg( GET_MODULE_NAME ).arg( dlerror() );
374 getVersion = (GET_VERSION_FUNC)dlsym( modLib, GET_VERSION_NAME );
378 CAM_Module* module = crtInst ? crtInst() : 0;
381 module->setModuleName( modName );
382 module->setName( moduleName( modName ) );
385 if ( !err.isEmpty() && showMsg ) {
386 if ( desktop() && desktop()->isVisible() )
387 SUIT_MessageBox::warning( desktop(), tr( "Error" ), err );
389 qWarning( qPrintable( err ) );
392 char* version = getVersion ? getVersion() : 0;
395 for ( ModuleInfoList::iterator it = myInfoList.begin(); it != myInfoList.end(); ++it ) {
396 if ( (*it).title == modName ) {
397 if( (*it).version.isEmpty() ) {
398 (*it).version = QString(version);
409 \brief Activate module \a modName.
410 \param modName module name
411 \return \c true, if module is loaded and activated successfully and \c false otherwise
413 bool CAM_Application::activateModule( const QString& modName )
415 if ( (!modName.isEmpty() && !activeStudy()) || myBlocked )
418 // VSR 25/10/2011: prevent nested activation/deactivation
419 // See issues 0021307, 0021373
420 BusyLocker lock( myBlocked );
423 if ( !modName.isEmpty() )
425 CAM_Module* mod = module( modName );
427 mod = loadModule( modName );
431 res = activateModule( mod );
434 res = activateModule( 0 );
440 \brief Activate module \a mod.
442 Shows error message if module could not be activated in the current study.
444 \param mod module object pointer
445 \return \c true, if module is loaded and activated successfully and \c false otherwise
447 bool CAM_Application::activateModule( CAM_Module* mod )
449 if ( mod && !activeStudy() )
452 if ( myModule == mod )
457 if ( !myModule->deactivateModule( activeStudy() ) )
465 // Connect the module to the active study
466 myModule->connectToStudy( dynamic_cast<CAM_Study*>( activeStudy() ) );
467 if ( !myModule->activateModule( activeStudy() ) )
469 myModule->setMenuShown( false );
470 myModule->setToolShown( false );
471 QString wrn = tr( "ERROR_ACTIVATE_MODULE_MSG" ).arg( myModule->moduleName() );
472 if ( desktop() && desktop()->isVisible() )
473 SUIT_MessageBox::critical( desktop(), tr( "ERROR_TLT" ), wrn );
475 qWarning( qPrintable( wrn ) );
481 updateCommandsStatus();
487 \brief Load module \a modName and activate its operation, corresponding to \a actionId.
488 This method is dedicated to run operations of some module from any other module.
489 \param modName module name
490 \param actionId is a numerical unique operation identifier
491 \return \c true in case of success and \c false otherwise
493 bool CAM_Application::activateOperation( const QString& modName, int actionId )
495 if (isModuleAccessible(modName)) {
496 CAM_Module* mod = loadModule(modName, false);
499 return mod->activateOperation(actionId);
506 \brief Load module \a modName and activate its operation, corresponding to \a actionId.
507 This method is dedicated to run operations of some module from any other module.
508 \param modName module name
509 \param actionId is a string unique operation identifier
510 \return \c true in case of success and \c false otherwise
512 bool CAM_Application::activateOperation( const QString& modName, const QString& actionId )
514 if (isModuleAccessible(modName)) {
515 CAM_Module* mod = loadModule(modName, false);
518 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 if (isModuleAccessible(modName)) {
538 CAM_Module* mod = loadModule(modName, false);
541 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 Get module name by its title (user name).
620 \param title module title (user name)
621 \return module name or null QString if module is not found
623 QString CAM_Application::moduleName( const QString& title )
626 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it )
628 if ( (*it).title == title )
635 \brief Get module title (user name) by its name.
636 \param name module name
637 \return module title (user name) or null QString if module is not found
639 QString CAM_Application::moduleTitle( const QString& name )
642 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it )
644 if ( (*it).name == name )
651 \brief Get module icon name.
652 \param name module name
653 \return module icon or null QString if module is not found
655 QString CAM_Application::moduleIcon( const QString& name )
658 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isNull(); ++it )
660 if ( (*it).name == name )
667 \brief Returns \c true if module is accessible for the current application.
668 Singleton module can be loaded only in one application object. In other application
669 objects this module will be unavailable.
670 \param title module title (user name)
671 \return \c true if module is accessible (can be loaded) or \c false otherwise
673 bool CAM_Application::isModuleAccessible( const QString& title )
676 bool blocked = false;
677 bool statusOK = false;
679 QStringList somewhereLoaded;
680 QList<SUIT_Application*> apps = SUIT_Session::session()->applications();
681 foreach( SUIT_Application* app, apps ) {
682 CAM_Application* camApp = dynamic_cast<CAM_Application*>( app );
683 if ( !camApp ) continue;
685 camApp->modules( loaded, true );
686 foreach( QString lm, loaded ) {
687 if ( !somewhereLoaded.contains( lm ) ) somewhereLoaded << lm;
691 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && !found; ++it )
693 found = (*it).title == title;
694 blocked = (*it).isSingleton && somewhereLoaded.contains((*it).title);
695 statusOK = (*it).status == stReady;
697 return found && statusOK && !blocked;
701 \brief Get module library name by its title (user name).
702 \param title module title (user name)
703 \param full if \c true, return full library name, otherwise return its internal name
704 \return module library name or null QString if module is not found
706 QString CAM_Application::moduleLibrary( const QString& title, const bool full )
709 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it )
711 if ( (*it).title == title )
714 if ( !res.isEmpty() && full )
715 res = SUIT_Tools::library( res );
720 \brief Read modules information list
722 This function first tries to get the modules names list by parsing
723 the application command line arguments, looking for the
724 "--modules ( <mod_name>[:<mod_name>...] )" option.
725 List of modules is separated by colon symbol (":").
727 If "--modules" command line option is not used, the list of modules
728 is retrieved from the application resource file: parameter "modules" of
729 the section "launch".
731 Then the information about each module (module title (user name),
732 library name) is retrieved from the corresponding section of resource
733 file with help of resources manager.
735 Shows the warning message, if module information list is empty.
739 void CAM_Application::readModuleList()
741 if ( !myInfoList.isEmpty() )
744 SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
748 QString args = QApplication::arguments().join( " " );
750 QRegExp rx1("--modules=([\\w,]*)");
751 rx1.setMinimal( false );
752 QRegExp rx2("--modules\\s+\\(\\s*(.*)\\s*\\)");
753 rx2.setMinimal( true );
757 int pos1 = rx1.indexIn( args, pos );
758 int pos2 = rx2.indexIn( args, pos );
759 if ( pos1 != -1 && pos2 != -1 ) {
760 modules = pos1 < pos2 ? rx1.cap( 1 ) : rx2.cap(1);
761 pos = pos1 < pos2 ? pos1 + rx1.matchedLength() : pos2 + rx2.matchedLength();
763 else if ( pos1 != -1 ) {
764 modules = rx1.cap( 1 );
765 pos = pos1 + rx1.matchedLength();
767 else if ( pos2 != -1 ) {
768 modules = rx2.cap( 1 );
769 pos = pos2 + rx2.matchedLength();
776 QStringList mods = modules.split( QRegExp( "[:|,\\s]" ), QString::SkipEmptyParts );
777 for ( int i = 0; i < mods.count(); i++ ) {
778 if ( !mods[i].trimmed().isEmpty() )
779 modList.append( mods[i].trimmed() );
783 if ( modList.isEmpty() ) {
784 QString mods = resMgr->stringValue( "launch", "modules", QString() );
785 modList = mods.split( ",", QString::SkipEmptyParts );
788 for ( QStringList::const_iterator it = modList.begin(); it != modList.end(); ++it )
790 QString modName = (*it).trimmed();
792 if ( modName.isEmpty() )
793 continue; // empty module name
795 if ( !moduleTitle( modName ).isEmpty() )
796 continue; // already added
798 if ( modName == "KERNEL" || modName == "GUI" )
799 continue; // omit KERNEL and GUI modules
801 QString modTitle = resMgr->stringValue( *it, "name", QString() );
802 if ( modTitle.isEmpty() )
804 printf( "****************************************************************\n" );
805 printf( " Warning: module %s is improperly configured!\n", qPrintable(*it) );
806 printf( " Module %s will not be available in GUI mode!\n", qPrintable(*it) );
807 printf( "****************************************************************\n" );
811 QString modIcon = resMgr->stringValue( *it, "icon", QString() );
813 QString modLibrary = resMgr->stringValue( *it, "library", QString() ).trimmed();
814 if ( !modLibrary.isEmpty() )
816 modLibrary = SUIT_Tools::file( modLibrary.trimmed() );
818 QString libExt = QString( "dll" );
819 #elif defined(__APPLE__)
820 QString libExt = QString( "dylib" );
822 QString libExt = QString( "so" );
824 if ( SUIT_Tools::extension( modLibrary ).toLower() == libExt )
825 modLibrary.truncate( modLibrary.length() - libExt.length() - 1 );
827 QString prefix = QString( "lib" );
828 if ( modLibrary.startsWith( prefix ) )
829 modLibrary.remove( 0, prefix.length() );
833 modLibrary = modName;
835 bool aIsSingleton = resMgr->booleanValue( *it, "singleton", false );
836 bool hasGui = resMgr->booleanValue( *it, "gui", true );
837 QString version = resMgr->stringValue( *it, "version", QString() );
841 inf.title = modTitle;
842 inf.status = hasGui ? stUnknown : stNoGui;
843 if ( hasGui ) inf.library = modLibrary;
845 inf.isSingleton = aIsSingleton;
846 inf.version = version;
847 myInfoList.append( inf );
850 if ( myInfoList.isEmpty() ) {
851 if ( desktop() && desktop()->isVisible() )
852 SUIT_MessageBox::warning( desktop(), tr( "Warning" ), tr( "Modules list is empty" ) );
855 printf( "****************************************************************\n" );
856 printf( "* Warning: modules list is empty.\n" );
857 printf( "****************************************************************\n" );
863 \brief Add common menu items to the popup menu.
865 Menu items list is defined by the active module.
867 \param type popup menu context
868 \param menu popup menu
869 \param title popup menu title, which can be set by the module if required
871 void CAM_Application::contextMenuPopup( const QString& type, QMenu* menu, QString& title )
873 // to do : add common items for popup menu ( if they are exist )
874 if ( activeModule() )
875 activeModule()->contextMenuPopup( type, menu, title );
879 \brief Create new empty study.
881 void CAM_Application::createEmptyStudy()
883 /*SUIT_Study* study = */activeStudy();
884 STD_Application::createEmptyStudy();
888 \brief Return information about version of the each module.
890 CAM_Application::ModuleShortInfoList CAM_Application::getVersionInfo()
892 ModuleShortInfoList info;
894 ModuleShortInfo kernel;
895 kernel.name = "KERNEL";
896 kernel.version = KERNEL_VERSION_STR;
901 gui.version = GUI_VERSION_STR;
904 for(int i = 0; i < myInfoList.size(); i++) {
905 ModuleShortInfo infoItem;
906 infoItem.name = myInfoList.at(i).title;
907 infoItem.version = myInfoList.at(i).version;
908 info.append(infoItem);
914 \brief Abort active operations if there are any
916 Iterates through all modules and asks each of them if there are pending operations that cannot be aborted.
918 \return \c false if some operation cannot be aborted
920 bool CAM_Application::abortAllOperations()
923 for ( QList<CAM_Module*>::const_iterator it = myModules.begin(); it != myModules.end() && aborted; ++it )
925 aborted = (*it)->abortAllOperations();