Salome HOME
[bos #35160][EDF](2023-T1) Keyboard shortcuts.
authordish <Dmitrii.SHVYDKOI@opencascade.com>
Mon, 4 Dec 2023 15:34:45 +0000 (15:34 +0000)
committerdish <dmitrii.shvydkoi@opencascade.com>
Thu, 25 Jan 2024 10:50:35 +0000 (10:50 +0000)
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.

36 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_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/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/SUIT/SUIT_Application.cxx
src/SUIT/SUIT_Application.h
src/SUIT/SUIT_PreferenceMgr.cxx
src/SUIT/SUIT_PreferenceMgr.h
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/SVTK/SVTK_ViewWindow.cxx

index 80f3e6318b16f7ab880f35e3c802e54c2a9c1130..734cefd5715bfc2523dfe8fe4f76355905c7d447 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"):
@@ -211,7 +211,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:
@@ -222,9 +222,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**
@@ -232,7 +232,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.
@@ -241,13 +241,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.
@@ -269,15 +269,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.
 
@@ -285,8 +285,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:
 
@@ -296,7 +296,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.
@@ -359,13 +359,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"
@@ -374,7 +374,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
@@ -391,7 +391,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 b1cb7e3bb7700699c931795ffa780757d9cd333a..2d7ce6bef85ca4dbed1f563e6dba47ff37438763 100644 (file)
@@ -1207,7 +1207,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
@@ -2423,7 +2423,7 @@ void LightApp_Application::showPreferences( const QStringList& path )
     resourceMgr()->save();
 
     // Update shortcuts
-    shortcutMgr()->updateShortcuts();
+    shortcutMgr()->rebindActionsToKeySequences();
   }
 
   delete prefDlg;
@@ -3536,11 +3536,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 9b6bd755856bfad46180f0c9b41141fcbf020c26..b90aa42fa6a1d8a7bcfc34c323e89dda2d621566 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/Objects(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/Objects(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 0e1c4249e764cebd02385eaca650232a47048102..84ba5ea31e289c506d22603b9a62b1a660d3eaaf 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>
-  <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="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="#General/Objects(s)/Show" value="Ctrl+Alt+S"/>
+    <parameter name="#General/Objects(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="shortcuts:SHAPER">
+  </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>
+  <!--Names of actions for shortcut editor
+       See SUIT_ShortcutMgr for details
+  -->
+  <section name="shortcut_translations:/#General/Objects(s)/Show">
+      <parameter name="en" value="Show"/>
+      <parameter name="fr" value="Afficher"/>
+      <parameter name="ja" value="表示"/>
+  </section>
+  <section name="shortcut_translations:/#General/Objects(s)/Hide">
+      <parameter name="en" value="Hide"/>
+      <parameter name="fr" value="Cacher"/>
+      <parameter name="ja" value="非表示"/>
+  </section>
+  <section name="shortcut_translations:/#Viewers/View/Set X+">
+      <parameter name="en" value="Back view"/>
+      <parameter name="fr" value="Vue arrière"/>
+      <parameter name="ja" value="背面図"/>
+  </section>
+  <section name="shortcut_translations:/#Viewers/View/Set X-">
+      <parameter name="en" value="Front view"/>
+      <parameter name="fr" value="Vue de devant"/>
+      <parameter name="ja" value="正面"/>
+  </section>
+  <section name="shortcut_translations:/#Viewers/View/Set Y+">
+      <parameter name="en" value="Left view"/>
+      <parameter name="fr" value="Vue de gauche"/>
+      <parameter name="ja" value="左側のビュー"/>
+  </section>
+   <section name="shortcut_translations:/#Viewers/View/Set Y-">
+      <parameter name="en" value="Right view"/>
+      <parameter name="fr" value="Vue de droite"/>
+      <parameter name="ja" value="右側のビュー"/>
+  </section>
+  <section name="shortcut_translations:/#Viewers/View/Set Z+">
+      <parameter name="en" value="Bottom view"/>
+      <parameter name="fr" value="Vue de dessous"/>
+      <parameter name="ja" value="下から表示します。"/>
+  </section>
+  <section name="shortcut_translations:/#Viewers/View/Set Z-">
+      <parameter name="en" value="Top view"/>
+      <parameter name="fr" value="Vue de dessus"/>
+      <parameter name="ja" value="上から見る"/>
+  </section>
+  <section name="shortcut_translations:/#Viewers/View/Rotate anticlockwise">
+      <parameter name="en" value="Rotate view counterclockwise"/>
+      <parameter name="fr" value="Tourner la vue à gauche"/>
+      <parameter name="ja" value="表示を左に"/>
+  </section>
+  <section name="shortcut_translations:/#Viewers/View/Rotate clockwise">
+      <parameter name="en" value="Rotate view clockwise"/>
+      <parameter name="fr" value="Tourner à droite"/>
+      <parameter name="ja" value="右のビューを回転させる"/>
+  </section>
+  <section name="shortcut_translations:/#Viewers/View/Reset">
+      <parameter name="en" value="Reset view point"/>
+      <parameter name="fr" value="Restaurer le point de vue"/>
+      <parameter name="ja" value="ビューのポイントを復元します。"/>
+  </section>
+  <section name="shortcut_translations:GEOM/Isolines/Increase number">
+      <parameter name="en" value="Increase number of isolines"/>
+      <parameter name="fr" value="Augmenter le nombre d'isolignes"/>
+      <parameter name="ja" value="等値線の数を増やす"/>
+  </section>
+  <section name="shortcut_translations:GEOM/Isolines/Decrease number">
+      <parameter name="en" value="Decrease number of isolines"/>
+      <parameter name="fr" value="Diminuer le nombre d'isolignes"/>
+      <parameter name="ja" value="等値線の数を減らす"/>
+  </section>
+  <section name="shortcut_translations:GEOM/Transparency/Increase">
+      <parameter name="en" value="Increase transparency"/>
+      <parameter name="fr" value="Augmenter la transparence"/>
+      <parameter name="ja" value="透明度を高める"/>
+  </section>
+   <section name="shortcut_translations:GEOM/Transparency/Decrease">
+      <parameter name="en" value="Decrease transparency"/>
+      <parameter name="fr" value="Diminuer la transparence"/>
+      <parameter name="ja" value="透明度を下げる"/>
   </section>
   <section name="ExternalBrowser" >
     <!-- External HELP browser settings -->
index 6cbc59b1681eeaf9825601042ced1b26e5135d5b..dfab111181bab635b6b66b7e075546dd1041e2ff 100644 (file)
@@ -1089,7 +1089,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 1a1214cc43ae6b25aeb65553cc773be2a675de34..3fcbf6f9fbfe2efab7d7906674c414df87109350 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;
@@ -3659,7 +3659,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] );
@@ -3669,7 +3669,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..e2a31045096f4943fb7e75490caeb26acaa16b24 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,28 @@ 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 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, 
-                     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 +116,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 +146,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 +174,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 +207,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 +228,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 +274,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 +293,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..3acae995648ac478f782b57ad0d7c72cf5be1f94 100644 (file)
@@ -52,9 +52,16 @@ public:
 
   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 412e178d40ac0d41ddd7a5009e37a23ef7d5788a..8cd626e8665d9764c29a6bd5ed498dc69c6adbc7 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()
 */
