--- /dev/null
+// 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 "QtxFeatureSearch.h"
+
+#include <QAction>
+#include <QWidget>
+#include <QLayout>
+#include <QList>
+#include <QMap>
+
+#include <QCollator>
+
+#include <QCheckBox>
+#include <QLineEdit>
+#include <QBrush>
+#include <QColor>
+#include <QHeaderView>
+
+#include <algorithm>
+
+
+QtxFoundActionTree::QtxFoundActionTree()
+{}
+
+
+QtxFoundActionTreeItem::QtxFoundActionTreeItem(const QString& theModuleID)
+: QTreeWidgetItem(), myModuleID(theModuleID)
+{ }
+
+QString QtxFoundActionTreeItem::name() const
+{
+ return text(QtxFoundActionTree::ElementIdx::Name);
+}
+
+QString QtxFoundActionTreeItem::toolTip() const
+{
+ return text(QtxFoundActionTree::ElementIdx::ToolTip);
+}
+
+
+QtxFoundActionTreeFolder::QtxFoundActionTreeFolder(const QString& theModuleID)
+: QtxFoundActionTreeItem(theModuleID)
+{
+ QFont f = font(QtxFoundActionTree::ElementIdx::Name);
+ f.setBold(true);
+ setFont(QtxFoundActionTree::ElementIdx::Name, f);
+ setText(QtxFoundActionTree::ElementIdx::Name, theModuleID);
+}
+
+void QtxFoundActionTreeFolder::setAssets(std::shared_ptr<const SUIT_ActionAssets> theAssets, const QString& theLang)
+{
+ if (!theAssets)
+ return;
+
+ setIcon(QtxFoundActionTree::ElementIdx::Name, theAssets->myIcon);
+
+ const auto& ldaMap = theAssets->myLangDependentAssets;
+ if (ldaMap.empty()) {
+ setText(QtxFoundActionTree::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(QtxFoundActionTree::ElementIdx::Name, name);
+}
+
+QString QtxFoundActionTreeFolder::getValue(QtxFoundActionTree::SortKey theKey) const
+{
+ switch (theKey) {
+ case QtxFoundActionTree::SortKey::ID:
+ return myModuleID;
+ case QtxFoundActionTree::SortKey::Name:
+ return name();
+ case QtxFoundActionTree::SortKey::ToolTip:
+ return toolTip();
+ default:
+ return QString();
+ }
+}
+
+bool QtxFoundActionTreeFolder::isEnabled() const
+{
+ return true;
+}
+
+
+QtxFoundActionTreeAction::QtxFoundActionTreeAction(const QString& theModuleID, const QString& theInModuleActionID)
+: QtxFoundActionTreeItem(theModuleID), myInModuleActionID(theInModuleActionID)
+{
+ setText(QtxFoundActionTree::ElementIdx::Name, theInModuleActionID);
+}
+
+/*static*/ QtxFoundActionTreeAction* QtxFoundActionTreeAction::create(const QString& theModuleID, const QString& theInModuleActionID)
+{
+ if (theInModuleActionID.isEmpty()) {
+ ShCutDbg("QtxFoundActionTreeItem: attempt to create item with empty action ID.");
+ return nullptr;
+ }
+
+ return new QtxFoundActionTreeAction(theModuleID, theInModuleActionID);
+}
+
+void QtxFoundActionTreeAction::setAssets(std::shared_ptr<const SUIT_ActionAssets> theAssets, const QString& theLang)
+{
+ if (!theAssets)
+ return;
+
+ setIcon(QtxFoundActionTree::ElementIdx::Name, theAssets->myIcon);
+
+ const auto& ldaMap = theAssets->myLangDependentAssets;
+ if (ldaMap.empty()) {
+ setText(QtxFoundActionTree::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(QtxFoundActionTree::ElementIdx::Name, name);
+
+ const QString& actionToolTip = lda.myToolTip.isEmpty() ? name : lda.myToolTip;
+ setText(QtxFoundActionTree::ElementIdx::ToolTip, actionToolTip);
+
+ if (isEnabled()) {
+ setToolTip(
+ QtxFoundActionTree::ElementIdx::Name,
+ QtxFoundActionTree::tr("Double click to start")
+ );
+
+ setToolTip(
+ QtxFoundActionTree::ElementIdx::ToolTip,
+ QtxFoundActionTree::tr("Double click to start")
+ );
+ }
+ else {
+ static const QBrush greyedOutBrush = QBrush(Qt::gray);
+ setForeground(QtxFoundActionTree::ElementIdx::Name, greyedOutBrush);
+ setForeground(QtxFoundActionTree::ElementIdx::ToolTip, greyedOutBrush);
+ }
+}
+
+QString QtxFoundActionTreeAction::getValue(QtxFoundActionTree::SortKey theKey) const
+{
+ switch (theKey) {
+ case QtxFoundActionTree::SortKey::ID:
+ return myInModuleActionID;
+ case QtxFoundActionTree::SortKey::Name:
+ return name();
+ case QtxFoundActionTree::SortKey::ToolTip:
+ return toolTip();
+ default:
+ return QString();
+ }
+}
+
+bool QtxFoundActionTreeAction::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();
+}
+
+void QtxFoundActionTreeAction::trigger() const
+{
+ const auto& actions = SUIT_ShortcutMgr::get()->getActions(myModuleID, myInModuleActionID);
+ for (const auto& action : actions) {
+ if (action->isEnabled())
+ action->trigger();
+ }
+}
+
+
+QtxFeatureSearchDialog::QtxFeatureSearchDialog(QWidget* theParent)
+: QDialog(theParent)
+{
+ setMinimumWidth(500);
+ setWindowTitle(tr("Search 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);
+ 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 QtxFoundActionTree();
+ layout->addWidget(myFoundActionsTree);
+
+ connect(myQueryLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(onQueryChanged(const QString&)));
+ connect(myIncludeUnavailableActionsCB, SIGNAL(stateChanged(Qt::CheckState)), this, SLOT(onSearchOptionUnavailableActionsChanged(Qt::CheckState)));
+ connect(myIncludeInactiveModulesCB, SIGNAL(stateChanged(Qt::CheckState)), this, SLOT(onSearchOptionInactiveModulesChanged(Qt::CheckState)));
+}
+
+void QtxFeatureSearchDialog::setActiveModuleID(const QString& theModuleID)
+{
+ if(myActionSearcher.setIncludedModuleIDs(std::set<QString>({SUIT_ShortcutMgr::ROOT_MODULE_ID, myActiveModuleID})))
+ updateUI();
+}
+
+void QtxFeatureSearchDialog::onQueryChanged(const QString& theQuery)
+{
+ if (myActionSearcher.setQuery(theQuery))
+ updateUI();
+}
+
+void QtxFeatureSearchDialog::onSearchOptionUnavailableActionsChanged(Qt::CheckState theState)
+{
+ if (myActionSearcher.includeDisabledActions(theState == Qt::CheckState::Checked))
+ updateUI();
+}
+
+void QtxFeatureSearchDialog::onSearchOptionInactiveModulesChanged(Qt::CheckState theState)
+{
+ bool resultsChanged = false;
+ if (theState == Qt::CheckState::Checked)
+ resultsChanged = myActionSearcher.setIncludedModuleIDs(SUIT_ShortcutMgr::get()->getShortcutContainer().getIDsOfAllModules());
+ else
+ resultsChanged = myActionSearcher.setIncludedModuleIDs(std::set<QString>({SUIT_ShortcutMgr::ROOT_MODULE_ID, myActiveModuleID}));
+
+ if (resultsChanged)
+ updateUI();
+}
+
+void QtxFeatureSearchDialog::updateUI()
+{
+
+}
+
--- /dev/null
+// 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
+//
+
+#ifndef QTXFEATURESEARCH_H
+#define QTXFEATURESEARCH_H
+
+#include "Qtx.h"
+#include <QDialog>
+#include <QFrame>
+#include <QTreeWidget>
+#include "SUIT_ShortcutMgr.h"
+#include <memory>
+#include <map>
+#include <set>
+#include <functional>
+
+
+class QtxFoundActionTreeItem;
+class QtxFoundActionTreeFolder;
+class QtxFoundActionTreeAction;;
+
+
+class QTX_EXPORT QtxFoundActionTree : public QTreeWidget
+{
+ Q_OBJECT
+
+public:
+ enum ElementIdx {
+ Name = 0,
+ ToolTip = 1
+ };
+
+ enum class SortKey {
+ ID,
+ Name,
+ ToolTip
+ };
+
+ enum class SortOrder {
+ Ascending,
+ Descending
+ };
+
+ QtxFoundActionTree();
+ QtxFoundActionTree(const QtxFoundActionTree&) = delete;
+ QtxFoundActionTree& operator=(const QtxFoundActionTree&) = delete;
+ virtual ~QtxFoundActionTree() = default;
+
+ void sort(QtxFoundActionTree::SortKey theKey, QtxFoundActionTree::SortOrder theOrder);
+
+private:
+ void updateItems();
+ std::pair<QtxFoundActionTreeFolder*, int> findModuleFolderItem(const QString& theModuleID) const;
+
+ std::set<QtxFoundActionTreeItem*, std::function<bool(QtxFoundActionTreeItem*, QtxFoundActionTreeItem*)>> getSortedChildren(QtxFoundActionTreeFolder* theParentItem);
+
+ void insertChild(
+ QtxFoundActionTreeFolder* theParentItem,
+ std::set<QtxFoundActionTreeItem*, std::function<bool(QtxFoundActionTreeItem*, QtxFoundActionTreeItem*)>>& theSortedChildren,
+ QtxFoundActionTreeItem* theChildItem
+ );
+
+private slots:
+ void onItemDoubleClicked(QTreeWidgetItem* theWidgetItem, int theColIdx);
+
+public:
+ static const QList<std::pair<QtxFoundActionTree::SortKey, QtxFoundActionTree::SortOrder>> DEFAULT_SORT_SCHEMA;
+
+private:
+ QtxFoundActionTree::SortKey mySortKey;
+ QtxFoundActionTree::SortOrder mySortOrder;
+};
+
+
+class QtxFoundActionTreeItem : public QTreeWidgetItem
+{
+public:
+ enum Type {
+ Folder = 0,
+ Action = 1,
+ };
+
+protected:
+ QtxFoundActionTreeItem(const QString& theModuleID);
+
+public:
+ virtual ~QtxFoundActionTreeItem() = default;
+ virtual QtxFoundActionTreeItem::Type type() const = 0;
+
+ virtual void setAssets(std::shared_ptr<const SUIT_ActionAssets> theAssets, const QString& theLang) = 0;
+ QString name() const;
+ QString toolTip() const;
+
+ virtual QString getValue(QtxFoundActionTree::SortKey theKey) const = 0;
+
+ virtual bool isEnabled() const = 0;
+
+public:
+ const QString myModuleID;
+};
+
+
+class QtxFoundActionTreeFolder : public QtxFoundActionTreeItem
+{
+public:
+ QtxFoundActionTreeFolder(const QString& theModuleID);
+ virtual ~QtxFoundActionTreeFolder() = default;
+ virtual QtxFoundActionTreeItem::Type type() const { return QtxFoundActionTreeItem::Type::Folder; };
+
+ virtual void setAssets(std::shared_ptr<const SUIT_ActionAssets> theAssets, const QString& theLang);
+
+ virtual QString getValue(QtxFoundActionTree::SortKey theKey) const;
+
+ virtual bool isEnabled() const;
+};
+
+
+class QtxFoundActionTreeAction : public QtxFoundActionTreeItem
+{
+private:
+ QtxFoundActionTreeAction(const QString& theModuleID, const QString& theInModuleActionID);
+
+public:
+ static QtxFoundActionTreeAction* create(const QString& theModuleID, const QString& theInModuleActionID);
+ virtual ~QtxFoundActionTreeAction() = default;
+ virtual QtxFoundActionTreeItem::Type type() const { return QtxFoundActionTreeItem::Type::Action; };
+
+ virtual void setAssets(std::shared_ptr<const SUIT_ActionAssets> theAssets, const QString& theLang);
+
+ virtual QString getValue(QtxFoundActionTree::SortKey theKey) const;
+
+ virtual bool isEnabled() const;
+
+ void trigger() const;
+
+ const QString myInModuleActionID;
+};
+
+
+class QCheckBox;
+class QLineEdit;
+class QLabel;
+class QPushButton;
+
+
+class QTX_EXPORT QtxFeatureSearchDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ QtxFeatureSearchDialog(QWidget* theParent);
+ QtxFeatureSearchDialog(const QtxFeatureSearchDialog&) = delete;
+ QtxFeatureSearchDialog& operator=(const QtxFeatureSearchDialog&) = delete;
+ virtual ~QtxFeatureSearchDialog() = default;
+
+ void setActiveModuleID(const QString& theModuleID);
+
+private slots:
+ void onQueryChanged(const QString& theKeyword);
+ void onSearchOptionUnavailableActionsChanged(Qt::CheckState theState);
+ void onSearchOptionInactiveModulesChanged(Qt::CheckState theState);
+
+private:
+ void updateUI();
+
+ QLineEdit* myQueryLineEdit;
+ QCheckBox* myIncludeUnavailableActionsCB;
+ QCheckBox* myIncludeInactiveModulesCB;
+ QtxFoundActionTree* myFoundActionsTree;
+
+ QString myActiveModuleID;
+ SUIT_ActionSearcher myActionSearcher;
+};
+
+#endif // QTXFEATURESEARCH_H
static const QString NO_ACTION = QString("");
/** Separates tokens in action ID. */
static const QString TOKEN_SEPARATOR = QString("/");
-static const QString ROOT_MODULE_ID = QString("");
+/*static*/ const QString SUIT_ShortcutMgr::ROOT_MODULE_ID = QString("");
static const QString META_ACTION_PREFIX = QString("#");
/** Prefix of names of shortcut setting sections in preference files. */
moduleShortcuts[theInModuleActionID] = theKeySequence.toString();
const QString fileName = theModuleID + DevTools::SHORTCUTS_OF_META_SUFFIX;
- const QString sectionName = SECTION_NAME_PREFIX + DevTools::XML_SECTION_TOKENS_SEPARATOR + ROOT_MODULE_ID;
+ const QString sectionName = SECTION_NAME_PREFIX + DevTools::XML_SECTION_TOKENS_SEPARATOR + SUIT_ShortcutMgr::ROOT_MODULE_ID;
std::map<QString, std::map<QString, QString>> sections;
sections[sectionName] = moduleShortcuts;
writeToXMLFile(fileName, sections);
const QAction* theAction
) {
if (SUIT_ShortcutMgr::isInModuleMetaActionID(theInModuleActionID)) {
- QString actionID = SUIT_ShortcutMgr::makeActionID(ROOT_MODULE_ID, theInModuleActionID);
+ QString actionID = SUIT_ShortcutMgr::makeActionID(SUIT_ShortcutMgr::ROOT_MODULE_ID, theInModuleActionID);
// { actionID, assets } []
auto& moduleAssets = myAssetsOfMetaActions[theModuleID];
SUIT_ShortcutContainer::SUIT_ShortcutContainer()
{
- myShortcuts.emplace(ROOT_MODULE_ID, std::map<QKeySequence, QString>());
- myShortcutsInversed.emplace(ROOT_MODULE_ID, std::map<QString, QKeySequence>());
+ myShortcuts.emplace(SUIT_ShortcutMgr::ROOT_MODULE_ID, std::map<QKeySequence, QString>());
+ myShortcutsInversed.emplace(SUIT_ShortcutMgr::ROOT_MODULE_ID, std::map<QString, QKeySequence>());
}
std::set<QString> SUIT_ShortcutContainer::getIDsOfInterferingModules(const QString& theModuleID) const
{
std::set<QString> IDsOfInterferingModules;
- if (theModuleID == ROOT_MODULE_ID) {
+ if (theModuleID == SUIT_ShortcutMgr::ROOT_MODULE_ID) {
for (const auto& moduleIDAndShortcuts : myShortcuts) {
IDsOfInterferingModules.emplace(moduleIDAndShortcuts.first);
}
}
else {
- IDsOfInterferingModules.emplace(ROOT_MODULE_ID);
- if (theModuleID != ROOT_MODULE_ID)
+ IDsOfInterferingModules.emplace(SUIT_ShortcutMgr::ROOT_MODULE_ID);
+ if (theModuleID != SUIT_ShortcutMgr::ROOT_MODULE_ID)
IDsOfInterferingModules.emplace(theModuleID);
}
return IDsOfInterferingModules;
}
if (SUIT_ShortcutMgr::isInModuleMetaActionID(theInModuleActionID))
- theModuleID = ROOT_MODULE_ID;
+ theModuleID = SUIT_ShortcutMgr::ROOT_MODULE_ID;
auto itModuleShortcuts = myShortcuts.find(theModuleID);
auto itModuleShortcutsInversed = myShortcutsInversed.find(theModuleID);
return std::set<std::pair<QString, QString>>();
if (SUIT_ShortcutMgr::isInModuleMetaActionID(theInModuleActionID))
- theModuleID = ROOT_MODULE_ID;
+ theModuleID = SUIT_ShortcutMgr::ROOT_MODULE_ID;
{ // Check if the shortcut is set.
const auto itModuleShortcuts = myShortcuts.find(theModuleID);
const QKeySequence& SUIT_ShortcutContainer::getKeySequence(QString theModuleID, const QString& theInModuleActionID) const
{
if (SUIT_ShortcutMgr::isInModuleMetaActionID(theInModuleActionID))
- theModuleID = ROOT_MODULE_ID;
+ theModuleID = SUIT_ShortcutMgr::ROOT_MODULE_ID;
const auto itModuleShortcutsInversed = myShortcutsInversed.find(theModuleID);
if (itModuleShortcutsInversed == myShortcutsInversed.end())
bool SUIT_ShortcutContainer::hasShortcut(QString theModuleID, const QString& theInModuleActionID) const
{
if (SUIT_ShortcutMgr::isInModuleMetaActionID(theInModuleActionID))
- theModuleID = ROOT_MODULE_ID;
+ theModuleID = SUIT_ShortcutMgr::ROOT_MODULE_ID;
const auto itModuleShortcutsInversed = myShortcutsInversed.find(theModuleID);
if (itModuleShortcutsInversed == myShortcutsInversed.end())
else
ShCutDbg("Discovered shortcut modules: \"" + moduleIDs.join("\", \"") + ".");
}
- moduleIDs.push_front(ROOT_MODULE_ID); // Resource manager filters out empty section suffices.
+ moduleIDs.push_front(SUIT_ShortcutMgr::ROOT_MODULE_ID); // Resource manager filters out empty section suffices.
moduleIDs.removeDuplicates();
for (size_t i = 0; i < moduleIDs.size(); i++) {
if (
!SUIT_ShortcutMgr::isInModuleActionIDValid(inModuleActionID) ||
!keySequence.first ||
- SUIT_ShortcutMgr::isInModuleMetaActionID(inModuleActionID) && moduleID != ROOT_MODULE_ID
+ SUIT_ShortcutMgr::isInModuleMetaActionID(inModuleActionID) && moduleID != SUIT_ShortcutMgr::ROOT_MODULE_ID
) {
std::list<std::pair<QString, QString>>& moduleInvalidShortcuts = invalidShortcuts[moduleID];
moduleInvalidShortcuts.push_back(std::pair<QString, QString>(inModuleActionID, keySequenceString));
else {
ShCutDbg(
"Action with ID \"" +
- (SUIT_ShortcutMgr::isInModuleMetaActionID(inModuleActionID) ? ROOT_MODULE_ID + TOKEN_SEPARATOR + inModuleActionID : theActionID) +
+ (SUIT_ShortcutMgr::isInModuleMetaActionID(inModuleActionID) ? SUIT_ShortcutMgr::ROOT_MODULE_ID + TOKEN_SEPARATOR + inModuleActionID : theActionID) +
"\" is not added to default resource files."
);
auto conflicts = myShortcutContainer.setShortcut(moduleID, inModuleActionID, theAction->shortcut(), false);
std::shared_ptr<const SUIT_ActionAssets> SUIT_ShortcutMgr::getActionAssets(const QString& theActionID) const
{
- const auto it = myActionAssets.find(theActionID);
- if (it == myActionAssets.end())
+ const auto moduleIDAndActionID = SUIT_ShortcutMgr::splitIntoModuleIDAndInModuleID(theActionID);
+ const QString& moduleID = moduleIDAndActionID.first;
+ const QString& inModuleActionID = moduleIDAndActionID.second;
+
+ if (inModuleActionID.isEmpty()) {
+ ShCutDbg() && ShCutDbg("Attempt to get assets of an action with invalid ID \"" + theActionID + "\".");
return std::shared_ptr<const SUIT_ActionAssets>(nullptr);
- else
- return it->second;
+ }
+
+ const auto itModuleActionAssets = myActionAssets.find(moduleID);
+ if (itModuleActionAssets == myActionAssets.end())
+ return std::shared_ptr<const SUIT_ActionAssets>(nullptr);
+ else {
+ const auto moduleActionAssets = itModuleActionAssets->second;
+ const auto itActionAssets = moduleActionAssets.find(inModuleActionID);
+ if (itActionAssets == moduleActionAssets.end())
+ return std::shared_ptr<const SUIT_ActionAssets>(nullptr);
+ else
+ return itActionAssets->second;
+ }
}
QString SUIT_ShortcutMgr::getActionName(const QString& theModuleID, const QString& theInModuleActionID, const QString& theLang) const
return actionID;
}
- const auto itActionAssets = myActionAssets.find(actionID);
- if (itActionAssets != myActionAssets.end() && !itActionAssets->second->myLangDependentAssets.empty()) {
+ const auto itModuleActionAssets = myActionAssets.find(theModuleID);
+ if (itModuleActionAssets == myActionAssets.end())
+ return actionID;
+
+ const auto moduleActionAssets = itModuleActionAssets->second;
+ const auto itActionAssets = moduleActionAssets.find(theInModuleActionID);
+ if (itActionAssets != moduleActionAssets.end() && !itActionAssets->second->myLangDependentAssets.empty()) {
const auto& ldaMap = itActionAssets->second->myLangDependentAssets;
if (ldaMap.empty())
return theInModuleActionID;
QJsonObject object = document.object();
SUIT_ActionAssets actionAssets;
for (const QString& actionID : object.keys()) {
- if (!SUIT_ShortcutMgr::isActionIDValid(actionID)) {
+ const auto moduleIDAndActionID = SUIT_ShortcutMgr::splitIntoModuleIDAndInModuleID(actionID);
+ const QString& moduleID = moduleIDAndActionID.first;
+ const QString& inModuleActionID = moduleIDAndActionID.second;
+
+ if (inModuleActionID.isEmpty()) {
ShCutDbg("Action asset file \"" + path + "\" contains invalid action ID \"" + actionID + "\".");
continue;
}
#endif
}
- auto itAssets = myActionAssets.find(actionID);
- if (itAssets == myActionAssets.end()) {
+ auto& moduleActionAssets = myActionAssets[moduleID];
+ auto itAssets = moduleActionAssets.find(inModuleActionID);
+ if (itAssets == moduleActionAssets.end()) {
auto pAssets = std::shared_ptr<SUIT_ActionAssets>(new SUIT_ActionAssets(actionAssets));
- itAssets = myActionAssets.emplace(actionID, pAssets).first;
+ itAssets = moduleActionAssets.emplace(actionID, pAssets).first;
}
else
itAssets->second->merge(actionAssets, true);
#ifdef SHORTCUT_MGR_DBG
ShCutDbg("Parsed assets: ");
QJsonObject object;
- for (const auto& actionIDAndAssets : myActionAssets) {
- actionIDAndAssets.second->toJSON(object);
- QJsonDocument doc(object);
- QString strJson = doc.toJson(QJsonDocument::Indented);
- ShCutDbg(actionIDAndAssets.first + " : " + strJson);
+ for (const auto& moduleIDAndAssets : myActionAssets) {
+ for (const auto& actionIDAndAssets : moduleIDAndAssets.second) {
+ actionIDAndAssets.second->toJSON(object);
+ QJsonDocument doc(object);
+ QString strJson = doc.toJson(QJsonDocument::Indented);
+ const QString actionID = SUIT_ShortcutMgr::makeActionID(moduleIDAndAssets.first, actionIDAndAssets.first);
+ ShCutDbg(actionID + " : " + strJson);
+ }
}
#endif
const auto assets = std::shared_ptr<SUIT_ActionAssets>(new SUIT_ActionAssets());
auto& lda = assets->myLangDependentAssets[DEFAULT_LANG];
- if (moduleID == ROOT_MODULE_ID) {
+ if (moduleID == SUIT_ShortcutMgr::ROOT_MODULE_ID) {
lda.myName = tr("General");
{ // Load icon.
myModuleAssets.emplace(moduleID, std::move(assets));
}
+}
+
+
+
+SUIT_SentenceMatcher::SUIT_SentenceMatcher()
+{
+ myUseExactWordOrder = false;
+ myUseFuzzyWords = true;
+ myIsCaseSensitive = false;
+}
+
+void SUIT_SentenceMatcher::setUseExactWordOrder(bool theOn)
+{
+ if (myUseExactWordOrder == theOn)
+ return;
+
+ myUseExactWordOrder = theOn;
+ if (!myWords || theOn)
+ return;
+
+ if (!myPermutatedSentences) {
+ myPermutatedSentences.reset(new QList<QStringList>());
+ SUIT_SentenceMatcher::makePermutatedSentences(*myWords, *myPermutatedSentences);
+ }
+
+ if (myUseFuzzyWords && !myFuzzyPermutatedSentences) {
+ myFuzzyPermutatedSentences.reset(new QList<QStringList>());
+ SUIT_SentenceMatcher::makePermutatedSentences(*myFuzzyWords, *myFuzzyPermutatedSentences);
+ }
+}
+
+void SUIT_SentenceMatcher::setUseFuzzyWords(bool theOn)
+{
+ if (myUseFuzzyWords == theOn)
+ return;
+
+ myUseFuzzyWords = theOn;
+ if (!myWords || !theOn || myFuzzyWords)
+ return;
+
+ myFuzzyWords.reset(new QStringList());
+ SUIT_SentenceMatcher::makeFuzzyWords(*myWords, *myFuzzyWords);
+
+ if (!myUseExactWordOrder) {
+ myFuzzyPermutatedSentences.reset(new QList<QStringList>());
+ SUIT_SentenceMatcher::makePermutatedSentences(*myFuzzyWords, *myFuzzyPermutatedSentences);
+ }
+}
+
+void SUIT_SentenceMatcher::setCaseSensitive(bool theOn)
+{
+ myIsCaseSensitive = theOn;
+}
+
+void SUIT_SentenceMatcher::setQuery(QString theQuery)
+{
+ theQuery = theQuery.simplified();
+ if (theQuery == myQuery)
+ return;
+
+ myQuery = theQuery;
+
+ { // Set exact words.
+ if (myWords)
+ myWords->clear();
+ else
+ myWords.reset(new QStringList());
+
+ *myWords = theQuery.split(" ", QString::SkipEmptyParts);
+ }
+
+ { // Set permutated sentences.
+ if (myUseExactWordOrder)
+ myPermutatedSentences.reset(nullptr);
+ else {
+ if (!myPermutatedSentences)
+ myPermutatedSentences.reset(new QList<QStringList>());
+
+ SUIT_SentenceMatcher::makePermutatedSentences(*myWords, *myPermutatedSentences);
+ }
+ }
+
+ // Set fuzzy words and sentences.
+ if (myUseFuzzyWords) {
+ if (!myFuzzyWords)
+ myFuzzyWords.reset(new QStringList());
+
+ SUIT_SentenceMatcher::makeFuzzyWords(*myWords, *myFuzzyWords);
+
+ if (myUseExactWordOrder)
+ myFuzzyPermutatedSentences.reset(nullptr);
+ else {
+ if (!myFuzzyPermutatedSentences)
+ myFuzzyPermutatedSentences.reset(new QList<QStringList>());
+
+ SUIT_SentenceMatcher::makePermutatedSentences(*myFuzzyWords, *myFuzzyPermutatedSentences);
+ }
+ }
+ else {
+ myFuzzyWords.reset(nullptr);
+ myFuzzyPermutatedSentences.reset(nullptr);
+ }
+}
+
+size_t SUIT_SentenceMatcher::match(const QString& theInputString) const
+{
+ size_t 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;
+ }
+ }
+ 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;
+ }
+ }
+
+ return n;
+}
+
+/*static*/ bool SUIT_SentenceMatcher::makePermutatedSentences(const QStringList& theWords, QList<QStringList>& theSentences)
+{
+ theSentences.clear();
+ theSentences.push_back(theWords);
+ QStringList nextPerm = theWords;
+ QStringList prevPerm = theWords;
+
+ bool hasNextPerm = true;
+ bool hasPrevPerm = true;
+
+ while (hasNextPerm || hasPrevPerm) {
+ if (hasNextPerm)
+ hasNextPerm = std::next_permutation(nextPerm.begin(), nextPerm.end());
+
+ if (hasNextPerm && !theSentences.contains(nextPerm))
+ theSentences.push_back(nextPerm);
+
+ if (hasPrevPerm)
+ hasPrevPerm = std::prev_permutation(prevPerm.begin(), prevPerm.end());
+
+ if (hasPrevPerm && !theSentences.contains(prevPerm))
+ theSentences.push_back(prevPerm);
+ }
+
+ return theSentences.size() > 1;
+}
+
+/*static*/ void SUIT_SentenceMatcher::makeFuzzyWords(const QStringList& theWords, QStringList& theFuzzyWords)
+{
+ theFuzzyWords.clear();
+ for (const QString& word : theWords) {
+ QString fuzzyWord;
+ for (int i = 0; i < word.size(); i++) {
+ fuzzyWord += word[i];
+ fuzzyWord += "\\w*";
+ }
+ theFuzzyWords.push_back(fuzzyWord);
+ }
+}
+
+/*static*/ size_t 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;
+}
+
+/*static*/ size_t SUIT_SentenceMatcher::matchWithSentencesIgnoreEndings(const QString& theInputString, const QList<QStringList>& theSentences, bool theCaseSensitive)
+{
+ for (const QStringList& sentence : theSentences) {
+ if (SUIT_SentenceMatcher::matchWithSentenceIgnoreEndings(theInputString, sentence, theCaseSensitive))
+ return sentence.size();
+ }
+ return 0;
+}
+
+/*static*/ size_t SUIT_SentenceMatcher::matchAtLeastOneWord(const QString& theInputString, const QStringList& theWords, bool theCaseSensitive)
+{
+ size_t n = 0;
+ for (const QString& word : theWords) {
+ // The same input word can be counted multiple times. Nobody cares.
+ if (theInputString.contains(QRegExp(word, theCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive)))
+ n++;
+ }
+ return n;
+}
+
+/*static*/ size_t 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;
+
+ return SUIT_SentenceMatcher::matchAtLeastOneWord(theInputString, theSentence, theCaseSensitive);
+}
+
+/*static*/ size_t 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;
+
+ if (theSentences.size())
+ return SUIT_SentenceMatcher::matchAtLeastOneWord(theInputString, theSentences[0], theCaseSensitive);
+ else
+ return 0;
+}
+
+
+SUIT_ActionSearcher::AssetsAndSearchData::AssetsAndSearchData(std::shared_ptr<SUIT_ActionAssets> theAssets, size_t theNumOfMatchingWords)
+: myAssets(theAssets), myNumOfMatchingWords(theNumOfMatchingWords)
+{}
+
+SUIT_ActionSearcher::SUIT_ActionSearcher()
+{
+ myIncludedModuleIDs = { SUIT_ShortcutMgr::ROOT_MODULE_ID };
+ myIncludeDisabledActions = false;
+ myFieldsToMatch = { SUIT_ActionSearcher::MatchField::Name, SUIT_ActionSearcher::MatchField::ToolTip };
+ myMatcher.setCaseSensitive(false);
+ myMatcher.setUseExactWordOrder(false);
+ myMatcher.setUseFuzzyWords(true);
+}
+
+bool SUIT_ActionSearcher::setIncludedModuleIDs(std::set<QString> theIncludedModuleIDs)
+{
+ if (myIncludedModuleIDs == theIncludedModuleIDs)
+ return false;
+
+ myIncludedModuleIDs = theIncludedModuleIDs;
+
+ bool res = false;
+ // Erase search results with excluded modules. Erase IDs of modules, which are already in search results, from theIncludedModuleIDs.
+ for (auto itFound = mySearchResults.begin(); itFound != mySearchResults.end(); ) {
+ const auto itModuleID = theIncludedModuleIDs.find(itFound->first);
+ if (itModuleID == theIncludedModuleIDs.end()) {
+ mySearchResults.erase(itFound);
+ res = true;
+ }
+ else {
+ itFound++;
+ theIncludedModuleIDs.erase(itModuleID);
+ }
+ }
+
+ // Filter assets of added modules.
+ const auto& allAssets = SUIT_ShortcutMgr::get()->getActionAssets();
+ for (const auto& moduleIDAndAssets : allAssets) {
+ const QString& moduleID = moduleIDAndAssets.first;
+ const auto& actionIDsAndAssets = moduleIDAndAssets.second;
+ if (theIncludedModuleIDs.find(moduleID) == theIncludedModuleIDs.end())
+ continue;
+
+ 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);
+ res = true;
+ }
+ }
+ }
+
+ return res;
+}
+
+bool SUIT_ActionSearcher::includeDisabledActions(bool theOn)
+{
+ if (myIncludeDisabledActions == theOn)
+ return false;
+
+ myIncludeDisabledActions = theOn;
+
+ if (myIncludeDisabledActions)
+ return extendResults();
+ else
+ return filterResults().first;
+}
+
+bool SUIT_ActionSearcher::setFieldsToMatch(const std::set<SUIT_ActionSearcher::MatchField>& theFields)
+{
+ if (myFieldsToMatch == theFields)
+ return false;
+
+ if (theFields.empty()) {
+ myFieldsToMatch = theFields;
+ mySearchResults.clear();
+ return true;
+ }
+
+ bool narrows = true;
+ for (const SUIT_ActionSearcher::MatchField field : theFields) {
+ if (myFieldsToMatch.find(field) == myFieldsToMatch.end()) {
+ narrows = false;
+ break;
+ }
+ }
+
+ bool extends = true;
+ for (const SUIT_ActionSearcher::MatchField field : myFieldsToMatch) {
+ if (theFields.find(field) == theFields.end()) {
+ extends = false;
+ break;
+ }
+ }
+
+ myFieldsToMatch = theFields;
+
+ if (narrows)
+ return filterResults().first;
+ else if (extends)
+ return extendResults();
+ else
+ return filter().first;
+}
+
+bool SUIT_ActionSearcher::setCaseSensitive(bool theOn)
+{
+ if (myMatcher.isCaseSensitive() == theOn)
+ return false;
+
+ myMatcher.setCaseSensitive(theOn);
+
+ if (theOn)
+ return filterResults().first;
+ else
+ return extendResults();
+}
+
+bool SUIT_ActionSearcher::setQuery(const QString& theQuery)
+{
+ if (theQuery.simplified() == myMatcher.getQuery().simplified())
+ return false;
+
+ myMatcher.setQuery(theQuery);
+ return filter().first;
+}
+
+const std::map<QString, std::map<QString, SUIT_ActionSearcher::AssetsAndSearchData>>& SUIT_ActionSearcher::getSearchResults() const
+{
+ return mySearchResults;
+}
+
+std::pair<bool, bool> SUIT_ActionSearcher::filter()
+{
+ auto res = std::pair<bool, bool>(false, false);
+
+ for (const auto& moduleIDAndAssets : SUIT_ShortcutMgr::get()->getActionAssets()) {
+ const auto& moduleID = moduleIDAndAssets.first;
+ if (myIncludedModuleIDs.find(moduleID) == myIncludedModuleIDs.end())
+ continue;
+
+ const auto& actionIDsAndAssets = moduleIDAndAssets.second;
+
+ auto itFoundModuleIDAndAssets = mySearchResults.find(moduleID);
+ for (const auto& actionIDAndAssets : actionIDsAndAssets) {
+ const QString& inModuleActionID = actionIDAndAssets.first;
+
+ if (itFoundModuleIDAndAssets != mySearchResults.end()) {
+ auto& foundActionIDsAndAssets = itFoundModuleIDAndAssets->second;
+ auto itFoundActionIDAndAssets = foundActionIDsAndAssets.find(inModuleActionID);
+ 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;
+ res.second = true;
+ }
+ }
+ else /* if n == 0 */ {
+ foundActionIDsAndAssets.erase(itFoundActionIDAndAssets);
+ res.first = true;
+ }
+ continue;
+ }
+ }
+
+ const size_t n = matchAction(moduleID, inModuleActionID, actionIDAndAssets.second);
+ if (n > 0) {
+ 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));
+ res.first = true;
+ }
+ }
+ }
+
+ return res;
+}
+
+std::pair<bool, bool> SUIT_ActionSearcher::filterResults()
+{
+ auto res = std::pair<bool, bool>(false, false);
+
+ for (auto itFoundModuleIDAndAssets = mySearchResults.begin(); itFoundModuleIDAndAssets != mySearchResults.end(); ) {
+ const QString& moduleID = itFoundModuleIDAndAssets->first;
+ auto& actionIDsAndAssets = itFoundModuleIDAndAssets->second;
+ 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) {
+ actionIDsAndAssets.erase(itActionIDAndAssets);
+ res.first = true;
+ }
+ else {
+ if (assetsAndSearchData.myNumOfMatchingWords != n) {
+ assetsAndSearchData.myNumOfMatchingWords = n;
+ res.second = true;
+ }
+ itActionIDAndAssets++;
+ }
+ }
+
+ if (actionIDsAndAssets.empty())
+ mySearchResults.erase(itFoundModuleIDAndAssets);
+ else
+ itFoundModuleIDAndAssets++;
+ }
+
+ return res;
+}
+
+bool SUIT_ActionSearcher::extendResults()
+{
+ bool res = false;
+ for (const auto& moduleIDAndAssets : SUIT_ShortcutMgr::get()->getActionAssets()) {
+ const auto& moduleID = moduleIDAndAssets.first;
+ if (myIncludedModuleIDs.find(moduleID) == myIncludedModuleIDs.end())
+ continue;
+
+ const auto& actionIDsAndAssets = moduleIDAndAssets.second;
+
+ auto itFoundModuleIDAndAssets = mySearchResults.find(moduleID);
+ for (const auto& actionIDAndAssets : actionIDsAndAssets) {
+ const QString& inModuleActionID = actionIDAndAssets.first;
+
+ if (itFoundModuleIDAndAssets != mySearchResults.end()) {
+ const auto& foundActionIDsAndAssets = itFoundModuleIDAndAssets->second;
+ if (foundActionIDsAndAssets.find(inModuleActionID) != foundActionIDsAndAssets.end())
+ continue; // Action is already in search results.
+ }
+
+ const size_t n = matchAction(moduleID, inModuleActionID, actionIDAndAssets.second);
+ if (n > 0) {
+ 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));
+ res = true;
+ }
+ }
+ }
+ return res;
+}
+
+size_t SUIT_ActionSearcher::matchAction(const QString& theModuleID, const QString& theInModuleActionID, std::shared_ptr<SUIT_ActionAssets> theAssets)
+{
+ 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;
+ }
+
+ for (const auto& langAndLDA : theAssets->myLangDependentAssets) {
+ if (myFieldsToMatch.find(SUIT_ActionSearcher::MatchField::ToolTip) != myFieldsToMatch.end()) {
+ if (myMatcher.match(langAndLDA.second.myToolTip))
+ return true;
+ }
+
+ if (myFieldsToMatch.find(SUIT_ActionSearcher::MatchField::Name) != myFieldsToMatch.end()) {
+ if (myMatcher.match(langAndLDA.second.myName))
+ return true;
+ }
+ }
+
+ if (myFieldsToMatch.find(SUIT_ActionSearcher::MatchField::ID) != myFieldsToMatch.end()) {
+ if (myMatcher.match(SUIT_ShortcutMgr::makeActionID(theModuleID, theInModuleActionID)))
+ return true;
+ }
+
+ 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;
+ }
+
+ return false;
}
\ No newline at end of file
#include <QObject>
#include <QString>
+#include <QStringList>
#include <QIcon>
#include <map>
#include <set>
#endif
// Define SHORTCUT_MGR_DBG to enable SUIT_ShortcutMgr debug logging.
-// #define SHORTCUT_MGR_DBG
+#define SHORTCUT_MGR_DBG
/*! \returns true, if SUIT_ShortcutMgr debug logging is enabled. */
SUIT_EXPORT extern inline bool ShCutDbg() {
#ifdef SHORTCUT_MGR_DBG
virtual ~SUIT_ShortcutMgr();
public:
+ static const QString ROOT_MODULE_ID;
+
/*! \brief Create new singleton-instance of shortcut manager, if it has not been created. */
static void Init();
std::shared_ptr<const SUIT_ActionAssets> getActionAssets(const QString& theActionID) const;
+ 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;
Sets of moduleIDs and inModuleActionIDs may NOT be equal for myActions and myShortcutContainer.
*/
- /* {actionID, assets}[] */
- std::map<QString, std::shared_ptr<SUIT_ActionAssets>> myActionAssets;
+ /* { moduleID, {inModuleActionID, assets}[] }[] */
+ std::map<QString, std::map<QString, std::shared_ptr<SUIT_ActionAssets>>> myActionAssets;
/* {moduleID, assets}[] */
mutable std::map<QString, std::shared_ptr<SUIT_ActionAssets>> myModuleAssets;
};
+
+/*!
+ \class SUIT_SentenceMatcher
+ \brief Approximate string matcher, treats strings as sentences composed of words.
+*/
+class SUIT_EXPORT SUIT_SentenceMatcher
+{
+public:
+ /*! Default config:
+ Exact word order = false;
+ Fuzzy words = true;
+ Case sensitive = false;
+ Query = ""; // matches nothing.
+ */
+ SUIT_SentenceMatcher();
+
+ void setUseExactWordOrder(bool theOn);
+ void setUseFuzzyWords(bool theOn);
+ void setCaseSensitive(bool theOn);
+ inline bool isCaseSensitive() const { return myIsCaseSensitive; };
+
+ /*! \param theQuery should not be regex. */
+ void setQuery(QString theQuery);
+
+ inline const QString& getQuery() const { return myQuery; };
+
+ /*! \returns number of matched words. */
+ size_t match(const QString& theInputString) 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);
+
+ static size_t matchAtLeastOneWord(const QString& theInputString, const QStringList& theWords, bool theCaseSensitive);
+
+ static size_t match(
+ const QString& theInputString,
+ const QStringList& theSentence,
+ bool theCaseSensitive
+ );
+
+ static size_t match(
+ const QString& theInputString,
+ const QList<QStringList>& theSentences,
+ bool theCaseSensitive
+ );
+
+ bool myUseExactWordOrder; // If false, try to match with sentences, composed of query's words in different orders.
+ bool myUseFuzzyWords; // Try to match with sentences, composed of query's truncated words.
+ bool myIsCaseSensitive;
+ QString myQuery;
+
+ std::unique_ptr<QStringList> myWords; // It is also original search sentence.
+ std::unique_ptr<QList<QStringList>> myPermutatedSentences;
+
+ std::unique_ptr<QStringList> myFuzzyWords; // Regexes.
+ std::unique_ptr<QList<QStringList>> myFuzzyPermutatedSentences;
+};
+
+
+typedef std::map<QString, std::map<QString, std::shared_ptr<SUIT_ActionAssets>>> SUIT_ActionAssetsMap2;
+
+
+/*!
+ \class SUIT_ActionSearcher
+ \brief Searches in data, provided in action asset files and shortcut preferences.
+*/
+class SUIT_EXPORT SUIT_ActionSearcher
+{
+public:
+ enum MatchField {
+ ID,
+ Name,
+ ToolTip,
+ KeySequence
+ };
+
+ struct AssetsAndSearchData
+ {
+ AssetsAndSearchData() = default;
+ AssetsAndSearchData(std::shared_ptr<SUIT_ActionAssets> theAssets, size_t theNumOfMatchingWords);
+
+ std::shared_ptr<SUIT_ActionAssets> myAssets;
+ size_t myNumOfMatchingWords;
+ };
+
+ /*! Default config:
+ Included modules' IDs = { ROOT_MODULE_ID };
+ Include disabled actions = false;
+ Fields to match = { Name, Tooltip };
+ Case sensitive = false;
+ Fuzzy matching = true;
+ Query = ""; // matches everything.
+ */
+ SUIT_ActionSearcher();
+ SUIT_ActionSearcher(const SUIT_ActionSearcher&) = delete;
+ SUIT_ActionSearcher& operator=(const SUIT_ActionSearcher&) = delete;
+ virtual ~SUIT_ActionSearcher() = default;
+
+ /*! \returns true, if set of results is changed. */
+ bool setIncludedModuleIDs(std::set<QString> theIncludedModuleIDs);
+
+ /*! \returns true, if set of results is changed. */
+ bool includeDisabledActions(bool theOn);
+ inline bool areDisabledActionsIncluded() const {return myIncludeDisabledActions;};
+
+ /*! \returns true, if set of results is changed. */
+ bool setFieldsToMatch(const std::set<SUIT_ActionSearcher::MatchField>& theFields);
+
+ /*! \returns true, if set of results is changed. */
+ bool setCaseSensitive(bool theOn);
+
+ /*! \returns true, if set of results is changed. */
+ bool setQuery(const QString& theQuery);
+ inline const QString& getQuery() const {return myMatcher.getQuery();};
+
+ const std::map<QString, std::map<QString, SUIT_ActionSearcher::AssetsAndSearchData>>& getSearchResults() const;
+
+
+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. */
+ 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. */
+ 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);
+
+
+ std::set<QString> myIncludedModuleIDs;
+ bool myIncludeDisabledActions;
+
+ std::set<SUIT_ActionSearcher::MatchField> myFieldsToMatch;
+ SUIT_SentenceMatcher myMatcher;
+
+ /* { moduleID, {inModuleActionID, assetsAndSearchData}[] }[]. */
+ std::map<QString, std::map<QString, SUIT_ActionSearcher::AssetsAndSearchData>> mySearchResults;
+};
+
+
#if defined WIN32
#pragma warning( default: 4251 )
#endif