Salome HOME
[bos #40644][CEA](2024-T1) Feature search.
[modules/gui.git] / src / SUIT / SUIT_ShortcutMgr.h
1 // Copyright (C) 2007-2024  CEA, EDF, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 #ifndef SUIT_SHORTCUTMGR_H
24 #define SUIT_SHORTCUTMGR_H
25
26 #include "SUIT.h"
27
28 #include <QObject>
29 #include <QString>
30 #include <QStringList>
31 #include <QIcon>
32 #include <map>
33 #include <set>
34 #include <memory>
35 #include <utility>
36
37 class QAction;
38 class QtxAction;
39 class QKeySequence;
40 class QJsonObject;
41
42 #if defined WIN32
43 #pragma warning( disable: 4251 )
44 #endif
45
46 // Define SHORTCUT_MGR_DBG to enable SUIT_ShortcutMgr debug logging.
47 // #define SHORTCUT_MGR_DBG
48 /*! \returns true, if SUIT_ShortcutMgr debug logging is enabled. */
49 SUIT_EXPORT extern inline bool ShCutDbg() {
50 #ifdef SHORTCUT_MGR_DBG
51   return true;
52 #else
53   return false;
54 #endif
55 }
56 /*! \brief Prints theString to std::wcout, if SUIT_ShortcutMgr debug logging is enabled. */
57 SUIT_EXPORT extern bool ShCutDbg(const QString& theString);
58 /*! \brief Prints theString to std::wcout, if SUIT_ShortcutMgr debug logging is enabled. */
59 SUIT_EXPORT extern bool ShCutDbg(const char* theString);
60
61
62 /*!
63   \class SUIT_ShortcutContainer
64   \brief Provides means to keep and edit shortcuts in compliance with the application logics.
65   \ref See SUIT_ShortcutMgr for details.
66 */
67 class SUIT_EXPORT SUIT_ShortcutContainer
68 {
69 public:
70   SUIT_ShortcutContainer();
71
72   /*! \returns IDs of modules, which interfere with the module:
73   if the module is root (theModuleID is empty) - returns all module IDs, otherwise returns ["", theModuleID]. */
74   std::set<QString> getIDsOfInterferingModules(const QString& theModuleID) const;
75
76   std::set<QString> getIDsOfAllModules() const;
77
78   /*! \brief Checks for conflicts. If theOverride, modifies incoming and disables all conflicting shortcuts.
79   Redefining a key sequence for the action, if theKeySequence does not conflict with other shortcuts, is not considered as a conflict.
80   \param theModuleID The method has no effect if theModuleID is invalid. \ref See SUIT_ShortcutMgr::isModuleIDValid(const QString&) for details.
81   \param theInModuleActionID The method has no effect if theInModuleActionID is invalid. \ref See SUIT_ShortcutMgr::isInModuleActionIDValid(const QString&).
82   If theInModuleActionID is meta-action ID, the shortcut is set to root module, and theModuleID is ignored.
83   \param theKeySequence Empty theKeySequence does not cause conflicts, in this case
84   a shortcut for the action is disabled: theInModuleActionID is added/retained in the container but mapped to empty key sequence.
85   \param theOverride If true, conflicting shortcuts are disabled.
86   \returns {moduleID, inModuleActionID}[] - Set of conflicting actions if theOverride = false,
87   otherwise set of actions (without incoming one), whose shortcuts have been disabled. */
88   std::set<std::pair<QString, QString>> setShortcut(
89     QString theModuleID,
90     const QString& theInModuleActionID,
91     const QKeySequence& theKeySequence,
92     bool theOverride
93   );
94
95   /*! \brief Checks for conflicts. Existence of a shortcut with another key sequence for the action,
96   if theKeySequence does not conflict with other shortcuts, is not considered as a conflict.
97   \param theInModuleActionID If theInModuleActionID is meta-action ID, the shortcut is looked for in root module, and theModuleID is ignored.
98   \param theKeySequence Empty theKeySequence does not have conflicts.
99   \returns {moduleID, inModuleActionID}[] - Set of conflicting actions. */
100   std::set<std::pair<QString, QString>> getConflicts(
101     QString theModuleID,
102     const QString& theInModuleActionID,
103     const QKeySequence& theKeySequence
104   ) const;
105
106   /*! \returns empty key sequence if shortcut for the action is not set.
107   \param theInModuleActionID If theInModuleActionID is meta-action ID, seeks in root module, and theModuleID is ignored.*/
108   const QKeySequence& getKeySequence(QString theModuleID, const QString& theInModuleActionID) const;
109
110   /*! \returns true, if shortcut for the action is set (even if the mapped key sequence is empty).
111    \param theInModuleActionID If theInModuleActionID is meta-action ID, seeks in root module, and theModuleID is ignored.*/
112   bool hasShortcut(QString theModuleID, const QString& theInModuleActionID) const;
113
114   /*! \returns {inModuleActionID, keySequence}[] - If the module was not added, the map is empty. */
115   const std::map<QString, QKeySequence>& getModuleShortcutsInversed(const QString& theModuleID) const;
116
117   /*! \brief Seeks for shortcuts in the module with in-module action IDs, which start with theInModuleActionIDPrefix.
118   \returns {inModuleActionID, keySequence}[] - If the module was not added, the map is empty. */
119   const std::map<QString, QKeySequence> getModuleShortcutsInversed(const QString& theModuleID, const QString& theInModuleActionIDPrefix) const;
120
121   /*! \brief Merges shortcuts of theOther into this.
122   \param theOverride if true, overrides conflicting shortcuts.
123   If false, and this has no shortcut for an incoming action, and the incoming shortcut conflicts
124   with an existing shortcut, disabled shortcut for the incoming action is set.
125   \param theTreatAbsentIncomingAsDisabled If theOverride == false, theTreatAbsentIncomingAsDisabled is ignored.
126   If theOverride and theTreatAbsentIncomingAsDisabled, and theOther has no shortcut for an action, which exists in this,
127   the existing shortcut in this is set disabled.
128   \returns { moduleID, { inModuleActionID, keySequence }[] }[] - Modiified shortcuts inversed. */
129   std::map<QString, std::map<QString, QKeySequence>> merge(
130     const SUIT_ShortcutContainer& theOther,
131     bool theOverride,
132     bool theTreatAbsentIncomingAsDisabled = false
133   );
134
135   /*! \brief Generates human-readable text representation of content. */
136   QString toString() const;
137
138 private:
139   /** { moduleID, { keySequence, inModuleActionID }[] }[]. keySequence can not be empty.
140    * Can not contain entries like { <non-root module ID>, { keySequence, <meta-action ID> } }. */
141   std::map<QString, std::map<QKeySequence, QString>> myShortcuts;
142
143   /** { moduleID, { inModuleActionID, keySequence }[] }[]. keySequence can be empty.
144    * Can not contain entries like { <non-root module ID>, { <meta-action ID>, keySequence } }. */
145   std::map<QString, std::map<QString, QKeySequence>> myShortcutsInversed;
146 };
147
148
149 /*! \brief GUI-related assets. */
150 struct SUIT_EXPORT SUIT_ActionAssets
151 {
152   struct LangDependentAssets
153   {
154     static const QString PROP_ID_NAME;
155     static const QString PROP_ID_TOOLTIP;
156
157     bool fromJSON(const QJsonObject& theJsonObject);
158     void toJSON(QJsonObject& oJsonObject) const;
159
160     QString myName;
161     QString myToolTip;
162   };
163
164   static const QString STRUCT_ID;
165   static const QString PROP_ID_LANG_DEPENDENT_ASSETS;
166   static const QString PROP_ID_ICON_PATH;
167
168   bool fromJSON(const QJsonObject& theJsonObject);
169   void toJSON(QJsonObject& oJsonObject) const;
170   QString toString() const;
171
172   QStringList getLangs() const;
173   void clearAllLangsExcept(const QString& theLang);
174
175   /*! \param theOverride If true, values of theOther override conflicting values of this. */
176   void merge(const SUIT_ActionAssets& theOther, bool theOverride);
177
178   std::map<QString, LangDependentAssets> myLangDependentAssets;
179   QString myIconPath;
180
181   /*! Is not serialized. */
182   QIcon myIcon;
183 };
184
185
186 /*!
187   \class SUIT_ShortcutMgr
188   \brief Handles action shortcut customization.
189
190   Register actions under action IDs. Set shortcuts, which are [action ID]<->[key sequence] mappings.
191   Every time an action is registered or a shorcut is set, if there are an action and a shortcut,
192   which are mapped to the same action ID, the action is bound to the key sequence of the shortcut.
193   Action IDs are also used to (de)serialize shortcut settings.
194   Several QActions may be registered under the same ID.
195
196   Most of actions are intercepted on creation in SUIT_ShortcutMgr:eventFilter(QObject* theObject, QEvent* theEvent).
197   If an intercepted action is instance of QtxAction, it is registered automatically.
198   Since non-QtxActions have no member ID(), SUIT_ShortcutMgr is unable to register them automatically
199   in SUIT_ShortcutMgr::eventFilter(). Thus, every non-QtxAction should be
200   passed to SUIT_ShortcutMgr::registerAction(const QString& theActionID, QAction* theAction).
201
202   Action ID is application-unique must be composed as <moduleID>/<inModuleActionID>.
203   If an action belongs to a desktop or is available even if no module is active (e.g. 'Save As'),
204   use empty string as <moduleID>. Let's call such actions as root actions.
205
206   There is a need to keep multiple actions, which do the same from user' perspective,
207   bound to the same key sequence. E.g. 'Front view'. Let's call such set of actions as meta-action.
208   Actions of a meta-action may belong to different modules, and/or there may be several actions
209   of the same meta-action in the same module. <inModuleActionID> of all members of a meta-action
210   must be the same and start with "#".
211   Meta-action is root action when it comes to checking for conflicts.
212   Shortcuts of meta-actions are (de)serialized to the same section of preference files as root shortcuts.
213
214   <inModuleActionID> can contain several "/". Go to \ref isInModuleActionIDValid(const QString&) for details.
215   You can refer to multiple actions, whose <inModuleActionID> starts with the prefix.
216
217   Only one module can be active at instance. So a key sequence must be unique within a joined temporary table of
218   root and active module shortcuts. An action is allowed to be bound with only key sequence.
219
220   WARNING!
221   Avoid assigning shortcuts to instances of QAction and all its descendants directly.
222   (1) Key sequence being bound directly to any registered/intercepted action with valid ID,
223       if the key sequence does not conflict with shortcuts kept by SUIT_ShortcutMgr,
224       is added to the manager and appears in user preference file. If it does conflict,
225       it is reassigned with a key sequence from preference files or
226       disabled and added to user preference files (if the files have no shortcut for the action).
227   (2) Key sequences being bound directly to non-QtxAction instances are disabled.
228 */
229 class SUIT_EXPORT SUIT_ShortcutMgr: public QObject
230 {
231   Q_OBJECT
232
233 private:
234   SUIT_ShortcutMgr();
235   SUIT_ShortcutMgr(const SUIT_ShortcutMgr&) = delete;
236   SUIT_ShortcutMgr& operator=(const SUIT_ShortcutMgr&) = delete;
237
238 protected:
239   virtual ~SUIT_ShortcutMgr();
240
241 public:
242   static const QString ROOT_MODULE_ID;
243
244   /*! \brief Create new singleton-instance of shortcut manager, if it has not been created. */
245   static void Init();
246
247   static SUIT_ShortcutMgr* get();
248
249   /*! \brief Checks whether the theKeySequence is platform-compatible. */
250   static bool isKeySequenceValid(const QKeySequence& theKeySequence);
251
252   /*! \returns {false, _ } if  theKeySequenceString is invalid. */
253   static std::pair<bool, QKeySequence> toKeySequenceIfValid(const QString& theKeySequenceString);
254
255   /*! \brief Valid module ID does not contain "/" and equals to result of QString(theModuleID).simplified().
256   Empty module ID is valid - it is root module ID. */
257   static bool isModuleIDValid(const QString& theModuleID);
258
259   /*! \brief Valid in-module action ID may consist of several tokens, separated by "/":
260   <token_0>/<token_1>...<token_N>/<token_N-1>.
261   Each <token> must be non-empty and be equal to QString(<token>).simplified().
262   Empty or "#" in-module action ID is not valid. */
263   static bool isInModuleActionIDValid(const QString& theInModuleActionID);
264
265   /*! \returns true, is theInModuleActionID starts with "#". */
266   static bool isInModuleMetaActionID(const QString& theInModuleActionID);
267
268   /*! \brief Extracts module ID and in-module action ID from application-unique action ID.
269   The theActionID must be composed as <moduleID>/<inModuleActionID>.
270   \returns { _ , "" }, if theActionID is invalid. */
271   static std::pair<QString, QString> splitIntoModuleIDAndInModuleID(const QString& theActionID);
272
273   /*! See \ref splitIntoModuleIDAndInModuleID(const QString&). */
274   static bool isActionIDValid(const QString& theActionID);
275
276   /*! \brief Creates application-unique action ID. Reverse to splitIntoModuleIDAndInModuleID.
277   \returns Emppty string, if either theModuleID or theInModuleActionID is invalid*/
278   static QString makeActionID(const QString& theModuleID, const QString& theInModuleActionID);
279
280   /*! \brief Sets all shortcuts from preferences to theContainer. Incoming shortcuts override existing ones.
281   If the container has shortcut for an action, which is absent in preferences, and the existing shortcut
282   does not conflict with incoming ones, it is untouched.
283   See \ref setShortcutsFromPreferences() for details.
284   \param theDefaultOnly If true, user preferences are ignored and only default preferences are used. */
285   static void fillContainerFromPreferences(SUIT_ShortcutContainer& theContainer, bool theDefaultOnly);
286
287   /*! \brief Checks the resource manager directly.
288   \returns {assetsExist, assets}. */
289   static std::pair<bool, SUIT_ActionAssets> getActionAssetsFromResources(const QString& theActionID);
290
291   /*! \returns Language being set in resource manager. */
292   static QString getLang();
293
294
295   /*! \brief Add theAction to map of managed actions. */
296   void registerAction(const QString& theActionID, QAction* theAction);
297
298   /*! \brief Add theAction to map of managed actions. QtxAction::ID() is used as action ID. */
299   void registerAction(QtxAction* theAction);
300
301   /*! \brief Get registered actions. If theInModuleActionID is meta-action ID, seeks in all modules. */
302   std::set<QAction*> getActions(const QString& theModuleID, const QString& theInModuleActionID) const;
303
304   /*! \brief Get module ID and in-module-ID of theAction.
305   \returns { _ , "" } if theAction is not registered. */
306   std::pair<QString, QString> getModuleIDAndInModuleID(const QAction* theAction) const;
307
308   /*! Returns true if theAction is registered. */
309   bool hasAction(const QAction* theAction) const;
310
311   /*! \brief Get action ID of theActon.
312   \returns empty string if theAction is not registered. */
313   QString getActionID(const QAction* theAction) const;
314
315   /*! \brief Enables/disable actions of the module.
316   Only those actions are affected, whose parent widget is active desktop. */
317   void setActionsOfModuleEnabled(const QString& theModuleID, const bool theEnable = true) const;
318
319   /*! \brief Enables/disables all registered actions whose in-module action ID begins with theInModuleActionIDPrefix.
320   Only those actions are affected, whose parent widget is active desktop. */
321   void setActionsWithPrefixInIDEnabled(const QString& theInModuleActionIDPrefix, bool theEnable = true) const;
322
323   [[deprecated("Use setActionsWithPrefixInIDEnabled(const QString&, bool) instead.")]]
324   void setSectionEnabled(const QString& theInModuleActionIDPrefix, bool theEnable = true) const;
325
326   /*! \brief For all registered actions binds key sequences from myShortcutContainer. */
327   void rebindActionsToKeySequences() const;
328
329   [[deprecated("Use rebindActionsToKeySequences() instead.")]]
330   void updateShortcuts() const;
331
332   /*! \brief Checks for conflicts. If theOverride, modifies incoming and disables all conflicting shortcuts.
333   Then binds key sequences with corresponding registered actions. Saves changes to preferences.
334
335   Redefining a key sequence for the action, if the key sequence does not conflict with other shortcuts, is not considered as a conflict.
336   \param theInModuleActionID The method has no effect if theInModuleActionID is empty.
337   \param theKeySequence Empty theKeySequence does not cause conflicts, in this case
338   a shortcut for the action is disabled: theInModuleActionID is added/retained in the container but mapped to empty key sequence.
339   \param theOverride If true, conflicting shortcuts are disabled.
340   \returns {moduleID, inModuleActionID}[] - Set of conflicting actions if theOverride = false,
341   otherwise set of actions (without incoming one), whose shortcuts have been disabled. */
342   std::set<std::pair<QString, QString>> setShortcut(const QString& theActionID, const QKeySequence& theKeySequence, bool theOverride = false);
343   std::set<std::pair<QString, QString>> setShortcut(const QString& theModuleID, const QString& theInModuleActionID, const QKeySequence& theKeySequence, bool theOverride = false);
344
345   const SUIT_ShortcutContainer& getShortcutContainer() const;
346
347   /*! \brief Does not perform validity checks on theModuleID and theInModuleActionID. */
348   void mergeShortcutContainer(const SUIT_ShortcutContainer& theContainer, bool theOverride = true, bool theTreatAbsentIncomingAsDisabled = false);
349
350   /*! \brief Get a key sequence mapped to the action. */
351   QKeySequence getKeySequence(const QString& theModuleID, const QString& theInModuleActionID) const;
352
353   /*! \returns {inModuleActionID, keySequence}[] */
354   const std::map<QString, QKeySequence>& getModuleShortcutsInversed(const QString& theModuleID) const;
355
356   /*! \returns All module IDs, which were added to myShortcutContainer. */
357   std::set<QString> getShortcutModuleIDs() const;
358
359   /*! \returns IDs of modules, which interfere with the module:
360   if the module is root (theModuleID is empty) - returns all module IDs, otherwise returns ["", theModuleID]. */
361   std::set<QString> getIDsOfInterferingModules(const QString& theModuleID) const;
362
363   std::shared_ptr<const SUIT_ActionAssets> getModuleAssets(const QString& theModuleID) const;
364
365   /*! \brief Retrieves module name, if the asset was loaded using \ref setAssetsFromResources(). If theLang is empty, it is effectively current language. */
366   QString getModuleName(const QString& theModuleID, const QString& theLang = "") const;
367
368   std::shared_ptr<const SUIT_ActionAssets> getActionAssets(const QString& theModuleID, const QString& theInModuleActionID) const;
369
370   std::shared_ptr<const SUIT_ActionAssets> getActionAssets(const QString& theActionID) const;
371
372   std::map<QString, std::map<QString, std::shared_ptr<SUIT_ActionAssets>>> getActionAssets() const { return myActionAssets; }
373
374   std::map<QString, std::shared_ptr<SUIT_ActionAssets>> getModuleAssets() const { return myModuleAssets; }
375
376   /*! \brief Retrieves action name, if the asset was loaded using \ref setAssetsFromResources(). If theLang is empty, it is effectively current language. */
377   QString getActionName(const QString& theModuleID, const QString& theInModuleActionID, const QString& theLang = "") const;
378
379 private slots:
380   /*!
381   \brief Called when the corresponding action is destroyed.
382   Removes destroyed action from maps of registered actions. Preserves shortcut.
383   \param theObject action being destroyed.
384   */
385   void onActionDestroyed(QObject* theObject);
386
387 private:
388   /*! \brief Overrides QObject::eventFilter().
389   If theEvent is QEvent::ActionAdded and the action is instance of QtxAction, registers it. */
390   virtual bool eventFilter(QObject* theObject, QEvent* theEvent);
391
392   /*! \brief Does not perform validity checks on theModuleID and theInModuleActionID. */
393   std::set<std::pair<QString, QString>> setShortcutNoIDChecks(const QString& theModuleID, const QString& theInModuleActionID, const QKeySequence& theKeySequence, bool theOverride);
394
395   /*! \brief Set shortcuts from preference files. The method is intended to be called before any calls to setShortcut() or mergeShortcutContainer().
396   Definition of this method assumes, that shortcut settings are serialized as prerefence entries {name=<inModuleActionID>, val=<keySequence>}
397   in dedicated section for each module, with names of sections being composed as "shortcuts:<moduleID>".
398
399   E.g. in case of XML file it may look like this:
400   <!--
401   <section name="<module ID>">
402     ...
403     <parameter name="<in-module action ID>" value="key sequence">
404     ...
405   </section>
406   -->
407   <section name="shortcuts:">
408     <parameter name="TOT_DESK_FILE_NEW" value="Ctrl+N"/>
409     <parameter name="TOT_DESK_FILE_OPEN" value="Ctrl+O"/>
410     <parameter name="#General/Show object(s)" value="Ctrl+Alt+S"/>
411     <parameter name="#General/Hide object(s)" value="Ctrl+Alt+H"/>
412     <parameter name="#Viewers/Back view" value="Ctrl+Alt+B"/>
413     <parameter name="#Viewers/Front view" value="Ctrl+Alt+F"/>
414   </section>
415    <section name="shortcuts:GEOM">
416     <parameter name="Isolines/Increase number" value="Meta+I"/>
417     <parameter name="Isolines/Decrease number" value="Meta+D"/>
418     <parameter name="Transparency/Increase" value="Meta+Y"/>
419     <parameter name="Transparency/Decrease" value="Meta+T"/>
420   </section>
421
422   Empty inModuleActionIDs are ignored.
423
424   nb! For any theQtxAction you wish user be able to assign it to a shortcut,
425   add theQtxAction.ID() to default resource files (you can map it to no key sequence).*/
426   void setShortcutsFromPreferences();
427
428   /*! \brief Writes shortcuts to preference files.
429   \param theShortcuts { moduleID, { inModuleActionID, keySequence }[] }[]. Empty inModuleActionIDs are ignored. */
430   static void saveShortcutsToPreferences(const std::map<QString, std::map<QString, QKeySequence>>& theShortcutsInversed);
431
432   /*! Fills myActionAssets from asset files in theLanguage.
433   \param theLanguage If default, fills assets in current language.
434   If an asset in requested language is not found, seeks for the asset EN in and then in FR.
435
436   Asset files must be structured like this:
437   {
438     ...
439     actionID : {
440       "langDependentAssets": {
441         ...
442         lang: {
443           "name": name,
444           "tooltip": tooltip
445         },
446         ...
447       },
448       "iconPath": iconPath
449     },
450     ...
451   }
452   */
453   void setAssetsFromResources(QString theLanguage = QString());
454
455 private:
456   static SUIT_ShortcutMgr* myShortcutMgr;
457
458   /** { moduleID, { inModuleActionID, action[] }[] }[]. May contain entries like { <non-root module ID>, { <meta-action ID>, actions[] } }. */
459   std::map<QString, std::map<QString, std::set<QAction*>>> myActions;
460
461   /** { action, { moduleID, inModuleActionID } }[]. May contain entries like { <non-root module ID>, { <meta-action ID>, actions[] } }. */
462   std::map<QAction*, std::pair<QString, QString>> myActionIDs; // To maintain uniqueness of actions and effectively retrieve IDs of registered actions.
463
464   /** Can not contain entries like { <non-root module ID>, { <meta-action ID>, actions[] } }. */
465   SUIT_ShortcutContainer myShortcutContainer;
466
467   /* nb!
468   Sets of moduleIDs and inModuleActionIDs are equal for myActions and myActionIDs.
469   Sets of moduleIDs and inModuleActionIDs may NOT be equal for myActions and myShortcutContainer.
470   */
471
472   /* { moduleID, {inModuleActionID, assets}[] }[] */
473   std::map<QString, std::map<QString, std::shared_ptr<SUIT_ActionAssets>>> myActionAssets;
474
475   /* {moduleID, assets}[] */
476   mutable std::map<QString, std::shared_ptr<SUIT_ActionAssets>> myModuleAssets;
477 };
478
479
480 /*!
481   \class SUIT_SentenceMatcher
482   \brief Approximate string matcher, treats strings as sentences composed of words.
483 */
484 class SUIT_EXPORT SUIT_SentenceMatcher
485 {
486 public:
487   /*! Default config:
488     Exact word order = false;
489     Fuzzy words = true;
490     Case sensitive = false;
491     Query = ""; // matches nothing.
492   */
493   SUIT_SentenceMatcher();
494
495   void setUseExactWordOrder(bool theOn);
496   void setUseFuzzyWords(bool theOn);
497   void setCaseSensitive(bool theOn);
498   inline bool isCaseSensitive() const { return myIsCaseSensitive; };
499
500   /*! \param theQuery should not be regex. */
501   void setQuery(QString theQuery);
502
503   inline const QString& getQuery() const { return myQuery; };
504
505   /*! \returns number of matched words. */
506   size_t match(const QString& theInputString) const;
507
508   QString toString() const;
509
510 private:
511   static bool makePermutatedSentences(const QStringList& theWords, QList<QStringList>& theSentences);
512   static void makeFuzzyWords(const QStringList& theWords, QStringList& theFuzzyWords);
513
514   static size_t matchWithSentenceIgnoreEndings(const QString& theInputString, const QStringList& theSentence, bool theCaseSensitive);
515   static size_t matchWithSentencesIgnoreEndings(const QString& theInputString, const QList<QStringList>& theSentences, bool theCaseSensitive);
516
517   static size_t matchAtLeastOneWord(const QString& theInputString, const QStringList& theWords, bool theCaseSensitive);
518
519   static size_t match(
520     const QString& theInputString,
521     const QStringList& theSentence,
522     bool theCaseSensitive
523   );
524
525   static size_t match(
526     const QString& theInputString,
527     const QList<QStringList>& theSentences,
528     bool theCaseSensitive
529   );
530
531   bool myUseExactWordOrder; // If false, try to match with sentences, composed of query's words in different orders.
532   bool myUseFuzzyWords; // Try to match with sentences, composed of query's truncated words.
533   bool myIsCaseSensitive;
534   QString myQuery;
535
536   QStringList myWords; // It is also original search sentence.
537   QList<QStringList> myPermutatedSentences;
538
539   QStringList myFuzzyWords; // Regexes.
540   QList<QStringList> myFuzzyPermutatedSentences;
541 };
542
543
544 typedef std::map<QString, std::map<QString, std::shared_ptr<SUIT_ActionAssets>>> SUIT_ActionAssetsMap2;
545
546
547 /*!
548   \class SUIT_ActionSearcher
549   \brief Searches in data, provided in action asset files and shortcut preferences.
550 */
551 class SUIT_EXPORT SUIT_ActionSearcher
552 {
553 public:
554   enum MatchField {
555     ID,
556     Name,
557     ToolTip,
558     KeySequence
559   };
560
561   struct AssetsAndSearchData
562   {
563     AssetsAndSearchData() = default;
564     AssetsAndSearchData(std::shared_ptr<SUIT_ActionAssets> theAssets, size_t theNumOfMatchingWords);
565
566     std::shared_ptr<SUIT_ActionAssets> myAssets;
567     size_t myNumOfMatchingWords;
568
569     void toJSON(QJsonObject& oJsonObject) const;
570     QString toString() const;
571   };
572
573   /*! Default config:
574       Included modules' IDs = { ROOT_MODULE_ID };
575       Include disabled actions = false;
576       Fields to match = { Name, Tooltip };
577       Case sensitive = false;
578       Fuzzy matching = true;
579       Query = ""; // matches everything.
580   */
581   SUIT_ActionSearcher();
582   SUIT_ActionSearcher(const SUIT_ActionSearcher&) = delete;
583   SUIT_ActionSearcher& operator=(const SUIT_ActionSearcher&) = delete;
584   virtual ~SUIT_ActionSearcher() = default;
585
586   /*! \returns true, if set of results is changed. */
587   bool setIncludedModuleIDs(std::set<QString> theIncludedModuleIDs);
588
589   /*! \returns true, if set of results is changed. */
590   bool includeDisabledActions(bool theOn);
591   inline bool areDisabledActionsIncluded() const {return myIncludeDisabledActions;};
592
593   /*! \returns true, if set of results is changed. */
594   bool setFieldsToMatch(const std::set<SUIT_ActionSearcher::MatchField>& theFields);
595
596   /*! \returns true, if set of results is changed. */
597   bool setCaseSensitive(bool theOn);
598
599   /*! \returns true, if set of results is changed. */
600   bool setQuery(const QString& theQuery);
601   inline const QString& getQuery() const {return myMatcher.getQuery();};
602
603   const std::map<QString, std::map<QString, SUIT_ActionSearcher::AssetsAndSearchData>>& getSearchResults() const;
604
605
606 private:
607   /*! \brief Applies filter to all actions, provided in asset files for SUIT_ShortcutMgr.
608   \returns { true, _ } if set of results is changed; { _ , true } if number of matching words is changed for at least one result. */
609   std::pair<bool, bool> filter();
610
611   /*! \brief Applies filter to search results only.
612   \returns { true, _ } if set of results is shrunk; { _ , true } if number of matching words is changed for at least one result. */
613   std::pair<bool, bool> filterResults();
614
615   /*! \brief Applies filter only to actions, which are not in search results.
616   \returns True, if set of results is extended. */
617   bool extendResults();
618
619   size_t matchAction(const QString& theModuleID, const QString& theInModuleActionID, std::shared_ptr<SUIT_ActionAssets> theAssets);
620
621   QString toString() const;
622
623
624   std::set<QString> myIncludedModuleIDs;
625   bool myIncludeDisabledActions;
626
627   std::set<SUIT_ActionSearcher::MatchField> myFieldsToMatch;
628   SUIT_SentenceMatcher myMatcher;
629
630   /* { moduleID, {inModuleActionID, assetsAndSearchData}[] }[]. */
631   std::map<QString, std::map<QString, SUIT_ActionSearcher::AssetsAndSearchData>> mySearchResults;
632 };
633
634
635 #if defined WIN32
636 #pragma warning( default: 4251 )
637 #endif
638
639 #endif