@@ -4426,119 +4428,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.
@@ -4548,7 +4499,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
@@ -4835,9 +4786,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
@@ -4846,7 +4797,7 @@ QtxUserDefinedItem::QtxUserDefinedItem( QtxPreferenceItem* parent )
   : QtxPageNamedPrefItem(QString(), parent), myContent(0)
 {
 }
-  
+
 /*!
   \brief Lets to Store preferences for content instance
   \sa retrieve()
@@ -4884,7 +4835,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)
@@ -4896,14 +4847,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 a327f47b2d07d918464110924028a9b52f9d8c89..24ed21539cebc9244949871396a49931bc539061 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& );
@@ -738,31 +740,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..aa78dfafdd3263efeb7a9fe0ea1343c58a8743e2 100644 (file)
 
 #include <QToolButton>
 #include <QLineEdit>
+#include <QLabel>
 #include <QTableWidgetItem>
+#include <QTextEdit>
 #include <QMessageBox>
+#include <QPushButton>
+#include <QBrush>
+#include <QColor>
 
 #include <QKeyEvent>
 #include <QKeySequence>
 
+
 #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( myShortcut->text() );
+  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()
+{
+  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)
+{
+  myActionName->setText("<b>" + theModuleName + "</b>&nbsp;&nbsp;" + theActionName);
+}
+
+void QtxEditKeySequenceDialog::setConfirmedKeySequence(const QKeySequence& theSequence)
 {
-  setColumnCount( 2 );
-  setSelectionMode( QAbstractItemView::SingleSelection );
-  setColumnWidth ( 0, COLUMN_SIZE);
-  setSortingEnabled(false);
-  headerItem()->setHidden ( true ); 
+  myKeySequenceEdit->setConfirmedKeySequence(theSequence);
+}
 
-  this->installEventFilter(this);
-  connect( this, SIGNAL( currentItemChanged ( QTreeWidgetItem*, QTreeWidgetItem* ) ), this, SLOT( onCurrentItemChanged ( QTreeWidgetItem*, QTreeWidgetItem* ) ) );
+QKeySequence QtxEditKeySequenceDialog::editedKeySequence() const
+{
+  return myKeySequenceEdit->editedKeySequence();
+}
 
+int QtxEditKeySequenceDialog::exec()
+{
+  myKeySequenceEdit->setFocus(Qt::ActiveWindowFocusReason);
+  return QDialog::exec();
 }
 
-/*!
-  \brief Destructor
-*/
-QtxShortcutTree::~QtxShortcutTree(){}
+void QtxEditKeySequenceDialog::onEditingStarted()
+{
+  myTextEdit->setEnabled(false);
+}
 
-/*!
-  \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 );
+void QtxEditKeySequenceDialog::onEditingFinished()
+{
+  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>";
       }
-      return true;
+      report += "</ul>";
     }
-    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 );
+    doc->setHtml(report);
+  }
+  else /* if no conflicts */ {
+    doc->clear();
+  }
+}
 
-      return true;
-    }    
-  } 
-  return false;
+void QtxEditKeySequenceDialog::onConfirm()
+{
+  if (myKeySequenceEdit->isKeySequenceModified())
+    accept();
+  else
+    reject();
 }
 
