Salome HOME
[bos #40644][CEA](2024-T1) Feature search.
authordish <dmitrii.shvydkoi@opencascade.com>
Sun, 14 Apr 2024 11:10:55 +0000 (11:10 +0000)
committerdish <dmitrii.shvydkoi@opencascade.com>
Sun, 14 Apr 2024 11:10:55 +0000 (11:10 +0000)
Add ranking of search results by number of character in matching words.
Add preselection of the best match on query update.

src/SUIT/SUIT_FindActionDialog.cxx
src/SUIT/SUIT_FindActionDialog.h
src/SUIT/SUIT_ShortcutMgr.cxx
src/SUIT/SUIT_ShortcutMgr.h
src/SUIT/SUIT_ShortcutTree.cxx
src/SalomeApp/SalomeApp_Application.cxx

index 413651321a6593e0337adf70548ab1df53a58b17..5f5ffb80dcbf6c1107869af79b9fbe8919f58ac9 100644 (file)
@@ -34,6 +34,7 @@
 #include <QHeaderView>
 
 #include <algorithm>
+#include <limits>
 
 
 SUIT_FindActionDialog::SUIT_FindActionDialog(QWidget* theParent)
@@ -132,23 +133,86 @@ SUIT_FoundActionTree::SUIT_FoundActionTree(SUIT_FindActionDialog* theParent)
   connect(this, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), this, SLOT(onItemDoubleClicked(QTreeWidgetItem*, int)));
 }
 
+/*! \brief Compensates lack of std::distance(), which is introduced in C++17.
+\returns -1, if theIt does not belong to the  */
+template <class Container>
+size_t indexOf(
+  const Container& theContainer,
+  const typename Container::iterator& theIt
+) {
+  auto it = theContainer.begin();
+  size_t distance = 0;
+  while (it != theContainer.end()) {
+    if (it == theIt)
+      return distance;
+
+    it++;
+    distance++;
+  }
+  return -1;
+}
+
 void SUIT_FoundActionTree::updateItems(const std::map<QString, std::map<QString, SUIT_ActionSearcher::AssetsAndSearchData>>& theAssets)
 {
-  clear();
+  std::set<QString> shownModuleIDs; // To sort module-items by their IDs.
+
+  // Remove shown module items, if updated search results have no matching actions from these modules.
+  for (int moduleIdx = 0; moduleIdx < topLevelItemCount(); ) {
+    SUIT_FoundActionTreeModule* moduleItem = static_cast<SUIT_FoundActionTreeModule*>(topLevelItem(moduleIdx));
+    myModuleItemExpansionStates[moduleItem->myModuleID] = moduleItem->isExpanded();
+
+    const auto itUpdatedAssetsOfShownModule = theAssets.find(moduleItem->myModuleID);
+    if (itUpdatedAssetsOfShownModule == theAssets.end()) {
+      delete takeTopLevelItem(moduleIdx);
+      continue;
+    }
+
+    if (itUpdatedAssetsOfShownModule->second.empty()) {
+      delete takeTopLevelItem(moduleIdx);
+      continue;
+    }
+
+    shownModuleIDs.emplace(moduleItem->myModuleID);
+    moduleIdx++;
+  }
 
   const auto shortcutMgr = SUIT_ShortcutMgr::get();
   const QString lang = SUIT_ShortcutMgr::getLang();
 
+  SUIT_FoundActionTreeAction* preselectedActionItem = nullptr;
+
   for (const auto& moduleIDAndAssets : theAssets) {
     const QString& moduleID = moduleIDAndAssets.first;
     const auto& moduleAssets = moduleIDAndAssets.second;
     if (moduleAssets.empty())
       continue;
 
-    const auto moduleItem = new SUIT_FoundActionTreeFolder(moduleID);
-    moduleItem->setAssets(shortcutMgr->getModuleAssets(moduleID), lang);
-    addTopLevelItem(moduleItem);
-    moduleItem->setFlags(Qt::ItemIsEnabled);
+    const auto moduleItemAndIdx = findModuleItem(moduleID);
+    SUIT_FoundActionTreeModule* moduleItem = moduleItemAndIdx.first;
+    if (!moduleItem) {
+      moduleItem = new SUIT_FoundActionTreeModule(moduleID);
+      moduleItem->setAssetsAndSearchData(SUIT_ActionSearcher::AssetsAndSearchData(shortcutMgr->getModuleAssets(moduleID)), lang);
+
+      const auto emplaceRes = shownModuleIDs.emplace(moduleID);
+      insertTopLevelItem(indexOf(shownModuleIDs, emplaceRes.first), moduleItem);
+
+      moduleItem->setFlags(Qt::ItemIsEnabled);
+
+      const auto itExpansionState = myModuleItemExpansionStates.find(moduleID);
+      if (itExpansionState == myModuleItemExpansionStates.end())
+        moduleItem->setExpanded(true); // Make module item expanded at first appearance.
+      else
+        moduleItem->setExpanded(itExpansionState->second);
+    }
+    else /* if the tree has the module-item */ {
+      const auto actionItems = moduleItem->takeChildren();
+      for (const auto actionItem : actionItems) {
+        delete actionItem;
+      }
+    }
+
+    // Fill module item with action items.
+    auto sortedActionItems = createActionSetWithComparator();
     for (const auto& actionIDAndAssets : moduleAssets) {
       const QString& inModuleActionID = actionIDAndAssets.first;
       const SUIT_ActionSearcher::AssetsAndSearchData& assetsAndSearchData = actionIDAndAssets.second;
@@ -159,28 +223,114 @@ void SUIT_FoundActionTree::updateItems(const std::map<QString, std::map<QString,
         continue;
       }
 
-      actionItem->setAssets(assetsAndSearchData.myAssets, lang);
+      actionItem->setAssetsAndSearchData(assetsAndSearchData, lang);
+      sortedActionItems.emplace(actionItem);
+    }
+
+    SUIT_FoundActionTreeAction* preselectedActionItemCand = nullptr;
+    for (const auto actionItem : sortedActionItems) {
+      moduleItem->addChild(actionItem);
+
+      // Consider first ranked available action in the module (if user did not collapsed it) as a candidate for preselected action.
+      if (!preselectedActionItemCand && moduleItem->isExpanded() && actionItem->isEnabledBufferedValue())
+        preselectedActionItemCand = actionItem;
+    }
+
+    if (preselectedActionItem) {
+      if (preselectedActionItemCand) {
+        if (preselectedActionItemCand->matchMetrics() < preselectedActionItem->matchMetrics())
+          preselectedActionItem = preselectedActionItemCand;
+      }
+    }
+    else
+      preselectedActionItem = preselectedActionItemCand;
+  }
+
+  if (preselectedActionItem)
+    setCurrentItem(preselectedActionItem);
+}
+
+void SUIT_FoundActionTree::sort(SUIT_FoundActionTree::SortKey theKey, SUIT_FoundActionTree::SortOrder theOrder)
+{
+  if (theKey == mySortKey && theOrder == mySortOrder)
+    return;
+
+  mySortKey == theKey;
+  mySortOrder = theOrder;
+
+  for (int moduleIdx = 0; moduleIdx < topLevelItemCount(); moduleIdx++) {
+    const auto moduleItem = static_cast<SUIT_FoundActionTreeModule*>(topLevelItem(moduleIdx));
+
+    auto sortedActionItems = createActionSetWithComparator();
+    for (int childIdx = 0; childIdx < moduleItem->childCount(); childIdx++) {
+      SUIT_FoundActionTreeAction* const actionItem = static_cast<SUIT_FoundActionTreeAction*>(moduleItem->child(childIdx));
+      sortedActionItems.emplace(actionItem);
+    }
+
+    moduleItem->takeChildren();
+
+    for (const auto actionItem : sortedActionItems) {
       moduleItem->addChild(actionItem);
     }
-    moduleItem->setExpanded(true); // Make tree expanded on first show.
   }
 }
 
