diff --git a/src/qtgui/rclmain_w.cpp b/src/qtgui/rclmain_w.cpp index f9d7dfac..b85bc726 100644 --- a/src/qtgui/rclmain_w.cpp +++ b/src/qtgui/rclmain_w.cpp @@ -69,10 +69,13 @@ #include "rclhelp.h" #include "readfile.h" #include "moc_rclmain_w.cpp" +#include "scbase.h" QString g_stringAllStem, g_stringNoStem; static const char *settingskey_toolarea="/Recoll/geometry/toolArea"; static const char *settingskey_resarea="/Recoll/geometry/resArea"; +static const QString scbctxt("Main Window"); + static Qt::ToolBarArea int2area(int in) { switch (in) { @@ -125,10 +128,6 @@ void RclMain::init() buildMenus(); - // A shortcut to get the focus back to the search entry, in table - // mode only. - m_tablefocseq = new QShortcut(QKeySequence("Ctrl+r"), this); - periodictimer = new QTimer(this); // idxstatus file. Make sure it exists before trying to watch it @@ -165,12 +164,10 @@ void RclMain::init() actionShowResultsAsTable->setChecked(prefs.showResultsAsTable); on_actionShowResultsAsTable_toggled(prefs.showResultsAsTable); - QKeySequence seq("Ctrl+Shift+s"); - QShortcut *sc = new QShortcut(seq, this); - connect(sc, SIGNAL (activated()), sSearch, SLOT (takeFocus())); - QKeySequence seql("Ctrl+l"); - sc = new QShortcut(seql, this); - connect(sc, SIGNAL (activated()), sSearch, SLOT (takeFocus())); + onNewShortcuts(); + + // Compat with old versions + new QShortcut(QKeySequence("Ctrl+Shift+s"), sSearch, SLOT(takeFocus())); connect(&m_watcher, SIGNAL(fileChanged(QString)), this, SLOT(updateIdxStatus())); @@ -340,6 +337,22 @@ void RclMain::init() periodictimer->start(1000); } +void RclMain::onNewShortcuts() +{ + SCBase& scb = SCBase::scBase(); + QKeySequence ks; + + ks = scb.get(scbctxt, "Focus to Search Entry", "Ctrl+l"); + if (!ks.isEmpty()) + new QShortcut(ks, sSearch, SLOT(takeFocus())); + + ks = scb.get(scbctxt, "Focus to Result Table", "Ctrl+r"); + if (!ks.isEmpty()) { + delete m_tablefocseq; + m_tablefocseq = new QShortcut(ks, this); + } +} + void RclMain::setupToolbars() { if (nullptr == m_toolsTB) { @@ -993,8 +1006,9 @@ void RclMain::on_actionShowResultsAsTable_toggled(bool on) if (docnum >= 0) { reslist->resultPageFor(docnum); } - disconnect(m_tablefocseq, SIGNAL(activated()), - restable, SLOT(takeFocus())); + if (m_tablefocseq) + disconnect(m_tablefocseq, SIGNAL(activated()), + restable, SLOT(takeFocus())); sSearch->takeFocus(); } else { int docnum = reslist->pageFirstDocNum(); @@ -1004,8 +1018,9 @@ void RclMain::on_actionShowResultsAsTable_toggled(bool on) nextPageAction->setEnabled(false); prevPageAction->setEnabled(false); firstPageAction->setEnabled(false); - connect(m_tablefocseq, SIGNAL(activated()), - restable, SLOT(takeFocus())); + if (m_tablefocseq) + connect(m_tablefocseq, SIGNAL(activated()), + restable, SLOT(takeFocus())); } } diff --git a/src/qtgui/rclmain_w.h b/src/qtgui/rclmain_w.h index 23e21864..a392a3ee 100644 --- a/src/qtgui/rclmain_w.h +++ b/src/qtgui/rclmain_w.h @@ -167,7 +167,8 @@ public slots: virtual void setFilterCtlStyle(int stl); virtual void showTrayMessage(const QString& text); virtual void onSetDescription(QString); - + virtual void onNewShortcuts(); + private slots: virtual void updateIdxStatus(); virtual void onWebcacheDestroyed(QObject *); diff --git a/src/qtgui/recoll.pro.in b/src/qtgui/recoll.pro.in index 677910a3..a955dd2a 100644 --- a/src/qtgui/recoll.pro.in +++ b/src/qtgui/recoll.pro.in @@ -83,6 +83,7 @@ SOURCES += \ respopup.cpp \ restable.cpp \ rtitool.cpp \ + scbase.cpp \ searchclause_w.cpp \ snippets_w.cpp \ spell_w.cpp \ diff --git a/src/qtgui/restable.cpp b/src/qtgui/restable.cpp index 6748d933..9ab74fc0 100644 --- a/src/qtgui/restable.cpp +++ b/src/qtgui/restable.cpp @@ -55,9 +55,11 @@ #include "multisave.h" #include "appformime.h" #include "transcode.h" +#include "scbase.h" static const QKeySequence quitKeySeq("Ctrl+q"); static const QKeySequence closeKeySeq("Ctrl+w"); +static const QString scbctxt("Result Table"); // Compensate for the default and somewhat bizarre vertical placement // of text in cells @@ -588,10 +590,8 @@ void ResTable::init() tableView->setSelectionBehavior(QAbstractItemView::SelectRows); tableView->setItemDelegate(new ResTableDelegate(this)); tableView->setContextMenuPolicy(Qt::CustomContextMenu); - new QShortcut(QKeySequence("Ctrl+o"), this, SLOT(menuEdit())); - new QShortcut(QKeySequence("Ctrl+Shift+o"), this, SLOT(menuEditAndQuit())); - new QShortcut(QKeySequence("Ctrl+d"), this, SLOT(menuPreview())); - new QShortcut(QKeySequence("Ctrl+e"), this, SLOT(menuShowSnippets())); + + onNewShortcuts(); connect(tableView, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(createPopupMenu(const QPoint&))); @@ -660,6 +660,27 @@ void ResTable::init() installEventFilter(this); } +void ResTable::onNewShortcuts() +{ + SCBase& scb = SCBase::scBase(); + QKeySequence ks; + ks = scb.get(scbctxt, "Open", "Ctrl+o"); + if (!ks.isEmpty()) + new QShortcut(ks, this, SLOT(menuEdit())); + + ks = scb.get(scbctxt, "Open and Quit", "Ctrl+Shift+o"); + if (!ks.isEmpty()) + new QShortcut(ks, this, SLOT(menuEditAndQuit())); + + ks = scb.get(scbctxt, "Preview", "Ctrl+d"); + if (!ks.isEmpty()) + new QShortcut(ks, this, SLOT(menuPreview())); + + ks = scb.get(scbctxt, "Show Snippets", "Ctrl+e"); + if (!ks.isEmpty()) + new QShortcut(ks, this, SLOT(menuShowSnippets())); +} + bool ResTable::eventFilter(QObject* obj, QEvent* event) { if (event->type() == QEvent::KeyPress) { diff --git a/src/qtgui/restable.h b/src/qtgui/restable.h index 9ac5cc1a..a67b1050 100644 --- a/src/qtgui/restable.h +++ b/src/qtgui/restable.h @@ -167,6 +167,7 @@ public slots: virtual void makeRowVisible(int row); virtual void takeFocus(); virtual void onUiPrefsChanged(); + virtual void onNewShortcuts(); signals: void docPreviewClicked(int, Rcl::Doc, int); diff --git a/src/qtgui/scbase.cpp b/src/qtgui/scbase.cpp new file mode 100644 index 00000000..e0650b32 --- /dev/null +++ b/src/qtgui/scbase.cpp @@ -0,0 +1,138 @@ +/* Copyright (C) 2017-2019 J.F.Dockes + * + * License: GPL 2.1 + * + * 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.1 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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 "scbase.h" + +#include +#include + +#include + + +#include "recoll.h" +#include "smallut.h" +#include "log.h" + +struct SCDef { + QString ctxt; + QString desc; + QKeySequence val; + QKeySequence dflt; +}; + +class SCBase::Internal { +public: + std::map scdefs; +}; + +static QString mapkey(const QString& ctxt, const QString& desc) +{ + return ctxt + "/" + desc; +} +#if 0 +static QString mapkey(const std::string& ctxt, const std::string& desc) +{ + return u8s2qs(ctxt) + "/" + u8s2qs(desc); +} +#endif + +QString SCBase::scBaseSettingsKey() +{ + return "/Recoll/prefs/sckeys"; +} +SCBase::SCBase() +{ + m = new Internal(); + + QSettings settings; + auto sl = settings.value(scBaseSettingsKey()).toStringList(); + + for (int i = 0; i < sl.size(); ++i) { + auto ssc = qs2utf8s(sl.at(i)); + std::vector co_des_val; + stringToStrings(ssc, co_des_val); + if (co_des_val.size() != 3) { + LOGERR("Bad shortcut def in prefs: [" << ssc << "]\n"); + continue; + } + QString ctxt = u8s2qs(co_des_val[0]); + QString desc = u8s2qs(co_des_val[1]); + QString val = u8s2qs(co_des_val[2]); + QString key = mapkey(ctxt, desc); + auto it = m->scdefs.find(key); + if (it == m->scdefs.end()) { + m->scdefs[key] = SCDef{ctxt, desc,QKeySequence(val), QKeySequence()}; + } else { + it->second.val = QKeySequence(val); + } + } +} + +SCBase::~SCBase() +{ + delete m; +} + +QKeySequence SCBase::get(const QString& ctxt, const QString& desc, + const QString& defks) +{ + std::cerr << "SCBase::get\n"; + QString key = mapkey(ctxt, desc); + auto it = m->scdefs.find(key); + if (it == m->scdefs.end()) { + if (defks.isEmpty()) { + return QKeySequence(); + } + QKeySequence qks(defks); + m->scdefs[key] = SCDef{ctxt, desc, qks, qks}; + std::cerr << "get(" << qs2utf8s(ctxt) << ", " << qs2utf8s(desc) << + ", " << qs2utf8s(defks) << ") -> " << + qs2utf8s(qks.toString()) << "\n"; + return qks; + } + std::cerr << "get(" << qs2utf8s(ctxt) << ", " << qs2utf8s(desc) << + ", " << qs2utf8s(defks) << ") -> " << + qs2utf8s(it->second.val.toString()) << "\n"; + return it->second.val; +} + +QStringList SCBase::getAll() +{ + QStringList result; + for (const auto& entry : m->scdefs) { + result.push_back(entry.second.ctxt); + result.push_back(entry.second.desc); + result.push_back(entry.second.val.toString()); + result.push_back(entry.second.dflt.toString()); + } + return result; +} + +static SCBase *theBase; + +SCBase& SCBase::scBase() +{ + std::cerr << "SCBase::scBase()\n"; + if (nullptr == theBase) { + std::cerr << "SCBase::scBase() creating class\n"; + theBase = new SCBase(); + } + std::cerr << "SCBase::scBase() returning " << theBase << "\n"; + return *theBase; +} diff --git a/src/qtgui/scbase.h b/src/qtgui/scbase.h new file mode 100644 index 00000000..27e17438 --- /dev/null +++ b/src/qtgui/scbase.h @@ -0,0 +1,45 @@ +/* Copyright (C) 2017-2019 J.F.Dockes + * + * License: GPL 2.1 + * + * 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.1 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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 _SCBASE_H_INCLUDED_ +#define _SCBASE_H_INCLUDED_ + +#include +#include +#include + +class SCBase { +public: + ~SCBase(); + + static SCBase& scBase(); + + QKeySequence get(const QString& context, const QString& description, + const QString& defkeyseq); + QStringList getAll(); + static QString scBaseSettingsKey(); + + class Internal; +private: + Internal *m{nullptr}; + SCBase(); +}; + +#endif /* _SCBASE_H_INCLUDED_ */ diff --git a/src/qtgui/uiprefs.ui b/src/qtgui/uiprefs.ui index a5d13d55..94249227 100644 --- a/src/qtgui/uiprefs.ui +++ b/src/qtgui/uiprefs.ui @@ -7,7 +7,7 @@ 0 0 542 - 641 + 810 @@ -20,7 +20,7 @@ - 0 + 1 @@ -463,9 +463,6 @@ - - - @@ -482,16 +479,16 @@ - - - - - - - - - - + + + + + Shortcuts + + + + + @@ -1264,7 +1261,7 @@ May be slow for big documents. - + diff --git a/src/qtgui/uiprefs_w.cpp b/src/qtgui/uiprefs_w.cpp index 6b15967f..37bcbf31 100644 --- a/src/qtgui/uiprefs_w.cpp +++ b/src/qtgui/uiprefs_w.cpp @@ -43,6 +43,8 @@ #include #include #include +#include +#include #include "recoll.h" #include "guiutils.h" @@ -54,6 +56,7 @@ #include "editdialog.h" #include "rclmain_w.h" #include "ptrans_w.h" +#include "scbase.h" void UIPrefsDialog::init() { @@ -269,6 +272,57 @@ void UIPrefsDialog::setFromPrefs() } } idxLV->sortItems(); + readShortcuts(); +} + +void UIPrefsDialog::readShortcuts() +{ + SCBase& scbase = SCBase::scBase(); + shortcutsTB->setColumnCount(3); + shortcutsTB->setHorizontalHeaderItem( + 0, new QTableWidgetItem(tr("Context"))); + shortcutsTB->setHorizontalHeaderItem( + 1, new QTableWidgetItem(tr("Description"))); + shortcutsTB->setHorizontalHeaderItem( + 2, new QTableWidgetItem(tr("Shortcut"))); + QStringList sl = scbase.getAll(); + int row = 0; + for (int i = 0; i < sl.size();) { + std::cerr << "Inserting row " << qs2utf8s(sl.at(i)) << " " << + qs2utf8s(sl.at(i+1)) << " " << + qs2utf8s(sl.at(i+2)) << "\n"; + shortcutsTB->insertRow(row); + shortcutsTB->setItem(row, 0, new QTableWidgetItem(sl.at(i++))); + shortcutsTB->setItem(row, 1, new QTableWidgetItem(sl.at(i++))); + auto ed = new QKeySequenceEdit(QKeySequence(sl.at(i++))); + shortcutsTB->setCellWidget(row, 2, ed); + i++; // Skip dlft, not needed here. + row++; + } + shortcutsTB->resizeColumnsToContents(); + shortcutsTB->horizontalHeader()->setStretchLastSection(true); +} + +void UIPrefsDialog::storeShortcuts() +{ + SCBase& scbase = SCBase::scBase(); + QStringList sl = scbase.getAll(); + QStringList slout; + for (int row = 0; row < shortcutsTB->rowCount(); row++) { + auto qsce = (QKeySequenceEdit*)(shortcutsTB->cellWidget(row, 2)); + QString val = qsce->keySequence().toString(); + QString dflt = sl[4 *row + 3]; + if (dflt != val) { + std::cerr << "Storing changed " << qs2utf8s(dflt) << " -> " << + qs2utf8s(val) << " at row " << row << "\n"; + std::string e = stringsToString( + std::vector{qs2utf8s(sl[4*row]), + qs2utf8s(sl[4*row+1]), qs2utf8s(val)}); + slout.append(u8s2qs(e)); + } + } + QSettings settings; + settings.setValue(SCBase::scBaseSettingsKey(), slout); } void UIPrefsDialog::setupReslistFontPB() @@ -401,6 +455,8 @@ void UIPrefsDialog::accept() } rwSettings(true); + storeShortcuts(); + string reason; maybeOpenDb(reason, true); emit uiprefsDone(); diff --git a/src/qtgui/uiprefs_w.h b/src/qtgui/uiprefs_w.h index b0d77144..a0de9e55 100644 --- a/src/qtgui/uiprefs_w.h +++ b/src/qtgui/uiprefs_w.h @@ -45,7 +45,7 @@ public: virtual void init(); void setFromPrefs(); - + public slots: virtual void showFontDialog(); virtual void resetReslistFont(); @@ -76,6 +76,8 @@ protected slots: virtual void reject(); private: void setupReslistFontPB(); + void readShortcuts(); + void storeShortcuts(); // Locally stored data (pending ok/cancel) QString paraFormat; QString headerText;