-/*!
-  \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 )
+
+/*! \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()))
 {
-  if ( prev && prev->text( 1 ).endsWith( "+" ) )
-      prev->setText( 1, myPrevBindings[ prev->parent()->text( 0 ) ][ prev->text( 0 ) ] );
+  setColumnCount(2);
+  setSelectionMode(QAbstractItemView::SingleSelection);
+  setColumnWidth(0, COLUMN_SIZE);
+  setSortingEnabled(false); // Items are sorted in the same way, as in ShortcutContainer.
+  headerItem()->setHidden(true);
+  setExpandsOnDoubleClick(false); // Open shortcut editor on double click instead.
+  setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents);
+  setToolTip(tr("Double click to edit key sequence."));
+  setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+  myEditDialog = new QtxEditKeySequenceDialog(this);
+
+  this->installEventFilter(this);
+  connect(this, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), this, SLOT(onItemDoubleClicked(QTreeWidgetItem*, int)));
+
+  QtxShortcutTree::instances[myShortcutContainer.get()].emplace(this);
 }
 
-/*!
-  \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 )
+QtxShortcutTree::~QtxShortcutTree()
 {
-  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();
-  }
-  for( ShortcutMap::const_iterator it = theShortcutMap.constBegin(); it != theShortcutMap.constEnd(); ++it )
-      item->addChild( new QTreeWidgetItem( QStringList() << it.key() << it.value() ) );
-  myPrevBindings.insert( title, theShortcutMap);
+  QtxShortcutTree::instances[myShortcutContainer.get()].erase(this);
+  if (QtxShortcutTree::instances[myShortcutContainer.get()].empty())
+    QtxShortcutTree::instances.erase(myShortcutContainer.get());
 }
 
-/*!
-  \brief Get all sections names.
-  \return list of section names
-*/
-QStringList QtxShortcutTree::sections() const
+/*! \brief Copies shortcuts from ShortcutMgr. (Re)displays shortcuts of myModuleIDs. */
+void QtxShortcutTree::setShortcutsFromManager()
 {
-  QStringList lst;
-  for( int i = 0; i < topLevelItemCount(); i++ )
-    lst << topLevelItem( i )->text( 0 ); 
-  return lst;
+  const auto shortcutMgr = SUIT_ShortcutMgr::get();
+  *myShortcutContainer = shortcutMgr->getShortcutContainer();
+  // nb! ShortcutMgr never removes shortcuts from its container, only disables.
+
+  updateItems(false /*theHighlightModified*/, true /*theUpdateSyncTrees*/);
 }
 
-ShortcutMap* QtxShortcutTree::bindings( const QString& sec ) const
+/*! \brief Copies shortcuts from resources, user files are not accounted. (Re)displays shortcuts of myModuleIDs. */
+void QtxShortcutTree::setDefaultShortcuts()
 {
-  ShortcutMap* aMap = new ShortcutMap();
-  QTreeWidgetItem* item = findItems( sec, Qt::MatchFixedString ).first();
-  int nbChildren = item->childCount();
+  SUIT_ShortcutContainer defaultShortcuts;
+  SUIT_ShortcutMgr::fillContainerFromPreferences(defaultShortcuts, true /*theDefaultOnly*/);
 
-  for( int i = 0; i < nbChildren; i++ ) {
-    QTreeWidgetItem* child =  item->child(i);
-    aMap->insert( child->text( 0 ), child->text( 1 ) );
-  }
+  myShortcutContainer->merge(defaultShortcuts, true /*theOverride*/, true /*theTreatAbsentIncomingAsDisabled*/);
+  // nb! SUIT_ShortcutContainer never erases shortcuts, only disables.
 
-  return aMap;
+  updateItems(true /*theHighlightModified*/, true /*theUpdateSyncTrees*/);
 }
 
-void QtxShortcutTree::focusOutEvent ( QFocusEvent* event )
+/*! \brief Applies pending changes to ShortcutMgr. Updates other instances of QtxShortcutTree. */
+void QtxShortcutTree::applyChangesToShortcutMgr()
 {
-  QWidget::focusOutEvent( event );
-  if ( currentItem() && currentItem()->isSelected() )
-    currentItem()->setText( 1, myPrevBindings[ currentItem()->parent()->text( 0 ) ][ currentItem()->text( 0 ) ] );
+  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();
+  }
 }
 
