diff --git a/src/qtgui/actsearch.ui b/src/qtgui/actsearch.ui new file mode 100644 index 00000000..e10f51e7 --- /dev/null +++ b/src/qtgui/actsearch.ui @@ -0,0 +1,37 @@ + + + ActSearchDLG + + + + 0 + 0 + 314 + 43 + + + + Menu search + + + + + + true + + + 15 + + + QComboBox::NoInsert + + + false + + + + + + + + diff --git a/src/qtgui/actsearch_w.cpp b/src/qtgui/actsearch_w.cpp new file mode 100644 index 00000000..973fed8f --- /dev/null +++ b/src/qtgui/actsearch_w.cpp @@ -0,0 +1,111 @@ +/* Copyright (C) 2020-2021 J.F.Dockes + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include "autoconfig.h" + +#include +#include +#include +#include +#include + +#include "log.h" +#include "actsearch_w.h" + +// A window for letting the user to search among menu entries (actions) and execute the one found. + +void ActSearchW::init() +{ + new QShortcut(QKeySequence("Esc"), this, SLOT(hide())); + connect(actCMB, SIGNAL(activated(int)), this, SLOT(onActivated(int))); +} + +void ActSearchW::setActList(QList actlist) +{ + for (int i = 0; i < actlist.size(); i++) { + if (!actlist[i]->text().isEmpty()) { + m_actions.push_back(actlist[i]); + } + } + std::sort(m_actions.begin(), m_actions.end(), + [] (const QAction *act1, const QAction *act2) { + auto txt1 = act1->text().remove('&'); + auto txt2 = act2->text().remove('&'); + return txt1.compare(txt2, Qt::CaseInsensitive) < 0;}); + QStringList sl; + for (const auto act : m_actions) { + auto txt = act->text().remove('&'); + actCMB->addItem(txt); + sl.push_back(txt); + } + actCMB->setCurrentText(""); + m_completer = new QCompleter(sl, this); + m_completer->setCaseSensitivity(Qt::CaseInsensitive); + if (m_match_contains) { + m_completer->setFilterMode(Qt::MatchContains); + } + actCMB->setCompleter(m_completer); + m_completer->popup()->installEventFilter(this); + actCMB->installEventFilter(this); +} + +// This is to avoid that if the user types Backspace or Del while we +// have inserted / selected the current completion, the lineedit text +// goes back to what it was, the completion fires, and it looks like +// nothing was typed. Disable the completion after Del or Backspace +// is typed. +bool ActSearchW::eventFilter(QObject *target, QEvent *event) +{ + Q_UNUSED(target); + if (event->type() != QEvent::KeyPress) { + return false; + } + QKeyEvent *keyEvent = (QKeyEvent *)event; + if (keyEvent->key() == Qt::Key_Backspace || keyEvent->key()==Qt::Key_Delete) { + actCMB->setCompleter(nullptr); + return false; + } else { + if (nullptr == actCMB->completer()) { + actCMB->setCompleter(m_completer); + } + } + return false; +} + +void ActSearchW::onTextChanged(const QString&) +{ + if (actCMB->completer() && actCMB->completer()->completionCount() == 1) { + // We append the completion part to the end of the current input, line, and select it so + // that the user has a clear indication of what will happen if they type Enter. + auto le = actCMB->lineEdit(); + int pos = le->cursorPosition(); + auto text = actCMB->completer()->currentCompletion(); + int len = text.size() - actCMB->currentText().size(); + le->setText(text); + if (!m_match_contains) { + le->setCursorPosition(pos); + le->setSelection(pos, len); + } + } +} + +void ActSearchW::onActivated(int index) +{ + if (index < 0 || index >= int(m_actions.size())) + return; + m_actions[index]->trigger(); + hide(); +} diff --git a/src/qtgui/actsearch_w.h b/src/qtgui/actsearch_w.h new file mode 100644 index 00000000..a334c900 --- /dev/null +++ b/src/qtgui/actsearch_w.h @@ -0,0 +1,57 @@ +/* Copyright (C) 2021 J.F.Dockes + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef _ACTSEARCH_H_INCLUDED_ +#define _ACTSEARCH_H_INCLUDED_ + +// A window for letting the user to search among menu entries (actions) and execute the one found. + +#include +#include + +#include + +#include "ui_actsearch.h" + +class QCompleter; +class QAction; +class QString; + +class ActSearchW : public QDialog, public Ui::ActSearchDLG { + Q_OBJECT; +public: + ActSearchW(QWidget* parent = 0) + : QDialog(parent) { + setupUi(this); + init(); + } + void setActList(QList); + virtual bool eventFilter(QObject *target, QEvent *event); + +private slots: + void onActivated(int); + void onTextChanged(const QString&); + +private: + void init(); + std::vector m_actions; + QCompleter *m_completer{nullptr}; + // Decides if we match the start only or anywhere. Anywhere seems better. May be made a pref one + // day. + bool m_match_contains{true}; +}; + +#endif /* _ACTSEARCH_H_INCLUDED_ */ diff --git a/src/qtgui/rclm_wins.cpp b/src/qtgui/rclm_wins.cpp index 7c8a551b..a917383e 100644 --- a/src/qtgui/rclm_wins.cpp +++ b/src/qtgui/rclm_wins.cpp @@ -36,6 +36,7 @@ #include "rclmain_w.h" #include "webcache.h" #include "restable.h" +#include "actsearch_w.h" using namespace std; @@ -489,3 +490,16 @@ void RclMain::showSnippets(Rcl::Doc doc) } m_snippets->show(); } + +void RclMain::showActionsSearch() +{ + if (nullptr == actsearchw) { + actsearchw = new ActSearchW(this); + actsearchw->setActList(findChildren()); + connect(actsearchw->actCMB, SIGNAL(editTextChanged(const QString&)), + actsearchw, SLOT(onTextChanged(const QString&))); + } + actsearchw->actCMB->setCurrentIndex(-1); + actsearchw->actCMB->clearEditText(); + actsearchw->show(); +} diff --git a/src/qtgui/rclmain_w.cpp b/src/qtgui/rclmain_w.cpp index 9ae403f6..d12845dd 100644 --- a/src/qtgui/rclmain_w.cpp +++ b/src/qtgui/rclmain_w.cpp @@ -367,12 +367,11 @@ void RclMain::onNewShortcuts() "Ctrl+Shift+S", m_focustosearcholdsc, takeFocus); // We could set this as an action shortcut, but then, it would not // be editable - SETSHORTCUT(this, "main:357", - tr("Main Window"), tr("Toggle tabular display"), + SETSHORTCUT(this, "main:357", tr("Main Window"), tr("Toggle tabular display"), "Ctrl+T", m_toggletablesc, toggleTable); - - ks = scb.get("rclmain:361", tr("Main Window"), - tr("Move keyboard focus to table"), "Ctrl+R"); + SETSHORTCUT(this, "main:373", tr("Main Window"), tr("Show menu search dialog"), + "Alt+/", m_actionssearchsc, showActionsSearch); + ks = scb.get("rclmain:361", tr("Main Window"), tr("Move keyboard focus to table"), "Ctrl+R"); if (!ks.isEmpty()) { delete m_focustotablesc; m_focustotablesc = new QShortcut(ks, this); diff --git a/src/qtgui/rclmain_w.h b/src/qtgui/rclmain_w.h index 679ce8c3..592e9ad4 100644 --- a/src/qtgui/rclmain_w.h +++ b/src/qtgui/rclmain_w.h @@ -50,6 +50,7 @@ class ConfIndexW; class RclTrayIcon; class QShortcut; class QActionGroup; +class ActSearchW; #include "ui_rclmain.h" @@ -134,6 +135,7 @@ public slots: virtual void docExpand(Rcl::Doc); virtual void showSubDocs(Rcl::Doc); virtual void showSnippets(Rcl::Doc); + virtual void showActionsSearch(); virtual void startPreview(int docnum, Rcl::Doc doc, int keymods); virtual void startPreview(Rcl::Doc); virtual void startNativeViewer(Rcl::Doc, int pagenum = -1, @@ -176,7 +178,7 @@ private slots: virtual void onWebcacheDestroyed(QObject *); virtual void onSSTypMenu(QAction *act); virtual void onSSTypCMB(int); - + signals: void docSourceChanged(std::shared_ptr); void stemLangChanged(const QString& lang); @@ -210,6 +212,7 @@ private: WebcacheEdit *webcache{0}; ResTable *restable{0}; bool displayingTable{false}; + ActSearchW *actsearchw{0}; QAction *m_idNoStem{0}; QAction *m_idAllStem{0}; QToolBar *m_toolsTB{0}; @@ -223,6 +226,7 @@ private: QShortcut *m_focustosearcholdsc{0}; QShortcut *m_clearsearchsc{0}; QShortcut *m_toggletablesc{0}; + QShortcut *m_actionssearchsc{0}; QFileSystemWatcher m_watcher; vector m_viewers; ExecCmd *m_idxproc{0}; // Indexing process diff --git a/src/qtgui/recoll-win.pro b/src/qtgui/recoll-win.pro index 350a0c05..730ce39a 100644 --- a/src/qtgui/recoll-win.pro +++ b/src/qtgui/recoll-win.pro @@ -1,5 +1,5 @@ # Note this is generated by configure on Linux (see recoll.pro.in). -# recoll.pro is under version control anyway and used on Windows +# recoll-win.pro is under version control anyway and used on Windows TEMPLATE = app LANGUAGE = C++ @@ -15,6 +15,7 @@ QT += xml printsupport DEFINES += BUILDING_RECOLL HEADERS += \ + actsearch_w.h \ advsearch_w.h \ advshist.h \ confgui/confgui.h \ @@ -45,6 +46,7 @@ HEADERS += \ webcache.h SOURCES += \ + actsearch_w.cpp \ advsearch_w.cpp \ advshist.cpp \ confgui/confgui.cpp \ diff --git a/src/qtgui/recoll.pro.in b/src/qtgui/recoll.pro.in index 134bc378..b3c331ac 100644 --- a/src/qtgui/recoll.pro.in +++ b/src/qtgui/recoll.pro.in @@ -27,6 +27,7 @@ CONFIG += qt warn_on thread release HEADERS += \ + actsearch_w.h \ advsearch_w.h \ advshist.h \ confgui/confgui.h \ @@ -59,6 +60,7 @@ HEADERS += \ widgets/qxtconfirmationmessage.h SOURCES += \ + actsearch_w.cpp \ advsearch_w.cpp \ advshist.cpp \ confgui/confgui.cpp \ @@ -98,6 +100,7 @@ SOURCES += \ xmltosd.cpp FORMS = \ + actsearch.ui \ advsearch.ui \ crontool.ui \ firstidx.ui \