1 // Copyright (C) 2007-2020 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 CAM_Module* mod = loadModule( (*it).title );
275 QString wrn = tr( "Can not load module %1" ).arg( (*it).title );
276 if ( desktop() && desktop()->isVisible() )
277 SUIT_MessageBox::critical( desktop(), tr( "Loading modules" ), wrn );
279 qWarning( qPrintable( wrn ) );
285 \brief Load module \a modName.
287 The function prints warning message if:
288 - modules information list is empty
289 - modules information list does not include specified module info
290 - module library can not be loaded by some reason
292 \param modName module name
293 \return module object pointer or 0 if module could not be loaded
295 CAM_Module* CAM_Application::loadModule( const QString& modName, const bool showMsg )
297 if ( myInfoList.isEmpty() )
299 qWarning( qPrintable( tr( "Modules configuration is not defined." ) ) );
303 QString libName = moduleLibrary( modName );
304 if ( libName.isEmpty() )
306 qWarning( qPrintable( tr( "Information about module \"%1\" doesn't exist." ).arg( modName ) ) );
311 GET_MODULE_FUNC crtInst = 0;
312 GET_VERSION_FUNC getVersion = 0;
316 LPTSTR str_libname = new TCHAR[libName.length() + 1];
317 str_libname[libName.toWCharArray(str_libname)] = '\0';
319 QByteArray arr = libName.toLatin1();
320 LPTSTR str_libname = arr.constData();
322 HINSTANCE modLib = ::LoadLibrary( str_libname );
329 ::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
330 FORMAT_MESSAGE_IGNORE_INSERTS, 0, ::GetLastError(), 0, (LPTSTR)&lpMsgBuf, 0, 0 );
332 QString out_err = QString::fromWCharArray((LPTSTR)lpMsgBuf);
334 QString out_err = (LPTSTR)lpMsgBuf;
336 err = QString( "Failed to load %1. %2" ).arg( libName ).arg(out_err);
337 ::LocalFree( lpMsgBuf );
341 crtInst = (GET_MODULE_FUNC)::GetProcAddress( modLib, GET_MODULE_NAME );
345 ::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
346 FORMAT_MESSAGE_IGNORE_INSERTS, 0, ::GetLastError(), 0, (LPTSTR)&lpMsgBuf, 0, 0 );
348 QString out_err = QString::fromWCharArray((LPTSTR)lpMsgBuf);
350 QString out_err = (LPTSTR)lpMsgBuf;
353 err = QString( "Failed to find %1 function. %2" ).arg( GET_MODULE_NAME ).arg( out_err );
354 ::LocalFree( lpMsgBuf );
357 getVersion = (GET_VERSION_FUNC)::GetProcAddress( modLib, GET_VERSION_NAME );
360 void* modLib = dlopen( libName.toUtf8(), RTLD_LAZY | RTLD_GLOBAL );
362 err = QString( "Can not load library %1. %2" ).arg( libName ).arg( dlerror() );
365 crtInst = (GET_MODULE_FUNC)dlsym( modLib, GET_MODULE_NAME );
367 err = QString( "Failed to find function %1. %2" ).arg( GET_MODULE_NAME ).arg( dlerror() );
369 getVersion = (GET_VERSION_FUNC)dlsym( modLib, GET_VERSION_NAME );
373 CAM_Module* module = crtInst ? crtInst() : 0;
376 module->setModuleName( modName );
377 module->setName( moduleName( modName ) );
380 if ( !err.isEmpty() && showMsg ) {
381 if ( desktop() && desktop()->isVisible() )
382 SUIT_MessageBox::warning( desktop(), tr( "Error" ), err );
384 qWarning( qPrintable( err ) );
387 char* version = getVersion ? getVersion() : 0;
390 for ( ModuleInfoList::iterator it = myInfoList.begin(); it != myInfoList.end(); ++it ) {
391 if ( (*it).title == modName ) {
392 if( (*it).version.isEmpty() ) {
393 (*it).version = QString(version);
404 \brief Activate module \a modName.
405 \param modName module name
406 \return \c true, if module is loaded and activated successfully and \c false otherwise
408 bool CAM_Application::activateModule( const QString& modName )
410 if ( (!modName.isEmpty() && !activeStudy()) || myBlocked )
413 // VSR 25/10/2011: prevent nested activation/deactivation
414 // See issues 0021307, 0021373
415 BusyLocker lock( myBlocked );
418 if ( !modName.isEmpty() )
420 setProperty("activateModule", true);
421 CAM_Module* mod = module( modName );
423 mod = loadModule( modName );
427 res = activateModule( mod );
428 setProperty("activateModule", QVariant());
431 res = activateModule( 0 );
437 \brief Activate module \a mod.
439 Shows error message if module could not be activated in the current study.
441 \param mod module object pointer
442 \return \c true, if module is loaded and activated successfully and \c false otherwise
444 bool CAM_Application::activateModule( CAM_Module* mod )
446 if ( mod && !activeStudy() )
449 if ( myModule == mod )
454 if ( !myModule->deactivateModule( activeStudy() ) )
462 // Connect the module to the active study
463 myModule->connectToStudy( dynamic_cast<CAM_Study*>( activeStudy() ) );
464 if ( !myModule->activateModule( activeStudy() ) )
466 myModule->setMenuShown( false );
467 myModule->setToolShown( false );
468 QString wrn = tr( "ERROR_ACTIVATE_MODULE_MSG" ).arg( myModule->moduleName() );
469 if ( desktop() && desktop()->isVisible() )
470 SUIT_MessageBox::critical( desktop(), tr( "ERROR_TLT" ), wrn );
472 qWarning( qPrintable( wrn ) );
478 updateCommandsStatus();
484 \brief Load module \a modName and activate its operation, corresponding to \a actionId.
485 This method is dedicated to run operations of some module from any other module.
486 \param modName module name
487 \param actionId is a numerical unique operation identifier
488 \return \c true in case of success and \c false otherwise
490 bool CAM_Application::activateOperation( const QString& modName, int actionId )
492 CAM_Module* mod = loadModule(modName, false);
495 return mod->activateOperation(actionId);
501 \brief Load module \a modName and activate its operation, corresponding to \a actionId.
502 This method is dedicated to run operations of some module from any other module.
503 \param modName module name
504 \param actionId is a string unique operation identifier
505 \return \c true in case of success and \c false otherwise
507 bool CAM_Application::activateOperation( const QString& modName, const QString& actionId )
509 CAM_Module* mod = loadModule(modName, false);
512 return mod->activateOperation(actionId);
518 \brief Load module \a modName and activate its operation,
519 corresponding to \a actionId and \a pluginName.
520 This method is dedicated to run operations of some module from any other module.
521 \param modName module name
522 \param actionId is a string unique operation identifier
523 \param pluginName is a name of a plugin where the operation is implemented
524 \return \c true in case of success and \c false otherwise
526 bool CAM_Application::activateOperation( const QString& modName,
527 const QString& actionId,
528 const QString& pluginName )
530 CAM_Module* mod = loadModule(modName, false);
533 return mod->activateOperation(actionId, pluginName);
539 \brief Create new study.
540 \return study object pointer
542 SUIT_Study* CAM_Application::createNewStudy()
544 return new CAM_Study( this );
548 \brief Update menu commands status.
550 void CAM_Application::updateCommandsStatus()
552 STD_Application::updateCommandsStatus();
554 if ( activeModule() )
555 activeModule()->updateCommandsStatus();
559 \brief Prepare application to study closing.
561 Closes all modules in study \a theDoc.
565 void CAM_Application::beforeCloseDoc( SUIT_Study* theDoc )
567 for ( QList<CAM_Module*>::iterator it = myModules.begin(); it != myModules.end(); ++it )
568 (*it)->studyClosed( theDoc );
571 void CAM_Application::afterCloseDoc()
576 \brief Set active study.
577 \param study study to be made active
579 void CAM_Application::setActiveStudy( SUIT_Study* study )
581 STD_Application::setActiveStudy( study );
585 \brief Check module availability.
587 The method can be redefined in successors. Default implementation returns \c true.
589 \param title module title
590 \return \c true if module is accessible; \c false otherwise
592 bool CAM_Application::checkModule( const QString& )
598 \brief Callback function, called when the module is added to the application.
600 This virtual method can be re-implemented in the successors. Base implementation
603 \param mod module being added
605 void CAM_Application::moduleAdded( CAM_Module* /*mod*/ )
610 \brief Get module name by its title (user name).
611 \param title module title (user name)
612 \return module name or null QString if module is not found
614 QString CAM_Application::moduleName( const QString& title )
617 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it )
619 if ( (*it).title == title )
626 \brief Get module title (user name) by its name.
627 \param name module name
628 \return module title (user name) or null QString if module is not found
630 QString CAM_Application::moduleTitle( const QString& name )
633 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it )
635 if ( (*it).name == name )
642 \brief Get module icon name.
643 \param name module name
644 \return module icon or null QString if module is not found
646 QString CAM_Application::moduleIcon( const QString& name )
649 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isNull(); ++it )
651 if ( (*it).name == name )
658 \brief Get module library name by its title (user name).
659 \param title module title (user name)
660 \param full if \c true, return full library name, otherwise return its internal name
661 \return module library name or null QString if module is not found
663 QString CAM_Application::moduleLibrary( const QString& title, const bool full )
666 for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it )
668 if ( (*it).title == title )
671 if ( !res.isEmpty() && full )
672 res = SUIT_Tools::library( res );
677 \brief Read modules information list
679 This function first tries to get the modules names list by parsing
680 the application command line arguments, looking for the
681 "--modules ( <mod_name>[:<mod_name>...] )" option.
682 List of modules is separated by colon symbol (":").
684 If "--modules" command line option is not used, the list of modules
685 is retrieved from the application resource file: parameter "modules" of
686 the section "launch".
688 Then the information about each module (module title (user name),
689 library name) is retrieved from the corresponding section of resource
690 file with help of resources manager.
692 Shows the warning message, if module information list is empty.
696 void CAM_Application::readModuleList()
698 if ( !myInfoList.isEmpty() )
701 SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
705 QString args = QApplication::arguments().join( " " );
707 QRegExp rx1("--modules=([\\w,]*)");
708 rx1.setMinimal( false );
709 QRegExp rx2("--modules\\s+\\(\\s*(.*)\\s*\\)");
710 rx2.setMinimal( true );
714 int pos1 = rx1.indexIn( args, pos );
715 int pos2 = rx2.indexIn( args, pos );
716 if ( pos1 != -1 && pos2 != -1 ) {
717 modules = pos1 < pos2 ? rx1.cap( 1 ) : rx2.cap(1);
718 pos = pos1 < pos2 ? pos1 + rx1.matchedLength() : pos2 + rx2.matchedLength();
720 else if ( pos1 != -1 ) {
721 modules = rx1.cap( 1 );
722 pos = pos1 + rx1.matchedLength();
724 else if ( pos2 != -1 ) {
725 modules = rx2.cap( 1 );
726 pos = pos2 + rx2.matchedLength();
733 QStringList mods = modules.split( QRegExp( "[:|,\\s]" ), QString::SkipEmptyParts );
734 for ( int i = 0; i < mods.count(); i++ ) {
735 if ( !mods[i].trimmed().isEmpty() )
736 modList.append( mods[i].trimmed() );
740 if ( modList.isEmpty() ) {
741 QString mods = resMgr->stringValue( "launch", "modules", QString() );
742 modList = mods.split( ",", QString::SkipEmptyParts );
745 for ( QStringList::const_iterator it = modList.begin(); it != modList.end(); ++it )
747 QString modName = (*it).trimmed();
749 if ( modName.isEmpty() )
750 continue; // empty module name
752 if ( !moduleTitle( modName ).isEmpty() )
753 continue; // already added
755 if ( modName == "KERNEL" || modName == "GUI" )
756 continue; // omit KERNEL and GUI modules
758 bool hasGui = resMgr->booleanValue( *it, "gui", true );
760 continue; // omit if module is explicitly declared as not having GUI
762 QString modTitle = resMgr->stringValue( *it, "name", QString() );
763 if ( modTitle.isEmpty() )
765 printf( "****************************************************************\n" );
766 printf( " Warning: module %s is improperly configured!\n", qPrintable(*it) );
767 printf( " Module %s will not be available in GUI mode!\n", qPrintable(*it) );
768 printf( "****************************************************************\n" );
772 QString modIcon = resMgr->stringValue( *it, "icon", QString() );
774 QString modLibrary = resMgr->stringValue( *it, "library", QString() ).trimmed();
775 if ( !modLibrary.isEmpty() )
777 modLibrary = SUIT_Tools::file( modLibrary.trimmed() );
779 QString libExt = QString( "dll" );
780 #elif defined(__APPLE__)
781 QString libExt = QString( "dylib" );
783 QString libExt = QString( "so" );
785 if ( SUIT_Tools::extension( modLibrary ).toLower() == libExt )
786 modLibrary.truncate( modLibrary.length() - libExt.length() - 1 );
788 QString prefix = QString( "lib" );
789 if ( modLibrary.startsWith( prefix ) )
790 modLibrary.remove( 0, prefix.length() );
794 modLibrary = modName;
796 QString version = resMgr->stringValue( *it, "version", QString() );
800 inf.title = modTitle;
801 inf.status = hasGui ? stUnknown : stNoGui;
802 if ( hasGui ) inf.library = modLibrary;
804 inf.version = version;
805 myInfoList.append( inf );
808 if ( myInfoList.isEmpty() ) {
809 if ( desktop() && desktop()->isVisible() )
810 SUIT_MessageBox::warning( desktop(), tr( "Warning" ), tr( "Modules list is empty" ) );
813 printf( "****************************************************************\n" );
814 printf( "* Warning: modules list is empty.\n" );
815 printf( "****************************************************************\n" );
821 \brief Add common menu items to the popup menu.
823 Menu items list is defined by the active module.
825 \param type popup menu context
826 \param menu popup menu
827 \param title popup menu title, which can be set by the module if required
829 void CAM_Application::contextMenuPopup( const QString& type, QMenu* menu, QString& title )
831 // to do : add common items for popup menu ( if they are exist )
832 if ( activeModule() )
833 activeModule()->contextMenuPopup( type, menu, title );
837 \brief Create new empty study.
839 void CAM_Application::createEmptyStudy()
841 /*SUIT_Study* study = */activeStudy();
842 STD_Application::createEmptyStudy();
846 \brief Return information about version of the each module.
848 CAM_Application::ModuleShortInfoList CAM_Application::getVersionInfo()
850 ModuleShortInfoList info;
852 ModuleShortInfo kernel;
853 kernel.name = "KERNEL";
854 kernel.version = KERNEL_VERSION_STR;
859 gui.version = GUI_VERSION_STR;
862 for(int i = 0; i < myInfoList.size(); i++) {
863 ModuleShortInfo infoItem;
864 infoItem.name = myInfoList.at(i).title;
865 infoItem.version = myInfoList.at(i).version;
866 info.append(infoItem);
872 \brief Abort active operations if there are any
874 Iterates through all modules and asks each of them if there are pending operations that cannot be aborted.
876 \return \c false if some operation cannot be aborted
878 bool CAM_Application::abortAllOperations()
881 for ( QList<CAM_Module*>::const_iterator it = myModules.begin(); it != myModules.end() && aborted; ++it )
883 aborted = (*it)->abortAllOperations();