-/*!
-  \brief Set the list of shortcuts general sections.
-  
-  Key combinations in general sections should not intersect
-  with any other key combinations.
+std::shared_ptr<const SUIT_ShortcutContainer> QtxShortcutTree::shortcutContainer() const
+{
+  return myShortcutContainer;
+}
 
-  \param sectionsList list of common section names
-*/
-void QtxShortcutTree::setGeneralSections( const QStringList& sectionsList )
+/*! \param If theUpdateSyncTrees, trees sharing the same shortcut container are updated. */
+void QtxShortcutTree::updateItems(bool theHighlightModified, bool theUpdateSyncTrees)
 {
-  myGeneralSections = sectionsList;
+  const auto shortcutMgr = SUIT_ShortcutMgr::get();
+
+  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)
+        takeTopLevelItem(moduleItemAndIdx.second);
+
+      continue;
+    }
+
+    const auto moduleItemAndIdx = findModuleFolderItem(moduleID);
+    QtxShortcutTreeItem* moduleItem = moduleItemAndIdx.first;
+    if (!moduleItem) {
+      moduleItem = QtxShortcutTreeItem::createFolderItem(moduleID);
+      moduleItem->setName(shortcutMgr->getModuleName(moduleID));
+      addTopLevelItem(moduleItem);
+      moduleItem->setFlags(Qt::ItemIsEnabled);
+
+      for (const auto& shortcut : moduleShortcuts) {
+        const QString& inModuleActionID = shortcut.first;
+        const QKeySequence& keySequence = shortcut.second;
+        const QString keySequenceString = keySequence.toString();
+
+        auto shortcutItem = QtxShortcutTreeItem::createShortcutItem(moduleID, inModuleActionID);
+        if (!shortcutItem) {
+          ShCutDbg("QtxShortcutTree can't create child item for action ID = \"" + SUIT_ShortcutMgr::makeActionID(moduleID, inModuleActionID) + "\".");
+          continue;
+        }
+
+        shortcutItem->setName(shortcutMgr->getActionName(moduleID, inModuleActionID));
+        shortcutItem->setKeySequence(keySequenceString);
+
+        if (theHighlightModified) {
+          const QKeySequence& appliedKeySequence = SUIT_ShortcutMgr::get()->getKeySequence(moduleID, inModuleActionID);
+          shortcutItem->highlightKeySequenceAsModified(keySequence != appliedKeySequence);
+        }
+
+        moduleItem->addChild(shortcutItem);
+      }
+
+      moduleItem->setExpanded(true); // Make tree expanded on first show.
+    }
+    else /* if the tree has the module-item */ {
+      for (int childIdx = 0; childIdx < moduleItem->childCount(); childIdx++) {
+        // Update exisiting items of a module.
+        QtxShortcutTreeItem* const childItem = static_cast<QtxShortcutTreeItem*>(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);
+      }
+
+      // 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()) {
+        // Module shortcuts and tree items must be ordered with the same comparator. Now it is std::less(inModuleActionID_A, inModuleActionID_B).
+        std::set<QString> actionIDsOfItems;
+        for (int childIdx = 0; childIdx < moduleItem->childCount(); childIdx++) {
+          QtxShortcutTreeItem* const childItem = static_cast<QtxShortcutTreeItem*>(moduleItem->child(childIdx));
+          actionIDsOfItems.emplace(childItem->myInModuleActionID);
+        }
+
+        for (const auto& shortcut : moduleShortcuts) {
+          const QString& inModuleActionID = shortcut.first;
+          const QKeySequence& keySequence = shortcut.second;
+
+          auto itNewActionID = actionIDsOfItems.emplace(inModuleActionID).first;
+          int newItemIdx = 0;
+          // Replace this with std::distance if C++ >= 17.
+          auto it = actionIDsOfItems.begin();
+          while (it != itNewActionID) {
+            it++;
+            newItemIdx++;
+          }
+
+          const auto shortcutItem = QtxShortcutTreeItem::createShortcutItem(moduleID, inModuleActionID);
+          if (!shortcutItem) {
+            ShCutDbg("QtxShortcutTree can't create child item for action ID = \"" + SUIT_ShortcutMgr::makeActionID(moduleID, inModuleActionID) + "\".");
+            continue;
+          }
+
+          shortcutItem->setName(shortcutMgr->getActionName(moduleID, inModuleActionID));
+          shortcutItem->setKeySequence(keySequence.toString());
+
+          if (theHighlightModified) {
+            const QKeySequence& appliedKeySequence = SUIT_ShortcutMgr::get()->getKeySequence(moduleID, inModuleActionID);
+            shortcutItem->highlightKeySequenceAsModified(keySequence != appliedKeySequence);
+          }
+
+          moduleItem->insertChild(newItemIdx, shortcutItem);
+        }
+      }
+    }
+  }
+
+  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 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);
+/*! \returns Pointer and index of top-level item.
+If the tree does not contain an item with theModuleID, returns {nullptr, -1}. */
+std::pair<QtxShortcutTreeItem*, int> QtxShortcutTree::findModuleFolderItem(const QString& theModuleID) const
+{
+  for (int moduleIdx = 0; moduleIdx < topLevelItemCount(); moduleIdx++) {
+    QtxShortcutTreeItem* moduleItem = static_cast<QtxShortcutTreeItem*>(topLevelItem(moduleIdx));
+    if (moduleItem->myModuleID == theModuleID)
+      return std::pair<QtxShortcutTreeItem*, int>(moduleItem, moduleIdx);
   }
+  return std::pair<QtxShortcutTreeItem*, int>(nullptr, -1);
+}
 
