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;
323 HINSTANCE modLib = ::LoadLibrary( libName.toLatin1() );
327 ::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
328 FORMAT_MESSAGE_IGNORE_INSERTS, 0, ::GetLastError(), 0, (LPTSTR)&lpMsgBuf, 0, 0 );
329 err = QString( "Failed to load %1. %2" ).arg( libName ).arg( (LPTSTR)lpMsgBuf );
330 ::LocalFree( lpMsgBuf );
334 crtInst = (GET_MODULE_FUNC)::GetProcAddress( modLib, GET_MODULE_NAME );
338 ::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
339 FORMAT_MESSAGE_IGNORE_INSERTS, 0, ::GetLastError(), 0, (LPTSTR)&lpMsgBuf, 0, 0 );
340 err = QString( "Failed to find %1 function. %2" ).arg( GET_MODULE_NAME ).arg( (LPTSTR)lpMsgBuf );
341 ::LocalFree( lpMsgBuf );
344 getVersion = (GET_VERSION_FUNC)::GetProcAddress( modLib, GET_VERSION_NAME );
347 void* modLib = dlopen( libName.toLatin1(), RTLD_LAZY | RTLD_GLOBAL );
349 err = QString( "Can not load library %1. %2" ).arg( libName ).arg( dlerror() );
352 crtInst = (GET_MODULE_FUNC)dlsym( modLib, GET_MODULE_NAME );
354 err = QString( "Failed to find function %1. %2" ).arg( GET_MODULE_NAME ).arg( dlerror() );
356 getVersion = (GET_VERSION_FUNC)dlsym( modLib, GET_VERSION_NAME );
360 CAM_Module* module = crtInst ? crtInst() : 0;
363 module->setModuleName( modName );
364 module->setName( moduleName( modName ) );
367 if ( !err.isEmpty() && showMsg ) {
368 if ( desktop() && desktop()->isVisible() )
369 SUIT_MessageBox::warning( desktop(), tr( "Error" ), err );
371 qWarning( qPrintable( err ) );
374 char* version = getVersion ? getVersion() : 0;
377 for ( ModuleInfoList::iterator it = myInfoList.begin(); it != myInfoList.end(); ++it ) {
378 if ( (*it).title == modName ) {
379 if( (*it).version.isEmpty() ) {
380 (*it).version = QString(version);
391 \brief Activate module \a modName.
392 \param modName module name
393 \return \c true, if module is loaded and activated successfully and \c false otherwise
395 bool CAM_Application::activateModule( const QString& modName )
397 if ( (!modName.isEmpty() && !activeStudy()) || myBlocked )
400 // VSR 25/10/2011: prevent nested activation/deactivation
401 // See issues 0021307, 0021373
402 BusyLocker lock( myBlocked );
405 if ( !modName.isEmpty() )
407 CAM_Module* mod = module( modName );
409 mod = loadModule( modName );
413 res = activateModule( mod );
416 res = activateModule( 0 );
422 \brief Activate module \a mod.
424 Shows error message if module could not be activated in the current study.
426 \param mod module object pointer
427 \return \c true, if module is loaded and activated successfully and \c false otherwise
429 bool CAM_Application::activateModule( CAM_Module* mod )
431 if ( mod && !activeStudy() )
434 if ( myModule == mod )
439 if ( !myModule->deactivateModule( activeStudy() ) )
447 // Connect the module to the active study
448 myModule->connectToStudy( dynamic_cast<CAM_Study*>( activeStudy() ) );
449 if ( !myModule->activateModule( activeStudy() ) )
451 myModule->setMenuShown( false );
452 myModule->setToolShown( false );
453 QString wrn = tr( "ERROR_ACTIVATE_MODULE_MSG" ).arg( myModule->moduleName() );
454 if ( desktop() && desktop()->isVisible() )
455 SUIT_MessageBox::critical( desktop(), tr( "ERROR_TLT" ), wrn );
457 qWarning( qPrintable( wrn ) );
463 updateCommandsStatus();
469 \brief Load module \a modName and activate its operation, corresponding to \a actionId.
470 This method is dedicated to run operations of some module from any other module.
471 \param modName module name
472 \param actionId is a numerical unique operation identifier
473 \return \c true in case of success and \c false otherwise
475 bool CAM_Application::activateOperation( const QString& modName, int actionId )
477 if (isModuleAccessible(modName)) {
478 CAM_Module* mod = loadModule(modName, false);
481 return mod->activateOperation(actionId);
488 \brief Load module \a modName and activate its operation, corresponding to \a actionId.
489 This method is dedicated to run operations of some module from any other module.
490 \param modName module name
491 \param actionId is a string unique operation identifier
492 \return \c true in case of success and \c false otherwise
494 bool CAM_Application::activateOperation( const QString& modName, const QString& actionId )
496 if (isModuleAccessible(modName)) {
497 CAM_Module* mod = loadModule(modName, false);
500 return mod->activateOperation(actionId);
507 \brief Load module \a modName and activate its operation,
508 corresponding to \a actionId and \a pluginName.
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 \param pluginName is a name of a plugin where the operation is implemented
513 \return \c true in case of success and \c false otherwise
515 bool CAM_Application::activateOperation( const QString& modName,
516 const QString& actionId,
517 const QString& pluginName )
519 if (isModuleAccessible(modName)) {
520 CAM_Module* mod = loadModule(modName, false);
523 return mod->activateOperation(actionId, pluginName);
530 \brief Create new study.
531 \return study object pointer
533 SUIT_Study* CAM_Application::createNewStudy()
535 return new CAM_Study( this );
539 \brief Update menu commands status.
541 void CAM_Application::updateCommandsStatus()
543 STD_Application::updateCommandsStatus();
545 if ( activeModule() )
546 activeModule()->updateCommandsStatus();
550 \brief Prepare application to study closing.
552 Closes all modules in study \a theDoc.
556 void CAM_Application::beforeCloseDoc( SUIT_Study* theDoc )
558 for ( QList<CAM_Module*>::iterator it = myModules.begin(); it != myModules.end(); ++it )
559 (*it)->studyClosed( theDoc );
562 void CAM_Application::afterCloseDoc()
567 \brief Set active study.
568 \param study study to be made active
570 void CAM_Application::setActiveStudy( SUIT_Study* study )
572 STD_Application::setActiveStudy( study );
576 \brief Check module availability.
578 The method can be redefined in successors. Default implementation returns \c true.
580 \param title module title
581 \return \c true if module is accessible; \c false otherwise
583 bool CAM_Application::checkModule( const QString& )
589 \brief Callback function, called when the module is added to the application.
591 This virtual method can be re-implemented in the successors. Base implementation
594 \param mod module being added
596 void CAM_Application::moduleAdded( CAM_Module* /*mod*/ )
601 \brief Get module name by its title (user name).
602 \param title module title (user name)
603 \return module name or null QString if module is not found
605 QString CAM_Application::moduleName( const QString& title )
608 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it )
610 if ( (*it).title == title )
617 \brief Get module title (user name) by its name.
618 \param name module name
619 \return module title (user name) or null QString if module is not found
621 QString CAM_Application::moduleTitle( const QString& name )
624 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it )
626 if ( (*it).name == name )
633 \brief Get module icon name.
634 \param name module name
635 \return module icon or null QString if module is not found
637 QString CAM_Application::moduleIcon( const QString& name )
640 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isNull(); ++it )
642 if ( (*it).name == name )
649 \brief Returns \c true if module is accessible for the current application.
650 Singleton module can be loaded only in one application object. In other application
651 objects this module will be unavailable.
652 \param title module title (user name)
653 \return \c true if module is accessible (can be loaded) or \c false otherwise
655 bool CAM_Application::isModuleAccessible( const QString& title )
658 bool blocked = false;
659 bool statusOK = false;
661 QStringList somewhereLoaded;
662 QList<SUIT_Application*> apps = SUIT_Session::session()->applications();
663 foreach( SUIT_Application* app, apps ) {
664 CAM_Application* camApp = dynamic_cast<CAM_Application*>( app );
665 if ( !camApp ) continue;
667 camApp->modules( loaded, true );
668 foreach( QString lm, loaded ) {
669 if ( !somewhereLoaded.contains( lm ) ) somewhereLoaded << lm;
673 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && !found; ++it )
675 found = (*it).title == title;
676 blocked = (*it).isSingleton && somewhereLoaded.contains((*it).title);
677 statusOK = (*it).status == stReady;
679 return found && statusOK && !blocked;
683 \brief Get module library name by its title (user name).
684 \param title module title (user name)
685 \param full if \c true, return full library name, otherwise return its internal name
686 \return module library name or null QString if module is not found
688 QString CAM_Application::moduleLibrary( const QString& title, const bool full )
691 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it )
693 if ( (*it).title == title )
696 if ( !res.isEmpty() && full )
697 res = SUIT_Tools::library( res );
702 \brief Read modules information list
704 This function first tries to get the modules names list by parsing
705 the application command line arguments, looking for the
706 "--modules ( <mod_name>[:<mod_name>...] )" option.
707 List of modules is separated by colon symbol (":").
709 If "--modules" command line option is not used, the list of modules
710 is retrieved from the application resource file: parameter "modules" of
711 the section "launch".
713 Then the information about each module (module title (user name),
714 library name) is retrieved from the corresponding section of resource
715 file with help of resources manager.
717 Shows the warning message, if module information list is empty.
721 void CAM_Application::readModuleList()
723 if ( !myInfoList.isEmpty() )
726 SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
730 QString args = QApplication::arguments().join( " " );
732 QRegExp rx1("--modules=([\\w,]*)");
733 rx1.setMinimal( false );
734 QRegExp rx2("--modules\\s+\\(\\s*(.*)\\s*\\)");
735 rx2.setMinimal( true );
739 int pos1 = rx1.indexIn( args, pos );
740 int pos2 = rx2.indexIn( args, pos );
741 if ( pos1 != -1 && pos2 != -1 ) {
742 modules = pos1 < pos2 ? rx1.cap( 1 ) : rx2.cap(1);
743 pos = pos1 < pos2 ? pos1 + rx1.matchedLength() : pos2 + rx2.matchedLength();
745 else if ( pos1 != -1 ) {
746 modules = rx1.cap( 1 );
747 pos = pos1 + rx1.matchedLength();
749 else if ( pos2 != -1 ) {
750 modules = rx2.cap( 1 );
751 pos = pos2 + rx2.matchedLength();
758 QStringList mods = modules.split( QRegExp( "[:|,\\s]" ), QString::SkipEmptyParts );
759 for ( int i = 0; i < mods.count(); i++ ) {
760 if ( !mods[i].trimmed().isEmpty() )
761 modList.append( mods[i].trimmed() );
765 if ( modList.isEmpty() ) {
766 QString mods = resMgr->stringValue( "launch", "modules", QString() );
767 modList = mods.split( ",", QString::SkipEmptyParts );
770 for ( QStringList::const_iterator it = modList.begin(); it != modList.end(); ++it )
772 QString modName = (*it).trimmed();
774 if ( modName.isEmpty() )
775 continue; // empty module name
777 if ( !moduleTitle( modName ).isEmpty() )
778 continue; // already added
780 if ( modName == "KERNEL" || modName == "GUI" )
781 continue; // omit KERNEL and GUI modules
783 QString modTitle = resMgr->stringValue( *it, "name", QString() );
784 if ( modTitle.isEmpty() )
786 printf( "****************************************************************\n" );
787 printf( " Warning: module %s is improperly configured!\n", qPrintable(*it) );
788 printf( " Module %s will not be available in GUI mode!\n", qPrintable(*it) );
789 printf( "****************************************************************\n" );
793 QString modIcon = resMgr->stringValue( *it, "icon", QString() );
795 QString modLibrary = resMgr->stringValue( *it, "library", QString() ).trimmed();
796 if ( !modLibrary.isEmpty() )
798 modLibrary = SUIT_Tools::file( modLibrary.trimmed() );
800 QString libExt = QString( "dll" );
801 #elif defined(__APPLE__)
802 QString libExt = QString( "dylib" );
804 QString libExt = QString( "so" );
806 if ( SUIT_Tools::extension( modLibrary ).toLower() == libExt )
807 modLibrary.truncate( modLibrary.length() - libExt.length() - 1 );
809 QString prefix = QString( "lib" );
810 if ( modLibrary.startsWith( prefix ) )
811 modLibrary.remove( 0, prefix.length() );
815 modLibrary = modName;
817 bool aIsSingleton = resMgr->booleanValue( *it, "singleton", false );
818 bool hasGui = resMgr->booleanValue( *it, "gui", true );
819 QString version = resMgr->stringValue( *it, "version", QString() );
823 inf.title = modTitle;
824 inf.status = hasGui ? stUnknown : stNoGui;
825 if ( hasGui ) inf.library = modLibrary;
827 inf.isSingleton = aIsSingleton;
828 inf.version = version;
829 myInfoList.append( inf );
832 if ( myInfoList.isEmpty() ) {
833 if ( desktop() && desktop()->isVisible() )
834 SUIT_MessageBox::warning( desktop(), tr( "Warning" ), tr( "Modules list is empty" ) );
837 printf( "****************************************************************\n" );
838 printf( "* Warning: modules list is empty.\n" );
839 printf( "****************************************************************\n" );
845 \brief Add common menu items to the popup menu.
847 Menu items list is defined by the active module.
849 \param type popup menu context
850 \param menu popup menu
851 \param title popup menu title, which can be set by the module if required
853 void CAM_Application::contextMenuPopup( const QString& type, QMenu* menu, QString& title )
855 // to do : add common items for popup menu ( if they are exist )
856 if ( activeModule() )
857 activeModule()->contextMenuPopup( type, menu, title );
861 \brief Create new empty study.
863 void CAM_Application::createEmptyStudy()
865 /*SUIT_Study* study = */activeStudy();
866 STD_Application::createEmptyStudy();
870 \brief Return information about version of the each module.
872 CAM_Application::ModuleShortInfoList CAM_Application::getVersionInfo()
874 ModuleShortInfoList info;
876 ModuleShortInfo kernel;
877 kernel.name = "KERNEL";
878 kernel.version = KERNEL_VERSION_STR;
883 gui.version = GUI_VERSION_STR;
886 for(int i = 0; i < myInfoList.size(); i++) {
887 ModuleShortInfo infoItem;
888 infoItem.name = myInfoList.at(i).title;
889 infoItem.version = myInfoList.at(i).version;
890 info.append(infoItem);
896 \brief Abort active operations if there are any
898 Iterates through all modules and asks each of them if there are pending operations that cannot be aborted.
900 \return \c false if some operation cannot be aborted
902 bool CAM_Application::abortAllOperations()
905 for ( QList<CAM_Module*>::const_iterator it = myModules.begin(); it != myModules.end() && aborted; ++it )
907 aborted = (*it)->abortAllOperations();