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)
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)
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()
--- /dev/null
+.. _extend_salome:
+
+******************************
+Add/remove modules dynamically
+******************************
+
+.. contents:: Table of Contents
+
+"SALOME on demand" feature provides a possibility to dynamically add or remove
+SALOME modules directly from GUI.
+
+.. _hp_salomex_format:
+
+Description file
+================
+
+**"SALOME on demand"** feature introduces notion of a module description file.
+This is the file in JSON format that has `.salomex` extension. The description
+file provides some parameters of the module; two parameters are mandatory:
+name of the module and path to its root directory.
+
+Example:
+
+.. code-block:: json
+
+ {
+ "name": "PYHELLO",
+ "root": "/home/user/salome/modules/PYHELLO"
+ }
+
+.. note:: In the description file, the `name` parameter is an **internal** name of the module,
+ in contrast to its **title** which is described in `SalomeApp.xml` or `LightApp.xml`
+ resource file.
+
+.. note:: One `.salomex` file may describe one SALOME module only.
+
+.. _hp_modules_toolbar:
+
+Modules toolbar
+===============
+
+The *Modules* toolbar provides two additional buttons, which allow adding or removing
+modules directly from GUI.
+
+.. figure:: ../images/modules_toolbar.png
+ :align: center
+ :alt: Modules toolbar
+
+.. |add| image:: ../images/modules_add.png
+ :height: 24px
+
+.. |remove| image:: ../images/modules_remove.png
+ :height: 24px
+
+.. _hp_modules_add:
+
+Adding modules
+--------------
+
+Pressing the first button |add| shows a standard *Open File* dialog that allows
+selecting one or more `.salomex` files. Each module correctly configured in the
+corresponding `.salomex` file is then added to the SALOME session and appears in the
+*Modules* toolbar. As soon as it is done, the user may work with the module in the same
+way as with any other module: all features are available after activating module from
+*Modules* toolbar.
+
+The modules added in this way become *persistent*. This means that after restarting
+SALOME these modules are available in the *Modules* toolbar.
+
+.. _hp_modules_remove:
+
+Removing modules
+----------------
+
+Pressing a small arrow near the second button |remove| shows a drop-down menu with
+the list of manually added modules, where the user may select a module to remove. As soon
+as user selects a module to remove, a message box is shown asking the user if it is
+necessary also to remove a directory, containing the module, from disk. If user
+confirms removal of the directory, it will be necessary to save the study, in order to
+avoid possible data loss.
+
+.. note:: The module libraries are not removed from RAM until exit from the current
+ SALOME session, so some its functionality may be still accessible, for example,
+ via Python API. However, using module functionality may have unpredicted
+ behavior.
drag_and_drop.rst
using_pluginsmanager.rst
using_help_panel.rst
-
+ extend_salome.rst
if ( !myInfoList.isEmpty() )
return;
+ // we cannot use own resourceMgr() as this method can be called from constructor
SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
QStringList modList;
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<ModuleInfo> it( myInfoList );
+ while ( it.hasNext() )
+ {
+ ModuleInfo info = it.next();
+ if ( info.name == modName )
+ {
+ it.remove();
+ break;
+ }
}
}
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<ModuleInfo> ModuleInfoList;
+ ModuleInfo() : status( stInvalid ) {}
+ };
+ void readModuleList();
private:
+ typedef QList<ModuleInfo> 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
};
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
)
}
return QString();
}
+
+ const bool HAS_SALOME_ON_DEMAND =
+#if defined(WITH_SALOME_ON_DEMAND)
+ true;
+#else
+ false;
+#endif
}
/*!Create new instance of LightApp_Application.*/
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
// 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 <QString, QString> 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<QString, QString > 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" );
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<QString, QString> 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 );
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<LightApp_ModuleAction*>( 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<QString, QString> 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<int, QString> opmap = activateModuleActions();
for ( QMap<int, QString>::ConstIterator it = opmap.begin(); it != opmap.end(); ++it )
aDlg.addButton( it.value(), it.key() );
else {
// cancelled
putInfo( tr("INF_CANCELLED") );
-
- LightApp_ModuleAction* moduleAction =
- qobject_cast<LightApp_ModuleAction*>( 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<LightApp_ModuleAction*>( 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<LightApp_Module*>( 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<LightApp_ModuleAction*>( 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.*/
{
// 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://" );
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() );
}
}
}
if ( !crt )
return myPrefs;
- SUIT_ResourceMgr* resMgr = resourceMgr();
-
QList<SUIT_Application*> appList = SUIT_Session::session()->applications();
for ( QList<SUIT_Application*>::iterator appIt = appList.begin(); appIt != appList.end(); ++appIt )
{
QStringList names;
app->modules( names, false );
- // icons of modules
- QMap<QString, QString> 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 )
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
}
}
*/
void LightApp_Application::updateModuleActions()
{
- QString modName;
- if ( activeModule() )
- modName = activeModule()->moduleName();
-
- LightApp_ModuleAction* moduleAction =
- qobject_cast<LightApp_ModuleAction*>( action( ModulesListId ) );
- if ( moduleAction )
- moduleAction->setActiveModule( modName );
-}
-
-void LightApp_Application::removeModuleAction( const QString& modName )
-{
- LightApp_ModuleAction* moduleAction =
- qobject_cast<LightApp_ModuleAction*>( action( ModulesListId ) );
- if ( moduleAction )
- moduleAction->removeModule( modName );
+ emit moduleActivated( activeModule() ? activeModule()->moduleName() : QString() );
}
bool LightApp_Application::checkModule( const QString& title )
}
}
-/*!
- Adds icon names for modules
-*/
-void LightApp_Application::moduleIconNames( QMap<QString, QString>& 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;
}
/*!
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 ) ) {
}
else if( answer == 1 ) {
if (closeExistingDoc) {
- closeDoc( false );
+ closeDoc( false );
}
} else if( answer == 2 ) {
result = false;
}
#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 <QString, QString> 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<QString, QString > 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 );
+ }
+}
#include <SUIT_DataObject.h>
#include <CAM_Application.h>
+#include <QPixmap>
#include <QPointer>
#include <QStringList>
#endif
signals:
+ void moduleActivated( const QString& );
void studyOpened();
void studySaved();
void studyClosed();
protected:
void showHelp( const QString& );
virtual void createActions();
+ virtual void customize();
virtual void createActionForViewer( const int id,
const int parentId,
const QString& suffix,
virtual PyConsole_Interp* createPyInterp();
#endif
+ virtual void addCatalogue( const QString&, const QString& ) {}
+
protected slots:
virtual void onDesktopActivated();
virtual void onViewManagerRemoved( SUIT_ViewManager* );
void onNewWindow();
virtual void onModuleActivation( const QString& );
+ void onModuleAdding();
+ void onModuleRemoving( const QString& );
void onCloseView( SUIT_ViewManager* );
virtual void onStudyCreated( SUIT_Study* );
void updateWindows();
void updateViewManagers();
void updateModuleActions();
- void removeModuleAction( const QString& );
bool checkModule( const QString& );
QString defaultModule() const;
virtual void currentWindows( QMap<int, int>& ) const;
void currentViewManagers( QStringList& ) const;
- void moduleIconNames( QMap<QString, QString>& ) const;
+ QPixmap moduleIcon( const QString&, const int = -1 ) const;
QDockWidget* windowDock( QWidget* ) const;
QByteArray dockWindowsState( const QMap<QString, bool>&, const QMap<QString, bool>& ) const;
void showPreferences( const QStringList& );
private:
+ bool addUserModule( const QString&, const QString&, bool = false );
void emptyPreferences( const QString& );
QList<QToolBar*> 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<QWidget> WinPtr;
- typedef QMap<int, WinPtr> WinMap;
- typedef QMap<QString, QByteArray> WinVis;
- typedef QMap<QString, QByteArray> WinGeom;
+ typedef QPointer<QWidget> WinPtr;
+ typedef QMap<int, WinPtr> WinMap;
+ typedef QMap<QString, QByteArray> WinVis;
+ typedef QMap<QString, QByteArray> WinGeom;
+ typedef QList<int> IdList;
+ typedef QMap<QString, IdList> IdMap;
enum { OpenReload = CAM_Application::OpenExist + 1 };
WinMap myWin;
WinVis myWinVis;
+ IdMap myHelpItems;
SUIT_Accel* myAccel;
QTimer* myAutoSaveTimer;
//
#include "LightApp_ModuleAction.h"
-#include <QtxComboBox.h>
#include <QtxActionSet.h>
+#include <QtxComboBox.h>
+#include <QtxResourceMgr.h>
#include <QVBoxLayout>
#include <QApplication>
#include <QEvent>
+#include <QMenu>
+#include <QSignalMapper>
/*!
\class LightApp_ModuleAction::ActionSet
QList<QWidget*> wlist = createdWidgets();
for ( QList<QWidget*>::const_iterator wit = wlist.begin(); wit != wlist.end(); ++wit )
- lst += (*wit)->findChildren<QtxComboBox*>();
+ lst += qobject_cast<QtxComboBox*>(*wit);
return lst;
}
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;
}
/*!
\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 ) ) );
}
/*!
*/
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();
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();
}
/*!
\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 );
}
/*!
{
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();
}
{
if ( w->inherits( "QToolBar" ) )
w->removeAction( myCombo );
+ w->removeAction( myAdd );
+ w->removeAction( myRemove );
+ w->removeAction( mySeparator );
w->removeAction( mySet );
}
\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
for ( QList<QtxComboBox*>::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 );
}
#endif
class QtxComboBox;
+class QtxResourceMgr;
+class QSignalMapper;
class LIGHTAPP_EXPORT LightApp_ModuleAction : public QtxAction
{
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;
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* );
signals:
void moduleActivated( const QString& );
+ void adding();
+ void removing( const QString& );
private:
- void init();
-
void update();
void update( QtxComboBox* );
private:
ComboAction* myCombo;
+ QtxAction* myAdd;
+ QtxAction* myRemove;
ActionSet* mySet;
+ QAction* mySeparator;
int myMode;
+ QSignalMapper* myMapper;
};
class LightApp_ModuleAction::ComboAction : public QtxAction
return id;
}
+/*!
+ Remove module preferences.
+*/
+void LightApp_Preferences::removeModule( const QString& mod )
+{
+ QMutableMapIterator<int, QString> it( myPrefMod );
+ while ( it.hasNext() )
+ {
+ it.next();
+ if ( it.value() == mod )
+ it.remove();
+ }
+ removeItem( mod );
+}
+
/*!
Checks: is preferences has module with name \a mod.
*/
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& );
<source>ABOUT_SPLASH</source>
<translation>icon_about.png</translation>
</message>
- <message>
- <source>APP_MODULE_BIG_ICO</source>
- <translation>icon_module_big.png</translation>
- </message>
<message>
<source>APP_BASE_LOGO</source>
<translation>icon_applogo.png</translation>
<source>ICON_LIFE_RIGN</source>
<translation>icon_life_ring.png</translation>
</message>
+ <message>
+ <source>ICON_ADD_MODULE</source>
+ <translation>icon_add_module.png</translation>
+ </message>
+ <message>
+ <source>ICON_REMOVE_MODULE</source>
+ <translation>icon_remove_module.png</translation>
+ </message>
</context>
</TS>
<source>INFO_AVAILABLE_MODULES</source>
<translation>Available modules</translation>
</message>
+ <message>
+ <source>WRN_MODULE_BAD_SALOMEX_FILE</source>
+ <translation>Cannot read module description file:
+%1.</translation>
+ </message>
+ <message>
+ <source>WRN_MODULE_EMPTY_NAME</source>
+ <translation>Empty or invalid module name in:
+%1.</translation>
+ </message>
+ <message>
+ <source>WRN_MODULE_EMPTY_ROOT</source>
+ <translation>Module root directory is not set or incorrectly specified in:
+%1.</translation>
+ </message>
+ <message>
+ <source>WRN_MODULE_ROOT_DOES_NOT_EXIST</source>
+ <translation>Module root directory does not exist:
+%1.</translation>
+ </message>
+ <message>
+ <source>WRN_MODULE_DUPLICATED</source>
+ <translation>Module "%1" is already present in this session</translation>
+ </message>
+ <message>
+ <source>WRN_MODULE_BAD_RESDIR</source>
+ <translation>Bad or non-existing resources directory:
+%1.</translation>
+ </message>
+ <message>
+ <source>WRN_MODULE_CANNOT_READ_CFG</source>
+ <translation>Cannot read XML configuration file for the module from:
+%1.</translation>
+ </message>
+ <message>
+ <source>WRN_MODULE_BAD_CFG_FILE</source>
+ <translation>XML configuration file for the module "%1" is bad or incomplete</translation>
+ </message>
+ <message>
+ <source>TLT_REMOVE_MODULE</source>
+ <translation>Remove module</translation>
+ </message>
+ <message>
+ <source>QUE_REMOVE_MODULE_DIR</source>
+ <translation>Do you also want to remove module directory:
+%1?
+
+If you answer "Yes", you may need to save your study before removal.</translation>
+ </message>
+ <message>
+ <source>WRN_CANNOT_REMOVE_DIR</source>
+ <translation>Cannot remove directory:
+%1.
+
+Continue?</translation>
+ </message>
</context>
<context>
<name>LightApp_Module</name>
<source>ACTIVATE_MODULE_TOP</source>
<translation>Activate/deactivate %1 module</translation>
</message>
+ <message>
+ <source>ADD_MODULE</source>
+ <translation>Add modules</translation>
+ </message>
+ <message>
+ <source>REMOVE_MODULE</source>
+ <translation>Remove modules</translation>
+ </message>
</context>
<context>
<name>LightApp_PyEditor</name>
<source>INFO_AVAILABLE_MODULES</source>
<translation>Modules disponibles</translation>
</message>
+ <message>
+ <source>WRN_MODULE_BAD_SALOMEX_FILE</source>
+ <translation>Cannot read module description file:
+%1.</translation>
+ </message>
+ <message>
+ <source>WRN_MODULE_EMPTY_NAME</source>
+ <translation>Empty or invalid module name in:
+%1.</translation>
+ </message>
+ <message>
+ <source>WRN_MODULE_EMPTY_ROOT</source>
+ <translation>Module root directory is not set or incorrectly specified in:
+%1.</translation>
+ </message>
+ <message>
+ <source>WRN_MODULE_ROOT_DOES_NOT_EXIST</source>
+ <translation>Module root directory does not exist:
+%1.</translation>
+ </message>
+ <message>
+ <source>WRN_MODULE_DUPLICATED</source>
+ <translation>Module "%1" is already present in this session</translation>
+ </message>
+ <message>
+ <source>WRN_MODULE_BAD_RESDIR</source>
+ <translation>Bad or non-existing resources directory:
+%1.</translation>
+ </message>
+ <message>
+ <source>WRN_MODULE_CANNOT_READ_CFG</source>
+ <translation>Cannot read XML configuration file for the module from:
+%1.</translation>
+ </message>
+ <message>
+ <source>WRN_MODULE_BAD_CFG_FILE</source>
+ <translation>XML configuration file for the module "%1" is bad or incomplete</translation>
+ </message>
+ <message>
+ <source>TLT_REMOVE_MODULE</source>
+ <translation>Remove module</translation>
+ </message>
+ <message>
+ <source>QUE_REMOVE_MODULE_DIR</source>
+ <translation>Do you also want to remove module directory:
+%1?
+
+If you answer "Yes", you may need to save your study before removal.</translation>
+ </message>
+ <message>
+ <source>WRN_CANNOT_REMOVE_DIR</source>
+ <translation>Cannot remove directory:
+%1.
+
+Continue?</translation>
+ </message>
</context>
<context>
<name>LightApp_Module</name>
<source>ACTIVATE_MODULE_TOP</source>
<translation>Activer/désactiver le module %1</translation>
</message>
+ <message>
+ <source>ADD_MODULE</source>
+ <translation>Ajouter les modules</translation>
+ </message>
+ <message>
+ <source>REMOVE_MODULE</source>
+ <translation>Supprimer les modules</translation>
+ </message>
</context>
<context>
<name>LightApp_PyEditor</name>
<source>INFO_AVAILABLE_MODULES</source>
<translation type="unfinished">Available modules</translation>
</message>
+ <message>
+ <source>WRN_MODULE_BAD_SALOMEX_FILE</source>
+ <translation>Cannot read module description file:
+%1.</translation>
+ </message>
+ <message>
+ <source>WRN_MODULE_EMPTY_NAME</source>
+ <translation>Empty or invalid module name in:
+%1.</translation>
+ </message>
+ <message>
+ <source>WRN_MODULE_EMPTY_ROOT</source>
+ <translation>Module root directory is not set or incorrectly specified in:
+%1.</translation>
+ </message>
+ <message>
+ <source>WRN_MODULE_ROOT_DOES_NOT_EXIST</source>
+ <translation>Module root directory does not exist:
+%1.</translation>
+ </message>
+ <message>
+ <source>WRN_MODULE_DUPLICATED</source>
+ <translation>Module "%1" is already present in this session</translation>
+ </message>
+ <message>
+ <source>WRN_MODULE_BAD_RESDIR</source>
+ <translation>Bad or non-existing resources directory:
+%1.</translation>
+ </message>
+ <message>
+ <source>WRN_MODULE_CANNOT_READ_CFG</source>
+ <translation>Cannot read XML configuration file for the module from:
+%1.</translation>
+ </message>
+ <message>
+ <source>WRN_MODULE_BAD_CFG_FILE</source>
+ <translation>XML configuration file for the module "%1" is bad or incomplete</translation>
+ </message>
+ <message>
+ <source>TLT_REMOVE_MODULE</source>
+ <translation>Remove module</translation>
+ </message>
+ <message>
+ <source>QUE_REMOVE_MODULE_DIR</source>
+ <translation>Do you also want to remove module directory:
+%1?
+
+If you answer "Yes", you may need to save your study before removal.</translation>
+ </message>
+ <message>
+ <source>WRN_CANNOT_REMOVE_DIR</source>
+ <translation>Cannot remove directory:
+%1.
+
+Continue?</translation>
+ </message>
</context>
<context>
<name>LightApp_Module</name>
<source>ACTIVATE_MODULE_TOP</source>
<translation>モジュール %1 の有効/無効にします。</translation>
</message>
+ <message>
+ <source>ADD_MODULE</source>
+ <translation>Add modules</translation>
+ </message>
+ <message>
+ <source>REMOVE_MODULE</source>
+ <translation>Remove modules</translation>
+ </message>
</context>
<context>
<name>LightApp_PyEditor</name>
return full ? QFileInfo( path ).completeSuffix() : QFileInfo( path ).suffix();
}
+/*!
+ \brief EXtract base library name.
+
+ The function removes platform-specific prefix (lib) and suffix (.dll/.so)
+ from the library file name.
+ For example, if \a str = "libmylib.so", "mylib" is returned..
+
+ \param libName library name
+ \return base library name
+*/
+QString Qtx::libraryName( const QString& libName )
+{
+ QString fullName = file( libName );
+#if defined(WIN32)
+ QString libExt = QString( "dll" );
+#elif defined(__APPLE__)
+ QString libExt = QString( "dylib" );
+#else
+ QString libExt = QString( "so" );
+#endif
+ if ( extension( fullName ).toLower() == libExt )
+ fullName.truncate( fullName.length() - libExt.length() - 1 );
+#ifndef WIN32
+ QString prefix = QString( "lib" );
+ if ( fullName.startsWith( prefix ) )
+ fullName.remove( 0, prefix.length() );
+#endif
+ return fullName;
+}
+
/*!
\brief Convert the given parameter to the platform-specific library name.
The function appends platform-specific prefix (lib) and suffix (.dll/.so)
to the library file name.
- For example, if \a str = "mylib", "libmylib.so" is returned for Linux and
+ For example, if \a libName = "mylib", "libmylib.so" is returned for Linux and
mylib.dll for Windows.
- \param str short library name
+ \param libName short library name
\return full library name
*/
-QString Qtx::library( const QString& str )
+QString Qtx::library( const QString& libName )
{
- QString path = dir( str, false );
- QString name = file( str, false );
- QString ext = extension( str );
+ QString path = dir( libName, false );
+ QString name = file( libName, false );
+ QString ext = extension( libName );
#ifndef WIN32
if ( !name.startsWith( "lib" ) )
return res;
}
+/*!
+ \brief Return full path obtained by joining given components with
+ the native directory separator.
+ \param path Separate path components
+ \return complete path
+*/
+QString Qtx::joinPath( const QStringList& path )
+{
+ return path.join( QDir::separator() );
+}
+
/*!
\brief Convert text file from DOS format to UNIX.
static QString file( const QString&, const bool = true );
static QString extension( const QString&, const bool = false );
+ static QString libraryName( const QString& );
static QString library( const QString& );
static QString tmpDir();
static bool rmDir( const QString& );
static bool dos2unix( const QString& );
static QString addSlash( const QString& );
+ static QString joinPath( const QStringList& );
static QCompleter* pathCompleter( const PathType, const QString& = QString() );
static QString findEnvVar( const QString&, int&, int& );
#include <QDir>
#include <QFile>
#include <QFileInfo>
+#include <QJsonDocument>
+#include <QJsonObject>
#include <QRegExp>
#include <QTextStream>
#include <QApplication>
/*!
- \brief Load resources from xml-file.
+ \brief Load resources from ini-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)
QString impFile = QDir::toNativeSeparators( Qtx::makeEnvVarSubst( data, Qtx::Always ) );
QFileInfo impFInfo( impFile );
if ( impFInfo.isRelative() )
- impFInfo.setFile( aFinfo.absoluteDir(), impFile );
-
+ impFInfo.setFile( aFinfo.absoluteDir(), impFile );
+
QMap<QString, Section> impMap;
if ( !load( impFInfo.absoluteFilePath(), impMap, importHistory ) )
{
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<QString, Section>& );
+ virtual bool save( const QString&, const QMap<QString, Section>& );
+
+private:
+ bool load( const QString&, QMap<QString, Section>&, QSet<QString>& );
+};
+
+/*!
+ \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<QString, Section>& secMap )
+{
+ QSet<QString> 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<QString, Section>& secMap, QSet<QString>& 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<QString, Section> impMap;
+ if ( !load( impFInfo.absoluteFilePath(), impMap, importHistory ) )
+ {
+ qDebug() << "QtxResourceMgr: Error with importing file:" << impPath;
+ }
+ else
+ {
+ QMap<QString, Section>::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<QString, Section>& secMap )
+{
+ if ( !Qtx::mkDir( QFileInfo( fname ).absolutePath() ) )
+ return false;
+
+ QFile file( fname );
+ if ( !file.open( QFile::WriteOnly ) )
+ return false;
+
+ QJsonObject root;
+ for ( QMap<QString, Section>::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.
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 );
}
(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.
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" ) );
}
*/
void QtxResourceMgr::initialize( const bool autoLoad ) const
{
- if ( !myResources.isEmpty() )
+ if ( !myResources.isEmpty() || appName().isEmpty() )
return;
QtxResourceMgr* that = (QtxResourceMgr*)this;
myFormats.removeAll( form );
myFormats.prepend( form );
- if ( myResources.isEmpty() )
+ if ( myResources.isEmpty() || appName().isEmpty() )
return;
ResList::Iterator resIt = myResources.begin();
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
initialize( true );
QMap<QChar, QString> substMap;
- substMap.insert( 'A', appName() );
+ if ( !appName().isEmpty() )
+ substMap.insert( 'A', appName() );
QString lang = language( preferableLanguage );
{
class IniFormat;
class XmlFormat;
+ class JsonFormat;
+ class SalomexFormat;
class Resources;
public:
} WorkingMode;
public:
+ QtxResourceMgr();
QtxResourceMgr( const QString&, const QString& = QString() );
virtual ~QtxResourceMgr();
bool load();
bool import( const QString& );
bool save();
+ bool addResource( const QString& );
QStringList sections() const;
QStringList sections(const QRegExp&) const;
void STD_Application::start()
{
createActions();
+ customize();
updateDesktopTitle();
updateCommandsStatus();
createTool( EditPasteId, stdTBar );
}
+/*!
+ Customize actions.
+*/
+void STD_Application::customize()
+{
+}
+
/*!Opens new application*/
void STD_Application::onNewDoc()
{
}
/*!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() )
if ( isOk )
studySaved( activeStudy() );
else
- onSaveAsDoc();
+ isOk = onSaveAsDoc();
+ return isOk;
}
/*! \retval \c true, if document saved successfully, else \c false.*/
virtual bool onNewDoc( const QString& );
virtual void onCloseDoc( bool ask = true );
- virtual void onSaveDoc();
+ virtual bool onSaveDoc();
virtual bool onSaveAsDoc();
virtual void onOpenDoc();
protected:
virtual void createActions();
+ virtual void customize();
virtual void updateCommandsStatus();
virtual void setDesktop( SUIT_Desktop* );
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 );
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;
#include <SALOMEDS_Tool.hxx>
+#include <SALOMEconfig.h>
+#include CORBA_CLIENT_HEADER(SALOME_ModuleCatalog)
+
std::unique_ptr<SALOME_NamingService_Abstract> SalomeApp_Application::_ns;
/*!Internal class that updates object browser item properties */
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() );
+ }
+}
virtual bool canOpenDoc( const QString& );
virtual void afterCloseDoc();
+ virtual void addCatalogue( const QString&, const QString& );
+
private slots:
void onDeleteInvalidReferences();
void onDblClick( SUIT_DataObject* );