-  // 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;     
+void QtxShortcutTree::onItemDoubleClicked(QTreeWidgetItem* theItem, int theColIdx)
+{
+  QtxShortcutTreeItem* const item = static_cast<QtxShortcutTreeItem*>(theItem);
+  // Do not react if folder-item is clicked.
+  if (item->isFolder())
+    return;
+
+  myEditDialog->setModuleAndActionID(item->myModuleID, item->myInModuleActionID);
+  myEditDialog->setModuleAndActionName(static_cast<QtxShortcutTreeItem*>(item->parent())->name(), item->name());
+  myEditDialog->setConfirmedKeySequence(QKeySequence::fromString(item->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(item->myModuleID, item->myInModuleActionID, newKeySequence, true /*override*/);
+
+  /** { moduleID, {inModuleActionID, keySequence}[] }[] */
+  std::map<QString, std::map<QString, QString>> changes;
+  changes[item->myModuleID][item->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++) {
+      QtxShortcutTreeItem* const childItem = static_cast<QtxShortcutTreeItem*>(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*/ std::map<SUIT_ShortcutContainer*, std::set<QtxShortcutTree*>> QtxShortcutTree::instances =
+std::map<SUIT_ShortcutContainer*, std::set<QtxShortcutTree*>>();
+
+
+QtxShortcutTreeItem::QtxShortcutTreeItem(const QString& theModuleID, const QString& theInModuleActionID)
+: QTreeWidgetItem(), myModuleID(theModuleID), myInModuleActionID(theInModuleActionID)
+{ }
+
+/*static*/ QtxShortcutTreeItem* QtxShortcutTreeItem::createFolderItem(const QString& theModuleID)
+{
+  auto item = new QtxShortcutTreeItem(theModuleID, QString());
+
+  QFont font = item->font(ElementIdx::Name);
+  font.setBold(true);
+  item->setFont(ElementIdx::Name, font);
+
+  return item;
+}
+
+/*! \returns nullptr if theInModuleActionID is empty. */
+/*static*/ QtxShortcutTreeItem* QtxShortcutTreeItem::createShortcutItem(const QString& theModuleID, const QString& theInModuleActionID)
+{
+  if (theInModuleActionID.isEmpty()) {
+    ShCutDbg("QtxShortcutTreeItem: attempt to create item with empty action ID.");
+    return nullptr;
+  }
+
+  return new QtxShortcutTreeItem(theModuleID, theInModuleActionID);
+}
 
-  return true;
+bool QtxShortcutTreeItem::isFolder() const
+{
+  return myInModuleActionID.isEmpty();
+}
+
+/*! \brief Highlights text at ElementIdx::KeySequence. */
+void QtxShortcutTreeItem::highlightKeySequenceAsModified(bool theHighlight)
+{
+  static const QBrush bgHighlitingBrush = QBrush(Qt::darkGreen);
+  static const QBrush fgHighlitingBrush = QBrush(Qt::white);
+  static const QBrush noBrush = QBrush();
+
+  setBackground(ElementIdx::KeySequence, theHighlight ? bgHighlitingBrush : noBrush);
+  setForeground(ElementIdx::KeySequence, theHighlight ? fgHighlitingBrush : noBrush);
+}
+
+void QtxShortcutTreeItem::setName(const QString& theName)
+{
+  setText(ElementIdx::Name, theName);
 }
+
+QString QtxShortcutTreeItem::name() const
+{
+  return text(ElementIdx::Name);
+}
+
+void QtxShortcutTreeItem::setKeySequence(const QString& theKeySequence)
+{
+  setText(ElementIdx::KeySequence, theKeySequence);
+}
+
+QString QtxShortcutTreeItem::keySequence() const
+{
+  return text(ElementIdx::KeySequence);
+}
\ No newline at end of file
index 231e65a9d2cc9abda50f542a66c638f6f494c57e..065959ea11027fde1e860ef340ce849913e5218d 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>
 
 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 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);
+
+  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 );
+  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;
+
+private:
+  void updateItems(bool theHighlightModified, bool theUpdateSyncTrees);
+  std::pair<QtxShortcutTreeItem*, int> findModuleFolderItem(const QString& theModuleID) const;
 
 private slots:
-  void                      onCurrentItemChanged( QTreeWidgetItem*, QTreeWidgetItem* );
+  void onItemDoubleClicked(QTreeWidgetItem* theWidgetItem, int theColIdx);
+
+public:
+  /** Keeps IDs of modules, which will be shown on setShortcutsFromManager(). */
+  std::set<QString> myModuleIDs;
 
 private:
-  QMap< QString, ShortcutMap > myPrevBindings;
-  QStringList myGeneralSections;
+  /** 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;
+
+  /**
+   * 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
+{
+private:
+  QtxShortcutTreeItem(const QString& theModuleID, const QString& theInModuleActionID);
+
+  enum ElementIdx {
+    Name = 0,
+    KeySequence = 1, // Empty, if item is used as folder.
+  };
+
+public:
+  static QtxShortcutTreeItem* createFolderItem(const QString& theModuleID);
+  static QtxShortcutTreeItem* createShortcutItem(const QString& theModuleID, const QString& theInModuleActionID);
+  virtual ~QtxShortcutTreeItem() = default;
+
+  bool isFolder() const;
+  void highlightKeySequenceAsModified(bool theHighlight);
+
+  void setName(const QString& theName);
+  QString name() const;
+
+  void setKeySequence(const QString& theKeySequence);
+  QString keySequence() const;
+
+  const QString myModuleID;
+  const QString myInModuleActionID; // Empty, if item is used as folder.
 };
 
-#endif // QTXSHORTCUTEDIT_H
+#endif // QTXSHORTCUTTREE_H
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..6c47b40b045e9facca8ccc89724810409bb4b36d 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>Double click to edit key sequence.</source>
+        <translation>Double-cliquez pour modifier la séquence de touches.</translation>
+    </message>
+</context>
 </TS>
index 173c8128288fe8d0517d008310985163c6a522a8..06d53157c1dce3fe31a2eb29daf6d6e500701f22 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>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..e5fc5df7fb58a694eaff6bc9a6fca20960e142f3 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,32 +171,32 @@ 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, tr( "TOT_DESK_VIEW_STATUSBAR" ),
                              QIcon(), tr( "MEN_DESK_VIEW_STATUSBAR" ),
@@ -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 fef1771b3e5bbba0bd0e0f355282b86f8908a0d8..41bbef40e35b2e78f10104a452c618646fbccb7a 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
 */
 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
 */
 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 )
index 0a0aa1830c85c0ce021ec4700323a9091da2683a..40f5a40e252522f27c2dd71f2900223504a088be 100644 (file)
@@ -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 );//@}
index 08c0208ee404be5612c8fb47299e8511cdbd5b1a..a45449d3674e9b94a8c6278a82659d030fca106f 100644 (file)
@@ -154,11 +154,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 e68d29becfe489c70e04bc9004a9e0d654e53b5b..edd0823ae953c6e08a380a50bc6310912e2b6ede 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,            
+                Slider, ShortcutTree, BiColor, Background,
                  UserDefined = 1000 } PrefItemType;
 
 public:
index 41b6d8b2a2bd96f0dff285b8e27fd4d3adfa11cb..4cfe2a2a7721a9f7be96e21f6585b0e1fc6b7bd1 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>
 