-std::pair<SUIT_FoundActionTreeFolder*, int> SUIT_FoundActionTree::findModuleFolderItem(const QString& theModuleID) const
+std::pair<SUIT_FoundActionTreeModule*, int> SUIT_FoundActionTree::findModuleItem(const QString& theModuleID) const
 {
   for (int moduleIdx = 0; moduleIdx < topLevelItemCount(); moduleIdx++) {
-    SUIT_FoundActionTreeFolder* moduleItem = static_cast<SUIT_FoundActionTreeFolder*>(topLevelItem(moduleIdx));
+    SUIT_FoundActionTreeModule* moduleItem = static_cast<SUIT_FoundActionTreeModule*>(topLevelItem(moduleIdx));
     if (moduleItem->myModuleID == theModuleID)
-      return std::pair<SUIT_FoundActionTreeFolder*, int>(moduleItem, moduleIdx);
+      return std::pair<SUIT_FoundActionTreeModule*, int>(moduleItem, moduleIdx);
+  }
+  return std::pair<SUIT_FoundActionTreeModule*, int>(nullptr, -1);
+}
+
+std::set<SUIT_FoundActionTreeAction*, std::function<bool(SUIT_FoundActionTreeAction*, SUIT_FoundActionTreeAction*)>> SUIT_FoundActionTree::createActionSetWithComparator() const
+{
+  QList<std::pair<SUIT_FoundActionTree::SortKey, SUIT_FoundActionTree::SortOrder>> sortSchema = SUIT_FoundActionTree::DEFAULT_SORT_SCHEMA;
+  {
+    for (auto itSameKey = sortSchema.begin(); itSameKey != sortSchema.end(); itSameKey++) {
+      if (itSameKey->first == mySortKey) {
+        sortSchema.erase(itSameKey);
+        break;
+      }
+    }
+    sortSchema.push_front(std::pair<SUIT_FoundActionTree::SortKey, SUIT_FoundActionTree::SortOrder>(mySortKey, mySortOrder));
   }
-  return std::pair<SUIT_FoundActionTreeFolder*, int>(nullptr, -1);
+
+  static const QCollator collator;
+  const std::function<bool(SUIT_FoundActionTreeAction*, SUIT_FoundActionTreeAction*)> comparator =
+  [sortSchema, &collator](const SUIT_FoundActionTreeAction* theItemA, const SUIT_FoundActionTreeAction* theItemB) {
+    for (const auto& keyAndOrder : sortSchema) {
+      const QVariant fieldOfA = theItemA->getValue(keyAndOrder.first);
+      const QVariant fieldOfB = theItemB->getValue(keyAndOrder.first);
+
+      bool* const fieldOfAIsDouble = new bool(false);
+      bool* const fieldOfBIsDouble = new bool(false);
+      const double matchMetricsA = fieldOfA.toDouble(fieldOfAIsDouble);
+      const double matchMetricsB = fieldOfB.toDouble(fieldOfBIsDouble);
+      if (fieldOfAIsDouble && fieldOfBIsDouble) {
+        const double res = matchMetricsA - matchMetricsB;
+        if (std::abs(res) > std::numeric_limits<double>::epsilon())
+          return keyAndOrder.second == SUIT_FoundActionTree::SortOrder::Ascending ? res < 0 : res > 0;
+      }
+      else {
+        const int res = collator.compare(fieldOfA.toString(), fieldOfB.toString());
+        if (res != 0)
+          return keyAndOrder.second == SUIT_FoundActionTree::SortOrder::Ascending ? res < 0 : res > 0;
+      }
+    }
+    return false;
+  };
+
+  return std::set<SUIT_FoundActionTreeAction*, std::function<bool(SUIT_FoundActionTreeAction*, SUIT_FoundActionTreeAction*)>>(comparator);
 }
 
 void SUIT_FoundActionTree::onItemDoubleClicked(QTreeWidgetItem* theItem, int theColIdx)
 {
   {
     SUIT_FoundActionTreeItem* const item = static_cast<SUIT_FoundActionTreeItem*>(theItem);
-    // Do not react if folder-item is clicked.
+    // Do not react if module-item is clicked.
     if (item->type() != SUIT_FoundActionTreeItem::Type::Action)
       return;
   }
@@ -190,6 +340,14 @@ void SUIT_FoundActionTree::onItemDoubleClicked(QTreeWidgetItem* theItem, int the
     static_cast<SUIT_FindActionDialog*>(parentWidget())->accept();
 }
 
+/*static*/ const QList<std::pair<SUIT_FoundActionTree::SortKey, SUIT_FoundActionTree::SortOrder>> SUIT_FoundActionTree::DEFAULT_SORT_SCHEMA =
+{
+  {SUIT_FoundActionTree::SortKey::MatchMetrics, SUIT_FoundActionTree::SortOrder::Ascending},
+  {SUIT_FoundActionTree::SortKey::Name, SUIT_FoundActionTree::SortOrder::Ascending},
+  {SUIT_FoundActionTree::SortKey::ToolTip, SUIT_FoundActionTree::SortOrder::Ascending},
+  {SUIT_FoundActionTree::SortKey::ID, SUIT_FoundActionTree::SortOrder::Ascending}
+};
+
 
 SUIT_FoundActionTreeItem::SUIT_FoundActionTreeItem(const QString& theModuleID)
 : QTreeWidgetItem(), myModuleID(theModuleID)
@@ -206,7 +364,7 @@ QString SUIT_FoundActionTreeItem::toolTip() const
 }
 
 
