1 // Copyright (C) 2007-2024 CEA, EDF, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
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.
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.
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
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 #ifndef SUIT_SHORTCUTMGR_H
24 #define SUIT_SHORTCUTMGR_H
30 #include <QKeySequence>
31 #include <QStringList>
44 #pragma warning( disable: 4251 )
47 // Define SHORTCUT_MGR_DBG to enable SUIT_ShortcutMgr debug logging.
48 // #define SHORTCUT_MGR_DBG
49 /*! \returns true, if SUIT_ShortcutMgr debug logging is enabled. */
50 SUIT_EXPORT extern inline bool ShCutDbg() {
51 #ifdef SHORTCUT_MGR_DBG
57 /*! \brief Prints theString to std::wcout, if SUIT_ShortcutMgr debug logging is enabled. */
58 SUIT_EXPORT extern bool ShCutDbg(const QString& theString);
59 /*! \brief Prints theString to std::wcout, if SUIT_ShortcutMgr debug logging is enabled. */
60 SUIT_EXPORT extern bool ShCutDbg(const char* theString);
64 \class SUIT_ShortcutContainer
65 \brief Provides means to keep and edit shortcuts in compliance with the application logics.
66 \ref See SUIT_ShortcutMgr for details.
68 class SUIT_EXPORT SUIT_ShortcutContainer
71 SUIT_ShortcutContainer();
73 /*! \returns IDs of modules, which interfere with the module:
74 if the module is root (theModuleID is empty) - returns all module IDs, otherwise returns ["", theModuleID]. */
75 std::set<QString> getIDsOfInterferingModules(const QString& theModuleID) const;
77 std::set<QString> getIDsOfAllModules() const;
79 /*! \brief Checks for conflicts. If theOverride, modifies incoming and disables all conflicting shortcuts.
80 Redefining a key sequence for the action, if theKeySequence does not conflict with other shortcuts, is not considered as a conflict.
81 \param theModuleID The method has no effect if theModuleID is invalid. \ref See SUIT_ShortcutMgr::isModuleIDValid(const QString&) for details.
82 \param theInModuleActionID The method has no effect if theInModuleActionID is invalid. \ref See SUIT_ShortcutMgr::isInModuleActionIDValid(const QString&).
83 If theInModuleActionID is meta-action ID, the shortcut is set to root module, and theModuleID is ignored.
84 \param theKeySequence Empty theKeySequence does not cause conflicts, in this case
85 a shortcut for the action is disabled: theInModuleActionID is added/retained in the container but mapped to empty key sequence.
86 \param theOverride If true, conflicting shortcuts are disabled.
87 \returns {moduleID, inModuleActionID}[] - Set of conflicting actions if theOverride = false,
88 otherwise set of actions (without incoming one), whose shortcuts have been disabled. */
89 std::set<std::pair<QString, QString>> setShortcut(
91 const QString& theInModuleActionID,
92 const QKeySequence& theKeySequence,
96 /*! \brief Checks for conflicts. Existence of a shortcut with another key sequence for the action,
97 if theKeySequence does not conflict with other shortcuts, is not considered as a conflict.
98 \param theInModuleActionID If theInModuleActionID is meta-action ID, the shortcut is looked for in root module, and theModuleID is ignored.
99 \param theKeySequence Empty theKeySequence does not have conflicts.
100 \returns {moduleID, inModuleActionID}[] - Set of conflicting actions. */
101 std::set<std::pair<QString, QString>> getConflicts(
103 const QString& theInModuleActionID,
104 const QKeySequence& theKeySequence
107 /*! \returns empty key sequence if shortcut for the action is not set.
108 \param theInModuleActionID If theInModuleActionID is meta-action ID, seeks in root module, and theModuleID is ignored.*/
109 const QKeySequence& getKeySequence(QString theModuleID, const QString& theInModuleActionID) const;
111 /*! \returns true, if shortcut for the action is set (even if the mapped key sequence is empty).
112 \param theInModuleActionID If theInModuleActionID is meta-action ID, seeks in root module, and theModuleID is ignored.*/
113 bool hasShortcut(QString theModuleID, const QString& theInModuleActionID) const;
115 /*! \returns {inModuleActionID, keySequence}[] - If the module was not added, the map is empty. */
116 const std::map<QString, QKeySequence>& getModuleShortcutsInversed(const QString& theModuleID) const;
118 /*! \brief Seeks for shortcuts in the module with in-module action IDs, which start with theInModuleActionIDPrefix.
119 \returns {inModuleActionID, keySequence}[] - If the module was not added, the map is empty. */
120 const std::map<QString, QKeySequence> getModuleShortcutsInversed(const QString& theModuleID, const QString& theInModuleActionIDPrefix) const;
122 /*! \brief Merges shortcuts of theOther into this.
123 \param theOverride if true, overrides conflicting shortcuts.
124 If false, and this has no shortcut for an incoming action, and the incoming shortcut conflicts
125 with an existing shortcut, disabled shortcut for the incoming action is set.
126 \param theTreatAbsentIncomingAsDisabled If theOverride == false, theTreatAbsentIncomingAsDisabled is ignored.
127 If theOverride and theTreatAbsentIncomingAsDisabled, and theOther has no shortcut for an action, which exists in this,
128 the existing shortcut in this is set disabled.
129 \returns { moduleID, { inModuleActionID, {oldKeySequence, newKeySequence} }[] }[] - Modiified shortcuts inversed. */
130 std::map<QString, std::map<QString, std::pair<QKeySequence, QKeySequence>>> merge(
131 const SUIT_ShortcutContainer& theOther,
133 bool theTreatAbsentIncomingAsDisabled = false
136 bool hasKeySequence(const QString& theModuleID, const QKeySequence& theKeySequence) const;
138 /*! \brief Generates human-readable text representation of content. */
139 QString toString() const;
142 /** { moduleID, { keySequence, inModuleActionID }[] }[]. keySequence can not be empty.
143 * Can not contain entries like { <non-root module ID>, { keySequence, <meta-action ID> } }. */
144 std::map<QString, std::map<QKeySequence, QString>> myShortcuts;
146 /** { moduleID, { inModuleActionID, keySequence }[] }[]. keySequence can be empty.
147 * Can not contain entries like { <non-root module ID>, { <meta-action ID>, keySequence } }. */
148 std::map<QString, std::map<QString, QKeySequence>> myShortcutsInversed;
152 /*! \brief GUI-related assets. */
153 struct SUIT_EXPORT SUIT_ActionAssets
155 struct LangDependentAssets
157 static const QString PROP_ID_NAME;
158 static const QString PROP_ID_TOOLTIP;
160 bool fromJSON(const QJsonObject& theJsonObject);
161 void toJSON(QJsonObject& oJsonObject) const;
167 static const QString STRUCT_ID;
168 static const QString PROP_ID_LANG_DEPENDENT_ASSETS;
169 static const QString PROP_ID_ICON_PATH;
171 bool fromJSON(const QJsonObject& theJsonObject);
172 void toJSON(QJsonObject& oJsonObject) const;
173 QString toString() const;
175 QStringList getLangs() const;
176 void clearAllLangsExcept(const QString& theLang);
178 /*! \param theOverride If true, values of theOther override conflicting values of this. */
179 void merge(const SUIT_ActionAssets& theOther, bool theOverride);
181 std::map<QString, LangDependentAssets> myLangDependentAssets;
184 /*! Is not serialized. */
190 \class SUIT_ShortcutMgr
191 \brief Handles action shortcut customization.
193 IDENTIFIED ACTIONS/SHORTCUTS
195 Register actions under action IDs. Set shortcuts, which are [action ID]<->[key sequence] mappings.
196 Every time an action is registered or a shorcut is set, if there are an action and a shortcut,
197 which are mapped to the same action ID, the action is bound to the key sequence of the shortcut.
198 Action IDs are also used to (de)serialize shortcut settings.
199 Several QActions may be registered under the same ID.
201 Most of actions are intercepted on creation in SUIT_ShortcutMgr:eventFilter(QObject* theObject, QEvent* theEvent).
202 If an intercepted action is instance of QtxAction, it is registered automatically.
203 Since non-QtxActions have no member ID(), SUIT_ShortcutMgr is unable to register them automatically
204 in SUIT_ShortcutMgr::eventFilter(). Thus, every non-QtxAction should be
205 passed to SUIT_ShortcutMgr::registerAction(const QString& theActionID, QAction* theAction).
207 Action ID is application-unique must be composed as <moduleID>/<inModuleActionID>.
208 If an action belongs to a desktop or is available even if no module is active (e.g. 'Save As'),
209 use empty string as <moduleID>. Let's call such actions as root actions.
211 There is a need to keep multiple actions, which do the same from user' perspective,
212 bound to the same key sequence. E.g. 'Front view'. Let's call such set of actions as meta-action.
213 Actions of a meta-action may belong to different modules, and/or there may be several actions
214 of the same meta-action in the same module. <inModuleActionID> of all members of a meta-action
215 must be the same and start with "#".
216 Meta-action is root action when it comes to checking for conflicts.
217 Shortcuts of meta-actions are (de)serialized to the same section of preference files as root shortcuts.
219 <inModuleActionID> can contain several "/". Go to \ref isInModuleActionIDValid(const QString&) for details.
220 You can refer to multiple actions, whose <inModuleActionID> starts with the prefix.
222 Only one module can be active at instance. So a key sequence must be unique within a joined temporary table of
223 root and active module shortcuts. An action is allowed to be bound with only key sequence.
225 ANONYMOUS ACTIONS/SHORTCUTS
227 Actions without action IDs or with invalid ones are called anonymous actions.
228 All anonymous actions with non-empty shortcut key sequences are registered by SUIT_ShortcutMgr.
229 If a shortcut for an anonymous action clashes with a shortcut for an action with defined ID (identified action/shortcut),
230 the shortcut for the anonymous action is disabled, but [the anonymous action, the hard-coded key sequence] pair
231 remains within the SUIT_ShortcutMgr. If user redefines key sequences for identified actions,
232 and the clash is gone, SUIT_ShortcutMgr enables back the shortcut for the anonymous action.
235 Avoid assigning shortcuts to instances of QAction and all its descendants directly.
236 (1) Key sequence being bound directly to any registered/intercepted action with valid ID,
237 if the key sequence does not conflict with shortcuts kept by SUIT_ShortcutMgr,
238 is added to the manager and appears in user preference file. If it does conflict,
239 it is reassigned with a key sequence from preference files or
240 disabled and added to user preference files (if the files have no shortcut for the action).
241 (2) It is not possible to reassign key sequences for anonymous actions using the Shortcut Editor GUI.
242 It is not possible to always warn user, if a key sequence, he assigns to an identified action,
243 disables an anonymous shortcut, because SUIT_ShortcutMgr has no data about anonymous actions until they appear in runtime.
244 To prevent the user from relying on such warnings, they are completely disabled.
246 class SUIT_EXPORT SUIT_ShortcutMgr: public QObject
252 SUIT_ShortcutMgr(const SUIT_ShortcutMgr&) = delete;
253 SUIT_ShortcutMgr& operator=(const SUIT_ShortcutMgr&) = delete;
256 virtual ~SUIT_ShortcutMgr();
259 static const QString ROOT_MODULE_ID;
261 /*! \brief Create new singleton-instance of shortcut manager, if it has not been created. */
264 static SUIT_ShortcutMgr* get();
266 /*! \brief Checks whether the theKeySequence is platform-compatible. */
267 static bool isKeySequenceValid(const QKeySequence& theKeySequence);
269 /*! \returns {false, _ } if theKeySequenceString is invalid. */
270 static std::pair<bool, QKeySequence> toKeySequenceIfValid(const QString& theKeySequenceString);
272 /*! \brief Valid module ID does not contain "/" and equals to result of QString(theModuleID).simplified().
273 Empty module ID is valid - it is root module ID. */
274 static bool isModuleIDValid(const QString& theModuleID);
276 /*! \brief Valid in-module action ID may consist of several tokens, separated by "/":
277 <token_0>/<token_1>...<token_N>/<token_N-1>.
278 Each <token> must be non-empty and be equal to QString(<token>).simplified().
279 Empty or "#" in-module action ID is not valid. */
280 static bool isInModuleActionIDValid(const QString& theInModuleActionID);
282 /*! \returns true, is theInModuleActionID starts with "#". */
283 static bool isInModuleMetaActionID(const QString& theInModuleActionID);
285 /*! \brief Extracts module ID and in-module action ID from application-unique action ID.
286 The theActionID must be composed as <moduleID>/<inModuleActionID>.
287 \returns { _ , "" }, if theActionID is invalid. */
288 static std::pair<QString, QString> splitIntoModuleIDAndInModuleID(const QString& theActionID);
290 /*! See \ref splitIntoModuleIDAndInModuleID(const QString&). */
291 static bool isActionIDValid(const QString& theActionID);
293 /*! \brief Creates application-unique action ID. Reverse to splitIntoModuleIDAndInModuleID.
294 \returns Emppty string, if either theModuleID or theInModuleActionID is invalid*/
295 static QString makeActionID(const QString& theModuleID, const QString& theInModuleActionID);
297 /*! \brief Sets all shortcuts from preferences to theContainer. Incoming shortcuts override existing ones.
298 If the container has shortcut for an action, which is absent in preferences, and the existing shortcut
299 does not conflict with incoming ones, it is untouched.
300 See \ref setShortcutsFromPreferences() for details.
301 \param theDefaultOnly If true, user preferences are ignored and only default preferences are used. */
302 static void fillContainerFromPreferences(SUIT_ShortcutContainer& theContainer, bool theDefaultOnly);
304 /*! \brief Checks the resource manager directly.
305 \returns {assetsExist, assets}. */
306 static std::pair<bool, SUIT_ActionAssets> getActionAssetsFromResources(const QString& theActionID);
308 /*! \returns Language, which is set in resource manager. */
309 static QString getLang();
312 /*! \brief Add theAction to map of managed actions. */
313 void registerAction(const QString& theActionID, QAction* theAction);
315 /*! \brief Add theAction to map of managed actions. QtxAction::ID() is used as action ID. */
316 void registerAction(QtxAction* theAction);
318 /*! \brief Get registered actions. If theInModuleActionID is meta-action ID, seeks in all modules. */
319 std::set<QAction*> getActions(const QString& theModuleID, const QString& theInModuleActionID) const;
321 /*! \brief Get module ID and in-module-ID of theAction.
322 \returns { _ , "" } if theAction is not registered. */
323 std::pair<QString, QString> getModuleIDAndInModuleID(const QAction* theAction) const;
325 /*! Returns true if theAction is registered. */
326 bool hasAction(const QAction* theAction) const;
328 /*! \brief Get action ID of theActon.
329 \returns empty string if theAction is not registered. */
330 QString getActionID(const QAction* theAction) const;
332 /*! \brief Enables/disable actions of the module.
333 Only those actions are affected, whose parent widget is active desktop. */
334 void setActionsOfModuleEnabled(const QString& theModuleID, const bool theEnable = true) const;
336 [[deprecated("Use setActionsOfModuleEnabled(const QString&, bool) instead.")]]
337 void setSectionEnabled(const QString& theInModuleActionIDPrefix, bool theEnable = true) const;
339 /*! \brief Enables/disables all registered actions whose in-module action ID begins with theInModuleActionIDPrefix.
340 Only those actions are affected, whose parent widget is active desktop. */
341 void setActionsWithPrefixInIDEnabled(const QString& theInModuleActionIDPrefix, bool theEnable = true) const;
343 /*! \brief For all registered actions binds key sequences from myShortcutContainer. */
344 void rebindActionsToKeySequences() const;
346 [[deprecated("Use rebindActionsToKeySequences() instead.")]]
347 void updateShortcuts() const;
349 /*! \brief Checks for conflicts. If theOverride, modifies incoming and disables all conflicting shortcuts.
350 Then binds key sequences with corresponding registered actions. Saves changes to preferences.
352 Redefining a key sequence for the action, if the key sequence does not conflict with other shortcuts, is not considered as a conflict.
353 \param theInModuleActionID The method has no effect if theInModuleActionID is empty.
354 \param theKeySequence Empty theKeySequence does not cause conflicts, in this case
355 a shortcut for the action is disabled: theInModuleActionID is added/retained in the container but mapped to empty key sequence.
356 \param theOverride If true, conflicting shortcuts are disabled.
357 \returns {moduleID, inModuleActionID}[] - Set of conflicting actions if theOverride = false,
358 otherwise set of actions (without incoming one), whose shortcuts have been disabled. */
359 std::set<std::pair<QString, QString>> setShortcut(const QString& theActionID, const QKeySequence& theKeySequence, bool theOverride = false);
360 std::set<std::pair<QString, QString>> setShortcut(const QString& theModuleID, const QString& theInModuleActionID, const QKeySequence& theKeySequence, bool theOverride = false);
362 const SUIT_ShortcutContainer& getShortcutContainer() const;
364 /*! \brief Does not perform validity checks on theModuleID and theInModuleActionID. */
365 void mergeShortcutContainer(const SUIT_ShortcutContainer& theContainer, bool theOverride = true, bool theTreatAbsentIncomingAsDisabled = false);
367 /*! \brief Get a key sequence mapped to the action. */
368 const QKeySequence& getKeySequence(const QString& theModuleID, const QString& theInModuleActionID) const;
370 /*! \returns {inModuleActionID, keySequence}[] */
371 const std::map<QString, QKeySequence>& getModuleShortcutsInversed(const QString& theModuleID) const;
373 /*! \returns All module IDs, which were added to myShortcutContainer. */
374 std::set<QString> getShortcutModuleIDs() const;
376 /*! \returns IDs of modules, which interfere with the module:
377 if the module is root (theModuleID is empty) - returns all module IDs, otherwise returns ["", theModuleID]. */
378 std::set<QString> getIDsOfInterferingModules(const QString& theModuleID) const;
380 /*! \returns assets, which describe module's header, not its content. */
381 std::shared_ptr<const SUIT_ActionAssets> getModuleAssets(const QString& theModuleID) const;
383 /*! \returns assets, which describe modules' headers, not their content. */
384 std::map<QString, std::shared_ptr<SUIT_ActionAssets>> getModuleAssets() const { return myModuleAssets; }
386 /*! \brief Retrieves module name, if the asset was loaded using \ref setAssetsFromResources(). If theLang is empty, it is effectively current language. */
387 QString getModuleName(const QString& theModuleID, const QString& theLang = "") const;
389 std::shared_ptr<const SUIT_ActionAssets> getActionAssets(const QString& theModuleID, const QString& theInModuleActionID) const;
391 std::shared_ptr<const SUIT_ActionAssets> getActionAssets(const QString& theActionID) const;
393 std::map<QString, std::map<QString, std::shared_ptr<SUIT_ActionAssets>>> getActionAssets() const { return myActionAssets; }
395 /*! \brief Retrieves action name, if the asset was loaded using \ref setAssetsFromResources(). If theLang is empty, it is effectively current language. */
396 QString getActionName(const QString& theModuleID, const QString& theInModuleActionID, const QString& theLang = "") const;
400 \brief Called when the corresponding action is destroyed.
401 Removes destroyed action from maps of registered actions. Preserves shortcut.
402 \param theObject action being destroyed.
404 void onActionDestroyed(QObject* theObject);
406 void onAnonymousActionDestroyed(QObject* theObject);
409 /*! \brief Overrides QObject::eventFilter().
410 If theEvent is QEvent::ActionAdded and the action is instance of QtxAction, registers it. */
411 virtual bool eventFilter(QObject* theObject, QEvent* theEvent);
413 /*! \brief Does not perform validity checks on theModuleID and theInModuleActionID. */
414 std::set<std::pair<QString, QString>> setShortcutNoIDChecks(const QString& theModuleID, const QString& theInModuleActionID, const QKeySequence& theKeySequence, bool theOverride);
416 /*! \brief Set shortcuts from preference files. The method is intended to be called before any calls to setShortcut() or mergeShortcutContainer().
417 Definition of this method assumes, that shortcut settings are serialized as prerefence entries {name=<inModuleActionID>, val=<keySequence>}
418 in dedicated section for each module, with names of sections being composed as "shortcuts:<moduleID>".
420 E.g. in case of XML file it may look like this:
422 <section name="<module ID>">
424 <parameter name="<in-module action ID>" value="key sequence">
428 <section name="shortcuts:">
429 <parameter name="TOT_DESK_FILE_NEW" value="Ctrl+N"/>
430 <parameter name="TOT_DESK_FILE_OPEN" value="Ctrl+O"/>
431 <parameter name="#General/Show object(s)" value="Ctrl+Alt+S"/>
432 <parameter name="#General/Hide object(s)" value="Ctrl+Alt+H"/>
433 <parameter name="#Viewers/Back view" value="Ctrl+Alt+B"/>
434 <parameter name="#Viewers/Front view" value="Ctrl+Alt+F"/>
436 <section name="shortcuts:GEOM">
437 <parameter name="Isolines/Increase number" value="Meta+I"/>
438 <parameter name="Isolines/Decrease number" value="Meta+D"/>
439 <parameter name="Transparency/Increase" value="Meta+Y"/>
440 <parameter name="Transparency/Decrease" value="Meta+T"/>
443 Empty inModuleActionIDs are ignored.
445 nb! For any theQtxAction you wish user be able to assign it to a shortcut,
446 add theQtxAction.ID() to default resource files (you can map it to no key sequence).*/
447 void setShortcutsFromPreferences();
449 /*! \brief Writes shortcuts to preference files.
450 \param theShortcuts { moduleID, { inModuleActionID, keySequence }[] }[]. Empty inModuleActionIDs are ignored. */
451 static void saveShortcutsToPreferences(const std::map<QString, std::map<QString, QKeySequence>>& theShortcutsInversed);
453 /*! \brief Writes shortcuts to preference files.
454 \param theShortcuts { moduleID, { inModuleActionID, {oldKeySequence, newKeySequence} }[] }[]. Empty inModuleActionIDs are ignored.
455 OldKeySequences are ignored. */
456 static void saveShortcutsToPreferences(const std::map<QString, std::map<QString, std::pair<QKeySequence, QKeySequence>>>& theShortcutsInversed);
458 /*! Fills myActionAssets from asset files in theLanguage.
459 \param theLanguage If default, fills assets in current language.
460 If an asset in requested language is not found, seeks for the asset EN in and then in FR.
462 Asset files must be structured like this:
466 "langDependentAssets": {
479 void setAssetsFromResources(QString theLanguage = QString());
481 void registerAnonymousShortcut(QAction* const theAction);
482 void enableAnonymousShortcutsClashingWith(const QString& theModuleID, const bool theEnable) const;
483 void enableAnonymousShortcutsClashingWith(const QKeySequence& theKeySequence, bool theEnable) const;
484 QString anonymousShortcutsToString() const;
487 static SUIT_ShortcutMgr* myShortcutMgr;
489 /** { moduleID, { inModuleActionID, action[] }[] }[]. May contain entries like { <non-root module ID>, { <meta-action ID>, actions[] } }. */
490 std::map<QString, std::map<QString, std::set<QAction*>>> myActions;
492 /** { action, { moduleID, inModuleActionID } }[]. May contain entries like { <non-root module ID>, { <meta-action ID>, actions[] } }. */
493 std::map<QAction*, std::pair<QString, QString>> myActionIDs; // To maintain uniqueness of actions and effectively retrieve IDs of registered actions.
495 /** Can not contain entries like { <non-root module ID>, { <meta-action ID>, actions[] } }. */
496 SUIT_ShortcutContainer myShortcutContainer;
499 Sets of moduleIDs and inModuleActionIDs are equal for myActions and myActionIDs.
500 Sets of moduleIDs and inModuleActionIDs may NOT be equal for myActions and myShortcutContainer.
503 /** { moduleID, {inModuleActionID, assets}[] }[] */
504 std::map<QString, std::map<QString, std::shared_ptr<SUIT_ActionAssets>>> myActionAssets;
506 /** {moduleID, assets}[] */
507 mutable std::map<QString, std::shared_ptr<SUIT_ActionAssets>> myModuleAssets;
509 mutable std::set<QString> myActiveModuleIDs;
511 /** Actions without IDs, but with hard-coded non-empty key sequences.
512 * Shortcuts, defined in preferences, override shortcuts of anonymous actions - if an active module has a preference shortcut,
513 * anonymous shortcuts with the same key sequence are disabled. If the root module has a preference shortcut, which
514 * is in clash with anonymous shortcuts, clashing anonymous actions are always disabled. */
515 std::map<QAction*, QKeySequence> myAnonymousShortcuts;
517 std::map<QKeySequence, std::set<QAction*>> myAnonymousShortcutsInverse;
522 \class SUIT_SentenceMatcher
523 \brief Approximate string matcher, treats strings as sentences composed of words.
525 class SUIT_EXPORT SUIT_SentenceMatcher
529 Exact word order = false;
531 Case sensitive = false;
532 Query = ""; // matches nothing.
534 SUIT_SentenceMatcher();
536 void setUseExactWordOrder(bool theOn);
537 void setUseFuzzyWords(bool theOn);
538 void setCaseSensitive(bool theOn);
539 inline bool isCaseSensitive() const { return myIsCaseSensitive; };
541 /*! \param theQuery should not be regex. */
542 void setQuery(QString theQuery);
544 inline const QString& getQuery() const { return myQuery; };
546 /*! \returns match metrics. The metrics >= 0. INF means mismatch.
547 The class is unable to differentiate exact match with some approximate matches! */
548 double match(const QString& theInputString) const;
550 /** \brief For debug. */
551 QString toString() const;
554 static bool makePermutatedSentences(const QStringList& theWords, QList<QStringList>& theSentences);
555 static void makeFuzzyWords(const QStringList& theWords, QStringList& theFuzzyWords);
557 /*! \returns number of characters in matched words. The number >= 0. */
558 static int matchWithSentenceIgnoreEndings(const QString& theInputString, const QStringList& theSentence, bool theCaseSensitive);
559 /*! \returns number of characters in matched words. The number >= 0. */
560 static int matchWithSentencesIgnoreEndings(const QString& theInputString, const QList<QStringList>& theSentences, bool theCaseSensitive);
562 /*! \returns number of characters in matched words. The number >= 0. */
563 static int matchAtLeastOneWord(const QString& theInputString, const QStringList& theWords, bool theCaseSensitive);
565 /*! \returns number of characters in matched words. The number >= 0. */
567 const QString& theInputString,
568 const QStringList& theSentence,
569 bool theCaseSensitive
572 /*! \returns number of characters in matched words. The number >= 0. */
574 const QString& theInputString,
575 const QList<QStringList>& theSentences,
576 bool theCaseSensitive
579 bool myUseExactWordOrder; // If false, try to match with sentences, composed of query's words in different orders.
580 bool myUseFuzzyWords; // Try to match with sentences, composed of query's truncated words.
581 bool myIsCaseSensitive;
584 QStringList myWords; // It is also original search sentence.
585 QList<QStringList> myPermutatedSentences;
587 QStringList myFuzzyWords; // Regexes.
588 QList<QStringList> myFuzzyPermutatedSentences;
593 \class SUIT_ActionSearcher
594 \brief Searches in data, provided in action asset files and shortcut preferences.
596 class SUIT_EXPORT SUIT_ActionSearcher
606 class AssetsAndSearchData
609 AssetsAndSearchData(std::shared_ptr<const SUIT_ActionAssets> theAssets = nullptr, double theMatchMetrics = std::numeric_limits<double>::infinity());
611 void setMatchMetrics(double theMatchMetrics);
612 double matchMetrics() const { return myMatchMetrics; };
614 std::shared_ptr<const SUIT_ActionAssets> myAssets;
616 void toJSON(QJsonObject& oJsonObject) const;
617 QString toString() const;
620 /*! \brief Ideally it should be number of weighted character permutations. Now it is just a number of characters in unmatched words. */
621 double myMatchMetrics;
625 Included modules' IDs = { ROOT_MODULE_ID };
626 Include disabled actions = false;
627 Fields to match = { Name, Tooltip };
628 Case sensitive = false;
629 Fuzzy matching = true;
630 Query = ""; // matches everything.
632 SUIT_ActionSearcher();
633 SUIT_ActionSearcher(const SUIT_ActionSearcher&) = delete;
634 SUIT_ActionSearcher& operator=(const SUIT_ActionSearcher&) = delete;
635 virtual ~SUIT_ActionSearcher() = default;
637 /*! \returns true, if set of results is changed. */
638 bool setIncludedModuleIDs(std::set<QString> theIncludedModuleIDs);
640 /*! \returns true, if set of results is changed. */
641 bool includeDisabledActions(bool theOn);
642 inline bool areDisabledActionsIncluded() const {return myIncludeDisabledActions;};
644 /*! \returns true, if set of results is changed. */
645 bool setFieldsToMatch(const std::set<SUIT_ActionSearcher::MatchField>& theFields);
647 /*! \returns true, if set of results is changed. */
648 bool setCaseSensitive(bool theOn);
650 /*! \returns true, if set of results is changed. */
651 bool setQuery(const QString& theQuery);
652 inline const QString& getQuery() const {return myMatcher.getQuery();};
654 const std::map<QString, std::map<QString, SUIT_ActionSearcher::AssetsAndSearchData>>& getSearchResults() const;
658 /*! \brief Applies filter to all actions, provided in asset files for SUIT_ShortcutMgr.
659 \returns { true, _ } if set of results is changed; { _ , true } if matching metrics is changed for at least one result. */
660 std::pair<bool, bool> filter();
662 /*! \brief Applies filter to search results only.
663 \returns { true, _ } if set of results is shrunk; { _ , true } if matching metrics is changed for at least one result. */
664 std::pair<bool, bool> filterResults();
666 /*! \brief Applies filter only to actions, which are not in search results.
667 \returns True, if set of results is extended. */
668 bool extendResults();
670 double matchAction(const QString& theModuleID, const QString& theInModuleActionID, std::shared_ptr<const SUIT_ActionAssets> theAssets);
672 QString toString() const;
675 std::set<QString> myIncludedModuleIDs;
676 bool myIncludeDisabledActions;
678 std::set<SUIT_ActionSearcher::MatchField> myFieldsToMatch;
679 SUIT_SentenceMatcher myMatcher;
681 /* { moduleID, {inModuleActionID, assetsAndSearchData}[] }[]. */
682 std::map<QString, std::map<QString, SUIT_ActionSearcher::AssetsAndSearchData>> mySearchResults;
687 #pragma warning( default: 4251 )