-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");
+
+/** Prefix of names of sections in preference files with shortcut actions' names. */
+static const QString SECTION_SHORTCUT_NAMES_PREFIX = QString("shortcut_translations");
+
+
+
+/**
+ * Uncomment this, to start collecting all shortcuts and action name translations (1),
+ * from instances of QtxActions, if a shortcut or name translation is absent in resource files.
+ *
+ * (1) Set required language in the application settings and run features of interest.
+ * For all actions from these features, their toolTip()s 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 <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 in runtime shortcuts,
+    using key sequences of QActions passed to the shortcut manager,
+    and translations, using QAction::toolTip(), of QtxActions passed to the shortcut manager.
+    Content of these files can be easily copied to resource 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;
+    }
+  }
+
+  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 collectTranslation(
+    const QString& theModuleID,
+    const QString& theInModuleActionID,
+    const QString& theLang,
+    const QString& theActionName
+  ) {
+    if (SUIT_ShortcutMgr::isInModuleMetaActionID(theInModuleActionID)) {
+      QString actionID = SUIT_ShortcutMgr::makeActionID(ROOT_MODULE_ID, theInModuleActionID);
+      // { actionID, {lang, actionName}[] } []
+      auto& moduleTranslations = myTranslationsOfMetaActions[theModuleID];
+
+      // {lang, actionName}[]
+      auto& actionTranslations = moduleTranslations[actionID];
+      actionTranslations[theLang] = theActionName;
+
+      const QString fileName = theModuleID + DevTools::TRANSLATIONS_OF_META_SUFFIX;
+      std::map<QString, std::map<QString, QString>> sections;
+      for (auto itAction = moduleTranslations.begin(); itAction != moduleTranslations.end(); itAction++) {
+        const QString sectionName = SECTION_SHORTCUT_NAMES_PREFIX + DevTools::XML_SECTION_TOKENS_SEPARATOR + ROOT_MODULE_ID;
+
+        // {lang, actionName}[]
+        std::map<QString, QString>& actionTranslations = itAction->second;
+        std::map<QString, QString>& section = sections[sectionName];
+        for (auto itTranslation = actionTranslations.begin(); itTranslation != actionTranslations.end(); itTranslation++) {
+          section[itTranslation->first] = itTranslation->second;
+        }
+      }
+      writeToXMLFile(fileName, sections);
+    }
+    else {
+      QString actionID = SUIT_ShortcutMgr::makeActionID(theModuleID, theInModuleActionID);
+      // { actionID, {lang, actionName}[] } []
+      auto& moduleTranslations = myTranslations[theModuleID];
+      // {lang, actionName}[]
+      auto& actionTranslations = moduleTranslations[actionID];
+      actionTranslations[theLang] = theActionName;
+
+      const QString fileName = theModuleID + DevTools::TRANSLATIONS_SUFFIX;
+      std::map<QString, std::map<QString, QString>> sections;
+      for (auto itAction = moduleTranslations.begin(); itAction != moduleTranslations.end(); itAction++) {
+        const QString sectionName = SECTION_SHORTCUT_NAMES_PREFIX + DevTools::XML_SECTION_TOKENS_SEPARATOR + itAction->first;
+
+        // {lang, actionName}[]
+        std::map<QString, QString>& actionTranslations = itAction->second;
+        std::map<QString, QString>& section = sections[sectionName];
+        for (auto itTranslation = actionTranslations.begin(); itTranslation != actionTranslations.end(); itTranslation++) {
+          section[itTranslation->first] = itTranslation->second;
+        }
+      }
+      writeToXMLFile(fileName, sections);
+    }
+  }
+
+  void collectShortcutAndTranslation(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 name (translation) in current language, if it is not provided in resource 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;
+
+      if (SUIT_ShortcutMgr::getActionNameFromResources(theAction->ID(), lang).first)
+        return;
+
+      collectTranslation(moduleIDAndActionID.first, moduleIDAndActionID.second, lang, theAction->toolTip());
+    }
+  }
+
+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
+  }
+
+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 TRANSLATIONS_SUFFIX;
+  static const QString TRANSLATIONS_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, {language, actionName} }[] }[] */
+  std::map<QString, std::map<QString, std::map<QString, QString>>> myTranslations;
+
+  /** { moduleID, { actionID, {language, actionName} }[] }[] */
+  std::map<QString, std::map<QString, std::map<QString, QString>>> myTranslationsOfMetaActions;
+
+#ifndef QT_NO_DOM
+  // { filename, {file, domDoc} }[]
+  std::map<QString, std::pair<QFile*, QDomDocument*>> myXMLFilesAndDocs;
+#endif // QT_NO_DOM
+
+  QFile* myActionsWithInvalidIDsFile;
+};
+/*static*/ DevTools* DevTools::instance = nullptr;
+/*static*/ const QString DevTools::SAVE_PATH = "shortcut_mgr_dev/";
+/*static*/ const QString DevTools::SHORTCUTS_SUFFIX = "_shortcuts";
+/*static*/ const QString DevTools::SHORTCUTS_OF_META_SUFFIX = "_shortcuts_of_meta_actions";
+/*static*/ const QString DevTools::TRANSLATIONS_SUFFIX = "_translations";
+/*static*/ const QString DevTools::TRANSLATIONS_OF_META_SUFFIX = "_translations_of_meta_actions";
+/*static*/ const QString DevTools::XML_SECTION_TOKENS_SEPARATOR = ":";
+/*static*/ const QString DevTools::INVALID_ID_ACTIONS_SUFFIX = "_actions_with_invalid_IDs";
+#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;
+}
+
+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 );
 }
 
-/*!
-  \brief Destructor
-*/
 SUIT_ShortcutMgr::~SUIT_ShortcutMgr()
 {
   qApp->removeEventFilter( this );
 }
 
-/*!
-  \brief Create new instance of shortcut manager.
-*/
-void SUIT_ShortcutMgr::Init()
+/*static*/ void SUIT_ShortcutMgr::Init()
 {
-  if( myShortcutMgr==NULL )
+  if( myShortcutMgr == nullptr) {
     myShortcutMgr = new SUIT_ShortcutMgr();
+    myShortcutMgr->setShortcutsFromPreferences();
+  }
 }
 
-/*!
-  \brief Return shortcut manager. 
-*/
-SUIT_ShortcutMgr* SUIT_ShortcutMgr::getShortcutMgr()
+/*static*/ SUIT_ShortcutMgr* SUIT_ShortcutMgr::get()
 {
   Init();
-  
   return myShortcutMgr;
 }
 
