From a7bd0f48622a6f4e24aa8e85fdaab8d0cb51a101 Mon Sep 17 00:00:00 2001 From: dish Date: Wed, 27 Nov 2024 03:17:47 +0000 Subject: [PATCH] Add user shortcut preferences migration. --- src/LightApp/resources/LightApp.xml | 3 + src/SUIT/CMakeLists.txt | 1 + src/SUIT/SUIT_ShortcutMgr.cxx | 317 +++++++++++++++++++- src/SUIT/SUIT_ShortcutMgr.h | 93 ++++++ src/SUIT/resources/action_id_mutations.json | 17 ++ 5 files changed, 427 insertions(+), 4 deletions(-) create mode 100644 src/SUIT/resources/action_id_mutations.json diff --git a/src/LightApp/resources/LightApp.xml b/src/LightApp/resources/LightApp.xml index 5e0d3cbf8..70869070a 100644 --- a/src/LightApp/resources/LightApp.xml +++ b/src/LightApp/resources/LightApp.xml @@ -253,6 +253,9 @@
+
+ +
diff --git a/src/SUIT/CMakeLists.txt b/src/SUIT/CMakeLists.txt index 403e27172..e7ad87196 100644 --- a/src/SUIT/CMakeLists.txt +++ b/src/SUIT/CMakeLists.txt @@ -106,6 +106,7 @@ SET(_other_RESOURCES resources/icon_visibility_off.png resources/view_sync.png resources/action_assets.json + resources/action_id_mutations.json ) # --- sources --- diff --git a/src/SUIT/SUIT_ShortcutMgr.cxx b/src/SUIT/SUIT_ShortcutMgr.cxx index d22c05a64..a0d49eec5 100644 --- a/src/SUIT/SUIT_ShortcutMgr.cxx +++ b/src/SUIT/SUIT_ShortcutMgr.cxx @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -568,6 +569,16 @@ std::set SUIT_ShortcutContainer::getIDsOfAllModules() const return res; } +bool SUIT_ShortcutContainer::isEmpty() const +{ + for (const auto& moduleIDAndShortcuts : myShortcutsInversed) { + const auto& moduleShortcutsInversed = moduleIDAndShortcuts.second; + if (!moduleShortcutsInversed.empty()) + return false; + } + return true; +} + std::set> SUIT_ShortcutContainer::setShortcut(QString theModuleID, const QString& theInModuleActionID, const QKeySequence& theKeySequence, bool theOverride) { if (!SUIT_ShortcutMgr::isModuleIDValid(theModuleID)) { @@ -1525,6 +1536,19 @@ SUIT_ShortcutMgr::~SUIT_ShortcutMgr() myShortcutMgr = new SUIT_ShortcutMgr(); myShortcutMgr->setAssetsFromResources(); myShortcutMgr->setShortcutsFromPreferences(); + + { // Migrate old shortcut preferences. + SUIT_ShortcutHistorian historian; + myShortcutMgr->mergeShortcutContainer( + historian.getContainerWithOldShortcuts(), + true /*theOverride*/, + false /*theTreatAbsentIncomingAsDisabled*/, + true /*theSaveToPreferences*/ + ); + + historian.removeOldShortcutPreferences(); + } + ShCutDbg("SUIT_ShortcutMgr initialization has finished."); } } @@ -1665,7 +1689,12 @@ SUIT_ShortcutMgr::~SUIT_ShortcutMgr() /*static*/ void SUIT_ShortcutMgr::fillContainerFromPreferences(SUIT_ShortcutContainer& theContainer, bool theDefaultOnly) { - ShCutDbg() && ShCutDbg(QString("SUIT_ShortcutMgr::fillContainerFromPreferences(theContainer, theDefaultOnly = ") + (theDefaultOnly ? "true" : "false") + ") started."); + return SUIT_ShortcutMgr::fillContainerFromPreferences(theContainer, theDefaultOnly, SECTION_NAME_PREFIX); +} + +/*static*/ void SUIT_ShortcutMgr::fillContainerFromPreferences(SUIT_ShortcutContainer& theContainer, bool theDefaultOnly, const QString& theSectionNamePrefix) +{ + ShCutDbg() && ShCutDbg(QString("SUIT_ShortcutMgr::fillContainerFromPreferences(theContainer, theDefaultOnly = ") + (theDefaultOnly ? "true" : "false") + ", theSectionPrefix = \"" + theSectionNamePrefix + "\") started."); SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr(); if (!resMgr) { @@ -1692,7 +1721,7 @@ SUIT_ShortcutMgr::~SUIT_ShortcutMgr() // 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); + QStringList moduleIDs = resMgr->subSections(theSectionNamePrefix, true); if (ShCutDbg()) { if (moduleIDs.isEmpty()) ShCutDbg("No discovered shortcut modules."); @@ -1709,7 +1738,7 @@ SUIT_ShortcutMgr::~SUIT_ShortcutMgr() continue; } - const QString sectionName = SECTION_NAME_PREFIX + resMgr->sectionsToken() + moduleID; + const QString sectionName = theSectionNamePrefix + resMgr->sectionsToken() + moduleID; QStringList moduleActionIDs = resMgr->parameters(sectionName); for(const QString& inModuleActionID : moduleActionIDs) { @@ -1806,7 +1835,7 @@ SUIT_ShortcutMgr::~SUIT_ShortcutMgr() resMgr->setWorkingMode(resMgrWorkingModeBefore); ShCutDbg() && ShCutDbg("theContainer holds following shortcuts:\n" + theContainer.toString()); - ShCutDbg() && ShCutDbg(QString("SUIT_ShortcutMgr::fillContainerFromPreferences(theContainer, theDefaultOnly = ") + (theDefaultOnly ? "true" : "false") + ") finished."); + ShCutDbg() && ShCutDbg(QString("SUIT_ShortcutMgr::fillContainerFromPreferences(theContainer, theDefaultOnly = ") + (theDefaultOnly ? "true" : "false") + ", theSectionPrefix = \"" + theSectionNamePrefix + "\") finished."); } /*static*/ std::pair, std::shared_ptr> @@ -3374,4 +3403,284 @@ QString SUIT_ActionSearcher::toString() const } return res; +} + + + +const QString SECTION_NAME_ACTION_ID_MUTATION_FILE_PATHS = "actionID_mutations"; + +/*static*/ const std::vector SUIT_ShortcutHistorian::SECTION_PREFIX_EVOLUTION = {"shortcuts_vA1.0", "shortcuts"}; + +/*static*/ const QString SUIT_ShortcutHistorian::AIDSMutation::PROP_ID_MUTATIONS = "mutations"; +/*static*/ const QString SUIT_ShortcutHistorian::AIDSMutation::PROP_ID_PREFIX_OLD = "sectionPrefixOld"; +/*static*/ const QString SUIT_ShortcutHistorian::AIDSMutation::PROP_ID_PREFIX_NEW = "sectionPrefixNew"; +/*static*/ const QString SUIT_ShortcutHistorian::AIDSMutation::PROP_ID_NEW_TO_OLD_ACTION_ID_MAP = "newToOldActionIDMap"; + +/*static*/ bool SUIT_ShortcutHistorian::AIDSMutation::isPairOfNewAndOldActionIDsValid(const QString& theSectionPrefixNew, const QString& theSectionPrefixOld) +{ + if (theSectionPrefixNew == theSectionPrefixOld) { + Warning("SUIT_ShortcutHistorian::AIDSMutation: new section prefix is the same as old one - \"" + theSectionPrefixNew + "\"."); + return false; + } + + return true; +} + +SUIT_ShortcutHistorian::AIDSMutation::AIDSMutation(const QString& theSectionPrefixNew, const QString& theSectionPrefixOld) +{ + if (!SUIT_ShortcutHistorian::AIDSMutation::isPairOfNewAndOldActionIDsValid(theSectionPrefixNew, theSectionPrefixOld)) + throw std::invalid_argument("AIDSMutation::AIDSMutation: invalid prefixes."); + + mySectionPrefixOld = theSectionPrefixOld; + mySectionPrefixNew = theSectionPrefixNew; +} + +SUIT_ShortcutHistorian::AIDSMutation::AIDSMutation(const QJsonObject& theJsonObject, const bool theParseMap) +{ + mySectionPrefixOld = theJsonObject[SUIT_ShortcutHistorian::AIDSMutation::PROP_ID_PREFIX_OLD].toString(); + mySectionPrefixNew = theJsonObject[SUIT_ShortcutHistorian::AIDSMutation::PROP_ID_PREFIX_NEW].toString(); + if (!SUIT_ShortcutHistorian::AIDSMutation::isPairOfNewAndOldActionIDsValid(mySectionPrefixNew, mySectionPrefixOld)) + throw std::invalid_argument("AIDSMutation::AIDSMutation: invalid prefixes."); + + if (!theParseMap) + return; + + const auto actionIDMapJSONObject = theJsonObject[SUIT_ShortcutHistorian::AIDSMutation::PROP_ID_NEW_TO_OLD_ACTION_ID_MAP].toObject(); + for (const QString& newActionID : actionIDMapJSONObject.keys()) { + if (!SUIT_ShortcutMgr::isActionIDValid(newActionID)) { + Warning("SUIT_ShortcutHistorian::AIDSMutation::fromJSON: invalid action ID \"" + newActionID + "\" has been encountered."); + continue; + } + + const QString oldActionID = actionIDMapJSONObject[newActionID].toString(); + if (!SUIT_ShortcutMgr::isActionIDValid(oldActionID)) { + Warning("SUIT_ShortcutHistorian::AIDSMutation::fromJSON: invalid action ID \"" + oldActionID + "\" has been encountered."); + continue; + } + + if (myOldToNewActionIDMap.find(oldActionID) != myOldToNewActionIDMap.end()) { + Warning("SUIT_ShortcutHistorian::AIDSMutation::fromJSON: old action ID \"" + oldActionID + "\" is not unique within mutation. Ignored."); + continue; + } + + if (myNewToOldActionIDMap.find(newActionID) != myNewToOldActionIDMap.end()) { + Warning("SUIT_ShortcutHistorian::AIDSMutation::fromJSON: new action ID \"" + newActionID + "\" is not unique within mutation. Ignored."); + continue; + } + + myOldToNewActionIDMap.emplace(oldActionID, newActionID); + myNewToOldActionIDMap.emplace(newActionID, oldActionID); + } +} + +bool SUIT_ShortcutHistorian::AIDSMutation::isConcurrent(const AIDSMutation& theOther) const +{ + return mySectionPrefixOld == theOther.mySectionPrefixOld && mySectionPrefixNew == theOther.mySectionPrefixNew; +} + +bool SUIT_ShortcutHistorian::AIDSMutation::merge(const AIDSMutation& theOther) +{ + if (!isConcurrent(theOther)) + return false; + + bool areMutationMapsExtended = false; + for (const auto& newAndOldActionIDOfOther : theOther.myNewToOldActionIDMap) { + const QString& newActionIDOfOther = newAndOldActionIDOfOther.first; + const QString& oldActionIDOfOther = newAndOldActionIDOfOther.second; + + if (myOldToNewActionIDMap.find(oldActionIDOfOther) != myOldToNewActionIDMap.end()) { + Warning("SUIT_ShortcutHistorian::AIDSMutation::merge: old action ID \"" + oldActionIDOfOther + "\" is not unique within mutation. Ignored."); + continue; + } + + if (myNewToOldActionIDMap.find(newActionIDOfOther) != myNewToOldActionIDMap.end()) { + Warning("SUIT_ShortcutHistorian::AIDSMutation::merge: new action ID \"" + newActionIDOfOther + "\" is not unique within mutation. Ignored."); + continue; + } + + myOldToNewActionIDMap.emplace(oldActionIDOfOther, newActionIDOfOther); + myNewToOldActionIDMap.emplace(newActionIDOfOther, oldActionIDOfOther); + areMutationMapsExtended = true; + } + + return areMutationMapsExtended; +} + + +SUIT_ShortcutHistorian::SUIT_ShortcutHistorian() { + parseMutations(); + ShCutDbg("SUIT_ShortcutHistorian: parsing of old shortcut preference sections started."); + for (const auto& oldPrefixAndMutation : myOldPrefixToMutationList) { + const QString& oldPrefix = oldPrefixAndMutation.first; + auto& container = myShortcutContainers.emplace(oldPrefix, SUIT_ShortcutContainer()).first->second; + SUIT_ShortcutMgr::fillContainerFromPreferences(container, false, oldPrefix); + } + ShCutDbg("SUIT_ShortcutHistorian: parsing of old shortcut preference sections finished."); + + for (auto itPrefix = SUIT_ShortcutHistorian::SECTION_PREFIX_EVOLUTION.rbegin(); itPrefix != SUIT_ShortcutHistorian::SECTION_PREFIX_EVOLUTION.rend(); itPrefix++) { + const auto itPrefixAndContainer = myShortcutContainers.find(*itPrefix); + if (itPrefixAndContainer == myShortcutContainers.end()) + continue; + + myShortcutContainer.merge(itPrefixAndContainer->second, true /*theOverride*/, false /*theTreatAbsentIncomingAsDisabled*/); + } +} + +bool SUIT_ShortcutHistorian::doOldShortcutPreferencesExist() const +{ + for (const auto& prefixAndContainer : myShortcutContainers) { + const auto& container = prefixAndContainer.second; + if (!container.isEmpty()) + return true; + } + return false; +} + +std::pair SUIT_ShortcutHistorian::getOldUserDefinedKeySequence(const QString& theActionID) const +{ + auto result = std::pair(false, QKeySequence()); + + /** ID of the same action before a mutation (migration) happened. */ + QString oldActionID = theActionID; + for (const auto& oldPrefixAndMutation : myOldPrefixToMutationList) { + const auto& mutation = oldPrefixAndMutation.second; + const auto itNewAndOldActionIDs = mutation.getNewToOldActionIDMap().find(theActionID); + if (itNewAndOldActionIDs != mutation.getNewToOldActionIDMap().end()) + oldActionID = itNewAndOldActionIDs->second; + + std::pair oldKeySequence = getUserDefinedKeySequenceInSection(oldActionID, mutation.getSectionPrefixOld()); + if (oldKeySequence.first) { + // The old shortcut is defined in the old section. No need to dig deeper into action ID evolution. + return result; + } + } + return result; +} + +void SUIT_ShortcutHistorian::removeOldShortcutPreferences() +{ + SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr(); + if (!resMgr) { + Warning("SUIT_ShortcutHistorian: can't retrieve resource manager!"); + return; + } + + for (const auto& prefixAndContainer : myShortcutContainers) { + const auto& sectionNamePrefix = prefixAndContainer.first; + const auto& container = prefixAndContainer.second; + + const auto& moduleIDs = container.getIDsOfAllModules(); + for (const QString& moduleID : moduleIDs) { + const QString sectionName = sectionNamePrefix + resMgr->sectionsToken() + moduleID; + resMgr->remove(sectionName); // TODO Check if SUIT_ResourceMgr really removes sections from preference files, not just from its fields. + } + } +} + +void SUIT_ShortcutHistorian::parseMutations() +{ + ShCutDbg() && ShCutDbg("Parsing action ID mutation files."); + + if (SUIT_ShortcutHistorian::SECTION_PREFIX_EVOLUTION.size() < 2) { + Warning("SUIT_ShortcutHistorian: shortcut settings' preference section name evolution is too short."); + return; + } + + if (SUIT_ShortcutHistorian::SECTION_PREFIX_EVOLUTION.front() != SECTION_NAME_PREFIX) { + Warning("SUIT_ShortcutHistorian: shortcut settings' preference section name evolution starts with a prefix, which differs from the actual one."); + return; + } + + for (size_t idx = 0; idx < SUIT_ShortcutHistorian::SECTION_PREFIX_EVOLUTION.size() - 1; idx++) { + const QString& newPrefix = SUIT_ShortcutHistorian::SECTION_PREFIX_EVOLUTION[idx]; + const QString& oldPrefix = SUIT_ShortcutHistorian::SECTION_PREFIX_EVOLUTION[idx + 1]; + myOldPrefixToMutationList.emplace_back(std::pair(oldPrefix, SUIT_ShortcutHistorian::AIDSMutation(newPrefix, oldPrefix))); + } + + SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr(); + if (!resMgr) { + Warning("SUIT_ShortcutHistorian can't retrieve resource manager!"); + return; + } + + QStringList mutationFilePaths = resMgr->parameters(SECTION_NAME_ACTION_ID_MUTATION_FILE_PATHS); +#ifdef SHORTCUT_MGR_DBG + ShCutDbg("Action ID mutation files: " + mutationFilePaths.join(", ") + "."); +#endif + for (const QString& mutationFilePath : mutationFilePaths) { + const QString path = ::SUIT_tools::substituteVars(mutationFilePath); + ShCutDbg("Parsing action ID mutation file \"" + path + "\"."); + QFile mutationFile(path); + if (!mutationFile.open(QIODevice::ReadOnly)) { + Warning("SUIT_ShortcutHistorian can't open action ID mutation file \"" + path + "\"!"); + continue; + } + + QJsonParseError jsonError; + QJsonDocument document = QJsonDocument::fromJson(mutationFile.readAll(), &jsonError); + mutationFile.close(); + if (jsonError.error != QJsonParseError::NoError) { + Warning("SUIT_ShortcutHistorian: error during parsing of action ID mutation file \"" + path + "\"!"); + continue; + } + + if (document.isObject()) { + const QJsonObject documentJSONObject = document.object(); + const auto itMutationsJSONVal = documentJSONObject.find(SUIT_ShortcutHistorian::AIDSMutation::PROP_ID_MUTATIONS); + if (itMutationsJSONVal == documentJSONObject.end()) { + Warning("Action ID mutation file \"" + path + "\" does not contain \"" + SUIT_ShortcutHistorian::AIDSMutation::PROP_ID_MUTATIONS + "\" array."); + continue; + } + + const auto& mutationsJSONVal = itMutationsJSONVal.value(); + if (!mutationsJSONVal.isArray()) { + Warning("Action ID mutation file \"" + path + "\" has a property \"" + SUIT_ShortcutHistorian::AIDSMutation::PROP_ID_MUTATIONS + "\", but it is not array."); + continue; + } + + const auto& mutationsJSONArray = mutationsJSONVal.toArray(); + for (const auto& mutationJSONVal : mutationsJSONArray) { + auto mutation = std::unique_ptr(nullptr); + try { + mutation.reset(new SUIT_ShortcutHistorian::AIDSMutation(mutationJSONVal.toObject())); + } + catch (const std::invalid_argument& e) { + Warning(e.what()); + continue; + } + + const auto predicate = [&mutation] (const std::pair& thePair) { return thePair.first == mutation->getSectionPrefixOld(); }; + const auto itOldPrefixToMutationList = std::find_if(myOldPrefixToMutationList.begin(), myOldPrefixToMutationList.end(), predicate); + if (itOldPrefixToMutationList == myOldPrefixToMutationList.end() || !mutation->isConcurrent(itOldPrefixToMutationList->second)) { + Warning("Action ID mutation file \"" + path + "\" contains a Mutation, which is not concurrent with mutations from evolution. Old prefix \"" + mutation->getSectionPrefixOld() + "\"; new prefix \"" + mutation->getSectionPrefixNew() + "\"."); + continue; + } + + itOldPrefixToMutationList->second.merge(*mutation); + } + } + } +} + +std::pair SUIT_ShortcutHistorian::getUserDefinedKeySequenceInSection(const QString& theActionID, const QString& theSectionPrefix) const +{ + auto result = std::pair(false, QKeySequence()); + + const auto moduleIDAndInModuleActionID = SUIT_ShortcutMgr::splitIntoModuleIDAndInModuleID(theActionID); + const QString& moduleID = moduleIDAndInModuleActionID.first; + const QString& inModuleActionID = moduleIDAndInModuleActionID.second; + if (inModuleActionID.isEmpty()) + return result; // Invalid action ID. + + const auto itContainers = myShortcutContainers.find(theSectionPrefix); + if (itContainers == myShortcutContainers.end()) + return result; + + const auto& container = itContainers->second; + if (!container.hasShortcut(moduleID, inModuleActionID)) + return result; + + result.first = true; + result.second = container.getKeySequence(moduleID, inModuleActionID); + return result; } \ No newline at end of file diff --git a/src/SUIT/SUIT_ShortcutMgr.h b/src/SUIT/SUIT_ShortcutMgr.h index 228de4067..2a138d0a3 100644 --- a/src/SUIT/SUIT_ShortcutMgr.h +++ b/src/SUIT/SUIT_ShortcutMgr.h @@ -76,6 +76,9 @@ public: std::set getIDsOfAllModules() const; + /*! \returns True, if no shortcut is added. */ + bool isEmpty() 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. @@ -542,6 +545,9 @@ public: \param theDefaultOnly If true, user preferences are ignored and only default preferences are used. */ static void fillContainerFromPreferences(SUIT_ShortcutContainer& theContainer, bool theDefaultOnly); + /*! \param theSectionNamePrefix Prefix of section name in preference files to look in. */ + static void fillContainerFromPreferences(SUIT_ShortcutContainer& theContainer, bool theDefaultOnly, const QString& theSectionNamePrefix); + /*! \brief Returns item assets as they are in asset files. Returned module assets is necessary to keep memory ownership of theAction ancestors. The module assets contain only ancestors of theActionID. \param theLangs If empty, all languages is parsed. */ @@ -793,6 +799,93 @@ private: }; +/*! \brief Sometimes developers change IDs of actions. And at the moment of release of these changes +users may already have cutomized shortcuts. This class assits with user defined shorcut settings' migrations. +It retrieves keysequences, assigned by user using older Salome versions, +which operated with old action ID sets. "Action ID set" is also referred as AIDS for brevity. */ +class SUIT_EXPORT SUIT_ShortcutHistorian +{ +public: + /** {name prefix of a section in user preference files}[]. Sorted from the newest to the oldest. + * The list is hardcoded. Update it every time, a new AIDS and appropriate AIDSMutation are added. */ + static const std::vector SECTION_PREFIX_EVOLUTION; + +private: + /*! \brief Describes how action IDs evolved between consequtive versions of action ID sets. + PS. Once upon a time I also wanted to cure AIDS. But instead I spawned new ones. With mutations. */ + class SUIT_EXPORT AIDSMutation + { + public: + static const QString PROP_ID_MUTATIONS; + static const QString PROP_ID_PREFIX_OLD; + static const QString PROP_ID_PREFIX_NEW; + static const QString PROP_ID_NEW_TO_OLD_ACTION_ID_MAP; + + static bool isPairOfNewAndOldActionIDsValid(const QString& theSectionPrefixNew, const QString& theSectionPrefixOld); + + AIDSMutation(const QString& theSectionPrefixNew, const QString& theSectionPrefixOld); + AIDSMutation(const QJsonObject& theJsonObject, const bool theParseMap = true); + + const QString& getSectionPrefixOld() const { return mySectionPrefixOld; }; + const QString& getSectionPrefixNew() const { return mySectionPrefixNew; }; + const std::map& getNewToOldActionIDMap() const { return myNewToOldActionIDMap; }; + + /*! \returns True, if both old and new prefixes are the same as ones of theOther. */ + bool isConcurrent(const AIDSMutation& theOther) const; + + /*! \returns True, if mutation maps are augmented. */ + bool merge(const AIDSMutation& theOther); + + private: + /** Old name prefix of a section in user preference files. */ + QString mySectionPrefixOld; + + /** New name prefix of a section in user preference files. */ + QString mySectionPrefixNew; + + /** The map only keeps changes between AIDS versions. It means, there is no need to add entries like {theSameActionID, theSameActionID}. */ + std::map myNewToOldActionIDMap; + + /** The map only keeps changes between AIDS versions. It means, there is no need to add entries like {theSameActionID, theSameActionID}. */ + std::map myOldToNewActionIDMap; + }; + +public: + SUIT_ShortcutHistorian(); + + /*! \returns True, if myShortcutContainers has at least one shortcut. */ + bool doOldShortcutPreferencesExist() const; + + /*! + \param theActionID Action ID in latest version (as elsewhere in ShortcutMgr code). + \returns {false, _ }, if shortcut is not defined in any outdated shortcut section of user preference files. */ + std::pair getOldUserDefinedKeySequence(const QString& theActionID) const; + + const SUIT_ShortcutContainer& getContainerWithOldShortcuts() const { return myShortcutContainer; }; + + /*! \brief Removes old shortcut sections from preference files and clears myShortcutContainers. */ + void removeOldShortcutPreferences(); + +private: + void parseMutations(); + + /*! + \param theSectionPrefix User preferences' section name with shortcuts. + \returns {false, _ }, is the shortcut is not defined in the section. */ + std::pair getUserDefinedKeySequenceInSection(const QString& theActionID, const QString& theSectionPrefix) const; + +private: + /** {sectionNamePrefixOld, mutation}[]. Sorted from the newest to the oldest. */ + std::list> myOldPrefixToMutationList; + + /** {sectionNamePrefixOld, shortcutContainer}[]. */ + std::map myShortcutContainers; + + /** Merged myShortcutContainers. Merge is performed from the oldest to the newest, newer shortcuts override older ones. */ + SUIT_ShortcutContainer myShortcutContainer; +}; + + namespace SUIT_tools { class SUIT_SentenceMatcher; } diff --git a/src/SUIT/resources/action_id_mutations.json b/src/SUIT/resources/action_id_mutations.json new file mode 100644 index 000000000..77ae18378 --- /dev/null +++ b/src/SUIT/resources/action_id_mutations.json @@ -0,0 +1,17 @@ +{ + "mutations": [ + { + "sectionPrefixOld": "shortcuts", + "sectionPrefixNew": "shortcuts_vA1.0", + "newToOldActionIDMap": { + "/File/Close" : "/TOT_DESK_FILE_CLOSE", + "/File/Exit" : "/TOT_DESK_FILE_EXIT", + "/File/New" : "/TOT_DESK_FILE_NEW", + "/File/Open" : "/TOT_DESK_FILE_OPEN", + "/File/Preferences" : "/PRP_DESK_PREFERENCES", + "/File/Save" : "/TOT_DESK_FILE_SAVE", + "/File/SaveAs" : "/TOT_DESK_FILE_SAVEAS" + } + } + ] +} \ No newline at end of file -- 2.39.2