Salome HOME
[bos #35160][EDF](2023-T1) Keyboard shortcuts. dish/CR35160--Keyboard_Shortcuts 12/head
authordish <Dmitrii.SHVYDKOI@opencascade.com>
Mon, 4 Dec 2023 15:34:45 +0000 (15:34 +0000)
committermbs <martin.bernhard@opencascade.com>
Wed, 15 May 2024 16:29:40 +0000 (17:29 +0100)
Shortcut Mgr is now actually responsible for managing shortcuts: it is solely responsible and capable to dynamically bind actions with hotkey combinations and (de)serialize shortcut preference changes using SUIT_ResourceMgr.

Add SUIT_ShortcutContainer, which ensures that there are no conflicts between shortcuts. Added the ability to change shortcut preferences through the GUI.

Add a development tool to grab all shortcuts of created at runtime QtxActions .
The tool also grabs tooltips of the actions for further dynamic translation from actionID to actionName in runtime
before actions with these ID are created. Both grabbed shortcuts and translations are dumped in XML files with
identical to project-conventional resource files formatting.

49 files changed:
doc/salome/gui/input/setting_preferences.rst
doc/salome/gui/locale/fr/LC_MESSAGES/setting_preferences.po
src/CAM/CAM_Module.cxx
src/CAM/CAM_Module.h
src/LightApp/LightApp_Application.cxx
src/LightApp/LightApp_Application.h
src/LightApp/LightApp_Module.cxx
src/LightApp/LightApp_PreferencesDlg.cxx
src/LightApp/resources/LightApp.xml
src/LightApp/resources/LightApp_msg_en.ts
src/OCCViewer/OCCViewer_ViewWindow.cxx
src/Qtx/CMakeLists.txt
src/Qtx/Qtx.qrc
src/Qtx/QtxAction.cxx
src/Qtx/QtxAction.h
src/Qtx/QtxPagePrefMgr.cxx
src/Qtx/QtxPagePrefMgr.h
src/Qtx/QtxPreferenceMgr.cxx
src/Qtx/QtxPreferenceMgr.h
src/Qtx/QtxShortcutEdit.cxx
src/Qtx/QtxShortcutEdit.h
src/Qtx/QtxWorkspaceAction.cxx
src/Qtx/QtxWorkspaceAction.h
src/Qtx/QtxWorkstack.cxx
src/Qtx/QtxWorkstack.h
src/Qtx/QtxWorkstackAction.cxx
src/Qtx/QtxWorkstackAction.h
src/Qtx/images/shortcut_disable.svg [new file with mode: 0644]
src/Qtx/images/shortcut_restore.svg [new file with mode: 0644]
src/Qtx/resources/Qtx_msg_fr.ts
src/Qtx/resources/Qtx_msg_ja.ts
src/SALOME_PYQT/SalomePyQt/SalomePyQt.h
src/SALOME_PYQT/SalomePyQt/SalomePyQt.sip
src/STD/STD_Application.cxx
src/STD/STD_TabDesktop.cxx
src/SUIT/CMakeLists.txt
src/SUIT/SUIT_Application.cxx
src/SUIT/SUIT_Application.h
src/SUIT/SUIT_PreferenceMgr.cxx
src/SUIT/SUIT_PreferenceMgr.h
src/SUIT/SUIT_ShortcutMgr. ReadMe.md [new file with mode: 0644]
src/SUIT/SUIT_ShortcutMgr.cxx
src/SUIT/SUIT_ShortcutMgr.h
src/SUIT/resources/SUIT_msg_fr.ts
src/SUIT/resources/SUIT_msg_ja.ts
src/SUIT/resources/action_assets.json [new file with mode: 0644]
src/SVTK/SVTK_ViewWindow.cxx
src/SalomeApp/SalomeApp_Application.cxx
tools/DevTools/ShortcutMgr/ShortcutMgr. Resource generator.xlsx [new file with mode: 0644]

index 4e892e81e568988de57094e369ba88a29fc09aa6..0eaa7a25800e9c892d4cac405a16da0d13dc0c5a 100644 (file)
@@ -1,4 +1,4 @@
-.. _setting_preferences_page: 
+.. _setting_preferences_page:
 
 *******************
 Setting Preferences
@@ -13,7 +13,7 @@ The **Preferences** dialog box consists of two parts:
 - Available preferences sections are listed in the left part of the dialog box; usually this list contains "SALOME" item that specifies general application preferences and a separate section for each SALOME module available in the current session.
 - The tabbed widget at the right side of the dialog box provides controls that can be used to customize the preferences.
 
-.. note:: The preferences for modules become accessible only after explicit loading of these modules. Until then the dialog box will show the corresponding warning message. 
+.. note:: The preferences for modules become accessible only after explicit loading of these modules. Until then the dialog box will show the corresponding warning message.
 
 More detailed information about preferences for certain modules can be found in the User's guide of the corresponding module.
 
@@ -23,7 +23,7 @@ application look-n-feel and common functionality.
 
 If the study has just been started and other
 modules have not been loaded yet, it will be possible to change only the settings which refer to
-the whole GUI SALOME session. 
+the whole GUI SALOME session.
 
 General Preferences
 ###################
@@ -40,11 +40,11 @@ General Preferences
 
   - **Show splash screen at start-up** - allows showing or hiding the splash screen at start-up.
   - **Opaque resize** - force opaque resize mode for viewers area (tabbed workspace). Clear this checkbox for less perfomant workstations.
-  - **Drop-down buttons in toolbars for action groups** - when checked, the action groups are represented in the viewer toolbars as a single drop-down button, switchable by the user. Otherwise, all the buttons from the action groups are displayed in the toolbar. 
+  - **Drop-down buttons in toolbars for action groups** - when checked, the action groups are represented in the viewer toolbars as a single drop-down button, switchable by the user. Otherwise, all the buttons from the action groups are displayed in the toolbar.
 
 - **Study Properties**
 
-  - **Multi file save** - if checked in, your study will be saved in several HDF files (one basic HDF file which will store the main information about the saved study and several other files for the data created by each component used during the study session). Opening of this study requires that **ALL** saved files should be stored in the **SAME** directory. If you would like to copy your saved study in another directory or machine, you should copy all stored files. Otherwise, if you try to open this study, some data will be lost and it will lead to invalid functioning of the SALOME platform. 
+  - **Multi file save** - if checked in, your study will be saved in several HDF files (one basic HDF file which will store the main information about the saved study and several other files for the data created by each component used during the study session). Opening of this study requires that **ALL** saved files should be stored in the **SAME** directory. If you would like to copy your saved study in another directory or machine, you should copy all stored files. Otherwise, if you try to open this study, some data will be lost and it will lead to invalid functioning of the SALOME platform.
   - **ASCII save** - if checked in, your study will be saved in ASCII format file (or files).
   - **Automatic loading of light modules when opening study** -  if checked in, Light Modules of the current study will be automatically loaded at the next study opening, allowing completion of object browser.
   - **Store positions of windows** -  if checked in, positions of windows will be saved in a special file at the end of the current session and then restored for a new session.
@@ -55,7 +55,7 @@ General Preferences
   - **Multi file python dump** - allows to generate multiple files (separately for each component) for dumping of a study to a python script. If the option is disabled, the study is dumped to a single python script.
   - **Save GUI state in python dump** - if this option is switched on, the Python script resulting from Dump Python operation will include commands related to the GUI state.
 
-- **External browser** - allows to define what browser will be used to show SALOME reference manuals: internal (built-in SALOME browser) or external (IE, Netscape, Mozilla, ...). In addition, it is possible to specify 
+- **External browser** - allows to define what browser will be used to show SALOME reference manuals: internal (built-in SALOME browser) or external (IE, Netscape, Mozilla, ...). In addition, it is possible to specify
 
     - **Application** - this option allows you to set an external browser (IE, Netscape) which will be used for viewing SALOME reference manuals. By default, Mozilla is used.
     - **Parameters** - additional parameters required for launching of the external browser (if applicable).
@@ -65,13 +65,13 @@ General Preferences
     - **Font** - allows quickly setting the parameters (style, size, face) of the :ref:`font_color_dlg` used in embedded Python console.
     - **Show banner** - this option allows to show/hide the Python banner on top of the console window.
 
-- **Show MRU items** - allows to define the maximum **Number** of items in **Most Recently Used** list and the **Link type**: 
+- **Show MRU items** - allows to define the maximum **Number** of items in **Most Recently Used** list and the **Link type**:
 
-  - **Long** - shows the full path to the file. 
+  - **Long** - shows the full path to the file.
   - **Short** - shows the file name only.
   - **Auto** - shows full paths to the files only if some files from different locations have the same name.
 
-- **Full-screen** 
+- **Full-screen**
 
   - **Hide object browser and viewers toolbars** - switches automatic hiding of Object Browser and OCC viewer toolbar in full-screen mode.
 
@@ -135,10 +135,10 @@ OCC 3D Viewer Preferences
   - **Enable V-Sync** - activates vertical synchronization.
   - **Enable quad-buffer support** - allows quad-buffered rendering.
 
-  .. note:: 
+  .. note::
        It is neccessary to enable quad-buffered stereoscopic rendering manually in graphic driver settings. SALOME does not do it automatically.
 
-  .. note:: 
+  .. note::
        All existing OCC 3D views should be re-created for quad-buffer support.
 
 - **Background** - specifies the default background for the viewers, separately for (for more details, refer to the :ref:`viewer_background` page"):
@@ -220,7 +220,7 @@ VTK 3D Viewer Preferences
     - Checkerboard
     - Split View Port Horizontal
 
-    .. note:: 
+    .. note::
                The stereo pair type selected in this combobox is applied for all existing VTK 3D views with stereo rendering already turned on within the same SALOME GUI session. It is not possible to use different stereo modes in several VTK 3D views at the same time within the same SALOME GUI session.
 
   - **Anaglyph filter** - specifies the format of anaglyph stereo pair:
@@ -231,9 +231,9 @@ VTK 3D Viewer Preferences
 
   - **Enable quad-buffer support** - allows quad-buffered rendering.
 
-  .. note:: 
+  .. note::
                It is neccessary to enable quad-buffered stereoscopic rendering manually in graphic driver settings. SALOME does not do it automatically.
-  .. note:: 
+  .. note::
                All existing VTK 3D views should be re-created for quad-buffer support.
 
 - **Selection**
@@ -241,7 +241,7 @@ VTK 3D Viewer Preferences
   - **Preselection** - allows to choose among three possible preselection modes:
 
     - **Standard** - this mode works quickly, by checking only bounding boxes of objects. It does not deal with the order of actors in the view or with their data (points/ cells).
-    - **Dynamic** - works directly with cells of actors, which provides the exact area of preselection. This mode is much more slower. 
+    - **Dynamic** - works directly with cells of actors, which provides the exact area of preselection. This mode is much more slower.
     - **Disabled** - switches off the preselection.
 
   - **Enable selection** - switches selection on/off.
@@ -250,13 +250,13 @@ VTK 3D Viewer Preferences
 
   - **Decrease Speed Increment** - decreases by 1 the speed increment used for the keyboard (same as [-] key).
   - **Increase Speed Increment** - increase by 1 the speed increment used for the keyboard (same as [+] key).
-  - **Dominant / combined switch** - toggles button to switch to dominant or combined movements. 
+  - **Dominant / combined switch** - toggles button to switch to dominant or combined movements.
 
 - **AVI Recording**
 
   - **Mode** - allows to choose from two recording regimes:
 
-    - **Recording all displayed frames** - records exactly at the FPS rate specified by the user. 
+    - **Recording all displayed frames** - records exactly at the FPS rate specified by the user.
     - **Recording at a given FPS** - records only when the contents of the viewer change (some activity is performed).  In the AVI file non-recorded images are substituted with the copies of the latest recorded image, which produces files with lower quality but requires less system resources.
 
   - **FPS** - allows to define the FPS (frames per second) rate for the clip. Set greater value for better quality.
@@ -278,15 +278,15 @@ Plot 2D Viewer Preferences
        :align: center
 
 - **Background color** - this submenu allows to select the background color. Click on the colored line to access to the :ref:`select_color_and_font_page` dialog box.
-- **Selection color** - this submenu allows to select the color of selected object in the viewer. 
+- **Selection color** - this submenu allows to select the color of selected object in the viewer.
 - **Viewer** - allows specifying the properties of the Plot 2D Viewer.
 
   - **Curve Type** - this allows to set the representation of graphs in your presentations. You can see only **Points**, points connected with **Lines** or points connected with smooth **Splines**.
   - **Marker Size** - this submenu allows you to set the size of markers in your graphs.
   - **Horizontal** and **Vertical axis scale** - this submenus allow you to set the scale for vertical and horizontal axes. It can be either **Linear** or **Logarithmic**. Note that the **Logarithmic** scale can be used only if the minimum value of corresponding component (abscissa or ordinate) of all points displayed in the viewer is greater than zero. If this condition is not met, the scale is switched to **Linear** automatically, even if it is set to **Logarithmic**.
-  - **Deviation marker color** - this submenu allows to select the color of the deviation marker. 
-  - **Deviation marker line width** allows to define line width of the deviation marker. 
-  - **Deviation marker tick size** allows to define size of the upper and lower horizontal lines of the deviation marker. 
+  - **Deviation marker color** - this submenu allows to select the color of the deviation marker.
+  - **Deviation marker line width** allows to define line width of the deviation marker.
+  - **Deviation marker tick size** allows to define size of the upper and lower horizontal lines of the deviation marker.
 
 - **Legend** - allows specifying the properties of the legend.
 
@@ -294,8 +294,8 @@ Plot 2D Viewer Preferences
   - **Legend Position** - this submenu allows to set the default position of the legend, it can be located to the left, to the right, on top or on bottom of the graph.
   - **Symbol type** you can select the type of legend item symbol from "Marker on line" or "Marker above line"
   - **Legend font** - this allows to set type and face for the font of Legend item.
-  - **Legend font color** - this allows to select the color of the font of the legend item. 
-  - **Highlighted legend font color** - this submenu allows to select the color of the font of the selected legend item. 
+  - **Legend font color** - this allows to select the color of the font of the legend item.
+  - **Highlighted legend font color** - this submenu allows to select the color of the font of the selected legend item.
 
 .. _default_python_preferences:
 
@@ -305,7 +305,7 @@ Python Viewer Preferences
 .. image:: ../images/pref_salome_pythonviewer.png
        :align: center
 
-.. note:: 
+.. note::
        The following settings are default and will be applied only for newly created Python viewers. Customization of already opened viewers can be done using local :ref:`custom_python_preferences` called by clicking on the corresponding icon of :ref:`python_viewer_page`.
 
 - **Font settings** allows setting font variant, size and style.
@@ -368,13 +368,13 @@ Object Browser Preferences
 
 - **Default columns** - these checkboxes allow to display or hide **Value**, **Entry**, **IOR** and **Reference entry** columns in the Object Browser.
 
-Shortcuts Preferences
+Shortcut Preferences
 #####################
 
 .. image:: ../images/pref_salome_shortcuts.png
        :align: center
 
-- **Shortcuts settings** widget allows to define custom shortcuts for various operations. To change keyboard sequence for a certain action - select the action and press the custom keys combination.
+- **Shortcut settings** widget allows to define custom shortcuts for various operations. To change keyboard sequence for a certain action - select the action and press the custom keys combination.
 
 
 :ref:`select_color_and_font_page`: "Font and color preferences"
@@ -383,7 +383,7 @@ are most oftenly used types of user settings.
 When you change settings (click **OK** or **Apply** button) each module
 receives the notification about what preferences are changed. You can
 also click **Defaults** button to restore default preferences or **Close**
-button to quit the dialog box without any changes. 
+button to quit the dialog box without any changes.
 
 **Import** button allows loading specific user file containing
 preferences from any location at the computer through a standard
@@ -400,7 +400,7 @@ separate preferences file for each SALOME version in use. Preferences
 are saved to the file in the end of the working session and restored
 at the application start-up.
 
-.. note:: 
+.. note::
        The preferences you set will be default preferences for all **new** objects, but they are not retroactive and do not automatically apply to the existing objects.
 
 
index 81d695f41f5681aefe46e2a8a533fed073a697c0..b5adda1b98813aac610a8fe7118e564b9bfc89ad 100644 (file)
@@ -1175,7 +1175,7 @@ msgstr ""
 # e081f044dacf408690aa128eca010f21
 #: ../../../../../../SRC/GUI_SRC/doc/salome/gui/input/setting_preferences.rst:357
 msgid ""
-"**Shortcuts settings** widget allows to define custom shortcuts for "
+"**Shortcut settings** widget allows to define custom shortcuts for "
 "various operations. To change keyboard sequence for a certain action - "
 "select the action and press the custom keys combination."
 msgstr ""
index 79b911de50d5aa554203ef961be7cbf560dbfdb3..a88c8e7217d3acd935e055551934d6179334ac4e 100644 (file)
@@ -34,6 +34,7 @@
 #include <SUIT_Desktop.h>
 #include <SUIT_Session.h>
 #include <SUIT_ResourceMgr.h>
+#include <SUIT_ShortcutMgr.h>
 
 #include <QMenu>
 
@@ -640,7 +641,7 @@ int CAM_Module::createMenu( const QString& subMenu, const int menu,
 {
   if ( !menuMgr() )
     return -1;
-  
+
   return menuMgr()->insert( subMenu, menu, group, id, idx, menuObj );
 }
 
@@ -704,7 +705,7 @@ int CAM_Module::createMenu( QAction* a, const int menu, const int id, const int
 {
   if ( !a || !menuMgr() )
     return -1;
-  
+
   ActionMgrLocker lock( menuMgr(), !myMenuShown );
 
   int regId = registerAction( id, a );
@@ -754,7 +755,7 @@ int CAM_Module::createMenu( QAction* a, const QString& menu, const int id, const
 
   if ( !myMenuShown )
     setMenuShown( a, false );
-  
+
   return intId != -1 ? regId : -1;
 }
 
@@ -932,6 +933,11 @@ void CAM_Module::setToolShown( const int id, const bool on )
   setToolShown( action( id ), on );
 }
 
+QString CAM_Module::makeActionID(const QString& theInModuleActionID) const
+{
+  return SUIT_ShortcutMgr::makeActionID(name(), theInModuleActionID);
+}
+
 /*!
   \brief Get action by specified \a id.
   \param id action ID
@@ -978,18 +984,19 @@ int CAM_Module::actionId( const QAction* a ) const
   \param icon action icon
   \param menu menu text
   \param tip status bar tip
-  \param key keyboard accelerator
+  \param key will be disabled by SUIT_ShortcutMgr!
   \param parent parent object
   \param toggle if \c true, the action will be toggled
   \param reciever action activation signal receiver object
   \param member action activation signal receiver slot
+  \param inModuleActionID module-unique action ID
 */
 QAction* CAM_Module::createAction( const int id, const QString& text, const QIcon& icon,
                                    const QString& menu, const QString& tip, const int key,
                                    QObject* parent, const bool toggle, QObject* reciever,
-                                  const char* member, const QString& shortcutAction )
+                                  const char* member, const QString& inModuleActionID )
 {
-  return createAction( id, text, icon, menu, tip, QKeySequence(key), parent, toggle, reciever, member, shortcutAction );
+  return createAction( id, text, icon, menu, tip, QKeySequence(key), parent, toggle, reciever, member, inModuleActionID );
 }
 
 /*!
@@ -1005,18 +1012,20 @@ QAction* CAM_Module::createAction( const int id, const QString& text, const QIco
   \param icon action icon
   \param menu menu text
   \param tip status bar tip
-  \param key keyboard accelerator
+  \param key will be disabled by SUIT_ShortcutMgr!
   \param parent parent object
   \param toggle if \c true, the action will be toggled
   \param reciever action activation signal receiver object
   \param member action activation signal receiver slot
+  \param inModuleActionID module-unique action ID
 */
 QAction* CAM_Module::createAction( const int id, const QString& text, const QIcon& icon,
                                    const QString& menu, const QString& tip, const QKeySequence& key,
                                    QObject* parent, const bool toggle, QObject* reciever,
-                                  const char* member, const QString& shortcutAction )
+                                  const char* member, const QString& inModuleActionID )
 {
-  QtxAction* a = new QtxAction( text, icon, menu, key, parent, toggle, shortcutAction );
+  const QString actionID = makeActionID(inModuleActionID);
+  QtxAction* a = new QtxAction( text, icon, menu, key, parent, toggle, actionID );
   a->setStatusTip( tip );
 
   connect( a, SIGNAL( triggered( bool ) ), this, SLOT( moduleActionActivated() ), Qt::UniqueConnection );
index 7956b982f1e3cbeebc81e9ef34d45847ae9f4d78..20df848319c366198d811d10f0459d7577fbab9e 100644 (file)
@@ -98,6 +98,8 @@ public:
   QtxActionMenuMgr*      menuMgr() const;
   QtxActionToolMgr*      toolMgr() const;
 
+  QString                makeActionID(const QString& theInModuleActionID) const;
+
   virtual QAction*       action( const int ) const;
   virtual int            actionId( const QAction* ) const;
   virtual QAction*       createAction( const int, const QString&, const QIcon&, const QString&,
@@ -125,7 +127,7 @@ public:
   virtual void           logAction( QAction* );
   bool                   isActionLoggingEnabled() const;
   void                   setActionLoggingEnabled( bool );
-  
+
   static QAction*        separator();
 
 public slots:
@@ -144,7 +146,7 @@ public slots:
 private slots:
   void                   onInfoChanged( QString );
 
-protected: 
+protected:
   virtual bool           isSelectionCompatible();
 
   virtual CAM_DataModel* createDataModel();
@@ -170,7 +172,7 @@ private:
   QMap<int, QAction*>    myActionMap;       //!< menu actions
   bool                   myMenuShown;       //!< menu shown flag
   bool                   myToolShown;       //!< tool shown flag
-  bool                   myActionLoggingEnabled; //!< action logging enabled 
+  bool                   myActionLoggingEnabled; //!< action logging enabled
 
   friend class CAM_Application;
 };
index bbf021081a344f0f9a80ad13b12c34d0192819f8..df9b04390d41ed396f65837b1d120fbd9f80d43a 100644 (file)
@@ -636,21 +636,14 @@ LightApp_SelectionMgr* LightApp_Application::selectionMgr() const
 /*!Creat action "New window" for certain type of viewer:*/
 void LightApp_Application::createActionForViewer( const int id,
                                                   const int parentId,
-                                                  const QString& suffix,
-                                                  const int accel )
+                                                  const QString& suffix )
 {
   QString vtlt = tr( QString( "NEW_WINDOW_%1" ).arg( suffix ).toLatin1().constData() );
   QString tip = tr( "CREATING_NEW_WINDOW" ).arg( vtlt.remove( "&" ) );
-  QAction* a = createAction( id,                      // menu action id
-                             tip,                     // status tip
-                             QIcon(),                 // icon
-                             vtlt,                    // menu text
-                             tip,                     // tooltip
-                             accel,                   // shortcut
-                             desktop(),               // parent
-                             false,                   // toggle flag
-                             this,                    // receiver
-                             SLOT( onNewWindow() ) ); // slot
+  QAction* a = createAction(id, desktop(), false /*toggle*/, "/PRP_CREATE_NEW_WINDOW_FOR_VIEWER_" + suffix,
+                            tip, vtlt, tip, QIcon(),
+                            this, SLOT(onNewWindow()));
+
   createMenu( a, parentId, -1 );
 }
 
@@ -666,7 +659,7 @@ void LightApp_Application::createActions()
   // Preferences
   createAction( PreferencesId, tr( "TOT_DESK_PREFERENCES" ), QIcon(),
                 tr( "MEN_DESK_PREFERENCES" ), tr( "PRP_DESK_PREFERENCES" ),
-                Qt::CTRL+Qt::Key_P, desk, false, this, SLOT( onPreferences() ) );
+                QKeySequence::UnknownKey, desk, false, this, SLOT( onPreferences() ), "/PRP_DESK_PREFERENCES" );
 
   // Help menu
 
@@ -794,6 +787,11 @@ void LightApp_Application::createActions()
 
   createAction( CloseId, tr( "TOT_CLOSE" ), QIcon(), tr( "MEN_DESK_CLOSE" ), tr( "PRP_CLOSE" ),
                 Qt::CTRL+Qt::Key_F4, desk, false, this, SLOT( onCloseWindow() ) );
+
+  createAction( CloseId, desk, false /*toggle*/, "/PRP_CLOSE",
+                tr( "TOT_CLOSE" ), tr( "MEN_DESK_CLOSE" ), tr( "PRP_CLOSE" ), QIcon(),
+                this, SLOT( onCloseWindow() ) );
+
   createAction( CloseAllId, tr( "TOT_CLOSE_ALL" ), QIcon(), tr( "MEN_DESK_CLOSE_ALL" ), tr( "PRP_CLOSE_ALL" ),
                 0, desk, false, this, SLOT( onCloseAllWindow() ) );
   createAction( GroupAllId, tr( "TOT_GROUP_ALL" ), QIcon(), tr( "MEN_DESK_GROUP_ALL" ), tr( "PRP_GROUP_ALL" ),
@@ -805,35 +803,37 @@ void LightApp_Application::createActions()
   createMenu( separator(), windowMenu, -1, 0 );
 
 #ifndef DISABLE_GLVIEWER
-  createActionForViewer( NewGLViewId, newWinMenu, QString::number( 0 ), Qt::ALT+Qt::Key_G );
+  createActionForViewer( NewGLViewId, newWinMenu, QString::number( 0 ) );
 #endif
 #ifndef DISABLE_PLOT2DVIEWER
-  createActionForViewer( NewPlot2dId, newWinMenu, QString::number( 1 ), Qt::ALT+Qt::Key_P );
+  createActionForViewer( NewPlot2dId, newWinMenu, QString::number( 1 ) );
 #endif
 #ifndef DISABLE_OCCVIEWER
-  createActionForViewer( NewOCCViewId, newWinMenu, QString::number( 2 ), Qt::ALT+Qt::Key_O );
+  createActionForViewer( NewOCCViewId, newWinMenu, QString::number( 2 ) );
 #endif
 #ifndef DISABLE_VTKVIEWER
-  createActionForViewer( NewVTKViewId, newWinMenu, QString::number( 3 ), Qt::ALT+Qt::Key_K );
+  createActionForViewer( NewVTKViewId, newWinMenu, QString::number( 3 ) );
 #endif
 #ifndef DISABLE_QXGRAPHVIEWER
-  createActionForViewer( NewQxSceneViewId, newWinMenu, QString::number( 4 ), Qt::ALT+Qt::Key_S );
+  createActionForViewer( NewQxSceneViewId, newWinMenu, QString::number( 4 ) );
 #endif
 #ifndef DISABLE_GRAPHICSVIEW
-  createActionForViewer( NewGraphicsViewId, newWinMenu, QString::number( 5 ), Qt::ALT+Qt::Key_C );
+  createActionForViewer( NewGraphicsViewId, newWinMenu, QString::number( 5 ) );
 #endif
 #ifndef DISABLE_PVVIEWER
-  createActionForViewer( NewPVViewId, newWinMenu, QString::number( 6 ), Qt::ALT+Qt::Key_A );
+  createActionForViewer( NewPVViewId, newWinMenu, QString::number( 6 ) );
 #endif
 #ifndef DISABLE_PYVIEWER
-  createActionForViewer( NewPyViewerId, newWinMenu, QString::number( 7 ), Qt::ALT+Qt::Key_Y );
+  createActionForViewer( NewPyViewerId, newWinMenu, QString::number( 7 ) );
 #endif
 #ifndef DISABLE_PV3DVIEWER
-  createActionForViewer( NewPV3DViewId, newWinMenu, QString::number( 8 ), Qt::ALT+Qt::Key_3 );
+  createActionForViewer( NewPV3DViewId, newWinMenu, QString::number( 8 ) );
 #endif
 
-  createAction( RenameId, tr( "TOT_RENAME" ), QIcon(), tr( "MEN_DESK_RENAME" ), tr( "PRP_RENAME" ),
-                Qt::ALT+Qt::SHIFT+Qt::Key_R, desk, false, this, SLOT( onRenameWindow() ) );
+  createAction( RenameId, desk, false /*toggle*/, "/PRP_RENAME",
+                tr( "TOT_RENAME" ), tr( "MEN_DESK_RENAME" ), tr( "PRP_RENAME" ), QIcon(),
+                this, SLOT( onRenameWindow() ) );
+
   createMenu( RenameId, windowMenu, -1 );
 
   int fileMenu = createMenu( tr( "MEN_DESK_FILE" ), -1 );
@@ -850,7 +850,7 @@ void LightApp_Application::createActions()
 #endif // USE_SALOME_STYLE
 
   createAction( FullScreenId, tr( "TOT_FULLSCREEN" ), QIcon(), tr( "MEN_DESK_FULLSCREEN" ), tr( "PRP_FULLSCREEN" ),
-                Qt::Key_F11, desk, false, this, SLOT( onFullScreen() ) );
+                QKeySequence::UnknownKey, desk, false, this, SLOT( onFullScreen() ), "/PRP_FULLSCREEN" );
 
 
   int viewMenu = createMenu( tr( "MEN_DESK_VIEW" ), -1 );
@@ -1227,7 +1227,7 @@ void LightApp_Application::onExtRemoving(const QString& title)
     MESSAGE("Removing of an extension was cancelled");
     return; // cancelled
   }
-  
+
   if (activeStudy() && activeStudy()->isModified() && !onSaveDoc())
   {
     // doc is not saved, or saving cancelled
@@ -2443,7 +2443,7 @@ void LightApp_Application::showPreferences( const QStringList& path )
     resourceMgr()->save();
 
     // Update shortcuts
-    shortcutMgr()->updateShortcuts();
+    shortcutMgr()->rebindActionsToKeySequences();
   }
 
   delete prefDlg;
@@ -3561,11 +3561,7 @@ void LightApp_Application::createPreferences( LightApp_Preferences* pref )
 
   // .. "Shortcuts" preferences tab <<start>>
   int shortcutTab = pref->addPreference( tr( "PREF_TAB_SHORTCUTS" ), salomeCat );
-  // ... "Shortcuts settings" group <<start>>
-  int shortcutGroup = pref->addPreference( tr( "PREF_GROUP_SHORTCUTS" ), shortcutTab );
-  pref->addPreference( tr( "" ), shortcutGroup,
-                       LightApp_Preferences::ShortcutTree, "shortcuts" );
-  // ... "Shortcuts settings" group <<end>>
+  pref->addPreference( tr( "" ), shortcutTab, LightApp_Preferences::ShortcutTree, "shortcuts" );
   // .. "Shortcuts" preferences tab <<end>>
   // . Top-level "SALOME" preferences group <<end>>
 
index b9c19317d3ed1e9d0df97867a0da2499867b0416..3a0b70ad311c9da565869fd5d32b0a662556a0a4 100644 (file)
@@ -224,8 +224,7 @@ protected:
   virtual void                        customize();
   virtual void                        createActionForViewer( const int id,
                                                              const int parentId,
-                                                             const QString& suffix,
-                                                             const int accel );
+                                                             const QString& suffix );
   virtual SUIT_Study*                 createNewStudy();
   virtual QWidget*                    createWindow( const int );
   virtual void                        defaultWindows( QMap<int, int>& ) const;
index 9b6bd755856bfad46180f0c9b41141fcbf020c26..fe3abbf30febaacc0e2e4e784f4ec0301ee30ef4 100644 (file)
@@ -159,7 +159,7 @@ void LightApp_Module::contextMenuPopup( const QString& client, QMenu* menu, QStr
 /*!Update object browser.
  * For updating model or whole object browser use update() method can be used.
 */
-void LightApp_Module::updateObjBrowser( bool theIsUpdateDataModel, 
+void LightApp_Module::updateObjBrowser( bool theIsUpdateDataModel,
                                         SUIT_DataObject* theDataObject )
 {
   if (!getApp()->objectBrowser())
@@ -242,7 +242,7 @@ bool LightApp_Module::activateModule( SUIT_Study* study )
   if ( action(myErase) )
     action(myErase)->setEnabled(true);
 
-  application()->shortcutMgr()->setSectionEnabled( moduleName() );
+  application()->shortcutMgr()->setActionsWithPrefixInIDEnabled( moduleName() );
 
   /*  BUG 0020498 : The Entry column is always shown at module activation
       The registration of column is moved into LightApp_Application
@@ -282,8 +282,8 @@ bool LightApp_Module::deactivateModule( SUIT_Study* study )
   if ( action(myErase) )
     action(myErase)->setEnabled(false);
 
-  application()->shortcutMgr()->setSectionEnabled( moduleName(), false );
-  
+  application()->shortcutMgr()->setActionsWithPrefixInIDEnabled( moduleName(), false );
+
   /*  BUG 0020498 : The Entry column is always shown at module activation
   QString EntryCol = QObject::tr( "ENTRY_COLUMN" );
   LightApp_DataModel* m = dynamic_cast<LightApp_DataModel*>( dataModel() );
@@ -302,9 +302,9 @@ bool LightApp_Module::deactivateModule( SUIT_Study* study )
 void LightApp_Module::studyClosed( SUIT_Study* theStudy )
 {
   CAM_Module::studyClosed( theStudy );
-  
+
   myIsFirstActivate = true;
-  
+
   LightApp_Application* app = dynamic_cast<LightApp_Application*>(application());
   if ( app ) {
     SUIT_DataBrowser* ob = app->objectBrowser();
@@ -452,12 +452,12 @@ QtxPopupMgr* LightApp_Module::popupMgr()
 
     QPixmap p;
     SUIT_Desktop* d = application()->desktop();
-    
-    QAction 
+
+    QAction
       *disp = createAction( -1, tr( "TOP_SHOW" ), p, tr( "MEN_SHOW" ), tr( "STB_SHOW" ),
-                            0, d, false, this, SLOT( onShowHide() ), QString("General:Show object(s)") ),
+                            0, d, false, this, SLOT( onShowHide() ), QString("#General/Object(s)/Show") ),
       *erase = createAction( -1, tr( "TOP_HIDE" ), p, tr( "MEN_HIDE" ), tr( "STB_HIDE" ),
-                             0, d, false, this, SLOT( onShowHide() ) , QString("General:Hide object(s)") ),
+                             0, d, false, this, SLOT( onShowHide() ) , QString("#General/Object(s)/Hide") ),
       *dispOnly = createAction( -1, tr( "TOP_DISPLAY_ONLY" ), p, tr( "MEN_DISPLAY_ONLY" ), tr( "STB_DISPLAY_ONLY" ),
                                 0, d, false, this, SLOT( onShowHide() ) ),
       *eraseAll = createAction( -1, tr( "TOP_ERASE_ALL" ), p, tr( "MEN_ERASE_ALL" ), tr( "STB_ERASE_ALL" ),
@@ -467,7 +467,7 @@ QtxPopupMgr* LightApp_Module::popupMgr()
     myDisplayOnly = actionId( dispOnly );
     myEraseAll    = actionId( eraseAll );
 
-    myPopupMgr->insert( disp, -1, 0 ); 
+    myPopupMgr->insert( disp, -1, 0 );
     myPopupMgr->insert( erase, -1, 0 );
     myPopupMgr->insert( dispOnly, -1, 0 );
     myPopupMgr->insert( eraseAll, -1, 0 );
@@ -616,7 +616,7 @@ void LightApp_Module::startOperation( const int id )
 *
 * Creates operation with given id. You should not call this method, it will be called
 * automatically from startOperation. You may redefine this method in concrete module to
-* create operations. 
+* create operations.
 */
 LightApp_Operation* LightApp_Module::createOperation( const int id ) const
 {
@@ -711,7 +711,7 @@ void LightApp_Module::onViewManagerRemoved( SUIT_ViewManager* )
 /*!
   \brief Returns instance of operation by its id; if there is no operation
   corresponding to this id, null pointer is returned
-  \param id - operation id 
+  \param id - operation id
   \return operation instance
 */
 LightApp_Operation* LightApp_Module::operation( const int id ) const
@@ -725,7 +725,7 @@ LightApp_Operation* LightApp_Module::operation( const int id ) const
 bool LightApp_Module::reusableOperation( const int /*id*/ )
 {
  return true;
-} 
+}
 
 /*!
   virtual method
@@ -816,7 +816,7 @@ void LightApp_Module::updateModuleVisibilityState()
   // update visibility state of objects
   LightApp_Application* app = dynamic_cast<LightApp_Application*>(SUIT_Session::session()->activeApplication());
   if ( !app ) return;
-  
+
   SUIT_DataBrowser* ob = app->objectBrowser();
   if ( !ob || !ob->model() ) return;
 
@@ -834,9 +834,9 @@ void LightApp_Module::updateModuleVisibilityState()
 
   SUIT_DataObject* rootObj = ob->root();
   if ( !rootObj ) return;
-  
+
   DataObjectList listObj = rootObj->children( true );
-  
+
   SUIT_ViewModel* vmod = 0;
   if ( SUIT_ViewManager* vman = app->activeViewManager() )
     vmod = vman->getViewModel();
@@ -861,15 +861,15 @@ void LightApp_Module::onObjectClicked( SUIT_DataObject* theObject, int theColumn
 
   LightApp_DataObject* lo = dynamic_cast<LightApp_DataObject*>( theObject );
   if ( !lo ) return;
-  
+
   // detect action index (from LightApp level)
   int id = -1;
-  
+
   if ( study->visibilityState( lo->entry() ) == Qtx::ShownState )
     id = myErase;
   else if ( study->visibilityState( lo->entry() ) == Qtx::HiddenState )
     id = myDisplay;
-  
+
   if ( id != -1 )
     startOperation( id );
 }
index c747e034c2994423917abeb4eaa75372b64708cb..44de2ffa4b6860d274ccfd6c2106ded133eccd8a 100644 (file)
@@ -150,7 +150,7 @@ void LightApp_PreferencesDlg::onDefault()
     if ( myPrefs && myPrefs->resourceMgr() )
     {
       QtxResourceMgr::WorkingMode prev = myPrefs->resourceMgr()->setWorkingMode( QtxResourceMgr::IgnoreUserValues );
-      myPrefs->retrieve();
+      myPrefs->retrieveDefault();
       myPrefs->resourceMgr()->setWorkingMode( prev );
     }
     emit defaultPressed();
index de4dc7130c69fb58b149c7626cbd424c23249f7d..391f0f0d01882d2952095268b90a2db071138dba 100644 (file)
     <parameter name="rt_reflection"       value="true"/>
     <parameter name="rt_antialiasing"     value="false"/>
     <parameter name="rt_shadow"           value="true"/>
-    <parameter name="rt_trans_shadow"     value="false"/>                     
+    <parameter name="rt_trans_shadow"     value="false"/>
     <parameter name="light_color"         value="255, 255, 255"/>
     <parameter name="light_dx"            value="0.0"/>
     <parameter name="light_dy"            value="0.0"/>
     <parameter name="animation" value="0"/>
     <parameter name="size" value="300"/>
   </section>
-  <!--Salome shortcut settings-->
-  <section name="shortcuts_settings">
-    <parameter name="general_sections" value="General;Viewers"/>
+  <section name="action_assets">
+    <parameter name="${GUI_ROOT_DIR}/share/salome/resources/gui/action_assets.json" value=""/>
   </section>
-  <section name="shortcuts:General">
-    <parameter name="Show object(s)" value="Ctrl+Alt+S"/>
-    <parameter name="Hide object(s)" value="Ctrl+Alt+H"/>
-  </section>
-  <section name="shortcuts:Viewers">
-    <parameter name="Front view" value="Ctrl+Alt+F"/>
-    <parameter name="Back view" value="Ctrl+Alt+B"/>
-    <parameter name="Top view" value="Ctrl+Alt+P"/>
-    <parameter name="Bottom view" value="Ctrl+Alt+O"/>
-    <parameter name="Left view" value="Ctrl+Alt+J"/>
-    <parameter name="Right view" value="Ctrl+Alt+G"/>
-    <parameter name="Reset view" value="Ctrl+Alt+E"/>
-  </section>
-  <section name="shortcuts:Geometry">
-    <parameter name="Increase number of isolines" value="Meta+I"/>
-    <parameter name="Decrease number of isolines" value="Meta+D"/>
-    <parameter name="Increase transparency" value="Meta+Y"/>
-    <parameter name="Decrease transparency" value="Meta+T"/>
+  <!--Salome shortcut settings
+       See SUIT_ShortcutMgr for details.
+  -->
+  <section name="shortcuts:">
+    <!--Actions of these shortcuts are available even if no module is active-->
+    <parameter name="PRP_DESK_FILE_LOAD_SCRIPT" value="Ctrl+T"/>
+    <parameter name="PRP_DESK_FILE_DUMP_STUDY" value="Ctrl+D"/>
+    <parameter name="PRP_DESK_CONNECT" value="Ctrl+L"/>
+    <parameter name="PRP_DESK_DISCONNECT" value="Ctrl+U"/>
+    <parameter name="PRP_DESK_CATALOG_GENERATOR" value="Alt+Shift+G"/>
+    <parameter name="PRP_DESK_PREFERENCES" value="Ctrl+P"/>
+    <parameter name="PRP_RENAME" value="Alt+Shift+R"/>
+    <parameter name="PRP_CLOSE" value="Ctrl+F4"/>
+    <parameter name="PRP_FULLSCREEN" value="F11"/>
+    <parameter name="PRP_DESK_WINDOW_HSPLIT" value="Alt+Shift+H"/>
+    <parameter name="PRP_DESK_WINDOW_VSPLIT" value="Alt+Shift+V"/>
+    <parameter name="PRP_DESK_VIEW_STATUSBAR" value="Alt+Shift+S"/>
+    <parameter name="PRP_DESK_HELP_ABOUT" value="Alt+Shift+A"/>
+    <parameter name="TOT_DESK_FILE_NEW" value="Ctrl+N"/>
+    <parameter name="TOT_DESK_FILE_OPEN" value="Ctrl+O"/>
+    <parameter name="TOT_DESK_FILE_CLOSE" value="Ctrl+W"/>
+    <parameter name="TOT_DESK_FILE_EXIT" value="Ctrl+Q"/>
+    <parameter name="TOT_DESK_FILE_SAVE" value="Ctrl+S"/>
+    <parameter name="TOT_DESK_FILE_SAVEAS" value="Ctrl+Shift+S"/>
+    <parameter name="#TOT_DESK_EDIT_COPY" value="Ctrl+C"/>
+    <parameter name="TOT_DESK_EDIT_PASTE" value="Ctrl+V"/>
+    <parameter name="PRP_CREATE_NEW_WINDOW_FOR_VIEWER_0" value="Alt+G"/>
+    <parameter name="PRP_CREATE_NEW_WINDOW_FOR_VIEWER_1" value="Alt+P"/>
+    <parameter name="PRP_CREATE_NEW_WINDOW_FOR_VIEWER_2" value="Alt+O"/>
+    <parameter name="PRP_CREATE_NEW_WINDOW_FOR_VIEWER_3" value="Alt+K"/>
+    <parameter name="PRP_CREATE_NEW_WINDOW_FOR_VIEWER_4" value="Alt+S"/>
+    <parameter name="PRP_CREATE_NEW_WINDOW_FOR_VIEWER_5" value="Alt+C"/>
+    <parameter name="PRP_CREATE_NEW_WINDOW_FOR_VIEWER_6" value="Alt+A"/>
+    <parameter name="PRP_CREATE_NEW_WINDOW_FOR_VIEWER_7" value="Alt+Y"/>
+    <parameter name="PRP_CREATE_NEW_WINDOW_FOR_VIEWER_8" value="Alt+3"/>
+    <parameter name="#General/Object(s)/Show" value="Ctrl+Alt+S"/>
+    <parameter name="#General/Object(s)/Hide" value="Ctrl+Alt+H"/>
+    <parameter name="#Viewers/View/Set X+" value="Ctrl+Alt+B"/>
+    <parameter name="#Viewers/View/Set X-" value="Ctrl+Alt+F"/>
+    <parameter name="#Viewers/View/Set Y+" value="Ctrl+Alt+J"/>
+    <parameter name="#Viewers/View/Set Y-" value="Ctrl+Alt+G"/>
+    <parameter name="#Viewers/View/Set Z+" value="Ctrl+Alt+O"/>
+    <parameter name="#Viewers/View/Set Z-" value="Ctrl+Alt+P"/>
+    <parameter name="#Viewers/View/Rotate anticlockwise" value=""/>
+    <parameter name="#Viewers/View/Rotate clockwise" value=""/>
+    <parameter name="#Viewers/View/Reset" value="Ctrl+Alt+E"/>
   </section>
   <section name="ExternalBrowser" >
     <!-- External HELP browser settings -->
index 490bc2252d18a17f2089dbd7aa603d75f297dbf8..38550acb5ea0b00246662eced648fb393eaf342f 100644 (file)
@@ -1093,7 +1093,7 @@ File does not exist</translation>
     </message>
     <message>
         <source>PREF_GROUP_SHORTCUTS</source>
-        <translation>Shortcuts settings</translation>
+        <translation>Shortcut settings</translation>
     </message>
     <message>
         <source>PREF_GROUP_FULL_SCREEN</source>
index 480c7cbe8d2700e253d5a16098a06f0e573d4c6b..3a5336beeaf12e47f767a5c3a12bbfb15fda09d4 100644 (file)
@@ -242,7 +242,7 @@ const char* imageCrossCursor[] = {
   "................................",
   "................................"};
 
+
   /*!
   \brief Constructor
   \param theDesktop main window of application
@@ -1285,42 +1285,42 @@ void OCCViewer_ViewWindow::createActions()
 
   // Projections
   aAction = new QtxAction(tr("MNU_FRONT_VIEW"), aResMgr->loadPixmap( "OCCViewer", tr( "ICON_OCCVIEWER_VIEW_FRONT" ) ),
-                           tr( "MNU_FRONT_VIEW" ), 0, this, false, "Viewers:Front view");
+                           tr( "MNU_FRONT_VIEW" ), 0, this, false, "/#Viewers/View/Set X-");
   aAction->setStatusTip(tr("DSC_FRONT_VIEW"));
   connect(aAction, SIGNAL(triggered()), this, SLOT(onFrontView()));
   this->addAction(aAction);
   toolMgr()->registerAction( aAction, FrontId );
 
   aAction = new QtxAction(tr("MNU_BACK_VIEW"), aResMgr->loadPixmap( "OCCViewer", tr( "ICON_OCCVIEWER_VIEW_BACK" ) ),
-                           tr( "MNU_BACK_VIEW" ), 0, this, false, "Viewers:Back view");
+                           tr( "MNU_BACK_VIEW" ), 0, this, false, "/#Viewers/View/Set X+");
   aAction->setStatusTip(tr("DSC_BACK_VIEW"));
   connect(aAction, SIGNAL(triggered()), this, SLOT(onBackView()));
   this->addAction(aAction);
   toolMgr()->registerAction( aAction, BackId );
 
   aAction = new QtxAction(tr("MNU_TOP_VIEW"), aResMgr->loadPixmap( "OCCViewer", tr( "ICON_OCCVIEWER_VIEW_TOP" ) ),
-                           tr( "MNU_TOP_VIEW" ), 0, this, false, "Viewers:Top view");
+                           tr( "MNU_TOP_VIEW" ), 0, this, false, "/#Viewers/View/Set Z-");
   aAction->setStatusTip(tr("DSC_TOP_VIEW"));
   connect(aAction, SIGNAL(triggered()), this, SLOT(onTopView()));
   this->addAction(aAction);
   toolMgr()->registerAction( aAction, TopId );
 
   aAction = new QtxAction(tr("MNU_BOTTOM_VIEW"), aResMgr->loadPixmap( "OCCViewer", tr( "ICON_OCCVIEWER_VIEW_BOTTOM" ) ),
-                           tr( "MNU_BOTTOM_VIEW" ), 0, this, false, "Viewers:Bottom view");
+                           tr( "MNU_BOTTOM_VIEW" ), 0, this, false, "/#Viewers/View/Set Z+");
   aAction->setStatusTip(tr("DSC_BOTTOM_VIEW"));
   connect(aAction, SIGNAL(triggered()), this, SLOT(onBottomView()));
   this->addAction(aAction);
   toolMgr()->registerAction( aAction, BottomId );
 
   aAction = new QtxAction(tr("MNU_LEFT_VIEW"), aResMgr->loadPixmap( "OCCViewer", tr( "ICON_OCCVIEWER_VIEW_LEFT" ) ),
-                           tr( "MNU_LEFT_VIEW" ), 0, this, false, "Viewers:Left view");
+                           tr( "MNU_LEFT_VIEW" ), 0, this, false, "/#Viewers/View/Set Y+");
   aAction->setStatusTip(tr("DSC_LEFT_VIEW"));
   connect(aAction, SIGNAL(triggered()), this, SLOT(onLeftView()));
   this->addAction(aAction);
   toolMgr()->registerAction( aAction, LeftId );
 
   aAction = new QtxAction(tr("MNU_RIGHT_VIEW"), aResMgr->loadPixmap( "OCCViewer", tr( "ICON_OCCVIEWER_VIEW_RIGHT" ) ),
-                           tr( "MNU_RIGHT_VIEW" ), 0, this, false, "Viewers:Right view");
+                           tr( "MNU_RIGHT_VIEW" ), 0, this, false, "/#Viewers/View/Set Y-");
   aAction->setStatusTip(tr("DSC_RIGHT_VIEW"));
   connect(aAction, SIGNAL(triggered()), this, SLOT(onRightView()));
   this->addAction(aAction);
@@ -1328,7 +1328,7 @@ void OCCViewer_ViewWindow::createActions()
 
   // rotate anticlockwise
   aAction = new QtxAction(tr("MNU_ANTICLOCKWISE_VIEW"), aResMgr->loadPixmap( "OCCViewer", tr( "ICON_OCCVIEWER_VIEW_ANTICLOCKWISE" ) ),
-                           tr( "MNU_ANTICLOCKWISE_VIEW" ), 0, this, false, "Viewers:Rotate anticlockwise");
+                           tr( "MNU_ANTICLOCKWISE_VIEW" ), 0, this, false, "/#Viewers/View/Rotate anticlockwise");
   aAction->setStatusTip(tr("DSC_ANTICLOCKWISE_VIEW"));
   connect(aAction, SIGNAL(triggered()), this, SLOT(onAntiClockWiseView()));
   this->addAction(aAction);
@@ -1336,7 +1336,7 @@ void OCCViewer_ViewWindow::createActions()
 
   // rotate clockwise
   aAction = new QtxAction(tr("MNU_CLOCKWISE_VIEW"), aResMgr->loadPixmap( "OCCViewer", tr( "ICON_OCCVIEWER_VIEW_CLOCKWISE" ) ),
-                           tr( "MNU_CLOCKWISE_VIEW" ), 0, this, false, "Viewers:Rotate clockwise");
+                           tr( "MNU_CLOCKWISE_VIEW" ), 0, this, false, "/#Viewers/View/Rotate clockwise");
   aAction->setStatusTip(tr("DSC_CLOCKWISE_VIEW"));
   connect(aAction, SIGNAL(triggered()), this, SLOT(onClockWiseView()));
   this->addAction(aAction);
@@ -1371,10 +1371,10 @@ void OCCViewer_ViewWindow::createActions()
   aProjectionGroup->addAction( toolMgr()->action( OrthographicId ) );
   aProjectionGroup->addAction( toolMgr()->action( PerspectiveId ) );
   connect(aProjectionGroup, SIGNAL(triggered(QAction*)), this, SLOT(onProjectionType(QAction*)));
-  
+
   // Reset
   aAction = new QtxAction(tr("MNU_RESET_VIEW"), aResMgr->loadPixmap( "OCCViewer", tr( "ICON_OCCVIEWER_VIEW_RESET" ) ),
-                           tr( "MNU_RESET_VIEW" ), 0, this, false, "Viewers:Reset view");
+                           tr( "MNU_RESET_VIEW" ), 0, this, false, "/#Viewers/View/Reset");
   aAction->setStatusTip(tr("DSC_RESET_VIEW"));
   connect(aAction, SIGNAL(triggered()), this, SLOT(onResetView()));
   this->addAction(aAction);
@@ -1665,7 +1665,7 @@ void OCCViewer_ViewWindow::onFrontView()
   Handle(V3d_View) aView3d = myViewPort->getView();
   if (myAutomaticZoom)
   {
-    if ( !aView3d.IsNull() ) 
+    if ( !aView3d.IsNull() )
       aView3d->SetProj (V3d_Xpos);
     onViewFitAll();
   }
@@ -1683,7 +1683,7 @@ void OCCViewer_ViewWindow::onBackView()
   Handle(V3d_View) aView3d = myViewPort->getView();
   if (myAutomaticZoom)
   {
-    if ( !aView3d.IsNull() ) 
+    if ( !aView3d.IsNull() )
       aView3d->SetProj (V3d_Xneg);
     onViewFitAll();
   }
@@ -1701,7 +1701,7 @@ void OCCViewer_ViewWindow::onTopView()
   Handle(V3d_View) aView3d = myViewPort->getView();
   if (myAutomaticZoom)
   {
-    if ( !aView3d.IsNull() ) 
+    if ( !aView3d.IsNull() )
       aView3d->SetProj (V3d_Zpos);
     onViewFitAll();
   }
@@ -1719,7 +1719,7 @@ void OCCViewer_ViewWindow::onBottomView()
   Handle(V3d_View) aView3d = myViewPort->getView();
   if (myAutomaticZoom)
   {
-    if ( !aView3d.IsNull() ) 
+    if ( !aView3d.IsNull() )
       aView3d->SetProj (V3d_Zneg);
     onViewFitAll();
   }
@@ -1737,7 +1737,7 @@ void OCCViewer_ViewWindow::onLeftView()
   Handle(V3d_View) aView3d = myViewPort->getView();
   if (myAutomaticZoom)
   {
-    if ( !aView3d.IsNull() ) 
+    if ( !aView3d.IsNull() )
       aView3d->SetProj (V3d_Yneg);
     onViewFitAll();
   }
@@ -1755,7 +1755,7 @@ void OCCViewer_ViewWindow::onRightView()
   Handle(V3d_View) aView3d = myViewPort->getView();
   if (myAutomaticZoom)
   {
-    if ( !aView3d.IsNull() ) 
+    if ( !aView3d.IsNull() )
       aView3d->SetProj (V3d_Ypos);
     onViewFitAll();
   }
@@ -2420,7 +2420,7 @@ QImage OCCViewer_ViewWindow::dumpView()
 
   Image_PixMap aPix;
   view->ToPixMap(aPix, aWidth, aHeight, Graphic3d_BT_RGB);
-  
+
   QImage anImage( aWidth, aHeight, QImage::Format_ARGB32 );
   for ( int i = 0; i < aWidth; i++ ) {
     for ( int j = 0; j < aHeight; j++ ) {
@@ -2429,7 +2429,7 @@ QImage OCCViewer_ViewWindow::dumpView()
       anImage.setPixelColor( i, j, color );
     }
   }
-    
+
   if ( aPix.IsTopDown() )
     anImage = anImage.mirrored();
   return anImage;
@@ -3660,7 +3660,7 @@ void OCCViewer_ViewWindow::synchronize( SUIT_ViewWindow* theView )
   aProps.getPosition( aPosition[0], aPosition[1], aPosition[2] );
   aProps.getViewUp( anUpDir[0], anUpDir[1], anUpDir[2] );
   aProps.getAxialScale( anAxialScale[0], anAxialScale[1], anAxialScale[2] );
-  
+
   try {
     aDestView->SetAt( aFocalPoint[0], aFocalPoint[1], aFocalPoint[2] );
     aDestView->SetEye( aPosition[0], aPosition[1], aPosition[2] );
@@ -3670,7 +3670,7 @@ void OCCViewer_ViewWindow::synchronize( SUIT_ViewWindow* theView )
     getViewPort()->setAxialScale( anAxialScale[0], anAxialScale[1], anAxialScale[2] );
     aDestView->SetImmediateUpdate( Standard_True );
     aDestView->Redraw();
-  } 
+  }
   catch (Standard_Failure&) {
   }
 
index 571509786781c1c4f40f47f9c89bb65c4cada7aa..f95706ef16d400bab4cb09507e04b52fe32668ab 100644 (file)
@@ -22,7 +22,10 @@ INCLUDE(UseQtExt)
 # --- options ---
 
 # additional include directories
-INCLUDE_DIRECTORIES(${QT_INCLUDES})
+INCLUDE_DIRECTORIES(
+  ${QT_INCLUDES}
+  ${PROJECT_SOURCE_DIR}/src/SUIT
+)
 
 # additional preprocessor / compiler flags
 ADD_DEFINITIONS(${QT_DEFINITIONS})
index e5017a8eba99cca8188126016b6b583910079a32..dc31318e33c66fa97422185a9bb351ac9f08d5b2 100644 (file)
@@ -32,6 +32,8 @@
   <file>images/open.png</file>
   <file>images/close.png</file>
   <file>images/appicon.png</file>
+  <file>images/shortcut_disable.svg</file>
+  <file>images/shortcut_restore.svg</file>
 </qresource>
 </RCC>
+
index 83dd376f729bccd88d151a9da3c7b38b20c93967..004207143b04512d35138059936dd0d81f2ade9b 100644 (file)
@@ -54,32 +54,30 @@ private:
   \class QtxAction
   \brief Generic action class.
 
-  The class QtxAction inherits QWidgetAction class and can be used 
+  The class QtxAction inherits QWidgetAction class and can be used
   as base class when implementing any custom menu/toolbar actions.
-  It is necessary to subclass from QtxAction and redefine virtual 
+  It is necessary to subclass from QtxAction and redefine virtual
   callback methods addedTo(), removedFrom() (which are called automatically
   when the action is added to the widget and removed from it) to customize
-  the action behavior.  
+  the action behavior.
 */
 
 /*!
   \brief Constructor.
 
-  Creates an action owned by \a parent. 
+  Creates an action owned by \a parent.
   Parameter \a toggle can be used to make the action checkable.
-  Parameter \a shortcutAction can be used to assign the shortcut from
-  preferences. This parameter value corresponds to shortcut action identifier
-  in shortcut preferences.
+  Parameter \a ID is used to de(serialize) shortcut settings.
 
   \param parent parent object
   \param toggle if \c true the action will be a toggle action
-  \param shortcutAction shortcut action identifier
+  \param ID shortcut action identifier
 */
-QtxAction::QtxAction( QObject* parent, bool toggle, const QString& shortcutAction )
+QtxAction::QtxAction( QObject* parent, bool toggle, const QString& ID )
 : QWidgetAction( parent )
 {
   setCheckable( toggle );
-  setShortcutActionName(shortcutAction);
+  setID(ID);
 
   QApplication::instance()->installEventFilter( this );
 }
@@ -88,30 +86,55 @@ QtxAction::QtxAction( QObject* parent, bool toggle, const QString& shortcutActio
   \brief Constructor.
 
   Creates an action owned by \a parent. Parameters \a text,
-  \a icon, \a menuText and \a accel specify the action's attributes.
+  \a menuText and \a ID specify the action's attributes.
   Parameter \a toggle can be used to make the action checkable.
-  Parameter \a shortcutAction can be used to assign the shortcut from
-  preferences. This parameter value corresponds to shortcut action identifier
-  in shortcut preferences.
+  Parameter \a ID is used to de(serialize) shortcut settings.
+
+  \param parent parent object
+  \param toggle if \c true the action is a toggle action
+  \param ID shortcut action identifier
+  \param toolTip tooltip text
+  \param text menu text
+*/
+QtxAction::QtxAction( QObject* parent, bool toggle, const QString& ID,
+                      const QString& toolTip, const QString& text, const QIcon& icon )
+: QWidgetAction( parent )
+{
+  setCheckable( toggle );
+  setID(ID);
+  setToolTip( toolTip );
+  setText( text );
+  setIcon( icon );
+
+  QApplication::instance()->installEventFilter( this );
+}
+
+/*!
+  \brief Constructor.
+
+  Creates an action owned by \a parent. Parameters \a text,
+  \a icon, \a menuText and \a ID specify the action's attributes.
+  Parameter \a toggle can be used to make the action checkable.
+  Parameter \a ID is used to de(serialize) shortcut settings.
 
   \param text tooltip text
   \param icon iconset
   \param menuText menu text
-  \param accel shortcut key sequence
+  \param accel will be disabled by SUIT_ShortcutMgr!
   \param parent parent object
   \param toggle if \c true the action will be a toggle action
-  \param shortcutAction shortcut action identifier
+  \param ID shortcut action identifier
 */
-QtxAction::QtxAction( const QString& text, const QIcon& icon, const QString& menuText, 
-                     int accel, QObject* parent, bool toggle, const QString& shortcutAction )
+QtxAction::QtxAction( const QString& text, const QIcon& icon, const QString& menuText,
+                     int accel, QObject* parent, bool toggle, const QString& ID )
 : QWidgetAction( parent )
 {
   setIcon( icon );
   setText( menuText );
   setToolTip( text );
-  setShortcut( accel );
   setCheckable( toggle );
-  setShortcutActionName(shortcutAction);
+  setID(ID);
+  setShortcut(accel);
 
   QApplication::instance()->installEventFilter( this );
 }
@@ -120,30 +143,28 @@ QtxAction::QtxAction( const QString& text, const QIcon& icon, const QString& men
   \brief Constructor.
 
   Creates an action owned by \a parent. Parameters \a text,
-  \a icon, \a menuText and \a accel specify the action's attributes.
+  \a icon, \a menuText and \a ID specify the action's attributes.
   Parameter \a toggle can be used to make the action checkable.
-  Parameter \a shortcutAction can be used to assign the shortcut from
-  preferences. This parameter value corresponds to shortcut action identifier
-  in shortcut preferences.
+  Parameter \a ID is used to de(serialize) shortcut settings.
 
   \param text tooltip text
   \param icon iconset
   \param menuText menu text
-  \param accel shortcut key sequence
+  \param accel will be disabled by SUIT_ShortcutMgr!
   \param parent parent object
   \param toggle if \c true the action will be a toggle action
-  \param shortcutAction shortcut action identifier
+  \param ID shortcut action identifier
 */
-QtxAction::QtxAction( const QString& text, const QIcon& icon, const QString& menuText, 
-                     const QKeySequence& accel, QObject* parent, bool toggle, const QString& shortcutAction )
+QtxAction::QtxAction( const QString& text, const QIcon& icon, const QString& menuText,
+                     const QKeySequence& accel, QObject* parent, bool toggle, const QString& ID )
 : QWidgetAction( parent )
 {
   setIcon( icon );
   setText( menuText );
   setToolTip( text );
-  setShortcut( accel );
   setCheckable( toggle );
-  setShortcutActionName(shortcutAction);
+  setID(ID);
+  setShortcut(accel);
 
   QApplication::instance()->installEventFilter( this );
 }
@@ -152,29 +173,27 @@ QtxAction::QtxAction( const QString& text, const QIcon& icon, const QString& men
   \brief Constructor.
 
   Creates an action owned by \a parent. Parameters \a text,
-  \a menuText and \a accel specify the action's attributes.
+  \a menuText and \a ID specify the action's attributes.
   Parameter \a toggle can be used to make the action checkable.
-  Parameter \a shortcutAction can be used to assign the shortcut from
-  preferences. This parameter value corresponds to shortcut action identifier
-  in shortcut preferences.
+  Parameter \a ID is used to de(serialize) shortcut settings.
 
   \param text tooltip text
   \param menuText menu text
-  \param accel shortcut key sequence
+  \param accel will be disabled by SUIT_ShortcutMgr!
   \param parent parent object
   \param toggle if \c true the action is a toggle action
-  \param shortcutAction shortcut action identifier
+  \param ID shortcut action identifier
 */
 QtxAction::QtxAction( const QString& text, const QString& menuText,
-                      int accel, QObject* parent, bool toggle, const QString& shortcutAction )
+                      int accel, QObject* parent, bool toggle, const QString& ID )
 : QWidgetAction( parent )
 {
   setText( menuText );
   setToolTip( text );
-  setShortcut( accel );
   setCheckable( toggle );
-  setShortcutActionName(shortcutAction);
-  
+  setID(ID);
+  setShortcut(accel);
+
   QApplication::instance()->installEventFilter( this );
 }
 
@@ -182,29 +201,27 @@ QtxAction::QtxAction( const QString& text, const QString& menuText,
   \brief Constructor.
 
   Creates an action owned by \a parent. Parameters \a text,
-  \a menuText and \a accel specify the action's attributes.
+  \a menuText and \a ID specify the action's attributes.
   Parameter \a toggle can be used to make the action checkable.
-  Parameter \a shortcutAction can be used to assign the shortcut from
-  preferences. This parameter value corresponds to shortcut action identifier
-  in shortcut preferences.
+  Parameter \a ID is used to de(serialize) shortcut settings.
 
   \param text tooltip text
   \param menuText menu text
-  \param accel shortcut key sequence
+  \param accel will be disabled by SUIT_ShortcutMgr!
   \param parent parent object
   \param toggle if \c true the action is a toggle action
-  \param shortcutAction shortcut action identifier
+  \param ID shortcut action identifier
 */
 QtxAction::QtxAction( const QString& text, const QString& menuText,
-                      const QKeySequence& accel, QObject* parent, bool toggle, const QString& shortcutAction )
+                      const QKeySequence& accel, QObject* parent, bool toggle, const QString& ID )
 : QWidgetAction( parent )
 {
   setText( menuText );
   setToolTip( text );
-  setShortcut( accel );
   setCheckable( toggle );
-  setShortcutActionName(shortcutAction);
-  
+  setID(ID);
+  setShortcut(accel);
+
   QApplication::instance()->installEventFilter( this );
 }
 
@@ -217,8 +234,8 @@ QtxAction::~QtxAction()
 
 /*!
   \brief Customize action events.
-  
-  Sends a notification event to the action when it is added to 
+
+  Sends a notification event to the action when it is added to
   the widget or removed from it in order to perform custom processing.
 
   \param o object
@@ -238,11 +255,29 @@ bool QtxAction::eventFilter( QObject* o, QEvent* e )
   return QWidgetAction::eventFilter( o, e );
 }
 
+QString QtxAction::shortcutActionName() const
+{
+  return myID;
+}
+
+void QtxAction::setShortcutActionName( const QString& shortcutAction )
+{
+  myID = shortcutAction;
+}
+
+const QString& QtxAction::ID() const {
+  return myID;
+}
+
+void QtxAction::setID( const QString& theID) {
+  myID = theID;
+}
+
 /*!
   \brief Called when the action is added to the widget.
 
-  This method can be redefined in the subclasses to customize 
-  the action behavior. Base implementation does nothing. 
+  This method can be redefined in the subclasses to customize
+  the action behavior. Base implementation does nothing.
 
   \param w widget (should be menu or toolbar)
   \sa removedFrom()
@@ -266,7 +301,7 @@ void QtxAction::removedFrom( QWidget* /*w*/ )
 
 /*!
   \brief Process notification events.
-  
+
   Calls addedTo() method when the action is added to the widget
   and removedFrom() when it is removed from the widget
   in order to perform custom processing.
@@ -285,27 +320,3 @@ void QtxAction::customEvent( QEvent* e )
   else
     removedFrom( ae->widget() );
 }
-
-/*!
-  \brief Return shortcut action name for the action.
-  
-  \return shortcut action name
-  \sa setShortcutActionName()
-*/
-QString QtxAction::shortcutActionName() const
-{
-  return myShortcutActionName;
-}
-
-/*!
-  \brief Set shortcut action name to the action.
-
-  Shortcut action name is used for shortcuts customization.
-
-  \param shortcutAction shortcut action name
-  \sa shortcutActionName()
-*/
-void QtxAction::setShortcutActionName( const QString& shortcutAction )
-{
-  myShortcutActionName = shortcutAction;
-}
index cb36f3f64fd5087b21a8304d33eb3b590cabee0c..a286a6ab63837c9da6a41457150aa015251c0914 100644 (file)
@@ -29,8 +29,7 @@
 #include "Qtx.h"
 
 #include <QWidgetAction>
-
-class QIcon;
+#include <QIcon>
 
 #ifdef WIN32
 #pragma warning ( disable:4251 )
@@ -44,17 +43,25 @@ class QTX_EXPORT QtxAction : public QWidgetAction
 
 public:
   QtxAction( QObject* = 0, bool = false, const QString& = QString() );
-  QtxAction( const QString&, const QString&, int, QObject*, bool = false, const QString& = QString() );
+  QtxAction( QObject*,     bool,         const QString&, const QString&, const QString& = QString(), const QIcon& = QIcon());
+  QtxAction( const QString&, const QString&, int                , QObject*, bool = false, const QString& = QString() );
   QtxAction( const QString&, const QString&, const QKeySequence&, QObject*, bool = false, const QString& = QString() );
-  QtxAction( const QString&, const QIcon&, const QString&, int, QObject*, bool = false, const QString& = QString() );
+  QtxAction( const QString&, const QIcon&, const QString&, int                , QObject*, bool = false, const QString& = QString() );
   QtxAction( const QString&, const QIcon&, const QString&, const QKeySequence&, QObject*, bool = false, const QString& = QString() );
   virtual ~QtxAction();
 
   virtual bool eventFilter( QObject*, QEvent* );
 
+  [[deprecated("Use ID() instead.")]]
   QString shortcutActionName() const;
+
+  [[deprecated("Use setID() instead.")]]
   void setShortcutActionName( const QString& );
 
+  /** Unique for the entire application ID. */
+  const QString& ID() const;
+  void setID( const QString& theID);
+
 protected:
   virtual void addedTo( QWidget* );
   virtual void removedFrom( QWidget* );
@@ -62,7 +69,7 @@ protected:
   virtual void customEvent( QEvent* );
 
 private:
-  QString myShortcutActionName;
+  QString myID;
 };
 
 #ifdef WIN32
index d0d861ed0060b74e1b4c7ab772bbd8f3f2b3436c..b81bcfb19d7b9e654fa316390ffc01092352c936 100644 (file)
@@ -34,6 +34,8 @@
 #include "QtxBackgroundTool.h"
 #include "QtxResourceMgr.h"
 
+#include "SUIT_ShortcutMgr.h"
+
 #include <QEvent>
 #include <QLayout>
 #include <QToolBox>
@@ -2216,7 +2218,7 @@ void QtxPagePrefEditItem::setDecimals( const int dec )
 {
   if ( myDecimals == dec )
     return;
-  
+
   myDecimals = dec;
   updateEditor();
 }
@@ -2237,10 +2239,10 @@ int QtxPagePrefEditItem::echoMode() const
   \sa echoMode()
 */
 void QtxPagePrefEditItem::setEchoMode( const int echo )
-{   
+{
   if ( myEchoMode == echo )
     return;
-  
+
   myEchoMode = echo;
   updateEditor();
 }
@@ -2329,7 +2331,7 @@ void QtxPagePrefEditItem::updateEditor()
     default:
       myEditor->setEchoMode(QLineEdit::Normal);
   }
-  
+
   QValidator* val = 0;
   switch ( inputType() )
   {
@@ -2370,14 +2372,14 @@ QtxPagePrefSliderItem::QtxPagePrefSliderItem( const QString& title, QtxPreferenc
 {
   setControl( mySlider = new QSlider( Qt::Horizontal ) );
   widget()->layout()->addWidget( myLabel = new QLabel( ) );
-    
+
   setMinimum( 0 );
   setMaximum( 0 );
   setSingleStep( 1 );
   setPageStep( 1 );
-  
+
   mySlider->setTickPosition( QSlider::TicksBothSides );
-  
+
   connect (mySlider, SIGNAL(valueChanged(int)), this, SLOT(setIcon(int)));
   updateSlider();
 }
@@ -2488,17 +2490,17 @@ void QtxPagePrefSliderItem::setMaximum( const int& max )
 */
 void QtxPagePrefSliderItem::setIcons( const QList<QIcon>& icns )
 {
-  if ( icns.isEmpty() ) 
+  if ( icns.isEmpty() )
     return;
   myIcons = icns;
-  
-  QSize maxsize(0, 0); 
+
+  QSize maxsize(0, 0);
   for ( QList<QIcon>::const_iterator it = myIcons.begin(); it != myIcons.end(); ++it ) {
     if ( (*it).isNull() ) continue;
     maxsize = maxsize.expandedTo( (*it).availableSizes().first() );
   }
   myLabel->setFixedSize( maxsize );
-  
+
   updateSlider();
 }
 
@@ -2597,13 +2599,13 @@ void QtxPagePrefSliderItem::updateSlider()
 
   control()->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed );
   mySlider->setFocusPolicy(Qt::StrongFocus);
-  
+
   mySlider->setValue( val );
   setSingleStep( stp );
   setPageStep( ptp );
   setMinimum( min );
   setMaximum( max );
-  
+
   myLabel->setVisible( !myIcons.empty() );
   widget()->layout()->setSpacing( !myIcons.empty() ? 6 : 0 );
 }
@@ -2781,7 +2783,7 @@ void QtxPagePrefSelectItem::setNumbers( const QList<QVariant>& ids )
   for ( QList<QVariant>::const_iterator it = ids.begin(); it != ids.end(); ++it, i++ ) {
     if ( i >= mySelector->count() )
       mySelector->addItem(QString("") );
-    
+
     mySelector->setId( i, *it );
   }
 }
@@ -2831,7 +2833,7 @@ void QtxPagePrefSelectItem::store()
 void QtxPagePrefSelectItem::retrieve()
 {
   QString txt = getString();
-  
+
   // try to find via the id
   int idx = mySelector->index( txt );
   if ( idx < 0 )
@@ -3539,7 +3541,7 @@ void QtxPagePrefColorItem::retrieve()
   \brief GUI implementation of the resources item to store a bi-color value.
 
   The main color is specified explicitly. The secondary color is calculated
-  by changing "value" and "saturation" parameters of the main color in the 
+  by changing "value" and "saturation" parameters of the main color in the
   HSV notation using specified delta.
 */
 
@@ -3700,10 +3702,10 @@ void QtxPagePrefFontItem::setFeatures( const int f )
 }
 
 /*!
-  \brief Specifies whether widget works in Native or Custom mode. Native mode 
-  is intended for working with system fonts. Custom mode is intended for 
-  working with manually defined set of fonts. Set of custom fonts can be 
-  specified with setFonts() method 
+  \brief Specifies whether widget works in Native or Custom mode. Native mode
+  is intended for working with system fonts. Custom mode is intended for
+  working with manually defined set of fonts. Set of custom fonts can be
+  specified with setFonts() method
   \param mode mode from QtxFontEdit::Mode enumeration
   \sa mode()
 */
@@ -3723,7 +3725,7 @@ int QtxPagePrefFontItem::mode() const
 }
 
 /*!
-  \brief Sets list of custom fonts. 
+  \brief Sets list of custom fonts.
   <b>This method is intended for working in Custom mode only.</b>
   \param fams list of families
   \sa fonts(), setMode()
@@ -3734,7 +3736,7 @@ void QtxPagePrefFontItem::setFonts( const QStringList& fams )
 }
 
 /*!
-  \brief Gets list of custom fonts 
+  \brief Gets list of custom fonts
   \return list of families
   \sa setFonts(), setMode()
 */
@@ -3744,8 +3746,8 @@ QStringList QtxPagePrefFontItem::fonts() const
 }
 
 /*!
-  \brief Sets list of available font sizes. 
-  <b>This method is intended for working in Custom mode only.</b> The list of sizes can 
+  \brief Sets list of available font sizes.
+  <b>This method is intended for working in Custom mode only.</b> The list of sizes can
   be empty. In this case system generate listof size automatically from 8 till 72.
   \param sizes list of sizes
   \sa sizes(), setMode()
@@ -3756,7 +3758,7 @@ void QtxPagePrefFontItem::setSizes( const QList<int>& sizes )
 }
 
 /*!
-  \brief Gets list of custom fonts 
+  \brief Gets list of custom fonts
   \return list of families
   \sa setFonts(), setMode()
 */
@@ -4454,119 +4456,68 @@ void QtxPagePrefDateTimeItem::updateDateTime()
   myDateTime->setDisplayFormat( dispFmt );
 }
 
-/*!
-  \brief Constructor.
-  \param title preference item title
-  \param parent parent preference item
-  \param sect resource file section associated with the preference item
-  \param param resource file parameter associated with the preference item
-*/
-QtxPagePrefShortcutBtnsItem::QtxPagePrefShortcutBtnsItem( const QString& title, QtxPreferenceItem* parent, const QString& sect,
-                                                          const QString& param ): QtxPageNamedPrefItem( title, parent, sect, param )
-{
-  setControl( myShortcut = new QtxShortcutEdit() );
-}
-
-/*!
-  \brief Destructor.
-*/  
-QtxPagePrefShortcutBtnsItem::~QtxPagePrefShortcutBtnsItem()
-{
-}
 
 /*!
-  \brief Store preference item to the resource manager.
-  \sa retrieve()
+  \brief Creates preference item for editing of key bindings
+  \param theParent parent preference item. Must not be nullptr.
 */
-void QtxPagePrefShortcutBtnsItem::store()
+QtxPagePrefShortcutTreeItem::QtxPagePrefShortcutTreeItem(QtxPreferenceItem* theParent)
+ : QtxPagePrefItem(QString(), theParent)
 {
-  setString( myShortcut->shortcut().toString() );
-}
+  auto container = std::shared_ptr<SUIT_ShortcutContainer>();
+  const auto itContainers = QtxPagePrefShortcutTreeItem::shortcutContainers.find(rootItem());
+  if (itContainers == QtxPagePrefShortcutTreeItem::shortcutContainers.end()) {
+    container.reset(new SUIT_ShortcutContainer());
+    QtxPagePrefShortcutTreeItem::shortcutContainers.emplace(rootItem(), container);
+  }
+  else {
+    container = itContainers->second.lock();
+    if (!container) {
+      container.reset(new SUIT_ShortcutContainer());
+      itContainers->second = container;
+    }
+  }
 
-/*!
-  \brief Retrieve preference item from the resource manager.
-  \sa store()
-*/
-void QtxPagePrefShortcutBtnsItem::retrieve()
-{
-  myShortcut->setShortcut( QKeySequence::fromString( getString() ) );
+  QtxShortcutTree* tree = new QtxShortcutTree(container);
+  tree->myModuleIDs = SUIT_ShortcutMgr::get()->getShortcutModuleIDs();
+  setWidget(tree);
 }
 
 /*!
-  \brief Constructor.
-
-  Creates preference item for editing of key bindings
-
-  \param title preference item title
-  \param parent parent preference item
-  \param sect resource file section associated with the preference item
-  \param param resource file parameter associated with the preference item
+  \brief Retrieves shortcut preferences from ShortcutMgr.
+  Updates UI of controlling widget.
+  \sa store()
 */
-QtxPagePrefShortcutTreeItem::QtxPagePrefShortcutTreeItem( const QString& title, QtxPreferenceItem* parent, const QString& sect, 
-                                                          const QString& /*param*/ ): QtxPageNamedPrefItem( title, parent, sect, "" )
+void QtxPagePrefShortcutTreeItem::retrieve()
 {
-  mySection = sect;
-
-  myShortcutTree = new QtxShortcutTree();
-
-  // Retrieve shortcuts common sections from resources
-  QtxResourceMgr* resMgr = resourceMgr();
-  if ( resMgr ){
-    QString generalSections = resourceMgr()->stringValue( "shortcuts_settings", "general_sections", QString() );
-    QStringList sectionsList = generalSections.split( ";", QString::SkipEmptyParts );
-    myShortcutTree->setGeneralSections( sectionsList );
-  }
-  setControl( myShortcutTree );
+  static_cast<QtxShortcutTree*>(widget())->setShortcutsFromManager();
 }
 
 /*!
-  \brief Destructor.
-*/
-QtxPagePrefShortcutTreeItem::~QtxPagePrefShortcutTreeItem()
-{
-}
-                                                   
-/*!
-  \brief Retrieve preference item from the resource manager.
+  \brief Retrieves shortcut preferences from resource files, ignoring user preferences.
+  Updates UI of controlling widget.
   \sa store()
 */
-void QtxPagePrefShortcutTreeItem::retrieve()
+void QtxPagePrefShortcutTreeItem::retrieveDefault()
 {
-  QtxResourceMgr* resMgr = resourceMgr();
-  if ( resMgr ){
-    QStringList secLst = resMgr->subSections( mySection, false );
-    ShortcutMap aMap; QStringList paramLst;
-    for( int i = 0; i < secLst.size(); i++ ) {
-      paramLst = resMgr->parameters( QStringList() << mySection << secLst.at( i ) );
-      for( int j = 0; j < paramLst.size(); j++ )
-        resMgr->value( mySection + resMgr->sectionsToken() + secLst.at( i ), paramLst.at( j ),aMap[ paramLst.at( j ) ], false );
-      myShortcutTree->setBindings( secLst.at( i ), aMap );
-      aMap.clear();
-    }
-  }
+  static_cast<QtxShortcutTree*>(widget())->setDefaultShortcuts();
 }
-             
+
 /*!
-  \brief Store preference item to the resource manager.
+  \brief Applies modified shortcut preferences to ShortcutMgr.
+  Updates UI of controlling widget.
+  And ShortcutMgr, in turn, serilizes shortcut preferences using the resource manager.
   \sa retrieve()
 */
 void QtxPagePrefShortcutTreeItem::store()
 {
-  QStringList lst = myShortcutTree->sections();
-  QString aSection;
-  QtxResourceMgr* resMgr = resourceMgr();
-  
-  if ( resMgr ) {
-    for( int i = 0; i < lst.size(); i++ ) {
-      ShortcutMap* aMap( myShortcutTree->bindings( lst.at( i ) ) );
-      aSection = mySection + resMgr->sectionsToken() + lst.at( i );
-      for( ShortcutMap::const_iterator it = aMap->constBegin(); it != aMap->constEnd(); ++it )
-       resMgr->setValue( aSection, it.key(), it.value() );
-    }
-  }
+  static_cast<QtxShortcutTree*>(widget())->applyChangesToShortcutMgr();
 }
 
+/*static*/ std::map<QtxPreferenceItem*, std::weak_ptr<SUIT_ShortcutContainer>> QtxPagePrefShortcutTreeItem::shortcutContainers =
+std::map<QtxPreferenceItem*, std::weak_ptr<SUIT_ShortcutContainer>>();
+
+
 /*!
   \class QtxPagePrefBackgroundItem
   \brief GUI implementation of the resources item to store background data.
@@ -4576,7 +4527,7 @@ void QtxPagePrefShortcutTreeItem::store()
   - texture image file
   - simple two-color gradient
   - complex custom gradient (NOT IMPLEMENTED YET)
-  
+
   Allowed background modes can be specified using setModeAllowed() method.
   Texture modes can be enabled/disabled using setTextureModeAllowed() method.
   Also, showing texture controls can be enabled/disabled by means of
@@ -4863,9 +4814,9 @@ void QtxPagePrefBackgroundItem::setOptionValue( const QString& name, const QVari
 /*!
   \brief Constructor.
 
-  Creates preference item of user defined widget. The item widget has to be inherited from 
+  Creates preference item of user defined widget. The item widget has to be inherited from
   QtxUserDefinedContent class. An instance of this class has to be installed into the item
-  with help of setContent method. Methods optionValue and setOptionValue use pointer on the 
+  with help of setContent method. Methods optionValue and setOptionValue use pointer on the
   content widget an qint64 value.
 
   \param parent parent preference item
@@ -4874,7 +4825,7 @@ QtxUserDefinedItem::QtxUserDefinedItem( QtxPreferenceItem* parent )
   : QtxPageNamedPrefItem(QString(), parent), myContent(0)
 {
 }
-  
+
 /*!
   \brief Lets to Store preferences for content instance
   \sa retrieve()
@@ -4912,7 +4863,7 @@ QVariant QtxUserDefinedItem::optionValue( const QString& theName ) const
 
 /*!
  * \brief Sets option value
- * \param theName is a string "content" 
+ * \param theName is a string "content"
  * \param theVal is a pointer on QtxUserDefinedContent class instance represented as qint64 value
  */
 void QtxUserDefinedItem::setOptionValue( const QString& theName, const QVariant& theVal)
@@ -4924,14 +4875,14 @@ void QtxUserDefinedItem::setOptionValue( const QString& theName, const QVariant&
   } else
     QtxPreferenceItem::setOptionValue( theName, theVal );
 }
-  
+
 /*!
- * \brief Defines content of the property item as a Widget which has to be inherited from 
+ * \brief Defines content of the property item as a Widget which has to be inherited from
  * QtxUserDefinedContent class.
  * \param theContent is an QtxUserDefinedContent class instance.
  */
-void QtxUserDefinedItem::setContent( QtxUserDefinedContent* theContent ) 
-{ 
-  myContent = theContent; 
+void QtxUserDefinedItem::setContent( QtxUserDefinedContent* theContent )
+{
+  myContent = theContent;
   setControl(myContent);
 }
index 5341734811f9890d3eb71aa8787df3153b097bb8..b2b854b429c3bd3b896ed26f60c7617951339f56 100644 (file)
 #include <QPointer>
 #include <QIcon>
 
+#include <map>
+#include <memory>
+
 class QtxGridBox;
 class QtxFontEdit;
 class QtxGroupBox;
 class QtxComboBox;
 class QtxColorButton;
 class QtxBiColorTool;
-class QtxShortcutEdit;
 class QtxShortcutTree;
 class QtxBackgroundTool;
 
@@ -409,7 +411,7 @@ public:
 
   int              decimals() const;
   void             setDecimals( const int );
-  
+
   int              echoMode() const;
   void             setEchoMode( const int );
 
@@ -443,7 +445,7 @@ public:
   int              pageStep() const;
   int              minimum() const;
   int              maximum() const;
-  QList<QIcon>     icons() const; 
+  QList<QIcon>     icons() const;
 
   void             setSingleStep( const int& );
   void             setPageStep( const int& );
@@ -741,31 +743,35 @@ private:
   QDateTimeEdit*   myDateTime;
 };
 
-class QTX_EXPORT QtxPagePrefShortcutBtnsItem : public QtxPageNamedPrefItem
-{
-public:
-  QtxPagePrefShortcutBtnsItem( const QString&, QtxPreferenceItem* = 0,
-                               const QString& = QString(), const QString& = QString() );
-  virtual ~QtxPagePrefShortcutBtnsItem();
-  virtual void     store();
-  virtual void     retrieve();
 
-private:
-  QtxShortcutEdit* myShortcut;
-};
+class SUIT_ShortcutContainer;
+
 
-class QTX_EXPORT QtxPagePrefShortcutTreeItem : public QtxPageNamedPrefItem
+class QTX_EXPORT QtxPagePrefShortcutTreeItem : public QtxPagePrefItem
 {
 public:
-  QtxPagePrefShortcutTreeItem( const QString&, QtxPreferenceItem* = 0, 
-                               const QString& = QString(), const QString& = QString() );
-  virtual ~QtxPagePrefShortcutTreeItem();
-  virtual void     store();
+  QtxPagePrefShortcutTreeItem(QtxPreferenceItem* theParent);
+  virtual ~QtxPagePrefShortcutTreeItem() = default;
+
   virtual void     retrieve();
-                                                                  
+  virtual void     retrieveDefault();
+  virtual void     store();
+
 private:
   QtxShortcutTree* myShortcutTree;
-  QString          mySection;
+
+  // { root item (preference window), shortcut container of synchronized trees (widgets within the same window) }
+  static std::map<QtxPreferenceItem*, std::weak_ptr<SUIT_ShortcutContainer>> shortcutContainers;
+  /** Why is this?
+   * Every QtxPagePrefMgr is eventually a preference window. Each preference window has button "Apply".
+   * When the button is pressed, all descendants of the QtxPagePrefMgr store changes they carry into preferences.
+   * The pitfall with shortcut trees is as follows: made in independent shortcut trees, changes may conflict,
+   * and merge of such changes is ambiguous. And the solution is to keep shortcut trees within the same window
+   * synchronized - all changes being made in a tree of a synchronized bundle are projected to other trees from the bundle
+   * without interacting with SUIT_ShortcutMgr.
+   *
+   * Every time shortcut preferences stored to the ShortcutMgr, all instances of QtxShortcutTree are updated.
+  */
 };
 
 class QTX_EXPORT QtxPagePrefBackgroundItem : public QObject, public QtxPageNamedPrefItem
index 84fd552a6655420153bc908e4c71ae95ec7b771a..3090568c7605aa7e8400a517c2784305c6b032a7 100644 (file)
@@ -122,7 +122,7 @@ void QtxPreferenceItem::Updater::customEvent( QEvent* /*e*/ )
   \class QtxPreferenceItem
   \brief Base class for implementing of all the preference items.
 
-  To implement any specific preference item, cubclass from the
+  To implement any specific preference item, subclass from the
   QtxPreferenceItem and redefine store() and retrieve() methods.
 */
 
@@ -258,7 +258,7 @@ void QtxPreferenceItem::appendItem( QtxPreferenceItem* item )
 
 /*!
   \brief Insert child preference item before specified item.
-  If the before item is 0 then new item is appended.
+  If the before item is 0, then new item is appended.
 
   Removes (if necessary) the item from the previous parent.
 
@@ -511,6 +511,14 @@ void QtxPreferenceItem::setRestartRequired( const bool on )
   \sa store()
 */
 
+/*! \brief Restore preference item (for example, from the resource file, ignoring user preferences).
+  If not overridden, it is equivalent to \ref retrieve().
+*/
+void QtxPreferenceItem::retrieveDefault()
+{
+  retrieve();
+}
+
 /*!
   \brief Get the value of the associated resource file setting.
   \return associated resource file setting value
@@ -996,6 +1004,17 @@ void QtxPreferenceMgr::retrieve()
     (*it)->retrieve();
 }
 
+/*!
+  \brief Retrieve all preference items from the resource manager, ignoring user preferences.
+  \sa store()
+*/
+void QtxPreferenceMgr::retrieveDefault()
+{
+  QList<QtxPreferenceItem*> items = childItems( true );
+  for ( QList<QtxPreferenceItem*>::iterator it = items.begin(); it != items.end(); ++it )
+    (*it)->retrieveDefault();
+}
+
 /*!
   \brief Dumps all values to the backup container.
   \sa fromBackup()
index db4c6f95cd9847e169b6ad9de5bd86b349493ac5..94512dc2e3e020e7e534fc1f72d0fb15ce3cc8a9 100644 (file)
@@ -83,6 +83,7 @@ public:
 
   virtual void              store() = 0;
   virtual void              retrieve() = 0;
+  virtual void              retrieveDefault();
 
   QString                   resourceValue() const;
   void                      setResourceValue( const QString& );
@@ -160,6 +161,7 @@ public:
 
   virtual void              store();
   virtual void              retrieve();
+  virtual void              retrieveDefault();
 
   virtual void              update();
 
index 6cf00f287c24e6df5e6b0bf2b8a5045087bf73f8..fe845b574fc887a1406cbf6dabd6bb0c9cd57cc3 100644 (file)
 #include <QWidget>
 #include <QLayout>
 #include <QList>
+#include <QMap>
 
 #include <QToolButton>
 #include <QLineEdit>
+#include <QLabel>
 #include <QTableWidgetItem>
+#include <QTextEdit>
 #include <QMessageBox>
+#include <QPushButton>
+#include <QBrush>
+#include <QColor>
+#include <QHeaderView>
 
 #include <QKeyEvent>
 #include <QKeySequence>
+#include <QCollator>
+
+#include <algorithm>
+
 
 #define COLUMN_SIZE  500
 
-static const char* delete_icon[] = {
-"16 16 3 1",
-"` c #810000",
-"  c none",
-"# c #ffffff",
-"                ",
-"                ",
-" ``#        ``# ",
-" ````#     ``#  ",
-"  ````#   ``#   ",
-"    ```# `#     ",
-"     `````#     ",
-"      ```#      ",
-"     `````#     ",
-"    ```# ``#    ",
-"   ```#   ``#   ",
-"  ```#     `#   ",
-"  ```#      `#  ",
-"   `#        `# ",
-"                ",
-"                "
-};
 
-/*!
-  \brief Constructor
-  \param parent parent widget
-*/
-QtxShortcutEdit::QtxShortcutEdit( QWidget* parent )
-: QFrame( parent )
+QtxKeySequenceEdit::QtxKeySequenceEdit(QWidget* parent)
+: QFrame(parent)
 {
   initialize();
-  myShortcut->installEventFilter(this);
+  myKeySequenceLineEdit->installEventFilter(this);
 }
 
-/*!
-  \brief Destructor
-*/
-QtxShortcutEdit::~QtxShortcutEdit()
+/*! \brief Set a key sequence to edit. */
+void QtxKeySequenceEdit::setConfirmedKeySequence(const QKeySequence& theKeySequence)
 {
+  myConfirmedKeySequenceString = theKeySequence.toString();
+  myKeySequenceLineEdit->setText(myConfirmedKeySequenceString);
+  myPrevKeySequenceString = myConfirmedKeySequenceString;
 }
 
-/*!
-  \brief Sets custom shortcut
-  \param seq a key sequence describes a combination of keys
-  \sa shortcut()
-*/
-void QtxShortcutEdit::setShortcut( const QKeySequence& seq )
+void QtxKeySequenceEdit::setEditedKeySequence(const QKeySequence& theKeySequence)
 {
-  QString txt = seq.toString(); 
-  myPrevShortcutText = txt;
-  myShortcut->setText( txt ); 
+  const QString keySequenceString = theKeySequence.toString();
+  myKeySequenceLineEdit->setText(keySequenceString);
+  myPrevKeySequenceString = keySequenceString;
 }
 
-/*!
-  \brief Gets custom shortcut 
-  \return a key sequence describes a combination of keys
-  \sa setShortcut()
-*/
-QKeySequence QtxShortcutEdit::shortcut()
+QKeySequence QtxKeySequenceEdit::editedKeySequence() const
+{
+  return QKeySequence::fromString(myKeySequenceLineEdit->text());
+}
+
+/*! \returns true, if the edited key sequence differs from confirmed one. */
+bool QtxKeySequenceEdit::isKeySequenceModified() const
+{
+  return QKeySequence(myConfirmedKeySequenceString) != editedKeySequence();
+}
+
+/*! \brief Set confirmed key sequence to line editor. */
+void QtxKeySequenceEdit::restoreKeySequence()
 {
-  return QKeySequence::fromString( myShortcut->text() );
+  myKeySequenceLineEdit->setText(myConfirmedKeySequenceString);
+  myPrevKeySequenceString = myConfirmedKeySequenceString;
 }
 
 /*!
-  \brief Gets the key sequence from keys that were pressed 
+  \brief Gets the key sequence from keys that were pressed
   \param e a key event
-  \return a string representation of the key sequence
+  \returns a string representation of the key sequence
 */
-QString QtxShortcutEdit::parseEvent( QKeyEvent* e )
+/*static*/ QString QtxKeySequenceEdit::parseEvent(QKeyEvent* e)
 {
   bool isShiftPressed = e->modifiers() & Qt::ShiftModifier;
   bool isControlPressed = e->modifiers() & Qt::ControlModifier;
   bool isAltPressed = e->modifiers() & Qt::AltModifier;
   bool isMetaPressed = e->modifiers() & Qt::MetaModifier;
-  bool isModifiersPressed = isShiftPressed || isControlPressed || isAltPressed || isMetaPressed;
+  bool isModifiersPressed = isControlPressed || isAltPressed || isMetaPressed; // Do not treat Shift alone as a modifier!
   int result=0;
-  if( isControlPressed )
+  if(isControlPressed)
     result += Qt::CTRL;
-  if( isAltPressed )
+  if(isAltPressed)
     result += Qt::ALT;
-  if( isShiftPressed )
+  if(isShiftPressed)
     result += Qt::SHIFT;
-  if( isMetaPressed )
+  if(isMetaPressed)
     result += Qt::META;
 
   int aKey = e->key();
-  if ( ( isValidKey( aKey ) && isModifiersPressed ) || ( ( aKey >= Qt::Key_F1 ) && ( aKey <= Qt::Key_F12 ) ) )
+  if ((isValidKey(aKey) && isModifiersPressed) || ((aKey >= Qt::Key_F1) && (aKey <= Qt::Key_F12)))
     result += aKey;
 
-  return QKeySequence( result ).toString();
+  return QKeySequence(result).toString();
 }
 
 /*!
   \brief Check if the key event contains a 'valid' key
-  \param aKey the code of the key
-  \return \c true if the key is 'valid'
+  \param theKey the code of the key
+  \returns \c true if the key is 'valid'
 */
-
-bool QtxShortcutEdit::isValidKey( int aKey )
+/*static*/ bool QtxKeySequenceEdit::isValidKey(int theKey)
 {
-  if ( aKey == Qt::Key_Underscore || aKey == Qt::Key_Escape || 
-     ( aKey >= Qt::Key_Backspace && aKey <= Qt::Key_Delete ) || 
-     ( aKey >= Qt::Key_Home && aKey <= Qt::Key_PageDown ) || 
-     ( aKey >= Qt::Key_F1 && aKey <= Qt::Key_F12 )  ||
-     ( aKey >= Qt::Key_Space && aKey <= Qt::Key_Asterisk ) ||
-     ( aKey >= Qt::Key_Comma && aKey <= Qt::Key_Question ) ||
-     ( aKey >= Qt::Key_A && aKey <= Qt::Key_AsciiTilde ) )
+  if ( theKey == Qt::Key_Underscore || theKey == Qt::Key_Escape ||
+     ( theKey >= Qt::Key_Backspace && theKey <= Qt::Key_Delete ) ||
+     ( theKey >= Qt::Key_Home && theKey <= Qt::Key_PageDown ) ||
+     ( theKey >= Qt::Key_F1 && theKey <= Qt::Key_F12 )  ||
+     ( theKey >= Qt::Key_Space && theKey <= Qt::Key_Asterisk ) ||
+     ( theKey >= Qt::Key_Comma && theKey <= Qt::Key_Question ) ||
+     ( theKey >= Qt::Key_A && theKey <= Qt::Key_AsciiTilde ) )
     return true;
   return false;
 }
 
-/*!
-  \brief Called when "Clear" button is clicked.
-*/
-void QtxShortcutEdit::onCliked() 
+/*! \brief Called when "Clear" button is clicked. */
+void QtxKeySequenceEdit::onClear()
 {
-  myShortcut->setText( "" );
+  myKeySequenceLineEdit->setText("");
+  myPrevKeySequenceString = "";
+  emit editingFinished();
 }
 
-/*!
-  \brief Called when myShortcut loses focus.
-*/
-void QtxShortcutEdit::onEditingFinished() 
+/*! \brief Called when myKeySequenceLineEdit loses focus. */
+void QtxKeySequenceEdit::onEditingFinished()
 {
-  if ( myShortcut->text().endsWith("+") )
-    myShortcut->setText( myPrevShortcutText );
+  if (myKeySequenceLineEdit->text().endsWith("+"))
+    myKeySequenceLineEdit->setText(myPrevKeySequenceString);
+  else
+    myPrevKeySequenceString = myKeySequenceLineEdit->text();
+    emit editingFinished();
 }
 
 /*!
   \brief Custom event filter.
   \param obj event receiver object
   \param event event
-  \return \c true if further event processing should be stopped
+  \returns \c true if further event processing should be stopped
 */
-bool QtxShortcutEdit::eventFilter(QObject* obj, QEvent* event) 
-{ 
-  if ( obj == myShortcut ) { 
-    if (event->type() == QEvent::KeyPress ) {
-      QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
-      QString text = parseEvent( keyEvent );
-      if ( keyEvent->key() == Qt::Key_Delete || keyEvent->key() == Qt::Key_Backspace )
-        onCliked();
-      if ( text != "" )
-        myShortcut->setText( text );
+bool QtxKeySequenceEdit::eventFilter(QObject* theObject, QEvent* theEvent)
+{
+  if (theObject == myKeySequenceLineEdit) {
+    if (theEvent->type() == QEvent::KeyPress) {
+      QKeyEvent* keyEvent = static_cast<QKeyEvent*>(theEvent);
+      QString text = parseEvent(keyEvent);
+      if (keyEvent->key() == Qt::Key_Delete || keyEvent->key() == Qt::Key_Backspace)
+        myKeySequenceLineEdit->setText("");
+      if (!text.isEmpty())
+        myKeySequenceLineEdit->setText(text);
+
+      emit editingStarted();
       return true;
     }
-    if ( event->type() == QEvent::KeyRelease ) {
-      if ( myShortcut->text().endsWith("+") )
-        myShortcut->setText( myPrevShortcutText );
-      else myPrevShortcutText = myShortcut->text();
-
+    if (theEvent->type() == QEvent::KeyRelease) {
+      onEditingFinished();
       return true;
     }
-  } 
+  }
   return false;
 }
 
 /*
   \brief Perform internal intialization.
 */
-void QtxShortcutEdit::initialize()
+void QtxKeySequenceEdit::initialize()
 {
-  myPrevShortcutText = QString();
+  static const int PIXMAP_SIZE = 30;
 
   QHBoxLayout* base = new QHBoxLayout( this );
-  base->setMargin( 0 );
-  base->setSpacing( 5 );
+  base->setMargin(0);
+  base->setSpacing(5);
+
+  base->addWidget(myKeySequenceLineEdit = new QLineEdit(this));
+  setFocusProxy(myKeySequenceLineEdit);
+
+  QToolButton* clearBtn = new QToolButton();
+  auto clearPixmap = QPixmap(":/images/shortcut_disable.svg");
+  clearPixmap.scaled(QSize(PIXMAP_SIZE, PIXMAP_SIZE), Qt::KeepAspectRatio, Qt::SmoothTransformation);
+  clearBtn->setIcon(clearPixmap);
+  clearBtn->setToolTip(tr("Disable shortcut."));
+  base->addWidget(clearBtn);
+
+  QToolButton* restoreBtn = new QToolButton();
+  auto restorePixmap = QPixmap(":/images/shortcut_restore.svg");
+  restorePixmap.scaled(QSize(PIXMAP_SIZE, PIXMAP_SIZE), Qt::KeepAspectRatio, Qt::SmoothTransformation);
+  restoreBtn->setIcon(restorePixmap);
+  restoreBtn->setToolTip(tr("Restore the currently applied key sequence."));
+  base->addWidget(restoreBtn);
+
+  myKeySequenceLineEdit->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
+  clearBtn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+  restoreBtn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+
+  connect(clearBtn, SIGNAL(clicked()), this, SLOT(onClear()));
+  connect(restoreBtn, SIGNAL(clicked()), this, SIGNAL(restoreFromShortcutMgrClicked()));
+  connect(myKeySequenceLineEdit, SIGNAL(editingFinished()), this, SLOT(onEditingFinished()));
+}
 
-  base->addWidget( myShortcut = new QLineEdit( this ) );
 
-  QToolButton* deleteBtn = new QToolButton();
-  deleteBtn->setIcon( QPixmap( delete_icon ) );
-  base->addWidget( deleteBtn );
-  myShortcut->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
-  deleteBtn->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
+/*! \param theParent must not be nullptr. */
+QtxEditKeySequenceDialog::QtxEditKeySequenceDialog(QtxShortcutTree* theParent)
+: QDialog(theParent)
+{
+  setMinimumWidth(500);
+  setWindowTitle(tr("Change key sequence"));
+  QVBoxLayout* layout = new QVBoxLayout(this);
+  myActionName = new QLabel(this);
+  myActionName->setTextFormat(Qt::RichText);
+  myKeySequenceEdit = new QtxKeySequenceEdit(this);
+  myTextEdit = new QTextEdit(this);
+  layout->addWidget(myActionName);
+  layout->addWidget(myKeySequenceEdit);
+  layout->addWidget(myTextEdit);
+  myActionName->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
+  myKeySequenceEdit->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
+  myTextEdit->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
+  myTextEdit->setReadOnly(true);
+  myTextEdit->setAcceptRichText(true);
+  myTextEdit->setPlaceholderText(tr("No conflicts."));
+  setFocusProxy(myKeySequenceEdit);
+
+  QHBoxLayout* buttonLayout = new QHBoxLayout(this);
+  layout->addLayout(buttonLayout);
+  QPushButton* confirmButton = new QPushButton(tr("Confirm"), this);
+  confirmButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+  QPushButton* cancelButton = new QPushButton(tr("Cancel"), this);
+  cancelButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+  buttonLayout->addStretch();
+  buttonLayout->addWidget(confirmButton);
+  buttonLayout->addWidget(cancelButton);
+
+  connect(myKeySequenceEdit, SIGNAL(editingStarted()), this, SLOT(onEditingStarted()));
+  connect(myKeySequenceEdit, SIGNAL(editingFinished()), this, SLOT(onEditingFinished()));
+  connect(myKeySequenceEdit, SIGNAL(restoreFromShortcutMgrClicked()), this, SLOT(onRestoreFromShortcutMgr()));
+  connect(confirmButton, SIGNAL(clicked()), this, SLOT(onConfirm()));
+  connect(cancelButton, SIGNAL(clicked()), this, SLOT(reject()));
+}
 
-  connect( deleteBtn, SIGNAL( clicked() ), this, SLOT( onCliked() ) );
-  connect( myShortcut, SIGNAL( editingFinished() ), this, SLOT( onEditingFinished() ) );
+void QtxEditKeySequenceDialog::setModuleAndActionID(const QString& theModuleID, const QString& theInModuleActionID)
+{
+  myModuleID = theModuleID;
+  myInModuleActionID = theInModuleActionID;
 }
 
-/*!
-  \brief Constructor
-  \param parent parent widget
-*/
-QtxShortcutTree::QtxShortcutTree( QWidget * parent ) : QTreeWidget( parent )
+const QString& QtxEditKeySequenceDialog::moduleID() const { return myModuleID; }
+const QString& QtxEditKeySequenceDialog::inModuleActionID() const { return myInModuleActionID; }
+
+void QtxEditKeySequenceDialog::setModuleAndActionName(const QString& theModuleName, const QString& theActionName, const QString& theActionToolTip)
+{
+  myActionName->setText("<b>" + theModuleName + "</b>&nbsp;&nbsp;" + theActionName);
+  myActionName->setToolTip(theActionToolTip);
+}
+
+void QtxEditKeySequenceDialog::setConfirmedKeySequence(const QKeySequence& theSequence)
+{
+  myKeySequenceEdit->setConfirmedKeySequence(theSequence);
+}
+
+QKeySequence QtxEditKeySequenceDialog::editedKeySequence() const
+{
+  return myKeySequenceEdit->editedKeySequence();
+}
+
+int QtxEditKeySequenceDialog::exec()
+{
+  myKeySequenceEdit->setFocus(Qt::ActiveWindowFocusReason);
+  return QDialog::exec();
+}
+
+void QtxEditKeySequenceDialog::onEditingStarted()
+{
+  myTextEdit->setEnabled(false);
+}
+
+void QtxEditKeySequenceDialog::onEditingFinished()
 {
-  setColumnCount( 2 );
-  setSelectionMode( QAbstractItemView::SingleSelection );
-  setColumnWidth ( 0, COLUMN_SIZE);
+  updateConflictsMessage();
+}
+
+void QtxEditKeySequenceDialog::onRestoreFromShortcutMgr()
+{
+  const auto shortcutMgr = SUIT_ShortcutMgr::get();
+  myKeySequenceEdit->setEditedKeySequence(shortcutMgr->getKeySequence(myModuleID, myInModuleActionID));
+  updateConflictsMessage();
+}
+
+/*! Updates message with list of actions, whose shortcuts will be disabled on Confirm. */
+void QtxEditKeySequenceDialog::updateConflictsMessage()
+{
+  myTextEdit->setEnabled(true);
+  QTextDocument* doc = myTextEdit->document();
+  if (!doc) {
+    doc = new QTextDocument(myTextEdit);
+    myTextEdit->setDocument(doc);
+  }
+
+  if (!myKeySequenceEdit->isKeySequenceModified()) {
+    doc->clear();
+    return;
+  }
+
+  const QKeySequence newKeySequence = editedKeySequence();
+
+  const auto shortcutTree = static_cast<QtxShortcutTree*>(parentWidget());
+  /** {moduleID, inModuleActionID}[] */
+  std::set<std::pair<QString, QString>> conflicts = shortcutTree->shortcutContainer()->getConflicts(myModuleID, myInModuleActionID, newKeySequence);
+  if (!conflicts.empty()) {
+    const auto shortcutMgr = SUIT_ShortcutMgr::get();
+
+    QString report = "<b>" + tr("These shortcuts will be disabled on confirm:") + "</b>";
+    {
+      report += "<ul>";
+      for (const auto& conflict : conflicts) {
+        const QString conflictingModuleName = shortcutMgr->getModuleName(conflict.first);
+        const QString conflictingActionName = shortcutMgr->getActionName(conflict.first, conflict.second);
+        report += "<li><b>" + conflictingModuleName + "</b>&nbsp;&nbsp;" + conflictingActionName + "</li>";
+      }
+      report += "</ul>";
+    }
+    doc->setHtml(report);
+  }
+  else /* if no conflicts */ {
+    doc->clear();
+  }
+}
+
+void QtxEditKeySequenceDialog::onConfirm()
+{
+  if (myKeySequenceEdit->isKeySequenceModified())
+    accept();
+  else
+    reject();
+}
+
+
+/*! \brief Compensates lack of std::distance(), which is introduced in C++17.
+\returns -1, if theIt does not belong to the  */
+template <class Container>
+size_t indexOf(
+  const Container& theContainer,
+  const typename Container::iterator& theIt
+) {
+  auto it = theContainer.begin();
+  size_t distance = 0;
+  while (it != theContainer.end()) {
+    if (it == theIt)
+      return distance;
+
+    it++;
+    distance++;
+  }
+  return -1;
+}
+
+
+/*! \param theContainer Share the same container between several trees,
+to edit them synchronously even without exchange of changes with SUIT_ShortcutMgr.
+Pass nullptr to create non-synchronized tree. */
+QtxShortcutTree::QtxShortcutTree(
+  std::shared_ptr<SUIT_ShortcutContainer> theContainer,
+  QWidget* theParent
+) : QTreeWidget(theParent),
+myShortcutContainer(theContainer ? theContainer : std::shared_ptr<SUIT_ShortcutContainer>(new SUIT_ShortcutContainer())),
+mySortKey(QtxShortcutTree::SortKey::Name), mySortOrder(QtxShortcutTree::SortOrder::Ascending)
+{
+  setColumnCount(2);
+  setSelectionMode(QAbstractItemView::SingleSelection);
+  setColumnWidth(0, COLUMN_SIZE);
+  setSortingEnabled(false); // Items are sorted in the same way, as in ShortcutContainer.
+  header()->setSectionResizeMode(QHeaderView::Interactive);
+  {
+    QMap<int, QString> labelMap;
+    labelMap[QtxShortcutTree::ElementIdx::Name]        = tr("Action");
+    labelMap[QtxShortcutTree::ElementIdx::KeySequence] = tr("Key sequence");
+    setHeaderLabels(labelMap.values());
+  }
+  setExpandsOnDoubleClick(false); // Open shortcut editor on double click instead.
   setSortingEnabled(false);
-  headerItem()->setHidden ( true ); 
+  setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents);
+  setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+  myEditDialog = new QtxEditKeySequenceDialog(this);
 
   this->installEventFilter(this);
-  connect( this, SIGNAL( currentItemChanged ( QTreeWidgetItem*, QTreeWidgetItem* ) ), this, SLOT( onCurrentItemChanged ( QTreeWidgetItem*, QTreeWidgetItem* ) ) );
+  connect(this, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), this, SLOT(onItemDoubleClicked(QTreeWidgetItem*, int)));
 
+  QtxShortcutTree::instances[myShortcutContainer.get()].emplace(this);
 }
 
-/*!
-  \brief Destructor
-*/
-QtxShortcutTree::~QtxShortcutTree(){}
+QtxShortcutTree::~QtxShortcutTree()
+{
+  QtxShortcutTree::instances[myShortcutContainer.get()].erase(this);
+  if (QtxShortcutTree::instances[myShortcutContainer.get()].empty())
+    QtxShortcutTree::instances.erase(myShortcutContainer.get());
+}
 
-/*!
-  \brief Custom event filter. 
-  \param obj event receiver object
-  \param event event
-  \return \c true if further event processing should be stopped
-*/
-bool QtxShortcutTree::eventFilter(QObject* /*obj*/, QEvent* event) 
-{ 
-  if ( currentItem() && currentItem()->isSelected() ) {
-    
-    if (event->type() == QEvent::KeyPress ) {
-      QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
-      QString text = QtxShortcutEdit::parseEvent( keyEvent );
-      if ( keyEvent->key() == Qt::Key_Delete || keyEvent->key() == Qt::Key_Backspace )
-        currentItem()->setText( 1, "" );
-      if ( text != "" ) {
-       if ( text.endsWith( "+" ) || checkUniqueness( currentItem(), text ) )
-          currentItem()->setText( 1, text );
+/*! \brief Copies shortcuts from ShortcutMgr. (Re)displays shortcuts of myModuleIDs. */
+void QtxShortcutTree::setShortcutsFromManager()
+{
+  const auto shortcutMgr = SUIT_ShortcutMgr::get();
+  *myShortcutContainer = shortcutMgr->getShortcutContainer();
+  // nb! ShortcutMgr never removes shortcuts from its container, only disables.
+
+  updateItems(false /*theHighlightModified*/, true /*theUpdateSyncTrees*/);
+}
+
+/*! \brief Copies shortcuts from resources, user files are not accounted. (Re)displays shortcuts of myModuleIDs. */
+void QtxShortcutTree::setDefaultShortcuts()
+{
+  SUIT_ShortcutContainer defaultShortcuts;
+  SUIT_ShortcutMgr::fillContainerFromPreferences(defaultShortcuts, true /*theDefaultOnly*/);
+
+  myShortcutContainer->merge(defaultShortcuts, true /*theOverride*/, true /*theTreatAbsentIncomingAsDisabled*/);
+  // nb! SUIT_ShortcutContainer never erases shortcuts, only disables.
+
+  updateItems(true /*theHighlightModified*/, true /*theUpdateSyncTrees*/);
+}
+
+/*! \brief Applies pending changes to ShortcutMgr. Updates other instances of QtxShortcutTree. */
+void QtxShortcutTree::applyChangesToShortcutMgr()
+{
+  const auto mgr = SUIT_ShortcutMgr::get();
+  mgr->mergeShortcutContainer(*myShortcutContainer);
+
+  // Update non-synchronized with this instances.
+  for (const auto& containerAndSyncTrees : QtxShortcutTree::instances) {
+    if (containerAndSyncTrees.first == myShortcutContainer.get())
+      continue;
+
+    const std::set<QtxShortcutTree*>& syncTrees = containerAndSyncTrees.second;
+    const auto itFirstSyncTree = syncTrees.begin();
+    if (itFirstSyncTree == syncTrees.end())
+      continue;
+
+    (*itFirstSyncTree)->setShortcutsFromManager();
+    const auto editDialog = (*itFirstSyncTree)->myEditDialog;
+    editDialog->setConfirmedKeySequence(mgr->getShortcutContainer().getKeySequence(editDialog->moduleID(), editDialog->inModuleActionID()));
+    editDialog->updateConflictsMessage();
+  }
+}
+
+std::shared_ptr<const SUIT_ShortcutContainer> QtxShortcutTree::shortcutContainer() const
+{
+  return myShortcutContainer;
+}
+
+/*! \brief Does not sort modules. */
+void QtxShortcutTree::sort(QtxShortcutTree::SortKey theKey, QtxShortcutTree::SortOrder theOrder)
+{
+  if (theKey == mySortKey && theOrder == mySortOrder)
+    return;
+
+  mySortKey == theKey;
+  mySortOrder = theOrder;
+
+  for (int moduleIdx = 0; moduleIdx < topLevelItemCount(); moduleIdx++) {
+    const auto moduleItem = static_cast<QtxShortcutTreeFolder*>(topLevelItem(moduleIdx));
+    const auto sortedChildren = getSortedChildren(moduleItem);
+    moduleItem->takeChildren();
+
+    for (const auto childItem : sortedChildren) {
+      moduleItem->addChild(childItem);
+    }
+  }
+}
+
+/*! \param If theUpdateSyncTrees, trees sharing the same shortcut container are updated. */
+void QtxShortcutTree::updateItems(bool theHighlightModified, bool theUpdateSyncTrees)
+{
+  const auto shortcutMgr = SUIT_ShortcutMgr::get();
+  const QString lang = SUIT_ShortcutMgr::getLang();
+
+  for (const QString& moduleID : myModuleIDs) {
+    const auto& moduleShortcuts = myShortcutContainer->getModuleShortcutsInversed(moduleID);
+    if (moduleShortcuts.empty()) {
+      // Do not display empty module.
+      const auto moduleItemAndIdx = findModuleFolderItem(moduleID);
+      if (moduleItemAndIdx.second >= 0)
+        delete takeTopLevelItem(moduleItemAndIdx.second);
+
+      continue;
+    }
+
+    const auto moduleItemAndIdx = findModuleFolderItem(moduleID);
+    QtxShortcutTreeFolder* moduleItem = moduleItemAndIdx.first;
+    if (!moduleItem) {
+      moduleItem = new QtxShortcutTreeFolder(moduleID);
+      moduleItem->setAssets(shortcutMgr->getModuleAssets(moduleID), lang);
+      addTopLevelItem(moduleItem);
+      moduleItem->setFlags(Qt::ItemIsEnabled);
+
+      auto sortedChildren = getSortedChildren(moduleItem);
+      for (const auto& shortcut : moduleShortcuts) {
+        const QString& inModuleActionID = shortcut.first;
+        const QKeySequence& keySequence = shortcut.second;
+        const QString keySequenceString = keySequence.toString();
+
+        auto actionItem = QtxShortcutTreeAction::create(moduleID, inModuleActionID);
+        if (!actionItem) {
+          ShCutDbg("QtxShortcutTree can't create child item for action ID = \"" + SUIT_ShortcutMgr::makeActionID(moduleID, inModuleActionID) + "\".");
+          continue;
+        }
+
+        actionItem->setAssets(shortcutMgr->getActionAssets(moduleID, inModuleActionID), lang);
+        actionItem->setKeySequence(keySequenceString);
+
+        if (theHighlightModified) {
+          const QKeySequence& appliedKeySequence = SUIT_ShortcutMgr::get()->getKeySequence(moduleID, inModuleActionID);
+          actionItem->highlightKeySequenceAsModified(keySequence != appliedKeySequence);
+        }
+
+        insertChild(moduleItem, sortedChildren, actionItem);
       }
-      return true;
+
+      moduleItem->setExpanded(true); // Make tree expanded on first show.
     }
-    if ( event->type() == QEvent::KeyRelease ) {
-      if ( currentItem()->text( 1 ).endsWith( "+" ) )
-       currentItem()->setText( 1, myPrevBindings[ currentItem()->parent()->text( 0 ) ][ currentItem()->text( 0 ) ] );
-      else myPrevBindings[ currentItem()->parent()->text( 0 ) ][ currentItem()->text( 0 ) ] = currentItem()->text( 1 );
+    else /* if the tree has the module-item */ {
+      for (int childIdx = 0; childIdx < moduleItem->childCount(); childIdx++) {
+        // Update exisiting items of a module.
+        QtxShortcutTreeAction* const childItem = static_cast<QtxShortcutTreeAction*>(moduleItem->child(childIdx));
+        const auto itShortcut = moduleShortcuts.find(childItem->myInModuleActionID);
+        if (itShortcut == moduleShortcuts.end()) {
+          // Shortcut of the item has been removed from myShortcutContainer - impossible.
+          continue;
+        }
+        const QKeySequence& newKeySequence = itShortcut->second;
+        const QString newKeySequenceString = newKeySequence.toString();
+        if (childItem->keySequence() != newKeySequenceString)
+          childItem->setKeySequence(newKeySequenceString);
+
+        if (theHighlightModified) {
+          const QKeySequence& appliedKeySequence = SUIT_ShortcutMgr::get()->getKeySequence(moduleID, childItem->myInModuleActionID);
+          childItem->highlightKeySequenceAsModified(newKeySequence != appliedKeySequence);
+        }
+        else
+          childItem->highlightKeySequenceAsModified(false);
+      }
 
-      return true;
-    }    
-  } 
-  return false;
+      // Add new items if myShortcutContainer acquired new shortcuts, which may happen if a developer forgot
+      // to add shortcuts for registered actions to resource files.
+      if (moduleItem->childCount() < moduleShortcuts.size()) {
+        auto sortedChildren = getSortedChildren(moduleItem);
+        for (const auto& shortcut : moduleShortcuts) {
+          const QString& inModuleActionID = shortcut.first;
+          const auto predicate = [&inModuleActionID](const QtxShortcutTreeItem* const theItem) -> bool {
+            return static_cast<const QtxShortcutTreeAction* const>(theItem)->myInModuleActionID == inModuleActionID;
+          };
+
+          if (std::find_if(sortedChildren.begin(), sortedChildren.end(), predicate) == sortedChildren.end()) {
+            const auto actionItem = QtxShortcutTreeAction::create(moduleID, inModuleActionID);
+            if (!actionItem) {
+              ShCutDbg("QtxShortcutTree can't create child item for action ID = \"" + SUIT_ShortcutMgr::makeActionID(moduleID, inModuleActionID) + "\".");
+              continue;
+            }
+
+            const QKeySequence& keySequence = shortcut.second;
+            actionItem->setAssets(shortcutMgr->getActionAssets(moduleID, inModuleActionID), lang);
+            actionItem->setKeySequence(keySequence.toString());
+
+            if (theHighlightModified) {
+              const QKeySequence& appliedKeySequence = SUIT_ShortcutMgr::get()->getKeySequence(moduleID, inModuleActionID);
+              actionItem->highlightKeySequenceAsModified(keySequence != appliedKeySequence);
+            }
+
+            insertChild(moduleItem, sortedChildren, actionItem);
+          }
+        }
+      }
+    }
+  }
+
+  if (theUpdateSyncTrees) {
+    const std::set<QtxShortcutTree*>& syncTrees = QtxShortcutTree::instances[myShortcutContainer.get()];
+    for (const auto syncTree: syncTrees) {
+      if (syncTree == this)
+        continue;
+
+      syncTree->updateItems(theHighlightModified, false /*theUpdateSyncTrees*/);
+      const auto editDialog = syncTree->myEditDialog;
+      editDialog->setConfirmedKeySequence(myShortcutContainer->getKeySequence(editDialog->moduleID(), editDialog->inModuleActionID()));
+      editDialog->updateConflictsMessage();
+    }
+  }
 }
 
-/*!
-  \brief Called when the current item changes.
-  \param cur the current item
-  \param prev the previous current item
-*/
-void QtxShortcutTree::onCurrentItemChanged( QTreeWidgetItem* /*cur*/, QTreeWidgetItem* prev )
+/*! \returns Pointer and index of top-level item.
+If the tree does not contain an item with theModuleID, returns {nullptr, -1}. */
+std::pair<QtxShortcutTreeFolder*, int> QtxShortcutTree::findModuleFolderItem(const QString& theModuleID) const
 {
-  if ( prev && prev->text( 1 ).endsWith( "+" ) )
-      prev->setText( 1, myPrevBindings[ prev->parent()->text( 0 ) ][ prev->text( 0 ) ] );
+  for (int moduleIdx = 0; moduleIdx < topLevelItemCount(); moduleIdx++) {
+    QtxShortcutTreeFolder* moduleItem = static_cast<QtxShortcutTreeFolder*>(topLevelItem(moduleIdx));
+    if (moduleItem->myModuleID == theModuleID)
+      return std::pair<QtxShortcutTreeFolder*, int>(moduleItem, moduleIdx);
+  }
+  return std::pair<QtxShortcutTreeFolder*, int>(nullptr, -1);
 }
 
-/*!
-  \brief Set key bindings to the tree
-  \param title the name of top-level item
-  \param theShortcutMap map of key bindings
-*/
-void QtxShortcutTree::setBindings( const QString& title, const ShortcutMap& theShortcutMap )
-{
-  QTreeWidgetItem* item= new QTreeWidgetItem();
-  QFont font = item->font(0);
-  font.setBold(true);
-  
-  if ( findItems( title, Qt::MatchFixedString ).isEmpty()  ) {
-    item->setText( 0, title );
-    item->setFont( 0, font );
-    addTopLevelItem( item );
-    item->setFlags( Qt::ItemIsEnabled );
-  } else {
-    item = findItems( title, Qt::MatchFixedString ).first();
-    item->takeChildren();
+/*! \returns Children of theParentItem being sorted according to current sort mode and order. */
+std::set<QtxShortcutTreeItem*, std::function<bool(QtxShortcutTreeItem*, QtxShortcutTreeItem*)>> QtxShortcutTree::getSortedChildren(QtxShortcutTreeFolder* theParentItem)
+{
+  QList<std::pair<QtxShortcutTree::SortKey, QtxShortcutTree::SortOrder>> sortSchema = QtxShortcutTree::DEFAULT_SORT_SCHEMA;
+  {
+    for (auto itSameKey = sortSchema.begin(); itSameKey != sortSchema.end(); itSameKey++) {
+      if (itSameKey->first == mySortKey) {
+        sortSchema.erase(itSameKey);
+        break;
+      }
+    }
+    sortSchema.push_front(std::pair<QtxShortcutTree::SortKey, QtxShortcutTree::SortOrder>(mySortKey, mySortOrder));
   }
-  for( ShortcutMap::const_iterator it = theShortcutMap.constBegin(); it != theShortcutMap.constEnd(); ++it )
-      item->addChild( new QTreeWidgetItem( QStringList() << it.key() << it.value() ) );
-  myPrevBindings.insert( title, theShortcutMap);
+
+  static const QCollator collator;
+  const std::function<bool(QtxShortcutTreeItem*, QtxShortcutTreeItem*)> comparator =
+  [this, sortSchema, &collator](const QtxShortcutTreeItem* theItemA, const QtxShortcutTreeItem* theItemB) {
+    int res = 0;
+    for (const auto& keyAndOrder : sortSchema) {
+      int res = 0;
+      res = collator.compare(theItemA->getValue(keyAndOrder.first), theItemB->getValue(keyAndOrder.first));
+      if (res != 0)
+        return keyAndOrder.second == QtxShortcutTree::SortOrder::Ascending ? res < 0 : res > 0;
+    }
+    return false;
+  };
+
+  std::set<QtxShortcutTreeItem*, std::function<bool(QtxShortcutTreeItem*, QtxShortcutTreeItem*)>> sortedChildren(comparator);
+  for (int childIdx = 0; childIdx < theParentItem->childCount(); childIdx++) {
+    QtxShortcutTreeAction* const childItem = static_cast<QtxShortcutTreeAction*>(theParentItem->child(childIdx));
+    sortedChildren.emplace(childItem);
+  }
+  return sortedChildren;
 }
 
-/*!
-  \brief Get all sections names.
-  \return list of section names
-*/
-QStringList QtxShortcutTree::sections() const
+/*! \brief Inserts theChildItem to theParentItem and theSortedChildren.
+Does not check whether theSortedChildren are actually child items of theParentItem.
+Does not check whether current item sort schema is same as one of theSortedChildren. */
+void QtxShortcutTree::insertChild(
+  QtxShortcutTreeFolder* theParentItem,
+  std::set<QtxShortcutTreeItem*, std::function<bool(QtxShortcutTreeItem*, QtxShortcutTreeItem*)>>& theSortedChildren,
+  QtxShortcutTreeItem* theChildItem
+) {
+  auto emplaceRes = theSortedChildren.emplace(theChildItem);
+  theParentItem->insertChild(indexOf(theSortedChildren, emplaceRes.first), theChildItem);
+}
+
+void QtxShortcutTree::onItemDoubleClicked(QTreeWidgetItem* theItem, int theColIdx)
+{
+  {
+    QtxShortcutTreeItem* const item = static_cast<QtxShortcutTreeItem*>(theItem);
+    // Do not react if folder-item is clicked.
+    if (item->type() != QtxShortcutTreeItem::Type::Action)
+      return;
+  }
+
+  QtxShortcutTreeAction* const actionItem = static_cast<QtxShortcutTreeAction*>(theItem);
+
+  myEditDialog->setModuleAndActionID(actionItem->myModuleID, actionItem->myInModuleActionID);
+  QString actionToolTip = actionItem->toolTip(QtxShortcutTree::ElementIdx::Name);
+  actionToolTip.truncate(actionToolTip.lastIndexOf('\n') + 1);
+  myEditDialog->setModuleAndActionName(
+    static_cast<QtxShortcutTreeItem*>(actionItem->parent())->name(),
+    actionItem->name(),
+    actionToolTip
+  );
+  myEditDialog->setConfirmedKeySequence(QKeySequence::fromString(actionItem->keySequence()));
+  myEditDialog->updateConflictsMessage();
+  const bool somethingChanged = myEditDialog->exec() == QDialog::Accepted;
+
+  if (!somethingChanged)
+    return;
+
+  const QKeySequence newKeySequence = myEditDialog->editedKeySequence();
+
+  /** { moduleID, inModuleActionID }[] */
+  std::set<std::pair<QString, QString>> disabledActionIDs = myShortcutContainer->setShortcut(actionItem->myModuleID, actionItem->myInModuleActionID, newKeySequence, true /*override*/);
+
+  /** { moduleID, {inModuleActionID, keySequence}[] }[] */
+  std::map<QString, std::map<QString, QString>> changes;
+  changes[actionItem->myModuleID][actionItem->myInModuleActionID] = newKeySequence.toString();
+  for (const auto moduleAndActionID : disabledActionIDs) {
+    changes[moduleAndActionID.first][moduleAndActionID.second] = QString();
+  }
+
+  // Set new key sequences to shortcut items.
+  for (const auto& moduleIDAndChanges : changes) {
+    const QString& moduleID = moduleIDAndChanges.first;
+
+    const auto moduleItemAndIdx = findModuleFolderItem(moduleID);
+    const auto moduleItem = moduleItemAndIdx.first;
+    if (!moduleItem)
+      continue;
+
+    /** {inModuleActionID, newKeySequence}[] */
+    const std::map<QString, QString>& moduleChanges = moduleIDAndChanges.second;
+
+    // Go through module' shortcut items, and highlight those, whose key sequences differ from applied key sequences.
+    for (int childIdx = 0; childIdx < moduleItem->childCount(); childIdx++) {
+      QtxShortcutTreeAction* const childItem = static_cast<QtxShortcutTreeAction*>(moduleItem->child(childIdx));
+      const auto itChange = moduleChanges.find(childItem->myInModuleActionID);
+      if (itChange == moduleChanges.end()) {
+        // The shortcut has not been changed.
+        continue;
+      }
+
+      childItem->setKeySequence(itChange->second);
+
+      const QKeySequence& appliedKeySequence = SUIT_ShortcutMgr::get()->getKeySequence(moduleID, childItem->myInModuleActionID);
+      childItem->highlightKeySequenceAsModified(QKeySequence::fromString(itChange->second) != appliedKeySequence);
+    }
+  }
+}
+
+/*static*/ const QList<std::pair<QtxShortcutTree::SortKey, QtxShortcutTree::SortOrder>> QtxShortcutTree::DEFAULT_SORT_SCHEMA =
+{
+  {QtxShortcutTree::SortKey::Name, QtxShortcutTree::SortOrder::Ascending},
+  {QtxShortcutTree::SortKey::ToolTip, QtxShortcutTree::SortOrder::Ascending},
+  {QtxShortcutTree::SortKey::KeySequence, QtxShortcutTree::SortOrder::Ascending},
+  {QtxShortcutTree::SortKey::ID, QtxShortcutTree::SortOrder::Ascending}
+};
+
+/*static*/ std::map<SUIT_ShortcutContainer*, std::set<QtxShortcutTree*>> QtxShortcutTree::instances =
+std::map<SUIT_ShortcutContainer*, std::set<QtxShortcutTree*>>();
+
+
+
+QtxShortcutTreeItem::QtxShortcutTreeItem(const QString& theModuleID)
+: QTreeWidgetItem(), myModuleID(theModuleID)
+{ }
+
+QString QtxShortcutTreeItem::name() const
+{
+  return text(QtxShortcutTree::ElementIdx::Name);
+}
+
+
+QtxShortcutTreeFolder::QtxShortcutTreeFolder(const QString& theModuleID)
+: QtxShortcutTreeItem(theModuleID)
 {
-  QStringList lst;
-  for( int i = 0; i < topLevelItemCount(); i++ )
-    lst << topLevelItem( i )->text( 0 ); 
-  return lst;
+  QFont f = font(QtxShortcutTree::ElementIdx::Name);
+  f.setBold(true);
+  setFont(QtxShortcutTree::ElementIdx::Name, f);
+  setText(QtxShortcutTree::ElementIdx::Name, theModuleID);
 }
 
-ShortcutMap* QtxShortcutTree::bindings( const QString& sec ) const
+void QtxShortcutTreeFolder::setAssets(std::shared_ptr<const SUIT_ActionAssets> theAssets, const QString& theLang)
 {
-  ShortcutMap* aMap = new ShortcutMap();
-  QTreeWidgetItem* item = findItems( sec, Qt::MatchFixedString ).first();
-  int nbChildren = item->childCount();
+  if (!theAssets)
+    return;
+
+  setIcon(QtxShortcutTree::ElementIdx::Name, theAssets->myIcon);
 
-  for( int i = 0; i < nbChildren; i++ ) {
-    QTreeWidgetItem* child =  item->child(i);
-    aMap->insert( child->text( 0 ), child->text( 1 ) );
+  const auto& ldaMap = theAssets->myLangDependentAssets;
+  if (ldaMap.empty()) {
+    setText(QtxShortcutTree::ElementIdx::Name, myModuleID);
+    return;
   }
 
-  return aMap;
+  auto itLDA = ldaMap.find(theLang);
+  if (itLDA == ldaMap.end())
+    itLDA = ldaMap.begin();
+
+  const SUIT_ActionAssets::LangDependentAssets& lda = itLDA->second;
+  const QString& name = lda.myName.isEmpty() ? myModuleID : lda.myName;
+  setText(QtxShortcutTree::ElementIdx::Name, name);
 }
 
-void QtxShortcutTree::focusOutEvent ( QFocusEvent* event )
+QString QtxShortcutTreeFolder::getValue(QtxShortcutTree::SortKey theKey) const
 {
-  QWidget::focusOutEvent( event );
-  if ( currentItem() && currentItem()->isSelected() )
-    currentItem()->setText( 1, myPrevBindings[ currentItem()->parent()->text( 0 ) ][ currentItem()->text( 0 ) ] );
+  switch (theKey) {
+    case QtxShortcutTree::SortKey::ID:
+      return myModuleID;
+    case QtxShortcutTree::SortKey::Name:
+      return name();
+    case QtxShortcutTree::SortKey::ToolTip:
+      return name();
+    default:
+      return QString();
+  }
 }
 
-/*!
-  \brief Set the list of shortcuts general sections.
-  
-  Key combinations in general sections should not intersect
-  with any other key combinations.
 
-  \param sectionsList list of common section names
-*/
-void QtxShortcutTree::setGeneralSections( const QStringList& sectionsList )
+QtxShortcutTreeAction::QtxShortcutTreeAction(const QString& theModuleID, const QString& theInModuleActionID)
+: QtxShortcutTreeItem(theModuleID), myInModuleActionID(theInModuleActionID)
 {
-  myGeneralSections = sectionsList;
+  setText(QtxShortcutTree::ElementIdx::Name, theInModuleActionID);
+  setToolTip(
+    QtxShortcutTree::ElementIdx::Name,
+    theInModuleActionID + (theInModuleActionID.at(theInModuleActionID.length()-1) == "." ? "\n" : ".\n") + QtxShortcutTree::tr("Double click to edit key sequence.")
+  );
+  setToolTip(QtxShortcutTree::ElementIdx::KeySequence, QtxShortcutTree::tr("Double click to edit key sequence."));
 }
 
-/*!
-  \brief Check uniqueness of the shortcut.
-  \param item current item of the shortcut tree
-  \param shortcut shortcut appointed for the current item
-  \return \c true if the given shortcut is allowed
-*/
-bool QtxShortcutTree::checkUniqueness( QTreeWidgetItem* item, const QString& shortcut )
-{
-  // List of sections to check shortcut intersections
-  QStringList sectionsList;
-
-  // Current section
-  QString currentSection = currentItem()->parent()->text( 0 );
-
-  // If the current section is general 
-  if ( myGeneralSections.contains(currentSection) ) {
-    sectionsList = sections();
-    int currentSectionIndex = sectionsList.indexOf(currentSection);
-    sectionsList.move( currentSectionIndex, 0);
-  } 
-  else {
-    sectionsList = myGeneralSections;
-    sectionsList.prepend(currentSection);
+/*static*/ QtxShortcutTreeAction* QtxShortcutTreeAction::create(const QString& theModuleID, const QString& theInModuleActionID)
+{
+  if (theInModuleActionID.isEmpty()) {
+    ShCutDbg("QtxShortcutTreeItem: attempt to create item with empty action ID.");
+    return nullptr;
   }
 
-  // Iterate on sections
-  QStringList::const_iterator it;
-  for( it = sectionsList.constBegin(); it != sectionsList.constEnd(); ++it ) {
-    QString section = *it;
-
-    // Iterate on actual section
-    QTreeWidgetItem* sectionRoot = findItems( section, Qt::MatchFixedString ).first();
-    int nbChildren = sectionRoot->childCount();
-
-    for( int i = 0; i < nbChildren; i++ ) {
-      QTreeWidgetItem* child =  sectionRoot->child(i);
-      
-      if ( (child != item) && (shortcut == child->text( 1 )) ) {
-       bool res = QMessageBox::warning( parentWidget(), tr("Warning"), 
-                                        tr("The \"%1\" shortcut has already used by the \"%2\" action.\n")
-                                        .arg(shortcut, section + ":" + child->text( 0 ) ) +
-                                        tr("Do you want to reassign it from that action to the current one?"),
-                                        QMessageBox::Yes, QMessageBox::No ) == QMessageBox::Yes;
-       if (res) 
-         child->setText( 1, "" );
-       return res;     
-      }
-    }
+  return new QtxShortcutTreeAction(theModuleID, theInModuleActionID);
+}
+
+void QtxShortcutTreeAction::setAssets(std::shared_ptr<const SUIT_ActionAssets> theAssets, const QString& theLang)
+{
+  if (!theAssets)
+    return;
+
+  setIcon(QtxShortcutTree::ElementIdx::Name, theAssets->myIcon);
+
+  const auto& ldaMap = theAssets->myLangDependentAssets;
+  if (ldaMap.empty()) {
+    setText(QtxShortcutTree::ElementIdx::Name, myInModuleActionID);
+    return;
   }
 
-  return true;
+  auto itLDA = ldaMap.find(theLang);
+  if (itLDA == ldaMap.end())
+    itLDA = ldaMap.begin();
+
+  const SUIT_ActionAssets::LangDependentAssets& lda = itLDA->second;
+  const QString& name = lda.myName.isEmpty() ? myInModuleActionID : lda.myName;
+  setText(QtxShortcutTree::ElementIdx::Name, name);
+
+  const QString& actionToolTip = lda.myToolTip.isEmpty() ? name : lda.myToolTip;
+  setToolTip(
+    QtxShortcutTree::ElementIdx::Name,
+    actionToolTip + (actionToolTip.at(actionToolTip.length()-1) == "." ? "\n" : ".\n") + QtxShortcutTree::tr("Double click to edit key sequence.")
+  );
 }
+
+QString QtxShortcutTreeAction::getValue(QtxShortcutTree::SortKey theKey) const
+{
+  switch (theKey) {
+    case QtxShortcutTree::SortKey::ID:
+      return myInModuleActionID;
+    case QtxShortcutTree::SortKey::Name:
+      return name();
+    case QtxShortcutTree::SortKey::ToolTip:
+      return toolTip(QtxShortcutTree::ElementIdx::Name);
+    case QtxShortcutTree::SortKey::KeySequence:
+      return keySequence();
+    default:
+      return QString();
+  }
+}
+
+void QtxShortcutTreeAction::setKeySequence(const QString& theKeySequence)
+{
+  setText(QtxShortcutTree::ElementIdx::KeySequence, theKeySequence);
+}
+
+QString QtxShortcutTreeAction::keySequence() const
+{
+  return text(QtxShortcutTree::ElementIdx::KeySequence);
+}
+
+/*! \brief Highlights text at ElementIdx::KeySequence. */
+void QtxShortcutTreeAction::highlightKeySequenceAsModified(bool theHighlight)
+{
+  static const QBrush bgHighlitingBrush = QBrush(Qt::darkGreen);
+  static const QBrush fgHighlitingBrush = QBrush(Qt::white);
+  static const QBrush noBrush = QBrush();
+
+  setBackground(QtxShortcutTree::ElementIdx::KeySequence, theHighlight ? bgHighlitingBrush : noBrush);
+  setForeground(QtxShortcutTree::ElementIdx::KeySequence, theHighlight ? fgHighlitingBrush : noBrush);
+}
\ No newline at end of file
index 231e65a9d2cc9abda50f542a66c638f6f494c57e..862c80d8610ad936e80ee3670cc5201fc065822c 100644 (file)
 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
 //
 
-#ifndef QTXSHORTCUTEDIT_H
-#define QTXSHORTCUTEDIT_H
+#ifndef QTXSHORTCUTTREE_H
+#define QTXSHORTCUTTREE_H
 
 #include "Qtx.h"
-
+#include <QDialog>
 #include <QFrame>
 #include <QTreeWidget>
+#include "SUIT_ShortcutMgr.h"
+#include <memory>
+#include <map>
+#include <set>
+#include <functional>
+
 
 class QLineEdit;
+class QLabel;
 class QPushButton;
 class QTreeWidgetItem;
 
-typedef QMap< QString, QString > ShortcutMap;
-
-class QTX_EXPORT QtxShortcutEdit : public QFrame
+class QTX_EXPORT QtxKeySequenceEdit : public QFrame
 {
   Q_OBJECT
 
 public:
-  QtxShortcutEdit( QWidget* = 0 );
-  virtual ~QtxShortcutEdit();
-  void           setShortcut( const QKeySequence& );
-  QKeySequence   shortcut();
-  static QString parseEvent( QKeyEvent* );
-  static bool    isValidKey( int );
+  QtxKeySequenceEdit(QWidget* = nullptr);
+  virtual ~QtxKeySequenceEdit() = default;
+
+  void           setConfirmedKeySequence(const QKeySequence&);
+  void           setEditedKeySequence(const QKeySequence&);
+  QKeySequence   editedKeySequence() const;
+  bool           isKeySequenceModified() const;
+  void           restoreKeySequence();
+
+  static QString parseEvent(QKeyEvent*);
+  static bool    isValidKey(int);
 
+signals:
+  void           editingStarted();
+  void           editingFinished();
+  void           restoreFromShortcutMgrClicked();
 
 private slots:
-  void           onCliked();
+  void           onClear();
   void           onEditingFinished();
 
 protected:
-  virtual bool   eventFilter( QObject*, QEvent* );
+  virtual bool   eventFilter(QObject*, QEvent*);
 
 private:
   void           initialize();
 
 private:
-  QLineEdit*     myShortcut;
-  QString        myPrevShortcutText;
+  QLineEdit*     myKeySequenceLineEdit;
+  QString        myConfirmedKeySequenceString;
+
+  // Last valid key sequence string from myKeySequenceLineEdit.
+  QString        myPrevKeySequenceString;
+};
+
+
+class QtxShortcutTree;
+class QtxShortcutTreeItem;
+class QtxShortcutTreeFolder;
+class QtxShortcutTreeAction;
+class QTextEdit;
+
+
+class QTX_EXPORT QtxEditKeySequenceDialog : public QDialog
+{
+  Q_OBJECT
+
+public:
+  QtxEditKeySequenceDialog(QtxShortcutTree* theParent);
+  QtxEditKeySequenceDialog(const QtxEditKeySequenceDialog&) = delete;
+  QtxEditKeySequenceDialog& operator=(const QtxEditKeySequenceDialog&) = delete;
+  virtual ~QtxEditKeySequenceDialog() = default;
+
+  void setModuleAndActionID(const QString& theModuleID, const QString& theInModuleActionID);
+  const QString& moduleID() const;
+  const QString& inModuleActionID() const;
+
+  void setModuleAndActionName(const QString& theModuleName, const QString& theActionName, const QString& theActionToolTip = "");
+
+  void setConfirmedKeySequence(const QKeySequence& theSequence);
+  QKeySequence editedKeySequence() const;
+
+  void updateConflictsMessage();
+
+  int exec();
+
+private slots:
+  void onEditingStarted();
+  void onEditingFinished();
+  void onRestoreFromShortcutMgr();
+  void onConfirm();
+
+private:
+  QString myModuleID;
+  QString myInModuleActionID;
+  QLabel* myActionName;
+  QtxKeySequenceEdit* myKeySequenceEdit;
+  QTextEdit* myTextEdit;
 };
 
+
 class QTX_EXPORT QtxShortcutTree : public QTreeWidget
 {
   Q_OBJECT
 
 public:
-  QtxShortcutTree( QWidget * parent = 0 );
+  enum ElementIdx {
+    Name = 0,
+    KeySequence = 1, // Empty, if item is folder item.
+  };
+
+  enum class SortKey {
+    ID,
+    Name,
+    ToolTip,
+    KeySequence,
+  };
+
+  enum class SortOrder {
+    Ascending,
+    Descending
+  };
+
+  QtxShortcutTree(
+    std::shared_ptr<SUIT_ShortcutContainer> theContainer = std::shared_ptr<SUIT_ShortcutContainer>(),
+    QWidget* theParent = nullptr
+  );
+  QtxShortcutTree(const QtxShortcutTree&) = delete;
+  QtxShortcutTree& operator=(const QtxShortcutTree&) = delete;
   virtual ~QtxShortcutTree();
-  void                      setBindings( const QString&, const ShortcutMap& );
-  ShortcutMap*              bindings( const QString& ) const;
-  QStringList               sections() const;
-  void                      setGeneralSections( const QStringList& );
 
-protected:
-  virtual bool              eventFilter( QObject*, QEvent* );
-  virtual void              focusOutEvent( QFocusEvent* );
-  virtual bool              checkUniqueness( QTreeWidgetItem*, const QString& );
+  void setShortcutsFromManager();
+  void setDefaultShortcuts();
+  void applyChangesToShortcutMgr();
+
+  std::shared_ptr<const SUIT_ShortcutContainer> shortcutContainer() const;
+
+  void sort(QtxShortcutTree::SortKey theKey, QtxShortcutTree::SortOrder theOrder);
+
+private:
+  void updateItems(bool theHighlightModified, bool theUpdateSyncTrees);
+  std::pair<QtxShortcutTreeFolder*, int> findModuleFolderItem(const QString& theModuleID) const;
+
+  std::set<QtxShortcutTreeItem*, std::function<bool(QtxShortcutTreeItem*, QtxShortcutTreeItem*)>> getSortedChildren(QtxShortcutTreeFolder* theParentItem);
+
+  void insertChild(
+    QtxShortcutTreeFolder* theParentItem,
+    std::set<QtxShortcutTreeItem*, std::function<bool(QtxShortcutTreeItem*, QtxShortcutTreeItem*)>>& theSortedChildren,
+    QtxShortcutTreeItem* theChildItem
+  );
 
 private slots:
-  void                      onCurrentItemChanged( QTreeWidgetItem*, QTreeWidgetItem* );
+  void onItemDoubleClicked(QTreeWidgetItem* theWidgetItem, int theColIdx);
+
+public:
+  /** Keeps IDs of modules, which will are shown on setShortcutsFromManager(). */
+  std::set<QString> myModuleIDs;
+
+  static const QList<std::pair<QtxShortcutTree::SortKey, QtxShortcutTree::SortOrder>> DEFAULT_SORT_SCHEMA;
+
+private:
+  /** Allows to modify plenty of shortcuts and then apply them to SUIT_ShortcutMgr as a batch. */
+  const std::shared_ptr<SUIT_ShortcutContainer> myShortcutContainer;
+
+  QtxEditKeySequenceDialog* myEditDialog;
+
+  QtxShortcutTree::SortKey mySortKey;
+  QtxShortcutTree::SortOrder mySortOrder;
+
+  /**
+   * Ensures that, if several QtxShortcutTree instances coexist,
+   * all of them are updated when one of them applies pending changes to SUIT_ShortcutMgr.
+   *
+   * Sharing of SUIT_ShortcutContainer allows to keep some trees synchronized even without
+   * applying changes to SUIT_ShortcutMgr. Why? See QtxPagePrefShortcutTreeItem.
+   *
+   * Access is not synchronized in assumption, that all instances live in the same thread.
+  */
+  static std::map<SUIT_ShortcutContainer*, std::set<QtxShortcutTree*>> instances;
+};
+
+
+class QtxShortcutTreeItem : public QTreeWidgetItem
+{
+public:
+  enum Type {
+    Folder = 0,
+    Action = 1,
+  };
+
+protected:
+  QtxShortcutTreeItem(const QString& theModuleID);
+
+public:
+  virtual ~QtxShortcutTreeItem() = default;
+  virtual QtxShortcutTreeItem::Type type() const = 0;
+
+  virtual void setAssets(std::shared_ptr<const SUIT_ActionAssets> theAssets, const QString& theLang) = 0;
+  QString name() const;
+
+  virtual QString getValue(QtxShortcutTree::SortKey theKey) const = 0;
 
+public:
+  const QString myModuleID;
+};
+
+
+class QtxShortcutTreeFolder : public QtxShortcutTreeItem
+{
+public:
+  QtxShortcutTreeFolder(const QString& theModuleID);
+  virtual ~QtxShortcutTreeFolder() = default;
+  virtual QtxShortcutTreeItem::Type type() const { return QtxShortcutTreeItem::Type::Folder; };
+
+  virtual void setAssets(std::shared_ptr<const SUIT_ActionAssets> theAssets, const QString& theLang);
+
+  virtual QString getValue(QtxShortcutTree::SortKey theKey) const;
+};
+
+
+class QtxShortcutTreeAction : public QtxShortcutTreeItem
+{
 private:
-  QMap< QString, ShortcutMap > myPrevBindings;
-  QStringList myGeneralSections;
+  QtxShortcutTreeAction(const QString& theModuleID, const QString& theInModuleActionID);
+
+public:
+  static QtxShortcutTreeAction* create(const QString& theModuleID, const QString& theInModuleActionID);
+  virtual ~QtxShortcutTreeAction() = default;
+  virtual QtxShortcutTreeItem::Type type() const { return QtxShortcutTreeItem::Type::Action; };
+
+  virtual void setAssets(std::shared_ptr<const SUIT_ActionAssets> theAssets, const QString& theLang);
+
+  virtual QString getValue(QtxShortcutTree::SortKey theKey) const;
+
+  void setKeySequence(const QString& theKeySequence);
+  QString keySequence() const;
+  void highlightKeySequenceAsModified(bool theHighlight);
+
+  const QString myInModuleActionID;
 };
 
-#endif // QTXSHORTCUTEDIT_H
+#endif // QTXSHORTCUTTREE_H
index 4b37a3c7588f6c0b0dbdfe0682dd6ec57487983d..0decdea84accb12d928d0dff4e2192a2b61b35eb 100644 (file)
@@ -78,8 +78,8 @@ QtxWorkspace* QtxWorkspaceAction::workspace() const
 
 /*!
   \brief Set actions to be visible in the menu.
-  
-  Actions, which IDs are set in \a flags parameter, will be shown in the 
+
+  Actions, which IDs are set in \a flags parameter, will be shown in the
   menu bar. Other actions will not be shown.
 
   \param flags ORed together actions flags
@@ -164,17 +164,6 @@ QString QtxWorkspaceAction::statusTip( const int id ) const
   return txt;
 }
 
-/*!
-  \brief Set keyboard accelerator for the specified action.
-  \param id menu action ID
-  \param a new keyboard accelerator
-*/
-void QtxWorkspaceAction::setAccel( const int id, const int a )
-{
-  if ( action( id ) )
-    action( id )->setShortcut( a );
-}
-
 /*!
   \brief Set menu item icon for the specified action.
   \param id menu action ID
@@ -403,7 +392,7 @@ void QtxWorkspaceAction::activateItem( const int idx )
 
 /*!
   \brief Called when menu item is activated by the user.
-  
+
   Perform the corresponding action.
 
   \param id menu item identifier
index 42e5f2ae355a9933226cd21e3ae49c16709eb59b..ffa676c46ab1b65ab27c1222c09681a7a932e70b 100644 (file)
@@ -63,7 +63,6 @@ public:
   int           accel( const int ) const;
   QString       statusTip( const int ) const;
 
-  void          setAccel( const int, const int );
   void          setIcon( const int, const QIcon& );
   void          setText( const int, const QString& );
   void          setStatusTip( const int, const QString& );
index 6a5ba33f5e7173d72d6b2ab67fdc874155fc9128..c8a4f9ab4cd6dbd1fc97955a578ce521c1c4507b 100644 (file)
@@ -1728,10 +1728,10 @@ QtxWorkstack::QtxWorkstack( QWidget* parent )
   myWorkWin( 0 ),
   myWorkArea( 0 )
 {
-  myActionsMap.insert( SplitVertical,   new QtxAction( QString(), tr( "Split vertically" ), 0, this ) );
-  myActionsMap.insert( SplitHorizontal, new QtxAction( QString(), tr( "Split horizontally" ), 0, this ) );
-  myActionsMap.insert( Close,           new QtxAction( QString(), tr( "Close" ), 0, this ) );
-  myActionsMap.insert( Rename,          new QtxAction( QString(), tr( "Rename" ), 0, this ) );
+  myActionsMap.insert( SplitVertical,   new QtxAction( this, false /*toggle*/, "/PRP_DESK_WINDOW_VSPLIT", tr( "Split vertically" ) ) );
+  myActionsMap.insert( SplitHorizontal, new QtxAction( this, false /*toggle*/, "/PRP_DESK_WINDOW_HSPLIT", tr( "Split horizontally" ) ) );
+  myActionsMap.insert( Close,           new QtxAction( this, false /*toggle*/, tr( "Close" ) ) );
+  myActionsMap.insert( Rename,          new QtxAction( this, false /*toggle*/, tr( "Rename" ) ) );
 
   connect( myActionsMap[SplitVertical], SIGNAL( triggered( bool ) ), this, SLOT( splitVertical() ) );
   connect( myActionsMap[SplitHorizontal], SIGNAL( triggered( bool ) ), this, SLOT( splitHorizontal() ) );
@@ -2181,19 +2181,6 @@ void QtxWorkstack::SetRelativePosition( QWidget* wid, const Qt::Orientation o,
   }
 }
 
-/*!
-  \brief Set accelerator key-combination for the action with specified \a id.
-  \param id action ID
-  \param accel action accelerator
-*/
-void QtxWorkstack::setAccel( const int id, const int accel )
-{
-  if ( !myActionsMap.contains( id ) )
-    return;
-
-  myActionsMap[id]->setShortcut( accel );
-}
-
 /*!
   \brief Get the action's accelerator key-combination.
   \param id action ID
index f9a192e807de6f47ebfd2a25333dee2dcb9f7c86..d696d9e6cbe38c8fb04f04bcc7c62e5b338ecdac 100644 (file)
@@ -96,7 +96,6 @@ public:
   void                setActiveWindow( QWidget* );
 
   int                 accel( const int ) const;
-  void                setAccel( const int, const int );
 
   QIcon               icon( const int ) const;
   void                setIcon( const int, const QIcon& );
@@ -112,7 +111,7 @@ public:
 
   QByteArray          saveState( int ) const;
   bool                restoreState( const QByteArray&, int );
-  
+
   void                setOpaqueResize( bool = true );
   bool                opaqueResize() const;
 
index 74913b2b15f4229198991098eca6d1a7588f9fa1..e441a5e599d5ee224ff0923cbb8cf0808afa086d 100644 (file)
@@ -125,14 +125,14 @@ QtxWorkstackAction::QtxWorkstackAction( QtxWorkstack* ws, QObject* parent )
   if ( myWorkstack )
     insertAction( myWorkstack->action( QtxWorkstack::SplitVertical ), SplitVertical );
   else
-    insertAction( new QtxAction( tr( "Split the active window on two vertical parts" ),
-                                 tr( "Split vertically" ), 0, this ), SplitVertical );
+    insertAction( new QtxAction(this, false /*toggle*/, "/PRP_DESK_WINDOW_VSPLIT",
+      tr( "Split the active window on two vertical parts" ), tr( "Split vertically" )), SplitVertical);
 
   if ( myWorkstack )
     insertAction( myWorkstack->action( QtxWorkstack::SplitHorizontal ), SplitHorizontal );
   else
-    insertAction( new QtxAction( tr( "Split the active window on two horizontal parts" ),
-                                 tr( "Split horizontally" ), 0, this ), SplitHorizontal );
+    insertAction( new QtxAction(this, false /*toggle*/, "/PRP_DESK_WINDOW_HSPLIT",
+      tr( "Split the active window on two horizontal parts" ), tr( "Split horizontally" )), SplitHorizontal);
 
   connect( this, SIGNAL( triggered( int ) ), this, SLOT( onTriggered( int ) ) );
 
@@ -249,17 +249,6 @@ QString QtxWorkstackAction::statusTip( const int id ) const
   return txt;
 }
 
-/*!
-  \brief Set keyboard accelerator for the specified action.
-  \param id menu action ID
-  \param a new keyboard accelerator
-*/
-void QtxWorkstackAction::setAccel( const int id, const int a )
-{
-  if ( action( id ) )
-    action( id )->setShortcut( a );
-}
-
 /*!
   \brief Set menu item icon for the specified action.
   \param id menu action ID
index e7313e6f512efd1ff18af2614cbac49544bfefc4..d524afb3976bdccd53969b113fcb2e4b8132779b 100644 (file)
@@ -67,7 +67,6 @@ public:
   int           accel( const int ) const;
   QString       statusTip( const int ) const;
 
-  void          setAccel( const int, const int );
   void          setIcon( const int, const QIcon& );
   void          setText( const int, const QString& );
   void          setStatusTip( const int, const QString& );
diff --git a/src/Qtx/images/shortcut_disable.svg b/src/Qtx/images/shortcut_disable.svg
new file mode 100644 (file)
index 0000000..cf661ff
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg  viewBox="0 0 21 21" xmlns="http://www.w3.org/2000/svg">
+<g fill="none" fill-rule="evenodd" stroke="#328930" stroke-linecap="round" stroke-linejoin="round" transform="matrix(0 -1 1 0 2.5 15.5)">
+<path d="m0 5.82842712v7.17157288c0 1.1045695.8954305 2 2 2h6c1.1045695 0 2-.8954305 2-2v-7.17157288c0-.53043297-.21071368-1.0391408-.58578644-1.41421356l-3.70710678-3.70710678c-.39052429-.39052429-1.02368927-.39052429-1.41421356 0l-3.70710678 3.70710678c-.37507276.37507276-.58578644.88378059-.58578644 1.41421356z"/>
+<g transform="matrix(0 1 -1 0 14 4)">
+<path d="m3 11 4-4"/>
+<path d="m3 7 4 4"/>
+</g>
+</g>
+</svg>
diff --git a/src/Qtx/images/shortcut_restore.svg b/src/Qtx/images/shortcut_restore.svg
new file mode 100644 (file)
index 0000000..90dfc47
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg  viewBox="0 0 21 21" xmlns="http://www.w3.org/2000/svg">
+<g fill="none" fill-rule="evenodd" stroke="#328930" stroke-linecap="round" stroke-linejoin="round" transform="translate(3 6)">
+<path d="m1.378 1.376 4.243.003v4.242" transform="matrix(-.70710678 .70710678 .70710678 .70710678 3.500179 -1.449821)"/>
+<path d="m5.5 9.49998326h5c2 .00089417 3-.99910025 3-2.99998326s-1-3.00088859-3-3.00001674h-10"/>
+</g>
+</svg>
index d61dfe7fb74c7b1b483a029ea7046f7b342c851a..2ea62824ee145c9d24c536a9b8c6d00140f5b8cc 100644 (file)
         <translation>%1 a été développé en utilisant %2</translation>
     </message>
 </context>
+<context>
+    <name>QtxKeySequenceEdit</name>
+    <message>
+        <source>Disable shortcut.</source>
+        <translation>Désactivez le raccourci.</translation>
+    </message>
+    <message>
+        <source>Restore the currently applied key sequence.</source>
+        <translation>Restaurez la séquence de touches actuellement appliquée.</translation>
+    </message>
+</context>
+<context>
+    <name>QtxEditKeySequenceDialog</name>
+    <message>
+        <source>Change key sequence</source>
+        <translation>Modifier la séquence de touches</translation>
+    </message>
+    <message>
+        <source>No conflicts.</source>
+        <translation>Aucun conflit.</translation>
+    </message>
+    <message>
+        <source>Confirm</source>
+        <translation>Confirmer</translation>
+    </message>
+    <message>
+        <source>Cancel</source>
+        <translation>Annuler</translation>
+    </message>
+    <message>
+        <source>These shortcuts will be disabled on confirm:</source>
+        <translation>Ces raccourcis seront désactivés lors de la confirmation :</translation>
+    </message>
+</context>
+<context>
+    <name>QtxShortcutTree</name>
+    <message>
+        <source>Action</source>
+        <translation>Action</translation>
+    </message>
+    <message>
+        <source>Key sequence</source>
+        <translation>Séquence de touches</translation>
+    </message>
+    <message>
+        <source>Double click to edit key sequence.</source>
+        <translation>Double-cliquez pour modifier la séquence de touches.</translation>
+    </message>
+</context>
 </TS>
index 173c8128288fe8d0517d008310985163c6a522a8..36900402a7cc12635914783b079578f185c3f9b7 100644 (file)
       <translation>%1 は %2 を使用して開発されています。</translation>
     </message>
   </context>
+  <context>
+    <name>QtxKeySequenceEdit</name>
+    <message>
+        <source>Disable shortcut.</source>
+        <translation>ショートカットを無効にします。</translation>
+    </message>
+    <message>
+        <source>Restore the currently applied key sequence.</source>
+        <translation>現在適用されているキー シーケンスを復元します。</translation>
+    </message>
+  </context>
+  <context>
+      <name>QtxEditKeySequenceDialog</name>
+      <message>
+          <source>Change key sequence</source>
+          <translation>キーシーケンスを変更する</translation>
+      </message>
+      <message>
+          <source>No conflicts.</source>
+          <translation>競合はありません。</translation>
+      </message>
+      <message>
+          <source>Confirm</source>
+          <translation>確認する</translation>
+      </message>
+      <message>
+          <source>Cancel</source>
+          <translation>キャンセル</translation>
+      </message>
+      <message>
+          <source>These shortcuts will be disabled on confirm:</source>
+          <translation>これらのショートカットは確認時に無効になります。</translation>
+      </message>
+  </context>
+  <context>
+      <name>QtxShortcutTree</name>
+      <message>
+        <source>Action</source>
+        <translation>アクション</translation>
+      </message>
+      <message>
+          <source>Key sequence</source>
+          <translation>キーシーケンス</translation>
+      </message>
+      <message>
+          <source>Double click to edit key sequence.</source>
+          <translation>ダブルクリックしてキー シーケンスを編集します。</translation>
+      </message>
+  </context>
 </TS>
index 8d9b2b42f23dfca25687329f1f0a23bccf6c2c32..aaf59b449f4cb6027f9d1638d37cf0d8c6cc72e8 100644 (file)
@@ -82,7 +82,7 @@ enum MenuName {
   Preferences = 4,
   Tools       = 5,
   Window      = 6,
-  Help        = 7  
+  Help        = 7
 };
 
 enum {
@@ -98,29 +98,28 @@ enum {
 #endif
 };
 
-enum { 
+enum {
   PT_Auto     = LightApp_Preferences::Auto,
   PT_Space    = LightApp_Preferences::Space,
-  PT_Bool     = LightApp_Preferences::Bool, 
+  PT_Bool     = LightApp_Preferences::Bool,
   PT_Color    = LightApp_Preferences::Color,
-  PT_String   = LightApp_Preferences::String, 
-  PT_Selector = LightApp_Preferences::Selector, 
-  PT_DblSpin  = LightApp_Preferences::DblSpin, 
-  PT_IntSpin  = LightApp_Preferences::IntSpin, 
-  PT_Double   = LightApp_Preferences::Double, 
-  PT_Integer  = LightApp_Preferences::Integer, 
-  PT_GroupBox = LightApp_Preferences::GroupBox, 
-  PT_Tab      = LightApp_Preferences::Tab, 
-  PT_Frame    = LightApp_Preferences::Frame, 
-  PT_Font     = LightApp_Preferences::Font, 
-  PT_DirList  = LightApp_Preferences::DirList, 
-  PT_File     = LightApp_Preferences::File, 
-  PT_Slider       = LightApp_Preferences::Slider, 
-  PT_Shortcut     = LightApp_Preferences::Shortcut, 
-  PT_ShortcutTree = LightApp_Preferences::ShortcutTree, 
-  PT_BiColor      = LightApp_Preferences::BiColor, 
-  PT_Background   = LightApp_Preferences::Background, 
-  PT_UserDefined  = LightApp_Preferences::UserDefined, 
+  PT_String   = LightApp_Preferences::String,
+  PT_Selector = LightApp_Preferences::Selector,
+  PT_DblSpin  = LightApp_Preferences::DblSpin,
+  PT_IntSpin  = LightApp_Preferences::IntSpin,
+  PT_Double   = LightApp_Preferences::Double,
+  PT_Integer  = LightApp_Preferences::Integer,
+  PT_GroupBox = LightApp_Preferences::GroupBox,
+  PT_Tab      = LightApp_Preferences::Tab,
+  PT_Frame    = LightApp_Preferences::Frame,
+  PT_Font     = LightApp_Preferences::Font,
+  PT_DirList  = LightApp_Preferences::DirList,
+  PT_File     = LightApp_Preferences::File,
+  PT_Slider       = LightApp_Preferences::Slider,
+  PT_ShortcutTree = LightApp_Preferences::ShortcutTree,
+  PT_BiColor      = LightApp_Preferences::BiColor,
+  PT_Background   = LightApp_Preferences::Background,
+  PT_UserDefined  = LightApp_Preferences::UserDefined,
 };
 
 class UserDefinedContent: public QWidget
@@ -136,7 +135,7 @@ public:
 
 //! Orientation
 enum Orientation {
-  Horizontal = 0, //!< Horizontal orientation 
+  Horizontal = 0, //!< Horizontal orientation
   Vertical   = 1  //!< Vertical orientation
 };
 
@@ -161,11 +160,11 @@ enum ObjectType
 };
 #endif
 
-enum VisibilityState 
+enum VisibilityState
 {
   ShownState,             //!< Object is shown in viewer
   HiddenState,            //!< Object is hidden in viewer
-  UnpresentableState      //!< Unpresentable object    
+  UnpresentableState      //!< Unpresentable object
 };
 
 #ifndef DISABLE_PLOT2DVIEWER
@@ -174,7 +173,7 @@ enum Axis {
   yRight   = QwtPlot::yRight,
   xBottom  = QwtPlot::xBottom,
   xTop     = QwtPlot::xTop,
-};     
+};
 #endif
 
 class SalomePyQt
@@ -247,7 +246,7 @@ public:
   static void              setColor( const QString&, const QColor& );
   static QColor            getColor( const QString& );
 
-  static void              setReference( const QString&, const QString& ); 
+  static void              setReference( const QString&, const QString& );
   static QString           getReference( const QString& );
 
   static QIcon             loadIcon( const QString&, const QString& );
@@ -267,23 +266,23 @@ public:
 
   static int               createMenu( const QString&, const int = -1,
                                        const int = -1, const int = -1, const int = -1 );
-  static int               createMenu( const QString&, const QString& = QString(), 
+  static int               createMenu( const QString&, const QString& = QString(),
                                        const int = -1, const int = -1, const int = -1 );
   static int               createMenu( const int,      const int = -1,
                                        const int = -1, const int = -1 );
-  static int               createMenu( const int,      const QString& = QString(), 
+  static int               createMenu( const int,      const QString& = QString(),
                                        const int = -1, const int = -1 );
-  static int               createMenu( QAction*,     const int,      const int = -1, 
+  static int               createMenu( QAction*,     const int,      const int = -1,
                                        const int = -1, const int = -1 );
-  static int               createMenu( QAction*,     const QString&, const int = -1, 
+  static int               createMenu( QAction*,     const QString&, const int = -1,
                                        const int = -1, const int = -1 );
 
   static QAction*          createSeparator();
 
   static QAction*          createAction( const int, const QString&,
-                                         const QString& = QString(), const QString& = QString(), 
+                                         const QString& = QString(), const QString& = QString(),
                                          const QString& = QString(), const int = 0, const bool = false );
-  
+
   static QtxActionGroup*   createActionGroup( const int, const bool = true );
 
   static QAction*          action( const int );
@@ -323,7 +322,7 @@ public:
 
   static void              message( const QString&, bool = true );
   static void              clearMessages();
-  
+
   static QList<int>        getViews();
   static int               getActiveView();
   static QString           getViewType( const int );
index 0c01923e89ace596928ca6c3659d9c2a3c2109a6..5b8bb37599908807b21c1d5ea1d5cc2eaea887eb 100644 (file)
@@ -72,7 +72,7 @@ enum MenuName {
   Preferences = 4,
   Tools       = 5,
   Window      = 6,
-  Help        = 7  
+  Help        = 7
 };
 
 enum WindowType {
@@ -86,25 +86,24 @@ enum WindowType {
   WT_User
 };
 
-enum PrefType { 
+enum PrefType {
   PT_Auto,
   PT_Space,
-  PT_Bool, 
+  PT_Bool,
   PT_Color,
-  PT_String, 
-  PT_Selector, 
-  PT_DblSpin, 
-  PT_IntSpin, 
-  PT_Double, 
-  PT_Integer, 
-  PT_GroupBox, 
-  PT_Tab, 
-  PT_Frame, 
-  PT_Font, 
-  PT_DirList, 
-  PT_File, 
+  PT_String,
+  PT_Selector,
+  PT_DblSpin,
+  PT_IntSpin,
+  PT_Double,
+  PT_Integer,
+  PT_GroupBox,
+  PT_Tab,
+  PT_Frame,
+  PT_Font,
+  PT_DirList,
+  PT_File,
   PT_Slider,
-  PT_Shortcut,
   PT_ShortcutTree,
   PT_BiColor,
   PT_Background,
@@ -112,14 +111,14 @@ enum PrefType {
 };
 
 enum Orientation {
-  Horizontal = 0, 
-  Vertical   = 1  
+  Horizontal = 0,
+  Vertical   = 1
 };
 
 enum Action {
-  MoveWidget   = 0, 
-  LeaveWidget  = 1, 
-  SplitAt      = 2  
+  MoveWidget   = 0,
+  LeaveWidget  = 1,
+  SplitAt      = 2
 };
 
 class QtxAction : QWidgetAction
@@ -264,11 +263,11 @@ public:
   virtual void retrieve();
 };
 
-enum VisibilityState 
+enum VisibilityState
 {
   ShownState,
   HiddenState,
-  UnpresentableState 
+  UnpresentableState
 };
 
 class SalomePyQt
@@ -334,7 +333,7 @@ public:
   static QString           getFileName         ( QWidget*, const QString&, const QStringList&, const QString&, bool ) /ReleaseGIL/ ;
   static QStringList       getOpenFileNames    ( QWidget*, const QString&, const QStringList&, const QString& ) /ReleaseGIL/ ;
   static QString           getExistingDirectory( QWidget*, const QString&, const QString& ) /ReleaseGIL/ ;
-                        
+
   static void              createRoot() /ReleaseGIL/ ;
   static QString           createObject( const QString& = QString("") )  /ReleaseGIL/ ;
   static QString           createObject( const QString&,
@@ -357,7 +356,7 @@ public:
   static void              setColor( const QString&, const QColor& ) /ReleaseGIL/ ;
   static QColor            getColor( const QString& ) /ReleaseGIL/ ;
 
-  static void              setReference( const QString& ,const QString& ) /ReleaseGIL/ ; 
+  static void              setReference( const QString& ,const QString& ) /ReleaseGIL/ ;
   static QString           getReference( const QString& ) /ReleaseGIL/ ;
 
   static void              removeObject(const QString&  )    /ReleaseGIL/ ;
@@ -381,20 +380,20 @@ public:
 
   static int               createMenu( const QString&, const int,
                                       const int = -1, const int = -1, const int = -1 ) /ReleaseGIL/ ;
-  static int               createMenu( const QString&, const QString&, 
+  static int               createMenu( const QString&, const QString&,
                                       const int = -1, const int = -1, const int = -1 ) /ReleaseGIL/ ;
   static int               createMenu( const int,      const int,
                                       const int = -1, const int = -1 ) /ReleaseGIL/ ;
-  static int               createMenu( const int,      const QString&, 
+  static int               createMenu( const int,      const QString&,
                                       const int = -1, const int = -1 ) /ReleaseGIL/ ;
-  static int               createMenu( QAction*,       const int,      const int = -1, 
+  static int               createMenu( QAction*,       const int,      const int = -1,
                                       const int = -1, const int = -1 ) /ReleaseGIL/ ;
-  static int               createMenu( QAction*,       const QString&, const int = -1, 
+  static int               createMenu( QAction*,       const QString&, const int = -1,
                                       const int = -1, const int = -1 ) /ReleaseGIL/ ;
   static QAction*          createSeparator() /ReleaseGIL/ ;
 
-  static QAction*          createAction( const int, const QString&, 
-                                        const QString& = QString(), const QString& = QString(), 
+  static QAction*          createAction( const int, const QString&,
+                                        const QString& = QString(), const QString& = QString(),
                                         const QString& = QString(), const int = 0, const bool = false ) /ReleaseGIL/ ;
 
   static QtxActionGroup*   createActionGroup( const int, const bool = true ) /ReleaseGIL/ ;
@@ -438,20 +437,20 @@ public:
                                           const QString& = QString(),
                                          const QString& = QString() ) /ReleaseGIL/ ;
   static QVariant          preferenceProperty( const int, const QString& ) /ReleaseGIL/ ;
-  static void              setPreferenceProperty( const int, 
+  static void              setPreferenceProperty( const int,
                                                   const QString&,
                                                   const QVariant& ) /ReleaseGIL/ ;
-  static void              setPreferencePropertyWg( const int, 
-                                                    const QString&,    
+  static void              setPreferencePropertyWg( const int,
+                                                    const QString&,
                                                     UserDefinedContent* ) /ReleaseGIL/ ;
-  static void              addPreferenceProperty( const int, 
-                                                  const QString&, 
-                                                 const int, 
+  static void              addPreferenceProperty( const int,
+                                                  const QString&,
+                                                 const int,
                                                  const QVariant& ) /ReleaseGIL/ ;
 
   static void              message( const QString&, bool = true ) /ReleaseGIL/ ;
   static void              clearMessages() /ReleaseGIL/ ;
-  
+
   static QList<int>        getViews() /ReleaseGIL/ ;
   static int               getActiveView() /ReleaseGIL/ ;
   static QString           getViewType( const int ) /ReleaseGIL/ ;
@@ -471,12 +470,12 @@ public:
   static bool              isViewVisible( const int id ) /ReleaseGIL/ ;
   static void              setViewClosable( const int id, const bool ) /ReleaseGIL/ ;
   static bool              isViewClosable( const int id ) /ReleaseGIL/ ;
-  
+
   static bool              groupAllViews() /ReleaseGIL/ ;
   static bool              splitView( const int, Orientation, Action ) /ReleaseGIL/ ;
   static bool              moveView( const int, const int, const bool ) /ReleaseGIL/ ;
   static QList<int>        neighbourViews( const int ) /ReleaseGIL/ ;
-  
+
 %If (ENABLE_PLOT2D)
 // start Plot2d-related functionality
   static void              displayCurve(const int, Plot2d_Curve*) /ReleaseGIL/ ;
index ff9cbd203bc678429896939796dd38db4671824f..6dceb529f25a24e0bd7951f1b789a734c2f144aa 100644 (file)
@@ -157,12 +157,12 @@ void STD_Application::createActions()
   createAction( FileNewId, tr( "TOT_DESK_FILE_NEW" ),
                 resMgr->loadPixmap( "STD", tr( "ICON_FILE_NEW" ) ),
                 tr( "MEN_DESK_FILE_NEW" ), tr( "PRP_DESK_FILE_NEW" ),
-                Qt::CTRL+Qt::Key_N, desk, false, this, SLOT( onNewDoc() ) );
+                QKeySequence::UnknownKey, desk, false, this, SLOT( onNewDoc() ), "/TOT_DESK_FILE_NEW" );
 
   createAction( FileOpenId, tr( "TOT_DESK_FILE_OPEN" ),
                 resMgr->loadPixmap( "STD", tr( "ICON_FILE_OPEN" ) ),
                 tr( "MEN_DESK_FILE_OPEN" ), tr( "PRP_DESK_FILE_OPEN" ),
-                Qt::CTRL+Qt::Key_O, desk, false, this, SLOT( onOpenDoc() ) );
+                QKeySequence::UnknownKey, desk, false, this, SLOT( onOpenDoc() ), "/TOT_DESK_FILE_OPEN" );
 
   createAction( FileReopenId, tr( "TOT_DESK_FILE_REOPEN" ), QIcon(),
                 tr( "MEN_DESK_FILE_REOPEN" ), tr( "PRP_DESK_FILE_REOPEN" ),
@@ -171,46 +171,46 @@ void STD_Application::createActions()
   createAction( FileCloseId, tr( "TOT_DESK_FILE_CLOSE" ),
                 resMgr->loadPixmap( "STD", tr( "ICON_FILE_CLOSE" ) ),
                 tr( "MEN_DESK_FILE_CLOSE" ), tr( "PRP_DESK_FILE_CLOSE" ),
-                Qt::CTRL+Qt::Key_W, desk, false, this, SLOT( onCloseDoc() ) );
+                QKeySequence::UnknownKey, desk, false, this, SLOT( onCloseDoc() ), "/TOT_DESK_FILE_CLOSE" );
   //no need in this action for mono-study application as it is same as NewDoc
   action( FileCloseId )->setVisible( false );
 
   createAction( FileExitId, tr( "TOT_DESK_FILE_EXIT" ), QIcon(),
                 tr( "MEN_DESK_FILE_EXIT" ), tr( "PRP_DESK_FILE_EXIT" ),
-                Qt::CTRL+Qt::Key_Q, desk, false, this, SLOT( onExit() ) );
+                QKeySequence::UnknownKey, desk, false, this, SLOT( onExit() ), "/TOT_DESK_FILE_EXIT" );
 
   createAction( FileSaveId, tr( "TOT_DESK_FILE_SAVE" ),
                 resMgr->loadPixmap( "STD", tr( "ICON_FILE_SAVE" ) ),
                 tr( "MEN_DESK_FILE_SAVE" ), tr( "PRP_DESK_FILE_SAVE" ),
-                Qt::CTRL+Qt::Key_S, desk, false, this, SLOT( onSaveDoc() ) );
+                QKeySequence::UnknownKey, desk, false, this, SLOT( onSaveDoc() ), "/TOT_DESK_FILE_SAVE" );
 
   createAction( FileSaveAsId, tr( "TOT_DESK_FILE_SAVEAS" ), QIcon(),
                 tr( "MEN_DESK_FILE_SAVEAS" ), tr( "PRP_DESK_FILE_SAVEAS" ),
-                Qt::CTRL+Qt::SHIFT+Qt::Key_S, desk, false, this, SLOT( onSaveAsDoc() ) );
+                QKeySequence::UnknownKey, desk, false, this, SLOT( onSaveAsDoc() ),  "/TOT_DESK_FILE_SAVEAS");
 
   createAction( EditCopyId, tr( "TOT_DESK_EDIT_COPY" ),
                 resMgr->loadPixmap( "STD", tr( "ICON_EDIT_COPY" ) ),
                 tr( "MEN_DESK_EDIT_COPY" ), tr( "PRP_DESK_EDIT_COPY" ),
-                Qt::CTRL+Qt::Key_C, desk, false, this, SLOT( onCopy() ) );
+                QKeySequence::UnknownKey, desk, false, this, SLOT( onCopy() ), "/#TOT_DESK_EDIT_COPY" );
 
   createAction( EditPasteId, tr( "TOT_DESK_EDIT_PASTE" ),
                 resMgr->loadPixmap( "STD", tr( "ICON_EDIT_PASTE" ) ),
                 tr( "MEN_DESK_EDIT_PASTE" ), tr( "PRP_DESK_EDIT_PASTE" ),
-                Qt::CTRL+Qt::Key_V, desk, false, this, SLOT( onPaste() ) );
+                QKeySequence::UnknownKey, desk, false, this, SLOT( onPaste() ), "/TOT_DESK_EDIT_PASTE" );
+
+  QAction* a = createAction( ViewStatusBarId, desk, true /*toggle*/, "/PRP_DESK_VIEW_STATUSBAR",
+                             tr( "TOT_DESK_VIEW_STATUSBAR" ), tr( "MEN_DESK_VIEW_STATUSBAR" ), tr( "PRP_DESK_VIEW_STATUSBAR" ));
 
-  QAction* a = createAction( ViewStatusBarId, tr( "TOT_DESK_VIEW_STATUSBAR" ),
-                             QIcon(), tr( "MEN_DESK_VIEW_STATUSBAR" ),
-                             tr( "PRP_DESK_VIEW_STATUSBAR" ), Qt::ALT+Qt::SHIFT+Qt::Key_S, desk, true );
   a->setChecked( desk->statusBar()->isVisibleTo( desk ) );
   connect( a, SIGNAL( toggled( bool ) ), this, SLOT( onViewStatusBar( bool ) ) );
 
   createAction( NewWindowId, tr( "TOT_DESK_NEWWINDOW" ), QIcon(),
                 tr( "MEN_DESK_NEWWINDOW" ), tr( "PRP_DESK_NEWWINDOW" ), 0, desk  );
 
-  createAction( HelpAboutId, tr( "TOT_DESK_HELP_ABOUT" ),
-                resMgr->loadPixmap( "STD", tr( "ICON_DESK_ABOUT" ) ),
-                tr( "MEN_DESK_HELP_ABOUT" ), tr( "PRP_DESK_HELP_ABOUT" ),
-                Qt::ALT+Qt::SHIFT+Qt::Key_A, desk, false, this, SLOT( onHelpAbout() ) );
+  createAction( HelpAboutId, desk, false /*toggle*/, "/PRP_DESK_HELP_ABOUT",
+                             tr( "TOT_DESK_HELP_ABOUT" ), tr( "MEN_DESK_HELP_ABOUT" ), tr( "PRP_DESK_HELP_ABOUT" ),
+                             resMgr->loadPixmap( "STD", tr( "ICON_DESK_ABOUT" ) ),
+                             this, SLOT( onHelpAbout() ) );
 
 
   QtxDockAction* dwa = new QtxDockAction( tr( "TOT_DOCKWINDOWS" ), tr( "MEN_DESK_VIEW_DOCKWINDOWS" ), desk );
@@ -233,7 +233,7 @@ void STD_Application::createActions()
 
   createMenu( FileNewId,    fileMenu, 0 );
   createMenu( FileOpenId,   fileMenu, 0 );
-  createMenu( FileReopenId, fileMenu, 0 ); 
+  createMenu( FileReopenId, fileMenu, 0 );
   createMenu( FileSaveId,   fileMenu, 5 );
   createMenu( FileSaveAsId, fileMenu, 5 );
   createMenu( FileCloseId,  fileMenu, 5 );
@@ -342,7 +342,7 @@ bool STD_Application::onOpenDoc( const QString& aName )
   QApplication::setOverrideCursor( Qt::WaitCursor );
 
   bool res = openAction( openChoice( aName ), aName );
-  
+
   QApplication::restoreOverrideCursor();
 
   return res;
@@ -375,7 +375,7 @@ bool STD_Application::onReopenDoc()
     // delete study
     delete study;
     study = 0;
-    
+
     // post closing actions
     afterCloseDoc();
 
@@ -730,11 +730,11 @@ void STD_Application::updateCommandsStatus()
 
 /*!
   \brief Show notification with specified text and title.
-  
+
   Notification will be automatically hidden after specified \a timeout
   (given in milliseconds). If \a timeout is zero, the notification
   is not automatically hidden; it can be only closed by the user manually.
-  
+
   \param text - Notification text
   \param title - Notification title
   \param timeout - Timeout in milliseconds
index ec674d5a2b417f4e3ab609150e023ae200819694..8b64dca057b33b0289b3d59b386f81f88689a32b 100644 (file)
@@ -56,10 +56,6 @@ myWorkstackAction( 0 )
   // But the workstack must occupy as much space as possible -- set Expanding for it.
   myWorkstack->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ) );
 
-  myWorkstack->setAccel( QtxWorkstack::SplitVertical,   Qt::ALT + Qt::SHIFT + Qt::Key_V );
-  myWorkstack->setAccel( QtxWorkstack::SplitHorizontal, Qt::ALT + Qt::SHIFT + Qt::Key_H );
-  //myWorkstack->setAccel( QtxWorkstack::Close,           Qt::CTRL + Qt::Key_F4 );
-
   SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
   if ( resMgr ) {
     myWorkstack->setIcon( QtxWorkstack::SplitVertical,
@@ -216,14 +212,12 @@ void STD_TabDesktop::createActions()
                               resMgr->loadPixmap( "STD", tr( "ICON_DESK_WINDOW_HSPLIT" ) ) );
   myWorkstackAction->setText( QtxWorkstackAction::SplitHorizontal, tr( "MEN_DESK_WINDOW_HSPLIT" ) );
   myWorkstackAction->setStatusTip( QtxWorkstackAction::SplitHorizontal, tr( "PRP_DESK_WINDOW_HSPLIT" ) );
-  myWorkstackAction->setAccel( QtxWorkstackAction::SplitHorizontal, Qt::ALT + Qt::SHIFT + Qt::Key_H );
 
   // Split Vertical
   myWorkstackAction->setIcon( QtxWorkstackAction::SplitVertical,
                               resMgr->loadPixmap( "STD", tr( "ICON_DESK_WINDOW_VSPLIT" ) ) );
   myWorkstackAction->setText( QtxWorkstackAction::SplitVertical, tr( "MEN_DESK_WINDOW_VSPLIT" ) );
   myWorkstackAction->setStatusTip( QtxWorkstackAction::SplitVertical, tr( "PRP_DESK_WINDOW_VSPLIT" ) );
-  myWorkstackAction->setAccel( QtxWorkstackAction::SplitVertical,   Qt::ALT + Qt::SHIFT + Qt::Key_V );
 
   QAction* anArrangeViewsAction = myWorkstackAction->getArrangeViewsAction();
   if( anArrangeViewsAction )
index 10c9f0079f6fe70e58149fb71da67dddfb2640e7..6c7c3f6af0a215c70d008c62d050b781df445c5c 100644 (file)
@@ -37,28 +37,28 @@ SET(_link_LIBRARIES ${PLATFORM_LIBS} ${QT_LIBRARIES} qtx ObjBrowser)
 # --- headers ---
 
 # header files / to be processed by moc
-SET(_moc_HEADERS   
-  SUIT_Accel.h  
-  SUIT_ActionOperation.h 
-  SUIT_Application.h 
-  SUIT_AutoRotate.h 
-  SUIT_DataBrowser.h 
-  SUIT_DataObject.h 
-  SUIT_Desktop.h 
-  SUIT_FileDlg.h 
+SET(_moc_HEADERS
+  SUIT_Accel.h
+  SUIT_ActionOperation.h
+  SUIT_Application.h
+  SUIT_AutoRotate.h
+  SUIT_DataBrowser.h
+  SUIT_DataObject.h
+  SUIT_Desktop.h
+  SUIT_FileDlg.h
   SUIT_LicenseDlg.h
   SUIT_MessageBox.h
-  SUIT_Operation.h 
-  SUIT_PopupClient.h 
-  SUIT_PreferenceMgr.h 
-  SUIT_SelectionMgr.h 
-  SUIT_Session.h 
+  SUIT_Operation.h
+  SUIT_PopupClient.h
+  SUIT_PreferenceMgr.h
+  SUIT_SelectionMgr.h
+  SUIT_Session.h
   SUIT_ShortcutMgr.h
   SUIT_Study.h
-  SUIT_TreeModel.h 
-  SUIT_ViewManager.h 
-  SUIT_ViewModel.h 
-  SUIT_ViewWindow.h 
+  SUIT_TreeModel.h
+  SUIT_ViewManager.h
+  SUIT_ViewModel.h
+  SUIT_ViewWindow.h
 )
 
 # header files / no moc processing
@@ -97,6 +97,7 @@ SET(_other_RESOURCES
   resources/icon_visibility_on.png
   resources/icon_visibility_off.png
   resources/view_sync.png
+  resources/action_assets.json
 )
 
 # --- sources ---
@@ -153,4 +154,3 @@ INSTALL(FILES ${suit_HEADERS} DESTINATION ${SALOME_INSTALL_HEADERS})
 QT_INSTALL_TS_RESOURCES("${_ts_RESOURCES}" "${SALOME_GUI_INSTALL_RES_DATA}")
 
 INSTALL(FILES ${_other_RESOURCES} DESTINATION ${SALOME_GUI_INSTALL_RES_DATA})
-
index fef1771b3e5bbba0bd0e0f355282b86f8908a0d8..17eab9ab13b41f11db013e66de5d5340ff12b3a5 100644 (file)
@@ -189,7 +189,7 @@ SUIT_ResourceMgr* SUIT_Application::resourceMgr() const
 */
 SUIT_ShortcutMgr* SUIT_Application::shortcutMgr() const
 {
-  return SUIT_ShortcutMgr::getShortcutMgr();
+  return SUIT_ShortcutMgr::get();
 }
 
 #define DEFAULT_MESSAGE_DELAY 3000
@@ -639,13 +639,14 @@ QList<int> SUIT_Application::actionIds() const
   \param toggle - if it is \c true the action will be a toggle action, otherwise it will be a command action
   \param reciever - object that contains slot
   \param member - slot to be called when action is activated
+  \param actionID - application-unique action ID. Required by SUIT_ShortcutMgr for shortcut customization. May be left blank.
 */
 QAction* SUIT_Application::createAction( const int id, const QString& text, const QIcon& icon,
                                          const QString& menu, const QString& tip, const int key,
-                                         QObject* parent, const bool toggle, QObject* reciever, 
-                                        const char* member, const QString& shortcutAction )
+                                         QObject* parent, const bool toggle, QObject* reciever,
+                                        const char* member, const QString& actionID )
 {
-  return createAction( id, text, icon, menu, tip, QKeySequence(key), parent, toggle, reciever, member, shortcutAction );
+  return createAction( id, text, icon, menu, tip, QKeySequence(key), parent, toggle, reciever, member, actionID );
 }
 
 /*!
@@ -661,13 +662,14 @@ QAction* SUIT_Application::createAction( const int id, const QString& text, cons
   \param toggle - if it is TRUE the action will be a toggle action, otherwise it will be a command action
   \param reciever - object that contains slot
   \param member - slot to be called when action is activated
+  \param actionID - application-unique action ID. Required by SUIT_ShortcutMgr for shortcut customization. May be left blank.
 */
 QAction* SUIT_Application::createAction( const int id, const QString& text, const QIcon& icon,
                                          const QString& menu, const QString& tip, const QKeySequence& key,
-                                         QObject* parent, const bool toggle, QObject* reciever, 
-                                        const char* member, const QString& shortcutAction )
+                                         QObject* parent, const bool toggle, QObject* reciever,
+                                        const char* member, const QString& actionID )
 {
-  QtxAction* a = new QtxAction( text, icon, menu, key, parent, toggle, shortcutAction );
+  QtxAction* a = new QtxAction( text, icon, menu, key, parent, toggle, actionID );
   a->setStatusTip( tip );
 
   if ( reciever && member )
@@ -678,6 +680,35 @@ QAction* SUIT_Application::createAction( const int id, const QString& text, cons
   return a;
 }
 
+/*!
+  Creates action and registers it both in menu manager and tool manager
+  \return new instance of action
+  \param id proposed SUIT identificator
+  \param parent parent object
+  \param toggle if it is TRUE the action will be a toggle action, otherwise it will be a command action
+  \param actionID application-unique action ID. Required by SUIT_ShortcutMgr for shortcut customization. May be left blank.
+  \param toolTip
+  \param menuText can be later retrieved using QAction::text();
+  \param statusTip
+  \param icon icon for toolbar
+  \param reciever object that contains slot
+  \param member slot to be called when action is activated
+*/
+QAction* SUIT_Application::createAction( const int id, QObject* parent, const bool toggle, const QString& actionID,
+                                         const QString& toolTip, const QString& menuText, const QString& statusTip, const QIcon& icon,
+                                         QObject* reciever, const char* member )
+{
+  QtxAction* a = new QtxAction( parent, toggle, actionID, toolTip, menuText, icon);
+  a->setStatusTip( statusTip );
+
+  if ( reciever && member )
+    connect( a, SIGNAL( triggered( bool ) ), reciever, member );
+
+  registerAction( id, a );
+
+  return a;
+}
+
 /*!
   Registers action both in menu manager and tool manager
   \param id - proposed SUIT identificator (if it is -1, auto generated one is used)
index 0a0aa1830c85c0ce021ec4700323a9091da2683a..cdc07a9715663ba44c2c9eba4a867bc5384e8d82 100644 (file)
@@ -27,8 +27,8 @@
 
 #include <QObject>
 #include <QMap>
+#include <QIcon>
 
-class QIcon;
 class QLabel;
 class QString;
 class QAction;
@@ -50,8 +50,8 @@ class SUIT_Study;
  */
 /*!
   An <b>Application</b> is a class which defines application configuration and behaviour.
-  For example Application object defines what Viewers are used in this application, what auxilliary windows
-  are present, how user can dial with them. Also Application object defines an sertain type of data structure by 
+  For example, Application object defines what Viewers are used in this application, what auxilliary windows
+  are present, how user can dial with them. Also Application object defines an certain type of data structure by
   holding of pointer on an instance of SUIT_Study class (which represents Document data structure). In other words
   Application defines type of sata structure, type of used Viewers, type of main GUI widget (Desktop),
   and other auxilliary tools.
@@ -68,7 +68,7 @@ public:
   //! Returns main widget (Desktop) of the application (if it exists)
   virtual SUIT_Desktop* desktop();
 
-  /*! Returns \c false if application can not be closed (because of non saved data for example). 
+  /*! Returns \c false if application can not be closed (because of non saved data for example).
       This method called by SUIT_Session whin closing of application was requested. */
   virtual bool          isPossibleToClose( bool& );
 
@@ -93,7 +93,7 @@ public:
   //! Creates new empty Study if active Study = 0
   virtual void          createEmptyStudy();
 
-  /*! Returns number of Studies. 
+  /*! Returns number of Studies.
    *  Must be redefined in Applications which support several studies for one Application instance. */
   virtual int           getNbStudies() const;
 
@@ -101,11 +101,11 @@ public:
 
   SUIT_ShortcutMgr*     shortcutMgr() const;
 
-  //! Puts the message to the status bar  
+  //! Puts the message to the status bar
   void                  putInfo ( const QString&, const int = 0 );
 
   //! Invokes application-specific "Open/Save File" dialog and returns the selected file name.
-  virtual QString       getFileName( bool open, const QString& initial, const QString& filters, 
+  virtual QString       getFileName( bool open, const QString& initial, const QString& filters,
                                      const QString& caption, QWidget* parent ) = 0;
 
   //! Invokes application-specific "Select Directory" dialog and returns the selected directory name.
@@ -156,7 +156,7 @@ protected:
   //! Creates a new Study instance. Must be redefined in new application according to its Study type.
   virtual SUIT_Study*   createNewStudy();
   virtual void          setActiveStudy( SUIT_Study* );
-  
+
   /** @name Set menu shown functions*/ //@{
   void                  setMenuShown( QAction*, const bool );
   void                  setMenuShown( const int, const bool );//@}
@@ -177,11 +177,14 @@ protected:
   QAction*              createAction( const int, const QString&, const QIcon&, const QString&,
                                       const QString&, const int, QObject* = 0,
                                       const bool = false, QObject* = 0, const char* = 0,
-                                     const QString& = QString() );
+                                                             const QString& = QString() );
   QAction*              createAction( const int, const QString&, const QIcon&, const QString&,
                                       const QString&, const QKeySequence&, QObject* = 0,
                                       const bool = false, QObject* = 0, const char* = 0,
-                                     const QString& = QString() );
+                                                             const QString& = QString() );
+  QAction*              createAction( const int, QObject*, const bool, const QString&,
+                                      const QString&, const QString&, const QString&, const QIcon& = QIcon(),
+                                      QObject* = nullptr, const char* = nullptr);
 
 protected slots:
   virtual void          onDesktopActivated();
index 0fd2e20e111ab04c7959cedfd04b8a94af4b6ee9..fa06180581967357638237140e1719a0eed26a46 100644 (file)
@@ -157,11 +157,8 @@ int SUIT_PreferenceMgr::addItem( const QString& title, const int pId,
   case DirList:
     item = new QtxPagePrefPathListItem( Qtx::PT_Directory, title, parent, sect, param );
     break;
-  case Shortcut:
-    item = new QtxPagePrefShortcutBtnsItem( title, parent, sect, param );
-    break;
   case ShortcutTree:
-    item = new QtxPagePrefShortcutTreeItem( title, parent, sect, param );
+    item = new QtxPagePrefShortcutTreeItem( parent );
     break;
   case BiColor:
     item = new QtxPagePrefBiColorItem( title, parent, sect, param );
index b47922521f00a437ff8091f3aa3683b0d01fe61b..515c3d18daa173553f7dd11c88452a05e2bf9cb2 100644 (file)
@@ -38,7 +38,7 @@ public:
   typedef enum { Auto, Space, Bool, Color, String, Selector,
                  DblSpin, IntSpin, Double, Integer,
                  GroupBox, Tab, Frame, Font, DirList, File,
-                Slider, Shortcut, ShortcutTree, BiColor, Background, Directory,                 
+                 Slider, Shortcut, ShortcutTree, BiColor, Background, Directory,                
                  UserDefined = 1000 } PrefItemType;
 
 public:
diff --git a/src/SUIT/SUIT_ShortcutMgr. ReadMe.md b/src/SUIT/SUIT_ShortcutMgr. ReadMe.md
new file mode 100644 (file)
index 0000000..d0cc5ad
--- /dev/null
@@ -0,0 +1,82 @@
+ # ShortcutMgr ReadMe
+
+Hot keys must be considered as resources, being shared between all components of an application. E.g. it is unacceptable to have 'Close file' and 'Redo' actions being assigned to the same key sequence. When the SHAPER module is active, the application desktop is active too. The desktop has own hot keys, and they must not interfere with ones of SHAPER. Since the task implies granting users a right to assign shortcuts on their will, the application must track all assigned shortcuts of all modules, prevent intolerable user shortcut modifications and govern actual binding of QActions with key sequences.
+
+`SUIT_ShortcutMgr` handles shortcuts of SALOME desktop and all modules. It is solely responsible and capable to dynamically bind actions with key sequences and (de)serialize shortcut preferences using `SUIT_ResourceMgr`. `SUIT_ShortcutContainer` encapsulates logics of conflict detecting and resolving. `QtxShortcutTree` widget provides GUI to change shortcut preferences conveniently: it allows to remap plenty of shortcuts without applying, displays conflict-resolving dialog, highlights modifications until they are applied (saved into preference files).
+
+To (de)serialize shortcut preferences without dependence on language environment, shortcuts must be stored as pairs {action ID, key sequence}, where action IDs must be application-unique.
+
+Since desktop shortcuts may also be changed and interfere with shortcuts of modules, `QtxShortcutTree` should always display desktop shortcuts and shortcuts of all modules altogether, even if some modules are inactive. It means, that `QtxShortcutTree` must be fed not only with shortcut data {action ID, key sequence}[], but also with dictionaries {action ID, action name}[]. `QtxShortcutTree` also requires other action assets - tool tip and icon path.
+
+Assets of actions may be retrieved from instances of actions, but there is a pitfall: if a module has not been activated yet, its actions have not been initialized either.
+Qt Linguist is no help in this case too. To retrieve an action name using `QObject::tr(actionID)`, the `tr(const char*)` method must be called with instance of the class, which is designated as a context for the actionID in *.ts files. And contexts are usually descendants of SUIT_Application and CAM_Module. Again, until a module instance is created, there is no way for `SUIT_ShortcutMgr` to get even a name of a context-class, which an action with an ID belongs to, without any additional data. Straightforward mechanism for loading of action assets in advance has been devised: for all actions, which are bound by default or may be bound by user to hotkeys, assets must be placed into asset files. People who do/refine localizations should keep this in mind and also process JSON files, which are referred in resource files in sections `<section name="action_assets">`.
+
+To alleviate process of composing resource and asset files, a development tool `DevTools` has been made. It grabs all shortcuts and assets (except icon path) of intercepted at runtime actions with valid IDs and dumps shortcuts into XML files with identical to project-conventional resource files structure and assets into properly-formatted JSON files. Run modules/features of interest, switch application language, and if IDs or names in the selected language of some actions of the module are not added yet to preference files, these dump files will be appended with new data – shortcuts and assets, if the last ones are provided in *.ts files.
+The tool also logs assets of intercepted actions with invalid IDs. The intent is as follows: run modules/features of interest at several languages in exactly the same sequence of actions, paste content of resulting language-dedicated *.csv files to corresponding sheet of  [“ShortcutMgr. Resource generator.xlsx”](../../tools/DevTools/ShortcutMgr/ShortcutMgr. Resource generator.xlsx). Then come up with proper IDs for the actions, type the action IDs and their module IDs into corresponding columns and take away ready resource and asset items.
+
+Shortcuts and assets for all actions of SHAPER and GEOM and those actions of desktop, which were bound (hardcoded) to non-empty key sequences, have been added to resource files. Now they are available for hot key remapping via GUI, no conflicts guaranteed. Any hardcoded shortcut is disabled, unless the same shortcut persists in resource files.
+
+## Possible conflicts between shortcuts of desktop and modules, except SHAPER and GEOM
+
+Most of remaining actions can not be made available for shortcut customization without further meticulous inspection of all other modules' code, devising and typing proper action IDs for all such occurrences.
+
+Since desktop shortcuts are allowed for remapping, user can end up with conflicting shortcuts of desktop actions and those actions of unprocessed modules (all, except SHAPER and GEOM), which are hardcoded to non-empty key sequences.
+
+Now actions with invalid IDs are disabled by `SUIT_ShortcutMgr` at runtime and not available for binding neither using UI, neither by editing of preference file. There are four approaches how to handle this:
+
+1. Keep as is: user can remap shortcuts of desktop, SHAPER and GEOM - no conflicts guaranteed, hardcoded (now means all) shortcuts of other modules are disabled.
+
+2. Go through unprocessed repositories and add valid ID in every occurrence of action creation. Or at least only for those invalid-ID actions, which are currently assigned non-empty key sequences. It should not be just dumb slapping of unique IDs: some actions comprise meta-action (see `SUIT_ShortcutMgr` class documentation) and must get the same ID, e.g. “Undo”, and some actions with similar names do absolutely unrelated things and must be given with different IDs.
+
+3. Comment the line in `SUIT_ShortcutMgr` code, which disables shortcuts of inavlid-ID actions. At that rate, users will get back all the shortcuts of unprocessed modules they have accustomed to, but since these actions can not be taken into account during conflict detection,
+users may face with shortcut interference, if a desktop action is bound to the same key sequence as a hardcoded shortcut from unprocessed module.
+
+4. Forbid user to edit shortcuts of desktop and shortcuts of unprocessed modules. When another module will be processed, allow to modify its shortcuts. When all modules will be processed, make desktop shortcuts available for customization.
+
+## Issue with ampersand-shortcuts
+
+`QPushButton::QPushButton(const QString&)`, being fed with an action name with an ampersand preceding a char, automatically creates a shortcut, bound to *Alt+\<char>* key sequence.
+
+E.g. `auto button = QPushButton(tr("&Help")` may create language-depdendent shortcuts. Suppose that \*_fr.ts file contains this:
+```
+<message>
+       <source>&amp;Help</source>
+       <translation>A&amp;ide</translation>
+<message>
+
+```
+
+Then help menu has different shortcuts if the application is run in EN or FR.
+
+**A part of the issue** is that some ampersand-shortcuts interfere between each other. You can witness this in master-native application. Switch language to FR, run the app, click on the area, where viewers appear, and stroke *Alt+A*. You will get a Qt-generated notification *"Ambiguous shortcut detected"*, because *Alt+A* is bound to *"New ParaView window"* and to *"Affichage"* simultaneously. If you stroke the sequence again, *"Affichage"* menu expands, since the button tray, where the menu-button resides, has gained focus.
+
+**Another part of the issue** is that ampersand-shortcuts are not always intercepted by `SUIT_ShortcutMgr::eventFilter(_)`. Normally, if the manager catches an unregistered `QAction`, it disables the action' shortcut. But, non-intercepted ampersand-shortcut remains enabled, and, if user binds some action to the same key sequence in the shortcut editor, the editor is not able to detect conflict and prevent interfering binding. Then user faces with the *"Ambiguous shortcut detected"* notification or button-menu is opened instead of executing the action, mapped to the key sequence in shortcut editor.
+
+**The question is how to handle ampersand-shortcuts?**
+1. Resolve conflicts between ampersand-shortcuts for all localizations. Prohibit binding actions to *Alt+\<char>* key sequences in shortcut editor.
+2. After every occurrence of `QPushButton` creation type something like this:
+```
+// Occurrence
+const auto helpButton = new QPushButton(tr("&Help");
+SUIT_ShortcutMgr::get()->registerButtonActions("/#AltHelp", *helpButton);
+
+----------------------------------------------------
+// SUIT_ShortcutMgr.cpp
+void SUIT_ShortcutMgr::registerButtonActions(const QString& theActionID, const QPushButton& theButton) const {
+       for (QAction* const action : theButton.actions()) {
+               registerAction(theActionID, action);
+       }
+}
+----------------------------------------------------
+// Resource file
+       <section name="shortcuts:">
+               <parameter name="#AltHelp" value="Alt+H"/>
+       </section>
+```
+Thus, ampersand-shortcuts will appear and be treated in shortcut editor as regular shortcuts.
+If the second option is preferable, should different ampersand-shortcuts for every target language be placed in resource files?
+
+## Minor issues
+1. `QtxShortcutTree` widget does not take the whole available height of preference window, it only takes as mush as its items require.
+2. Selection of `QtxShortcutTree`' item shadows "modified" highlighter. Can be fixed by replacing base `QTreeWidget` of `QtxShortcutTree` with `QTreeView`, or may be by applying some style sheet.
+3. `SUIT_ShortcutMgr` introduces concept of module, but the first module class is `CAM_Module` is introduced along with `CAM_Application`, which is descendant of `SUIT_Application`.
index 41b6d8b2a2bd96f0dff285b8e27fd4d3adfa11cb..bfae01ec37a16085c86d705c67fba665d7801f2e 100644 (file)
 
 #include "SUIT_Session.h"
 #include "SUIT_ResourceMgr.h"
+#include "SUIT_MessageBox.h"
 
+#include <QAction>
 #include <QtxAction.h>
 
 #include <QApplication>
 #include <QActionEvent>
+#include <QKeySequence>
+#include <QDebug>
+#include <QJsonObject>
+#include <QJsonDocument>
+#include <QJsonParseError>
+#include <QFile>
+#include <QProcessEnvironment>
+#include <QRegExp>
+#include <Qtx.h>
 
-SUIT_ShortcutMgr* SUIT_ShortcutMgr::myShortcutMgr = NULL;
+#include <list>
 
-/*!
-  \brief Constructor
+
+#include <iostream>
+#include <string>
+const std::wstring SHORTCUT_MGR_LOG_PREFIX = L"SHORTCUT_MGR_DBG: ";
+bool ShCutDbg(const QString& theString)
+{
+  if (ShCutDbg()) {
+    std::wcout << SHORTCUT_MGR_LOG_PREFIX << theString.toStdWString() << std::endl;
+    return true;
+  }
+  return false;
+}
+bool ShCutDbg(const char* src)
+{
+  if (ShCutDbg()) {
+    std::wcout << SHORTCUT_MGR_LOG_PREFIX << std::wstring(src, src + strlen(src)) << std::endl;
+    return true;
+  }
+  return false;
+}
+
+void Warning(const QString& theString)
+{
+  std::wcout << theString.toStdWString() << std::endl;
+}
+void Warning(const char* src)
+{
+  std::wcout << std::wstring(src, src + strlen(src)) << std::endl;
+}
+
+
+static const QKeySequence NO_KEYSEQUENCE = QKeySequence(QString(""));
+static const QString NO_ACTION = QString("");
+/** Separates tokens in action ID. */
+static const QString TOKEN_SEPARATOR = QString("/");
+static const QString ROOT_MODULE_ID = QString("");
+static const QString META_ACTION_PREFIX = QString("#");
+
+/** Prefix of names of shortcut setting sections in preference files. */
+static const QString SECTION_NAME_PREFIX = QString("shortcuts");
+
+
+const QString DEFAULT_LANG = QString("en");
+const QStringList LANG_PRIORITY_LIST = QStringList({DEFAULT_LANG, "fr"});
+const QString LANG_SECTION = QString("language");
+
+static const QString SECTION_NAME_ACTION_ASSET_FILE_PATHS = QString("action_assets");
+
+
+
+/**
+ * Uncomment this, to start collecting all shortcuts and action assets (1),
+ * from instances of QtxActions, if a shortcut or action assets are absent in resource/asset files.
+ *
+ * (1) Set required language in the application settings and run features of interest.
+ * For all actions from these features, their assets will be dumped to appropriate places in dump files.
+ *
+ * Content of dump files is appended on every run. Files are located in "<APP_DIR>/shortcut_mgr_dev/".
 */
+// #define SHORTCUT_MGR_DEVTOOLS
+#ifdef SHORTCUT_MGR_DEVTOOLS
+#include <QDir>
+#include <QFile>
+#include <QFileInfo>
+#include <QTextStream>
+#include "QtxMap.h"
+#include <functional>
+#ifndef QT_NO_DOM
+#include <QDomDocument>
+#include <QDomElement>
+#include <QDomNode>
+#endif // QT_NO_DOM
+
+/*! \brief Generates XML files with appearing at runtime shortcuts,
+    using key sequences of QActions passed to the shortcut manager,
+    and JSON files with assets of QtxActions passed to the shortcut manager.
+    Content of these files can be easily copied to resource/asset files. */
+class DevTools
+{
+private:
+  DevTools() : myActionsWithInvalidIDsFile(nullptr) {};
+  DevTools(const DevTools&) = delete;
+  void operator=(const DevTools&) = delete;
+
+public:
+  ~DevTools()
+  {
+    for (const auto& fileNameAndPtrs : myXMLFilesAndDocs) {
+      delete fileNameAndPtrs.second.second;
+      delete fileNameAndPtrs.second.first;
+    }
+
+    for (const auto& fileNameAndPtrs : myJSONFilesAndDocs) {
+      delete fileNameAndPtrs.second.second;
+      delete fileNameAndPtrs.second.first;
+    }
+  }
+
+  static DevTools* get() {
+    if (!DevTools::instance)
+      DevTools::instance = new DevTools();
+
+    return DevTools::instance;
+  }
+
+  void collectShortcut(
+    const QString& theModuleID,
+    const QString& theInModuleActionID,
+    const QKeySequence& theKeySequence
+  ) {
+    if (SUIT_ShortcutMgr::isInModuleMetaActionID(theInModuleActionID)) {
+      auto& moduleShortcuts = myShortcutsOfMetaActions[theModuleID];
+      moduleShortcuts[theInModuleActionID] = theKeySequence.toString();
+
+      const QString fileName = theModuleID + DevTools::SHORTCUTS_OF_META_SUFFIX;
+      const QString sectionName = SECTION_NAME_PREFIX + DevTools::XML_SECTION_TOKENS_SEPARATOR + ROOT_MODULE_ID;
+      std::map<QString, std::map<QString, QString>> sections;
+      sections[sectionName] = moduleShortcuts;
+      writeToXMLFile(fileName, sections);
+    }
+    else {
+      auto& moduleShortcuts = myShortcuts[theModuleID];
+      moduleShortcuts[theInModuleActionID] = theKeySequence.toString();
+
+      const QString fileName = theModuleID + DevTools::SHORTCUTS_SUFFIX;
+      const QString sectionName = SECTION_NAME_PREFIX + DevTools::XML_SECTION_TOKENS_SEPARATOR + theModuleID;
+      std::map<QString, std::map<QString, QString>> sections;
+      sections[sectionName] = moduleShortcuts;
+      writeToXMLFile(fileName, sections);
+    }
+  }
+
+  void collectAssets(
+    const QString& theModuleID,
+    const QString& theInModuleActionID,
+    const QString& theLang,
+    const QAction* theAction
+  ) {
+    if (SUIT_ShortcutMgr::isInModuleMetaActionID(theInModuleActionID)) {
+      QString actionID = SUIT_ShortcutMgr::makeActionID(ROOT_MODULE_ID, theInModuleActionID);
+      // { actionID, assets } []
+      auto& moduleAssets = myAssetsOfMetaActions[theModuleID];
+
+      auto& actionAssets = moduleAssets[actionID];
+      actionAssets.myLangDependentAssets[theLang].myName = theAction->text();
+      actionAssets.myLangDependentAssets[theLang].myToolTip = theAction->statusTip();
+
+      const QString fileName = theModuleID + DevTools::ASSETS_OF_META_SUFFIX;
+      writeToJSONFile(fileName, actionID, actionAssets);
+    }
+    else {
+      QString actionID = SUIT_ShortcutMgr::makeActionID(theModuleID, theInModuleActionID);
+      // { actionID, assets } []
+      auto& moduleAssets = myAssets[theModuleID];
+
+      auto& actionAssets = moduleAssets[actionID];
+      actionAssets.myLangDependentAssets[theLang].myName = theAction->text();
+      actionAssets.myLangDependentAssets[theLang].myToolTip = theAction->statusTip();
+
+      const QString fileName = theModuleID + DevTools::ASSETS_SUFFIX;
+      writeToJSONFile(fileName, actionID, actionAssets);
+    }
+  }
+
+  void collectShortcutAndAssets(const QtxAction* const theAction)
+  {
+    const auto moduleIDAndActionID = SUIT_ShortcutMgr::splitIntoModuleIDAndInModuleID(theAction->ID());
+    if (moduleIDAndActionID.second.isEmpty())
+      return;
+
+    if (!SUIT_ShortcutMgr::get()->getShortcutContainer().hasShortcut(moduleIDAndActionID.first, moduleIDAndActionID.second))
+      collectShortcut(moduleIDAndActionID.first, moduleIDAndActionID.second, theAction->shortcut());
+
+    { // Collect action assets, if they are not provided in asset files.
+      SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
+      if (!resMgr) {
+        Warning("DevTools for SUIT_ShortcutMgr can't retrieve resource manager!");
+        return;
+      }
+
+      const QString lang = resMgr->stringValue(LANG_SECTION, LANG_SECTION);
+      if (lang.isEmpty())
+        return;
+
+      const auto& assetsInResources = SUIT_ShortcutMgr::getActionAssetsFromResources(theAction->ID());
+      if (assetsInResources.first && assetsInResources.second.myLangDependentAssets.find(lang) != assetsInResources.second.myLangDependentAssets.end())
+        return;
+
+      collectAssets(moduleIDAndActionID.first, moduleIDAndActionID.second, lang, theAction);
+    }
+  }
+
+private:
+  /*! Appends new entries to content of dump files. */
+  bool writeToXMLFile(const QString& theFileName, const std::map<QString, std::map<QString, QString>>& theSections)
+  {
+#ifdef QT_NO_DOM
+  Warning("DebugTools for SUIT_ShortcutMgr can't create XML - #QT_NO_DOM is defined.");
+  return false;
+#else QT_NO_DOM
+    static const QString DOC_TAG = "document";
+    static const QString SECTION_TAG = "section";
+    static const QString PARAMETER_TAG = "parameter";
+    static const QString NAME_ATTR = "name";
+    static const QString VAL_ATTR = "value";
+
+    const auto itFileAndDoc = myXMLFilesAndDocs.find(theFileName);
+    if (itFileAndDoc == myXMLFilesAndDocs.end()) {
+      const QString fullPath = DevTools::SAVE_PATH + theFileName + ".xml";
+      if (!Qtx::mkDir(QFileInfo(fullPath).absolutePath())) {
+        myXMLFilesAndDocs[theFileName] = std::pair<QFile*, QDomDocument*>(nullptr, nullptr);
+        return false;
+      }
+
+      QFile* file = new QFile(fullPath);
+      if (!file->open(QFile::ReadWrite | QIODevice::Text)) {
+        delete file;
+        myXMLFilesAndDocs[theFileName] = std::pair<QFile*, QDomDocument*>(nullptr, nullptr);
+        return false;
+      }
+
+      QDomDocument* dom = new QDomDocument(DOC_TAG);
+      QTextStream instream(file);
+      dom->setContent(instream.readAll());
+      myXMLFilesAndDocs[theFileName] = std::pair<QFile*, QDomDocument*>(file, dom);
+    }
+    else if (itFileAndDoc->second.first == nullptr) {
+      return false;
+    }
+
+    const auto fileAndDom = myXMLFilesAndDocs[theFileName];
+    QFile* const file = fileAndDom.first;
+    QDomDocument* const dom = fileAndDom.second;
+
+    QDomElement doc = dom->documentElement();
+    if (doc.isNull()) {
+      *dom = QDomDocument(DOC_TAG);
+      doc = dom->createElement(DOC_TAG);
+      dom->appendChild(doc);
+    }
+
+    static const std::function<void(const std::map<QString, QString>&, QDomDocument&, QDomElement&)> mergeParamsToSection =
+    [&](const std::map<QString, QString>& parameters, QDomDocument& dom, QDomElement& sectionInDom)
+    {
+      for (const std::pair<QString, QString>& nameAndVal : parameters) {
+        const QString& paramName = nameAndVal.first;
+        const QString& paramVal = nameAndVal.second;
+        bool fileHasParam = false;
+        for (QDomElement paramInDom = sectionInDom.firstChildElement(PARAMETER_TAG); !paramInDom.isNull(); paramInDom = paramInDom.nextSiblingElement(PARAMETER_TAG)) {
+          const QString paramNameInDom = paramInDom.attribute(NAME_ATTR);
+          if (paramName == paramNameInDom) {
+            const QString paramValInDom = paramInDom.attribute(VAL_ATTR);
+            if (paramValInDom != paramVal) {
+              QDomElement replaceElement = dom.createElement(PARAMETER_TAG);
+              replaceElement.setAttribute(NAME_ATTR, paramName);
+              replaceElement.setAttribute(VAL_ATTR, paramVal);
+              sectionInDom.replaceChild(replaceElement, paramInDom);
+            }
+
+            fileHasParam = true;
+            break;
+          }
+        }
+        if (!fileHasParam) {
+          QDomElement newParam = dom.createElement(PARAMETER_TAG);
+          newParam.setAttribute(NAME_ATTR, paramName);
+          newParam.setAttribute(VAL_ATTR, paramVal);
+          sectionInDom.insertAfter(newParam, sectionInDom.lastChildElement(PARAMETER_TAG));
+        }
+      }
+      return;
+    };
+
+    for (const auto& sectionNameAndParams : theSections) {
+      const QString& sectionName = sectionNameAndParams.first;
+      const std::map<QString, QString>& parameters = sectionNameAndParams.second;
+
+      bool fileHasSection = false;
+      for (QDomElement sectionInDom = doc.firstChildElement(SECTION_TAG); !sectionInDom.isNull(); sectionInDom = sectionInDom.nextSiblingElement(SECTION_TAG)) {
+        QString sectionNameInDom = sectionInDom.attribute(NAME_ATTR);
+        if (sectionNameInDom == sectionName) {
+          mergeParamsToSection(parameters, *dom, sectionInDom);
+          fileHasSection = true;
+          break;
+        }
+      }
+
+      if (!fileHasSection) {
+        QDomElement newSection = dom->createElement(SECTION_TAG);
+        newSection.setAttribute(NAME_ATTR, sectionName);
+        doc.insertAfter(newSection, doc.lastChildElement(SECTION_TAG));
+        mergeParamsToSection(parameters, *dom, newSection);
+      }
+    }
+
+    file->resize(0);
+    QTextStream outstream(file);
+    outstream << dom->toString();
+
+    return true;
+#endif // QT_NO_DOM
+  }
+
+  /*! Appends new entries to content of dump files. */
+  bool writeToJSONFile(const QString& theFileName, const QString& theActionID, const SUIT_ActionAssets& theAssets)
+  {
+    const auto itFileAndDoc = myJSONFilesAndDocs.find(theFileName);
+    if (itFileAndDoc == myJSONFilesAndDocs.end()) {
+      const QString fullPath = DevTools::SAVE_PATH + theFileName + ".json";
+      if (!Qtx::mkDir(QFileInfo(fullPath).absolutePath())) {
+        myJSONFilesAndDocs[theFileName] = std::pair<QFile*, QJsonDocument*>(nullptr, nullptr);
+        return false;
+      }
+
+      const bool fileExisted = QFileInfo::exists(fullPath);
+      QFile* file = new QFile(fullPath);
+      if (!file->open(QFile::ReadWrite | QIODevice::Text)) {
+        delete file;
+        myJSONFilesAndDocs[theFileName] = std::pair<QFile*, QJsonDocument*>(nullptr, nullptr);
+        return false;
+      }
+
+      QJsonParseError jsonError;
+      QJsonDocument* document = new QJsonDocument(QJsonDocument::fromJson(file->readAll(), &jsonError));
+      if (jsonError.error != QJsonParseError::NoError && fileExisted) {
+        Warning("SUIT_ShortcutMgr: error during parsing of action asset dump file \"" + fullPath + "\"!");
+        delete file;
+        delete document;
+        myJSONFilesAndDocs[theFileName] = std::pair<QFile*, QJsonDocument*>(nullptr, nullptr);
+        return false;
+      }
+
+      if (!document->isObject()) {
+        document->setObject(QJsonObject());
+        file->resize(0);
+        QTextStream outstream(file);
+        outstream << document->toJson(QJsonDocument::Indented);
+      }
+
+      myJSONFilesAndDocs[theFileName] = std::pair<QFile*, QJsonDocument*>(file, document);
+    }
+    else if (itFileAndDoc->second.first == nullptr) {
+      return false;
+    }
+
+    const auto fileAndDoc = myJSONFilesAndDocs[theFileName];
+    QFile* const file = fileAndDoc.first;
+    QJsonDocument* const document = fileAndDoc.second;
+
+    QJsonObject rootJSON = document->object();
+    QJsonObject actionAssetsJSON = rootJSON[theActionID].toObject();
+    SUIT_ActionAssets actionAssets;
+    actionAssets.fromJSON(actionAssetsJSON);
+    actionAssets.merge(theAssets, true /*theOverride*/);
+    actionAssets.toJSON(actionAssetsJSON);
+    rootJSON[theActionID] = actionAssetsJSON;
+    document->setObject(rootJSON);
+
+    file->resize(0);
+    QTextStream outstream(file);
+    outstream << document->toJson(QJsonDocument::Indented);
+
+    return true;
+  }
+
+public:
+  void collectAssetsOfActionWithInvalidID(const QAction* const theAction)
+  {
+    SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
+    if (!resMgr) {
+      Warning("DevTools for SUIT_ShortcutMgr can't retrieve resource manager!");
+      return;
+    }
+
+    const QString lang = resMgr->stringValue(LANG_SECTION, LANG_SECTION);
+    if (lang.isEmpty())
+      return;
+
+    if (!myActionsWithInvalidIDsFile) {
+      const QString fullPath = DevTools::SAVE_PATH + lang + DevTools::INVALID_ID_ACTIONS_SUFFIX + ".csv";
+      if (!Qtx::mkDir(QFileInfo(fullPath).absolutePath()))
+        return;
+
+      myActionsWithInvalidIDsFile = new QFile(fullPath);
+      if (!myActionsWithInvalidIDsFile->open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {
+        delete myActionsWithInvalidIDsFile;
+        myActionsWithInvalidIDsFile = nullptr;
+        return;
+      }
+
+      QTextStream ostream(myActionsWithInvalidIDsFile);
+      ostream << "text\t" << "tool tip\t" << "status tip\t" << "key sequence\t" << "QtxAction?\t" << "ID\n";
+      ostream.flush();
+    }
+
+    QTextStream ostream(myActionsWithInvalidIDsFile);
+    const auto aQtxAction = qobject_cast<const QtxAction*>(theAction);
+    ostream << theAction->text() << "\t" << theAction->toolTip() << "\t" << theAction->statusTip() << "\t"
+    << theAction->shortcut().toString() << "\t" << (aQtxAction ? "yes\t" : "no\t") << (aQtxAction ? aQtxAction->ID() + "\n" : "\n");
+    ostream.flush();
+  }
+
+  static const QString SAVE_PATH;
+  static const QString SHORTCUTS_SUFFIX;
+  static const QString SHORTCUTS_OF_META_SUFFIX;
+  static const QString ASSETS_SUFFIX;
+  static const QString ASSETS_OF_META_SUFFIX;
+  static const QString INVALID_ID_ACTIONS_SUFFIX;
+
+  static DevTools* instance;
+  static const QString XML_SECTION_TOKENS_SEPARATOR;
+
+  /** { moduleID, { inModuleActionID, keySequence }[] }[]. keySequence can be empty. */
+  std::map<QString, std::map<QString, QString>> myShortcuts;
+
+  /** { moduleID, { inModuleActionID, keySequence }[] }[]. keySequence can be empty. */
+  std::map<QString, std::map<QString, QString>> myShortcutsOfMetaActions;
+
+  /** { moduleID, { actionID, assets }[] }[] */
+  std::map<QString, std::map<QString, SUIT_ActionAssets>> myAssets;
+
+  /** { moduleID, { actionID, assets }[] }[] */
+  std::map<QString, std::map<QString, SUIT_ActionAssets>> myAssetsOfMetaActions;
+
+#ifndef QT_NO_DOM
+  // { filename, {file, domDoc} }[]
+  std::map<QString, std::pair<QFile*, QDomDocument*>> myXMLFilesAndDocs;
+#endif // QT_NO_DOM
+  // { filename, {file, jsonDoc} }[]
+  std::map<QString, std::pair<QFile*, QJsonDocument*>> myJSONFilesAndDocs;
+
+  QFile* myActionsWithInvalidIDsFile;
+};
+/*static*/ DevTools* DevTools::instance = nullptr;
+/*static*/ const QString DevTools::SAVE_PATH = "shortcut_mgr_dev/";
+/*static*/ const QString DevTools::INVALID_ID_ACTIONS_SUFFIX = "_actions_with_invalid_IDs";
+/*static*/ const QString DevTools::XML_SECTION_TOKENS_SEPARATOR = ":";
+/*static*/ const QString DevTools::SHORTCUTS_SUFFIX = "_shortcuts";
+/*static*/ const QString DevTools::SHORTCUTS_OF_META_SUFFIX = "_shortcuts_of_meta_actions";
+/*static*/ const QString DevTools::ASSETS_SUFFIX = "_assets";
+/*static*/ const QString DevTools::ASSETS_OF_META_SUFFIX = "_assets_of_meta_actions";
+#endif // SHORTCUT_MGR_DEVTOOLS
+
+
+
+SUIT_ShortcutContainer::SUIT_ShortcutContainer()
+{
+  myShortcuts.emplace(ROOT_MODULE_ID, std::map<QKeySequence, QString>());
+  myShortcutsInversed.emplace(ROOT_MODULE_ID, std::map<QString, QKeySequence>());
+}
+
+std::set<QString> SUIT_ShortcutContainer::getIDsOfInterferingModules(const QString& theModuleID) const
+{
+  std::set<QString> IDsOfInterferingModules;
+  if (theModuleID == ROOT_MODULE_ID) {
+    for (const auto& moduleIDAndShortcuts : myShortcuts) {
+      IDsOfInterferingModules.emplace(moduleIDAndShortcuts.first);
+    }
+  }
+  else {
+    IDsOfInterferingModules.emplace(ROOT_MODULE_ID);
+    if (theModuleID != ROOT_MODULE_ID)
+      IDsOfInterferingModules.emplace(theModuleID);
+  }
+  return IDsOfInterferingModules;
+}
+
+std::set<QString> SUIT_ShortcutContainer::getIDsOfAllModules() const
+{
+  std::set<QString> res;
+  for (const auto& moduleIDAndShortcuts : myShortcutsInversed) {
+    res.emplace(moduleIDAndShortcuts.first);
+  }
+  return res;
+}
+
+std::set<std::pair<QString, QString>> SUIT_ShortcutContainer::setShortcut(QString theModuleID, const QString& theInModuleActionID, const QKeySequence& theKeySequence, bool theOverride)
+{
+  if (!SUIT_ShortcutMgr::isModuleIDValid(theModuleID)) {
+    ShCutDbg() && ShCutDbg("Attempt to define a shortcut using invalid module ID = \"" + theModuleID + "\".");
+    return std::set<std::pair<QString, QString>>();
+  }
+
+  if (!SUIT_ShortcutMgr::isInModuleActionIDValid(theInModuleActionID)) {
+    ShCutDbg() && ShCutDbg("Attempt to define a shortcut using invalid in-module action ID = \"" + theInModuleActionID + "\".");
+    return std::set<std::pair<QString, QString>>();
+  }
+
+  if (SUIT_ShortcutMgr::isInModuleMetaActionID(theInModuleActionID))
+    theModuleID = ROOT_MODULE_ID;
+
+  auto itModuleShortcuts = myShortcuts.find(theModuleID);
+  auto itModuleShortcutsInversed = myShortcutsInversed.find(theModuleID);
+  if (itModuleShortcuts == myShortcuts.end()) {
+    itModuleShortcuts = myShortcuts.emplace(theModuleID, std::map<QKeySequence, QString>()).first;
+    itModuleShortcutsInversed = myShortcutsInversed.emplace(theModuleID, std::map<QString, QKeySequence>()).first;
+  }
+
+  std::map<QKeySequence, QString>& moduleShortcuts = itModuleShortcuts->second;
+  std::map<QString, QKeySequence>& moduleShortcutsInversed = itModuleShortcutsInversed->second;
+
+  if (theKeySequence.isEmpty()) {
+    // Disable shortcut.
+
+    auto itShortcutInversed = moduleShortcutsInversed.find(theInModuleActionID);
+    if (itShortcutInversed == moduleShortcutsInversed.end()) {
+      // No key sequence was mapped to the action earlier.
+      // Set disabled shortcut.
+      moduleShortcutsInversed.emplace(theInModuleActionID, NO_KEYSEQUENCE);
+      return std::set<std::pair<QString, QString>>();
+    }
+    else /* if keySequence was mapped to the action earlier. */ {
+      QKeySequence& keySequence = itShortcutInversed->second;
+
+      moduleShortcuts.erase(keySequence);
+      keySequence = NO_KEYSEQUENCE;
+
+      return std::set<std::pair<QString, QString>>();
+    }
+  }
+
+  { // Check if the shortcut is already set.
+    const auto itShortcut = moduleShortcuts.find(theKeySequence);
+    if (itShortcut != moduleShortcuts.end()) {
+      if (itShortcut->second == theInModuleActionID) {
+        // The shortcut was set earlier. Nothing to change.
+        return std::set<std::pair<QString, QString>>();
+      }
+    }
+  }
+
+  auto conflictingActionIDs = std::set<std::pair<QString, QString>>();
+  { // Look for conflicting shortcuts with the same key sequence from interfering modules.
+    std::set<QString> IDsOfInterferingModules = getIDsOfInterferingModules(theModuleID);
+    for (const QString& IDOfInterferingModule : IDsOfInterferingModules) {
+      std::map<QKeySequence, QString>& shortcutsOfInterferingModule = myShortcuts.at(IDOfInterferingModule);
+      auto itConflictingShortcut = shortcutsOfInterferingModule.find(theKeySequence);
+      if (itConflictingShortcut != shortcutsOfInterferingModule.end()) {
+        const QString& conflictingActionID = itConflictingShortcut->second;
+
+        conflictingActionIDs.insert(std::pair<QString, QString>(IDOfInterferingModule, conflictingActionID));
+
+        if (theOverride) {
+          // Disable conflicting shortcuts.
+          std::map<QString, QKeySequence>& shortcutsOfInterferingModuleInversed = myShortcutsInversed.at(IDOfInterferingModule);
+          shortcutsOfInterferingModuleInversed[conflictingActionID] = NO_KEYSEQUENCE;
+          shortcutsOfInterferingModule.erase(itConflictingShortcut);
+        }
+      }
+    }
+
+    if (!theOverride && !conflictingActionIDs.empty())
+      return conflictingActionIDs;
+  }
+
+  { // Ensure, that the module has only shortcut for the action ID.
+    auto itShortcutInversed = moduleShortcutsInversed.find(theInModuleActionID);
+    if (itShortcutInversed != moduleShortcutsInversed.end()) {
+      // Redefine key sequence for existing action.
+
+      QKeySequence& keySequence = itShortcutInversed->second;
+
+      moduleShortcuts.erase(keySequence);
+      moduleShortcuts[theKeySequence] = theInModuleActionID;
+
+      keySequence = theKeySequence;
+    }
+    else /* if the action has not been added earlier. */ {
+      moduleShortcuts[theKeySequence] = theInModuleActionID;
+      moduleShortcutsInversed[theInModuleActionID] = theKeySequence;
+    }
+  }
+
+  return conflictingActionIDs;
+}
+
+std::set<std::pair<QString, QString>> SUIT_ShortcutContainer::getConflicts(
+  QString theModuleID,
+  const QString& theInModuleActionID,
+  const QKeySequence& theKeySequence
+) const
+{
+  if (theKeySequence.isEmpty())
+    return std::set<std::pair<QString, QString>>();
+
+  if (SUIT_ShortcutMgr::isInModuleMetaActionID(theInModuleActionID))
+    theModuleID = ROOT_MODULE_ID;
+
+  { // Check if the shortcut is set.
+    const auto itModuleShortcuts = myShortcuts.find(theModuleID);
+    if (itModuleShortcuts != myShortcuts.end()) {
+      const std::map<QKeySequence, QString>& moduleShortcuts = itModuleShortcuts->second;
+      const auto itShortcut = moduleShortcuts.find(theKeySequence);
+      if (itShortcut != moduleShortcuts.end()) {
+        if (itShortcut->second == theInModuleActionID) {
+          // The shortcut is set => no conflicts.
+          return std::set<std::pair<QString, QString>>();
+        }
+      }
+    }
+  }
+
+  auto conflictingActionIDs = std::set<std::pair<QString, QString>>();
+  { // Look for conflicting shortcuts with the same key sequence from interfering modules.
+    std::set<QString> IDsOfInterferingModules = getIDsOfInterferingModules(theModuleID);
+    for (const QString& IDOfInterferingModule : IDsOfInterferingModules) {
+      const std::map<QKeySequence, QString>& shortcutsOfInterferingModule = myShortcuts.at(IDOfInterferingModule);
+      const auto itConflictingShortcut = shortcutsOfInterferingModule.find(theKeySequence);
+      if (itConflictingShortcut != shortcutsOfInterferingModule.end()) {
+        const QString& conflictingActionID = itConflictingShortcut->second;
+        conflictingActionIDs.insert(std::pair<QString, QString>(IDOfInterferingModule, conflictingActionID));
+      }
+    }
+  }
+  return conflictingActionIDs;
+}
+
+const QKeySequence& SUIT_ShortcutContainer::getKeySequence(QString theModuleID, const QString& theInModuleActionID) const
+{
+  if (SUIT_ShortcutMgr::isInModuleMetaActionID(theInModuleActionID))
+    theModuleID = ROOT_MODULE_ID;
+
+  const auto itModuleShortcutsInversed = myShortcutsInversed.find(theModuleID);
+  if (itModuleShortcutsInversed == myShortcutsInversed.end())
+    return NO_KEYSEQUENCE;
+
+  const auto& moduleShortcutsInversed = itModuleShortcutsInversed->second;
+  const auto itShortcutInversed = moduleShortcutsInversed.find(theInModuleActionID);
+  if (itShortcutInversed == moduleShortcutsInversed.end())
+    return NO_KEYSEQUENCE;
+
+  return itShortcutInversed->second;
+}
+
+bool SUIT_ShortcutContainer::hasShortcut(QString theModuleID, const QString& theInModuleActionID) const
+{
+  if (SUIT_ShortcutMgr::isInModuleMetaActionID(theInModuleActionID))
+    theModuleID = ROOT_MODULE_ID;
+
+  const auto itModuleShortcutsInversed = myShortcutsInversed.find(theModuleID);
+  if (itModuleShortcutsInversed == myShortcutsInversed.end())
+    return false;
+
+  const auto& moduleShortcutsInversed = itModuleShortcutsInversed->second;
+  const auto itShortcutInversed = moduleShortcutsInversed.find(theInModuleActionID);
+  if (itShortcutInversed == moduleShortcutsInversed.end())
+    return false;
+
+  return true;
+}
+
+const std::map<QString, QKeySequence>& SUIT_ShortcutContainer::getModuleShortcutsInversed(const QString& theModuleID) const
+{
+  static const std::map<QString, QKeySequence> EMPTY_RES;
+  const auto it = myShortcutsInversed.find(theModuleID);
+  if (it == myShortcutsInversed.end())
+    return EMPTY_RES;
+
+  return it->second;
+}
+
+const std::map<QString, QKeySequence> SUIT_ShortcutContainer::getModuleShortcutsInversed(const QString& theModuleID, const QString& theActionIDPrefix) const
+{
+  const auto it = myShortcutsInversed.find(theModuleID);
+  if (it == myShortcutsInversed.end())
+    return std::map<QString, QKeySequence>();
+
+  std::map<QString, QKeySequence> shortcutsInversed;
+  for (const auto& existingShortcut : it->second) {
+    if (existingShortcut.first.startsWith(theActionIDPrefix))
+      shortcutsInversed[existingShortcut.first] = existingShortcut.second;
+  }
+  return shortcutsInversed;
+}
+
+QString SUIT_ShortcutContainer::toString() const
+{
+  QString text;
+  text += "Shortcuts inversed:\n";
+  for (auto it = myShortcutsInversed.begin(); it != myShortcutsInversed.end(); it++) {
+    const QString& moduleID = it->first;
+    const auto& moduleShortcuts = it->second;
+    text += (it == myShortcutsInversed.begin() ? "\"" : "\n\"")  + moduleID + "\"";
+    for (const auto& shortcut : moduleShortcuts) {
+      text += "\n\t\"" + shortcut.first + "\"\t\"" + shortcut.second.toString() + "\"";
+    }
+  }
+  text += "\nShortcuts:\n";
+  for (auto it = myShortcuts.begin(); it != myShortcuts.end(); it++) {
+    const QString& moduleID = it->first;
+    const auto& moduleShortcuts = it->second;
+    text += (it == myShortcuts.begin() ? "\"" : "\n\"")  + moduleID + "\"";
+    for (const auto& shortcut : moduleShortcuts) {
+      text += "\n\t\"" + shortcut.first.toString() + "\"\t\"" + shortcut.second + "\"";
+    }
+  }
+  return text;
+}
+
+/*static*/ const QString SUIT_ActionAssets::LangDependentAssets::PROP_ID_NAME = "name";
+/*static*/ const QString SUIT_ActionAssets::LangDependentAssets::PROP_ID_TOOLTIP = "tooltip";
+
+bool SUIT_ActionAssets::LangDependentAssets::fromJSON(const QJsonObject& theJsonObject)
+{
+  myName    = theJsonObject[SUIT_ActionAssets::LangDependentAssets::PROP_ID_NAME].toString();
+  myToolTip = theJsonObject[SUIT_ActionAssets::LangDependentAssets::PROP_ID_TOOLTIP].toString();
+
+  if (myName.isEmpty())
+    myName = myToolTip;
+
+  return !myName.isEmpty();
+}
+
+void SUIT_ActionAssets::LangDependentAssets::toJSON(QJsonObject& oJsonObject) const
+{
+  oJsonObject[SUIT_ActionAssets::LangDependentAssets::PROP_ID_NAME] = myName;
+  oJsonObject[SUIT_ActionAssets::LangDependentAssets::PROP_ID_TOOLTIP] = myToolTip;
+}
+
+/*static*/ const QString SUIT_ActionAssets::STRUCT_ID = "SUIT_ActionAssets";
+/*static*/ const QString SUIT_ActionAssets::PROP_ID_LANG_DEPENDENT_ASSETS = "langDependentAssets";
+/*static*/ const QString SUIT_ActionAssets::PROP_ID_ICON_PATH = "iconPath";
+
+bool SUIT_ActionAssets::fromJSON(const QJsonObject& theJsonObject)
+{
+  myLangDependentAssets.clear();
+
+  auto lda = SUIT_ActionAssets::LangDependentAssets();
+  const auto& langToLdaJson = theJsonObject[SUIT_ActionAssets::PROP_ID_LANG_DEPENDENT_ASSETS].toObject();
+  for (const QString& lang : langToLdaJson.keys()) {
+    if (!lda.fromJSON(langToLdaJson[lang].toObject()))
+      continue;
+
+    myLangDependentAssets[lang] = lda;
+  }
+
+  myIconPath = theJsonObject[SUIT_ActionAssets::PROP_ID_ICON_PATH].toString();
+
+  return !myLangDependentAssets.empty();
+}
+
+void SUIT_ActionAssets::toJSON(QJsonObject& oJsonObject) const
+{
+  auto langDependentAssetsJSON = QJsonObject();
+
+  auto langDependentAssetsItemJSON = QJsonObject();
+  for (const auto& langAndLDA : myLangDependentAssets) {
+    langAndLDA.second.toJSON(langDependentAssetsItemJSON);
+    langDependentAssetsJSON[langAndLDA.first] = langDependentAssetsItemJSON;
+  }
+  oJsonObject[SUIT_ActionAssets::PROP_ID_LANG_DEPENDENT_ASSETS] = langDependentAssetsJSON;
+
+  oJsonObject[SUIT_ActionAssets::PROP_ID_ICON_PATH] = myIconPath;
+}
+
+QString SUIT_ActionAssets::toString() const
+{
+  QJsonObject jsonObject;
+  toJSON(jsonObject);
+  return QString::fromStdString(QJsonDocument(jsonObject).toJson(QJsonDocument::Indented).toStdString());
+}
+
+QStringList SUIT_ActionAssets::getLangs() const
+{
+  QStringList langs;
+
+  for (const auto& langAndAssets : myLangDependentAssets) {
+    langs.push_back(langAndAssets.first);
+  }
+
+  return langs;
+}
+
+void SUIT_ActionAssets::clearAllLangsExcept(const QString& theLang)
+{
+  for (auto it = myLangDependentAssets.begin(); it != myLangDependentAssets.end();) {
+    if (it->first == theLang)
+      it++;
+    else
+      it = myLangDependentAssets.erase(it);
+  }
+}
+
+void SUIT_ActionAssets::merge(const SUIT_ActionAssets& theOther, bool theOverride)
+{
+  for (const auto& otherLangAndLDA : theOther.myLangDependentAssets) {
+    const QString& lang = otherLangAndLDA.first;
+    const auto& otherLDA = otherLangAndLDA.second;
+    auto& thisLDA = myLangDependentAssets[lang];
+
+    if (thisLDA.myName.isEmpty() || theOverride && !otherLDA.myName.isEmpty())
+      thisLDA.myName = otherLDA.myName;
+
+    if (thisLDA.myToolTip.isEmpty() || theOverride && !otherLDA.myToolTip.isEmpty())
+      thisLDA.myToolTip = otherLDA.myToolTip;
+  }
+
+  if (theOverride)
+    myIconPath = theOther.myIconPath;
+}
+
+std::map<QString, std::map<QString, QKeySequence>> SUIT_ShortcutContainer::merge(
+  const SUIT_ShortcutContainer& theOther,
+  bool theOverride,
+  bool theTreatAbsentIncomingAsDisabled
+) {
+  std::map<QString, std::map<QString, QKeySequence>> changesOfThis;
+
+  for (const auto& shortcutsInversedOfOtherPair : theOther.myShortcutsInversed) {
+    const QString& moduleIDOther = shortcutsInversedOfOtherPair.first;
+    const auto& shortcutsInversedOther = shortcutsInversedOfOtherPair.second;
+    for (const auto& shortcutInversedOther : shortcutsInversedOther) {
+      const QString& inModuleActionIDOther = shortcutInversedOther.first;
+      const QKeySequence& keySequenceOther = shortcutInversedOther.second;
+      if (theOverride) {
+        if (hasShortcut(moduleIDOther, inModuleActionIDOther) && getKeySequence(moduleIDOther, inModuleActionIDOther) == keySequenceOther) {
+          continue;
+        }
+        else /* if this has no shortcut for the action  or  if this has a shortcut for the action, but the key sequence differs. */ {
+          const auto disabledActionsOfThis = setShortcut(moduleIDOther, inModuleActionIDOther, keySequenceOther, true);
+          changesOfThis[moduleIDOther][inModuleActionIDOther] = keySequenceOther;
+          for (const auto& disabledActionOfThis : disabledActionsOfThis) {
+            changesOfThis[disabledActionOfThis.first][disabledActionOfThis.second] = NO_KEYSEQUENCE;
+          }
+        }
+      }
+      else /* if (!theOverride) */ {
+        if (hasShortcut(moduleIDOther, inModuleActionIDOther))
+          continue;
+        else {
+          const auto conflictingActionsOfThis = setShortcut(moduleIDOther, inModuleActionIDOther, keySequenceOther, false);
+          if (conflictingActionsOfThis.empty()) {
+            changesOfThis[moduleIDOther][inModuleActionIDOther] = keySequenceOther;
+          }
+          else /* if this has no shortcut for the action, but the incoming key sequence conflicts with others shortcuts. */ {
+            changesOfThis[moduleIDOther][inModuleActionIDOther] = NO_KEYSEQUENCE;
+          }
+        }
+      }
+    }
+  }
+
+  if (theOverride && theTreatAbsentIncomingAsDisabled) {
+    // Disable existing shortcuts, if they are absent in theOther.
+    for (auto& shortcutsInversedPair : myShortcutsInversed) {
+      const QString& moduleID = shortcutsInversedPair.first;
+      auto& moduleShortcutsInversed = shortcutsInversedPair.second;
+      for (auto& inversedShortcut : moduleShortcutsInversed) {
+        if (theOther.hasShortcut(moduleID, inversedShortcut.first))
+          continue;
+
+        if (inversedShortcut.second.isEmpty())
+          continue; // Existing shortcut is already disabled.
+
+        auto itShortcutsPair = myShortcuts.find(moduleID);
+        if (itShortcutsPair == myShortcuts.end())
+          continue; // The check is an overhead in an error-free designed class, but let be just in case.
+
+        auto& moduleShortcuts = itShortcutsPair->second;
+        moduleShortcuts.erase(inversedShortcut.second);
+        inversedShortcut.second = NO_KEYSEQUENCE;
+        changesOfThis[moduleID][inversedShortcut.first] = NO_KEYSEQUENCE;
+      }
+    }
+  }
+
+  return changesOfThis;
+}
+
+
+SUIT_ShortcutMgr* SUIT_ShortcutMgr::myShortcutMgr = nullptr;
+
 SUIT_ShortcutMgr::SUIT_ShortcutMgr()
 : QObject()
 {
-  qApp->installEventFilter( this );
+  qApp->installEventFilter( this );
+}
+
+SUIT_ShortcutMgr::~SUIT_ShortcutMgr()
+{
+  qApp->removeEventFilter( this );
+}
+
+/*static*/ void SUIT_ShortcutMgr::Init()
+{
+  if( myShortcutMgr == nullptr) {
+    myShortcutMgr = new SUIT_ShortcutMgr();
+    myShortcutMgr->setShortcutsFromPreferences();
+  }
+}
+
+/*static*/ SUIT_ShortcutMgr* SUIT_ShortcutMgr::get()
+{
+  Init();
+  return myShortcutMgr;
+}
+
+/*static*/ bool SUIT_ShortcutMgr::isKeySequenceValid(const QKeySequence& theKeySequence)
+{
+  // TODO Perform check whether a key sequence is platform-compatible.
+  return true;
+}
+
+/*static*/ std::pair<bool, QKeySequence> SUIT_ShortcutMgr::toKeySequenceIfValid(const QString& theKeySequenceString)
+{
+  auto res = std::pair<bool, QKeySequence>(false, QKeySequence());
+
+  try {
+    res.second = QKeySequence::fromString(theKeySequenceString);
+    if (res.second.toString() != theKeySequenceString)
+      return std::pair<bool, QKeySequence>(false, QKeySequence());
+
+    if (!SUIT_ShortcutMgr::isKeySequenceValid(res.second))
+      return std::pair<bool, QKeySequence>(false, QKeySequence());
+  }
+  catch (...) {
+    return std::pair<bool, QKeySequence>(false, QKeySequence());
+  }
+
+  res.first = true;
+  return res;
+}
+
+/*static*/ bool SUIT_ShortcutMgr::isModuleIDValid(const QString& theModuleID)
+{
+  if (theModuleID.contains(TOKEN_SEPARATOR))
+    return false;
+
+  if (theModuleID.simplified() != theModuleID)
+    return false;
+
+  return true;
+}
+
+/*static*/ bool SUIT_ShortcutMgr::isInModuleActionIDValid(const QString& theInModuleActionID)
+{
+  QStringList tokens = theInModuleActionID.split(TOKEN_SEPARATOR);
+   for (QStringList::size_type i = 0; i < tokens.length(); i++) {
+    const QString simplifiedToken = tokens[i].simplified();
+    if (
+      simplifiedToken.isEmpty() ||
+      simplifiedToken != tokens[i] ||
+      i == 0 && simplifiedToken == META_ACTION_PREFIX ||
+      i != 0 && simplifiedToken.startsWith(META_ACTION_PREFIX)
+    )
+      return false;
+  }
+  return true;
+}
+
+/*static*/ bool SUIT_ShortcutMgr::isInModuleMetaActionID(const QString& theInModuleActionID)
+{
+  return theInModuleActionID.startsWith(META_ACTION_PREFIX);
+}
+
+/*static*/ std::pair<QString, QString> SUIT_ShortcutMgr::splitIntoModuleIDAndInModuleID(const QString& theActionID)
+{
+  QStringList tokens = theActionID.split(TOKEN_SEPARATOR);
+  if (tokens.length() < 2)
+    return std::pair<QString, QString>();
+
+  auto res = std::pair<QString, QString>();
+
+  if (tokens[0].simplified() != tokens[0])
+    return std::pair<QString, QString>();
+
+  res.first = tokens[0];
+  tokens.pop_front();
+
+  for (QStringList::size_type i = 0; i < tokens.length(); i++) {
+    const QString simplifiedToken = tokens[i].simplified();
+    if (
+      simplifiedToken.isEmpty() ||
+      simplifiedToken != tokens[i] ||
+      i == 0 && simplifiedToken == META_ACTION_PREFIX ||
+      i != 0 && simplifiedToken.startsWith(META_ACTION_PREFIX)
+    )
+      return std::pair<QString, QString>();
+  }
+  res.second = tokens.join(TOKEN_SEPARATOR);
+
+  return res;
+}
+
+/*static*/ bool SUIT_ShortcutMgr::isActionIDValid(const QString& theActionID)
+{
+  return !SUIT_ShortcutMgr::splitIntoModuleIDAndInModuleID(theActionID).second.isEmpty();
+}
+
+/*static*/ QString SUIT_ShortcutMgr::makeActionID(const QString& theModuleID, const QString& theInModuleActionID)
+{
+  if (!SUIT_ShortcutMgr::isModuleIDValid(theModuleID))
+    return QString();
+
+  if (!isInModuleActionIDValid(theInModuleActionID))
+    return QString();
+
+  return theModuleID + TOKEN_SEPARATOR + theInModuleActionID;
 }
 
-/*!
-  \brief Destructor
-*/
-SUIT_ShortcutMgr::~SUIT_ShortcutMgr()
+/*static*/ void SUIT_ShortcutMgr::fillContainerFromPreferences(SUIT_ShortcutContainer& theContainer, bool theDefaultOnly)
 {
-  qApp->removeEventFilter( this );
+  ShCutDbg() && ShCutDbg("Retrieving preferences from resources.");
+
+  SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
+  if (!resMgr) {
+    Warning("SUIT_ShortcutMgr can't retrieve resource manager!");
+    return;
+  }
+
+  const auto resMgrWorkingModeBefore = resMgr->workingMode();
+  if (theDefaultOnly)
+    resMgr->setWorkingMode(QtxResourceMgr::IgnoreUserValues);
+
+  /** List of modules with invalid IDs. */
+  QStringList invalidModuleIDs;
+
+  /** { moduleID, {inModuleActionID, keySequence}[] }[] */
+  std::map<QString, std::list<std::pair<QString, QString>>> invalidShortcuts;
+
+  /**
+   * Shortcuts, which have not been set, because they are in conflict with previously parsed shortcuts.
+   * { moduleID, {inModuleActionID, keySequence}[] }[] */
+  std::map<QString, std::list<std::pair<QString, QKeySequence>>> conflicts;
+
+  // Resource manager strips leading and trailing whitespaces from subsections' names.
+  // And then it is not able to retrieve parametes from that subsections,
+  // because parsed subsection names differ from the ones in resource file.
+  // Anyway, it does not affect operability of ShortcutMgr.
+  QStringList moduleIDs = resMgr->subSections(SECTION_NAME_PREFIX, true);
+  if (ShCutDbg()) {
+    if (moduleIDs.isEmpty())
+      ShCutDbg("No discovered shortcut modules.");
+    else
+      ShCutDbg("Discovered shortcut modules: \"" + moduleIDs.join("\", \"") + ".");
+  }
+  moduleIDs.push_front(ROOT_MODULE_ID); // Resource manager filters out empty section suffices.
+  moduleIDs.removeDuplicates();
+
+  for (size_t i = 0; i < moduleIDs.size(); i++) {
+    const auto& moduleID = moduleIDs[i];
+    if (!SUIT_ShortcutMgr::isModuleIDValid(moduleID)) {
+      invalidModuleIDs.push_back(moduleID);
+      continue;
+    }
+
+    const QString sectionName = SECTION_NAME_PREFIX + resMgr->sectionsToken() + moduleID;
+    QStringList moduleActionIDs = resMgr->parameters(sectionName);
+
+    for(const QString& inModuleActionID : moduleActionIDs) {
+      QString keySequenceString = QString("");
+      resMgr->value(sectionName, inModuleActionID, keySequenceString);
+      const auto keySequence = SUIT_ShortcutMgr::toKeySequenceIfValid(keySequenceString);
+
+      ShCutDbg() && ShCutDbg("Shortcut discovered: \"" + moduleID + "\"\t\"" + inModuleActionID + "\"\t\"" + keySequenceString + "\".");
+
+      if (
+        !SUIT_ShortcutMgr::isInModuleActionIDValid(inModuleActionID) ||
+        !keySequence.first ||
+        SUIT_ShortcutMgr::isInModuleMetaActionID(inModuleActionID) && moduleID != ROOT_MODULE_ID
+      ) {
+        std::list<std::pair<QString, QString>>& moduleInvalidShortcuts = invalidShortcuts[moduleID];
+        moduleInvalidShortcuts.push_back(std::pair<QString, QString>(inModuleActionID, keySequenceString));
+        continue;
+      }
+
+      const auto shortcutConflicts = theContainer.setShortcut(moduleID, inModuleActionID, keySequence.second, false /*override*/);
+      if (!shortcutConflicts.empty()) {
+        auto& moduleConflicts = conflicts[moduleID];
+        moduleConflicts.push_back(std::pair<QString, QKeySequence>(inModuleActionID, keySequence.second));
+      }
+    }
+  }
+
+  if (!invalidModuleIDs.isEmpty() || !invalidShortcuts.empty() || !conflicts.empty())
+  { // Prepare report and show warning.
+    QString report;
+    if (!invalidModuleIDs.isEmpty()) {
+      report += tr("Invalid module IDs") + ":";
+      for (const QString& invalidModuleID : invalidModuleIDs) {
+        report += "\n\t\"" + invalidModuleID + "\"" ;
+      }
+    }
+
+    if (!invalidShortcuts.empty()) {
+      if (!report.isEmpty())
+        report += "\n\n";
+
+      report += tr("Invalid shortcuts") + ":";
+      for (const auto& moduleAndShortcuts : invalidShortcuts) {
+        report += "\n\t\"" + moduleAndShortcuts.first + "\"";
+        const std::list<std::pair<QString, QString>>& moduleShortcuts = moduleAndShortcuts.second;
+        for (const auto& shortcut : moduleShortcuts) {
+          report += "\n\t\t\"" + shortcut.first + "\"\t\"" + shortcut.second + "\"";
+        }
+      }
+    }
+
+    if (!conflicts.empty()) {
+      if (!report.isEmpty())
+        report += "\n\n";
+
+      report += tr("These shortcuts have not been set to theContainer, because they conflict with previously parsed ones") + ":";
+      for (const auto& moduleAndShortcuts : conflicts) {
+        report += "\n\t\"" + moduleAndShortcuts.first + "\"";
+
+        const std::list<std::pair<QString, QKeySequence>>& moduleShortcuts = moduleAndShortcuts.second;
+        for (const auto& shortcut : moduleShortcuts) {
+          report += "\n\t\t\"" + shortcut.first + "\"\t\"" + shortcut.second.toString() + "\"";
+        }
+      }
+    }
+
+    report += "\n.";
+
+    const auto text = tr("Invalid shortcuts in preferences");
+    const auto informativeText = tr("Fix the following entries in the preference files manually");
+    if (!theDefaultOnly) {
+      // If user preferences are accounted, show warning in UI.
+      SUIT_Application* app = SUIT_Session::session()->activeApplication();
+      if (app && app->desktop()) {
+        // Is not compiled without cast or with static_cast<QWidget*>.
+        QMessageBox msgBox((QWidget*)app->desktop());
+        msgBox.setIcon(QMessageBox::Warning);
+        msgBox.setTextFormat(Qt::RichText);
+        msgBox.setText("<b>" + text + "</b>");
+        msgBox.setInformativeText(informativeText + ":");
+        msgBox.setWindowFlags(Qt::WindowType::Popup);
+        msgBox.setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
+        msgBox.setDetailedText(report);
+        msgBox.setStandardButtons(QMessageBox::Ok);
+        msgBox.setDefaultButton(QMessageBox::Ok);
+        msgBox.setMinimumWidth(600);
+        msgBox.exec();
+      }
+    }
+    Warning(text + ". " + informativeText + ":\n" + report);
+  }
+
+  if (theDefaultOnly)
+    resMgr->setWorkingMode(resMgrWorkingModeBefore);
+
+  ShCutDbg() && ShCutDbg("theContainer holds following shortcuts:\n" + theContainer.toString());
 }
 
-/*!
-  \brief Create new instance of shortcut manager.
-*/
-void SUIT_ShortcutMgr::Init()
+QString substituteBashVars(const QString& theString)
 {
-  if( myShortcutMgr==NULL )
-    myShortcutMgr = new SUIT_ShortcutMgr();
+  QString res = theString;
+  const auto env = QProcessEnvironment::systemEnvironment();
+  int pos = 0;
+  QRegExp rx("\\$\\{([^\\}]+)\\}"); // Match substrings enclosed by "${" and "}".
+  rx.setMinimal(true); // Set search to non-greedy.
+  while((pos = rx.indexIn(res, pos)) != -1) {
+    QString capture = rx.cap(1);
+    QString subst = env.value(capture);
+    ShCutDbg("capture = " + capture);
+    ShCutDbg("subst   = " + subst);
+    res.replace("${" + capture + "}", subst);
+    pos += rx.matchedLength();
+  }
+  return res;
 }
 
-/*!
-  \brief Return shortcut manager. 
-*/
-SUIT_ShortcutMgr* SUIT_ShortcutMgr::getShortcutMgr()
+QString substitutePowerShellVars(const QString& theString)
 {
-  Init();
-  
-  return myShortcutMgr;
+  QString res = theString;
+  int pos = 0;
+  QRegExp rx("%([^%]+)%"); // Match substrings enclosed by "%".
+  rx.setMinimal(true); // Set search to non-greedy.
+  while((pos = rx.indexIn(res, pos)) != -1) {
+    QString capture = rx.cap(1);
+    QString subst = Qtx::getenv(capture.toUtf8().constData());
+    ShCutDbg("capture = " + capture);
+    ShCutDbg("subst   = " + subst);
+    res.replace("%" + capture + "%", subst);
+    pos += rx.matchedLength();
+  }
+  return res;
 }
 
-/*!
-  \brief Custom event filter for qapplication .
-  
-  Redefined from QObject::eventFilter();
-*/
-bool SUIT_ShortcutMgr::eventFilter( QObject* o, QEvent* e )
+QString substituteVars(const QString& theString)
 {
-  if ( e->type() == QEvent::ActionAdded ) {
-    QActionEvent* anActionEvent = (QActionEvent*)e;
-    if (anActionEvent) {
-      QtxAction* anAction = qobject_cast<QtxAction*>( anActionEvent->action() );
-      if ( anAction )
-       processAction( anAction );
+  QString str = substituteBashVars(theString);
+  return substitutePowerShellVars(str);
+}
+
+/*static*/ std::pair<bool, SUIT_ActionAssets> SUIT_ShortcutMgr::getActionAssetsFromResources(const QString& theActionID)
+{
+  auto res = std::pair<bool, SUIT_ActionAssets>(false, SUIT_ActionAssets());
+
+  SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
+  if (!resMgr) {
+    Warning("SUIT_ShortcutMgr can't retrieve resource manager!");
+    return res;
+  }
+
+  QStringList actionAssetFilePaths = resMgr->parameters(SECTION_NAME_ACTION_ASSET_FILE_PATHS);
+  for (const QString& actionAssetFilePath : actionAssetFilePaths) {
+    const QString path = substituteVars(actionAssetFilePath);
+    QFile actionAssetFile(path);
+    if (!actionAssetFile.open(QIODevice::ReadOnly)) {
+      Warning("SUIT_ShortcutMgr can't open action asset file \"" + path + "\"!");
+      continue;
+    }
+
+    QJsonParseError jsonError;
+    QJsonDocument document = QJsonDocument::fromJson(actionAssetFile.readAll(), &jsonError);
+    actionAssetFile.close();
+    if(jsonError.error != QJsonParseError::NoError) {
+      Warning("SUIT_ShortcutMgr: error during parsing of action asset file \"" + path + "\"!");
+      continue;
+    }
+
+    if(!document.isObject()) {
+      Warning("SUIT_ShortcutMgr: empty action asset file \"" + path + "\"!");
+      continue;
+    }
+
+    QJsonObject object = document.object();
+    if (object.keys().indexOf(theActionID) == -1)
+      continue;
+
+    SUIT_ActionAssets actionAssets;
+    if (!actionAssets.fromJSON(object[theActionID].toObject())) {
+      ShCutDbg("Action asset file \"" + path + "\" contains invalid action assets with ID \"" + theActionID + "\".");
+      continue;
     }
+
+    res.second.merge(actionAssets, true);
   }
 
-  return QObject::eventFilter( o, e );
+  res.first = true;
+  return res;
 }
 
-/*!
-  \brief Return key sequence for shortcut action name.
-  \param actionName name of shortcut action in preferences
-  \return key sequence defined in preferences or empty sequence
-*/
-QKeySequence SUIT_ShortcutMgr::getShortcutByActionName( const QString& actionName ) const
+
+/*static*/ QString SUIT_ShortcutMgr::getLang()
 {
   SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
+  if (!resMgr) {
+    Warning("SUIT_ShortcutMgr can't retrieve resource manager!");
+    return DEFAULT_LANG;
+  }
+
+  return resMgr->stringValue(LANG_SECTION, LANG_SECTION, DEFAULT_LANG);
+}
+
+
+void SUIT_ShortcutMgr::registerAction(const QString& theActionID, QAction* theAction)
+{
+  const auto moduleIDAndActionID = splitIntoModuleIDAndInModuleID(theActionID);
+  const QString& moduleID = moduleIDAndActionID.first;
+  const QString& inModuleActionID = moduleIDAndActionID.second;
+
+  if (inModuleActionID.isEmpty()) {
+    ShCutDbg() && ShCutDbg("Attempt to register an action \"" + theAction->toolTip() + "\" with invalid ID \"" + theActionID + "\".");
+    if (theAction->shortcut() != NO_KEYSEQUENCE)
+      theAction->setShortcut(NO_KEYSEQUENCE);
+
+    return;
+  }
+
+  { // If an action with the same memory address was registered earlier,
+    // clear all data about it to start registering procedure from scratch.
+    auto itPreviousModuleAndActionID = myActionIDs.find(theAction);
+    if (itPreviousModuleAndActionID != myActionIDs.end()) {
+      // Clear the data from myActions.
+      const auto& previousModuleAndActionID = itPreviousModuleAndActionID->second;
+      auto itActions = myActions.find(previousModuleAndActionID.first);
+      if (itActions != myActions.end()) {
+        std::map<QString, std::set<QAction*>>& moduleActions = itActions->second;
+        auto itModuleActions = moduleActions.find(previousModuleAndActionID.second);
+        if (itModuleActions != moduleActions.end()) {
+          std::set<QAction*>& registeredActions = itModuleActions->second;
+          registeredActions.erase(theAction);
+        }
+      }
+
+      myActionIDs.erase(itPreviousModuleAndActionID);
+    }
+  }
+
+  auto itActions = myActions.find(moduleID);
+  if (itActions == myActions.end()) {
+    itActions = myActions.emplace(moduleID, std::map<QString, std::set<QAction*>>()).first;
+  }
 
-  QString section = actionName.section( resMgr->sectionsToken(), 0, 0 );
-  section.prepend( QString("shortcuts") + resMgr->sectionsToken() );
-  QString parameter = actionName.section( resMgr->sectionsToken(), 1, 1 );
+  std::map<QString, std::set<QAction*>>& moduleActions = itActions->second;
+  auto itModuleActions = moduleActions.find(inModuleActionID);
+  if (itModuleActions != moduleActions.end()) {
+    std::set<QAction*>& registeredActions = itModuleActions->second;
+    const bool actionIsNew = registeredActions.emplace(theAction).second;
+    if (actionIsNew)
+      myActionIDs[theAction] = moduleIDAndActionID;
+  }
+  else {
+    std::set<QAction*>& registeredActions = moduleActions[inModuleActionID];
+    registeredActions.emplace(theAction);
+    myActionIDs[theAction] = moduleIDAndActionID;
+  }
 
-  QString shortcutValue;
-  bool hasValue = resMgr->value( section, parameter, shortcutValue, false );
+  connect(theAction, SIGNAL(destroyed(QObject*)), this, SLOT (onActionDestroyed(QObject*)));
 
-  if ( !hasValue )
-    return QKeySequence();
+  if (myShortcutContainer.hasShortcut(moduleID, inModuleActionID)) {
+    const QKeySequence& keySequence = getKeySequence(moduleID, inModuleActionID);
+    theAction->setShortcut(keySequence);
+  }
+  else {
+    ShCutDbg(
+      "Action with ID \"" +
+      (SUIT_ShortcutMgr::isInModuleMetaActionID(inModuleActionID) ? ROOT_MODULE_ID + TOKEN_SEPARATOR + inModuleActionID : theActionID) +
+      "\" is not added to default resource files."
+    );
+    auto conflicts = myShortcutContainer.setShortcut(moduleID, inModuleActionID, theAction->shortcut(), false);
+    if (!conflicts.empty())
+      theAction->setShortcut(NO_KEYSEQUENCE); // Unbind any key sequence, if it was bound outside of the class and interferes with other shortcuts.
+  }
+}
 
-  return QKeySequence::fromString( shortcutValue );
+void SUIT_ShortcutMgr::registerAction(QtxAction* theAction)
+{
+  registerAction(theAction->ID(), theAction);
 }
 
-/*!
-  \brief Set shortcut to the given action if the shortcut is defined.
-  \param action action to process
- */
-void SUIT_ShortcutMgr::processAction( QtxAction* action )
+std::set<QAction*> SUIT_ShortcutMgr::getActions(const QString& theModuleID, const QString& theInModuleActionID) const
 {
-  QString shortcutActionName = action->shortcutActionName();
-  if ( !shortcutActionName.isEmpty() ) {
-    // Add action to the actions map
-    if ( !myShortcutActions.contains( shortcutActionName, action ) ) {
-      myShortcutActions.insert( shortcutActionName, action );
-      connect( action, SIGNAL( destroyed( QObject* ) ), 
-              this, SLOT ( onActionDestroyed( QObject* ) ) );
+  if (SUIT_ShortcutMgr::isInModuleMetaActionID(theInModuleActionID)) {
+    std::set<QAction*> actions;
+    for (const auto& actionAndID : myActionIDs) {
+      if (actionAndID.second.second == theInModuleActionID)
+        actions.emplace(actionAndID.first);
     }
+    return actions;
+  }
+  else {
+    const auto itActions = myActions.find(theModuleID);
+    if (itActions == myActions.end())
+      return std::set<QAction*>();
+
+    const std::map<QString, std::set<QAction*>>& moduleActions = itActions->second;
+    const auto itModuleActions = moduleActions.find(theInModuleActionID);
+    if (itModuleActions == moduleActions.end())
+      return std::set<QAction*>();
 
-    QKeySequence keySeq = getShortcutByActionName( shortcutActionName );
-    action->setShortcut( keySeq );
+    return itModuleActions->second;
   }
 }
 
-/*!
-  \brief Enable/disable a shortcuts section.
+std::pair<QString, QString> SUIT_ShortcutMgr::getModuleIDAndInModuleID(const QAction* theAction) const {
+  const auto it = myActionIDs.find(const_cast<QAction*>(theAction));
+  if (it == myActionIDs.end())
+    return std::pair<QString, QString>();
 
-  Enables or disables actions which belong to the given shortcuts section.
-  Only actions which have an active desktop as a parent widget 
-  are taken into account.
+  return it->second;
+}
 
-  \param section shorcuts section
-  \param on if \c true - action will be enabled, otherwise - disabled
-*/
-void SUIT_ShortcutMgr::setSectionEnabled( const QString& section, const bool on )
-{
-  QMap<QString, QtxAction*>::ConstIterator it;
-  for ( it = myShortcutActions.constBegin(); it != myShortcutActions.constEnd(); ++it ) {
-    QtxAction* action = it.value();
-    QString shortcutActionName = action->shortcutActionName();
-    QString actionSection = shortcutActionName.section( ":", 0, 0 );
-    if ( actionSection == section ) {
-      // Check if the action parent widget equals to the active desktop
-      SUIT_Application* app = SUIT_Session::session()->activeApplication();
-      if ( !app )
-       return;
-      if ( action->parentWidget() == (QWidget*)app->desktop() )
-       action->setEnabled( on );
+bool SUIT_ShortcutMgr::hasAction(const QAction* theAction) const
+{
+  return myActionIDs.find(const_cast<QAction*>(theAction)) != myActionIDs.end();
+}
+
+QString SUIT_ShortcutMgr::getActionID(const QAction* theAction) const
+{
+  const auto it = myActionIDs.find(const_cast<QAction*>(theAction));
+  if (it == myActionIDs.end())
+    return QString();
+
+  return SUIT_ShortcutMgr::makeActionID(it->second.first, it->second.second);
+}
+
+void SUIT_ShortcutMgr::setActionsOfModuleEnabled(const QString& theModuleID, const bool theEnable) const
+{
+  const auto itModuleActions = myActions.find(theModuleID);
+  if (itModuleActions == myActions.end())
+    return;
+
+  SUIT_Application* app = SUIT_Session::session()->activeApplication();
+  if (!app)
+    return;
+
+  const std::map<QString, std::set<QAction*>>& moduleActions = itModuleActions->second;
+  for (const auto& idAndActions : moduleActions) {
+    const std::set<QAction*>& actions = idAndActions.second;
+    for (QAction* const action : actions) {
+      if (action->parentWidget() == (QWidget*)app->desktop()) // Is not compiled without cast or with static_cast<QWidget*>.
+        action->setEnabled(theEnable);
     }
   }
 }
 
-/*!
-  \brief Update shortcuts from preferences.
-*/
-void SUIT_ShortcutMgr::updateShortcuts()
+void SUIT_ShortcutMgr::setActionsWithPrefixInIDEnabled(const QString& theInModuleActionIDPrefix, bool theEnable) const
 {
-  QMap<QString, QtxAction*>::ConstIterator it;
-  for ( it = myShortcutActions.constBegin(); it != myShortcutActions.constEnd(); ++it ) {
-    QtxAction* action = it.value();
-    QKeySequence keySeq = getShortcutByActionName( action->shortcutActionName() );
-    action->setShortcut( keySeq );
+  SUIT_Application* app = SUIT_Session::session()->activeApplication();
+  if (!app)
+    return;
+
+  for (const std::pair<QAction*, std::pair<QString, QString>>& actionAndID : myActionIDs) {
+    QAction* const action = actionAndID.first;
+    // Is not compiled without cast or with static_cast<QWidget*>.
+    if (action->parentWidget() == (QWidget*)app->desktop()) {
+      const QString& inModuleActionID = actionAndID.second.second;
+      if (inModuleActionID.startsWith(theInModuleActionIDPrefix))
+        action->setEnabled(theEnable);
+    }
   }
 }
 
-/*!
-  \brief Called when the corresponding action is destroyed.
-  
-  Removes destroyed action from the actions list.
+void SUIT_ShortcutMgr::setSectionEnabled(const QString& theInModuleActionIDPrefix, bool theEnable) const
+{
+  setActionsWithPrefixInIDEnabled(theInModuleActionIDPrefix, theEnable);
+}
 
-  \param obj action being destroyed
-*/
-void SUIT_ShortcutMgr::onActionDestroyed( QObject* obj )
+void SUIT_ShortcutMgr::rebindActionsToKeySequences() const
+{
+  ShCutDbg() && ShCutDbg("SUIT_ShortcutMgr::rebindActionsToKeySequences()");
+  for (const std::pair<QAction*, std::pair<QString, QString>>& actionAndID : myActionIDs) {
+    actionAndID.first->setShortcut(getKeySequence(actionAndID.second.first, actionAndID.second.second));
+  }
+}
+
+void SUIT_ShortcutMgr::updateShortcuts() const
+{
+  rebindActionsToKeySequences();
+}
+
+std::set<std::pair<QString, QString>> SUIT_ShortcutMgr::setShortcut(const QString& theActionID, const QKeySequence& theKeySequence, bool theOverride)
+{
+  const auto moduleIDAndActionID = splitIntoModuleIDAndInModuleID(theActionID);
+  const QString& moduleID = moduleIDAndActionID.first;
+  const QString& inModuleActionID = moduleIDAndActionID.second;
+
+  if (inModuleActionID.isEmpty()) {
+    ShCutDbg() && ShCutDbg("Attempt to set shortcut with invalid action ID \"" + theActionID + "\".");
+    return std::set<std::pair<QString, QString>>();
+  }
+
+  return setShortcutNoIDChecks(moduleID, inModuleActionID, theKeySequence, theOverride);
+}
+
+std::set<std::pair<QString, QString>> SUIT_ShortcutMgr::setShortcut(const QString& theModuleID, const QString& theInModuleActionID, const QKeySequence& theKeySequence, bool theOverride)
+{
+  if (!SUIT_ShortcutMgr::isModuleIDValid(theModuleID)) {
+    ShCutDbg() && ShCutDbg("Attempt to set shortcut with invalid module ID \"" + theModuleID + "\".");
+    return std::set<std::pair<QString, QString>>();
+  }
+
+  if (!SUIT_ShortcutMgr::isInModuleActionIDValid(theInModuleActionID)) {
+    ShCutDbg() && ShCutDbg("Attempt to set shortcut with invalid in-module action ID \"" + theInModuleActionID + "\".");
+    return std::set<std::pair<QString, QString>>();
+  }
+
+  return setShortcutNoIDChecks(theModuleID, theInModuleActionID, theKeySequence, theOverride);
+}
+
+const SUIT_ShortcutContainer& SUIT_ShortcutMgr::getShortcutContainer() const
+{
+  return myShortcutContainer;
+}
+
+void SUIT_ShortcutMgr::mergeShortcutContainer(const SUIT_ShortcutContainer& theContainer, bool theOverride, bool theTreatAbsentIncomingAsDisabled)
+{
+  ShCutDbg() && ShCutDbg("ShortcutMgr merges shortcut container...");
+  const auto changes = myShortcutContainer.merge(theContainer, theOverride, theTreatAbsentIncomingAsDisabled);
+  ShCutDbg() && ShCutDbg("ShortcutMgr keeps following shortcuts:\n" + myShortcutContainer.toString());
+
+  // Turn off hotkeys for disabled shortcuts.
+  for (const auto& moduleIDAndChanges : changes) {
+    const QString& moduleID = moduleIDAndChanges.first;
+    const auto& moduleChanges = moduleIDAndChanges.second;
+    for (const std::pair<QString, QKeySequence>& modifiedShortcut : moduleChanges) {
+      if (modifiedShortcut.second == NO_KEYSEQUENCE) {
+        const std::set<QAction*> actions = getActions(moduleID, modifiedShortcut.first);
+        for (QAction* const action : actions) {
+          action->setShortcut(NO_KEYSEQUENCE);
+        }
+      }
+    }
+  }
+
+  // Turn on hotkeys for enabled shortcuts.
+  for (const auto& moduleIDAndChanges : changes) {
+    const QString& moduleID = moduleIDAndChanges.first;
+    const auto& moduleChanges = moduleIDAndChanges.second;
+    for (const std::pair<QString, QKeySequence>& modifiedShortcut : moduleChanges) {
+      if (modifiedShortcut.second != NO_KEYSEQUENCE) {
+        const std::set<QAction*> actions = getActions(moduleID, modifiedShortcut.first);
+        for (QAction* const action : actions) {
+          action->setShortcut(modifiedShortcut.second);
+        }
+      }
+    }
+  }
+
+  SUIT_ShortcutMgr::saveShortcutsToPreferences(changes);
+}
+
+QKeySequence SUIT_ShortcutMgr::getKeySequence(const QString& theModuleID, const QString& theInModuleActionID) const
+{
+  return myShortcutContainer.getKeySequence(theModuleID, theInModuleActionID);
+}
+
+const std::map<QString, QKeySequence>& SUIT_ShortcutMgr::getModuleShortcutsInversed(const QString& theModuleID) const
+{
+  return myShortcutContainer.getModuleShortcutsInversed(theModuleID);
+}
+
+std::set<QString> SUIT_ShortcutMgr::getShortcutModuleIDs() const
+{
+  return myShortcutContainer.getIDsOfAllModules();
+}
+
+std::set<QString> SUIT_ShortcutMgr::getIDsOfInterferingModules(const QString& theModuleID) const
+{
+  return myShortcutContainer.getIDsOfInterferingModules(theModuleID);
+}
+
+std::shared_ptr<const SUIT_ActionAssets> SUIT_ShortcutMgr::getModuleAssets(const QString& theModuleID) const
+{
+  const auto itModuleAssets = myModuleAssets.find(theModuleID);
+  if (itModuleAssets == myModuleAssets.end()) {
+    auto assets = std::shared_ptr<SUIT_ActionAssets>(new SUIT_ActionAssets());
+    auto lda = SUIT_ActionAssets::LangDependentAssets();
+    lda.myName = theModuleID; // At least something meaningful.
+
+    assets->myLangDependentAssets.emplace(SUIT_ShortcutMgr::getLang(), lda);
+    return assets;
+  }
+  return itModuleAssets->second;
+}
+
+QString SUIT_ShortcutMgr::getModuleName(const QString& theModuleID, const QString& theLang) const
+{
+  const auto assets = getModuleAssets(theModuleID);
+  const auto& ldaMap = assets->myLangDependentAssets;
+  if (ldaMap.empty())
+    return theModuleID;
+
+  auto itLang = ldaMap.find(theLang.isEmpty() ? SUIT_ShortcutMgr::getLang() : theLang);
+  if (itLang == ldaMap.end())
+    itLang = ldaMap.begin(); // Get name in any language.
+
+  const auto& name = itLang->second.myName;
+  return name.isEmpty() ? theModuleID : name;
+}
+
+std::shared_ptr<const SUIT_ActionAssets> SUIT_ShortcutMgr::getActionAssets(const QString& theModuleID, const QString& theInModuleActionID) const
+{
+  const QString actionID = SUIT_ShortcutMgr::makeActionID(theModuleID, theInModuleActionID);
+  if (actionID.isEmpty()) {
+    ShCutDbg() && ShCutDbg("Can't get action assets: either/both module ID \"" + theModuleID + "\" or/and in-module action ID \"" + theInModuleActionID + "\" is/are invalid.");
+    return std::shared_ptr<const SUIT_ActionAssets>(nullptr);
+  }
+  return getActionAssets(actionID);
+}
+
+std::shared_ptr<const SUIT_ActionAssets> SUIT_ShortcutMgr::getActionAssets(const QString& theActionID) const
+{
+  const auto it = myActionAssets.find(theActionID);
+  if (it == myActionAssets.end())
+    return std::shared_ptr<const SUIT_ActionAssets>(nullptr);
+  else
+    return it->second;
+}
+
+QString SUIT_ShortcutMgr::getActionName(const QString& theModuleID, const QString& theInModuleActionID, const QString& theLang) const
+{
+  const QString actionID = SUIT_ShortcutMgr::makeActionID(theModuleID, theInModuleActionID);
+  if (actionID.isEmpty()) {
+    ShCutDbg() && ShCutDbg("Can't get action name: either/both module ID \"" + theModuleID + "\" or/and in-module action ID \"" + theInModuleActionID + "\" is/are invalid.");
+    return actionID;
+  }
+
+  const auto itActionAssets = myActionAssets.find(actionID);
+  if (itActionAssets != myActionAssets.end() && !itActionAssets->second->myLangDependentAssets.empty()) {
+    const auto& ldaMap = itActionAssets->second->myLangDependentAssets;
+    if (ldaMap.empty())
+      return theInModuleActionID;
+
+    auto itLang = ldaMap.find(theLang.isEmpty() ? SUIT_ShortcutMgr::getLang() : theLang);
+    if (itLang == ldaMap.end())
+      itLang = ldaMap.begin(); // Get name in any language.
+
+    const auto& name = itLang->second.myName;
+    return name.isEmpty() ? theInModuleActionID : name;
+  }
+  else /* if action assets have not been loaded. */ {
+    // Try to get action->text() and use it as a name.
+
+    // Pitfall of the approach: at the time this code block is called, the action may not exist.
+    // Moreover, an action with such an ID may not even have been created at the time of calling this method.
+    // Thus, even buffering of assets of every action ever created at runtime does not guarantee,
+    // that the assets will be available at any point in the life of the application,
+    // unless the assets are added to dedicated section in an asset file.
+
+    const auto actions = getActions(theModuleID, theInModuleActionID);
+    for (const auto& action : actions) {
+      if (!action->text().isEmpty())
+        return action->text();
+    }
+    return theInModuleActionID;
+  }
+}
+
+void SUIT_ShortcutMgr::onActionDestroyed(QObject* theObject)
+{
+  QAction* action = static_cast<QAction*>(theObject);
+
+  auto itID = myActionIDs.find(action);
+  if (itID == myActionIDs.end())
+    return;
+
+  const QString& moduleID = itID->second.first;
+  const QString& inModuleActionID = itID->second.second;
+
+  auto itModuleActions = myActions.find(moduleID);
+  if (itModuleActions != myActions.end()) {
+    std::map<QString, std::set<QAction*>>& moduleActions = itModuleActions->second;
+    auto itActions = moduleActions.find(inModuleActionID);
+    if (itActions != moduleActions.end()) {
+      std::set<QAction*>& actions = itActions->second;
+      actions.erase(action);
+    }
+  }
+
+  myActionIDs.erase(itID);
+}
+
+bool SUIT_ShortcutMgr::eventFilter(QObject* theObject, QEvent* theEvent)
+{
+  if (theEvent) {
+    if (theEvent->type() == QEvent::ActionAdded) {
+      auto anActionEvent = static_cast<QActionEvent*>(theEvent);
+
+      QtxAction* aQtxAction = qobject_cast<QtxAction*>(anActionEvent->action());
+      if (aQtxAction) {
+#ifdef SHORTCUT_MGR_DBG
+        {
+          const auto moduleIDAndActionID = splitIntoModuleIDAndInModuleID(aQtxAction->ID());
+          if (moduleIDAndActionID.second.isEmpty())
+            ShCutDbg("ActionAdded event, but ID of the action is invalid. Action name = \"" + aQtxAction->toolTip() + "\", ID = \"" + aQtxAction->ID() + "\".");
+          else if (!myShortcutContainer.hasShortcut(moduleIDAndActionID.first, moduleIDAndActionID.second))
+            ShCutDbg("ActionAdded event, but shortcut container has no shortcut for the action. It is ok, if preference files has not been parsed yet. Action ID = \"" + moduleIDAndActionID.second + "\".");
+        }
+#endif//SHORTCUT_MGR_DBG
+#ifdef SHORTCUT_MGR_DEVTOOLS
+        {
+          DevTools::get()->collectShortcutAndAssets(aQtxAction);
+          const auto moduleIDAndActionID = splitIntoModuleIDAndInModuleID(aQtxAction->ID());
+          if (moduleIDAndActionID.second.isEmpty())
+            DevTools::get()->collectAssetsOfActionWithInvalidID(aQtxAction);
+        }
+#endif//SHORTCUT_MGR_DEVTOOLS
+             registerAction(aQtxAction);
+      }
+      else {
+        QAction* aQAction = qobject_cast<QAction*>(anActionEvent->action());
+#ifdef SHORTCUT_MGR_DEVTOOLS
+        if (aQAction)
+          DevTools::get()->collectAssetsOfActionWithInvalidID(aQAction);
+#endif//SHORTCUT_MGR_DEVTOOLS
+        if (aQAction && aQAction->shortcut() != NO_KEYSEQUENCE) {
+#ifdef SHORTCUT_MGR_DBG
+          ShCutDbg("ActionAdded event, but the added action is not QtxAction and bound to non-empty key sequence. name: \"" + aQAction->toolTip() + "\".");
+#endif//SHORTCUT_MGR_DBG
+          // Since non-QtxAction has no ID, it is impossible to properly manage its shortcut.
+          // And the shortcut may interfere with managed ones.
+          aQAction->setShortcut(NO_KEYSEQUENCE);
+        }
+      }
+    }
+  }
+
+  return QObject::eventFilter(theObject, theEvent);
+}
+
+std::set<std::pair<QString, QString>> SUIT_ShortcutMgr::setShortcutNoIDChecks(const QString& theModuleID, const QString& theInModuleActionID, const QKeySequence& theKeySequence, bool theOverride)
+{
+  std::set<std::pair<QString, QString>> disabledShortcutsIDs = myShortcutContainer.setShortcut(theModuleID, theInModuleActionID, theKeySequence, theOverride);
+
+  if (theOverride || disabledShortcutsIDs.empty()) {
+    // Bind actions to corresponding modified key sequences. Save changes to preferences.
+
+    /** { moduleID, {inModuleActionID, keySequence}[] }[] */
+    std::map<QString, std::map<QString, QKeySequence>> modifiedShortcuts;
+
+    for (const auto& moduleIDAndActionID : disabledShortcutsIDs) {
+      // Unbind actions of disabled shortcuts.
+
+      const QString& moduleID = moduleIDAndActionID.first;
+      const QString& inModuleActionID = moduleIDAndActionID.second;
+
+      std::map<QString, QKeySequence>& modifiedModuleShortcuts = modifiedShortcuts[moduleID];
+      modifiedModuleShortcuts[inModuleActionID] = NO_KEYSEQUENCE;
+
+      const std::set<QAction*> actions = getActions(moduleID, inModuleActionID);
+      for (QAction* const action : actions) {
+        action->setShortcut(NO_KEYSEQUENCE);
+      }
+    }
+
+    { // Bind actions to theKeySequence.
+      std::map<QString, QKeySequence>& modifiedModuleShortcuts = modifiedShortcuts[theModuleID];
+      modifiedModuleShortcuts[theInModuleActionID] = theKeySequence;
+
+      const std::set<QAction*> actions = getActions(theModuleID, theInModuleActionID);
+      for (QAction* const action : actions) {
+        action->setShortcut(theKeySequence);
+      }
+    }
+
+    SUIT_ShortcutMgr::saveShortcutsToPreferences(modifiedShortcuts);
+  }
+
+  return disabledShortcutsIDs;
+}
+
+void SUIT_ShortcutMgr::setShortcutsFromPreferences()
+{
+  ShCutDbg() && ShCutDbg("ShortcutMgr is initializing...");
+
+  SUIT_ShortcutContainer container;
+  SUIT_ShortcutMgr::fillContainerFromPreferences(container, false /*theDefaultOnly*/);
+  mergeShortcutContainer(container, true /*theOverrde*/, false /*theTreatAbsentIncomingAsDisabled*/);
+  setAssetsFromResources();
+
+  ShCutDbg() && ShCutDbg("ShortcutMgr has been initialized.");
+}
+
+/*static*/ void SUIT_ShortcutMgr::saveShortcutsToPreferences(const std::map<QString, std::map<QString, QKeySequence>>& theShortcutsInversed)
 {
-  QtxAction* anAction = (QtxAction*)obj;
-  
-  if ( anAction )
-    myShortcutActions.remove( anAction->shortcutActionName(), anAction );
+  ShCutDbg() && ShCutDbg("Saving preferences to resources.");
+
+  SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
+  if (!resMgr) {
+    Warning("SUIT_ShortcutMgr can't retrieve resource manager!");
+    return;
+  }
+
+  for (const auto& moduleIDAndShortcutsInversed : theShortcutsInversed) {
+    const auto& moduleID = moduleIDAndShortcutsInversed.first;
+    const auto& moduleShortcutsInversed = moduleIDAndShortcutsInversed.second;
+    for (const auto& shortcutInversed : moduleShortcutsInversed) {
+      if (shortcutInversed.first.isEmpty()) {
+        ShCutDbg("Attempt to serialize a shortcut with empty action ID.");
+        continue;
+      }
+
+      const QString sectionName = SECTION_NAME_PREFIX + resMgr->sectionsToken() + moduleID;
+      resMgr->setValue(sectionName, shortcutInversed.first, shortcutInversed.second.toString());
+
+      ShCutDbg() && ShCutDbg("Saving shortcut: \"" + moduleID + "\"\t\"" + shortcutInversed.first + "\"\t\"" + shortcutInversed.second.toString() + "\"");
+    }
+  }
 }
+
+void SUIT_ShortcutMgr::setAssetsFromResources(QString theLanguage)
+{
+  ShCutDbg() && ShCutDbg("Retrieving action assets.");
+
+  SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
+  if (!resMgr) {
+    Warning("SUIT_ShortcutMgr can't retrieve resource manager!");
+    return;
+  }
+
+  if (theLanguage.isEmpty())
+    theLanguage = resMgr->stringValue(LANG_SECTION, LANG_SECTION, DEFAULT_LANG);
+
+  QStringList langPriorityList = LANG_PRIORITY_LIST;
+  langPriorityList.push_front(theLanguage);
+  langPriorityList.removeDuplicates();
+
+  QStringList actionAssetFilePaths = resMgr->parameters(SECTION_NAME_ACTION_ASSET_FILE_PATHS);
+#ifdef SHORTCUT_MGR_DBG
+  ShCutDbg("Asset files: " + actionAssetFilePaths.join(", ") + ".");
+#endif
+  for (const QString& actionAssetFilePath : actionAssetFilePaths) {
+    const QString path = substituteVars(actionAssetFilePath);
+    QFile actionAssetFile(path);
+    if (!actionAssetFile.open(QIODevice::ReadOnly)) {
+      Warning("SUIT_ShortcutMgr can't open action asset file \"" + path + "\"!");
+      continue;
+    }
+
+    QJsonParseError jsonError;
+    QJsonDocument document = QJsonDocument::fromJson(actionAssetFile.readAll(), &jsonError);
+    actionAssetFile.close();
+    if(jsonError.error != QJsonParseError::NoError) {
+        Warning("SUIT_ShortcutMgr: error during parsing of action asset file \"" + path + "\"!");
+        continue;
+    }
+
+    if(document.isObject()) {
+      QJsonObject object = document.object();
+      SUIT_ActionAssets actionAssets;
+      for (const QString& actionID : object.keys()) {
+        if (!SUIT_ShortcutMgr::isActionIDValid(actionID)) {
+          ShCutDbg("Action asset file \"" + path + "\" contains invalid action ID \"" + actionID + "\".");
+          continue;
+        }
+
+        if (!actionAssets.fromJSON(object[actionID].toObject())) {
+          ShCutDbg("Action asset file \"" + path + "\" contains invalid action assets with ID \"" + actionID + "\".");
+          continue;
+        }
+
+        const bool nameInCurLangExists = actionAssets.myLangDependentAssets.find(theLanguage) != actionAssets.myLangDependentAssets.end();
+        if (nameInCurLangExists) {
+          actionAssets.clearAllLangsExcept(theLanguage);
+        }
+        else {
+          bool nameInLinguaFrancaExists = false;
+          QString usedLanguage = QString();
+          for (int i = 1; i < langPriorityList.length(); i++) {
+            nameInLinguaFrancaExists = actionAssets.myLangDependentAssets.find(langPriorityList[i]) != actionAssets.myLangDependentAssets.end();
+            if (nameInLinguaFrancaExists) {
+              usedLanguage = langPriorityList[i];
+              actionAssets.clearAllLangsExcept(usedLanguage);
+              break;
+            }
+          }
+
+          #ifdef SHORTCUT_MGR_DBG
+          if (nameInLinguaFrancaExists)
+            ShCutDbg("Can't find assets for action with ID \"" + actionID + "\" at current (" + theLanguage + ") language. Assets in " + usedLanguage + " is used for the action." );
+          else {
+            ShCutDbg("Can't find assets for action with ID \"" + actionID + "\". Tried " + langPriorityList.join(", ") + " languages." );
+            continue;
+          }
+          #endif
+        }
+
+        auto itAssets = myActionAssets.find(actionID);
+        if (itAssets == myActionAssets.end()) {
+          auto pAssets = std::shared_ptr<SUIT_ActionAssets>(new SUIT_ActionAssets(actionAssets));
+          itAssets = myActionAssets.emplace(actionID, pAssets).first;
+        }
+        else
+          itAssets->second->merge(actionAssets, true);
+
+        const auto& assets = itAssets->second;
+        if (!assets->myIconPath.isEmpty() && assets->myIcon.isNull())
+          assets->myIcon = QIcon(substituteVars(assets->myIconPath));
+      }
+    }
+  }
+
+  #ifdef SHORTCUT_MGR_DBG
+  ShCutDbg("Parsed assets: ");
+  QJsonObject object;
+  for (const auto& actionIDAndAssets : myActionAssets) {
+    actionIDAndAssets.second->toJSON(object);
+    QJsonDocument doc(object);
+    QString strJson = doc.toJson(QJsonDocument::Indented);
+    ShCutDbg(actionIDAndAssets.first + " : " +  strJson);
+  }
+  #endif
+
+  // Fill myModuleAssets.
+  for (const auto& moduleID : myShortcutContainer.getIDsOfAllModules()) {
+    const auto assets = std::shared_ptr<SUIT_ActionAssets>(new SUIT_ActionAssets());
+    auto& lda = assets->myLangDependentAssets[DEFAULT_LANG];
+
+    if (moduleID == ROOT_MODULE_ID) {
+      lda.myName = tr("General");
+
+      { // Load icon.
+        QString dirPath;
+        if (resMgr->value("resources", "LightApp", dirPath)) {
+          assets->myIconPath = dirPath + (!dirPath.isEmpty() && dirPath[dirPath.length() - 1] == "/" ? "" : "/") + "icon_default.png";
+          assets->myIcon = QIcon(substituteVars(assets->myIconPath));
+        }
+      }
+    }
+    else {
+      QString moduleName = moduleID;
+      resMgr->value(moduleID, "name", moduleName);
+      lda.myName = moduleName;
+
+      resMgr->value(moduleID, "description", lda.myToolTip);
+
+      { // Load icon.
+        QString dirPath;
+        QString fileName;
+        if (resMgr->value("resources", moduleID, dirPath) && resMgr->value(moduleID, "icon", fileName)) {
+          assets->myIconPath = dirPath + (!dirPath.isEmpty() && dirPath[dirPath.length() - 1] == "/" ? "" : "/") + fileName;
+          assets->myIcon = QIcon(substituteVars(assets->myIconPath));
+        }
+      }
+    }
+
+    myModuleAssets.emplace(moduleID, std::move(assets));
+  }
+}
\ No newline at end of file
index c86363f693d0f741bcce8cdb9e54e75765bff5ee..c8debfb81b1094fb5681980cfbe95fb7954c80b6 100644 (file)
 #include "SUIT.h"
 
 #include <QObject>
-#include <QMultiMap>
+#include <QString>
+#include <QIcon>
+#include <map>
+#include <set>
+#include <memory>
+#include <utility>
 
+class QAction;
 class QtxAction;
-
 class QKeySequence;
+class QJsonObject;
 
 #if defined WIN32
 #pragma warning( disable: 4251 )
 #endif
 
+// Define SHORTCUT_MGR_DBG to enable SUIT_ShortcutMgr debug logging.
+// #define SHORTCUT_MGR_DBG
+/*! \returns true, if SUIT_ShortcutMgr debug logging is enabled. */
+SUIT_EXPORT extern inline bool ShCutDbg() {
+#ifdef SHORTCUT_MGR_DBG
+  return true;
+#else
+  return false;
+#endif
+}
+/*! \brief Prints theString to std::wcout, if SUIT_ShortcutMgr debug logging is enabled. */
+SUIT_EXPORT extern bool ShCutDbg(const QString& theString);
+/*! \brief Prints theString to std::wcout, if SUIT_ShortcutMgr debug logging is enabled. */
+SUIT_EXPORT extern bool ShCutDbg(const char* theString);
+
+
+/*!
+  \class SUIT_ShortcutContainer
+  \brief Provides means to keep and edit shortcuts in compliance with the application logics.
+  \ref See SUIT_ShortcutMgr for details.
+*/
+class SUIT_EXPORT SUIT_ShortcutContainer
+{
+public:
+  SUIT_ShortcutContainer();
+
+  /*! \returns IDs of modules, which interfere with the module:
+  if the module is root (theModuleID is empty) - returns all module IDs, otherwise returns ["", theModuleID]. */
+  std::set<QString> getIDsOfInterferingModules(const QString& theModuleID) const;
+
+  std::set<QString> getIDsOfAllModules() const;
+
+  /*! \brief Checks for conflicts. If theOverride, modifies incoming and disables all conflicting shortcuts.
+  Redefining a key sequence for the action, if theKeySequence does not conflict with other shortcuts, is not considered as a conflict.
+  \param theModuleID The method has no effect if theModuleID is invalid. \ref See SUIT_ShortcutMgr::isModuleIDValid(const QString&) for details.
+  \param theInModuleActionID The method has no effect if theInModuleActionID is invalid. \ref See SUIT_ShortcutMgr::isInModuleActionIDValid(const QString&).
+  If theInModuleActionID is meta-action ID, the shortcut is set to root module, and theModuleID is ignored.
+  \param theKeySequence Empty theKeySequence does not cause conflicts, in this case
+  a shortcut for the action is disabled: theInModuleActionID is added/retained in the container but mapped to empty key sequence.
+  \param theOverride If true, conflicting shortcuts are disabled.
+  \returns {moduleID, inModuleActionID}[] - Set of conflicting actions if theOverride = false,
+  otherwise set of actions (without incoming one), whose shortcuts have been disabled. */
+  std::set<std::pair<QString, QString>> setShortcut(
+    QString theModuleID,
+    const QString& theInModuleActionID,
+    const QKeySequence& theKeySequence,
+    bool theOverride
+  );
+
+  /*! \brief Checks for conflicts. Existence of a shortcut with another key sequence for the action,
+  if theKeySequence does not conflict with other shortcuts, is not considered as a conflict.
+  \param theInModuleActionID If theInModuleActionID is meta-action ID, the shortcut is looked for in root module, and theModuleID is ignored.
+  \param theKeySequence Empty theKeySequence does not have conflicts.
+  \returns {moduleID, inModuleActionID}[] - Set of conflicting actions. */
+  std::set<std::pair<QString, QString>> getConflicts(
+    QString theModuleID,
+    const QString& theInModuleActionID,
+    const QKeySequence& theKeySequence
+  ) const;
+
+  /*! \returns empty key sequence if shortcut for the action is not set.
+  \param theInModuleActionID If theInModuleActionID is meta-action ID, seeks in root module, and theModuleID is ignored.*/
+  const QKeySequence& getKeySequence(QString theModuleID, const QString& theInModuleActionID) const;
+
+  /*! \returns true, if shortcut for the action is set (even if the mapped key sequence is empty).
+   \param theInModuleActionID If theInModuleActionID is meta-action ID, seeks in root module, and theModuleID is ignored.*/
+  bool hasShortcut(QString theModuleID, const QString& theInModuleActionID) const;
+
+  /*! \returns {inModuleActionID, keySequence}[] - If the module was not added, the map is empty. */
+  const std::map<QString, QKeySequence>& getModuleShortcutsInversed(const QString& theModuleID) const;
+
+  /*! \brief Seeks for shortcuts in the module with in-module action IDs, which start with theInModuleActionIDPrefix.
+  \returns {inModuleActionID, keySequence}[] - If the module was not added, the map is empty. */
+  const std::map<QString, QKeySequence> getModuleShortcutsInversed(const QString& theModuleID, const QString& theInModuleActionIDPrefix) const;
+
+  /*! \brief Merges shortcuts of theOther into this.
+  \param theOverride if true, overrides conflicting shortcuts.
+  If false, and this has no shortcut for an incoming action, and the incoming shortcut conflicts
+  with an existing shortcut, disabled shortcut for the incoming action is set.
+  \param theTreatAbsentIncomingAsDisabled If theOverride == false, theTreatAbsentIncomingAsDisabled is ignored.
+  If theOverride and theTreatAbsentIncomingAsDisabled, and theOther has no shortcut for an action, which exists in this,
+  the existing shortcut in this is set disabled.
+  \returns { moduleID, { inModuleActionID, keySequence }[] }[] - Modiified shortcuts inversed. */
+  std::map<QString, std::map<QString, QKeySequence>> merge(
+    const SUIT_ShortcutContainer& theOther,
+    bool theOverride,
+    bool theTreatAbsentIncomingAsDisabled = false
+  );
+
+  /*! \brief Generates human-readable text representation of content. */
+  QString toString() const;
+
+private:
+  /** { moduleID, { keySequence, inModuleActionID }[] }[]. keySequence can not be empty.
+   * Can not contain entries like { <non-root module ID>, { keySequence, <meta-action ID> } }. */
+  std::map<QString, std::map<QKeySequence, QString>> myShortcuts;
+
+  /** { moduleID, { inModuleActionID, keySequence }[] }[]. keySequence can be empty.
+   * Can not contain entries like { <non-root module ID>, { <meta-action ID>, keySequence } }. */
+  std::map<QString, std::map<QString, QKeySequence>> myShortcutsInversed;
+};
+
+
+/*! \brief GUI-related assets. */
+struct SUIT_EXPORT SUIT_ActionAssets
+{
+  struct LangDependentAssets
+  {
+    static const QString PROP_ID_NAME;
+    static const QString PROP_ID_TOOLTIP;
+
+    bool fromJSON(const QJsonObject& theJsonObject);
+    void toJSON(QJsonObject& oJsonObject) const;
+
+    QString myName;
+    QString myToolTip;
+  };
+
+  static const QString STRUCT_ID;
+  static const QString PROP_ID_LANG_DEPENDENT_ASSETS;
+  static const QString PROP_ID_ICON_PATH;
+
+  bool fromJSON(const QJsonObject& theJsonObject);
+  void toJSON(QJsonObject& oJsonObject) const;
+  QString toString() const;
+
+  QStringList getLangs() const;
+  void clearAllLangsExcept(const QString& theLang);
+
+  /*! \param theOverride If true, values of theOther override conflicting values of this. */
+  void merge(const SUIT_ActionAssets& theOther, bool theOverride);
+
+  std::map<QString, LangDependentAssets> myLangDependentAssets;
+  QString myIconPath;
+
+  /*! Is not serialized. */
+  QIcon myIcon;
+};
+
+
 /*!
   \class SUIT_ShortcutMgr
-  \brief Class which manages shortcuts customization.
+  \brief Handles action shortcut customization.
+
+  Register actions under action IDs. Set shortcuts, which are [action ID]<->[key sequence] mappings.
+  Every time an action is registered or a shorcut is set, if there are an action and a shortcut,
+  which are mapped to the same action ID, the action is bound to the key sequence of the shortcut.
+  Action IDs are also used to (de)serialize shortcut settings.
+  Several QActions may be registered under the same ID.
+
+  Most of actions are intercepted on creation in SUIT_ShortcutMgr:eventFilter(QObject* theObject, QEvent* theEvent).
+  If an intercepted action is instance of QtxAction, it is registered automatically.
+  Since non-QtxActions have no member ID(), SUIT_ShortcutMgr is unable to register them automatically
+  in SUIT_ShortcutMgr::eventFilter(). Thus, every non-QtxAction should be
+  passed to SUIT_ShortcutMgr::registerAction(const QString& theActionID, QAction* theAction).
+
+  Action ID is application-unique must be composed as <moduleID>/<inModuleActionID>.
+  If an action belongs to a desktop or is available even if no module is active (e.g. 'Save As'),
+  use empty string as <moduleID>. Let's call such actions as root actions.
+
+  There is a need to keep multiple actions, which do the same from user' perspective,
+  bound to the same key sequence. E.g. 'Front view'. Let's call such set of actions as meta-action.
+  Actions of a meta-action may belong to different modules, and/or there may be several actions
+  of the same meta-action in the same module. <inModuleActionID> of all members of a meta-action
+  must be the same and start with "#".
+  Meta-action is root action when it comes to checking for conflicts.
+  Shortcuts of meta-actions are (de)serialized to the same section of preference files as root shortcuts.
+
+  <inModuleActionID> can contain several "/". Go to \ref isInModuleActionIDValid(const QString&) for details.
+  You can refer to multiple actions, whose <inModuleActionID> starts with the prefix.
+
+  Only one module can be active at instance. So a key sequence must be unique within a joined temporary table of
+  root and active module shortcuts. An action is allowed to be bound with only key sequence.
+
+  WARNING!
+  Avoid assigning shortcuts to instances of QAction and all its descendants directly.
+  (1) Key sequence being bound directly to any registered/intercepted action with valid ID,
+      if the key sequence does not conflict with shortcuts kept by SUIT_ShortcutMgr,
+      is added to the manager and appears in user preference file. If it does conflict,
+      it is reassigned with a key sequence from preference files or
+      disabled and added to user preference files (if the files have no shortcut for the action).
+  (2) Key sequences being bound directly to non-QtxAction instances are disabled.
 */
-class SUIT_EXPORT SUIT_ShortcutMgr: public QObject 
+class SUIT_EXPORT SUIT_ShortcutMgr: public QObject
 {
   Q_OBJECT
-public:
-  static void Init();
-  static SUIT_ShortcutMgr* getShortcutMgr();
 
-  void setSectionEnabled( const QString&, const bool = true );
-  void updateShortcuts();
+private:
+  SUIT_ShortcutMgr();
+  SUIT_ShortcutMgr(const SUIT_ShortcutMgr&) = delete;
+  SUIT_ShortcutMgr& operator=(const SUIT_ShortcutMgr&) = delete;
 
 protected:
-  SUIT_ShortcutMgr();
   virtual ~SUIT_ShortcutMgr();
 
+public:
+  /*! \brief Create new singleton-instance of shortcut manager, if it has not been created. */
+  static void Init();
+
+  static SUIT_ShortcutMgr* get();
+
+  /*! \brief Checks whether the theKeySequence is platform-compatible. */
+  static bool isKeySequenceValid(const QKeySequence& theKeySequence);
+
+  /*! \returns {false, _ } if  theKeySequenceString is invalid. */
+  static std::pair<bool, QKeySequence> toKeySequenceIfValid(const QString& theKeySequenceString);
+
+  /*! \brief Valid module ID does not contain "/" and equals to result of QString(theModuleID).simplified().
+  Empty module ID is valid - it is root module ID. */
+  static bool isModuleIDValid(const QString& theModuleID);
+
+  /*! \brief Valid in-module action ID may consist of several tokens, separated by "/":
+  <token_0>/<token_1>...<token_N>/<token_N-1>.
+  Each <token> must be non-empty and be equal to QString(<token>).simplified().
+  Empty or "#" in-module action ID is not valid. */
+  static bool isInModuleActionIDValid(const QString& theInModuleActionID);
+
+  /*! \returns true, is theInModuleActionID starts with "#". */
+  static bool isInModuleMetaActionID(const QString& theInModuleActionID);
+
+  /*! \brief Extracts module ID and in-module action ID from application-unique action ID.
+  The theActionID must be composed as <moduleID>/<inModuleActionID>.
+  \returns { _ , "" }, if theActionID is invalid. */
+  static std::pair<QString, QString> splitIntoModuleIDAndInModuleID(const QString& theActionID);
+
+  /*! See \ref splitIntoModuleIDAndInModuleID(const QString&). */
+  static bool isActionIDValid(const QString& theActionID);
+
+  /*! \brief Creates application-unique action ID. Reverse to splitIntoModuleIDAndInModuleID.
+  \returns Emppty string, if either theModuleID or theInModuleActionID is invalid*/
+  static QString makeActionID(const QString& theModuleID, const QString& theInModuleActionID);
+
+  /*! \brief Sets all shortcuts from preferences to theContainer. Incoming shortcuts override existing ones.
+  If the container has shortcut for an action, which is absent in preferences, and the existing shortcut
+  does not conflict with incoming ones, it is untouched.
+  See \ref setShortcutsFromPreferences() for details.
+  \param theDefaultOnly If true, user preferences are ignored and only default preferences are used. */
+  static void fillContainerFromPreferences(SUIT_ShortcutContainer& theContainer, bool theDefaultOnly);
+
+  /*! \brief Checks the resource manager directly.
+  \returns {assetsExist, assets}. */
+  static std::pair<bool, SUIT_ActionAssets> getActionAssetsFromResources(const QString& theActionID);
+
+  /*! \returns Language being set in resource manager. */
+  static QString getLang();
+
+
+  /*! \brief Add theAction to map of managed actions. */
+  void registerAction(const QString& theActionID, QAction* theAction);
+
+  /*! \brief Add theAction to map of managed actions. QtxAction::ID() is used as action ID. */
+  void registerAction(QtxAction* theAction);
+
+  /*! \brief Get registered actions. If theInModuleActionID is meta-action ID, seeks in all modules. */
+  std::set<QAction*> getActions(const QString& theModuleID, const QString& theInModuleActionID) const;
+
+  /*! \brief Get module ID and in-module-ID of theAction.
+  \returns { _ , "" } if theAction is not registered. */
+  std::pair<QString, QString> getModuleIDAndInModuleID(const QAction* theAction) const;
+
+  /*! Returns true if theAction is registered. */
+  bool hasAction(const QAction* theAction) const;
+
+  /*! \brief Get action ID of theActon.
+  \returns empty string if theAction is not registered. */
+  QString getActionID(const QAction* theAction) const;
+
+  /*! \brief Enables/disable actions of the module.
+  Only those actions are affected, whose parent widget is active desktop. */
+  void setActionsOfModuleEnabled(const QString& theModuleID, const bool theEnable = true) const;
+
+  /*! \brief Enables/disables all registered actions whose in-module action ID begins with theInModuleActionIDPrefix.
+  Only those actions are affected, whose parent widget is active desktop. */
+  void setActionsWithPrefixInIDEnabled(const QString& theInModuleActionIDPrefix, bool theEnable = true) const;
+
+  [[deprecated("Use setActionsWithPrefixInIDEnabled(const QString&, bool) instead.")]]
+  void setSectionEnabled(const QString& theInModuleActionIDPrefix, bool theEnable = true) const;
+
+  /*! \brief For all registered actions binds key sequences from myShortcutContainer. */
+  void rebindActionsToKeySequences() const;
+
+  [[deprecated("Use rebindActionsToKeySequences() instead.")]]
+  void updateShortcuts() const;
+
+  /*! \brief Checks for conflicts. If theOverride, modifies incoming and disables all conflicting shortcuts.
+  Then binds key sequences with corresponding registered actions. Saves changes to preferences.
+
+  Redefining a key sequence for the action, if the key sequence does not conflict with other shortcuts, is not considered as a conflict.
+  \param theInModuleActionID The method has no effect if theInModuleActionID is empty.
+  \param theKeySequence Empty theKeySequence does not cause conflicts, in this case
+  a shortcut for the action is disabled: theInModuleActionID is added/retained in the container but mapped to empty key sequence.
+  \param theOverride If true, conflicting shortcuts are disabled.
+  \returns {moduleID, inModuleActionID}[] - Set of conflicting actions if theOverride = false,
+  otherwise set of actions (without incoming one), whose shortcuts have been disabled. */
+  std::set<std::pair<QString, QString>> setShortcut(const QString& theActionID, const QKeySequence& theKeySequence, bool theOverride = false);
+  std::set<std::pair<QString, QString>> setShortcut(const QString& theModuleID, const QString& theInModuleActionID, const QKeySequence& theKeySequence, bool theOverride = false);
+
+  const SUIT_ShortcutContainer& getShortcutContainer() const;
+
+  /*! \brief Does not perform validity checks on theModuleID and theInModuleActionID. */
+  void mergeShortcutContainer(const SUIT_ShortcutContainer& theContainer, bool theOverride = true, bool theTreatAbsentIncomingAsDisabled = false);
+
+  /*! \brief Get a key sequence mapped to the action. */
+  QKeySequence getKeySequence(const QString& theModuleID, const QString& theInModuleActionID) const;
+
+  /*! \returns {inModuleActionID, keySequence}[] */
+  const std::map<QString, QKeySequence>& getModuleShortcutsInversed(const QString& theModuleID) const;
+
+  /*! \returns All module IDs, which were added to myShortcutContainer. */
+  std::set<QString> getShortcutModuleIDs() const;
+
+  /*! \returns IDs of modules, which interfere with the module:
+  if the module is root (theModuleID is empty) - returns all module IDs, otherwise returns ["", theModuleID]. */
+  std::set<QString> getIDsOfInterferingModules(const QString& theModuleID) const;
+
+  std::shared_ptr<const SUIT_ActionAssets> getModuleAssets(const QString& theModuleID) const;
+
+  /*! \brief Retrieves module name, if the asset was loaded using \ref setAssetsFromResources(). If theLang is empty, it is effectively current language. */
+  QString getModuleName(const QString& theModuleID, const QString& theLang = "") const;
+
+  std::shared_ptr<const SUIT_ActionAssets> getActionAssets(const QString& theModuleID, const QString& theInModuleActionID) const;
+
+  std::shared_ptr<const SUIT_ActionAssets> getActionAssets(const QString& theActionID) const;
+
+  /*! \brief Retrieves action name, if the asset was loaded using \ref setAssetsFromResources(). If theLang is empty, it is effectively current language. */
+  QString getActionName(const QString& theModuleID, const QString& theInModuleActionID, const QString& theLang = "") const;
+
 private slots:
-  void onActionDestroyed( QObject* );
+  /*!
+  \brief Called when the corresponding action is destroyed.
+  Removes destroyed action from maps of registered actions. Preserves shortcut.
+  \param theObject action being destroyed.
+  */
+  void onActionDestroyed(QObject* theObject);
 
 private:
-  virtual bool eventFilter( QObject* o, QEvent* e );
+  /*! \brief Overrides QObject::eventFilter().
+  If theEvent is QEvent::ActionAdded and the action is instance of QtxAction, registers it. */
+  virtual bool eventFilter(QObject* theObject, QEvent* theEvent);
+
+  /*! \brief Does not perform validity checks on theModuleID and theInModuleActionID. */
+  std::set<std::pair<QString, QString>> setShortcutNoIDChecks(const QString& theModuleID, const QString& theInModuleActionID, const QKeySequence& theKeySequence, bool theOverride);
 
-  void processAction( QtxAction* );
-  QKeySequence getShortcutByActionName( const QString& ) const;
+  /*! \brief Set shortcuts from preference files. The method is intended to be called before any calls to setShortcut() or mergeShortcutContainer().
+  Definition of this method assumes, that shortcut settings are serialized as prerefence entries {name=<inModuleActionID>, val=<keySequence>}
+  in dedicated section for each module, with names of sections being composed as "shortcuts:<moduleID>".
+
+  E.g. in case of XML file it may look like this:
+  <!--
+  <section name="<module ID>">
+    ...
+    <parameter name="<in-module action ID>" value="key sequence">
+    ...
+  </section>
+  -->
+  <section name="shortcuts:">
+    <parameter name="TOT_DESK_FILE_NEW" value="Ctrl+N"/>
+    <parameter name="TOT_DESK_FILE_OPEN" value="Ctrl+O"/>
+    <parameter name="#General/Show object(s)" value="Ctrl+Alt+S"/>
+    <parameter name="#General/Hide object(s)" value="Ctrl+Alt+H"/>
+    <parameter name="#Viewers/Back view" value="Ctrl+Alt+B"/>
+    <parameter name="#Viewers/Front view" value="Ctrl+Alt+F"/>
+  </section>
+   <section name="shortcuts:GEOM">
+    <parameter name="Isolines/Increase number" value="Meta+I"/>
+    <parameter name="Isolines/Decrease number" value="Meta+D"/>
+    <parameter name="Transparency/Increase" value="Meta+Y"/>
+    <parameter name="Transparency/Decrease" value="Meta+T"/>
+  </section>
+
+  Empty inModuleActionIDs are ignored.
+
+  nb! For any theQtxAction you wish user be able to assign it to a shortcut,
+  add theQtxAction.ID() to default resource files (you can map it to no key sequence).*/
+  void setShortcutsFromPreferences();
+
+  /*! \brief Writes shortcuts to preference files.
+  \param theShortcuts { moduleID, { inModuleActionID, keySequence }[] }[]. Empty inModuleActionIDs are ignored. */
+  static void saveShortcutsToPreferences(const std::map<QString, std::map<QString, QKeySequence>>& theShortcutsInversed);
+
+  /*! Fills myActionAssets from asset files in theLanguage.
+  \param theLanguage If default, fills assets in current language.
+  If an asset in requested language is not found, seeks for the asset EN in and then in FR.
+
+  Asset files must be structured like this:
+  {
+    ...
+    actionID : {
+      "langDependentAssets": {
+        ...
+        lang: {
+          "name": name,
+          "tooltip": tooltip
+        },
+        ...
+      },
+      "iconPath": iconPath
+    },
+    ...
+  }
+  */
+  void setAssetsFromResources(QString theLanguage = QString());
 
 private:
   static SUIT_ShortcutMgr* myShortcutMgr;
-  QMultiMap<QString, QtxAction*> myShortcutActions;
+
+  /** { moduleID, { inModuleActionID, action[] }[] }[]. May contain entries like { <non-root module ID>, { <meta-action ID>, actions[] } }. */
+  std::map<QString, std::map<QString, std::set<QAction*>>> myActions;
+
+  /** { action, { moduleID, inModuleActionID } }[]. May contain entries like { <non-root module ID>, { <meta-action ID>, actions[] } }. */
+  std::map<QAction*, std::pair<QString, QString>> myActionIDs; // To maintain uniqueness of actions and effectively retrieve IDs of registered actions.
+
+  /** Can not contain entries like { <non-root module ID>, { <meta-action ID>, actions[] } }. */
+  SUIT_ShortcutContainer myShortcutContainer;
+
+  /* nb!
+  Sets of moduleIDs and inModuleActionIDs are equal for myActions and myActionIDs.
+  Sets of moduleIDs and inModuleActionIDs may NOT be equal for myActions and myShortcutContainer.
+  */
+
+  /* {actionID, assets}[] */
+  std::map<QString, std::shared_ptr<SUIT_ActionAssets>> myActionAssets;
+
+  /* {moduleID, assets}[] */
+  mutable std::map<QString, std::shared_ptr<SUIT_ActionAssets>> myModuleAssets;
 };
 
 #if defined WIN32
index 97f2cf33def5d1571587908cea522fe80abf3b6b..bdbbb196e3f99171ab9b0e145805c9b94e6b2ff5 100644 (file)
@@ -170,5 +170,32 @@ Voulez-vous l&apos;écraser ?</translation>
         <source>TLT_IMAGE_FILES</source>
         <translation>Fichiers images (*.bmp *.png *.jpg *.jpeg)</translation>
     </message>
-</context>     
+</context>
+<context>
+    <name>SUIT_ShortcutMgr</name>
+    <message>
+        <source>General</source>
+        <translation>Général</translation>
+    </message>
+     <message>
+        <source>Invalid module IDs</source>
+        <translation>ID de module invalides</translation>
+    </message>
+    <message>
+        <source>Invalid shortcuts</source>
+        <translation>Raccourcis invalides</translation>
+    </message>
+    <message>
+        <source>These shortcuts have not been set, because they conflict with previously parsed ones</source>
+        <translation>Ces raccourcis n'ont pas été définis car ils entrent en conflit avec ceux analysés précédemment</translation>
+    </message>
+    <message>
+        <source>Invalid shortcuts in preferences</source>
+        <translation>Raccourcis invalides dans les préférence</translation>
+    </message>
+    <message>
+        <source>Fix the following entries in the preference files manually</source>
+        <translation>Corrigez manuellement les entrées suivantes dans les fichiers de préférences</translation>
+    </message>
+</context>
 </TS>
index 8da7266dbb2c71e43d0bb2ca53bcdf0a05b7b6da..2fb333637a5b516eef5477ce4619f3f1d7ad3add 100644 (file)
       <source>TLT_IMAGE_FILES</source>
       <translation>イメージ (*.bmp *.png *.jpg *.jpeg) ファイル</translation>
     </message>
-</context>     
+</context>
+<context>
+    <name>SUIT_ShortcutMgr</name>
+    <message>
+        <source>General</source>
+        <translation>一般的な</translation>
+    </message>
+     <message>
+        <source>Invalid module IDs</source>
+        <translation>無効なモジュール ID</translation>
+    </message>
+    <message>
+        <source>Invalid shortcuts</source>
+        <translation>無効なショートカットです</translation>
+    </message>
+    <message>
+        <source>These shortcuts have not been set, because they conflict with previously parsed ones</source>
+        <translation>これらのショートカットは、以前に解析されたショートカットと競合するため、設定されていません</translation>
+    </message>
+    <message>
+        <source>Invalid shortcuts in preferences</source>
+        <translation>環境設定のショートカットが無効です</translation>
+    </message>
+    <message>
+        <source>Fix the following entries in the preference files manually</source>
+        <translation>設定ファイル内の次のエントリを手動で修正します</translation>
+    </message>
+</context>
 </TS>
diff --git a/src/SUIT/resources/action_assets.json b/src/SUIT/resources/action_assets.json
new file mode 100644 (file)
index 0000000..eefb701
--- /dev/null
@@ -0,0 +1,699 @@
+{
+    "/#TOT_DESK_EDIT_COPY": {
+        "iconPath": "${GUI_ROOT_DIR}/share/salome/resources/gui/copy.png",
+        "langDependentAssets": {
+            "en": {
+                "name": "Copy",
+                "tooltip": "Copy the selection to the Clipboard"
+            },
+            "fr": {
+                "name": "Copier",
+                "tooltip": "Copier la sélection dans le presse-papiers"
+            },
+            "ja": {
+                "name": "コピー",
+                "tooltip": "選択範囲をクリップボードにコピー"
+            }
+        }
+    },
+    "/#Viewers/View/Reset": {
+        "iconPath": "",
+        "langDependentAssets": {
+            "en": {
+                "name": "Reset",
+                "tooltip": "Reset View Point"
+            },
+            "fr": {
+                "name": "Restaurer",
+                "tooltip": "Restaurer le point de vue"
+            },
+            "ja": {
+                "name": "復元",
+                "tooltip": "ビューのポイントを復元します。"
+            }
+        }
+    },
+    "/#Viewers/View/Rotate anticlockwise": {
+        "iconPath": "",
+        "langDependentAssets": {
+            "en": {
+                "name": "Rotate counterclockwise",
+                "tooltip": "Rotate view counterclockwise"
+            },
+            "fr": {
+                "name": "Tourner la vue à gauche",
+                "tooltip": "Tourner la vue à gauche"
+            },
+            "ja": {
+                "name": "表示を左に",
+                "tooltip": "表示を左に"
+            }
+        }
+    },
+    "/#Viewers/View/Rotate clockwise": {
+        "iconPath": "",
+        "langDependentAssets": {
+            "en": {
+                "name": "Rotate clockwise",
+                "tooltip": "Rotate View Clockwise"
+            },
+            "fr": {
+                "name": "Tourner la vue à droite",
+                "tooltip": "Tourner la vue à droite"
+            },
+            "ja": {
+                "name": "右のビューを回転させる",
+                "tooltip": "右のビューを回転させる"
+            }
+        }
+    },
+    "/#Viewers/View/Set X+": {
+        "iconPath": "",
+        "langDependentAssets": {
+            "en": {
+                "name": "+OX",
+                "tooltip": "+OX View"
+            },
+            "fr": {
+                "name": "+OX",
+                "tooltip": "Vue +OX"
+            },
+            "ja": {
+                "name": "+OX",
+                "tooltip": "+OX View"
+            }
+        }
+    },
+    "/#Viewers/View/Set X-": {
+        "iconPath": "",
+        "langDependentAssets": {
+            "en": {
+                "name": "-OX",
+                "tooltip": "-OX View"
+            },
+            "fr": {
+                "name": "-OX",
+                "tooltip": "Vue -OX"
+            },
+            "ja": {
+                "name": "-OX",
+                "tooltip": "-OX View"
+            }
+        }
+    },
+    "/#Viewers/View/Set Y+": {
+        "iconPath": "",
+        "langDependentAssets": {
+            "en": {
+                "name": "+OY",
+                "tooltip": "+OY View"
+            },
+            "fr": {
+                "name": "+OY",
+                "tooltip": "Vue +OY"
+            },
+            "ja": {
+                "name": "+OY",
+                "tooltip": "+OY View"
+            }
+        }
+    },
+    "/#Viewers/View/Set Y-": {
+        "iconPath": "",
+        "langDependentAssets": {
+            "en": {
+                "name": "-OY",
+                "tooltip": "-OY View"
+            },
+            "fr": {
+                "name": "-OY",
+                "tooltip": "Vue -OY"
+            },
+            "ja": {
+                "name": "-OY",
+                "tooltip": "-OY View"
+            }
+        }
+    },
+    "/#Viewers/View/Set Z+": {
+        "iconPath": "",
+        "langDependentAssets": {
+            "en": {
+                "name": "+OZ",
+                "tooltip": "+OZ View"
+            },
+            "fr": {
+                "name": "+OZ",
+                "tooltip": "Vue +OZ"
+            },
+            "ja": {
+                "name": "+OZ",
+                "tooltip": "+OZ View"
+            }
+        }
+    },
+    "/#Viewers/View/Set Z-": {
+        "iconPath": "",
+        "langDependentAssets": {
+            "en": {
+                "name": "-OZ",
+                "tooltip": "-OZ View"
+            },
+            "fr": {
+                "name": "-OZ",
+                "tooltip": "Vue -OZ"
+            },
+            "ja": {
+                "name": "-OZ",
+                "tooltip": "-OZ View"
+            }
+        }
+    },
+    "/#General/Object(s)/Hide": {
+        "iconPath": "",
+        "langDependentAssets": {
+            "en": {
+                "name": "Hide",
+                "tooltip": "Hide"
+            },
+            "fr": {
+                "name": "Cacher",
+                "tooltip": "Cacher"
+            },
+            "ja": {
+                "name": "非表示",
+                "tooltip": "非表示"
+            }
+        }
+    },
+    "/#General/Object(s)/Show": {
+        "iconPath": "",
+        "langDependentAssets": {
+            "en": {
+                "name": "Show",
+                "tooltip": "Show"
+            },
+            "fr": {
+                "name": "Afficher",
+                "tooltip": "Afficher"
+            },
+            "ja": {
+                "name": "表示",
+                "tooltip": "表示"
+            }
+        }
+    },
+    "/PRP_CLOSE": {
+        "iconPath": "",
+        "langDependentAssets": {
+            "en": {
+                "name": "Close",
+                "tooltip": "Close active window"
+            },
+            "fr": {
+                "name": "Fermer",
+                "tooltip": "Fermer la fenêtre active"
+            },
+            "ja": {
+                "name": "閉じる",
+                "tooltip": "アクティブ ウィンドウを閉じる"
+            }
+        }
+    },
+    "/PRP_CREATE_NEW_WINDOW_FOR_VIEWER_0": {
+        "iconPath": "",
+        "langDependentAssets": {
+            "en": {
+                "name": "GL 2D view",
+                "tooltip": "Create new GL 2D view"
+            },
+            "fr": {
+                "name": "Scène GL ",
+                "tooltip": "Créer une nouvelle Scène GL "
+            },
+            "ja": {
+                "name": "GL 2D view",
+                "tooltip": "新しい GL 2D view を作成します。"
+            }
+        }
+    },
+    "/PRP_CREATE_NEW_WINDOW_FOR_VIEWER_1": {
+        "iconPath": "",
+        "langDependentAssets": {
+            "en": {
+                "name": "Plot 2D view",
+                "tooltip": "Create new Plot 2D view"
+            },
+            "fr": {
+                "name": "Scène Plot2d ",
+                "tooltip": "Créer une nouvelle Scène Plot2d "
+            },
+            "ja": {
+                "name": "Plot 2D View",
+                "tooltip": "新しい Plot 2D View を作成します。"
+            }
+        }
+    },
+    "/PRP_CREATE_NEW_WINDOW_FOR_VIEWER_2": {
+        "iconPath": "",
+        "langDependentAssets": {
+            "en": {
+                "name": "OCC 3D view",
+                "tooltip": "Create new OCC 3D view"
+            },
+            "fr": {
+                "name": "Scène OCC",
+                "tooltip": "Créer une nouvelle Scène OCC"
+            },
+            "ja": {
+                "name": "OCC 3D View",
+                "tooltip": "新しい OCC 3D View を作成します。"
+            }
+        }
+    },
+    "/PRP_CREATE_NEW_WINDOW_FOR_VIEWER_3": {
+        "iconPath": "",
+        "langDependentAssets": {
+            "en": {
+                "name": "VTK 3D view",
+                "tooltip": "Create new VTK 3D view"
+            },
+            "fr": {
+                "name": "Scène VTK",
+                "tooltip": "Créer une nouvelle Scène VTK"
+            },
+            "ja": {
+                "name": "VTK 3D View",
+                "tooltip": "新しい VTK 3D View を作成します。"
+            }
+        }
+    },
+    "/PRP_CREATE_NEW_WINDOW_FOR_VIEWER_4": {
+        "iconPath": "",
+        "langDependentAssets": {
+            "en": {
+                "name": "QxScene 2D view",
+                "tooltip": "Create new QxScene 2D view"
+            },
+            "fr": {
+                "name": "Scène QxScene",
+                "tooltip": "Créer une nouvelle Scène QxScene"
+            },
+            "ja": {
+                "name": "シーン QxScene",
+                "tooltip": "新しい シーン QxScene を作成します。"
+            }
+        }
+    },
+    "/PRP_CREATE_NEW_WINDOW_FOR_VIEWER_5": {
+        "iconPath": "",
+        "langDependentAssets": {
+            "en": {
+                "name": "Graphics view",
+                "tooltip": "Create new Graphics view"
+            },
+            "fr": {
+                "name": "Scène Graphiques",
+                "tooltip": "Créer une nouvelle Scène Graphiques"
+            },
+            "ja": {
+                "name": "グラフィックの表示",
+                "tooltip": "新しい グラフィックの表示 を作成します。"
+            }
+        }
+    },
+    "/PRP_CREATE_NEW_WINDOW_FOR_VIEWER_6": {
+        "iconPath": "",
+        "langDependentAssets": {
+            "en": {
+                "name": "ParaView view",
+                "tooltip": "Create new ParaView view"
+            },
+            "fr": {
+                "name": "Scène ParaView",
+                "tooltip": "Créer une nouvelle Scène ParaView"
+            },
+            "ja": {
+                "name": "ParaView 表示 ",
+                "tooltip": "新しい ParaView 表示  を作成します。"
+            }
+        }
+    },
+    "/PRP_CREATE_NEW_WINDOW_FOR_VIEWER_7": {
+        "iconPath": "",
+        "langDependentAssets": {
+            "en": {
+                "name": "Python view",
+                "tooltip": "Create new Python view"
+            },
+            "fr": {
+                "name": "Vue Python",
+                "tooltip": "Créer une nouvelle Vue Python"
+            },
+            "ja": {
+                "name": "Python view",
+                "tooltip": "新しい Python view を作成します。"
+            }
+        }
+    },
+    "/PRP_CREATE_NEW_WINDOW_FOR_VIEWER_8": {
+        "iconPath": "",
+        "langDependentAssets": {
+            "en": {
+                "name": "ParaView3D view",
+                "tooltip": "Create new ParaView3D view"
+            },
+            "fr": {
+                "name": "ParaView3D view",
+                "tooltip": "Créer une nouvelle ParaView3D view"
+            },
+            "ja": {
+                "name": "ParaView3D view",
+                "tooltip": "新しい ParaView3D view を作成します。"
+            }
+        }
+    },
+    "/PRP_DESK_CATALOG_GENERATOR": {
+        "iconPath": "",
+        "langDependentAssets": {
+            "en": {
+                "name": "Catalog Generator",
+                "tooltip": "Generates XML catalog of a component's interface"
+            },
+            "fr": {
+                "name": "Genérateur de catalogue",
+                "tooltip": "Génére un catalogue XML de l'interface du composant"
+            },
+            "ja": {
+                "name": "カタログ ジェネレーター",
+                "tooltip": "コンポーネントインターフェイスのXMLカタログを生成"
+            }
+        }
+    },
+    "/PRP_DESK_CONNECT": {
+        "iconPath": "",
+        "langDependentAssets": {
+            "en": {
+                "name": "Connect",
+                "tooltip": "Connect active study"
+            },
+            "fr": {
+                "name": "Connecter",
+                "tooltip": "Connecter l'étude en cours"
+            },
+            "ja": {
+                "name": "接続",
+                "tooltip": "アクティブスタディの接続"
+            }
+        }
+    },
+    "/PRP_DESK_DISCONNECT": {
+        "iconPath": "",
+        "langDependentAssets": {
+            "en": {
+                "name": "Disconnect",
+                "tooltip": "Disconnect the current study"
+            },
+            "fr": {
+                "name": "Déconnecter",
+                "tooltip": "Déconnecter l'étude en cours"
+            },
+            "ja": {
+                "name": "切断",
+                "tooltip": "カレントスタディの切断"
+            }
+        }
+    },
+    "/PRP_DESK_FILE_DUMP_STUDY": {
+        "iconPath": "",
+        "langDependentAssets": {
+            "en": {
+                "name": "Dump Study...",
+                "tooltip": "Dumps study to the python script"
+            },
+            "fr": {
+                "name": "Générer le script de l'étude...",
+                "tooltip": "Génère le script python de l'étude"
+            },
+            "ja": {
+                "name": "スクリプトを保存",
+                "tooltip": "Pythonスクリプトにスタディをダンプする"
+            }
+        }
+    },
+    "/PRP_DESK_FILE_LOAD_SCRIPT": {
+        "iconPath": "",
+        "langDependentAssets": {
+            "en": {
+                "name": "Load Script...",
+                "tooltip": "Loads python script from file"
+            },
+            "fr": {
+                "name": "Exécuter un script...",
+                "tooltip": "Exécute un script Python à partir d'un fichier"
+            },
+            "ja": {
+                "name": "スクリプトを読込み...",
+                "tooltip": "ファイルからPythonスクリプトを読込み"
+            }
+        }
+    },
+    "/PRP_DESK_HELP_ABOUT": {
+        "iconPath": "",
+        "langDependentAssets": {
+            "en": {
+                "name": "About...",
+                "tooltip": "Shows 'About' dialog"
+            },
+            "fr": {
+                "name": "A propos de...",
+                "tooltip": "Montre la boîte de dialogue 'A propos'"
+            },
+            "ja": {
+                "name": "バージョン情報...",
+                "tooltip": "ソフト情報の表示"
+            }
+        }
+    },
+    "/PRP_DESK_PREFERENCES": {
+        "iconPath": "",
+        "langDependentAssets": {
+            "en": {
+                "name": "Preferences...",
+                "tooltip": "Allow to change the preferences"
+            },
+            "fr": {
+                "name": "Préférences...",
+                "tooltip": "Permettre de changer les préférences"
+            },
+            "ja": {
+                "name": "環境設定...",
+                "tooltip": "設定を変更することができます。"
+            }
+        }
+    },
+    "/PRP_DESK_VIEW_STATUSBAR": {
+        "iconPath": "",
+        "langDependentAssets": {
+            "en": {
+                "name": "Status Bar",
+                "tooltip": "Toggles status bar view on/off"
+            },
+            "fr": {
+                "name": "Barre de status",
+                "tooltip": "Activer ou désactiver la barre de status"
+            },
+            "ja": {
+                "name": "ステータス バー",
+                "tooltip": "ステータスバーの有効/無効"
+            }
+        }
+    },
+    "/PRP_DESK_WINDOW_HSPLIT": {
+        "iconPath": "",
+        "langDependentAssets": {
+            "en": {
+                "name": "Split Horizontally",
+                "tooltip": "Splits the active window on two horizontal parts"
+            },
+            "fr": {
+                "name": "Séparation horizontale",
+                "tooltip": "Diviser la fenêtre actuelle en deux parties horizontales"
+            },
+            "ja": {
+                "name": "水平分割",
+                "tooltip": "現在のウィンドウを 2つに水平分割"
+            }
+        }
+    },
+    "/PRP_DESK_WINDOW_VSPLIT": {
+        "iconPath": "",
+        "langDependentAssets": {
+            "en": {
+                "name": "Split Vertically",
+                "tooltip": "Splits the active window on two vertical parts"
+            },
+            "fr": {
+                "name": "Séparation verticale",
+                "tooltip": "Diviser la fenêtre actuelle en deux parties verticales"
+            },
+            "ja": {
+                "name": "垂直分割",
+                "tooltip": "現在のウィンドウを2つに上下分割"
+            }
+        }
+    },
+    "/PRP_FULLSCREEN": {
+        "iconPath": "",
+        "langDependentAssets": {
+            "en": {
+                "name": "Full screen",
+                "tooltip": "Switch to full screen mode"
+            },
+            "fr": {
+                "name": "Plein écran",
+                "tooltip": "Basculer en mode plein écran"
+            },
+            "ja": {
+                "name": "全画面表示",
+                "tooltip": "全画面表示モードに切り替え"
+            }
+        }
+    },
+    "/PRP_RENAME": {
+        "iconPath": "",
+        "langDependentAssets": {
+            "en": {
+                "name": "Rename",
+                "tooltip": "Rename active window"
+            },
+            "fr": {
+                "name": "Renommer",
+                "tooltip": "Renommer la fenêtre active"
+            },
+            "ja": {
+                "name": "名前変更",
+                "tooltip": "アクティブなウィンドウの名前を変更"
+            }
+        }
+    },
+    "/TOT_DESK_EDIT_PASTE": {
+        "iconPath": "",
+        "langDependentAssets": {
+            "en": {
+                "name": "Paste",
+                "tooltip": "Inserts the Clipboard content at the insertion point"
+            },
+            "fr": {
+                "name": "Coller",
+                "tooltip": "Insérer le contenu du presse-papiers au point d'insertion"
+            },
+            "ja": {
+                "name": "貼り付け",
+                "tooltip": "クリップボードの内容を挿入"
+            }
+        }
+    },
+    "/TOT_DESK_FILE_CLOSE": {
+        "iconPath": "",
+        "langDependentAssets": {
+            "en": {
+                "name": "Close",
+                "tooltip": "Closes the active document"
+            },
+            "fr": {
+                "name": "Fermer",
+                "tooltip": "Ferme le document actuel"
+            },
+            "ja": {
+                "name": "閉じる",
+                "tooltip": "現在のドキュメントを閉じる"
+            }
+        }
+    },
+    "/TOT_DESK_FILE_EXIT": {
+        "iconPath": "",
+        "langDependentAssets": {
+            "en": {
+                "name": "Exit",
+                "tooltip": "Exits the application"
+            },
+            "fr": {
+                "name": "Quitter",
+                "tooltip": "Quitte l'application"
+            },
+            "ja": {
+                "name": "終了",
+                "tooltip": "アプリケーションを終了"
+            }
+        }
+    },
+    "/TOT_DESK_FILE_NEW": {
+        "iconPath": "",
+        "langDependentAssets": {
+            "en": {
+                "name": "New",
+                "tooltip": "Create a new document"
+            },
+            "fr": {
+                "name": "Nouveau",
+                "tooltip": "Créer une nouvelle étude"
+            },
+            "ja": {
+                "name": "新規作成",
+                "tooltip": "新しいドキュメントを作成"
+            }
+        }
+    },
+    "/TOT_DESK_FILE_OPEN": {
+        "iconPath": "",
+        "langDependentAssets": {
+            "en": {
+                "name": "Open...",
+                "tooltip": "Open an existing document"
+            },
+            "fr": {
+                "name": "Ouvrir...",
+                "tooltip": "Ouvre une étude existant"
+            },
+            "ja": {
+                "name": "開く...",
+                "tooltip": "既存のドキュメントを開く"
+            }
+        }
+    },
+    "/TOT_DESK_FILE_SAVE": {
+        "iconPath": "",
+        "langDependentAssets": {
+            "en": {
+                "name": "Save",
+                "tooltip": "Save the active document"
+            },
+            "fr": {
+                "name": "Enregistrer",
+                "tooltip": "Sauvegarder l'étude actuelle"
+            },
+            "ja": {
+                "name": "保存",
+                "tooltip": "現在のドキュメントを保存"
+            }
+        }
+    },
+    "/TOT_DESK_FILE_SAVEAS": {
+        "iconPath": "",
+        "langDependentAssets": {
+            "en": {
+                "name": "Save As...",
+                "tooltip": "Saves the active document with a new name"
+            },
+            "fr": {
+                "name": "Enregistrer sous...",
+                "tooltip": "Sauvegarder le document actuel sous un nouveau nom"
+            },
+            "ja": {
+                "name": "別名保存...",
+                "tooltip": "現在のドキュメントを新しい名前で保存"
+            }
+        }
+    }
+}
\ No newline at end of file
index 796da66e555cb35300e38c50e0a3e80195e543fa..74412fa3bd3a1d85bc066a009f377aa2c2a9920f 100644 (file)
@@ -129,7 +129,7 @@ namespace SVTK
     case SUIT_Accel::RotateLeft  : return SVTK::RotateLeftEvent;
     case SUIT_Accel::RotateRight : return SVTK::RotateRightEvent;
     case SUIT_Accel::RotateUp    : return SVTK::RotateUpEvent;
-    case SUIT_Accel::RotateDown  : return SVTK::RotateDownEvent;  
+    case SUIT_Accel::RotateDown  : return SVTK::RotateDownEvent;
     }
     return accelAction;
   }
@@ -208,21 +208,21 @@ void SVTK_ViewWindow::Initialize(SVTK_ViewModelBase* theModel)
     ( getAction( ChangeRotationPointId ), this, "SVTK_SetRotationPointDlg" );
   myViewParameterDlg = new SVTK_ViewParameterDlg
     ( getAction( ViewParametersId ), this, "SVTK_ViewParameterDlg" );
-  
+
   myDefaultInteractorStyle = SVTK_InteractorStyle::New();
   myInteractor->PushInteractorStyle(myDefaultInteractorStyle);
   myDefaultInteractorStyle->Delete();
-  
+
   myRecorder = SVTK_Recorder::New();
-  
+
   myRecorder->SetNbFPS( 17.3 );
   myRecorder->SetQuality( 100 );
   myRecorder->SetProgressiveMode( true );
   myRecorder->SetUseSkippedFrames( true );
   myRecorder->SetRenderWindow( myInteractor->getRenderWindow() );
-  
+
   setCentralWidget(myInteractor);
-  
+
   myAxesWidget = salomevtk::vtkPVAxesWidget::New();
   myAxesWidget->SetParentRenderer(aRenderer->GetDevice());
   myAxesWidget->SetViewport(0, 0, 0.25, 0.25);
@@ -306,9 +306,9 @@ SVTK_ViewWindow::~SVTK_ViewWindow()
 /*!
   \return corresponding view
 */
-SVTK_View* SVTK_ViewWindow::getView() 
-{ 
-  return myView; 
+SVTK_View* SVTK_ViewWindow::getView()
+{
+  return myView;
 }
 
 /*!
@@ -355,8 +355,8 @@ SVTK_Renderer* SVTK_ViewWindow::GetRenderer() const
   \return corresponding vtk selector
 */
 SVTK_Selector* SVTK_ViewWindow::GetSelector() const
-{ 
-  return GetInteractor()->GetSelector(); 
+{
+  return GetInteractor()->GetSelector();
 }
 
 /*!
@@ -507,7 +507,7 @@ Selection_Mode SVTK_ViewWindow::SelectionMode() const
 /*!
   Unhilights all objects in viewer
 */
-void SVTK_ViewWindow::unHighlightAll() 
+void SVTK_ViewWindow::unHighlightAll()
 {
   myView->unHighlightAll();
 }
@@ -518,9 +518,9 @@ void SVTK_ViewWindow::unHighlightAll()
   \param theIsHighlight - if it is true, object will be hilighted, otherwise it will be unhilighted
   \param theIsUpdate - update current viewer
 */
-void SVTK_ViewWindow::highlight(const Handle(SALOME_InteractiveObject)& theIO, 
-                                bool theIsHighlight, 
-                                bool theIsUpdate ) 
+void SVTK_ViewWindow::highlight(const Handle(SALOME_InteractiveObject)& theIO,
+                                bool theIsHighlight,
+                                bool theIsUpdate )
 {
   myView->highlight( theIO, theIsHighlight, theIsUpdate );
 }
@@ -529,7 +529,7 @@ void SVTK_ViewWindow::highlight(const Handle(SALOME_InteractiveObject)& theIO,
   \return true if object is in viewer or in collector
   \param theIO - object to be checked
 */
-bool SVTK_ViewWindow::isInViewer( const Handle(SALOME_InteractiveObject)& theIO ) 
+bool SVTK_ViewWindow::isInViewer( const Handle(SALOME_InteractiveObject)& theIO )
 {
   return myView->isInViewer( theIO );
 }
@@ -538,7 +538,7 @@ bool SVTK_ViewWindow::isInViewer( const Handle(SALOME_InteractiveObject)& theIO
   \return true if object is displayed in viewer
   \param theIO - object to be checked
 */
-bool SVTK_ViewWindow::isVisible( const Handle(SALOME_InteractiveObject)& theIO ) 
+bool SVTK_ViewWindow::isVisible( const Handle(SALOME_InteractiveObject)& theIO )
 {
   return myView->isVisible( theIO );
 }
@@ -547,7 +547,7 @@ bool SVTK_ViewWindow::isVisible( const Handle(SALOME_InteractiveObject)& theIO )
   Display object
   \param theEntry - entry that corresponds to intractive objects
 */
-Handle(SALOME_InteractiveObject) SVTK_ViewWindow::FindIObject(const char* theEntry) 
+Handle(SALOME_InteractiveObject) SVTK_ViewWindow::FindIObject(const char* theEntry)
 {
   return myView->FindIObject(theEntry);
 }
@@ -558,7 +558,7 @@ Handle(SALOME_InteractiveObject) SVTK_ViewWindow::FindIObject(const char* theEnt
   \param theImmediatly - update viewer
 */
 void SVTK_ViewWindow::Display(const Handle(SALOME_InteractiveObject)& theIO,
-                              bool theImmediatly) 
+                              bool theImmediatly)
 {
   myView->Display(theIO,theImmediatly);
 }
@@ -569,7 +569,7 @@ void SVTK_ViewWindow::Display(const Handle(SALOME_InteractiveObject)& theIO,
   \param theImmediatly - update viewer
 */
 void SVTK_ViewWindow::Erase(const Handle(SALOME_InteractiveObject)& theIO,
-                            bool theImmediatly) 
+                            bool theImmediatly)
 {
   myView->Erase(theIO,theImmediatly);
 }
@@ -578,7 +578,7 @@ void SVTK_ViewWindow::Erase(const Handle(SALOME_InteractiveObject)& theIO,
   Display only passed object
   \param theIO - object
 */
-void SVTK_ViewWindow::DisplayOnly(const Handle(SALOME_InteractiveObject)& theIO) 
+void SVTK_ViewWindow::DisplayOnly(const Handle(SALOME_InteractiveObject)& theIO)
 {
   myView->DisplayOnly(theIO);
 }
@@ -586,7 +586,7 @@ void SVTK_ViewWindow::DisplayOnly(const Handle(SALOME_InteractiveObject)& theIO)
 /*!
   Display all objects in view
 */
-void SVTK_ViewWindow::DisplayAll() 
+void SVTK_ViewWindow::DisplayAll()
 {
   myView->DisplayAll();
 }
@@ -594,7 +594,7 @@ void SVTK_ViewWindow::DisplayAll()
 /*!
   Erase all objects in view
 */
-void SVTK_ViewWindow::EraseAll() 
+void SVTK_ViewWindow::EraseAll()
 {
   myView->EraseAll();
 }
@@ -793,7 +793,7 @@ void SVTK_ViewWindow::PopInteractorStyle()
 */
 void SVTK_ViewWindow::Repaint(bool theUpdateTrihedron)
 {
-  if(theUpdateTrihedron) 
+  if(theUpdateTrihedron)
     GetRenderer()->OnAdjustTrihedron();
 
   GetInteractor()->update();
@@ -805,7 +805,7 @@ void SVTK_ViewWindow::Repaint(bool theUpdateTrihedron)
       if( GetRenderer() ) {
         aStyle->SetCurrentRenderer(GetRenderer()->GetDevice());
       }
-    }  
+    }
 #endif
     aStyle->OnTimer();
   }
@@ -814,7 +814,7 @@ void SVTK_ViewWindow::Repaint(bool theUpdateTrihedron)
 /*!
   Redirect the request to #SVTK_Renderer::GetScale
 */
-void SVTK_ViewWindow::GetScale( double theScale[3] ) 
+void SVTK_ViewWindow::GetScale( double theScale[3] )
 {
   GetRenderer()->GetScale( theScale );
 }
@@ -822,7 +822,7 @@ void SVTK_ViewWindow::GetScale( double theScale[3] )
 /*!
   Redirect the request to #SVTK_Renderer::SetScale
 */
-void SVTK_ViewWindow::SetScale( double theScale[3] ) 
+void SVTK_ViewWindow::SetScale( double theScale[3] )
 {
   GetRenderer()->SetScale( theScale );
   Repaint();
@@ -1128,7 +1128,7 @@ void SVTK_ViewWindow::SetSelectionEnabled( bool theEnable )
   QtxAction* a = getAction( EnableSelectionId );
   if ( a->isChecked() !=  theEnable)
     a->setChecked( theEnable );
-  QtxActionGroup* aPreselectionGroup = 
+  QtxActionGroup* aPreselectionGroup =
     dynamic_cast<QtxActionGroup*>( getAction( PreselectionId ) );
   if ( aPreselectionGroup )
     aPreselectionGroup->setEnabled( theEnable );
@@ -1211,7 +1211,7 @@ void SVTK_ViewWindow::onEnableSelection( bool on )
 {
   SVTK_Viewer* aViewer = dynamic_cast<SVTK_Viewer*>(myModel);
   if(aViewer)
-    aViewer->enableSelection(on);  
+    aViewer->enableSelection(on);
 }
 
 /*!
@@ -1231,7 +1231,7 @@ void SVTK_ViewWindow::SetIncrementalSpeed(const int theValue, const int theMode)
   \param theBtn2 - spacemouse button for the "increase speed increment"
   \param theBtn3 - spacemouse button for the "dominant combined switch"
 */
-void SVTK_ViewWindow::SetSpacemouseButtons(const int theBtn1, 
+void SVTK_ViewWindow::SetSpacemouseButtons(const int theBtn1,
                                            const int theBtn2,
                                            const int theBtn3)
 {
@@ -1267,7 +1267,7 @@ void SVTK_ViewWindow::AdjustTrihedrons(const bool /*theIsForcedUpdate*/)
   Redirect the request to #SVTK_Renderer::OnAdjustTrihedron
 */
 void SVTK_ViewWindow::onAdjustTrihedron()
-{   
+{
   GetRenderer()->OnAdjustTrihedron();
 }
 
@@ -1275,7 +1275,7 @@ void SVTK_ViewWindow::onAdjustTrihedron()
   Redirect the request to #SVTK_Renderer::OnAdjustCubeAxes
 */
 void SVTK_ViewWindow::onAdjustCubeAxes()
-{   
+{
   GetRenderer()->OnAdjustCubeAxes();
 }
 
@@ -1339,12 +1339,12 @@ void SVTK_ViewWindow::onMouseDoubleClicked( QMouseEvent* event )
 /*!
   Redirect the request to #SVTK_Renderer::AddActor
 */
-void SVTK_ViewWindow::AddActor( VTKViewer_Actor* theActor, 
+void SVTK_ViewWindow::AddActor( VTKViewer_Actor* theActor,
                                 bool theUpdate,
                                 bool theIsAdjustActors )
 {
   GetRenderer()->AddActor(theActor, theIsAdjustActors);
-  if(theUpdate) 
+  if(theUpdate)
     Repaint();
   emit actorAdded(theActor);
 }
@@ -1352,7 +1352,7 @@ void SVTK_ViewWindow::AddActor( VTKViewer_Actor* theActor,
 /*!
   Redirect the request to #SVTK_Renderer::RemoveActor
 */
-void SVTK_ViewWindow::RemoveActor( VTKViewer_Actor* theActor, 
+void SVTK_ViewWindow::RemoveActor( VTKViewer_Actor* theActor,
                                    bool theUpdate,
                                    bool theIsAdjustActors )
 {
@@ -1361,7 +1361,7 @@ void SVTK_ViewWindow::RemoveActor( VTKViewer_Actor* theActor,
     myDefaultInteractorStyle->FreeActors();
   if ( myKeyFreeInteractorStyle )
     myKeyFreeInteractorStyle->FreeActors();
-  if(theUpdate) 
+  if(theUpdate)
     Repaint();
   emit actorRemoved(theActor);
 }
@@ -1372,7 +1372,7 @@ QImage SVTK_ViewWindow::dumpViewContent()
   int* aSize = aWindow->GetSize();
   int aWidth = aSize[0];
   int aHeight = aSize[1];
-  
+
 #ifndef DISABLE_GLVIEWER
   OpenGLUtils_FrameBuffer aFrameBuffer;
   if( aFrameBuffer.init( aWidth, aHeight ) )
@@ -1383,7 +1383,7 @@ QImage SVTK_ViewWindow::dumpViewContent()
 
     // draw scene
     aWindow->Render();
-   
+
     //aFrameBuffer.unbind();
     glPopAttrib();
 
@@ -1400,9 +1400,9 @@ QImage SVTK_ViewWindow::dumpViewContent()
 #endif
 
   // if frame buffers are unsupported, use old functionality
-  unsigned char *aData = 
+  unsigned char *aData =
     aWindow->GetRGBACharPixelData( 0, 0, aWidth-1, aHeight-1, 0 );
-  
+
   QImage anImage( aData, aWidth, aHeight, QImage::Format_ARGB32 );
 
   anImage = anImage.rgbSwapped();
@@ -1417,7 +1417,7 @@ QImage SVTK_ViewWindow::dumpView()
 {
   if( myDumpImage.isNull() )
     return dumpViewContent();
-  
+
   RefreshDumpImage();
   return myDumpImage;
 }
@@ -1445,12 +1445,12 @@ bool SVTK_ViewWindow::dumpViewToFormat( const QImage& img, const QString& fileNa
     anExporter->SetSort((vtkGL2PSExporter::SortScheme)optionsDlg->getSortType());
     anExporter->SetWrite3DPropsAsRasterImage((int)optionsDlg->isRasterize3D());
     anExporter->SetPS3Shading((int)optionsDlg->isPs3Shading());
-    
+
     if ( format == "PS" ) {
       anExporter->SetFileFormatToPS();
       anExporter->CompressOff();
     }
-    
+
     if ( format == "EPS" ) {
       anExporter->SetFileFormatToEPS();
       anExporter->CompressOff();
@@ -1459,7 +1459,7 @@ bool SVTK_ViewWindow::dumpViewToFormat( const QImage& img, const QString& fileNa
     if ( format == "PDF" ) {
       anExporter->SetFileFormatToPDF();
     }
-    
+
     QString aFilePrefix(fileName);
     QString anExtension(SUIT_Tools::extension(fileName));
     aFilePrefix.truncate(aFilePrefix.length() - 1 - anExtension.length());
@@ -1468,7 +1468,7 @@ bool SVTK_ViewWindow::dumpViewToFormat( const QImage& img, const QString& fileNa
     anExporter->Delete();
   }
   delete optionsDlg;
-  return true;  
+  return true;
 }
 
 /*!
@@ -1482,10 +1482,10 @@ void SVTK_ViewWindow::RefreshDumpImage()
 /*!
   Redirect the request to #SVTK_Renderer::SetSelectionProp
 */
-void SVTK_ViewWindow::SetSelectionProp(const double& theRed, 
-                                       const double& theGreen, 
-                                       const double& theBlue, 
-                                       const int& theWidth) 
+void SVTK_ViewWindow::SetSelectionProp(const double& theRed,
+                                       const double& theGreen,
+                                       const double& theBlue,
+                                       const int& theWidth)
 {
   myView->SetSelectionProp(theRed,theGreen,theBlue,theWidth);
 }
@@ -1493,10 +1493,10 @@ void SVTK_ViewWindow::SetSelectionProp(const double& theRed,
 /*!
   Redirect the request to #SVTK_Renderer::SetSelectionProp
 */
-void SVTK_ViewWindow::SetPreselectionProp(const double& theRed, 
-                                          const double& theGreen, 
-                                          const double& theBlue, 
-                                          const int& theWidth) 
+void SVTK_ViewWindow::SetPreselectionProp(const double& theRed,
+                                          const double& theGreen,
+                                          const double& theBlue,
+                                          const int& theWidth)
 {
   myView->SetPreselectionProp(theRed,theGreen,theBlue,theWidth);
 }
@@ -1504,7 +1504,7 @@ void SVTK_ViewWindow::SetPreselectionProp(const double& theRed,
 /*!
   Redirect the request to #SVTK_Renderer::SetSelectionTolerance
 */
-void SVTK_ViewWindow::SetSelectionTolerance(const double& theTolNodes, 
+void SVTK_ViewWindow::SetSelectionTolerance(const double& theTolNodes,
                                             const double& theTolItems,
                                             const double& theTolObjects)
 {
@@ -1551,7 +1551,7 @@ QtxAction* SVTK_ViewWindow::getAction( int id ) const
 }
 
 
-// old visual parameters had 13 values.  New format added additional 
+// old visual parameters had 13 values.  New format added additional
 // 76 values for graduated axes, so both numbers are processed.
 const int nNormalParams = 13;   // number of view windows parameters excluding graduated axes params
 const int nGradAxisParams = 25; // number of parameters of ONE graduated axis (X, Y, or Z)
@@ -1602,7 +1602,7 @@ void getGradAxisVisualParams( QXmlStreamWriter& writer, vtkAxisActor2D* actor, Q
   writer.writeEndElement();
   writer.writeEndElement();
 
-  //params.sprintf( "* Graduated Axis: * Name *%u*%s*%.2f*%.2f*%.2f*%u*%u*%u*%u", isVisible, 
+  //params.sprintf( "* Graduated Axis: * Name *%u*%s*%.2f*%.2f*%.2f*%u*%u*%u*%u", isVisible,
   //              title.toLatin1().data(), color[0], color[1], color[2], font, bold, italic, shadow );
 
   // Labels
@@ -1639,7 +1639,7 @@ void getGradAxisVisualParams( QXmlStreamWriter& writer, vtkAxisActor2D* actor, Q
   writer.writeAttribute("B", QString("%1").arg(color[2]));
   writer.writeEndElement();
   writer.writeEndElement();
-  //  params += QString().sprintf( "* Labels *%u*%u*%u*%.2f*%.2f*%.2f*%u*%u*%u*%u", isVisible, labels, offset,  
+  //  params += QString().sprintf( "* Labels *%u*%u*%u*%.2f*%.2f*%.2f*%u*%u*%u*%u", isVisible, labels, offset,
   //                           color[0], color[1], color[2], font, bold, italic, shadow );
 
   // Tick marks
@@ -1649,9 +1649,9 @@ void getGradAxisVisualParams( QXmlStreamWriter& writer, vtkAxisActor2D* actor, Q
   writer.writeAttribute("isVisible", QString("%1").arg(isVisible));
   writer.writeAttribute("Length", QString("%1").arg(length));
   writer.writeEndElement();
-  
+
   //params += QString().sprintf( "* Tick marks *%u*%u", isVisible, length );
-  
+
   writer.writeEndElement();
   //return params;
 }
@@ -1679,7 +1679,7 @@ void setGradAxisVisualParams(QXmlStreamReader& reader, vtkAxisActor2D* actor)
   do {
     reader.readNext();
   } while (!reader.isStartElement());
-  
+
   // Read title color
   aAttr = reader.attributes();
 
@@ -1704,7 +1704,7 @@ void setGradAxisVisualParams(QXmlStreamReader& reader, vtkAxisActor2D* actor)
 
   do {
     reader.readNext();
-  } while (!reader.isStartElement()); 
+  } while (!reader.isStartElement());
   // Read labels
   aAttr = reader.attributes();
   isVisible = aAttr.value("isVisible").toString().toUShort();
@@ -1717,7 +1717,7 @@ void setGradAxisVisualParams(QXmlStreamReader& reader, vtkAxisActor2D* actor)
 
   do {
     reader.readNext();
-  } while (!reader.isStartElement()); 
+  } while (!reader.isStartElement());
   // Read Color
   aAttr = reader.attributes();
 
@@ -1740,13 +1740,13 @@ void setGradAxisVisualParams(QXmlStreamReader& reader, vtkAxisActor2D* actor)
   // Tick Marks
   do {
     reader.readNext();
-  } while (!reader.isStartElement()); 
+  } while (!reader.isStartElement());
   aAttr = reader.attributes();
 
   // retrieve and set tick marks properties
   isVisible = aAttr.value("isVisible").toString().toUShort();
   int length = aAttr.value("Length").toString().toInt();
-  
+
   actor->SetTickVisibility( isVisible );
   actor->SetTickLength( length );
 }
@@ -1823,7 +1823,7 @@ void setGradAxisVisualParams( vtkAxisActor2D* actor, const QString& params )
 QString SVTK_ViewWindow::getVisualParameters()
 {
   double pos[3], focalPnt[3], viewUp[3], parScale, scale[3];
-  
+
   // save position, focal point, viewUp, scale
   vtkCamera* camera = getRenderer()->GetActiveCamera();
   camera->GetPosition( pos );
@@ -1833,7 +1833,7 @@ QString SVTK_ViewWindow::getVisualParameters()
   GetScale( scale );
 
   // Parameters are given in the following format:view position (3 digits), focal point position (3 digits)
-  // view up values (3 digits), parallel scale (1 digit), scale (3 digits, 
+  // view up values (3 digits), parallel scale (1 digit), scale (3 digits,
   // Graduated axes parameters (X, Y, Z axes parameters)
   QString retStr;
   QXmlStreamWriter aWriter(&retStr);
@@ -1894,13 +1894,13 @@ QString SVTK_ViewWindow::getVisualParameters()
 
 /*!
   The method restores visual parameters of this view or postpones it untill the view is shown
-*/ 
+*/
 void SVTK_ViewWindow::setVisualParameters( const QString& parameters )
 {
   //printf("#### %s\n", qPrintable(parameters));
   SVTK_RenderWindowInteractor* anInteractor = GetInteractor();
   if ( anInteractor->isVisible() ) {
-    doSetVisualParameters( parameters ); 
+    doSetVisualParameters( parameters );
   }
   else {
     myVisualParams = parameters;
@@ -2188,7 +2188,7 @@ void SVTK_ViewWindow::createActions(SUIT_ResourceMgr* theResourceMgr)
   QtxActionToolMgr* mgr = toolMgr();
 
   // Dump view
-  anAction = new QtxAction(tr("MNU_DUMP_VIEW"), 
+  anAction = new QtxAction(tr("MNU_DUMP_VIEW"),
                            theResourceMgr->loadPixmap( "VTKViewer", tr( "ICON_VTKVIEWER_VIEW_DUMP" ) ),
                            tr( "MNU_DUMP_VIEW" ), 0, this);
   anAction->setStatusTip(tr("DSC_DUMP_VIEW"));
@@ -2196,7 +2196,7 @@ void SVTK_ViewWindow::createActions(SUIT_ResourceMgr* theResourceMgr)
   mgr->registerAction( anAction, DumpId );
 
   // FitAll
-  anAction = new QtxAction(tr("MNU_FITALL"), 
+  anAction = new QtxAction(tr("MNU_FITALL"),
                            theResourceMgr->loadPixmap( "VTKViewer", tr( "ICON_VTKVIEWER_VIEW_FITALL" ) ),
                            tr( "MNU_FITALL" ), 0, this);
   anAction->setStatusTip(tr("DSC_FITALL"));
@@ -2204,7 +2204,7 @@ void SVTK_ViewWindow::createActions(SUIT_ResourceMgr* theResourceMgr)
   mgr->registerAction( anAction, FitAllId );
 
   // FitRect
-  anAction = new QtxAction(tr("MNU_FITRECT"), 
+  anAction = new QtxAction(tr("MNU_FITRECT"),
                            theResourceMgr->loadPixmap( "VTKViewer", tr( "ICON_VTKVIEWER_VIEW_FITAREA" ) ),
                            tr( "MNU_FITRECT" ), 0, this);
   anAction->setStatusTip(tr("DSC_FITRECT"));
@@ -2220,7 +2220,7 @@ void SVTK_ViewWindow::createActions(SUIT_ResourceMgr* theResourceMgr)
   mgr->registerAction( anAction, FitSelectionId );
 
   // Zoom
-  anAction = new QtxAction(tr("MNU_ZOOM_VIEW"), 
+  anAction = new QtxAction(tr("MNU_ZOOM_VIEW"),
                            theResourceMgr->loadPixmap( "VTKViewer", tr( "ICON_VTKVIEWER_VIEW_ZOOM" ) ),
                            tr( "MNU_ZOOM_VIEW" ), 0, this);
   anAction->setStatusTip(tr("DSC_ZOOM_VIEW"));
@@ -2228,7 +2228,7 @@ void SVTK_ViewWindow::createActions(SUIT_ResourceMgr* theResourceMgr)
   mgr->registerAction( anAction, ZoomId );
 
   // Panning
-  anAction = new QtxAction(tr("MNU_PAN_VIEW"), 
+  anAction = new QtxAction(tr("MNU_PAN_VIEW"),
                            theResourceMgr->loadPixmap( "VTKViewer", tr( "ICON_VTKVIEWER_VIEW_PAN" ) ),
                            tr( "MNU_PAN_VIEW" ), 0, this);
   anAction->setStatusTip(tr("DSC_PAN_VIEW"));
@@ -2236,7 +2236,7 @@ void SVTK_ViewWindow::createActions(SUIT_ResourceMgr* theResourceMgr)
   mgr->registerAction( anAction, PanId );
 
   // Global Panning
-  anAction = new QtxAction(tr("MNU_GLOBALPAN_VIEW"), 
+  anAction = new QtxAction(tr("MNU_GLOBALPAN_VIEW"),
                            theResourceMgr->loadPixmap( "VTKViewer", tr( "ICON_VTKVIEWER_VIEW_GLOBALPAN" ) ),
                            tr( "MNU_GLOBALPAN_VIEW" ), 0, this);
   anAction->setStatusTip(tr("DSC_GLOBALPAN_VIEW"));
@@ -2244,7 +2244,7 @@ void SVTK_ViewWindow::createActions(SUIT_ResourceMgr* theResourceMgr)
   mgr->registerAction( anAction, GlobalPanId );
 
   // Change rotation point
-  anAction = new QtxAction(tr("MNU_CHANGINGROTATIONPOINT_VIEW"), 
+  anAction = new QtxAction(tr("MNU_CHANGINGROTATIONPOINT_VIEW"),
                            theResourceMgr->loadPixmap( "VTKViewer", tr( "ICON_SVTK_ROTATION_POINT" ) ),
                            tr( "MNU_CHANGINGROTATIONPOINT_VIEW" ), 0, this);
   anAction->setStatusTip(tr("DSC_CHANGINGROTATIONPOINT_VIEW"));
@@ -2253,7 +2253,7 @@ void SVTK_ViewWindow::createActions(SUIT_ResourceMgr* theResourceMgr)
   mgr->registerAction( anAction, ChangeRotationPointId );
 
   // Rotation
-  anAction = new QtxAction(tr("MNU_ROTATE_VIEW"), 
+  anAction = new QtxAction(tr("MNU_ROTATE_VIEW"),
                            theResourceMgr->loadPixmap( "VTKViewer", tr( "ICON_VTKVIEWER_VIEW_ROTATE" ) ),
                            tr( "MNU_ROTATE_VIEW" ), 0, this);
   anAction->setStatusTip(tr("DSC_ROTATE_VIEW"));
@@ -2261,9 +2261,9 @@ void SVTK_ViewWindow::createActions(SUIT_ResourceMgr* theResourceMgr)
   mgr->registerAction( anAction, RotationId );
 
   // Projections
-  anAction = new QtxAction(tr("MNU_FRONT_VIEW"), 
+  anAction = new QtxAction(tr("MNU_FRONT_VIEW"),
                            theResourceMgr->loadPixmap( "VTKViewer", tr( "ICON_VTKVIEWER_VIEW_FRONT" ) ),
-                           tr( "MNU_FRONT_VIEW" ), 0, this, false, "Viewers:Front view");
+                           tr( "MNU_FRONT_VIEW" ), 0, this, false, "/#Viewers/View/Set X-");
   anAction->setStatusTip(tr("DSC_FRONT_VIEW"));
   connect(anAction, SIGNAL(triggered()), this, SLOT(onFrontView()));
   this->addAction(anAction);
@@ -2271,7 +2271,7 @@ void SVTK_ViewWindow::createActions(SUIT_ResourceMgr* theResourceMgr)
 
   anAction = new QtxAction(tr("MNU_BACK_VIEW"),
                            theResourceMgr->loadPixmap( "VTKViewer", tr( "ICON_VTKVIEWER_VIEW_BACK" ) ),
-                           tr( "MNU_BACK_VIEW" ), 0, this, false, "Viewers:Back view");
+                           tr( "MNU_BACK_VIEW" ), 0, this, false, "/#Viewers/View/Set X+");
   anAction->setStatusTip(tr("DSC_BACK_VIEW"));
   connect(anAction, SIGNAL(triggered()), this, SLOT(onBackView()));
   this->addAction(anAction);
@@ -2279,7 +2279,7 @@ void SVTK_ViewWindow::createActions(SUIT_ResourceMgr* theResourceMgr)
 
   anAction = new QtxAction(tr("MNU_TOP_VIEW"),
                            theResourceMgr->loadPixmap( "VTKViewer", tr( "ICON_VTKVIEWER_VIEW_TOP" ) ),
-                           tr( "MNU_TOP_VIEW" ), 0, this, false, "Viewers:Top view");
+                           tr( "MNU_TOP_VIEW" ), 0, this, false, "/#Viewers/View/Set Z-");
   anAction->setStatusTip(tr("DSC_TOP_VIEW"));
   connect(anAction, SIGNAL(triggered()), this, SLOT(onTopView()));
   this->addAction(anAction);
@@ -2287,7 +2287,7 @@ void SVTK_ViewWindow::createActions(SUIT_ResourceMgr* theResourceMgr)
 
   anAction = new QtxAction(tr("MNU_BOTTOM_VIEW"),
                            theResourceMgr->loadPixmap( "VTKViewer", tr( "ICON_VTKVIEWER_VIEW_BOTTOM" ) ),
-                           tr( "MNU_BOTTOM_VIEW" ), 0, this, false, "Viewers:Bottom view");
+                           tr( "MNU_BOTTOM_VIEW" ), 0, this, false, "/#Viewers/View/Set Z+");
   anAction->setStatusTip(tr("DSC_BOTTOM_VIEW"));
   connect(anAction, SIGNAL(triggered()), this, SLOT(onBottomView()));
   this->addAction(anAction);
@@ -2295,7 +2295,7 @@ void SVTK_ViewWindow::createActions(SUIT_ResourceMgr* theResourceMgr)
 
   anAction = new QtxAction(tr("MNU_LEFT_VIEW"),
                            theResourceMgr->loadPixmap( "VTKViewer", tr( "ICON_VTKVIEWER_VIEW_LEFT" ) ),
-                           tr( "MNU_LEFT_VIEW" ), 0, this, false, "Viewers:Left view");
+                           tr( "MNU_LEFT_VIEW" ), 0, this, false, "/#Viewers/View/Set Y+");
   anAction->setStatusTip(tr("DSC_LEFT_VIEW"));
   connect(anAction, SIGNAL(triggered()), this, SLOT(onLeftView()));
   this->addAction(anAction);
@@ -2303,7 +2303,7 @@ void SVTK_ViewWindow::createActions(SUIT_ResourceMgr* theResourceMgr)
 
   anAction = new QtxAction(tr("MNU_RIGHT_VIEW"),
                            theResourceMgr->loadPixmap( "VTKViewer", tr( "ICON_VTKVIEWER_VIEW_RIGHT" ) ),
-                           tr( "MNU_RIGHT_VIEW" ), 0, this, false, "Viewers:Right view");
+                           tr( "MNU_RIGHT_VIEW" ), 0, this, false, "/#Viewers/View/Set Y-");
   anAction->setStatusTip(tr("DSC_RIGHT_VIEW"));
   connect(anAction, SIGNAL(triggered()), this, SLOT(onRightView()));
   this->addAction(anAction);
@@ -2312,7 +2312,7 @@ void SVTK_ViewWindow::createActions(SUIT_ResourceMgr* theResourceMgr)
   // rotate anticlockwise
   anAction = new QtxAction(tr("MNU_ANTICLOCKWISE_VIEW"),
                            theResourceMgr->loadPixmap( "VTKViewer", tr( "ICON_VTKVIEWER_VIEW_ANTICLOCKWISE" ) ),
-                           tr( "MNU_ANTICLOCKWISE_VIEW" ), 0, this, false, "Viewers:Rotate anticlockwise");
+                           tr( "MNU_ANTICLOCKWISE_VIEW" ), 0, this, false, "/#Viewers/View/Rotate anticlockwise");
   anAction->setStatusTip(tr("DSC_ANTICLOCKWISE_VIEW"));
   connect(anAction, SIGNAL(triggered()), this, SLOT(onAntiClockWiseView()));
   this->addAction(anAction);
@@ -2321,34 +2321,34 @@ void SVTK_ViewWindow::createActions(SUIT_ResourceMgr* theResourceMgr)
   // rotate clockwise
   anAction = new QtxAction(tr("MNU_CLOCKWISE_VIEW"),
                            theResourceMgr->loadPixmap( "VTKViewer", tr( "ICON_VTKVIEWER_VIEW_CLOCKWISE" ) ),
-                           tr( "MNU_CLOCKWISE_VIEW" ), 0, this, false, "Viewers:Rotate clockwise");
+                           tr( "MNU_CLOCKWISE_VIEW" ), 0, this, false, "/#Viewers/View/Rotate clockwise");
   anAction->setStatusTip(tr("DSC_CLOCKWISE_VIEW"));
   connect(anAction, SIGNAL(triggered()), this, SLOT(onClockWiseView()));
   this->addAction(anAction);
   mgr->registerAction( anAction, ClockWiseId );
 
   // Reset
-  anAction = new QtxAction(tr("MNU_RESET_VIEW"), 
+  anAction = new QtxAction(tr("MNU_RESET_VIEW"),
                            theResourceMgr->loadPixmap( "VTKViewer", tr( "ICON_VTKVIEWER_VIEW_RESET" ) ),
-                           tr( "MNU_RESET_VIEW" ), 0, this, false, "Viewers:Reset view");
+                           tr( "MNU_RESET_VIEW" ), 0, this, false, "/#Viewers/View/Reset");
   anAction->setStatusTip(tr("DSC_RESET_VIEW"));
   connect(anAction, SIGNAL(triggered()), this, SLOT(onResetView()));
   this->addAction(anAction);
   mgr->registerAction( anAction, ResetId );
 
   // onViewTrihedron: Shows - Hides Trihedron
-  anAction = new QtxAction(tr("MNU_SHOW_TRIHEDRON"), 
+  anAction = new QtxAction(tr("MNU_SHOW_TRIHEDRON"),
                            theResourceMgr->loadPixmap( "VTKViewer", tr( "ICON_VTKVIEWER_VIEW_TRIHEDRON" ) ),
                            tr( "MNU_SHOW_TRIHEDRON" ), 0, this);
   anAction->setCheckable( true );
   anAction->setChecked( true );
-  
+
   anAction->setStatusTip(tr("DSC_SHOW_TRIHEDRON"));
   connect(anAction, SIGNAL(toggled(bool)), this, SLOT(onViewTrihedron(bool)));
   mgr->registerAction( anAction, ViewTrihedronId );
 
   // onNonIsometric: Manage non-isometric params
-  anAction = new QtxAction(tr("MNU_SVTK_SCALING"), 
+  anAction = new QtxAction(tr("MNU_SVTK_SCALING"),
                            theResourceMgr->loadPixmap( "VTKViewer", tr( "ICON_SVTK_SCALING" ) ),
                            tr( "MNU_SVTK_SCALING" ), 0, this);
   anAction->setStatusTip(tr("DSC_SVTK_SCALING"));
@@ -2357,7 +2357,7 @@ void SVTK_ViewWindow::createActions(SUIT_ResourceMgr* theResourceMgr)
   mgr->registerAction( anAction, NonIsometric );
 
   // onGraduatedAxes: Manage graduated axes params
-  anAction = new QtxAction(tr("MNU_SVTK_GRADUATED_AXES"), 
+  anAction = new QtxAction(tr("MNU_SVTK_GRADUATED_AXES"),
                            theResourceMgr->loadPixmap( "VTKViewer", tr( "ICON_SVTK_GRADUATED_AXES" ) ),
                            tr( "MNU_SVTK_GRADUATED_AXES" ), 0, this);
   anAction->setStatusTip(tr("DSC_SVTK_GRADUATED_AXES"));
@@ -2366,7 +2366,7 @@ void SVTK_ViewWindow::createActions(SUIT_ResourceMgr* theResourceMgr)
   mgr->registerAction( anAction, GraduatedAxes );
 
   // onGraduatedAxes: Manage graduated axes params
-  anAction = new QtxAction(tr("MNU_SVTK_UPDATE_RATE"), 
+  anAction = new QtxAction(tr("MNU_SVTK_UPDATE_RATE"),
                            theResourceMgr->loadPixmap( "VTKViewer", tr( "ICON_SVTK_UPDATE_RATE" ) ),
                            tr( "MNU_SVTK_UPDATE_RATE" ), 0, this);
   anAction->setStatusTip(tr("DSC_SVTK_UPDATE_RATE"));
@@ -2375,14 +2375,14 @@ void SVTK_ViewWindow::createActions(SUIT_ResourceMgr* theResourceMgr)
   mgr->registerAction( anAction, UpdateRate );
 
   // Set perspective mode group
-  anAction = new QtxAction(tr("MNU_SVTK_PARALLEL_MODE"), 
+  anAction = new QtxAction(tr("MNU_SVTK_PARALLEL_MODE"),
                            theResourceMgr->loadPixmap( "VTKViewer", tr( "ICON_SVTK_VIEW_PARALLEL" ) ),
                            tr( "MNU_SVTK_PARALLEL_MODE" ), 0, this);
   anAction->setStatusTip(tr("DSC_SVTK_PARALLEL_MODE"));
   anAction->setCheckable(true);
   mgr->registerAction( anAction, ParallelModeId );
 
-  anAction = new QtxAction(tr("MNU_SVTK_PERSPECTIVE_MODE"), 
+  anAction = new QtxAction(tr("MNU_SVTK_PERSPECTIVE_MODE"),
                            theResourceMgr->loadPixmap( "VTKViewer", tr( "ICON_SVTK_VIEW_PERSPECTIVE" ) ),
                            tr( "MNU_SVTK_PERSPECTIVE_MODE" ), 0, this);
   anAction->setStatusTip(tr("DSC_SVTK_PERSPECTIVE_MODE"));
@@ -2403,7 +2403,7 @@ void SVTK_ViewWindow::createActions(SUIT_ResourceMgr* theResourceMgr)
   connect(aPerspectiveGroup, SIGNAL(triggered(QAction*)), this, SLOT(onProjectionMode(QAction*)));
 
   // View Parameters
-  anAction = new QtxAction(tr("MNU_VIEWPARAMETERS_VIEW"), 
+  anAction = new QtxAction(tr("MNU_VIEWPARAMETERS_VIEW"),
                            theResourceMgr->loadPixmap( "VTKViewer", tr( "ICON_SVTK_VIEW_PARAMETERS" ) ),
                            tr( "MNU_VIEWPARAMETERS_VIEW" ), 0, this);
   anAction->setStatusTip(tr("DSC_VIEWPARAMETERS_VIEW"));
@@ -2411,11 +2411,11 @@ void SVTK_ViewWindow::createActions(SUIT_ResourceMgr* theResourceMgr)
   connect(anAction, SIGNAL(toggled(bool)), this, SLOT(onViewParameters(bool)));
   mgr->registerAction( anAction, ViewParametersId );
 
-  // Synchronize View 
+  // Synchronize View
   mgr->registerAction( synchronizeAction(), SynchronizeId );
 
   // Switch between interaction styles
-  anAction = new QtxAction(tr("MNU_SVTK_STYLE_SWITCH"), 
+  anAction = new QtxAction(tr("MNU_SVTK_STYLE_SWITCH"),
                            theResourceMgr->loadPixmap( "VTKViewer", tr( "ICON_SVTK_STYLE_SWITCH" ) ),
                            tr( "MNU_SVTK_STYLE_SWITCH" ), 0, this);
   anAction->setStatusTip(tr("DSC_SVTK_STYLE_SWITCH"));
@@ -2424,7 +2424,7 @@ void SVTK_ViewWindow::createActions(SUIT_ResourceMgr* theResourceMgr)
   mgr->registerAction( anAction, SwitchInteractionStyleId );
 
   // Switch between zooming styles
-  anAction = new QtxAction(tr("MNU_SVTK_ZOOMING_STYLE_SWITCH"), 
+  anAction = new QtxAction(tr("MNU_SVTK_ZOOMING_STYLE_SWITCH"),
                            theResourceMgr->loadPixmap( "VTKViewer", tr( "ICON_SVTK_ZOOMING_STYLE_SWITCH" ) ),
                            tr( "MNU_SVTK_ZOOMING_STYLE_SWITCH" ), 0, this);
   anAction->setStatusTip(tr("DSC_SVTK_ZOOMING_STYLE_SWITCH"));
@@ -2436,7 +2436,7 @@ void SVTK_ViewWindow::createActions(SUIT_ResourceMgr* theResourceMgr)
   QSignalMapper* aSignalMapper = new QSignalMapper( this );
   connect(aSignalMapper, SIGNAL(mapped(int)), this, SLOT(onSwitchPreSelectionMode(int)));
 
-  anAction = new QtxAction(tr("MNU_SVTK_PRESELECTION_STANDARD"), 
+  anAction = new QtxAction(tr("MNU_SVTK_PRESELECTION_STANDARD"),
                            theResourceMgr->loadPixmap( "VTKViewer", tr( "ICON_SVTK_PRESELECTION_STANDARD" ) ),
                            tr( "MNU_SVTK_PRESELECTION_STANDARD" ), 0, this);
   anAction->setStatusTip(tr("DSC_SVTK_PRESELECTION_STANDARD"));
@@ -2444,8 +2444,8 @@ void SVTK_ViewWindow::createActions(SUIT_ResourceMgr* theResourceMgr)
   connect(anAction, SIGNAL(triggered()), aSignalMapper, SLOT(map()));
   aSignalMapper->setMapping( anAction, Standard_Preselection );
   mgr->registerAction( anAction, StandardPreselectionId );
-  
-  anAction = new QtxAction(tr("MNU_SVTK_PRESELECTION_DYNAMIC"), 
+
+  anAction = new QtxAction(tr("MNU_SVTK_PRESELECTION_DYNAMIC"),
                            theResourceMgr->loadPixmap( "VTKViewer", tr( "ICON_SVTK_PRESELECTION_DYNAMIC" ) ),
                            tr( "MNU_SVTK_PRESELECTION_DYNAMIC" ), 0, this);
   anAction->setStatusTip(tr("DSC_SVTK_PRESELECTION_DYNAMIC"));
@@ -2454,7 +2454,7 @@ void SVTK_ViewWindow::createActions(SUIT_ResourceMgr* theResourceMgr)
   aSignalMapper->setMapping( anAction, Dynamic_Preselection );
   mgr->registerAction( anAction, DynamicPreselectionId );
 
-  anAction = new QtxAction(tr("MNU_SVTK_PRESELECTION_DISABLED"), 
+  anAction = new QtxAction(tr("MNU_SVTK_PRESELECTION_DISABLED"),
                            theResourceMgr->loadPixmap( "VTKViewer", tr( "ICON_SVTK_PRESELECTION_DISABLED" ) ),
                            tr( "MNU_SVTK_PRESELECTION_DISABLED" ), 0, this);
   anAction->setStatusTip(tr("DSC_SVTK_PRESELECTION_DISABLED"));
@@ -2470,7 +2470,7 @@ void SVTK_ViewWindow::createActions(SUIT_ResourceMgr* theResourceMgr)
   mgr->registerAction( aPreselectionAction, PreselectionId );
 
   // Selection
-  anAction = new QtxAction(tr("MNU_SVTK_ENABLE_SELECTION"), 
+  anAction = new QtxAction(tr("MNU_SVTK_ENABLE_SELECTION"),
                            theResourceMgr->loadPixmap( "VTKViewer", tr( "ICON_SVTK_SELECTION" ) ),
                            tr( "MNU_SVTK_ENABLE_SELECTION" ), 0, this);
   anAction->setStatusTip(tr("DSC_SVTK_ENABLE_SELECTION"));
@@ -2479,7 +2479,7 @@ void SVTK_ViewWindow::createActions(SUIT_ResourceMgr* theResourceMgr)
   mgr->registerAction( anAction, EnableSelectionId );
 
   // Start recording
-  myStartAction = new QtxAction(tr("MNU_SVTK_RECORDING_START"), 
+  myStartAction = new QtxAction(tr("MNU_SVTK_RECORDING_START"),
                                 theResourceMgr->loadPixmap( "VTKViewer", tr( "ICON_SVTK_RECORDING_START" ) ),
                                 tr( "MNU_SVTK_RECORDING_START" ), 0, this);
   myStartAction->setStatusTip(tr("DSC_SVTK_RECORDING_START"));
@@ -2487,7 +2487,7 @@ void SVTK_ViewWindow::createActions(SUIT_ResourceMgr* theResourceMgr)
   mgr->registerAction( myStartAction, StartRecordingId );
 
   // Play recording
-  myPlayAction = new QtxAction(tr("MNU_SVTK_RECORDING_PLAY"), 
+  myPlayAction = new QtxAction(tr("MNU_SVTK_RECORDING_PLAY"),
                                theResourceMgr->loadPixmap( "VTKViewer", tr( "ICON_SVTK_RECORDING_PLAY" ) ),
                                tr( "MNU_SVTK_RECORDING_PLAY" ), 0, this);
   myPlayAction->setStatusTip(tr("DSC_SVTK_RECORDING_PLAY"));
@@ -2496,7 +2496,7 @@ void SVTK_ViewWindow::createActions(SUIT_ResourceMgr* theResourceMgr)
   mgr->registerAction( myPlayAction, PlayRecordingId );
 
   // Pause recording
-  myPauseAction = new QtxAction(tr("MNU_SVTK_RECORDING_PAUSE"), 
+  myPauseAction = new QtxAction(tr("MNU_SVTK_RECORDING_PAUSE"),
                                 theResourceMgr->loadPixmap( "VTKViewer", tr( "ICON_SVTK_RECORDING_PAUSE" ) ),
                                 tr( "MNU_SVTK_RECORDING_PAUSE" ), 0, this);
   myPauseAction->setStatusTip(tr("DSC_SVTK_RECORDING_PAUSE"));
@@ -2505,7 +2505,7 @@ void SVTK_ViewWindow::createActions(SUIT_ResourceMgr* theResourceMgr)
   mgr->registerAction( myPauseAction, PauseRecordingId );
 
   // Stop recording
-  myStopAction = new QtxAction(tr("MNU_SVTK_RECORDING_STOP"), 
+  myStopAction = new QtxAction(tr("MNU_SVTK_RECORDING_STOP"),
                                theResourceMgr->loadPixmap( "VTKViewer", tr( "ICON_SVTK_RECORDING_STOP" ) ),
                                tr( "MNU_SVTK_RECORDING_STOP" ), 0, this);
   myStopAction->setStatusTip(tr("DSC_SVTK_RECORDING_STOP"));
@@ -2520,13 +2520,13 @@ void SVTK_ViewWindow::createActions(SUIT_ResourceMgr* theResourceMgr)
 void SVTK_ViewWindow::createToolBar()
 {
   QtxActionToolMgr* mgr = toolMgr();
-  
+
   mgr->append( DumpId, myToolBar );
   mgr->append( SwitchInteractionStyleId, myToolBar );
   mgr->append( SwitchZoomingStyleId, myToolBar );
 
   mgr->append( mgr->separator(), myToolBar );
+
   mgr->append( PreselectionId, myToolBar );
   mgr->append( EnableSelectionId, myToolBar );
 
@@ -2733,7 +2733,7 @@ void SVTK_ViewWindow::onViewParameters(bool theIsActivate)
 /*!
   Custom show event handler
 */
-void SVTK_ViewWindow::showEvent( QShowEvent * theEvent ) 
+void SVTK_ViewWindow::showEvent( QShowEvent * theEvent )
 {
   emit Show( theEvent );
 }
@@ -2741,7 +2741,7 @@ void SVTK_ViewWindow::showEvent( QShowEvent * theEvent )
 /*!
   Custom hide event handler
 */
-void SVTK_ViewWindow::hideEvent( QHideEvent * theEvent ) 
+void SVTK_ViewWindow::hideEvent( QHideEvent * theEvent )
 {
   emit Hide( theEvent );
 }
@@ -2778,7 +2778,7 @@ SUIT_CameraProperties SVTK_ViewWindow::cameraProperties()
   vtkCamera* aCamera = getRenderer()->GetActiveCamera();
   if ( !aCamera )
     return aProps;
-  
+
   aProps.setDimension( SUIT_CameraProperties::Dim3D );
   if ( toolMgr()->action( ParallelModeId ) ) {
     if ( toolMgr()->action( ParallelModeId )->isChecked() )
@@ -2796,7 +2796,7 @@ SUIT_CameraProperties SVTK_ViewWindow::cameraProperties()
   aCamera->GetFocalPoint( aFocalPoint );
   aCamera->GetPosition( aPosition );
   aCamera->GetViewUp( aViewUp );
-  
+
   aProps.setFocalPoint( aFocalPoint[0], aFocalPoint[1], aFocalPoint[2] );
   aProps.setPosition( aPosition[0], aPosition[1], aPosition[2] );
   aProps.setViewUp( aViewUp[0], aViewUp[1], aViewUp[2] );
@@ -2809,7 +2809,7 @@ SUIT_CameraProperties SVTK_ViewWindow::cameraProperties()
 
   GetRenderer()->GetScale( anAxialScale );
   aProps.setAxialScale( anAxialScale[0], anAxialScale[1], anAxialScale[2] );
-  
+
   return aProps;
 }
 
@@ -2831,7 +2831,7 @@ void SVTK_ViewWindow::synchronize( SUIT_ViewWindow* theView )
 
   // get camera
   vtkCamera* aCamera = getRenderer()->GetActiveCamera();
-  
+
   double aFocalPoint[3];
   double aPosition[3];
   double aViewUp[3];
@@ -2842,7 +2842,7 @@ void SVTK_ViewWindow::synchronize( SUIT_ViewWindow* theView )
   aProps.getPosition( aPosition[0], aPosition[1], aPosition[2] );
   aProps.getFocalPoint( aFocalPoint[0], aFocalPoint[1], aFocalPoint[2] );
   aProps.getAxialScale( anAxialScale[0], anAxialScale[1], anAxialScale[2] );
-  
+
   // restore properties to the camera
   aCamera->SetViewUp( aViewUp );
   aCamera->SetPosition( aPosition );
index 640bf3833e6eb7e5b4935232285494e18311c45b..6dc0e9eb59c3bb5834479ae52e1ac272ffc7abed 100644 (file)
@@ -342,12 +342,12 @@ void SalomeApp_Application::createActions()
   //! Dump study
   createAction( DumpStudyId, tr( "TOT_DESK_FILE_DUMP_STUDY" ), QIcon(),
                 tr( "MEN_DESK_FILE_DUMP_STUDY" ), tr( "PRP_DESK_FILE_DUMP_STUDY" ),
-                Qt::CTRL+Qt::Key_D, desk, false, this, SLOT( onDumpStudy() ) );
+                QKeySequence::UnknownKey, desk, false, this, SLOT( onDumpStudy() ), "/PRP_DESK_FILE_DUMP_STUDY" );
 
   //! Load script
   createAction( LoadScriptId, tr( "TOT_DESK_FILE_LOAD_SCRIPT" ), QIcon(),
                 tr( "MEN_DESK_FILE_LOAD_SCRIPT" ), tr( "PRP_DESK_FILE_LOAD_SCRIPT" ),
-                Qt::CTRL+Qt::Key_T, desk, false, this, SLOT( onLoadScript() ) );
+                QKeySequence::UnknownKey, desk, false, this, SLOT( onLoadScript() ), "/PRP_DESK_FILE_LOAD_SCRIPT" );
 
   //! Properties
   createAction( PropertiesId, tr( "TOT_DESK_PROPERTIES" ), QIcon(),
@@ -357,7 +357,7 @@ void SalomeApp_Application::createActions()
   //! Catalog Generator
   createAction( CatalogGenId, tr( "TOT_DESK_CATALOG_GENERATOR" ),  QIcon(),
                 tr( "MEN_DESK_CATALOG_GENERATOR" ), tr( "PRP_DESK_CATALOG_GENERATOR" ),
-                Qt::ALT+Qt::SHIFT+Qt::Key_G, desk, false, this, SLOT( onCatalogGen() ) );
+                QKeySequence::UnknownKey, desk, false, this, SLOT( onCatalogGen() ), "/PRP_DESK_CATALOG_GENERATOR" );
 
   //! Registry Display
   createAction( RegDisplayId, tr( "TOT_DESK_REGISTRY_DISPLAY" ),  QIcon(),
@@ -366,13 +366,13 @@ void SalomeApp_Application::createActions()
 
   createAction( ConnectId, tr( "TOT_DESK_CONNECT_STUDY" ), QIcon(),
                 tr( "MEN_DESK_CONNECT" ), tr( "PRP_DESK_CONNECT" ),
-                Qt::CTRL+Qt::Key_L, desk, false, this, SLOT( onLoadDoc() ) );
+                QKeySequence::UnknownKey, desk, false, this, SLOT( onLoadDoc() ), "/PRP_DESK_CONNECT" );
   //no need at this action for mono-study application because study is always exists
   action( ConnectId )->setVisible( false );
 
   createAction( DisconnectId, tr( "TOT_DESK_DISCONNECT_STUDY" ), QIcon(),
                 tr( "MEN_DESK_DISCONNECT" ), tr( "PRP_DESK_DISCONNECT" ),
-                Qt::CTRL+Qt::Key_U, desk, false, this, SLOT( onUnloadDoc() ) );
+                QKeySequence::UnknownKey, desk, false, this, SLOT( onUnloadDoc() ), "/PRP_DESK_DISCONNECT" );
   //no need at this action for mono-study application because study is always exists
   action( DisconnectId )->setVisible( false );
 
@@ -2155,7 +2155,7 @@ void SalomeApp_Application::ensureShaperIsActivated()
     study->dataModels( models );
     for( int i = 0; i < models.count() && !shaperIsActive; i++ )
       shaperIsActive = models[i]->module()->moduleName() == "Shaper";
-       
+
     if (shaper && !shaperIsActive)
       onDesktopMessage("register_module_in_study/Shaper");
   }
diff --git a/tools/DevTools/ShortcutMgr/ShortcutMgr. Resource generator.xlsx b/tools/DevTools/ShortcutMgr/ShortcutMgr. Resource generator.xlsx
new file mode 100644 (file)
index 0000000..bfc86f9
Binary files /dev/null and b/tools/DevTools/ShortcutMgr/ShortcutMgr. Resource generator.xlsx differ