-/*!
-  \brief Custom event filter for qapplication .
-  
-  Redefined from QObject::eventFilter();
-*/
-bool SUIT_ShortcutMgr::eventFilter( QObject* o, QEvent* e )
+/*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)
 {
-  if ( e->type() == QEvent::ActionAdded ) {
-    QActionEvent* anActionEvent = (QActionEvent*)e;
-    if (anActionEvent) {
-      QtxAction* anAction = qobject_cast<QtxAction*>( anActionEvent->action() );
-      if ( anAction )
-       processAction( anAction );
+  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;
+}
+
+/*static*/ void SUIT_ShortcutMgr::fillContainerFromPreferences(SUIT_ShortcutContainer& theContainer, bool theDefaultOnly)
+{
+  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);
   }
 
-  return QObject::eventFilter( o, e );
+  if (theDefaultOnly)
+    resMgr->setWorkingMode(resMgrWorkingModeBefore);
+
+  ShCutDbg() && ShCutDbg("theContainer holds following shortcuts:\n" + theContainer.toString());
 }
 
-/*!
-  \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*/ std::pair<bool, QString> SUIT_ShortcutMgr::getActionNameFromResources(const QString& theActionID, QString theLanguage)
 {
   SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
+  if (!resMgr) {
+    Warning("SUIT_ShortcutMgr can't retrieve resource manager!");
+    return std::pair<bool, QString>(false, QString());
+  }
+
+  if (theLanguage.isEmpty())
+    theLanguage = resMgr->stringValue(LANG_SECTION, LANG_SECTION);
+
+  if (theLanguage.isEmpty())
+    return std::pair<bool, QString>(false, QString());
+
+  QStringList actionIDs = resMgr->subSections(SECTION_SHORTCUT_NAMES_PREFIX, false);
+  if (actionIDs.indexOf(theActionID) == -1)
+    return std::pair<bool, QString>(false, QString());
+
+  const QString sectionName = SECTION_SHORTCUT_NAMES_PREFIX + resMgr->sectionsToken() + theActionID;
+  QStringList availableActionNameLangs = resMgr->parameters(sectionName);
+  if (availableActionNameLangs.indexOf(theLanguage) == -1)
+    return std::pair<bool, QString>(false, QString());
+
+  QString actionName;
+  const bool nameInCurLangExists = resMgr->value(sectionName, theLanguage, actionName);
+
+  if (!nameInCurLangExists)
+    return std::pair<bool, QString>(false, QString());
+
+  return std::pair<bool, QString>(true, actionName);
+}
+
+
+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;
+  }
+
+  { // 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*>();
 
-    QKeySequence keySeq = getShortcutByActionName( shortcutActionName );
-    action->setShortcut( keySeq );
+    const std::map<QString, std::set<QAction*>>& moduleActions = itActions->second;
+    const auto itModuleActions = moduleActions.find(theInModuleActionID);
+    if (itModuleActions == moduleActions.end())
+      return std::set<QAction*>();
+
+    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
 {
-  QtxAction* anAction = (QtxAction*)obj;
-  
-  if ( anAction )
-    myShortcutActions.remove( anAction->shortcutActionName(), anAction );
+  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);
+}
+
+QString SUIT_ShortcutMgr::getModuleName(const QString& theModuleID) const
+{
+  return theModuleID == ROOT_MODULE_ID ? tr("General") : theModuleID;
+
+  /*
+  // TODO ? The SUIT_ShortcutMgr should be renamed and moved to CAM folder.
+  // Because the CAM_Application class is the closest to SUIT_Application in the inheritance hierarchy,
+  // who has concept of application module. Enabling this chunk of code, due to the presence of CAM_Application,
+  // requires to heap up cyclic dependencies in compilation units.
+  // Or it is reasond to add like-action-name-resources to preference files.
+
+  SUIT_Application* aSUIT_Application = SUIT_Session::session()->activeApplication();
+  const auto aCAM_Application = dynamic_cast<CAM_Application*>(aSUIT_Application);
+  if (aCAM_Application)
+    return aCAM_Application->moduleTitle(theModuleID);
+  */
+
+  // At least something meaningful.
+  return theModuleID;
+}
+
+QString SUIT_ShortcutMgr::getActionName(const QString& theModuleID, const QString& theInModuleActionID) 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;
+  }
+  return getActionName(actionID);
+}
+
+QString SUIT_ShortcutMgr::getActionName(const QString& theActionID) const
+{
+  const auto moduleAndInModuleActionIDs = SUIT_ShortcutMgr::splitIntoModuleIDAndInModuleID(theActionID);
+  if (moduleAndInModuleActionIDs.second.isEmpty()) {
+    ShCutDbg() && ShCutDbg("Can't get action name using invalid action ID \"" + theActionID + "\".");
+    return QString();
+  }
+
+  const auto itActionNames = myActionNames.find(theActionID);
+  if (itActionNames != myActionNames.end() && !itActionNames->second.empty()) {
+    QString lang;
+    SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
+    if (!resMgr) {
+      Warning("SUIT_ShortcutMgr can't retrieve resource manager!");
+      lang = DEFAULT_LANG;
+    }
+    else {
+      lang = resMgr->stringValue(LANG_SECTION, LANG_SECTION, DEFAULT_LANG);
+    }
+
+    QStringList langPriorityList = LANG_PRIORITY_LIST;
+    langPriorityList.push_front(lang);
+    langPriorityList.removeDuplicates();
+
+    const std::map<QString, QString>& translations = itActionNames->second;
+    for (const QString& lang : langPriorityList) {
+      const auto itTranslation = translations.find(lang);
+      if (itTranslation != translations.end()) {
+        return itTranslation->second;
+      }
+    }
+    return translations.begin()->second;
+  }
+  else /* if action ID has no loaded name in any language. */ {
+    // Try to get action->toolTip() 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 names of every action ever created at runtime does not guarantee,
+    // that the name will be available at any point in the life of the application,
+    // unless the name is added to dedicated section in a preference file.
+
+    const QString& moduleID = moduleAndInModuleActionIDs.first;
+    const QString& inModuleActionID = moduleAndInModuleActionIDs.second;
+
+    if (SUIT_ShortcutMgr::isInModuleMetaActionID(inModuleActionID)) {
+      for (const auto& actionAndID : myActionIDs) {
+        if (actionAndID.second.second == inModuleActionID)
+          return actionAndID.first->toolTip();
+      }
+      return inModuleActionID;
+    }
+    else {
+      const auto itModuleActions = myActions.find(moduleID);
+      if (itModuleActions == myActions.end())
+        return inModuleActionID;
+
+      const std::map<QString, std::set<QAction*>>& moduleActions = itModuleActions->second;
+      const auto itActions = moduleActions.find(inModuleActionID);
+      if (itActions == moduleActions.end())
+        return inModuleActionID;
+
+      const std::set<QAction*>& actions = itActions->second;
+      if (actions.empty())
+        return inModuleActionID;
+
+      return (*actions.begin())->toolTip();
+    }
+  }
+}
+
+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()->collectShortcutAndTranslation(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*/);
+  setActionNamesFromResources();
+
+  ShCutDbg() && ShCutDbg("ShortcutMgr has been initialized.");
+}
+
+/*static*/ void SUIT_ShortcutMgr::saveShortcutsToPreferences(const std::map<QString, std::map<QString, QKeySequence>>& theShortcutsInversed)
+{
+  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::setActionNamesFromResources(QString theLanguage)
+{
+  ShCutDbg() && ShCutDbg("Retrieving action names (translations) from preference files.");
+
+  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 actionIDs = resMgr->subSections(SECTION_SHORTCUT_NAMES_PREFIX, false);
+  for (const QString& actionID : actionIDs) {
+    // {language, actionName}[]
+    std::map<QString, QString>& actionTranslations = myActionNames[actionID];
+    const QString sectionName = SECTION_SHORTCUT_NAMES_PREFIX + resMgr->sectionsToken() + actionID;
+    QStringList availableActionNameLangs = resMgr->parameters(sectionName);
+
+    QString actionName = actionID;
+    const bool nameInCurLangExists = resMgr->value(sectionName, theLanguage, actionName);
+    if (nameInCurLangExists) {
+      actionTranslations[theLanguage] = actionName;
+    }
+    else {
+      bool nameInlinguaFrancaExists = false;
+      QString usedLanguage = QString();
+      for (int i = 1; i < langPriorityList.length(); i++) {
+        nameInlinguaFrancaExists = resMgr->value(sectionName, langPriorityList[i], actionName);
+        if (nameInlinguaFrancaExists) {
+          usedLanguage = langPriorityList[i];
+          break;
+        }
+      }
+      actionTranslations[theLanguage] = actionName;
+
+#ifdef SHORTCUT_MGR_DBG
+      if (nameInlinguaFrancaExists)
+        ShCutDbg("Can't find in preference files a name for action with ID \"" + actionID + "\" at current (" + theLanguage + ") language. " + usedLanguage + " is used for the name." );
+      else
+        ShCutDbg("Can't find in preference files a name for action with ID \"" + actionID + "\". Also tried " + langPriorityList.join(", ") + " languages." );
+#endif
+    }
+  }
+}
\ No newline at end of file
index c86363f693d0f741bcce8cdb9e54e75765bff5ee..41ca842c4d1e71ee5bd1bea5a30dfeb764d72654 100644 (file)
 #include "SUIT.h"
 
 #include <QObject>
-#include <QMultiMap>
+#include <QString>
+#include <map>
+#include <set>
+#include <utility>
 
+class QAction;
 class QtxAction;
-
 class QKeySequence;
 
 #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;
+};
+
+
 /*!
   \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 {nameExists, name}. */
