1 // Copyright (C) 2007-2013 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.
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
22 #include "CAM_Application.h"
24 #include "CAM_Study.h"
25 #include "CAM_Module.h"
27 #include <SUIT_Tools.h>
28 #include <SUIT_Desktop.h>
29 #include <SUIT_Session.h>
30 #include <SUIT_MessageBox.h>
31 #include <SUIT_ResourceMgr.h>
33 #include <KERNEL_version.h>
34 #include <GUI_version.h>
36 #include <QApplication>
53 BusyLocker( bool& busy ) : myPrev( busy ), myBusy( busy ) { myBusy = true; }
54 ~BusyLocker() { myBusy = myPrev; }
62 \brief Create new instance of CAM_Application.
63 \return new instance of CAM_Application class
65 extern "C" CAM_EXPORT SUIT_Application* createApplication()
67 return new CAM_Application();
71 \class CAM_Application
72 \brief Introduces an application class which provides modular architecture.
74 This class defines multi-modular application configuration and behaviour.
75 Each module (CAM_Module) can have own data model, document windows and
78 An application provides all necessary functionality for modules management,
81 - modules activation/deactivation
85 CAM_Application::ModuleInfoList CAM_Application::myInfoList;
90 Read modules list (from command line or from resource file).
91 If \a autoLoad parameter is \c true all the modules will be loaded
92 immediately after application starting, otherwise each module will
93 be loaded by demand (with activateModule()).
95 \param autoLoad auto loading flag
97 CAM_Application::CAM_Application( const bool autoLoad )
100 myAutoLoad( autoLoad ),
109 Does nothing currently.
111 CAM_Application::~CAM_Application()
113 for ( QList<CAM_Module*>::const_iterator it = myModules.begin(); it != myModules.end(); ++it )
119 \brief Start an application.
121 Load all modules, if "auto loading" flag has been set to \c true.
123 \sa CAM_Application()
125 void CAM_Application::start()
130 STD_Application::start();
134 \brief Get active module.
135 \return active module or 0 if there are no any
137 CAM_Module* CAM_Application::activeModule() const
143 \brief Get the module with specified name.
144 \return module or 0 if not found
146 CAM_Module* CAM_Application::module( const QString& modName ) const
149 for ( QList<CAM_Module*>::const_iterator it = myModules.begin();
150 it != myModules.end() && !mod; ++it )
151 if ( (*it)->moduleName() == modName )
157 \brief Get all loaded modules.
158 \return list of modules
160 CAM_Application::ModuleList CAM_Application::modules() const
166 \brief Get all loaded modules.
167 \param returning list of modules
169 void CAM_Application::modules( CAM_Application::ModuleList& out ) const
173 for ( QList<CAM_Module*>::const_iterator it = myModules.begin();
174 it != myModules.end(); ++it )
179 \brief Get names of all modules.
181 Get loaded modules names if \a loaded is \c true,
182 otherwise get all avaiable modules names.
184 \param lst output list of modules names
185 \param loaded boolean flag, defines what modules names to return
187 void CAM_Application::modules( QStringList& lst, const bool loaded ) const
193 for ( QList<CAM_Module*>::const_iterator it = myModules.begin();
194 it != myModules.end(); ++it )
195 lst.append( (*it)->moduleName() );
199 for ( ModuleInfoList::const_iterator it = myInfoList.begin();
200 it != myInfoList.end(); ++it )
201 lst.append( (*it).title );
206 \brief Add module \a mod to the modules list.
208 Performes module initialization. Does nothing if the module
211 \param mod module being added
212 \sa CAM_Module::initialize()
214 void CAM_Application::addModule( CAM_Module* mod )
216 if ( !mod || myModules.contains( mod ) )
219 mod->initialize( this );
221 QMap<CAM_Module*, int> map;
224 for ( ModuleInfoList::const_iterator it = myInfoList.begin();
225 it != myInfoList.end(); ++it )
227 if ( (*it).title == mod->moduleName() )
228 newList.append( mod );
231 CAM_Module* curMod = module( (*it).title );
233 newList.append( curMod );
237 for ( QList<CAM_Module*>::const_iterator it = myModules.begin();
238 it != myModules.end(); ++it )
240 if ( !newList.contains( *it ) )
241 newList.append( *it );
244 if ( !newList.contains( mod ) )
245 newList.append( mod );
253 \brief Load modules from the modules information list.
255 If some module can not be loaded, an error message is shown.
257 void CAM_Application::loadModules()
259 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end(); ++it )
261 if ( !isModuleAccessible( (*it).title ) ) {
264 CAM_Module* mod = loadModule( (*it).title );
268 QString wrn = tr( "Can not load module %1" ).arg( (*it).title );
269 if ( desktop() && desktop()->isVisible() )
270 SUIT_MessageBox::critical( desktop(), tr( "Loading modules" ), wrn );
272 qWarning( qPrintable( wrn ) );
278 \brief Load module \a modName.
280 The function prints warning message if:
281 - modules information list is empty
282 - modules information list does not include specified module info
283 - module library can not be loaded by some reason
285 \param modName module name
286 \return module object pointer or 0 if module could not be loaded
288 CAM_Module* CAM_Application::loadModule( const QString& modName, const bool showMsg )
290 if ( myInfoList.isEmpty() )
292 qWarning( qPrintable( tr( "Modules configuration is not defined." ) ) );
296 if ( !isModuleAccessible( modName ) ) {
297 qWarning( qPrintable( tr( "Module \"%1\" cannot be loaded in this application." ).arg( modName ) ) );
301 QString libName = moduleLibrary( modName );
302 if ( libName.isEmpty() )
304 qWarning( qPrintable( tr( "Information about module \"%1\" doesn't exist." ).arg( modName ) ) );
309 GET_MODULE_FUNC crtInst = 0;
310 GET_VERSION_FUNC getVersion = 0;
313 HINSTANCE modLib = ::LoadLibrary( libName.toLatin1() );
317 ::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
318 FORMAT_MESSAGE_IGNORE_INSERTS, 0, ::GetLastError(), 0, (LPTSTR)&lpMsgBuf, 0, 0 );
319 err = QString( "Failed to load %1. %2" ).arg( libName ).arg( (LPTSTR)lpMsgBuf );
320 ::LocalFree( lpMsgBuf );
324 crtInst = (GET_MODULE_FUNC)::GetProcAddress( modLib, GET_MODULE_NAME );
328 ::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
329 FORMAT_MESSAGE_IGNORE_INSERTS, 0, ::GetLastError(), 0, (LPTSTR)&lpMsgBuf, 0, 0 );
330 err = QString( "Failed to find %1 function. %2" ).arg( GET_MODULE_NAME ).arg( (LPTSTR)lpMsgBuf );
331 ::LocalFree( lpMsgBuf );
334 getVersion = (GET_VERSION_FUNC)::GetProcAddress( modLib, GET_VERSION_NAME );
337 void* modLib = dlopen( libName.toLatin1(), RTLD_LAZY );
339 err = QString( "Can not load library %1. %2" ).arg( libName ).arg( dlerror() );
342 crtInst = (GET_MODULE_FUNC)dlsym( modLib, GET_MODULE_NAME );
344 err = QString( "Failed to find function %1. %2" ).arg( GET_MODULE_NAME ).arg( dlerror() );
346 getVersion = (GET_VERSION_FUNC)dlsym( modLib, GET_VERSION_NAME );
350 CAM_Module* module = crtInst ? crtInst() : 0;
353 module->setModuleName( modName );
354 module->setName( moduleName( modName ) );
357 if ( !err.isEmpty() && showMsg ) {
358 if ( desktop() && desktop()->isVisible() )
359 SUIT_MessageBox::warning( desktop(), tr( "Error" ), err );
361 qWarning( qPrintable( err ) );
364 char* version = getVersion ? getVersion() : 0;
367 for ( ModuleInfoList::iterator it = myInfoList.begin(); it != myInfoList.end(); ++it ) {
368 if ( (*it).title == modName ) {
369 if( (*it).version.isEmpty() ) {
370 (*it).version = QString(version);
381 \brief Activate module \a modName.
382 \param modName module name
383 \return \c true, if module is loaded and activated successfully and \c false otherwise
385 bool CAM_Application::activateModule( const QString& modName )
387 if ( (!modName.isEmpty() && !activeStudy()) || myBlocked )
390 // VSR 25/10/2011: prevent nested activation/deactivation
391 // See issues 0021307, 0021373
392 BusyLocker lock( myBlocked );
395 if ( !modName.isEmpty() )
397 CAM_Module* mod = module( modName );
398 if ( !mod && !moduleLibrary( modName ).isEmpty() )
400 mod = loadModule( modName );
405 res = activateModule( mod );
408 res = activateModule( 0 );
414 \brief Activate module \a mod.
416 Shows error message if module could not be activated in the current study.
418 \param mod module object pointer
419 \return \c true, if module is loaded and activated successfully and \c false otherwise
421 bool CAM_Application::activateModule( CAM_Module* mod )
423 if ( mod && !activeStudy() )
426 if ( myModule == mod )
431 if ( !myModule->deactivateModule( activeStudy() ) )
439 // Connect the module to the active study
440 myModule->connectToStudy( dynamic_cast<CAM_Study*>( activeStudy() ) );
441 if ( !myModule->activateModule( activeStudy() ) )
443 myModule->setMenuShown( false );
444 myModule->setToolShown( false );
445 QString wrn = tr( "ERROR_ACTIVATE_MODULE_MSG" ).arg( myModule->moduleName() );
446 if ( desktop() && desktop()->isVisible() )
447 SUIT_MessageBox::critical( desktop(), tr( "ERROR_TLT" ), wrn );
449 qWarning( qPrintable( wrn ) );
455 updateCommandsStatus();
461 \brief Load module \a modName and activate its operation, corresponding to \a actionId.
462 This method is dedicated to run operations of some module from any other module.
463 \param modName module name
464 \param actionId is a numerical unique operation identifier
465 \return \c true in case of success and \c false otherwise
467 bool CAM_Application::activateOperation( const QString& modName, int actionId )
469 if (isModuleAccessible(modName)) {
470 CAM_Module* mod = loadModule(modName, false);
473 return mod->activateOperation(actionId);
480 \brief Load module \a modName and activate its operation, corresponding to \a actionId.
481 This method is dedicated to run operations of some module from any other module.
482 \param modName module name
483 \param actionId is a string unique operation identifier
484 \return \c true in case of success and \c false otherwise
486 bool CAM_Application::activateOperation( const QString& modName, const QString& actionId )
488 if (isModuleAccessible(modName)) {
489 CAM_Module* mod = loadModule(modName, false);
492 return mod->activateOperation(actionId);
499 \brief Load module \a modName and activate its operation,
500 corresponding to \a actionId and \a pluginName.
501 This method is dedicated to run operations of some module from any other module.
502 \param modName module name
503 \param actionId is a string unique operation identifier
504 \param pluginName is a name of a plugin where the operation is implemented
505 \return \c true in case of success and \c false otherwise
507 bool CAM_Application::activateOperation( const QString& modName,
508 const QString& actionId,
509 const QString& pluginName )
511 if (isModuleAccessible(modName)) {
512 CAM_Module* mod = loadModule(modName, false);
515 return mod->activateOperation(actionId, pluginName);
522 \brief Create new study.
523 \return study object pointer
525 SUIT_Study* CAM_Application::createNewStudy()
527 return new CAM_Study( this );
531 \brief Update menu commands status.
533 void CAM_Application::updateCommandsStatus()
535 STD_Application::updateCommandsStatus();
537 if ( activeModule() )
538 activeModule()->updateCommandsStatus();
542 \brief Prepare application to study closing.
544 Closes all modules in study \a theDoc.
548 void CAM_Application::beforeCloseDoc( SUIT_Study* theDoc )
550 for ( QList<CAM_Module*>::iterator it = myModules.begin(); it != myModules.end(); ++it )
551 (*it)->studyClosed( theDoc );
554 void CAM_Application::afterCloseDoc()
559 \brief Set active study.
560 \param study study to be made active
562 void CAM_Application::setActiveStudy( SUIT_Study* study )
564 STD_Application::setActiveStudy( study );
568 \brief Callback function, called when the module is added to the application.
570 This virtual method can be re-implemented in the successors. Base implementation
573 \param mod module being added
575 void CAM_Application::moduleAdded( CAM_Module* /*mod*/ )
580 \brief Get module name by its title (user name).
581 \param title module title (user name)
582 \return module name or null QString if module is not found
584 QString CAM_Application::moduleName( const QString& title )
587 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it )
589 if ( (*it).title == title )
596 \brief Get module title (user name) by its name.
597 \param name module name
598 \return module title (user name) or null QString if module is not found
600 QString CAM_Application::moduleTitle( const QString& name )
603 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it )
605 if ( (*it).name == name )
612 \brief Get module icon name.
613 \param name module name
614 \return module icon or null QString if module is not found
616 QString CAM_Application::moduleIcon( const QString& name )
619 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isNull(); ++it )
621 if ( (*it).name == name )
628 \brief Returns \c true if module is accessible for the current application.
629 Singleton module can be loaded only in one application object. In other application
630 objects this module will be unavailable.
631 \param title module title (user name)
632 \return \c true if module is accessible (can be loaded) or \c false otherwise
634 bool CAM_Application::isModuleAccessible( const QString& title )
637 bool blocked = false;
639 QStringList somewhereLoaded;
640 QList<SUIT_Application*> apps = SUIT_Session::session()->applications();
641 foreach( SUIT_Application* app, apps ) {
642 CAM_Application* camApp = dynamic_cast<CAM_Application*>( app );
643 if ( !camApp ) continue;
645 camApp->modules( loaded, true );
646 foreach( QString lm, loaded ) {
647 if ( !somewhereLoaded.contains( lm ) ) somewhereLoaded << lm;
651 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && !found; ++it )
653 found = (*it).title == title;
654 blocked = (*it).isSingleton && somewhereLoaded.contains((*it).title);
656 return found && !blocked;
660 \brief Get module library name by its title (user name).
661 \param title module title (user name)
662 \param full if \c true, return full library name, otherwise return its internal name
663 \return module library name or null QString if module is not found
665 QString CAM_Application::moduleLibrary( const QString& title, const bool full )
668 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it )
670 if ( (*it).title == title )
671 res = (*it).internal;
673 if ( !res.isEmpty() && full )
674 res = SUIT_Tools::library( res );
679 \brief Read modules information list
681 This function first tries to get the modules names list by parsing
682 the application command line arguments, looking for the
683 "--modules ( <mod_name>[:<mod_name>...] )" option.
684 List of modules is separated by colon symbol (":").
686 If "--modules" command line option is not used, the list of modules
687 is retrieved from the application resource file: parameter "modules" of
688 the section "launch".
690 Then the information about each module (module title (user name),
691 library name) is retrieved from the corresponding section of resource
692 file with help of resources manager.
694 Shows the warning message, if module information list is empty.
698 void CAM_Application::readModuleList()
700 if ( !myInfoList.isEmpty() )
703 SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
707 QString args = QApplication::arguments().join( " " );
709 QRegExp rx1("--modules=([\\w,]*)");
710 rx1.setMinimal( false );
711 QRegExp rx2("--modules\\s+\\(\\s*(.*)\\s*\\)");
712 rx2.setMinimal( true );
716 int pos1 = rx1.indexIn( args, pos );
717 int pos2 = rx2.indexIn( args, pos );
718 if ( pos1 != -1 && pos2 != -1 ) {
719 modules = pos1 < pos2 ? rx1.cap( 1 ) : rx2.cap(1);
720 pos = pos1 < pos2 ? pos1 + rx1.matchedLength() : pos2 + rx2.matchedLength();
722 else if ( pos1 != -1 ) {
723 modules = rx1.cap( 1 );
724 pos = pos1 + rx1.matchedLength();
726 else if ( pos2 != -1 ) {
727 modules = rx2.cap( 1 );
728 pos = pos2 + rx2.matchedLength();
735 QStringList mods = modules.split( QRegExp( "[:|,\\s]" ), QString::SkipEmptyParts );
736 for ( int i = 0; i < mods.count(); i++ ) {
737 if ( !mods[i].trimmed().isEmpty() )
738 modList.append( mods[i].trimmed() );
742 if ( modList.isEmpty() ) {
743 QString mods = resMgr->stringValue( "launch", "modules", QString() );
744 modList = mods.split( ",", QString::SkipEmptyParts );
747 for ( QStringList::const_iterator it = modList.begin(); it != modList.end(); ++it )
749 QString modName = (*it).trimmed();
751 if ( modName.isEmpty() )
752 continue; // empty module name
754 if ( !moduleTitle( modName ).isEmpty() )
755 continue; // already added
757 QString modTitle = resMgr->stringValue( *it, "name", QString() );
758 if ( modTitle.isEmpty() )
760 printf( "****************************************************************\n" );
761 printf( "* Warning: %s GUI resources are not found.\n", qPrintable(*it) );
762 printf( "* %s GUI will not be available.\n", qPrintable(*it) );
763 printf( "****************************************************************\n" );
767 QString modIcon = resMgr->stringValue( *it, "icon", QString() );
769 QString modLibrary = resMgr->stringValue( *it, "library", QString() ).trimmed();
770 if ( !modLibrary.isEmpty() )
772 modLibrary = SUIT_Tools::file( modLibrary.trimmed() );
774 QString libExt = QString( "dll" );
776 QString libExt = QString( "so" );
778 if ( SUIT_Tools::extension( modLibrary ).toLower() == libExt )
779 modLibrary.truncate( modLibrary.length() - libExt.length() - 1 );
781 QString prefix = QString( "lib" );
782 if ( modLibrary.startsWith( prefix ) )
783 modLibrary.remove( 0, prefix.length() );
787 modLibrary = modName;
789 bool aIsSingleton = resMgr->booleanValue(*it, "singleton", false);
791 QString ver = resMgr->stringValue(*it, "version", QString());
795 inf.title = modTitle;
796 inf.internal = modLibrary;
798 inf.isSingleton = aIsSingleton;
800 myInfoList.append( inf );
803 if ( myInfoList.isEmpty() ) {
804 if ( desktop() && desktop()->isVisible() )
805 SUIT_MessageBox::warning( desktop(), tr( "Warning" ), tr( "Modules list is empty" ) );
808 printf( "****************************************************************\n" );
809 printf( "* Warning: modules list is empty.\n" );
810 printf( "****************************************************************\n" );
816 \brief Add common menu items to the popup menu.
818 Menu items list is defined by the active module.
820 \param type popup menu context
821 \param menu popup menu
822 \param title popup menu title, which can be set by the module if required
824 void CAM_Application::contextMenuPopup( const QString& type, QMenu* menu, QString& title )
826 // to do : add common items for popup menu ( if they are exist )
827 if ( activeModule() )
828 activeModule()->contextMenuPopup( type, menu, title );
832 \brief Create new empty study.
834 void CAM_Application::createEmptyStudy()
836 /*SUIT_Study* study = */activeStudy();
837 STD_Application::createEmptyStudy();
841 \brief Return information about version of the each module.
843 CAM_Application::ModuleShortInfoList CAM_Application::getVersionInfo()
845 ModuleShortInfoList info;
847 ModuleShortInfo kernel;
848 kernel.name = "KERNEL";
849 kernel.version = KERNEL_VERSION_STR;
854 gui.version = GUI_VERSION_STR;
857 for(int i = 0; i < myInfoList.size(); i++) {
858 ModuleShortInfo infoItem;
859 infoItem.name = myInfoList.at(i).title;
860 infoItem.version = myInfoList.at(i).version;
861 info.append(infoItem);