-SUIT_FoundActionTreeFolder::SUIT_FoundActionTreeFolder(const QString& theModuleID)
+SUIT_FoundActionTreeModule::SUIT_FoundActionTreeModule(const QString& theModuleID)
 : SUIT_FoundActionTreeItem(theModuleID)
 {
   QFont f = font(SUIT_FoundActionTree::ElementIdx::Name);
@@ -215,14 +373,14 @@ SUIT_FoundActionTreeFolder::SUIT_FoundActionTreeFolder(const QString& theModuleI
   setText(SUIT_FoundActionTree::ElementIdx::Name, theModuleID);
 }
 
-void SUIT_FoundActionTreeFolder::setAssets(std::shared_ptr<const SUIT_ActionAssets> theAssets, const QString& theLang)
+void SUIT_FoundActionTreeModule::setAssetsAndSearchData(const SUIT_ActionSearcher::AssetsAndSearchData& theAssetsAndSD, const QString& theLang)
 {
-  if (!theAssets)
+  if (!theAssetsAndSD.myAssets)
     return;
 
-  setIcon(SUIT_FoundActionTree::ElementIdx::Name, theAssets->myIcon);
+  setIcon(SUIT_FoundActionTree::ElementIdx::Name, theAssetsAndSD.myAssets->myIcon);
 
-  const auto& ldaMap = theAssets->myLangDependentAssets;
+  const auto& ldaMap = theAssetsAndSD.myAssets->myLangDependentAssets;
   if (ldaMap.empty()) {
     setText(SUIT_FoundActionTree::ElementIdx::Name, myModuleID);
     return;
@@ -237,9 +395,11 @@ void SUIT_FoundActionTreeFolder::setAssets(std::shared_ptr<const SUIT_ActionAsse
   setText(SUIT_FoundActionTree::ElementIdx::Name, name);
 }
 
-QString SUIT_FoundActionTreeFolder::getValue(SUIT_FoundActionTree::SortKey theKey) const
+QVariant SUIT_FoundActionTreeModule::getValue(SUIT_FoundActionTree::SortKey theKey) const
 {
   switch (theKey) {
+    case SUIT_FoundActionTree::SortKey::MatchMetrics:
+      return double(0);
     case SUIT_FoundActionTree::SortKey::ID:
       return myModuleID;
     case SUIT_FoundActionTree::SortKey::Name:
@@ -251,14 +411,15 @@ QString SUIT_FoundActionTreeFolder::getValue(SUIT_FoundActionTree::SortKey theKe
   }
 }
 
-bool SUIT_FoundActionTreeFolder::isEnabled() const
+bool SUIT_FoundActionTreeModule::isEnabled() const
 {
   return true;
 }
 
 
 SUIT_FoundActionTreeAction::SUIT_FoundActionTreeAction(const QString& theModuleID, const QString& theInModuleActionID)
-: SUIT_FoundActionTreeItem(theModuleID), myInModuleActionID(theInModuleActionID)
+: SUIT_FoundActionTreeItem(theModuleID), myInModuleActionID(theInModuleActionID),
+  myMatchMetrics(std::numeric_limits<double>::infinity()), myIsEnabledBufferedValue(false)
 {
   setText(SUIT_FoundActionTree::ElementIdx::Name, theInModuleActionID);
 }
@@ -273,14 +434,14 @@ SUIT_FoundActionTreeAction::SUIT_FoundActionTreeAction(const QString& theModuleI
   return new SUIT_FoundActionTreeAction(theModuleID, theInModuleActionID);
 }
 
-void SUIT_FoundActionTreeAction::setAssets(std::shared_ptr<const SUIT_ActionAssets> theAssets, const QString& theLang)
+void SUIT_FoundActionTreeAction::setAssetsAndSearchData(const SUIT_ActionSearcher::AssetsAndSearchData& theAssetsAndSD, const QString& theLang)
 {
-  if (!theAssets)
+  if (!theAssetsAndSD.myAssets)
     return;
 
-  setIcon(SUIT_FoundActionTree::ElementIdx::Name, theAssets->myIcon);
+  setIcon(SUIT_FoundActionTree::ElementIdx::Name, theAssetsAndSD.myAssets->myIcon);
 
-  const auto& ldaMap = theAssets->myLangDependentAssets;
+  const auto& ldaMap = theAssetsAndSD.myAssets->myLangDependentAssets;
   if (ldaMap.empty()) {
     setText(SUIT_FoundActionTree::ElementIdx::Name, myInModuleActionID);
     return;
@@ -312,11 +473,15 @@ void SUIT_FoundActionTreeAction::setAssets(std::shared_ptr<const SUIT_ActionAsse
     setForeground(SUIT_FoundActionTree::ElementIdx::Name,    greyedOutBrush);
     setForeground(SUIT_FoundActionTree::ElementIdx::ToolTip, greyedOutBrush);
   }
+
+  myMatchMetrics = theAssetsAndSD.matchMetrics();
 }
 
-QString SUIT_FoundActionTreeAction::getValue(SUIT_FoundActionTree::SortKey theKey) const
+QVariant SUIT_FoundActionTreeAction::getValue(SUIT_FoundActionTree::SortKey theKey) const
 {
   switch (theKey) {
+    case SUIT_FoundActionTree::SortKey::MatchMetrics:
+      return myMatchMetrics;
     case SUIT_FoundActionTree::SortKey::ID:
       return myInModuleActionID;
     case SUIT_FoundActionTree::SortKey::Name:
@@ -331,7 +496,8 @@ QString SUIT_FoundActionTreeAction::getValue(SUIT_FoundActionTree::SortKey theKe
 bool SUIT_FoundActionTreeAction::isEnabled() const
 {
   const auto& actions = SUIT_ShortcutMgr::get()->getActions(myModuleID, myInModuleActionID);
-  return std::find_if(actions.begin(), actions.end(), [](const QAction* const theAction){ return theAction->isEnabled(); }) != actions.end();
+  myIsEnabledBufferedValue = std::find_if(actions.begin(), actions.end(), [](const QAction* const theAction){ return theAction->isEnabled(); }) != actions.end();
+  return myIsEnabledBufferedValue;
 }
 
 bool SUIT_FoundActionTreeAction::trigger() const
index 16827219187044f979ab538ab3293a2c31598e6a..4207cb4108eb382b142325d5f43ac3f9b48dfcb6 100644 (file)
 #define SUIT_FINDACTIONDIALOG_H
 
 #include "SUIT.h"
+#include "SUIT_ShortcutMgr.h"
 #include <QDialog>
 #include <QFrame>
 #include <QTreeWidget>
-#include "SUIT_ShortcutMgr.h"
+#include <QList>
+#include <QVariant>
 #include <memory>
 #include <map>
 #include <set>
 #include <functional>
+#include <utility>
 
 
 class QCheckBox;
@@ -72,7 +75,7 @@ private:
 
 
 class SUIT_FoundActionTreeItem;
-class SUIT_FoundActionTreeFolder;
+class SUIT_FoundActionTreeModule;
 class SUIT_FoundActionTreeAction;
 
 
@@ -87,11 +90,17 @@ public:
   };
 
   enum class SortKey {
+    MatchMetrics,
     ID,
     Name,
     ToolTip
   };
 
+  enum class SortOrder {
+    Ascending,
+    Descending
+  };
+
   SUIT_FoundActionTree(SUIT_FindActionDialog* theParent);
   SUIT_FoundActionTree(const SUIT_FoundActionTree&) = delete;
   SUIT_FoundActionTree& operator=(const SUIT_FoundActionTree&) = delete;
@@ -99,11 +108,24 @@ public:
 
   void updateItems(const std::map<QString, std::map<QString, SUIT_ActionSearcher::AssetsAndSearchData>>& theAssets);
 
+  void sort(SUIT_FoundActionTree::SortKey theKey, SUIT_FoundActionTree::SortOrder theOrder);
+
 private:
-  std::pair<SUIT_FoundActionTreeFolder*, int> findModuleFolderItem(const QString& theModuleID) const;
+  std::pair<SUIT_FoundActionTreeModule*, int> findModuleItem(const QString& theModuleID) const;
+  std::set<SUIT_FoundActionTreeAction*, std::function<bool(SUIT_FoundActionTreeAction*, SUIT_FoundActionTreeAction*)>> createActionSetWithComparator() const;
 
 private slots:
   void onItemDoubleClicked(QTreeWidgetItem* theWidgetItem, int theColIdx);
+
+public:
+  static const QList<std::pair<SUIT_FoundActionTree::SortKey, SUIT_FoundActionTree::SortOrder>> DEFAULT_SORT_SCHEMA;
+
+private:
+  SUIT_FoundActionTree::SortKey mySortKey;
+  SUIT_FoundActionTree::SortOrder mySortOrder;
+
+  /** {moduleID, isExpanded}[] */
+  std::map<QString, bool> myModuleItemExpansionStates;
 };
 
 
@@ -111,7 +133,7 @@ class SUIT_FoundActionTreeItem : public QTreeWidgetItem
 {
 public:
   enum Type {
-    Folder = 0,
+    Module = 0,
     Action = 1,
   };
 
@@ -122,11 +144,11 @@ public:
   virtual ~SUIT_FoundActionTreeItem() = default;
   virtual SUIT_FoundActionTreeItem::Type type() const = 0;
 
-  virtual void setAssets(std::shared_ptr<const SUIT_ActionAssets> theAssets, const QString& theLang) = 0;
+  virtual void setAssetsAndSearchData(const SUIT_ActionSearcher::AssetsAndSearchData& theAssetsAndSD, const QString& theLang) = 0;
   QString name() const;
   QString toolTip() const;
 
-  virtual QString getValue(SUIT_FoundActionTree::SortKey theKey) const = 0;
+  virtual QVariant getValue(SUIT_FoundActionTree::SortKey theKey) const = 0;
 
   virtual bool isEnabled() const = 0;
 
@@ -135,16 +157,17 @@ public:
 };
 
 
-class SUIT_FoundActionTreeFolder : public SUIT_FoundActionTreeItem
+class SUIT_FoundActionTreeModule : public SUIT_FoundActionTreeItem
 {
 public:
-  SUIT_FoundActionTreeFolder(const QString& theModuleID);
-  virtual ~SUIT_FoundActionTreeFolder() = default;
-  virtual SUIT_FoundActionTreeItem::Type type() const { return SUIT_FoundActionTreeItem::Type::Folder; };
+  SUIT_FoundActionTreeModule(const QString& theModuleID);
+  virtual ~SUIT_FoundActionTreeModule() = default;
+  virtual SUIT_FoundActionTreeItem::Type type() const { return SUIT_FoundActionTreeItem::Type::Module; };
 
-  virtual void setAssets(std::shared_ptr<const SUIT_ActionAssets> theAssets, const QString& theLang);
+  /*! \brief Search data is unused. */
+  virtual void setAssetsAndSearchData(const SUIT_ActionSearcher::AssetsAndSearchData& theAssetsAndSD, const QString& theLang);
 
-  virtual QString getValue(SUIT_FoundActionTree::SortKey theKey) const;
+  virtual QVariant getValue(SUIT_FoundActionTree::SortKey theKey) const;
 
   virtual bool isEnabled() const;
 };
@@ -160,15 +183,21 @@ public:
   virtual ~SUIT_FoundActionTreeAction() = default;
   virtual SUIT_FoundActionTreeItem::Type type() const { return SUIT_FoundActionTreeItem::Type::Action; };
 
-  virtual void setAssets(std::shared_ptr<const SUIT_ActionAssets> theAssets, const QString& theLang);
+  virtual void setAssetsAndSearchData(const SUIT_ActionSearcher::AssetsAndSearchData& theAssetsAndSD, const QString& theLang);
 
-  virtual QString getValue(SUIT_FoundActionTree::SortKey theKey) const;
+  virtual QVariant getValue(SUIT_FoundActionTree::SortKey theKey) const;
+  double matchMetrics() const { return myMatchMetrics; };
 
   virtual bool isEnabled() const;
+  bool isEnabledBufferedValue() const { return myIsEnabledBufferedValue; };
 
   bool trigger() const;
 
   const QString myInModuleActionID;
+
+private:
+  double myMatchMetrics;
+  mutable bool myIsEnabledBufferedValue;
 };
 
 #endif // SUIT_FINDACTIONDIALOG_H
index 068414e3f57c43d55c4d3a1ba743940f58a46363..1204d32f806c23d3b8cc003f024814a4a4cad59c 100644 (file)
@@ -2046,33 +2046,35 @@ void SUIT_SentenceMatcher::setQuery(QString theQuery)
   }
 }
 
-size_t SUIT_SentenceMatcher::match(const QString& theInputString) const
+double SUIT_SentenceMatcher::match(const QString& theInputString) const
 {
-  size_t n = 0;
+  int n = 0;
   if (myUseExactWordOrder) {
     n = SUIT_SentenceMatcher::match(theInputString, myWords, myIsCaseSensitive);
-    if (n > 0)
-      return n;
-
-    if (myUseFuzzyWords) {
-      n = SUIT_SentenceMatcher::match(theInputString, myFuzzyWords, myIsCaseSensitive);
-      if (n > 0)
-        return n;
+    if (n != theInputString.length() && myUseFuzzyWords) {
+      const int nFuzzy = SUIT_SentenceMatcher::match(theInputString, myFuzzyWords, myIsCaseSensitive);
+      if (nFuzzy > n)
+        n = nFuzzy;
     }
   }
   else /* if match with permutated query sentences */ {
     n = SUIT_SentenceMatcher::match(theInputString, myPermutatedSentences, myIsCaseSensitive);
-    if (n > 0)
-      return n;
-
-    if (myUseFuzzyWords) {
-      n = SUIT_SentenceMatcher::match(theInputString, myFuzzyPermutatedSentences, myIsCaseSensitive);
-      if (n > 0)
-        return n;
+    if (n != theInputString.length() && myUseFuzzyWords) {
+      const int nFuzzy = SUIT_SentenceMatcher::match(theInputString, myFuzzyPermutatedSentences, myIsCaseSensitive);
+      if (nFuzzy > n)
+        n = nFuzzy;
     }
   }
 
-  return n;
+  if (n <= 0)
+    return std::numeric_limits<double>::infinity();
+
+  const auto strLength = theInputString.length() > myQuery.length() ? theInputString.length() : myQuery.length();
+
+  if (n > strLength)
+    return 0; // Exact match or almost exact.
+
+  return double(strLength - n);
 }
 
 QString SUIT_SentenceMatcher::toString() const
@@ -2138,70 +2140,100 @@ QString SUIT_SentenceMatcher::toString() const
   }
 }
 
-/*static*/ size_t SUIT_SentenceMatcher::matchWithSentenceIgnoreEndings(const QString& theInputString, const QStringList& theSentence, bool theCaseSensitive)
+/*static*/ int SUIT_SentenceMatcher::matchWithSentenceIgnoreEndings(const QString& theInputString, const QStringList& theSentence, bool theCaseSensitive)
 {
-  QRegExp regExp("^" + theSentence.join("\\w*\\W+"), theCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive);
-  if (theInputString.contains(regExp))
-    return theSentence.size();
-  else
-    return 0;
+  const QRegExp regExp("^" + theSentence.join("\\w*\\W+"), theCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive);
+  regExp.indexIn(theInputString);
+  const int matchMetrics = regExp.matchedLength();
+  return matchMetrics > 0 ? matchMetrics : 0;
 }
 
-/*static*/ size_t SUIT_SentenceMatcher::matchWithSentencesIgnoreEndings(const QString& theInputString, const QList<QStringList>& theSentences, bool theCaseSensitive)
+/*static*/ int SUIT_SentenceMatcher::matchWithSentencesIgnoreEndings(const QString& theInputString, const QList<QStringList>& theSentences, bool theCaseSensitive)
 {
+  int res = 0;
   for (const QStringList& sentence : theSentences) {
-    if (SUIT_SentenceMatcher::matchWithSentenceIgnoreEndings(theInputString, sentence, theCaseSensitive))
-      return sentence.size();
+    const int matchMetrics = SUIT_SentenceMatcher::matchWithSentenceIgnoreEndings(theInputString, sentence, theCaseSensitive);
+    if (matchMetrics > res) {
+      res = matchMetrics;
+      if (res == theInputString.length())
+        return res;
+    }
   }
-  return 0;
+  return res;
 }
 
-/*static*/ size_t SUIT_SentenceMatcher::matchAtLeastOneWord(const QString& theInputString, const QStringList& theWords, bool theCaseSensitive)
+/*static*/ int SUIT_SentenceMatcher::matchAtLeastOneWord(const QString& theInputString, const QStringList& theWords, bool theCaseSensitive)
 {
-  size_t n = 0;
+  int res = 0;
   for (const QString& word : theWords) {
+    const auto regExp = QRegExp(word, theCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive);
+    regExp.indexIn(theInputString);
+    const int matchMetrics = regExp.matchedLength();
     // The same input word can be counted multiple times. Nobody cares.
-    if (theInputString.contains(QRegExp(word, theCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive)))
-      n++;
+    if (matchMetrics > 0)
+      res += matchMetrics;
   }
-  return n;
+  return res;
 }
 
-/*static*/ size_t SUIT_SentenceMatcher::match(
+/*static*/ int SUIT_SentenceMatcher::match(
   const QString& theInputString,
   const QStringList& theSentence,
   bool theCaseSensitive
 ) {
-  size_t n = SUIT_SentenceMatcher::matchWithSentenceIgnoreEndings(theInputString, theSentence, theCaseSensitive);
-  if (n > 0)
-    return n;
+  int res = SUIT_SentenceMatcher::matchWithSentenceIgnoreEndings(theInputString, theSentence, theCaseSensitive);
+  if (res == theInputString.length())
+    return res;
 
-  return SUIT_SentenceMatcher::matchAtLeastOneWord(theInputString, theSentence, theCaseSensitive);
+  const int matchMetrics = SUIT_SentenceMatcher::matchAtLeastOneWord(theInputString, theSentence, theCaseSensitive);
+  if (matchMetrics > res)
+    res = matchMetrics;
+
+  return res;
 }
 
-/*static*/ size_t SUIT_SentenceMatcher::match(
+/*static*/ int SUIT_SentenceMatcher::match(
   const QString& theInputString,
   const QList<QStringList>& theSentences,
   bool theCaseSensitive
 ) {
-  size_t n = SUIT_SentenceMatcher::matchWithSentencesIgnoreEndings(theInputString, theSentences, theCaseSensitive);
-  if (n > 0)
-    return n;
+  int res = SUIT_SentenceMatcher::matchWithSentencesIgnoreEndings(theInputString, theSentences, theCaseSensitive);
+  if (res == theInputString.length())
+    return res;
 
-  if (theSentences.size() > 0)
-    return SUIT_SentenceMatcher::matchAtLeastOneWord(theInputString, theSentences[0], theCaseSensitive);
-  else
-    return 0;
+  if (theSentences.size() > 0) {
+    const int matchMetrics = SUIT_SentenceMatcher::matchAtLeastOneWord(theInputString, theSentences[0], theCaseSensitive);
+    if (matchMetrics > res)
+      res = matchMetrics;
+  }
+
+  return res;
 }
 
 
-SUIT_ActionSearcher::AssetsAndSearchData::AssetsAndSearchData(std::shared_ptr<SUIT_ActionAssets> theAssets, size_t theNumOfMatchingWords)
-: myAssets(theAssets), myNumOfMatchingWords(theNumOfMatchingWords)
-{}
+SUIT_ActionSearcher::AssetsAndSearchData::AssetsAndSearchData(std::shared_ptr<const SUIT_ActionAssets> theAssets, double theMatchMetrics)
+: myAssets(theAssets), myMatchMetrics(theMatchMetrics)
+{
+  if (theMatchMetrics < 0) {
+    myMatchMetrics = std::numeric_limits<double>::infinity();
+    ShCutDbg("WARNING: SUIT_ActionSearcher::AssetsAndSearchData: match metrics < 0. INF is assigned instead.");
+  }
+}
+
+void SUIT_ActionSearcher::AssetsAndSearchData::setMatchMetrics(double theMatchMetrics)
+{
+  if (theMatchMetrics < 0) {
+    myMatchMetrics = std::numeric_limits<double>::infinity();
+    ShCutDbg("WARNING: SUIT_ActionSearcher::AssetsAndSearchData: match metrics < 0. INF is assigned instead.");
+    return;
+  }
+
+  myMatchMetrics = theMatchMetrics;
+}
 
 void SUIT_ActionSearcher::AssetsAndSearchData::toJSON(QJsonObject& oJsonObject) const
 {
-  oJsonObject["myNumOfMatchingWords"] = int(myNumOfMatchingWords);
+  oJsonObject["myMatchMetrics"] = myMatchMetrics;
 
   if (myAssets) {
     QJsonObject assetsJSON;
@@ -2261,9 +2293,9 @@ bool SUIT_ActionSearcher::setIncludedModuleIDs(std::set<QString> theIncludedModu
 
     for (const auto& actionIDAndAssets : actionIDsAndAssets) {
       const QString& inModuleActionID = actionIDAndAssets.first;
-      const size_t n = matchAction(moduleID, inModuleActionID, actionIDAndAssets.second);
-      if (n > 0) {
-        mySearchResults[moduleID][inModuleActionID] = SUIT_ActionSearcher::AssetsAndSearchData(actionIDAndAssets.second, n);
+      const double matchMetrics = matchAction(moduleID, inModuleActionID, actionIDAndAssets.second);
+      if (matchMetrics < std::numeric_limits<double>::infinity()) {
+        mySearchResults[moduleID][inModuleActionID] = SUIT_ActionSearcher::AssetsAndSearchData(actionIDAndAssets.second, matchMetrics);
         res = true;
       }
     }
@@ -2392,10 +2424,10 @@ std::pair<bool, bool> SUIT_ActionSearcher::filter()
         if (itFoundActionIDAndAssets != foundActionIDsAndAssets.end()) {
           // Action is already in search results.
           SUIT_ActionSearcher::AssetsAndSearchData& aAndD = itFoundActionIDAndAssets->second;
-          const size_t n = matchAction(moduleID, inModuleActionID, aAndD.myAssets);
-          if (n > 0) {
-            if (n != aAndD.myNumOfMatchingWords) {
-              aAndD.myNumOfMatchingWords = n;
+          const double matchMetrics = matchAction(moduleID, inModuleActionID, aAndD.myAssets);
+          if (matchMetrics < std::numeric_limits<double>::infinity()) {
+            if (matchMetrics != aAndD.matchMetrics()) {
+              aAndD.setMatchMetrics(matchMetrics);
               res.second = true;
             }
           }
@@ -2407,12 +2439,12 @@ std::pair<bool, bool> SUIT_ActionSearcher::filter()
         }
       }
 
-      const size_t n = matchAction(moduleID, inModuleActionID, actionIDAndAssets.second);
-      if (n > 0) {
+      const double matchMetrics = matchAction(moduleID, inModuleActionID, actionIDAndAssets.second);
+      if (matchMetrics < std::numeric_limits<double>::infinity()) {
         if (itFoundModuleIDAndAssets == mySearchResults.end())
           itFoundModuleIDAndAssets = mySearchResults.emplace(moduleID, std::map<QString, SUIT_ActionSearcher::AssetsAndSearchData>()).first;
 
-        itFoundModuleIDAndAssets->second.emplace(inModuleActionID, SUIT_ActionSearcher::AssetsAndSearchData(actionIDAndAssets.second, n));
+        itFoundModuleIDAndAssets->second.emplace(inModuleActionID, SUIT_ActionSearcher::AssetsAndSearchData(actionIDAndAssets.second, matchMetrics));
         res.first = true;
       }
     }
@@ -2431,14 +2463,14 @@ std::pair<bool, bool> SUIT_ActionSearcher::filterResults()
     for (auto itActionIDAndAssets = actionIDsAndAssets.begin(); itActionIDAndAssets != actionIDsAndAssets.end(); ) {
       const QString& inModuleActionID = itActionIDAndAssets->first;
       SUIT_ActionSearcher::AssetsAndSearchData& assetsAndSearchData = itActionIDAndAssets->second;
-      const size_t n = matchAction(moduleID, inModuleActionID, assetsAndSearchData.myAssets);
-      if (n == 0) {
+      const double matchMetrics = matchAction(moduleID, inModuleActionID, assetsAndSearchData.myAssets);
+      if (matchMetrics == std::numeric_limits<double>::infinity()) {
         itActionIDAndAssets = actionIDsAndAssets.erase(itActionIDAndAssets);
         res.first = true;
       }
       else {
-        if (assetsAndSearchData.myNumOfMatchingWords != n) {
-          assetsAndSearchData.myNumOfMatchingWords = n;
+        if (assetsAndSearchData.matchMetrics() != matchMetrics) {
+          assetsAndSearchData.setMatchMetrics(matchMetrics);
           res.second = true;
         }
         itActionIDAndAssets++;
@@ -2477,13 +2509,13 @@ bool SUIT_ActionSearcher::extendResults()
       }
 
       ShCutDbg() && ShCutDbg("SUIT_ActionSearcher::extendResults(): " + moduleID + "/" + inModuleActionID + "." );
-      const size_t n = matchAction(moduleID, inModuleActionID, actionIDAndAssets.second);
-      if (n > 0) {
-        ShCutDbg("SUIT_ActionSearcher::extendResults(): match");
+      const double matchMetrics = matchAction(moduleID, inModuleActionID, actionIDAndAssets.second);
+      if (matchMetrics < std::numeric_limits<double>::infinity()) {
+        ShCutDbg("SUIT_ActionSearcher::extendResults(): match, metrics = " + QString::fromStdString(std::to_string(matchMetrics)));
         if (itFoundModuleIDAndAssets == mySearchResults.end())
           itFoundModuleIDAndAssets = mySearchResults.emplace(moduleID, std::map<QString, SUIT_ActionSearcher::AssetsAndSearchData>()).first;
 
-        itFoundModuleIDAndAssets->second.emplace(inModuleActionID, SUIT_ActionSearcher::AssetsAndSearchData(actionIDAndAssets.second, n));
+        itFoundModuleIDAndAssets->second.emplace(inModuleActionID, SUIT_ActionSearcher::AssetsAndSearchData(actionIDAndAssets.second, matchMetrics));
         res = true;
       }
     }
@@ -2491,44 +2523,50 @@ bool SUIT_ActionSearcher::extendResults()
   return res;
 }
 
-size_t SUIT_ActionSearcher::matchAction(const QString& theModuleID, const QString& theInModuleActionID, std::shared_ptr<SUIT_ActionAssets> theAssets)
+double SUIT_ActionSearcher::matchAction(const QString& theModuleID, const QString& theInModuleActionID, std::shared_ptr<const SUIT_ActionAssets> theAssets)
 {
   if (!theAssets) {
     ShCutDbg("WARNING: SUIT_ActionSearcher::matchAction: theAssets is nullptr.");
-    return 0;
+    return std::numeric_limits<double>::infinity();
   }
 
   if (!myIncludeDisabledActions) {
     const auto& actions = SUIT_ShortcutMgr::get()->getActions(theModuleID, theInModuleActionID);
     const bool actionEnabled = std::find_if(actions.begin(), actions.end(), [](const QAction* const theAction){ return theAction->isEnabled(); } ) != actions.end();
     if (!actionEnabled)
-      return false;
+      return std::numeric_limits<double>::infinity();
   }
 
+  double res = std::numeric_limits<double>::infinity();
+
   for (const auto& langAndLDA : theAssets->myLangDependentAssets) {
     if (myFieldsToMatch.find(SUIT_ActionSearcher::MatchField::ToolTip) != myFieldsToMatch.end()) {
-      if (myMatcher.match(langAndLDA.second.myToolTip))
-        return true;
+      const double matchMetrics = myMatcher.match(langAndLDA.second.myToolTip);
+      if (matchMetrics < res)
+        res = matchMetrics;
     }
 
     if (myFieldsToMatch.find(SUIT_ActionSearcher::MatchField::Name) != myFieldsToMatch.end()) {
-      if (myMatcher.match(langAndLDA.second.myName))
-        return true;
+      const double matchMetrics = myMatcher.match(langAndLDA.second.myName);
+      if (matchMetrics < res)
+        res = matchMetrics;
     }
   }
 
   if (myFieldsToMatch.find(SUIT_ActionSearcher::MatchField::ID) != myFieldsToMatch.end()) {
-    if (myMatcher.match(SUIT_ShortcutMgr::makeActionID(theModuleID, theInModuleActionID)))
-      return true;
+    const double matchMetrics = myMatcher.match(SUIT_ShortcutMgr::makeActionID(theModuleID, theInModuleActionID));
+    if (matchMetrics < res)
+        res = matchMetrics;
   }
 
   if (myFieldsToMatch.find(SUIT_ActionSearcher::MatchField::KeySequence) != myFieldsToMatch.end()) {
     const QString keySequence = SUIT_ShortcutMgr::get()->getKeySequence(theModuleID, theInModuleActionID).toString();
-    if (myMatcher.match(keySequence))
-      return true;
+    const double matchMetrics = myMatcher.match(keySequence);
+    if (matchMetrics < res)
+        res = matchMetrics;
   }
 
-  return false;
+  return res;
 }
 
 QString SUIT_ActionSearcher::toString() const
index fddc4c419b78c0fc0bc5901af6e52083ceb8ac62..f6682c5f350a9f60e316200c33d2aa7a3644469f 100644 (file)
@@ -33,6 +33,7 @@
 #include <set>
 #include <memory>
 #include <utility>
+#include <limits>
 
 class QAction;
 class QtxAction;
@@ -288,7 +289,7 @@ public:
   \returns {assetsExist, assets}. */
   static std::pair<bool, SUIT_ActionAssets> getActionAssetsFromResources(const QString& theActionID);
 
-  /*! \returns Language being set in resource manager. */
+  /*! \returns Language, which is set in resource manager. */
   static QString getLang();
 
 
@@ -360,8 +361,12 @@ public:
   if the module is root (theModuleID is empty) - returns all module IDs, otherwise returns ["", theModuleID]. */
   std::set<QString> getIDsOfInterferingModules(const QString& theModuleID) const;
 
+  /*! \returns assets, which describe module's header, not its content. */
   std::shared_ptr<const SUIT_ActionAssets> getModuleAssets(const QString& theModuleID) const;
 
+  /*! \returns assets, which describe modules' headers, not their content. */
+  std::map<QString, std::shared_ptr<SUIT_ActionAssets>> getModuleAssets() const { return myModuleAssets; }
+
   /*! \brief Retrieves module name, if the asset was loaded using \ref setAssetsFromResources(). If theLang is empty, it is effectively current language. */
   QString getModuleName(const QString& theModuleID, const QString& theLang = "") const;
 
@@ -371,8 +376,6 @@ public:
 
   std::map<QString, std::map<QString, std::shared_ptr<SUIT_ActionAssets>>> getActionAssets() const { return myActionAssets; }
 
-  std::map<QString, std::shared_ptr<SUIT_ActionAssets>> getModuleAssets() const { return myModuleAssets; }
-
   /*! \brief Retrieves action name, if the asset was loaded using \ref setAssetsFromResources(). If theLang is empty, it is effectively current language. */
   QString getActionName(const QString& theModuleID, const QString& theInModuleActionID, const QString& theLang = "") const;
 
@@ -502,27 +505,34 @@ public:
 
   inline const QString& getQuery() const { return myQuery; };
 
-  /*! \returns number of matched words. */
-  size_t match(const QString& theInputString) const;
+  /*! \returns match metrics. The metrics >= 0. INF means mismatch.
+  The class is unable to differentiate exact match with some approximate matches! */
+  double match(const QString& theInputString) const;
 
+  /** \brief For debug. */
   QString toString() const;
 
 private:
   static bool makePermutatedSentences(const QStringList& theWords, QList<QStringList>& theSentences);
   static void makeFuzzyWords(const QStringList& theWords, QStringList& theFuzzyWords);
 
-  static size_t matchWithSentenceIgnoreEndings(const QString& theInputString, const QStringList& theSentence, bool theCaseSensitive);
-  static size_t matchWithSentencesIgnoreEndings(const QString& theInputString, const QList<QStringList>& theSentences, bool theCaseSensitive);
+  /*! \returns number of characters in matched words. The number >= 0. */
+  static int matchWithSentenceIgnoreEndings(const QString& theInputString, const QStringList& theSentence, bool theCaseSensitive);
+  /*! \returns number of characters in matched words. The number >= 0. */
+  static int matchWithSentencesIgnoreEndings(const QString& theInputString, const QList<QStringList>& theSentences, bool theCaseSensitive);
 
-  static size_t matchAtLeastOneWord(const QString& theInputString, const QStringList& theWords, bool theCaseSensitive);
+  /*! \returns number of characters in matched words. The number >= 0. */
+  static int matchAtLeastOneWord(const QString& theInputString, const QStringList& theWords, bool theCaseSensitive);
 
-  static size_t match(
+  /*! \returns number of characters in matched words. The number >= 0. */
+  static int match(
     const QString& theInputString,
     const QStringList& theSentence,
     bool theCaseSensitive
   );
 
-  static size_t match(
+  /*! \returns number of characters in matched words. The number >= 0. */
+  static int match(
     const QString& theInputString,
     const QList<QStringList>& theSentences,
     bool theCaseSensitive
@@ -558,16 +568,22 @@ public:
     KeySequence
   };
 
-  struct AssetsAndSearchData
+  class AssetsAndSearchData
   {
-    AssetsAndSearchData() = default;
-    AssetsAndSearchData(std::shared_ptr<SUIT_ActionAssets> theAssets, size_t theNumOfMatchingWords);
+  public:
+    AssetsAndSearchData(std::shared_ptr<const SUIT_ActionAssets> theAssets = nullptr, double theMatchMetrics = std::numeric_limits<double>::infinity());
+
+    void setMatchMetrics(double theMatchMetrics);
+    double matchMetrics() const { return myMatchMetrics; };
 
-    std::shared_ptr<SUIT_ActionAssets> myAssets;
-    size_t myNumOfMatchingWords;
+    std::shared_ptr<const SUIT_ActionAssets> myAssets;
 
     void toJSON(QJsonObject& oJsonObject) const;
     QString toString() const;
+
+  private:
+    /*! \brief Ideally it should be number of weighted character permutations. Now it is just a number of characters in unmatched words. */
+    double myMatchMetrics;
   };
 
   /*! Default config:
@@ -605,18 +621,18 @@ public:
 
 private:
   /*! \brief Applies filter to all actions, provided in asset files for SUIT_ShortcutMgr.
-  \returns { true, _ } if set of results is changed; { _ , true } if number of matching words is changed for at least one result. */
+  \returns { true, _ } if set of results is changed; { _ , true } if matching metrics is changed for at least one result. */
   std::pair<bool, bool> filter();
 
   /*! \brief Applies filter to search results only.
-  \returns { true, _ } if set of results is shrunk; { _ , true } if number of matching words is changed for at least one result. */
+  \returns { true, _ } if set of results is shrunk; { _ , true } if matching metrics is changed for at least one result. */
   std::pair<bool, bool> filterResults();
 
   /*! \brief Applies filter only to actions, which are not in search results.
   \returns True, if set of results is extended. */
   bool extendResults();
 
-  size_t matchAction(const QString& theModuleID, const QString& theInModuleActionID, std::shared_ptr<SUIT_ActionAssets> theAssets);
+  double matchAction(const QString& theModuleID, const QString& theInModuleActionID, std::shared_ptr<const SUIT_ActionAssets> theAssets);
 
   QString toString() const;
 
index d6ae07181afeb3c409665ca4a73e2a53b3d01a54..3e0c6288d66897f79866bb1c5c5a787744f843fa 100644 (file)
@@ -630,10 +630,8 @@ std::set<SUIT_ShortcutTreeItem*, std::function<bool(SUIT_ShortcutTreeItem*, SUIT
   static const QCollator collator;
   const std::function<bool(SUIT_ShortcutTreeItem*, SUIT_ShortcutTreeItem*)> comparator =
   [this, sortSchema, &collator](const SUIT_ShortcutTreeItem* theItemA, const SUIT_ShortcutTreeItem* theItemB) {
-    int res = 0;
     for (const auto& keyAndOrder : sortSchema) {
-      int res = 0;
-      res = collator.compare(theItemA->getValue(keyAndOrder.first), theItemB->getValue(keyAndOrder.first));
+      const int res = collator.compare(theItemA->getValue(keyAndOrder.first), theItemB->getValue(keyAndOrder.first));
       if (res != 0)
         return keyAndOrder.second == SUIT_ShortcutTree::SortOrder::Ascending ? res < 0 : res > 0;
     }
index e522b64db8362107e38ab3042a2208ebdb227bd8..b51d9b193d3731182ea08369a88bf655a7021c8c 100644 (file)
@@ -71,9 +71,9 @@
 #include <SUIT_ViewManager.h>
 #include <SUIT_ViewModel.h>
 #include <SUIT_OverrideCursor.h>
+#include <SUIT_FindActionDialog.h>
 
 #include <QtxTreeView.h>
-#include <SUIT_FindActionDialog.h>
 
 #include <SALOME_EventFilter.h>