+  static std::pair<bool, QString> getActionNameFromResources(const QString& theActionID, QString theLanguage = QString());
+
+
+  /*! \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;
+
+  /*! \brief Retrieves module name translated to appropriate language. */
+  QString getModuleName(const QString& theModuleID) const;
+
+  /*! \brief Retrieves at runtime action name translated to appropriate language,
+  if the language was loaded using \ref setActionNamesFromResources(QString). */
+  QString getActionName(const QString& theModuleID, const QString& theInModuleActionID) const;
+
+  /*! \brief Retrieves at runtime action name translated to appropriate language,
+  if the language was loaded using \ref setActionNamesFromResources(QString). */
+  QString getActionName(const QString& theActionID) 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);
+
+  /*! \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>".
 
-  void processAction( QtxAction* );
-  QKeySequence getShortcutByActionName( const QString& ) const;
+  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 myActionNames from preference files in theLanguage.
+  \param theLanguage If default, fills names in current language.
+  If a name in requested language is not found, seeks for the name EN in and then in FR.
+
+  In case if resource file is XML, it should look like this:
+  <!--
+  <section name="<action ID>"> Note, that apllication-unique must be typed, not in-module action ID.
+    ...
+    <parameter name="<language>" value="action name">
+    ...
+  </section>
+  -->
+  <section name="shortcut_translations:/#Viewers/Reset view">
+      <parameter name="en" value="Reset View Point"/>
+      <parameter name="fr" value="Restaurer le point de vue"/>
+      <parameter name="ja" value="ビューのポイントを復元します。"/>
+  </section>
+  <section name="shortcut_translations:GEOM/Isolines/Increase number">
+      <parameter name="en" value="Increase number of isolines"/>
+      <parameter name="fr" value="Augmenter le nombre d'isolignes"/>
+      <parameter name="ja" value="等値線の数を増やす"/>
+  </section>
+  */
+  void setActionNamesFromResources(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, {language, actionName} }[] */
+  std::map<QString, std::map<QString, QString>> myActionNames;
 };
 
 #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>
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 );