From 6ebe476d3bc01ddc4f7529ff63807a8153a6cb95 Mon Sep 17 00:00:00 2001 From: vsr Date: Thu, 21 Apr 2022 16:07:06 +0300 Subject: [PATCH] bos #29458 Salome on demand --- CMakeLists.txt | 6 + SalomeGUIConfig.cmake.in | 4 + doc/salome/gui/images/modules_add.png | Bin 0 -> 408 bytes doc/salome/gui/images/modules_remove.png | Bin 0 -> 676 bytes doc/salome/gui/images/modules_toolbar.png | Bin 0 -> 14219 bytes doc/salome/gui/input/extend_salome.rst | 85 +++ .../gui/input/howtos_and_best_practives.rst | 2 +- src/CAM/CAM_Application.cxx | 134 +++-- src/CAM/CAM_Application.h | 18 +- src/LightApp/CMakeLists.txt | 3 +- src/LightApp/LightApp_Application.cxx | 549 +++++++++++------- src/LightApp/LightApp_Application.h | 32 +- src/LightApp/LightApp_ModuleAction.cxx | 198 ++++--- src/LightApp/LightApp_ModuleAction.h | 28 +- src/LightApp/LightApp_Preferences.cxx | 15 + src/LightApp/LightApp_Preferences.h | 1 + src/LightApp/resources/LightApp_images.ts | 12 +- src/LightApp/resources/LightApp_msg_en.ts | 64 ++ src/LightApp/resources/LightApp_msg_fr.ts | 64 ++ src/LightApp/resources/LightApp_msg_ja.ts | 64 ++ src/LightApp/resources/icon_add_module.png | Bin 0 -> 807 bytes src/LightApp/resources/icon_module.png | Bin 1712 -> 3958 bytes src/LightApp/resources/icon_module_big.png | Bin 3958 -> 0 bytes src/LightApp/resources/icon_remove_module.png | Bin 0 -> 2016 bytes src/Qtx/Qtx.cxx | 53 +- src/Qtx/Qtx.h | 2 + src/Qtx/QtxResourceMgr.cxx | 295 +++++++++- src/Qtx/QtxResourceMgr.h | 4 + src/STD/STD_Application.cxx | 17 +- src/STD/STD_Application.h | 3 +- src/SUIT/SUIT_PreferenceMgr.cxx | 12 + src/SUIT/SUIT_PreferenceMgr.h | 1 + src/SalomeApp/SalomeApp_Application.cxx | 19 + src/SalomeApp/SalomeApp_Application.h | 2 + 34 files changed, 1290 insertions(+), 397 deletions(-) create mode 100644 doc/salome/gui/images/modules_add.png create mode 100644 doc/salome/gui/images/modules_remove.png create mode 100644 doc/salome/gui/images/modules_toolbar.png create mode 100644 doc/salome/gui/input/extend_salome.rst create mode 100644 src/LightApp/resources/icon_add_module.png delete mode 100644 src/LightApp/resources/icon_module_big.png create mode 100644 src/LightApp/resources/icon_remove_module.png diff --git a/CMakeLists.txt b/CMakeLists.txt index 06d28fc02..069b7fea0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -63,6 +63,7 @@ SET(BUILD_SHARED_LIBS TRUE) OPTION(SALOME_BUILD_DOC "Generate SALOME GUI documentation" ON) OPTION(SALOME_BUILD_TESTS "Build SALOME tests" ON) OPTION(SALOME_GUI_USE_OBSERVERS "Use study observers in GUI (advanced)" ON) +OPTION(SALOME_ON_DEMAND "Activate SALOME on demand feature" OFF) CMAKE_DEPENDENT_OPTION(SALOME_GUI_BUILD_FRENCH_DOC "Generate SALOME GUI French documentation" OFF "SALOME_BUILD_DOC" OFF) @@ -136,6 +137,11 @@ IF(SALOME_GUI_USE_OBSERVERS) ADD_DEFINITIONS(-DWITH_SALOMEDS_OBSERVER) ENDIF() +# SALOME on demand +IF(SALOME_ON_DEMAND) + ADD_DEFINITIONS(-DWITH_SALOME_ON_DEMAND) +ENDIF() + # Single-study GUI IF(SALOME_USE_SINGLE_DESKTOP) ADD_DEFINITIONS(-DSINGLE_DESKTOP) diff --git a/SalomeGUIConfig.cmake.in b/SalomeGUIConfig.cmake.in index 6edcf645d..62159e832 100644 --- a/SalomeGUIConfig.cmake.in +++ b/SalomeGUIConfig.cmake.in @@ -64,7 +64,11 @@ SET(SALOME_USE_PYCONSOLE @SALOME_USE_PYCONSOLE@) SET(SALOME_USE_SALOMEOBJECT @SALOME_USE_SALOMEOBJECT@) SET(SALOME_GUI_USE_OBSERVERS @SALOME_GUI_USE_OBSERVERS@) SET(SALOME_GUI_USE_OPENGL2 @SALOME_GUI_USE_OPENGL2@) +SET(SALOME_ON_DEMAND @SALOME_ON_DEMAND@) +IF(SALOME_ON_DEMAND) + LIST(APPEND GUI_DEFINITIONS "-DWITH_SALOME_ON_DEMAND") +ENDIF() IF(SALOME_GUI_LIGHT_ONLY) LIST(APPEND GUI_DEFINITIONS "-DGUI_DISABLE_CORBA") ENDIF() diff --git a/doc/salome/gui/images/modules_add.png b/doc/salome/gui/images/modules_add.png new file mode 100644 index 0000000000000000000000000000000000000000..0e37c5cdbca87aa542a24dac72911e390561e028 GIT binary patch literal 408 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 QStringList modList; @@ -801,88 +802,85 @@ void CAM_Application::readModuleList() modList = mods.split( ",", QString::SkipEmptyParts ); } - for ( QStringList::const_iterator it = modList.begin(); it != modList.end(); ++it ) - { - QString modName = (*it).trimmed(); - - if ( modName.isEmpty() ) - continue; // empty module name - - if ( !moduleTitle( modName ).isEmpty() ) - continue; // already added + // extra modules loaded manually on previous session + // ... - if ( modName == "KERNEL" || modName == "GUI" ) - continue; // omit KERNEL and GUI modules + foreach ( QString modName, modList ) + appendModuleInfo( modName.trimmed() ); - bool hasGui = resMgr->booleanValue( *it, "gui", true ); - - QString modTitle, modIcon, modLibrary, modDescription; - - if ( hasGui ) - { - // if module has GUI, check that it is present - modTitle = resMgr->stringValue( *it, "name", QString() ); - if ( modTitle.isEmpty() ) + if ( myInfoList.isEmpty() ) { + if ( desktop() && desktop()->isVisible() ) + SUIT_MessageBox::warning( desktop(), tr( "Warning" ), tr( "Modules list is empty" ) ); + else { printf( "****************************************************************\n" ); - printf( " Warning: module %s is improperly configured!\n", qPrintable(*it) ); - printf( " Module %s will not be available in GUI mode!\n", qPrintable(*it) ); + printf( "* Warning: modules list is empty.\n" ); printf( "****************************************************************\n" ); - continue; } + } +} - modIcon = resMgr->stringValue( *it, "icon", QString() ); +bool CAM_Application::appendModuleInfo( const QString& modName ) +{ + if ( modName.isEmpty() ) + return false; // empty module name - modDescription = resMgr->stringValue( *it, "description", QString() ); + if ( !moduleTitle( modName ).isEmpty() ) + return false; // already added - modLibrary = resMgr->stringValue( *it, "library", QString() ).trimmed(); - if ( !modLibrary.isEmpty() ) - { - modLibrary = SUIT_Tools::file( modLibrary.trimmed() ); -#if defined(WIN32) - QString libExt = QString( "dll" ); -#elif defined(__APPLE__) - QString libExt = QString( "dylib" ); -#else - QString libExt = QString( "so" ); -#endif - if ( SUIT_Tools::extension( modLibrary ).toLower() == libExt ) - modLibrary.truncate( modLibrary.length() - libExt.length() - 1 ); -#ifndef WIN32 - QString prefix = QString( "lib" ); - if ( modLibrary.startsWith( prefix ) ) - modLibrary.remove( 0, prefix.length() ); -#endif - } - else - modLibrary = modName; - } + if ( modName == "KERNEL" || modName == "GUI" ) + return false; // skip KERNEL and GUI modules - QString version = resMgr->stringValue( *it, "version", QString() ); + // we cannot use own resourceMgr() as this method can be called from constructor + SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr(); - QString modDisplayer = resMgr->stringValue( *it, "displayer", QString() ); + // "gui" option explicitly says that module has GUI + bool hasGui = resMgr->booleanValue( modName, "gui", true ); - ModuleInfo inf; - inf.name = modName; - inf.title = modTitle; - inf.status = hasGui ? stUnknown : stNoGui; - if ( hasGui ) inf.library = modLibrary; - inf.icon = modIcon; - inf.description = modDescription; - inf.displayer = modDisplayer; - inf.version = version; - myInfoList.append( inf ); + ModuleInfo inf; + + // module internal name + inf.name = modName; + // module version + inf.version = resMgr->stringValue( modName, "version", QString() ).trimmed(); + // displayer, if module does not have GUI, displayer may be delegated to other module + inf.displayer = resMgr->stringValue( modName, "displayer", QString() ).trimmed(); + // status; if module has GUI, availability will be checked on activation + inf.status = hasGui ? stUnknown : stNoGui; + + if ( hasGui ) + { + // module with GUI must explicitly specify title (GUI name) + inf.title = resMgr->stringValue( modName, "name", QString() ).trimmed(); + if ( inf.title.isEmpty() ) + inf.status = stInvalid; + // icon + inf.icon = resMgr->stringValue( modName, "icon", QString() ).trimmed(); + // description, for Info panel + inf.description = resMgr->stringValue( modName, "description", QString() ); + // library; if not specified, we use internal module name + inf.library = SUIT_Tools::libraryName( resMgr->stringValue( modName, "library", QString() ).trimmed() ); + if ( inf.library.isEmpty() ) + inf.library = modName; } - if ( myInfoList.isEmpty() ) { - if ( desktop() && desktop()->isVisible() ) - SUIT_MessageBox::warning( desktop(), tr( "Warning" ), tr( "Modules list is empty" ) ); - else - { - printf( "****************************************************************\n" ); - printf( "* Warning: modules list is empty.\n" ); - printf( "****************************************************************\n" ); - } + if ( inf.status != stInvalid ) + myInfoList.append( inf ); + + return true; +} + +void CAM_Application::removeModuleInfo( const QString& modName ) +{ + QMutableListIterator it( myInfoList ); + while ( it.hasNext() ) + { + ModuleInfo info = it.next(); + if ( info.name == modName ) + { + it.remove(); + break; + } } } diff --git a/src/CAM/CAM_Application.h b/src/CAM/CAM_Application.h index 3dc79da71..daa7624e5 100644 --- a/src/CAM/CAM_Application.h +++ b/src/CAM/CAM_Application.h @@ -97,21 +97,25 @@ protected: virtual bool abortAllOperations(); -private: - void readModuleList(); +protected: + bool appendModuleInfo( const QString& ); + void removeModuleInfo( const QString& ); private: - enum { stUnknown = 0, stNoGui, stInaccessible, stReady }; - typedef struct { + enum { stInvalid = -1, stUnknown = 0, stNoGui, stInaccessible, stReady }; + struct ModuleInfo + { QString name, title, icon, library, version, description, displayer; int status; - } ModuleInfo; - typedef QList ModuleInfoList; + ModuleInfo() : status( stInvalid ) {} + }; + void readModuleList(); private: + typedef QList ModuleInfoList; + static ModuleInfoList myInfoList; //!< modules info list CAM_Module* myModule; //!< active module ModuleList myModules; //!< loaded modules list - static ModuleInfoList myInfoList; //!< modules info list bool myAutoLoad; //!< auto loading flag bool myBlocked; //!< "blocked" flag, internal usage }; diff --git a/src/LightApp/CMakeLists.txt b/src/LightApp/CMakeLists.txt index efb0579fe..993298328 100644 --- a/src/LightApp/CMakeLists.txt +++ b/src/LightApp/CMakeLists.txt @@ -224,10 +224,11 @@ SET(_other_RESOURCES resources/icon_applogo.png resources/icon_default.png resources/icon_module.png - resources/icon_module_big.png resources/icon_select.png resources/icon_earth.png resources/icon_life_ring.png + resources/icon_add_module.png + resources/icon_remove_module.png resources/LightApp.xml ) diff --git a/src/LightApp/LightApp_Application.cxx b/src/LightApp/LightApp_Application.cxx index e64fef2c7..718efbd13 100644 --- a/src/LightApp/LightApp_Application.cxx +++ b/src/LightApp/LightApp_Application.cxx @@ -323,6 +323,13 @@ namespace } return QString(); } + + const bool HAS_SALOME_ON_DEMAND = +#if defined(WITH_SALOME_ON_DEMAND) + true; +#else + false; +#endif } /*!Create new instance of LightApp_Application.*/ @@ -625,8 +632,6 @@ void LightApp_Application::createActions() int helpMenu = createMenu( tr( "MEN_DESK_HELP" ), -1, -1, 1000 ); - int id = LightApp_Application::UserID + FIRST_HELP_ID; - QString url; // a) Link to web site @@ -688,87 +693,17 @@ void LightApp_Application::createActions() // e) Help for modules - // - First create top-level menus to preserve correct order - QString userGuide = "User's Guide"; - QString devGuide = "Developer's Guide"; - createMenu( userGuide, helpMenu, -1, 5 ); - createMenu( devGuide, helpMenu, -1, 5 ); - QStringList aModuleList; modules( aModuleList, false ); aModuleList.prepend( "GUI" ); aModuleList.prepend( "KERNEL" ); - QString aModule; - foreach( aModule, aModuleList ) { - if ( aModule.isEmpty() ) // module title (user name) - continue; - IMap helpData; // list of help files for the module - QString helpSubMenu; // help submenu name (empty if not needed) - QString modName = moduleName( aModule ); // module name - if ( modName.isEmpty() ) modName = aModule; // for KERNEL and GUI - QString rootDir = QString( "%1_ROOT_DIR" ).arg( modName ); // module root dir env variable - QString modDir = Qtx::getenv( rootDir.toUtf8().constData() ); // module root dir path - QString docSection; - if (resMgr->hasValue( modName, "documentation" ) ) - docSection = resMgr->stringValue(modName, "documentation"); - else if ( resMgr->hasSection( modName + "_documentation" ) ) - docSection = modName + "_documentation"; - if ( !docSection.isEmpty() ) { - helpSubMenu = resMgr->stringValue( docSection, "sub_menu", "" ); - if ( helpSubMenu.contains( "%1" ) ) - helpSubMenu = helpSubMenu.arg( aModule ); - foreach( QString paramName, resMgr->parameters( docSection ) ) { - QString key = paramName.contains( "%1" ) ? paramName.arg( aModule ) : paramName; - QString helpItem = getHelpItem( docSection, paramName ); - if ( !helpItem.isEmpty() ) - helpData.insert( key, helpItem ); - } - } - - if ( helpData.isEmpty() && !modDir.isEmpty() ) { - QStringList idxLst = QStringList() << modDir << "share" << "doc" << "salome" << "gui" << modName << "index.html"; - QString indexFile = idxLst.join( QDir::separator() ); // index file - if ( QFile::exists( indexFile ) ) - helpData.insert( tr( "%1 module Users's Guide" ).arg( aModule ), indexFile ); - } - - IMapConstIterator fileIt; - for ( fileIt = helpData.begin(); fileIt != helpData.end(); fileIt++ ) { - QString helpItemPath = fileIt.key(); - // remove all '//' occurances - while ( helpItemPath.contains( "//" ) ) - helpItemPath.replace( "//", "" ); - // obtain submenus hierarchy if given - QStringList smenus = helpItemPath.split( "/" ); - helpItemPath = smenus.takeLast(); - // workaround for User's Guide and Developer's Guide to avoid having single item in module's submenu. - if ( helpItemPath == userGuide || helpItemPath == devGuide ) { - QString menuPath = smenus.join( "/" ); - QStringList allKeys = helpData.keys(); - QStringList total = allKeys.filter( QRegExp( QString( "^%1" ).arg( menuPath ) ) ); - if ( total.count() == 1 && smenus.count() > 0 ) - helpItemPath = smenus.takeLast(); - } - QPixmap helpIcon = fileIt.value().startsWith( "http", Qt::CaseInsensitive ) ? - resMgr->loadPixmap( "STD", tr( "ICON_WWW" ), false ) : resMgr->loadPixmap( "STD", tr( "ICON_HELP" ), false ); - QAction* a = createAction( id, helpItemPath, helpIcon, helpItemPath, helpItemPath, - 0, desk, false, this, SLOT( onHelpContentsModule() ) ); - a->setData( fileIt.value() ); - if ( !helpSubMenu.isEmpty() ) { - smenus.prepend( helpSubMenu ); - } - // create sub-menus hierarchy - int menuId = helpMenu; - foreach ( QString subMenu, smenus ) - menuId = createMenu( subMenu, menuId, -1, 5 ); - createMenu( a, menuId, -1, ( menuId != helpMenu && (helpItemPath == userGuide || helpItemPath == devGuide) ) ? 0 : 5 ); - id++; - } - } - - // - Additional help items + foreach( QString aModule, aModuleList ) + createHelpItems( aModule ); + + // f) Additional help items + int id = LightApp_Application::UserID + FIRST_HELP_ID + 1000; createMenu( separator(), helpMenu, -1, 10 ); QStringList addHelpItems = resMgr->parameters( "add_help" ); @@ -790,57 +725,24 @@ void LightApp_Application::createActions() connect( mru, SIGNAL( activated( const QString& ) ), this, SLOT( onMRUActivated( const QString& ) ) ); registerAction( MRUId, mru ); - // default icon for neutral point ('SALOME' module) - QPixmap defIcon = resMgr->loadPixmap( "LightApp", tr( "APP_DEFAULT_ICO" ), false ); - if ( defIcon.isNull() ) - defIcon = QPixmap( imageEmptyIcon ); - - //! default icon for any module - QPixmap modIcon = resMgr->loadPixmap( "LightApp", tr( "APP_MODULE_ICO" ), false ); - if ( modIcon.isNull() ) - modIcon = QPixmap( imageEmptyIcon ); - + // List of modules + LightApp_ModuleAction* moduleAction = new LightApp_ModuleAction( resMgr, desk ); + registerAction( ModulesListId, moduleAction ); + // a. here we add regular modules (specified to GUI via --modules cmd line option, or default list from configuration) + // b. custom modules are added in customize() method QStringList modList; modules( modList, false ); - - if ( modList.count() > 1 ) - { - LightApp_ModuleAction* moduleAction = - new LightApp_ModuleAction( tr( "APP_NAME" ), defIcon, desk ); - - QMap iconMap; - moduleIconNames( iconMap ); - - const int iconSize = 20; - - QStringList::Iterator it; - for ( it = modList.begin(); it != modList.end(); ++it ) - { - QString modName = moduleName( *it ); - - QString iconName; - if ( iconMap.contains( *it ) ) - iconName = iconMap[*it]; - - QPixmap icon = resMgr->loadPixmap( modName, iconName, false ); - if ( icon.isNull() ) - { - icon = modIcon; - INFOS( std::endl << - "****************************************************************" << std::endl << - " Warning: icon for " << qPrintable(*it) << " is not found!" << std::endl << - " Using the default icon." << std::endl << - "****************************************************************" << std::endl); - } - icon = Qtx::scaleIcon( icon, iconSize ); - - moduleAction->insertModule( *it, icon ); - } - - connect( moduleAction, SIGNAL( moduleActivated( const QString& ) ), - this, SLOT( onModuleActivation( const QString& ) ) ); - registerAction( ModulesListId, moduleAction ); - } + foreach ( QString aModule, modList ) + moduleAction->insertModule( aModule, moduleIcon( aModule, 20 ) ); // scale icon to 20x20 pix + + connect( this, SIGNAL( moduleActivated( QString ) ), + moduleAction, SLOT( setActiveModule( QString ) ) ); + connect( moduleAction, SIGNAL( moduleActivated( const QString& ) ), + this, SLOT( onModuleActivation( const QString& ) ) ); + connect( moduleAction, SIGNAL( adding() ), + this, SLOT( onModuleAdding() ) ); + connect( moduleAction, SIGNAL( removing( QString ) ), + this, SLOT( onModuleRemoving( QString ) ) ); // New window int windowMenu = createMenu( tr( "MEN_DESK_WINDOW" ), -1, MenuWindowId, 100 ); @@ -910,26 +812,44 @@ void LightApp_Application::createActions() createMenu( StyleId, viewMenu, 20, -1 ); #endif // USE_SALOME_STYLE createMenu( FullScreenId, viewMenu, 20, -1 ); + createMenu( separator(), viewMenu, -1, 20, -1 ); + createMenu( ModulesListId, viewMenu ); int modTBar = createTool( tr( "INF_TOOLBAR_MODULES" ), // title (language-dependant) QString( "SalomeModules" ) ); // name (language-independant) createTool( ModulesListId, modTBar ); } +/*! + Customize actions. +*/ +void LightApp_Application::customize() +{ + // List of modules + LightApp_ModuleAction* moduleAction = qobject_cast( action( ModulesListId ) ); + // a. regular modules were added in createActions() method + // b. here we add custom modules (manually added by the user) + if ( HAS_SALOME_ON_DEMAND ) + { + QStringList modList = resourceMgr()->stringValue( "launch", "user_modules" ).split( ";", QString::SkipEmptyParts ); + foreach ( QString aModule, modList ) + addUserModule( aModule, resourceMgr()->stringValue( "user_modules", aModule ) ); + } + else + { + moduleAction->setModeEnabled( LightApp_ModuleAction::AddRemove, false ); + } +} + /*!On module activation action.*/ -void LightApp_Application::onModuleActivation( const QString& modName ) +void LightApp_Application::onModuleActivation( const QString& modTitle ) { // Force user to create/open a study before module activation - QMap iconMap; - moduleIconNames( iconMap ); - QPixmap icon = resourceMgr()->loadPixmap( moduleName( modName ), iconMap[ modName ], false ); - if ( icon.isNull() ) - icon = resourceMgr()->loadPixmap( "LightApp", tr( "APP_MODULE_BIG_ICO" ), false ); // default icon for any module - + QPixmap icon = moduleIcon( modTitle ); bool cancelled = false; - while ( !modName.isEmpty() && !activeStudy() && !cancelled ){ - LightApp_ModuleDlg aDlg( desktop(), modName, icon ); + while ( !modTitle.isEmpty() && !activeStudy() && !cancelled ){ + LightApp_ModuleDlg aDlg( desktop(), modTitle, icon ); QMap opmap = activateModuleActions(); for ( QMap::ConstIterator it = opmap.begin(); it != opmap.end(); ++it ) aDlg.addButton( it.value(), it.key() ); @@ -942,17 +862,186 @@ void LightApp_Application::onModuleActivation( const QString& modName ) else { // cancelled putInfo( tr("INF_CANCELLED") ); - - LightApp_ModuleAction* moduleAction = - qobject_cast( action( ModulesListId ) ); - if ( moduleAction ) - moduleAction->setActiveModule( QString() ); + emit moduleActivated( QString() ); cancelled = true; } } if ( !cancelled ) - activateModule( modName ); + activateModule( modTitle ); +} + +/*!On module adding action.*/ +void LightApp_Application::onModuleAdding() +{ + // show dialog to browse configuration file + QStringList filters = ( QStringList() << tr( "Config files") + " (*.salomex)" << tr( "All files" ) + " (*)" ); + QStringList paths = getOpenFileNames( QString(), filters.join( ";;" ), QString(), desktop() ); + if ( paths.isEmpty() ) // cancelled + return; + + // loop via selected configuration files + foreach( QString path, paths ) + { + // read description file (.salomex) and check it's OK + QtxResourceMgr resMgr; + if ( !resMgr.addResource( path ) ) + { + SUIT_MessageBox::warning( desktop(), tr( "WRN_WARNING" ), tr( "WRN_MODULE_BAD_SALOMEX_FILE" ).arg( path ) ); + continue; + } + // retrieve module name + QString name = resMgr.stringValue( "General", "name" ).trimmed(); + if ( name.isEmpty() ) + { + SUIT_MessageBox::warning( desktop(), tr( "WRN_WARNING" ), tr( "WRN_MODULE_EMPTY_NAME" ).arg( path ) ); + continue; + } + // retrieve root directory + QString root = resMgr.stringValue( "General", "root" ).trimmed(); + if ( root.isEmpty() ) + { + SUIT_MessageBox::warning( desktop(), tr( "WRN_WARNING" ), tr( "WRN_MODULE_EMPTY_ROOT" ).arg( path ) ); + continue; + } + addUserModule( name, root, true ); + } +} + +/*Add user module.*/ +bool LightApp_Application::addUserModule( const QString& name, const QString& root, bool interactive ) +{ + if ( name.isEmpty() || root.isEmpty() ) + return false; + + if ( !moduleTitle( name ).isEmpty() ) // module alread in current session + { + if ( interactive ) + SUIT_MessageBox::warning( desktop(), tr( "WRN_WARNING" ), tr( "WRN_MODULE_DUPLICATED" ).arg( name ) ); + return false; + } + if ( !QFileInfo( root ).exists() ) // root directory does not exist + { + if ( interactive ) + SUIT_MessageBox::warning( desktop(), tr( "WRN_WARNING" ), tr( "WRN_MODULE_ROOT_DOES_NOT_EXIST" ).arg( root ) ); + return false; + } + // resources directory + QString resDir = Qtx::joinPath( QStringList() << root << "share" << "salome" << "resources" << name.toLower() ); + if ( !QFileInfo( resDir ).exists() ) // resources directory does not exist + { + if ( interactive ) + SUIT_MessageBox::warning( desktop(), tr( "WRN_WARNING" ), tr( "WRN_MODULE_BAD_RESDIR" ).arg( resDir ) ); + return false; + } + // read XML configuration file + resourceMgr()->setConstant( QString( "%1_ROOT_DIR" ).arg( name ), root ); + if ( !resourceMgr()->addResource( resDir ) ) // cannot read configuration + { + if ( interactive ) + SUIT_MessageBox::warning( desktop(), tr( "WRN_WARNING" ), tr( "WRN_MODULE_CANNOT_READ_CFG" ).arg( resDir ) ); + return false; + } + // fill in information about module + if ( !appendModuleInfo( name ) ) // cannot append module information to internal table + { + if ( interactive ) + SUIT_MessageBox::warning( desktop(), tr( "WRN_WARNING" ), tr( "WRN_MODULE_BAD_CFG_FILE" ).arg( name ) ); + return false; + } + // load translations + resourceMgr()->loadLanguage( name ); + // append module to the menu / toolbar + LightApp_ModuleAction* moduleAction = qobject_cast( action( ModulesListId ) ); + if ( moduleAction ) + moduleAction->insertModule( moduleTitle( name ), moduleIcon( moduleTitle( name ), 20 ), true ); // scale icon to 20x20 pix + // add empty page to Preferences dialog + LightApp_Preferences* prefs = preferences(); + if ( prefs && !prefs->hasModule( moduleTitle( name ) ) ) + { + int prefId = prefs->addPreference( moduleTitle( name ) ); + prefs->setItemIcon( prefId, moduleIcon( moduleTitle( name ), 20 ) ); // scale icon to 20x20 pix + LightApp_Module* m = qobject_cast( module( moduleTitle( name ) ) ); + if ( m ) + { + m->createPreferences(); + emptyPreferences( moduleTitle( name ) ); + } + } + // add Help items + createHelpItems( moduleTitle( name ) ); + // extend module catalog + QString catalogue = QDir( resDir ).filePath( QString( "%1Catalog.xml" ).arg( name ) ); + addCatalogue( name, catalogue ); + // update windows (in particular, Info panel) + updateWindows(); + // save module in the resource manager + if ( interactive ) + { + QStringList customModules = resourceMgr()->stringValue( "launch", "user_modules" ).split( ";", QString::SkipEmptyParts ); + customModules << name; + customModules.removeDuplicates(); + resourceMgr()->setValue( "launch", "user_modules", customModules.join( ";" ) ); + resourceMgr()->setValue( "user_modules", name, root ); + } + return true; +} + +/*!On module removing action.*/ +void LightApp_Application::onModuleRemoving( const QString& title ) +{ + QString root = resourceMgr()->stringValue( "user_modules", moduleName( title ) ); + QDir rootDirectory = QDir( root ); + + if ( rootDirectory.exists() ) + { + int answer = SUIT_MessageBox::question( desktop(), + tr( "TLT_REMOVE_MODULE" ), + tr( "QUE_REMOVE_MODULE_DIR" ).arg( root ), + SUIT_MessageBox::Yes | SUIT_MessageBox::No | SUIT_MessageBox::Cancel, + SUIT_MessageBox::No ); + if ( answer == SUIT_MessageBox::Cancel ) + return; // cancelled + if ( answer == SUIT_MessageBox::Yes ) + { + if ( activeStudy() && activeStudy()->isModified() && !onSaveDoc() ) + // doc is not saved, or saving cancelled + return; + if ( !rootDirectory.removeRecursively() ) + { + // canont remove directory + if ( SUIT_MessageBox::question( desktop(), + tr( "WRN_WARNING" ), + tr( "WRN_CANNOT_REMOVE_DIR" ).arg( root ), + SUIT_MessageBox::Yes | SUIT_MessageBox::No, + SUIT_MessageBox::No ) == SUIT_MessageBox::No ) + return; // removal is cancelled + } + } + } + + if ( activeModule() && activeModule()->moduleName() == title ) + activateModule( "" ); + + // remove from "Modules" menu and toolbar + LightApp_ModuleAction* moduleAction = qobject_cast( action( ModulesListId ) ); + if ( moduleAction ) + { + moduleAction->removeModule( title ); + } + // remove Help menu items + removeHelpItems( title ); + // remove Preferences + LightApp_Preferences* prefs = preferences(); + if ( prefs ) + prefs->removeModule( title ); + // remove settings + QStringList customModules = resourceMgr()->stringValue( "launch", "user_modules" ).split( ";", QString::SkipEmptyParts ); + customModules.removeAll( moduleName( title ) ); + resourceMgr()->setValue( "launch", "user_modules", customModules.join( ";" ) ); + removeModuleInfo( moduleName( title ) ); + // update windows (in particular, Info panel) + updateWindows(); } /*!Default module activation.*/ @@ -1220,7 +1309,7 @@ protected: { // normalize path if ( myUrl.startsWith( "file://", Qt::CaseInsensitive ) ) - myUrl = myUrl.remove( 0, QString( "file://" ).count() ); + myUrl = myUrl.remove( 0, QString( "file://" ).count() ); // For the external browser we always specify 'file://' protocol, // because some web browsers (e.g. Mozilla Firefox) can't open local file without protocol. myUrl = myUrl.prepend( "file://" ); @@ -1335,7 +1424,7 @@ void LightApp_Application::onHelpContextModule( const QString& component, QString rootDir = Qtx::getenv( (component + "_ROOT_DIR").toLatin1().constData() ); if ( !rootDir.isEmpty() ) { - path = (QStringList() << rootDir << "share" << "doc" << "salome" << "gui" << component << url).join( QDir::separator() ); + path = (QStringList() << rootDir << "share" << "doc" << "salome" << "gui" << component << url).join( QDir::separator() ); } } } @@ -2260,8 +2349,6 @@ LightApp_Preferences* LightApp_Application::preferences( const bool crt ) const if ( !crt ) return myPrefs; - SUIT_ResourceMgr* resMgr = resourceMgr(); - QList appList = SUIT_Session::session()->applications(); for ( QList::iterator appIt = appList.begin(); appIt != appList.end(); ++appIt ) { @@ -2273,10 +2360,6 @@ LightApp_Preferences* LightApp_Application::preferences( const bool crt ) const QStringList names; app->modules( names, false ); - // icons of modules - QMap icons; - app->moduleIconNames( icons ); - // step 1: iterate through list of all available modules // and add empty preferences page for ( QStringList::const_iterator it = names.begin(); it != names.end(); ++it ) @@ -2284,9 +2367,7 @@ LightApp_Preferences* LightApp_Application::preferences( const bool crt ) const if ( !_prefs_->hasModule( *it ) ) // prevent possible duplications { int modId = _prefs_->addPreference( *it ); // add empty page - if ( icons.contains( *it ) ) // set icon - _prefs_->setItemIcon( modId, Qtx::scaleIcon( resMgr->loadPixmap( moduleName( *it ), - icons[*it], false ), 20 ) ); + _prefs_->setItemIcon( modId, moduleIcon( *it, 20 ) ); // scale icon to 20x20 pix } } @@ -4045,22 +4126,7 @@ void LightApp_Application::afterCloseDoc() */ void LightApp_Application::updateModuleActions() { - QString modName; - if ( activeModule() ) - modName = activeModule()->moduleName(); - - LightApp_ModuleAction* moduleAction = - qobject_cast( action( ModulesListId ) ); - if ( moduleAction ) - moduleAction->setActiveModule( modName ); -} - -void LightApp_Application::removeModuleAction( const QString& modName ) -{ - LightApp_ModuleAction* moduleAction = - qobject_cast( action( ModulesListId ) ); - if ( moduleAction ) - moduleAction->removeModule( modName ); + emit moduleActivated( activeModule() ? activeModule()->moduleName() : QString() ); } bool LightApp_Application::checkModule( const QString& title ) @@ -4452,34 +4518,18 @@ void LightApp_Application::dockWindowsState( const QByteArray& arr, QMap& iconMap ) const +QPixmap LightApp_Application::moduleIcon( const QString& moduleTitle, const int size ) const { - iconMap.clear(); - - SUIT_ResourceMgr* resMgr = resourceMgr(); - if ( !resMgr ) - return; - - QStringList modList; - modules( modList, false ); - - for ( QStringList::const_iterator it = modList.begin(); it != modList.end(); ++it ) + QPixmap icon; + if ( resourceMgr() ) { - QString modName = *it; - QString modIntr = moduleName( modName ); - QString modIcon = resMgr->stringValue( modIntr, "icon", QString() ); - - if ( modIcon.isEmpty() ) - continue; - - if ( SUIT_Tools::extension( modIcon ).isEmpty() ) - modIcon += QString( ".png" ); - - iconMap.insert( modName, modIcon ); + QPixmap defaultIcon = resourceMgr()->loadPixmap( "LightApp", tr( "APP_MODULE_ICO" ), QPixmap( imageEmptyIcon ) ); + QString iconName = resourceMgr()->stringValue( moduleName( moduleTitle ), "icon", QString() ); + icon = resourceMgr()->loadPixmap( moduleName( moduleTitle ), iconName, defaultIcon ); + if ( size > 0 ) + icon = Qtx::scaleIcon( icon, size ); } + return icon; } /*! @@ -5401,17 +5451,17 @@ bool LightApp_Application::checkExistingDoc( bool closeExistingDoc ) if( activeStudy() ) { int answer = !activeStudy()->isModified() ? 1 : SUIT_MessageBox::question( desktop(), - tr( "APPCLOSE_CAPTION" ), - tr( "STUDYCLOSE_DESCRIPTION" ), - tr( "APPCLOSE_SAVE" ), - tr( "APPCLOSE_CLOSE" ), - tr( "APPCLOSE_CANCEL" ), 0 ); + tr( "APPCLOSE_CAPTION" ), + tr( "STUDYCLOSE_DESCRIPTION" ), + tr( "APPCLOSE_SAVE" ), + tr( "APPCLOSE_CLOSE" ), + tr( "APPCLOSE_CANCEL" ), 0 ); if(answer == 0) { if ( activeStudy()->isSaved() ) { onSaveDoc(); - if (closeExistingDoc) { - closeDoc(false); - } + if (closeExistingDoc) { + closeDoc(false); + } } else if ( onSaveAsDoc() ) { if (closeExistingDoc) { if( !closeDoc( false ) ) { @@ -5424,7 +5474,7 @@ bool LightApp_Application::checkExistingDoc( bool closeExistingDoc ) } else if( answer == 1 ) { if (closeExistingDoc) { - closeDoc( false ); + closeDoc( false ); } } else if( answer == 2 ) { result = false; @@ -5451,3 +5501,98 @@ PyConsole_Interp* LightApp_Application::createPyInterp() } #endif // DISABLE_PYCONSOLE + +void LightApp_Application::createHelpItems( const QString& modTitle ) +{ + if ( modTitle.isEmpty() ) + return; + + QString userGuide = "User's Guide"; + QString devGuide = "Developer's Guide"; + + int helpMenu = createMenu( tr( "MEN_DESK_HELP" ), -1, -1, 1000 ); + + createMenu( userGuide, helpMenu, -1, 5 ); + createMenu( devGuide, helpMenu, -1, 5 ); + + IMap helpData; // list of help files for the module + QString helpSubMenu; // help submenu name (empty if not needed) + QString modName = moduleName( modTitle ); // module name + if ( modName.isEmpty() ) modName = modTitle; // for KERNEL and GUI + QString rootDir = QString( "%1_ROOT_DIR" ).arg( modName ); // module root dir env variable + QString modDir = Qtx::getenv( rootDir.toUtf8().constData() ); // module root dir path + QString docSection; + if ( resourceMgr()->hasValue( modName, "documentation" ) ) + docSection = resourceMgr()->stringValue( modName, "documentation" ); + else if ( resourceMgr()->hasSection( modName + "_documentation" ) ) + docSection = modName + "_documentation"; + if ( !docSection.isEmpty() ) + { + helpSubMenu = resourceMgr()->stringValue( docSection, "sub_menu", "" ); + if ( helpSubMenu.contains( "%1" ) ) + helpSubMenu = helpSubMenu.arg( modTitle ); + foreach( QString paramName, resourceMgr()->parameters( docSection ) ) + { + QString key = paramName.contains( "%1" ) ? paramName.arg( modTitle ) : paramName; + QString helpItem = getHelpItem( docSection, paramName ); + if ( !helpItem.isEmpty() ) + helpData.insert( key, helpItem ); + } + } + + if ( helpData.isEmpty() && !modDir.isEmpty() ) + { + QStringList idxLst = QStringList() << modDir << "share" << "doc" << "salome" << "gui" << modName << "index.html"; + QString indexFile = idxLst.join( QDir::separator() ); // index file + if ( QFile::exists( indexFile ) ) + helpData.insert( tr( "%1 module Users's Guide" ).arg( modTitle ), indexFile ); + } + + IMapConstIterator fileIt; + for ( fileIt = helpData.begin(); fileIt != helpData.end(); fileIt++ ) + { + QString helpItemPath = fileIt.key(); + // remove all '//' occurances + while ( helpItemPath.contains( "//" ) ) + helpItemPath.replace( "//", "" ); + // obtain submenus hierarchy if given + QStringList smenus = helpItemPath.split( "/" ); + helpItemPath = smenus.takeLast(); + // workaround for User's Guide and Developer's Guide to avoid having single item in module's submenu. + if ( helpItemPath == userGuide || helpItemPath == devGuide ) + { + QString menuPath = smenus.join( "/" ); + QStringList allKeys = helpData.keys(); + QStringList total = allKeys.filter( QRegExp( QString( "^%1" ).arg( menuPath ) ) ); + if ( total.count() == 1 && smenus.count() > 0 ) + helpItemPath = smenus.takeLast(); + } + QPixmap helpIcon = fileIt.value().startsWith( "http", Qt::CaseInsensitive ) ? + resourceMgr()->loadPixmap( "STD", tr( "ICON_WWW" ), false ) : + resourceMgr()->loadPixmap( "STD", tr( "ICON_HELP" ), false ); + QAction* a = createAction( -1, helpItemPath, helpIcon, helpItemPath, helpItemPath, + 0, desktop(), false, this, SLOT( onHelpContentsModule() ) ); + a->setData( fileIt.value() ); + if ( !helpSubMenu.isEmpty() ) + smenus.prepend( helpSubMenu ); + // create sub-menus hierarchy + int menuId = helpMenu; + foreach ( QString subMenu, smenus ) + menuId = createMenu( subMenu, menuId, -1, 5 ); + createMenu( a, menuId, -1, ( menuId != helpMenu && ( helpItemPath == userGuide || helpItemPath == devGuide ) ) ? 0 : 5 ); + if ( !myHelpItems.contains( modName ) ) + myHelpItems[modName] = IdList(); + myHelpItems[modName].append( actionId( a ) ); + } +} + +void LightApp_Application::removeHelpItems( const QString& modTitle ) +{ + QString modName = moduleName( modTitle ); + if ( myHelpItems.contains( modName ) ) + { + foreach( int id, myHelpItems[modName] ) + setMenuShown( id, false ); + myHelpItems.remove( modName ); + } +} diff --git a/src/LightApp/LightApp_Application.h b/src/LightApp/LightApp_Application.h index c6e43cf47..aae6c005e 100644 --- a/src/LightApp/LightApp_Application.h +++ b/src/LightApp/LightApp_Application.h @@ -36,6 +36,7 @@ #include #include +#include #include #include @@ -194,6 +195,7 @@ public: #endif signals: + void moduleActivated( const QString& ); void studyOpened(); void studySaved(); void studyClosed(); @@ -217,6 +219,7 @@ public slots: protected: void showHelp( const QString& ); virtual void createActions(); + virtual void customize(); virtual void createActionForViewer( const int id, const int parentId, const QString& suffix, @@ -251,6 +254,8 @@ protected: virtual PyConsole_Interp* createPyInterp(); #endif + virtual void addCatalogue( const QString&, const QString& ) {} + protected slots: virtual void onDesktopActivated(); virtual void onViewManagerRemoved( SUIT_ViewManager* ); @@ -258,6 +263,8 @@ protected slots: void onNewWindow(); virtual void onModuleActivation( const QString& ); + void onModuleAdding(); + void onModuleRemoving( const QString& ); void onCloseView( SUIT_ViewManager* ); virtual void onStudyCreated( SUIT_Study* ); @@ -293,7 +300,6 @@ protected: void updateWindows(); void updateViewManagers(); void updateModuleActions(); - void removeModuleAction( const QString& ); bool checkModule( const QString& ); @@ -309,7 +315,7 @@ protected: QString defaultModule() const; virtual void currentWindows( QMap& ) const; void currentViewManagers( QStringList& ) const; - void moduleIconNames( QMap& ) const; + QPixmap moduleIcon( const QString&, const int = -1 ) const; QDockWidget* windowDock( QWidget* ) const; QByteArray dockWindowsState( const QMap&, const QMap& ) const; @@ -322,20 +328,25 @@ protected: void showPreferences( const QStringList& ); private: + bool addUserModule( const QString&, const QString&, bool = false ); void emptyPreferences( const QString& ); QList findToolBars( const QStringList& names = QStringList() ); + void createHelpItems( const QString& ); + void removeHelpItems( const QString& ); QByteArray processState(QByteArray& input, - const bool processWin, - const bool processTb, - const bool isRestoring, - QByteArray defaultState = QByteArray()); + const bool processWin, + const bool processTb, + const bool isRestoring, + QByteArray defaultState = QByteArray()); protected: - typedef QPointer WinPtr; - typedef QMap WinMap; - typedef QMap WinVis; - typedef QMap WinGeom; + typedef QPointer WinPtr; + typedef QMap WinMap; + typedef QMap WinVis; + typedef QMap WinGeom; + typedef QList IdList; + typedef QMap IdMap; enum { OpenReload = CAM_Application::OpenExist + 1 }; @@ -347,6 +358,7 @@ protected: WinMap myWin; WinVis myWinVis; + IdMap myHelpItems; SUIT_Accel* myAccel; QTimer* myAutoSaveTimer; diff --git a/src/LightApp/LightApp_ModuleAction.cxx b/src/LightApp/LightApp_ModuleAction.cxx index d6a8ae939..f7a8f27af 100644 --- a/src/LightApp/LightApp_ModuleAction.cxx +++ b/src/LightApp/LightApp_ModuleAction.cxx @@ -22,11 +22,14 @@ // #include "LightApp_ModuleAction.h" -#include #include +#include +#include #include #include #include +#include +#include /*! \class LightApp_ModuleAction::ActionSet @@ -146,7 +149,7 @@ QList LightApp_ModuleAction::ComboAction::widgets() const QList wlist = createdWidgets(); for ( QList::const_iterator wit = wlist.begin(); wit != wlist.end(); ++wit ) - lst += (*wit)->findChildren(); + lst += qobject_cast(*wit); return lst; } @@ -162,18 +165,11 @@ QWidget* LightApp_ModuleAction::ComboAction::createWidget( QWidget* parent ) if ( !parent->inherits( "QToolBar" ) ) return 0; - QWidget* dumb = new QWidget( parent ); - QVBoxLayout* l = new QVBoxLayout( dumb ); - l->setSpacing( 0 ); l->setMargin( 0 ); - QtxComboBox* cb = new QtxComboBox( dumb ); + QtxComboBox* cb = new QtxComboBox( parent ); cb->setSizeAdjustPolicy( QComboBox::AdjustToContents ); cb->setFocusPolicy( Qt::NoFocus ); - l->addWidget( cb ); - l->addSpacing( 3 ); - connect( cb, SIGNAL( activatedId( int ) ), this, SIGNAL( activatedId( int ) ) ); - - return dumb; + return cb; } /*! @@ -205,63 +201,71 @@ private: \brief An action, representing the list of modules to be inserted to the toolbar. - This action is represented in the toolbar as combo box and a set of buttons - for each module. In addition to the modules items, the combo box contains - an item corresponding to the "neutral point" of the application - (when there is no active module). - - The action can be constructed with up to two parameters, defining the text - and icon to be displayed for the "neutral point". + In the toolbar this action is represented as the combo box with the list of + available modules, and a set of buttons for each module. Additionally, combo box + contains an item representing "neutral point" of the application (i.e. no active module). + + In menu, the action is represented as a plain list of items, one per module. Only one module can be active at the moment. It can be set programmatically with setActiveModule() function. Use this method with empty string to turn to the "neutral point". To get active module, use activeModule() function. - When user activates/deactivates any module, the signal moduleActivated() + When user activates/deactivates a module, the moduleActivated() signal is emitted. - The action can be represented in the toolbar in different modes: - * as combo box only (Qtx::ComboItem) - * as set of modules buttons only (Qtx::Buttons) - * as combo box followed by the set of modules buttons (Qtx::All) - * as none (Qtx::None) - By default, both combo box and buttons set are shown. Use method - setMode() to change this behavior. + The action also provides an additional separate item "Add modules"; when + this button is pressed, a adding() signal is emitted. This signal + can be connected to a dedicated slot aimed to dynamically add a new module + into the application. In addition, a button "Remove module" shows a dropdown menu + with the list of user modules; when any item is selected, the removing() signal + is emitted. This signal may be connected to a slot aimed to dynamically remove + selected user module from application. - An action can be also added to the popup menu, but combo box is never shown - in this case, only modules buttons. + It is possible to customize which elements to show via the setMode() of setModeEnabled() + functions. By default, all elements are shown. The following choices are possible: + + - LightApp_ModuleAction::Buttons: show separate items for all modules + - LightApp_ModuleAction::List: show combo box with list of modules (in toolbar only) + - LightApp_ModuleAction::AddRemove: show "Add modules" and "Remove modules" items + - LightApp_ModuleAction::All: show all items */ /*! \brief Constructor - - Creates an module action with "neutral point" item described by \a text. - - \param text "neutral point" item's text + \param resMgr resource manager \param parent parent object */ -LightApp_ModuleAction::LightApp_ModuleAction( const QString& text, QObject* parent ) +LightApp_ModuleAction::LightApp_ModuleAction( QtxResourceMgr* resMgr, QObject* parent ) : QtxAction( parent ) { - setText( text ); - init(); -} + setText( tr( "APP_NAME" ) ); + setIcon( resMgr->loadPixmap( "LightApp", tr( "APP_DEFAULT_ICO" ), false ) ); + setVisible( false ); -/*! - \brief Constructor + myMode = All; + myCombo = new ComboAction( this ); + myAdd = new QtxAction( tr( "ADD_MODULE"), + resMgr->loadPixmap( "LightApp", tr( "ICON_ADD_MODULE" ), false ), + tr( "ADD_MODULE"), + 0, this ); + myRemove = new QtxAction( tr( "REMOVE_MODULE"), + resMgr->loadPixmap( "LightApp", tr( "ICON_REMOVE_MODULE" ), false ), + tr( "REMOVE_MODULE"), + 0, this ); + myRemove->setEnabled( false ); + myRemove->setMenu( new QMenu() ); + mySeparator = new QAction( this ); + mySeparator->setSeparator( true ); + mySet = new ActionSet( this ); - Creates an module action with "neutral point" item described by \a text and \a ico. + myMapper = new QSignalMapper( this ); - \param text "neutral point" item's text - \param ico "neutral point" item's icon - \param parent parent object -*/ -LightApp_ModuleAction::LightApp_ModuleAction( const QString& text, const QIcon& ico, QObject* parent ) -: QtxAction( parent ) -{ - setText( text ); - setIcon( ico ); - init(); + connect( this, SIGNAL( changed() ), this, SLOT( onChanged() ) ); + connect( myAdd, SIGNAL( triggered( bool ) ), this, SIGNAL( adding() ) ); + connect( mySet, SIGNAL( triggered( int ) ), this, SLOT( onTriggered( int ) ) ); + connect( myCombo, SIGNAL( activatedId( int ) ), this, SLOT( onComboActivated( int ) ) ); + connect( myMapper, SIGNAL( mapped( QString ) ), this, SIGNAL( removing( QString ) ) ); } /*! @@ -341,9 +345,32 @@ QAction* LightApp_ModuleAction::moduleAction( const QString& name ) const */ void LightApp_ModuleAction::insertModule( const QString& name, const QIcon& ico, const int idx ) +{ + insertModule( name, ico, false, idx ); +} + +/*! + \brief Add module into the list. + \param name module name + \param ico module icon + \param isCustom \c false to insert regular module, \c true to insert user module + \param idx position in the module list (if -1, the module is added to the end of list) + \sa removeModule() +*/ +void LightApp_ModuleAction::insertModule( const QString& name, const QIcon& ico, + bool isCustom, const int idx) + { QtxAction* a = new QtxAction( name, ico, name, 0, this, true ); a->setStatusTip( tr( "ACTIVATE_MODULE_TOP" ).arg( name ) ); + a->setData( isCustom ); + if ( isCustom ) + { + myRemove->setEnabled( true ); + QAction* inserted = myRemove->menu()->addAction( name ); + connect( inserted, SIGNAL( triggered() ), myMapper, SLOT( map() ) ); + myMapper->setMapping( inserted, name ); + } mySet->insertAction( a, -1, idx ); update(); @@ -360,7 +387,23 @@ void LightApp_ModuleAction::removeModule( const QString& name ) if ( id == -1 ) return; + QAction* a = moduleAction( name ); + bool isCustom = a->data().toBool(); + mySet->removeAction( id ); + if ( isCustom ) + { + foreach ( QAction* ma, myRemove->menu()->actions() ) + { + if ( ma->text() == name ) + { + myRemove->menu()->removeAction( ma ); + break; + } + } + myRemove->setEnabled( !myRemove->menu()->actions().isEmpty() ); + } + update(); } @@ -399,30 +442,38 @@ void LightApp_ModuleAction::setActiveModule( const QString& name ) /*! \brief Set action display mode. - - Action can be represented in the toolbar as - * combo box only (Qtx::ComboItem) - * set of modules buttons only (Qtx::Buttons) - * combo box followed by the set of modules buttons (Qtx::All) - * none (Qtx::None) - - \param mode action display mode + \param mode action display options (combination of flags) \sa mode() */ -void LightApp_ModuleAction::setMode( const int mode ) +void LightApp_ModuleAction::setMode( const LightApp_ModuleAction::Mode& mode ) { myMode = mode; update(); } +/*! + \brief Enable / disable action display mode. + \param mode action display options (combination of flags) + \param enabled \c true to enable mode, \c false to disable mode + \sa mode() +*/ +void LightApp_ModuleAction::setModeEnabled( const LightApp_ModuleAction::Mode& mode, bool enabled ) +{ + if ( enabled ) + myMode |= mode; + else + myMode &= ~mode; + update(); +} + /*! \brief Get action display mode. \param mode action display mode \sa setMode() */ -int LightApp_ModuleAction::mode() const +bool LightApp_ModuleAction::isModeEnabled( const LightApp_ModuleAction::Mode& mode ) const { - return myMode; + return (bool)( myMode & mode ); } /*! @@ -433,6 +484,9 @@ void LightApp_ModuleAction::addedTo( QWidget* w ) { if ( w->inherits( "QToolBar" ) ) w->insertAction( this, myCombo ); + w->insertAction( this, myAdd ); + w->insertAction( this, myRemove ); + w->insertAction( this, mySeparator ); w->insertAction( this, mySet ); update(); } @@ -447,6 +501,9 @@ void LightApp_ModuleAction::removedFrom( QWidget* w ) { if ( w->inherits( "QToolBar" ) ) w->removeAction( myCombo ); + w->removeAction( myAdd ); + w->removeAction( myRemove ); + w->removeAction( mySeparator ); w->removeAction( mySet ); } @@ -470,23 +527,6 @@ bool LightApp_ModuleAction::event( QEvent* e ) \param name module name (empty string for neutral point) */ -/*! - \brief Initialize an action, - \internal -*/ -void LightApp_ModuleAction::init() -{ - setVisible( false ); - - myMode = All; - myCombo = new ComboAction( this ); - mySet = new ActionSet( this ); - - connect( this, SIGNAL( changed() ), this, SLOT( onChanged() ) ); - connect( mySet, SIGNAL( triggered( int ) ), this, SLOT( onTriggered( int ) ) ); - connect( myCombo, SIGNAL( activatedId( int ) ), this, SLOT( onComboActivated( int ) ) ); -} - /*! \brief Update an action. \internal @@ -497,7 +537,9 @@ void LightApp_ModuleAction::update() for ( QList::const_iterator it = lst.begin(); it != lst.end(); ++it ) update( *it ); - myCombo->setVisible( myMode & ComboItem ); + myCombo->setVisible( myMode & List ); + myAdd->setVisible( myMode & AddRemove ); + myRemove->setVisible( myMode & AddRemove ); mySet->setVisible( myMode & Buttons ); } diff --git a/src/LightApp/LightApp_ModuleAction.h b/src/LightApp/LightApp_ModuleAction.h index f669d681e..acc92746e 100644 --- a/src/LightApp/LightApp_ModuleAction.h +++ b/src/LightApp/LightApp_ModuleAction.h @@ -32,6 +32,8 @@ #endif class QtxComboBox; +class QtxResourceMgr; +class QSignalMapper; class LIGHTAPP_EXPORT LightApp_ModuleAction : public QtxAction { @@ -43,11 +45,14 @@ private: class ActivateEvent; public: - enum { None = 0x00, Buttons = 0x01, ComboItem = 0x02, All = Buttons | ComboItem }; + typedef enum { Buttons = 0x01, + List = 0x02, + AddRemove = 0x04, + All = Buttons | List | AddRemove + } Mode; public: - LightApp_ModuleAction( const QString&, QObject* = 0 ); - LightApp_ModuleAction( const QString&, const QIcon&, QObject* = 0 ); + LightApp_ModuleAction( QtxResourceMgr*, QObject* = 0 ); virtual ~LightApp_ModuleAction(); int count() const; @@ -59,13 +64,18 @@ public: QAction* moduleAction( const QString& ) const; void insertModule( const QString&, const QIcon&, const int = -1 ); + void insertModule( const QString&, const QIcon&, bool, const int = -1 ); void removeModule( const QString& ); QString activeModule() const; + + void setMode( const Mode& ); + void setModeEnabled( const Mode&, bool ); + bool isModeEnabled( const Mode& ) const; + +public slots: void setActiveModule( const QString& ); - void setMode( const int ); - int mode() const; protected: virtual void addedTo( QWidget* ); @@ -75,10 +85,10 @@ protected: signals: void moduleActivated( const QString& ); + void adding(); + void removing( const QString& ); private: - void init(); - void update(); void update( QtxComboBox* ); @@ -92,8 +102,12 @@ private slots: private: ComboAction* myCombo; + QtxAction* myAdd; + QtxAction* myRemove; ActionSet* mySet; + QAction* mySeparator; int myMode; + QSignalMapper* myMapper; }; class LightApp_ModuleAction::ComboAction : public QtxAction diff --git a/src/LightApp/LightApp_Preferences.cxx b/src/LightApp/LightApp_Preferences.cxx index ea93eeb8d..2e1d35246 100644 --- a/src/LightApp/LightApp_Preferences.cxx +++ b/src/LightApp/LightApp_Preferences.cxx @@ -61,6 +61,21 @@ int LightApp_Preferences::addPreference( const QString& mod, const QString& labe return id; } +/*! + Remove module preferences. +*/ +void LightApp_Preferences::removeModule( const QString& mod ) +{ + QMutableMapIterator it( myPrefMod ); + while ( it.hasNext() ) + { + it.next(); + if ( it.value() == mod ) + it.remove(); + } + removeItem( mod ); +} + /*! Checks: is preferences has module with name \a mod. */ diff --git a/src/LightApp/LightApp_Preferences.h b/src/LightApp/LightApp_Preferences.h index f589541c2..91fcad6dc 100644 --- a/src/LightApp/LightApp_Preferences.h +++ b/src/LightApp/LightApp_Preferences.h @@ -53,6 +53,7 @@ public: const QString& section = QString(), const QString& param = QString() ); bool hasModule( const QString& ) const; + void removeModule( const QString& ); void activateItem( const QString& ); void activateItem( const QStringList& ); diff --git a/src/LightApp/resources/LightApp_images.ts b/src/LightApp/resources/LightApp_images.ts index d8a16f562..29f083f39 100644 --- a/src/LightApp/resources/LightApp_images.ts +++ b/src/LightApp/resources/LightApp_images.ts @@ -15,10 +15,6 @@ ABOUT_SPLASH icon_about.png - - APP_MODULE_BIG_ICO - icon_module_big.png - APP_BASE_LOGO icon_applogo.png @@ -39,5 +35,13 @@ ICON_LIFE_RIGN icon_life_ring.png + + ICON_ADD_MODULE + icon_add_module.png + + + ICON_REMOVE_MODULE + icon_remove_module.png + diff --git a/src/LightApp/resources/LightApp_msg_en.ts b/src/LightApp/resources/LightApp_msg_en.ts index 1f810e27b..1a159cacf 100644 --- a/src/LightApp/resources/LightApp_msg_en.ts +++ b/src/LightApp/resources/LightApp_msg_en.ts @@ -1149,6 +1149,62 @@ File does not exist INFO_AVAILABLE_MODULES Available modules + + WRN_MODULE_BAD_SALOMEX_FILE + Cannot read module description file: +%1. + + + WRN_MODULE_EMPTY_NAME + Empty or invalid module name in: +%1. + + + WRN_MODULE_EMPTY_ROOT + Module root directory is not set or incorrectly specified in: +%1. + + + WRN_MODULE_ROOT_DOES_NOT_EXIST + Module root directory does not exist: +%1. + + + WRN_MODULE_DUPLICATED + Module "%1" is already present in this session + + + WRN_MODULE_BAD_RESDIR + Bad or non-existing resources directory: +%1. + + + WRN_MODULE_CANNOT_READ_CFG + Cannot read XML configuration file for the module from: +%1. + + + WRN_MODULE_BAD_CFG_FILE + XML configuration file for the module "%1" is bad or incomplete + + + TLT_REMOVE_MODULE + Remove module + + + QUE_REMOVE_MODULE_DIR + Do you also want to remove module directory: +%1? + +If you answer "Yes", you may need to save your study before removal. + + + WRN_CANNOT_REMOVE_DIR + Cannot remove directory: +%1. + +Continue? + LightApp_Module @@ -1269,6 +1325,14 @@ File does not exist ACTIVATE_MODULE_TOP Activate/deactivate %1 module + + ADD_MODULE + Add modules + + + REMOVE_MODULE + Remove modules + LightApp_PyEditor diff --git a/src/LightApp/resources/LightApp_msg_fr.ts b/src/LightApp/resources/LightApp_msg_fr.ts index 76415b674..29e2505d8 100644 --- a/src/LightApp/resources/LightApp_msg_fr.ts +++ b/src/LightApp/resources/LightApp_msg_fr.ts @@ -1149,6 +1149,62 @@ Le fichier n'existe pas INFO_AVAILABLE_MODULES Modules disponibles + + WRN_MODULE_BAD_SALOMEX_FILE + Cannot read module description file: +%1. + + + WRN_MODULE_EMPTY_NAME + Empty or invalid module name in: +%1. + + + WRN_MODULE_EMPTY_ROOT + Module root directory is not set or incorrectly specified in: +%1. + + + WRN_MODULE_ROOT_DOES_NOT_EXIST + Module root directory does not exist: +%1. + + + WRN_MODULE_DUPLICATED + Module "%1" is already present in this session + + + WRN_MODULE_BAD_RESDIR + Bad or non-existing resources directory: +%1. + + + WRN_MODULE_CANNOT_READ_CFG + Cannot read XML configuration file for the module from: +%1. + + + WRN_MODULE_BAD_CFG_FILE + XML configuration file for the module "%1" is bad or incomplete + + + TLT_REMOVE_MODULE + Remove module + + + QUE_REMOVE_MODULE_DIR + Do you also want to remove module directory: +%1? + +If you answer "Yes", you may need to save your study before removal. + + + WRN_CANNOT_REMOVE_DIR + Cannot remove directory: +%1. + +Continue? + LightApp_Module @@ -1269,6 +1325,14 @@ Le fichier n'existe pas ACTIVATE_MODULE_TOP Activer/désactiver le module %1 + + ADD_MODULE + Ajouter les modules + + + REMOVE_MODULE + Supprimer les modules + LightApp_PyEditor diff --git a/src/LightApp/resources/LightApp_msg_ja.ts b/src/LightApp/resources/LightApp_msg_ja.ts index e23d9b4b5..50e43b8f3 100644 --- a/src/LightApp/resources/LightApp_msg_ja.ts +++ b/src/LightApp/resources/LightApp_msg_ja.ts @@ -1147,6 +1147,62 @@ Pythonファイルは、文字、数字、アンダースコアが含まれて INFO_AVAILABLE_MODULES Available modules + + WRN_MODULE_BAD_SALOMEX_FILE + Cannot read module description file: +%1. + + + WRN_MODULE_EMPTY_NAME + Empty or invalid module name in: +%1. + + + WRN_MODULE_EMPTY_ROOT + Module root directory is not set or incorrectly specified in: +%1. + + + WRN_MODULE_ROOT_DOES_NOT_EXIST + Module root directory does not exist: +%1. + + + WRN_MODULE_DUPLICATED + Module "%1" is already present in this session + + + WRN_MODULE_BAD_RESDIR + Bad or non-existing resources directory: +%1. + + + WRN_MODULE_CANNOT_READ_CFG + Cannot read XML configuration file for the module from: +%1. + + + WRN_MODULE_BAD_CFG_FILE + XML configuration file for the module "%1" is bad or incomplete + + + TLT_REMOVE_MODULE + Remove module + + + QUE_REMOVE_MODULE_DIR + Do you also want to remove module directory: +%1? + +If you answer "Yes", you may need to save your study before removal. + + + WRN_CANNOT_REMOVE_DIR + Cannot remove directory: +%1. + +Continue? + LightApp_Module @@ -1266,6 +1322,14 @@ Pythonファイルは、文字、数字、アンダースコアが含まれて ACTIVATE_MODULE_TOP モジュール %1 の有効/無効にします。 + + ADD_MODULE + Add modules + + + REMOVE_MODULE + Remove modules + LightApp_PyEditor diff --git a/src/LightApp/resources/icon_add_module.png b/src/LightApp/resources/icon_add_module.png new file mode 100644 index 0000000000000000000000000000000000000000..ac8bb01efd2165fee4346a7818e01ef01b19877b GIT binary patch literal 807 zcmV+?1K9kDP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0=!8?K~!i%?U+qy z6G0S!=glVB{52{yh_?zV{?ZyT7riLzG3i~9;6aL7Z7GUJPu>bPk@g_8o;?Wh;JNrm zv5=BdYOQ+mAXe*7lGenS87G^kxOS6P^XNv|@3QZ`ox}T<9p>#a2Z!bWypnP;eQ-xn z<`P0ycBxcq;Ck+6fzg}YQ?F_(=pYw3m)?7-C<>#J2@jSGfbo(Jfzu=1vv;sW0;xc6 zICo?%i`let2SRnuy5fn^3xSe4NL+;ilZ2|Eb4p0)T2p2%GKw2yW!mYJz)|6uD!>w zWf4NW#n8YcpfT5d3|kfs+Bk*=CV?Oi-%vZ9{ND`~oM|6?eB&Ud1}1^Q*rU7`LdRU7 zKgLqapfUHXYX108Z1Odh3@lIL+5CvjA5noVkVOf7M7ec@L8CE1?|(<}5Z z;t1G{BVadw023J2MMPL^`2TwuX_pborfB%H|T4GLl=f{dRu8KR%z&f1l6$^Z2|z&-d$9kDoI(ITNtU z8?U)W6954Gb|23`)oT0iP{*p~z39cP^CmyT+LPNo89_;JLgh-OyD$I?pxWAO+fa1i z4xhO9>+;o$mmhu8QR3RI9fHfgihXwK02qRqwZ?8Cs6@QJ+c?RG+W*Ox&JVRhLyr}q z3v))SWLHDT`w1|t0TQny|OD8#1OQ8q3W7F0dsS6A?sQ{s1{O^n6MqS-rkNX zkH}1tycoCPGZ%$?&x|$--X5CP#eei=6?c(nwQ;!tBiR zys3$m|0s3tl2j=2hxXU_LWRTf^>g;%7j&l9RRB1dt3hN^Agk1j1?T5&YR*z))oYd; z10@bT$ZCD3&K%GwrErPT)wak-vm2Z2T299oa>}?DI3;^m4%-!wIfM?Z7pnOw>PJP{ zs+bOuV?HVGaZ5K$`K!8{6@4{Nox(E`i~H%|r^rxtXp(Z&6ugA2+>?mL5PEZ<;+x1d zyzpV-6y=VLJRDEJ55tP3BKG|43YKtRk~w11$9*@=S|iA8w=mOk?0@kD>LU+D3_PAP z7mE4{%puEr{7M!ZGvfU!A2G5q+0ah0G1EMu-{1nH(CU#8{W*zTaINgbuvibf&3T_s zHt?C#Dh*SjFX{2j7w7wq*cy~yWqslpD;3|( zgwl~4q?nC6Nef8>lI`Q4K32qwJ|D6S?P&v0=}pE+^6}6!4Rz_1!{Oc>~wZF0+KRAy!}LP{D+3qdi1$3qX`kL3pE@!= zj%mEApr*)YOQi3{L!t);aRhpjD{#mdImA2>U<5!tk^aWW4g;nJ%GW`jk_LI5w1iC2 zhMvv}n|=n}kt#d-yWPa0!&JHQo1Ro700D|#aB+S7wJU5KUABe?I|#3h943BsO%=&z zmWRqF;xOtIi_nX32XFt}d{ryd`hJM9A(kHf+sctRoF@wrs9WNWTy6wN`z1t;dzRwQOw8rweuO)2ud(KF1cb&+K%V$jg!`?K-=B zY-y=-Vz38i2IcfKXrx_Z_#eG?!HI3{`nJ{;L*4NTf)A|UAH-AJ@cI{tsU8Jk78upt zx~6HZpzH)4A}s37=vz$JW9dC$0A9;#oUv_f_1}WA1+7=cc7qR(W=i{-9HAI_0_5wv zAIh02T%VVf?BmJIm43e^lF$HcLl$>>IV+!+KnUb2m%1hTmEcvFDwj{|`}I{0iV22$ zFJfT=-LJE2?A4SzL5;s>BtJxnv=4>EZ-sO%c@{u*GH~lEeT-IJ-8QyxDbj&!)Ym4) zS|1&cKacDh(d#ZxHpa4CT3yWG4kB4N} z)#yOut&M7QVrFbAehS|D%xqxGPZ32h$9DGCa#Zw6U-Bwcla(~nnp-@vQ&VQ!y}!Nt zK=$v7y4Y%3*UFT<%Dgfw*kw*9Y32SM+aJ9?b_zvAwsx*@`=xFinEFuD-uHC{GMOMk z0T_0**A#pJ$MHO1mb%UYAH?fRc?7_I=#0*gqrGX~0b{^j;qlXbt*%o~tGZwj=5Ec{ zj>^bvsOMN1)>LcDgTydLCSkozb9qMf$Njdy0`}b$9E*TS8@$-TRVr^@X|%W8es5i9 zGt^*k!hkpW8t6P1qggm!xZ;dAhAGJP;h%I4yup4(Ygl}8_qF4F7QZ`!I0FOF;08fhH#NoI85*z>t#^vohCIZdqGInt|_yJNRcw#vzd4cpkC0XN0AGeTD&Ax8EF7ICXg%Z7k1%IsWxcK3`k&fjH z_eb|d{9ZE+e-D_lC72e>zfztdeXAr(T9QY^n%RF|w%c@Nw2d$77OD=i?)(Dd4&2=w z@C@_x9VLEBVT1)wgYiv)NR`fDQCil@eoDZtnO>p3q!Hifn?n@2C(U52dwT_E3ez!2 z-pF_)ZtiR=HQ4PzF~jG*_Mp8Q-WCBmr+EsM%Y*Om94i>VHYSo`q@BK%%=0pI!BM%n zx$448^Pixq_im^}FM5~yX!e1oI23*a-4yF{T>j9%iuiueUBEZHj7y~X>937CxiWv`%DEYg)dV8U-2zRNTx7s}68rU!^zdr)aBY*jX7WF(j`_R@< zC(z1lqgo=Zx)fUmYMxGYi_85Ao2#ohCg5z0x2}D|C+JFW`rvmq;~hQVg}(DA#$Ubx z7Pxz{|0ht*|51SXx8-X!6czcesr<^$!(BVV#F0lA+V5T>rRy0T&2;r9ZlXl!3F5Riplew%pbM2<$P?~pStP`$*%=Q@tcg@GTmEYfs0we z2Q>o_*93t2t-4@-y(U;bGp zK}yxntvvj3A98qW0cWBT_^uy5Hp;P^HVS@CamghZl#e3GxS$545T7$5IM?hh@hZRn zyQT2M+AmOIWs0xxA2U$pgpz^LB+@n`>f^L%&K&-LZK zP_kII)UzW#5Pff)&1H2SbV>LNwy`u7Q!FgoC~G@#asDP*H1RZV&v>7E!9U|;VOpf& zyZi0D)~OI#GxPfyjkk_kT?W$8V!!W_m=&nD< z#pWa@HLs!Io!PXxSP#rdl^8u~Y0+!tGoYHPC$LW<&?y4MZ@rOIk z#;+y}RTck*lT;4;UCs#ZB~C8j_UZ_-e$dS#D8L#!0Dl@0&)|E~{i5iuGTi9pkb>JA zqQXJ*#FNV4`u(aFde{`n@g)EUnX$K+R}6JjdB+p3H{~XWzFk_&+VM(K>Huyr>bJSdl>cpWJhxe>%Tu z=AxQ{7{E)OhXbMbW_r@vh%_2{%@S}=%I?A#YEk--h=|l>Msz9X&|dL&np;3L7k;tn zn2K?pJ7-Lqg4p+{4>%OJPBY#=I#HnpK{gZ@c^^5zE3+{+iL#R&vl9%R^iU;K&)LmZ zVh%J2*IV2^7SMYLkIlO)Unc?fVuO}R> z^bW{M2u?V9^4yODM@}->+n6&rSer2X`t=KJ20#EYF(85e|Nk>HGBWZrGckhsKY#uH zA;{0;sjV*0FDos{&&0^={_NGqfA;Uc{{3fWW@PyL=Pv_LB`*^rBisyt0AfNijDdrL zed?EQzaGAM|Fs>+7vbjStQQgF|Ifn0!XPZj%OEBy#PI6Xo1Z^_{j~Z0`wyR}n8emU z|Cw+7`u*F4nHgdfKmakO)ExvF#=*+QJV8)^KaGuzm64s5iGi1w^Cb%tf1~)j_a7O) ze0sz1?>_?rBMT1$FE=*>KOfugzkmLGWMttMdGP231JmD+@2o8hy7_oGy8r@+MO{Ub z0qA1(_aDEd{{iY^XJcXDeuU0RRXK5&#Mc3jqH9{{Z;-`T+L#`T)hr&H(!|F8~-A6#(`1@BkzwCi)~F z7mp1J1@r=lnMclz;q%uY|KET3a*~0GCH39g*E~0_o&hpB8PwEue;5q(v>3ECR2bxB zr5O15`58do`SRr(!>c#%82EU3fhpq`gPM}`4LM1{;5TpI{s#ykW=)GohI@}+@xOil zwOCq0#QWm8<6xJmscSRXS?B@7jDvxhi3Mm569YFF2ZOM%FvF*hpFoaac>nPW$S{US z4{nRRdhz7#)2ENFe*pv#3m+f%1{oz_AGmGXXvx24FG+8+QEgZiaJbPcb}sfA*5$_n&_ZJV1FC#@}G? zzkBzAfrD3!L0Q|D`NLNRIe-8h(!mOVFbo6Guy&w$^8bHO*k!F!b?w22{-=l35#mwaGgVdZ4tO9cf3=ASd0>B6m0EQVC*qb1ieg6D`;m1jXz`+0ik(ue=es)g5PqMPI+@`=pd+y>jhS%>uF}ww4 zJr*Wlf9(AK2lgh|Wn7#Lx|#~WB*n#W>&s6DZB6Mb0z9mHzcT$}00~je&)Yn}Lmu1DFE17z}mQ7=-xw7+$>j z$nfLWe}GKzcSMR>Eym<3DhJ%B<4j_P-K!u~D zwdrR!J0lMcmLDH4TsQ+v3jY`c1^KVsxqY4C`(9;gE@a{ ze`zS5eDm}g!;=Ts7~I|6A80B|d$O}Kd<6&~=IG=KU>N=dCIQB0qQZjzcm+kwO!br} z$Vv%Ev$L_jJbUhf2NSyxBPg3azITa1SzZ61wS~T?jJQC(w2Taoq?C-brix@BFpSTG z%0qwvV%&4)E4;J?<<~Di{_yi~FnwSXXIN9&7QM+03FQVNQW9yQih$CTG6YBvX@VfbNJM5x04WNHSO7(GLueubQbtfp zbSwyrAdY~Fk{BQ$p;+FCijq(S6{UzYL-OK#KhD|TIY0K<`#Wo|z1P`*J)T&_O^N^j zV7$s*WM^W8 zSD+sNoH7Ogm<0fX`)S&nW?QLr{Rdwdce9V!*=M&qw8&8>3?h>{ zJ2}vCBhAfPybf0>K7D^GUuv++uR#Dq%3o{hH*L6DxONE;!l3*=%j^T z`?H(F;lpu>%Ofn>?sq!A8wik^5?Mbe5b;pF2 z2aPs#eJ;Gg6eQlY=hjJQJ31gnx-?=1D-bal(t|75pdUZ@N3t77(``Z@q$!tvCm6V; zarB)}3@mKdj8@6^A5Djy=O#bfXXmu;u}xV+vA#U{pa26;J|l|89q&7GD^z9>v|pkQ z=0tXXZk^32VO_tq4K%l6H6va)nRXscMFzQ1il4bQa?VZpi?X0*>=hKfBZu(pbsGr8a(9Xth>sfRYh z!*S$I6C28N$kyOO=a0fs!dK^0-E(7;=Gl5RVOHYx`3oXx$0+1$5eEroGN8CnH;6Qn z2DPV+K))PH68b7HY{$DfOXa9D=@M~?sIT=1RMUZUo?kWRy_yXpxHxw_5vt=^qMun- zV8{fGI0r*RpPr1}%CrxebPw8E`qBc(o2<7dJSKH;IGsl^Ntm0#4<+ zO2hJ0Yb&krQTozl5s&W=h1B^##baXAB^&SyGF$1Itcvm#@KhpXo}RVh_`FljQKT#T z$x22bJD} zuVZ-+lV^!{=JduAao912WUYk0e7lOq+n-_t>kqQt&C@nUggGEM=**#7*jgA`kd{qeRSn6--B3c?Mp44o1LLlX%A@XyAUeWc6lVbvP^e)H! zUb(;*>B1z2_xjh<%=_Ok4i!XgMa8&nw8uj96rnsqioC4OHd(RB&vBueE%;R-}cVD zzMDq*_O=uh9)&`|A4AG1LFE^j=c=%kdl?dO|J;0^f3{|$ixug#Bt!K1;-G12ennQ5 zXt}Wl+C1cv@!Udf{ zLAQ#)e2fzi7@gSiGIA(DEdnQ;1e|n(j@y7QbaNYT8x7D7-yURNy_ym7C+o6KoQ%fO z!X1u%R5q$XL~sP#2%lJ$B&L^6J>-N|6E?KEwwzx zx^{~^v+qX-*|{l6&lfC#dMaHRcuue9-aVz<&S7B2i|Pvl=2}j?anEID*L-kd`uNlo zs^yx*Hch-(CVDp&7CStG!I4v(fkV3RA?nEh9RTW&_Sc1XYf%*tK6c`?49Me@8DxOe zc6XFm4pGSVwsS|1Sx+B2Y%7+2Qx_=&z(C0h7G{uRyus4mX=h}p9q;=1VfYbz?3d9DW6Ll>3jBEelPOd1u(zPCWcr;ry*lG{OiIX57pAaZ- zq1dz_C)LZHnlJiZ&li#aWo;U3em$pn$_SnhOJmUujxBjJtDwq2VX+t z$i6+jldoo7adMn}2NkFP|UDGA>6$$7jU0GF{empvNLyioz z)Nhp|6z8n^Lc0163+a4eX|KRh&`D02w?akvwFgg4rF)+Oaga#C(aCZIL|(<>E)yD)=loNj?RVp zk4K;lly*Ewj+$4#?1TP=I}PCS?i=0 zd*U_Fb3RV7c&d2ASx*#|oA1RrY2dm0tEZW$|t6 z5kmR?)V*suGYvDs(m9>d_e}2}{AE`Tysn@eS>#Ex25{s=6;w)yb;H{v`GabHls!x) zc9;+io?K5!T_LaZ93I=-S*^-|l9oxQ&@_S3N9s}+o@6=;^XggtSS^4?$yvxgdidly};dcSCXk8cm(t*NT7wlL#JCz_Fl@<%u4eiZOhcIM7 z6fdAWI3nfj?Hw3^zQU}vCbouA(si(TQG9tZM2m(u9GsdEqX~YeQ{IsSUT8XgqWtX>V1l_9|9|4C`#%Ms zeztg}=8_WsP1RrN1(@qcs2IJ8m9D#&37P6TN3&i2=kdy}qDHAydKqm~R8^&J5q0no z@RgVGfGLZujUa%08j->=^_NzsS7%JxQh_?UepSl8#Ghy^vb7M*YsYCrYj5%r`7AVp z9;+8T%>u~4hM5b@>>mvcl^k@&UuyCzsjs;@iQ9BtvRykETqnb#4{}-{rWF7+JJi6! zCPlC&Qv-Fb&U#MXhkk-&I&mKR8#ms(e4J8tb2k~md-Uz|+yfONTrPiJ_3+32@Zp_B z%;{?2yJqC%1k-w6C-gPZDIcd*IRU3)f|}uCY~DEce4DG#qw@YSGu{W|FHmxInh)<^ zLr^yNqyiHugk3u5leCw2k2SLnM=0BA%$eG1Vw6aZOiq1r7(QV%RWP^KzdJDyd2fo& zqV*hfO8VQ<%s@aiG3z86cViO#^>avqdG@}kLD!;xrzRtm2xE6ctUWf%l!07Wvg)-9 zh?hC51;1kj8^hg5P(K8X8|~2F`Gp~&6f4A(&aUS7VbQS#x|B=r2! zd+Iv{9ND1+XZncw{N2*l!-UR*$GN@oe^zd*8b<3{gJn4OQLUjNOw1x+^K4mVdPpkt zc3DXSYNIg=KyAb##tSr;3s`f#b6UBr5~E&5yepesOF!Ik)O|IrEsOVWjIet6pGr#T z0Dfi#6Rg6``9U^}A_AMN0qhxAuz>B)^o=1q&tWF6gcaT15)%m;C7+UpHigKV=3xUk z&j$w_q{i1%uWGBvWQ}{10V_59?V2%d_cOj3lu25Kk++$KSf$U^R zI3)vFJI@KRw&lFC$SmO`spodqi#qPf4N0W`0X+};x96K;KB6^<0=#6q+2KlWWu_QM zWss1Y)_{8=dM`>_i8u&HMWwG(V#}F_f(70rmw;H-i*3hc%;xU5px+uqzi0b^Np$Hk zWdExY6Rs6xNpunq>IHaYx5TFq{pfLiVEBxiOoX~GZZ{V&QT#yW`FnaD>F`dyiA1Y) z6(6a7!lg3mLoz^wYkzr$MY1>84zV{zCJOc1lXKpe$Pjl6@l#;x0=pbzI-&c~IS|cx hOXzvqKm-{^08}PMaS`P&l4)eXd)FTK+itXs{{rD$`dt73 diff --git a/src/LightApp/resources/icon_remove_module.png b/src/LightApp/resources/icon_remove_module.png new file mode 100644 index 0000000000000000000000000000000000000000..037c8265fbc49083386f15f6c90286ed0bc89ecb GIT binary patch literal 2016 zcmV<62Os!}P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D2X#qAK~!i%?U-F` z9Mu)a=g!y8etOsT#$O46gir$Er3BN5sxNr~;)JSz5Fn5uS`u%FP*55m6;Ya$FY$z0 z#|c#t4^^dz3aMZ_5TK|*Ax3=IB#srNB0!{Z%Gw*R?VYckxwrqxUXqaAnVq#I`r@B- zclMsSbLX6U&pr3t5f)iwkwyNm5Pl8hn?JrJ9NxVr!*sogx~?cHioqh0g0%8lYxAd{ z@K*DuKbw`AX{J!3M3yK?B#OQHNuA#gq^*|!n z$*Erw$9YO{-Jf!C1wv++B>f!*Je$+?FS)cK_Q&I$#I|=3*S%k$w2h0sz6C*&-b4Q{ zvTLqBk~{qc<9Di%__Zb*9Oo9iU>`ca8PzL|jS$9e5M0`Eo2Gqm*l|i+9*UO|@swWk3Jt z_V!LLt;@?@OEaor?uGIXqK`NiUx@@G)-v0^^U%hPzMkT1fOYn48Xc^{3u0X4he{Z| zm!3cW!Yj*{*EQnRRo7+M)Z`xsrJGQVR-`^KmV}XO3ZtV2!+><3_nAEJu<_*TRGf{T0! zoPqBdDwm5rnl?rVX~B-{xl>ppy#B+wUIkSd5@cmp&M;4LNg%#a=t{(h`fs9Xdr2hn z8b;Q4YhMUNRgaK}_B&BkPI6IQLd}53fN8RgiFiSZ#zv5IwPGf&gIKQIT_8j#V_*(T z$>bBLo!ne3h902s{(KqziNv+YQFg(UBCiDzk&Y*1< zZd27USl@=SkeRTNHor|}c~8zXKj*TBn3n+_L#|s`AB&wth`a_n>-AjR7_xj62cunE zO>>k>8Yc82u7#9JeTV5U@@nIV;+zzZo#nN8G4C1Z%Vg4|IK4w~oCg|>^B1ACLnu3m zo=kdVKsOpXPkhh34ER$zosvo=hIqcpUxJWXDoOoRRbNE@G`Y<%_>KC6o`F|atRS?cXNh4x zhLQg*lutvvaOErDWJlK`EX$IjjQuLxT6$x4j^7FKf43oJo(+hz`j`GkfBAwiMH=x^D6UOL6n%PPVoOV33eeuy!?EsQuBu> z$}keUr{FTfT;xk`ZtfIGCOsqmS?6R|#%!G+>GU|S*Cg15qS2$s7(c{6?{HoV2EQJb z(&?SQDHObRq1b%>{1^=00R`WO5w&Mnl179`q`&vfnZW!gFbWNt=G^*d^lL#>n-zu0`A@;YlhzQdi#I|q3 zfOMf?C#HEQKi=BYbNZe`hj>eWLWjo#t*voE*IV#F1j<`D1{U0Sth8<7T<8P1AAei7 yj?7I@OM*y #include #include +#include +#include #include #include #include @@ -536,7 +538,7 @@ bool QtxResourceMgr::IniFormat::load( const QString& fname, QMap impMap; if ( !load( impFInfo.absoluteFilePath(), impMap, importHistory ) ) { @@ -1032,6 +1034,222 @@ QString QtxResourceMgr::XmlFormat::valueAttribute() const return str; } +/*! + \class QtxResourceMgr::JsonFormat + \internal + \brief Reader/writer for .json resources files. +*/ + +class QtxResourceMgr::JsonFormat : public Format +{ +public: + JsonFormat(); + ~JsonFormat(); + +protected: + JsonFormat( const QString& ); + virtual bool load( const QString&, QMap& ); + virtual bool save( const QString&, const QMap& ); + +private: + bool load( const QString&, QMap&, QSet& ); +}; + +/*! + \brief Constructor. +*/ +QtxResourceMgr::JsonFormat::JsonFormat() +: QtxResourceMgr::JsonFormat( "json" ) +{ +} + +/*! + \brief Constructor. +*/ +QtxResourceMgr::JsonFormat::JsonFormat( const QString& fmt ) +: Format( fmt ) +{ +} + +/*! + \brief Destructor. +*/ +QtxResourceMgr::JsonFormat::~JsonFormat() +{ +} + +/*! + \brief Load resources from json-file. + \param fname resources file name + \param secMap resources map to be filled in + \return \c true on success and \c false on error +*/ +bool QtxResourceMgr::JsonFormat::load( const QString& fname, QMap& secMap ) +{ + QSet importHistory; + return load( fname, secMap, importHistory ); +} + +/*! + \brief Load resources from json-file. + \param fname resources file name + \param secMap resources map to be filled in + \param importHistory list of already imported resources files (to prevent import loops) + \return \c true on success or \c false on error +*/ +bool QtxResourceMgr::JsonFormat::load( const QString& fname, QMap& secMap, QSet& importHistory ) +{ + QString aFName = fname.trimmed(); + if ( !QFileInfo( aFName ).exists() ) + { + if ( QFileInfo( aFName + ".json" ).exists() ) + aFName += ".json"; + else if ( QFileInfo( aFName + ".JSON" ).exists() ) + aFName += ".JSON"; + else + return false; // file does not exist + } + QFileInfo aFinfo( aFName ); + aFName = aFinfo.canonicalFilePath(); + + if ( !importHistory.contains( aFName ) ) + importHistory.insert( aFName ); + else + return true; // already imported (prevent import loops) + + QFile file( aFName ); + if ( !file.open( QFile::ReadOnly ) ) + return false; // file is not accessible + + QJsonDocument document = QJsonDocument::fromJson( file.readAll() ); + if ( document.isNull() ) + return false; // invalid json file + + QJsonObject root = document.object(); + foreach ( QString sectionName, root.keys() ) + { + if ( sectionName == "import" ) + { + QString impFile = root.value( sectionName ).toString(); + if ( impFile.isEmpty() ) + continue; + QString impPath = QDir::toNativeSeparators( Qtx::makeEnvVarSubst( impFile, Qtx::Always ) ); + QFileInfo impFInfo( impPath ); + if ( impFInfo.isRelative() ) + impFInfo.setFile( aFinfo.absoluteDir(), impPath ); + QMap impMap; + if ( !load( impFInfo.absoluteFilePath(), impMap, importHistory ) ) + { + qDebug() << "QtxResourceMgr: Error with importing file:" << impPath; + } + else + { + QMap::const_iterator it = impMap.constBegin(); + for ( ; it != impMap.constEnd() ; ++it ) + { + if ( !secMap.contains( it.key() ) ) + { + // insert full section + secMap.insert( it.key(), it.value() ); + } + else + { + // insert all parameters from the section + Section::ConstIterator paramIt = it.value().begin(); + for ( ; paramIt != it.value().end() ; ++paramIt ) + { + if ( !secMap[it.key()].contains( paramIt.key() ) ) + secMap[it.key()].insert( paramIt.key(), paramIt.value() ); + } + } + } + } + } + else + { + QJsonObject section = root.value( sectionName ).toObject(); + if ( !section.isEmpty() ) + { + // case when a top-level item is a section + foreach ( QString parameterName, section.keys() ) + { + // each value must be a string, number, or boolean + QJsonValue parameter = section.value( parameterName ); + if ( parameter.isDouble() ) + secMap[sectionName].insert( parameterName, QString::number( parameter.toDouble() ) ); + else if ( parameter.isBool() ) + secMap[sectionName].insert( parameterName, QString( parameter.toBool() ? "true" : "false" ) ); + else if ( parameter.isString() ) + secMap[sectionName].insert( parameterName, parameter.toString() ); + } + } + else + { + QString parameterName = sectionName; + sectionName = "General"; // default section name for top-level items + // each value must be a string, number, or boolean + QJsonValue parameter = root.value( parameterName ); + if ( parameter.isDouble() ) + secMap[sectionName].insert( parameterName, QString::number( parameter.toDouble() ) ); + else if ( parameter.isBool() ) + secMap[sectionName].insert( parameterName, QString( parameter.toBool() ? "true" : "false" ) ); + else if ( parameter.isString() ) + secMap[sectionName].insert( parameterName, parameter.toString() ); + } + } + } + + if ( !secMap.isEmpty() ) + qDebug() << "QtxResourceMgr: File" << fname << "is loaded successfully"; + return true; +} + +/*! + \brief Save resources to the json-file. + \param fname resources file name + \param secMap resources map + \return \c true on success and \c false on error +*/ +bool QtxResourceMgr::JsonFormat::save( const QString& fname, const QMap& secMap ) +{ + if ( !Qtx::mkDir( QFileInfo( fname ).absolutePath() ) ) + return false; + + QFile file( fname ); + if ( !file.open( QFile::WriteOnly ) ) + return false; + + QJsonObject root; + for ( QMap::ConstIterator it = secMap.begin(); it != secMap.end(); ++it ) + { + // note: we write all values as string, as it's enough to store resources as strings + // anyway resource manager converts values to strings when reading JSON file + QJsonObject section; + for ( Section::ConstIterator iter = it.value().begin(); iter != it.value().end(); ++iter ) + section.insert( iter.key(), iter.value() ); + root.insert( it.key(), section ); + } + + QJsonDocument document; + document.setObject( root ); + file.write( document.toJson() ); + file.close(); + return true; +} + +/*! + \class QtxResourceMgr::SalomexFormat + \internal + \brief Reader/writer for .salomex resources files. This is an alias for JSON format. +*/ + +class QtxResourceMgr::SalomexFormat : public JsonFormat +{ +public: + SalomexFormat() : JsonFormat( "salomex" ) {} +}; + + /*! \class QtxResourceMgr::Format \brief Generic resources files reader/writer class. @@ -1127,9 +1345,13 @@ bool QtxResourceMgr::Format::save( Resources* res ) if ( !res ) return false; + QtxResourceMgr* mgr = res->resMgr(); + + if ( mgr->appName().isEmpty() ) + return false; + Qtx::mkDir( Qtx::dir( res->myFileName ) ); - QtxResourceMgr* mgr = res->resMgr(); QString name = mgr ? mgr->userFileName( mgr->appName(), false ) : res->myFileName; return save( name, res->mySections ); } @@ -1168,7 +1390,7 @@ bool QtxResourceMgr::Format::save( Resources* res ) (internationalization mechanism), load pixmaps and other resources from external files, etc. - Currently it supports .ini and .xml resources file formats. To implement + Currently it supports .ini, .xml, and .json resources file formats. To implement own resources file format, inherit from the Format class and implement virtual Format::load() and Format::save() methods. @@ -1264,6 +1486,26 @@ QtxResourceMgr::QtxResourceMgr( const QString& appName, const QString& resVarTem installFormat( new XmlFormat() ); installFormat( new IniFormat() ); + installFormat( new JsonFormat() ); + installFormat( new SalomexFormat() ); + + setOption( "translators", QString( "%P_msg_%L.qm|%P_images.qm" ) ); +} + +/*! + \brief Default constructor +*/ +QtxResourceMgr::QtxResourceMgr() +: myCheckExist( true ), + myDefaultPix( 0 ), + myIsPixmapCached( true ), + myHasUserValues( false ), + myWorkingMode( IgnoreUserValues ) +{ + installFormat( new XmlFormat() ); + installFormat( new IniFormat() ); + installFormat( new JsonFormat() ); + installFormat( new SalomexFormat() ); setOption( "translators", QString( "%P_msg_%L.qm|%P_images.qm" ) ); } @@ -1339,7 +1581,7 @@ QStringList QtxResourceMgr::dirList() const */ void QtxResourceMgr::initialize( const bool autoLoad ) const { - if ( !myResources.isEmpty() ) + if ( !myResources.isEmpty() || appName().isEmpty() ) return; QtxResourceMgr* that = (QtxResourceMgr*)this; @@ -2189,7 +2431,7 @@ void QtxResourceMgr::setCurrentFormat( const QString& fmt ) myFormats.removeAll( form ); myFormats.prepend( form ); - if ( myResources.isEmpty() ) + if ( myResources.isEmpty() || appName().isEmpty() ) return; ResList::Iterator resIt = myResources.begin(); @@ -2382,6 +2624,42 @@ bool QtxResourceMgr::save() return result; } +/*! + \brief Load resource from given file. +*/ +bool QtxResourceMgr::addResource( const QString& fname ) +{ + if ( fname.isEmpty() ) + return false; + + QFileInfo fi( fname ); + + if ( fi.exists() && fi.isDir() && !appName().isEmpty() ) + fi.setFile( QDir( fname ).filePath( globalFileName( appName() ) ) ); + + if ( !fi.exists() ) + return false; + + QString dirName = fi.absolutePath(); + if ( myDirList.contains( dirName ) ) + return true; // file already loaded + + Format* fmt = format( fi.suffix() ); + if ( !fmt ) + return false; + + Resources* resource = new Resources( this, fi.absoluteFilePath() ); + if ( !fmt->load( resource ) ) + { + delete resource; + return false; + } + + myDirList << dirName; + myResources << resource; + return true; +} + /*! \brief Get all sections names. \return list of section names @@ -2744,7 +3022,8 @@ void QtxResourceMgr::loadLanguage( const QString& pref, const QString& preferabl initialize( true ); QMap substMap; - substMap.insert( 'A', appName() ); + if ( !appName().isEmpty() ) + substMap.insert( 'A', appName() ); QString lang = language( preferableLanguage ); diff --git a/src/Qtx/QtxResourceMgr.h b/src/Qtx/QtxResourceMgr.h index 9c9ced174..3694ae9d6 100644 --- a/src/Qtx/QtxResourceMgr.h +++ b/src/Qtx/QtxResourceMgr.h @@ -53,6 +53,8 @@ class QTX_EXPORT QtxResourceMgr { class IniFormat; class XmlFormat; + class JsonFormat; + class SalomexFormat; class Resources; public: @@ -71,6 +73,7 @@ public: } WorkingMode; public: + QtxResourceMgr(); QtxResourceMgr( const QString&, const QString& = QString() ); virtual ~QtxResourceMgr(); @@ -168,6 +171,7 @@ public: bool load(); bool import( const QString& ); bool save(); + bool addResource( const QString& ); QStringList sections() const; QStringList sections(const QRegExp&) const; diff --git a/src/STD/STD_Application.cxx b/src/STD/STD_Application.cxx index 1fa73dfe1..27a0c1722 100644 --- a/src/STD/STD_Application.cxx +++ b/src/STD/STD_Application.cxx @@ -88,6 +88,7 @@ QString STD_Application::applicationName() const void STD_Application::start() { createActions(); + customize(); updateDesktopTitle(); updateCommandsStatus(); @@ -270,6 +271,13 @@ void STD_Application::createActions() createTool( EditPasteId, stdTBar ); } +/*! + Customize actions. +*/ +void STD_Application::customize() +{ +} + /*!Opens new application*/ void STD_Application::onNewDoc() { @@ -561,13 +569,13 @@ bool STD_Application::openAction( const int choice, const QString& aName ) } /*!Save document if all ok, else error message.*/ -void STD_Application::onSaveDoc() +bool STD_Application::onSaveDoc() { if ( !activeStudy() ) - return; + return false; if ( !abortAllOperations() ) - return; + return false; bool isOk = false; if ( activeStudy()->isSaved() ) @@ -594,7 +602,8 @@ void STD_Application::onSaveDoc() if ( isOk ) studySaved( activeStudy() ); else - onSaveAsDoc(); + isOk = onSaveAsDoc(); + return isOk; } /*! \retval \c true, if document saved successfully, else \c false.*/ diff --git a/src/STD/STD_Application.h b/src/STD/STD_Application.h index 25273d5f1..4a3631c1d 100644 --- a/src/STD/STD_Application.h +++ b/src/STD/STD_Application.h @@ -125,7 +125,7 @@ public slots: virtual bool onNewDoc( const QString& ); virtual void onCloseDoc( bool ask = true ); - virtual void onSaveDoc(); + virtual bool onSaveDoc(); virtual bool onSaveAsDoc(); virtual void onOpenDoc(); @@ -154,6 +154,7 @@ protected: protected: virtual void createActions(); + virtual void customize(); virtual void updateCommandsStatus(); virtual void setDesktop( SUIT_Desktop* ); diff --git a/src/SUIT/SUIT_PreferenceMgr.cxx b/src/SUIT/SUIT_PreferenceMgr.cxx index b19b0797d..4b2c482a6 100644 --- a/src/SUIT/SUIT_PreferenceMgr.cxx +++ b/src/SUIT/SUIT_PreferenceMgr.cxx @@ -174,6 +174,18 @@ int SUIT_PreferenceMgr::addItem( const QString& title, const int pId, return item ? item->id() : -1; } +void SUIT_PreferenceMgr::removeItem( const QString& title ) +{ + if ( myRoot ) + { + QtxPreferenceItem* item = myRoot->findItem( title, false ); + if ( item ) { + QtxPagePrefMgr::removeItem( item ); + delete item; + } + } +} + QVariant SUIT_PreferenceMgr::optionValue( const QString& name ) const { QVariant val = QtxPagePrefMgr::optionValue( name ); diff --git a/src/SUIT/SUIT_PreferenceMgr.h b/src/SUIT/SUIT_PreferenceMgr.h index b78490e8d..8e150a2b4 100644 --- a/src/SUIT/SUIT_PreferenceMgr.h +++ b/src/SUIT/SUIT_PreferenceMgr.h @@ -53,6 +53,7 @@ public: int addItem( const QString&, const int pId = -1, const PrefItemType = Auto, const QString& = QString(), const QString& = QString() ); + void removeItem( const QString& ); protected: virtual QVariant optionValue( const QString& ) const; diff --git a/src/SalomeApp/SalomeApp_Application.cxx b/src/SalomeApp/SalomeApp_Application.cxx index 3e60d3282..1e9492add 100644 --- a/src/SalomeApp/SalomeApp_Application.cxx +++ b/src/SalomeApp/SalomeApp_Application.cxx @@ -110,6 +110,9 @@ #include +#include +#include CORBA_CLIENT_HEADER(SALOME_ModuleCatalog) + std::unique_ptr SalomeApp_Application::_ns; /*!Internal class that updates object browser item properties */ @@ -2156,3 +2159,19 @@ void SalomeApp_Application::ensureShaperIsActivated() onDesktopMessage("register_module_in_study/Shaper"); } } + +void SalomeApp_Application::addCatalogue( const QString& moduleName, const QString& catalogue ) +{ + CORBA::Object_var obj = namingService()->Resolve( "/Kernel/ModulCatalog" ); + SALOME_ModuleCatalog::ModuleCatalog_var moduleCatalogue = SALOME_ModuleCatalog::ModuleCatalog::_narrow( obj ); + QFileInfo fi( catalogue ); + if ( !CORBA::is_nil( moduleCatalogue ) && fi.isFile() ) + { + SALOME_ModuleCatalog::ListOfComponents_var known = moduleCatalogue->GetComponentList(); + bool loaded = false; + for ( int i = 0; i < (int)known->length() && !loaded; i++ ) + loaded = QString( known[i].in() ) == moduleName; + if ( !loaded ) + moduleCatalogue->ImportXmlCatalogFile( catalogue.toUtf8().constData() ); + } +} diff --git a/src/SalomeApp/SalomeApp_Application.h b/src/SalomeApp/SalomeApp_Application.h index c0e84fc37..f96590866 100644 --- a/src/SalomeApp/SalomeApp_Application.h +++ b/src/SalomeApp/SalomeApp_Application.h @@ -178,6 +178,8 @@ protected: virtual bool canOpenDoc( const QString& ); virtual void afterCloseDoc(); + virtual void addCatalogue( const QString&, const QString& ); + private slots: void onDeleteInvalidReferences(); void onDblClick( SUIT_DataObject* ); -- 2.30.2