Salome HOME
[bos #40644][CEA](2024-T1) Feature search.
[modules/gui.git] / src / SUIT / SUIT_FindActionDialog.cxx
diff --git a/src/SUIT/SUIT_FindActionDialog.cxx b/src/SUIT/SUIT_FindActionDialog.cxx
new file mode 100644 (file)
index 0000000..1d07958
--- /dev/null
@@ -0,0 +1,557 @@
+// Copyright (C) 2007-2024  CEA, EDF, OPEN CASCADE
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#include "SUIT_FindActionDialog.h"
+
+#include <QAction>
+#include <QWidget>
+#include <QLayout>
+#include <QList>
+#include <QMap>
+#include <QApplication>
+
+#include <QCollator>
+
+#include <QCheckBox>
+#include <QLineEdit>
+#include <QBrush>
+#include <QColor>
+#include <QHeaderView>
+#include <QKeyEvent>
+
+#include <algorithm>
+#include <limits>
+
+
+SUIT_FindActionDialog::SUIT_FindActionDialog(QWidget* theParent)
+: QDialog(theParent)
+{
+  setMinimumWidth(500);
+  setWindowTitle(tr("Find action"));
+  QVBoxLayout* layout = new QVBoxLayout(this);
+
+  myQueryLineEdit = new QLineEdit(this);
+  layout->addWidget(myQueryLineEdit);
+  myQueryLineEdit->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
+  setFocusProxy(myQueryLineEdit);
+
+  QHBoxLayout* searchOptionsLayout = new QHBoxLayout(this);
+  layout->addLayout(searchOptionsLayout);
+  myIncludeUnavailableActionsCB = new QCheckBox(tr("Unavailable actions"), this);
+  myIncludeUnavailableActionsCB->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+  myIncludeUnavailableActionsCB->setCheckState(Qt::CheckState::Checked);
+  myActionSearcher.includeDisabledActions(true);
+  myIncludeInactiveModulesCB = new QCheckBox(tr("Inactive modules"), this);
+  myIncludeInactiveModulesCB->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+  myIncludeInactiveModulesCB->setCheckState(Qt::CheckState::Unchecked);
+  searchOptionsLayout->addWidget(myIncludeUnavailableActionsCB);
+  searchOptionsLayout->addWidget(myIncludeInactiveModulesCB);
+
+  myFoundActionsTree = new SUIT_FoundActionTree(this);
+  layout->addWidget(myFoundActionsTree);
+
+  connect(myQueryLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(onQueryChanged(const QString&)));
+  connect(myIncludeUnavailableActionsCB, SIGNAL(stateChanged(int)), this, SLOT(onSearchOptionUnavailableActionsChanged(int)));
+  connect(myIncludeInactiveModulesCB, SIGNAL(stateChanged(int)), this, SLOT(onSearchOptionInactiveModulesChanged(int)));
+
+  myQueryLineEdit->installEventFilter(myFoundActionsTree);
+}
+
+void SUIT_FindActionDialog::setActiveModuleID(const QString& theModuleID)
+{
+  myActiveModuleID = theModuleID;
+  if(myActionSearcher.setIncludedModuleIDs(std::set<QString>({SUIT_ShortcutMgr::ROOT_MODULE_ID, myActiveModuleID})))
+    updateUI();
+}
+
+void SUIT_FindActionDialog::onQueryChanged(const QString& theQuery)
+{
+  if (myActionSearcher.setQuery(theQuery))
+    updateUI();
+}
+
+void SUIT_FindActionDialog::onSearchOptionUnavailableActionsChanged(int theState)
+{
+  if (myActionSearcher.includeDisabledActions(theState == Qt::CheckState::Checked))
+    updateUI();
+}
+
+void SUIT_FindActionDialog::onSearchOptionInactiveModulesChanged(int theState)
+{
+  bool resultsChanged = false;
+  if (theState == Qt::CheckState::Checked) {
+    myIncludeUnavailableActionsCB->setDisabled(true);
+    myIncludeUnavailableActionsCB->setCheckState(Qt::CheckState::Checked);
+    resultsChanged = myActionSearcher.setIncludedModuleIDs(SUIT_ShortcutMgr::get()->getShortcutContainer().getIDsOfAllModules());
+  }
+  else {
+    myIncludeUnavailableActionsCB->setDisabled(false);
+    resultsChanged = myActionSearcher.setIncludedModuleIDs(std::set<QString>({SUIT_ShortcutMgr::ROOT_MODULE_ID, myActiveModuleID}));
+  }
+
+  if (resultsChanged)
+    updateUI();
+}
+
+void SUIT_FindActionDialog::updateUI()
+{
+  myFoundActionsTree->updateItems(myActionSearcher.getSearchResults());
+}
+
+
+
+SUIT_FoundActionTree::SUIT_FoundActionTree(SUIT_FindActionDialog* theParent)
+: QTreeWidget(theParent)
+{
+  setColumnCount(2);
+  setSelectionMode(QAbstractItemView::SingleSelection);
+  setSortingEnabled(false);
+  header()->setSectionResizeMode(QHeaderView::Interactive);
+  {
+    QMap<int, QString> labelMap;
+    labelMap[SUIT_FoundActionTree::ElementIdx::Name]    = SUIT_FindActionDialog::tr("Action");
+    labelMap[SUIT_FoundActionTree::ElementIdx::ToolTip] = SUIT_FindActionDialog::tr("Description");
+    setHeaderLabels(labelMap.values());
+  }
+  setExpandsOnDoubleClick(false); // Implemented manually.
+  setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents);
+  setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+
+  setColumnWidth(SUIT_FoundActionTree::ElementIdx::Name, 120);
+  setColumnWidth(SUIT_FoundActionTree::ElementIdx::Name, 250);
+  setMinimumHeight(300);
+
+  setWindowFlags(windowFlags() | Qt::FramelessWindowHint);
+
+  mySortKey = SUIT_FoundActionTree::SortKey::MatchMetrics;
+  mySortOrder = SUIT_FoundActionTree::SortOrder::Ascending;
+
+  connect(this, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), this, SLOT(onItemExecuted(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)
+{
+  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 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;
+
+      auto actionItem = SUIT_FoundActionTreeAction::create(moduleID, inModuleActionID);
+      if (!actionItem) {
+        ShCutDbg("SUIT_FoundActionTree can't create child item for action ID = \"" + SUIT_ShortcutMgr::makeActionID(moduleID, inModuleActionID) + "\".");
+        continue;
+      }
+
+      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);
+    }
+  }
+}
+
+void SUIT_FoundActionTree::keyPressEvent(QKeyEvent* theEvent)
+{
+  const auto key = theEvent->key();
+  const auto selectedItem = currentItem();
+  if ((key == Qt::Key_Enter || key == Qt::Key_Return) && selectedItem)
+    onItemExecuted(selectedItem, SUIT_FoundActionTree::ElementIdx::Name);
+  else
+    QTreeWidget::keyPressEvent(theEvent);
+}
+
+bool SUIT_FoundActionTree::eventFilter(QObject* theQObject, QEvent* theEvent)
+{
+  if (theEvent->type() == QEvent::KeyPress) {
+    QKeyEvent* const keyEvent = static_cast<QKeyEvent*>(theEvent);
+    const auto key = keyEvent->key();
+
+    if (key == Qt::Key_Enter || key == Qt::Key_Return || key == Qt::Key_Up || key == Qt::Key_Down) {
+      keyPressEvent(keyEvent);
+      return true;
+    }
+  }
+
+  return false;
+}
+
+std::pair<SUIT_FoundActionTreeModule*, int> SUIT_FoundActionTree::findModuleItem(const QString& theModuleID) const
+{
+  for (int moduleIdx = 0; moduleIdx < topLevelItemCount(); moduleIdx++) {
+    SUIT_FoundActionTreeModule* moduleItem = static_cast<SUIT_FoundActionTreeModule*>(topLevelItem(moduleIdx));
+    if (moduleItem->myModuleID == theModuleID)
+      return std::pair<SUIT_FoundActionTreeModule*, int>(moduleItem, moduleIdx);
+  }
+  return std::pair<SUIT_FoundActionTreeModule*, int>(nullptr, -1);
+}
+
+template <typename Float>
+bool approximatelyEqual(Float a, Float b, Float relativeTol = std::numeric_limits<Float>::epsilon())
+{
+    return std::abs(a - b) <= ( (std::abs(a) < std::abs(b) ? std::abs(b) : std::abs(a)) * relativeTol);
+}
+
+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));
+  }
+
+  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) {
+        if (!approximatelyEqual(matchMetricsA, matchMetricsB)) {
+          const double res = matchMetricsA - matchMetricsB;
+          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::onItemExecuted(QTreeWidgetItem* theItem, int theColIdx)
+{
+  SUIT_FoundActionTreeItem* const item = static_cast<SUIT_FoundActionTreeItem*>(theItem);
+  if (item->type() == SUIT_FoundActionTreeItem::Type::Action) {
+    SUIT_FoundActionTreeAction* const actionItem = static_cast<SUIT_FoundActionTreeAction*>(theItem);
+    if (actionItem->trigger())
+      static_cast<SUIT_FindActionDialog*>(parentWidget())->accept();
+  }
+  else /* if (item->type() == SUIT_FoundActionTreeItem::Type::Module) */ {
+    item->setExpanded(!item->isExpanded());
+  }
+}
+
+/*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)
+{ }
+
+QString SUIT_FoundActionTreeItem::name() const
+{
+  return text(SUIT_FoundActionTree::ElementIdx::Name);
+}
+
+QString SUIT_FoundActionTreeItem::toolTip() const
+{
+  return text(SUIT_FoundActionTree::ElementIdx::ToolTip);
+}
+
+
+SUIT_FoundActionTreeModule::SUIT_FoundActionTreeModule(const QString& theModuleID)
+: SUIT_FoundActionTreeItem(theModuleID)
+{
+  QFont f = font(SUIT_FoundActionTree::ElementIdx::Name);
+  f.setBold(true);
+  setFont(SUIT_FoundActionTree::ElementIdx::Name, f);
+  setText(SUIT_FoundActionTree::ElementIdx::Name, theModuleID);
+}
+
+void SUIT_FoundActionTreeModule::setAssetsAndSearchData(const SUIT_ActionSearcher::AssetsAndSearchData& theAssetsAndSD, const QString& theLang)
+{
+  if (!theAssetsAndSD.myAssets)
+    return;
+
+  setIcon(SUIT_FoundActionTree::ElementIdx::Name, theAssetsAndSD.myAssets->myIcon);
+
+  const auto& ldaMap = theAssetsAndSD.myAssets->myLangDependentAssets;
+  if (ldaMap.empty()) {
+    setText(SUIT_FoundActionTree::ElementIdx::Name, myModuleID);
+    return;
+  }
+
+  auto itLDA = ldaMap.find(theLang);
+  if (itLDA == ldaMap.end())
+    itLDA = ldaMap.begin();
+
+  const SUIT_ActionAssets::LangDependentAssets& lda = itLDA->second;
+  const QString& name = lda.myName.isEmpty() ? myModuleID : lda.myName;
+  setText(SUIT_FoundActionTree::ElementIdx::Name, name);
+}
+
+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:
+      return name();
+    case SUIT_FoundActionTree::SortKey::ToolTip:
+      return toolTip();
+    default:
+      return QString();
+  }
+}
+
+bool SUIT_FoundActionTreeModule::isEnabled() const
+{
+  return true;
+}
+
+
+SUIT_FoundActionTreeAction::SUIT_FoundActionTreeAction(const QString& theModuleID, const QString& theInModuleActionID)
+: SUIT_FoundActionTreeItem(theModuleID), myInModuleActionID(theInModuleActionID),
+  myMatchMetrics(std::numeric_limits<double>::infinity()), myIsEnabledBufferedValue(false)
+{
+  setText(SUIT_FoundActionTree::ElementIdx::Name, theInModuleActionID);
+}
+
+/*static*/ SUIT_FoundActionTreeAction* SUIT_FoundActionTreeAction::create(const QString& theModuleID, const QString& theInModuleActionID)
+{
+  if (theInModuleActionID.isEmpty()) {
+    ShCutDbg("SUIT_FoundActionTreeItem: attempt to create item with empty action ID.");
+    return nullptr;
+  }
+
+  return new SUIT_FoundActionTreeAction(theModuleID, theInModuleActionID);
+}
+
+void SUIT_FoundActionTreeAction::setAssetsAndSearchData(const SUIT_ActionSearcher::AssetsAndSearchData& theAssetsAndSD, const QString& theLang)
+{
+  if (!theAssetsAndSD.myAssets)
+    return;
+
+  setIcon(SUIT_FoundActionTree::ElementIdx::Name, theAssetsAndSD.myAssets->myIcon);
+
+  const auto& ldaMap = theAssetsAndSD.myAssets->myLangDependentAssets;
+  if (ldaMap.empty()) {
+    setText(SUIT_FoundActionTree::ElementIdx::Name, myInModuleActionID);
+    return;
+  }
+
+  auto itLDA = ldaMap.find(theLang);
+  if (itLDA == ldaMap.end())
+    itLDA = ldaMap.begin();
+
+  const SUIT_ActionAssets::LangDependentAssets& lda = itLDA->second;
+  const QString& name = lda.myName.isEmpty() ? myInModuleActionID : lda.myName;
+  setText(SUIT_FoundActionTree::ElementIdx::Name, name);
+
+  setText(SUIT_FoundActionTree::ElementIdx::ToolTip, lda.myToolTip);
+
+  if (isEnabled()) {
+    setToolTip(
+      SUIT_FoundActionTree::ElementIdx::Name,
+      SUIT_FoundActionTree::tr("Double click to start")
+    );
+
+    setToolTip(
+      SUIT_FoundActionTree::ElementIdx::ToolTip,
+      SUIT_FoundActionTree::tr("Double click to start")
+    );
+  }
+  else {
+    static const QBrush greyedOutBrush = QBrush(Qt::gray);
+    setForeground(SUIT_FoundActionTree::ElementIdx::Name,    greyedOutBrush);
+    setForeground(SUIT_FoundActionTree::ElementIdx::ToolTip, greyedOutBrush);
+  }
+
+  myMatchMetrics = theAssetsAndSD.matchMetrics();
+}
+
+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:
+      return name();
+    case SUIT_FoundActionTree::SortKey::ToolTip:
+      return toolTip();
+    default:
+      return QString();
+  }
+}
+
+bool SUIT_FoundActionTreeAction::isEnabled() const
+{
+  const auto& actions = SUIT_ShortcutMgr::get()->getActions(myModuleID, myInModuleActionID);
+  myIsEnabledBufferedValue = std::find_if(actions.begin(), actions.end(), [](const QAction* const theAction){ return theAction->isEnabled(); }) != actions.end();
+  return myIsEnabledBufferedValue;
+}
+
+bool SUIT_FoundActionTreeAction::trigger() const
+{
+  bool res = false;
+  const auto& actions = SUIT_ShortcutMgr::get()->getActions(myModuleID, myInModuleActionID);
+  for (const auto& action : actions) {
+    if (action->isEnabled()) {
+      action->trigger();
+      res = true;
+    }
+  }
+  return res;
+}
\ No newline at end of file