From 064018ea6996fa4324a00881db564657551b8285 Mon Sep 17 00:00:00 2001 From: Jean-Francois Dockes Date: Sat, 26 Feb 2022 12:12:36 +0100 Subject: [PATCH] GUI: implemented sidebar for filtering on directory location --- src/Makefile.am | 3 + src/qtgui/confgui/confguiindex.cpp | 1 + src/qtgui/confgui/confguiindex.h | 3 + src/qtgui/guiutils.cpp | 85 ++++++++-------------- src/qtgui/guiutils.h | 1 + src/qtgui/idxmodel.cpp | 109 +++++++++++++++++++++++++++++ src/qtgui/idxmodel.h | 42 +++++++++++ src/qtgui/rclm_sidefilters.cpp | 54 ++++++++++++++ src/qtgui/rclm_wins.cpp | 1 + src/qtgui/rclmain.ui | 63 ++++++++--------- src/qtgui/rclmain_w.cpp | 49 ++++++++++--- src/qtgui/rclmain_w.h | 13 ++-- src/qtgui/recoll.pro.in | 3 + src/qtgui/uiprefs.ui | 54 ++++++++++++-- src/qtgui/uiprefs_w.cpp | 29 +++----- src/utils/smallut.cpp | 29 +++++++- src/utils/smallut.h | 3 + 17 files changed, 416 insertions(+), 126 deletions(-) create mode 100644 src/qtgui/idxmodel.cpp create mode 100644 src/qtgui/idxmodel.h create mode 100644 src/qtgui/rclm_sidefilters.cpp diff --git a/src/Makefile.am b/src/Makefile.am index 7ac3d2d1..5cc95d1a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -410,6 +410,8 @@ qtgui/fragbuts.h \ qtgui/guiutils.cpp \ qtgui/guiutils.h \ qtgui/i18n/*.qm qtgui/i18n/*.ts \ +qtgui/idxmodel.cpp \ +qtgui/idxmodel.h \ qtgui/idxsched.h \ qtgui/idxsched.ui \ qtgui/images/asearch.png \ @@ -478,6 +480,7 @@ qtgui/rclm_idx.cpp \ qtgui/rclm_menus.cpp \ qtgui/rclm_preview.cpp \ qtgui/rclm_saveload.cpp \ +qtgui/rclm_sidefilters.cpp \ qtgui/rclm_view.cpp \ qtgui/rclm_wins.cpp \ qtgui/rclmain.ui \ diff --git a/src/qtgui/confgui/confguiindex.cpp b/src/qtgui/confgui/confguiindex.cpp index a9dc5507..341cec94 100644 --- a/src/qtgui/confgui/confguiindex.cpp +++ b/src/qtgui/confgui/confguiindex.cpp @@ -211,6 +211,7 @@ void ConfIndexW::acceptChanges() delete m_conf; m_conf = 0; m_rclconf->updateMainConfig(); + emit idxConfigPossiblyChanged(); } void ConfIndexW::initPanels() diff --git a/src/qtgui/confgui/confguiindex.h b/src/qtgui/confgui/confguiindex.h index be00ad68..4e647570 100644 --- a/src/qtgui/confgui/confguiindex.h +++ b/src/qtgui/confgui/confguiindex.h @@ -50,6 +50,9 @@ public slots: void showPrefs(bool modal); void acceptChanges(); QWidget *getDialog() {return m_w;} + +signals: + void idxConfigPossiblyChanged(); private: void initPanels(); diff --git a/src/qtgui/guiutils.cpp b/src/qtgui/guiutils.cpp index 2debd53e..fa96f01d 100644 --- a/src/qtgui/guiutils.cpp +++ b/src/qtgui/guiutils.cpp @@ -108,11 +108,10 @@ void rwSettings(bool writing) SETTING_RW(prefs.pvheight, "/Recoll/geometry/pvheight", Int, 0); SETTING_RW(prefs.ssearchTypSav, "/Recoll/prefs/ssearchTypSav", Bool, 0); SETTING_RW(prefs.ssearchTyp, "/Recoll/prefs/simpleSearchTyp", Int, 3); - SETTING_RW(prefs.startWithAdvSearchOpen, - "/Recoll/prefs/startWithAdvSearchOpen", Bool, false); + SETTING_RW(prefs.startWithAdvSearchOpen, "/Recoll/prefs/startWithAdvSearchOpen", Bool, false); SETTING_RW(prefs.previewHtml, "/Recoll/prefs/previewHtml", Bool, true); - SETTING_RW(prefs.previewActiveLinks, - "/Recoll/prefs/previewActiveLinks", Bool, false); + SETTING_RW(prefs.previewActiveLinks, "/Recoll/prefs/previewActiveLinks", Bool, false); + SETTING_RW(prefs.idxFilterTreeDepth, "/Recoll/prefs/ssearch/idxfiltertreedepth", Int, 2); QString advSearchClauses; const int maxclauselistsize = 20; @@ -149,24 +148,18 @@ void rwSettings(bool writing) } } - SETTING_RW(prefs.ssearchNoComplete, - "/Recoll/prefs/ssearch/noComplete", Bool, false); - SETTING_RW(prefs.ssearchStartOnComplete, - "/Recoll/prefs/ssearch/startOnComplete", Bool, true); + SETTING_RW(prefs.ssearchNoComplete, "/Recoll/prefs/ssearch/noComplete", Bool, false); + SETTING_RW(prefs.ssearchStartOnComplete, "/Recoll/prefs/ssearch/startOnComplete", Bool, true); SETTING_RW(prefs.filterCtlStyle, "/Recoll/prefs/filterCtlStyle", Int, 0); - SETTING_RW(prefs.ssearchAutoPhrase, - "/Recoll/prefs/ssearchAutoPhrase", Bool, true); - SETTING_RW(prefs.ssearchAutoPhraseThreshPC, + SETTING_RW(prefs.ssearchAutoPhrase, "/Recoll/prefs/ssearchAutoPhrase", Bool, true); + SETTING_RW(prefs.ssearchAutoPhraseThreshPC, "/Recoll/prefs/ssearchAutoPhraseThreshPC", Double, 2.0); SETTING_RW(prefs.respagesize, "/Recoll/prefs/reslist/pagelen", Int, 8); SETTING_RW(prefs.historysize, "/Recoll/prefs/historysize", Int, -1); - SETTING_RW(prefs.collapseDuplicates, - "/Recoll/prefs/reslist/collapseDuplicates", Bool, false); - SETTING_RW(prefs.showResultsAsTable, - "/Recoll/prefs/showResultsAsTable", Bool, false); + SETTING_RW(prefs.collapseDuplicates, "/Recoll/prefs/reslist/collapseDuplicates", Bool, false); + SETTING_RW(prefs.showResultsAsTable, "/Recoll/prefs/showResultsAsTable", Bool, false); - SETTING_RW(prefs.maxhltextkbs, "/Recoll/prefs/preview/maxhltextkbs", Int, - 3000); + SETTING_RW(prefs.maxhltextkbs, "/Recoll/prefs/preview/maxhltextkbs", Int, 3000); // Compat: if maxhltextkbs is not set but old maxhltextmbs is set use it if (!writing && !settings.contains("/Recoll/prefs/preview/maxhltextkbs") && settings.contains("/Recoll/prefs/preview/maxhltextmbs")) { @@ -174,14 +167,12 @@ void rwSettings(bool writing) "/Recoll/prefs/preview/maxhltextmbs").toInt() * 1024; } - SETTING_RW(prefs.previewPlainPre, - "/Recoll/prefs/preview/plainPre", Int, PrefsPack::PP_PREWRAP); + SETTING_RW(prefs.previewPlainPre, "/Recoll/prefs/preview/plainPre", Int, PrefsPack::PP_PREWRAP); // History: used to be able to only set a bare color name. Can now // set any CSS style. Hack on ':' presence to keep compat with old // values - SETTING_RW(prefs.qtermstyle, "/Recoll/prefs/qtermcolor", String, - "color: blue"); + SETTING_RW(prefs.qtermstyle, "/Recoll/prefs/qtermcolor", String, "color: blue"); if (!writing && prefs.qtermstyle == "") prefs.qtermstyle = "color: blue"; { // histo compatibility hack @@ -225,23 +216,17 @@ void rwSettings(bool writing) prefs.creslistformat = qs2utf8s(prefs.reslistformat); } - SETTING_RW(prefs.reslistheadertext, "/Recoll/prefs/reslist/headertext", - String, ""); + SETTING_RW(prefs.reslistheadertext, "/Recoll/prefs/reslist/headertext", String, ""); SETTING_RW(prefs.darkMode, "/Recoll/prefs/darkMode", Bool, 0); SETTING_RW(prefs.qssFile, "/Recoll/prefs/stylesheet", String, ""); SETTING_RW(prefs.snipCssFile, "/Recoll/prefs/snippets/cssfile", String, ""); - SETTING_RW(prefs.queryStemLang, "/Recoll/prefs/query/stemLang", String, - "english"); - SETTING_RW(prefs.useDesktopOpen, "/Recoll/prefs/useDesktopOpen", - Bool, true); + SETTING_RW(prefs.queryStemLang, "/Recoll/prefs/query/stemLang", String, "english"); + SETTING_RW(prefs.useDesktopOpen, "/Recoll/prefs/useDesktopOpen", Bool, true); - SETTING_RW(prefs.keepSort, - "/Recoll/prefs/keepSort", Bool, false); + SETTING_RW(prefs.keepSort, "/Recoll/prefs/keepSort", Bool, false); SETTING_RW(prefs.sortField, "/Recoll/prefs/sortField", String, ""); - SETTING_RW(prefs.sortActive, - "/Recoll/prefs/sortActive", Bool, false); - SETTING_RW(prefs.sortDesc, - "/Recoll/prefs/query/sortDesc", Bool, 0); + SETTING_RW(prefs.sortActive, "/Recoll/prefs/sortActive", Bool, false); + SETTING_RW(prefs.sortDesc, "/Recoll/prefs/query/sortDesc", Bool, 0); if (!writing) { // Handle transition from older prefs which did not store sortColumn // (Active always meant sort by date). @@ -249,29 +234,22 @@ void rwSettings(bool writing) prefs.sortField = "mtime"; } - SETTING_RW(prefs.queryBuildAbstract, - "/Recoll/prefs/query/buildAbstract", Bool, true); - SETTING_RW(prefs.queryReplaceAbstract, - "/Recoll/prefs/query/replaceAbstract", Bool, false); - SETTING_RW(prefs.syntAbsLen, "/Recoll/prefs/query/syntAbsLen", - Int, 250); - SETTING_RW(prefs.syntAbsCtx, "/Recoll/prefs/query/syntAbsCtx", - Int, 4); + SETTING_RW(prefs.queryBuildAbstract, "/Recoll/prefs/query/buildAbstract", Bool, true); + SETTING_RW(prefs.queryReplaceAbstract, "/Recoll/prefs/query/replaceAbstract", Bool, false); + SETTING_RW(prefs.syntAbsLen, "/Recoll/prefs/query/syntAbsLen", Int, 250); + SETTING_RW(prefs.syntAbsCtx, "/Recoll/prefs/query/syntAbsCtx", Int, 4); // Abstract snippet separator SETTING_RW(prefs.abssep, "/Recoll/prefs/reslist/abssep", String,"…"); if (!writing && prefs.abssep == "") prefs.abssep = "…"; SETTING_RW(prefs.snipwMaxLength, "/Recoll/prefs/snipwin/maxlen", Int, 1000); SETTING_RW(prefs.snipwSortByPage,"/Recoll/prefs/snipwin/bypage", Bool,false); - SETTING_RW(prefs.alwaysSnippets, "/Recoll/prefs/reslist/alwaysSnippets", - Bool,false); + SETTING_RW(prefs.alwaysSnippets, "/Recoll/prefs/reslist/alwaysSnippets", Bool,false); SETTING_RW(prefs.autoSuffs, "/Recoll/prefs/query/autoSuffs", String, ""); - SETTING_RW(prefs.autoSuffsEnable, - "/Recoll/prefs/query/autoSuffsEnable", Bool, false); + SETTING_RW(prefs.autoSuffsEnable, "/Recoll/prefs/query/autoSuffsEnable", Bool, false); - SETTING_RW(prefs.synFileEnable, - "/Recoll/prefs/query/synFileEnable", Bool, false); + SETTING_RW(prefs.synFileEnable, "/Recoll/prefs/query/synFileEnable", Bool, false); SETTING_RW(prefs.synFile, "/Recoll/prefs/query/synfile", String, ""); SETTING_RW(prefs.termMatchType, "/Recoll/prefs/query/termMatchType", @@ -284,20 +262,17 @@ void rwSettings(bool writing) // Ssearch combobox history list if (writing) { - settings.setValue("/Recoll/prefs/query/ssearchHistory", - prefs.ssearchHistory); + settings.setValue("/Recoll/prefs/query/ssearchHistory",prefs.ssearchHistory); } else { - prefs.ssearchHistory = - settings.value("/Recoll/prefs/query/ssearchHistory").toStringList(); + prefs.ssearchHistory = settings.value("/Recoll/prefs/query/ssearchHistory").toStringList(); } // Ignored file types (advanced search) if (writing) { - settings.setValue("/Recoll/prefs/query/asearchIgnFilTyps", - prefs.asearchIgnFilTyps); + settings.setValue("/Recoll/prefs/query/asearchIgnFilTyps", prefs.asearchIgnFilTyps); } else { - prefs.asearchIgnFilTyps = settings.value( - "/Recoll/prefs/query/asearchIgnFilTyps").toStringList(); + prefs.asearchIgnFilTyps = + settings.value("/Recoll/prefs/query/asearchIgnFilTyps").toStringList(); } SETTING_RW(prefs.fileTypesByCats, "/Recoll/prefs/query/asearchFilTypByCat", Bool, false); diff --git a/src/qtgui/guiutils.h b/src/qtgui/guiutils.h index 24849e7f..a92eb528 100644 --- a/src/qtgui/guiutils.h +++ b/src/qtgui/guiutils.h @@ -45,6 +45,7 @@ public: // toolbar+combobox or as a button group under simple search enum FilterCtlStyle {FCS_BT, FCS_CMB, FCS_MN}; int filterCtlStyle; + int idxFilterTreeDepth{2}; int respagesize{8}; int historysize{0}; int maxhltextkbs; diff --git a/src/qtgui/idxmodel.cpp b/src/qtgui/idxmodel.cpp new file mode 100644 index 00000000..67b7dfe0 --- /dev/null +++ b/src/qtgui/idxmodel.cpp @@ -0,0 +1,109 @@ +#include +#include +#include + +#include + +#include "recoll.h" +#include "rclconfig.h" +#include "fstreewalk.h" +#include "idxmodel.h" + + +class WalkerCB : public FsTreeWalkerCB { +public: + WalkerCB(RclConfig *config, const std::string& topstring, FsTreeWalker& walker, + IdxTreeModel *model, const QModelIndex& index) + : m_config(config), m_topstring(topstring), m_walker(walker), m_model(model) + { + m_indexes.push(index); + m_rows.push(0); + } + virtual FsTreeWalker::Status processone( + const std::string& path, const struct PathStat *, FsTreeWalker::CbFlag flg) override; + + RclConfig *m_config; + std::string m_topstring; + FsTreeWalker& m_walker; + IdxTreeModel *m_model; + std::stack m_indexes; + std::stack m_rows; +}; + +FsTreeWalker::Status WalkerCB::processone( + const std::string& path, const struct PathStat *, FsTreeWalker::CbFlag flg) +{ + if (flg == FsTreeWalker::FtwDirEnter || flg == FsTreeWalker::FtwDirReturn) { + m_config->setKeyDir(path); + // Set up filter/skipped patterns for this subtree. + m_walker.setOnlyNames(m_config->getOnlyNames()); + m_walker.setSkippedNames(m_config->getSkippedNames()); + } + if (flg == FsTreeWalker::FtwDirReturn) { + m_indexes.pop(); + m_rows.pop(); + return FsTreeWalker::FtwOk; + } + if (flg == FsTreeWalker::FtwDirEnter) { + if (m_model->columnCount(m_indexes.top()) == 0) { + if (!m_model->insertColumn(0, m_indexes.top())) + return FsTreeWalker::FtwError; + } + if (!m_model->insertRow(m_rows.top(), m_indexes.top())) + return FsTreeWalker::FtwError; + const QModelIndex child = m_model->index(m_rows.top(), 0, m_indexes.top()); + // Setting the short path in DisplayRole and the real one in EditRole does not seem to work, + // the treeview shows the EditRole?? So use the ToolTip to store the full value + std::string disp; + if (m_topstring.empty()) { + disp = path_getsimple(path); + } else { + disp = m_topstring; + m_topstring.clear(); + } + m_model->setData(child, QVariant(path2qs(disp)), Qt::DisplayRole); + m_model->setData(child, QVariant(path2qs(path)), Qt::ToolTipRole); + ++m_rows.top(); + m_indexes.push(child); + m_rows.push(0); + } + return FsTreeWalker::FtwOk; +} + +static void populateDir(RclConfig *config, const std::string& topstr, IdxTreeModel *model, + const QModelIndex& index, const std::string& path, int depth) +{ + FsTreeWalker walker; + walker.setSkippedPaths(config->getSkippedPaths()); +// walker.setOpts(walker.getOpts() | FsTreeWalker::FtwSkipDotFiles); + walker.setMaxDepth(depth); + WalkerCB cb(config, topstr, walker, model, index); + walker.walk(path, cb); +} + +void IdxTreeModel::populate() +{ + QModelIndex index = this->index(0,0); + auto topdirs = m_config->getTopdirs(); + + auto prefix = commonprefix(topdirs); + + if (this->columnCount(index) == 0) { + if (!this->insertColumn(0, index)) + return; + } + + int row = 0; + for (const auto& topdir : topdirs) { + const QModelIndex child = this->index(row, 0, index); + std::string topdisp; + if (prefix.size() > 1) { + topdisp = topdir.substr(prefix.size()); + } else { + topdisp = topdir; + } + populateDir(m_config, topdisp, this, child, topdir, m_depth); + ++row; + } + sort(0, Qt::AscendingOrder); +} diff --git a/src/qtgui/idxmodel.h b/src/qtgui/idxmodel.h new file mode 100644 index 00000000..4e3edaaf --- /dev/null +++ b/src/qtgui/idxmodel.h @@ -0,0 +1,42 @@ +/* Copyright (C) 2022 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 _IDXMODEL_H +#define _IDXMODEL_H + +#include + +class RclConfig; + +class IdxTreeModel : public QStandardItemModel { + Q_OBJECT; +public: + IdxTreeModel(RclConfig *config, int depth, QWidget *parent = nullptr) + : QStandardItemModel(0, 0, (QObject*)parent), m_config(config), m_depth(depth) { + } + ~IdxTreeModel() {} + void populate(); + int getDepth() {return m_depth;} +private: + RclConfig *m_config{nullptr}; + int m_depth; +}; + +#endif // _IDXMODEL_H diff --git a/src/qtgui/rclm_sidefilters.cpp b/src/qtgui/rclm_sidefilters.cpp new file mode 100644 index 00000000..74c10d06 --- /dev/null +++ b/src/qtgui/rclm_sidefilters.cpp @@ -0,0 +1,54 @@ +/* Copyright (C) 2022 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 "log.h" +#include "rclmain_w.h" +#include "idxmodel.h" +#include "guiutils.h" + +void RclMain::populateFilters() +{ + m_idxtreemodel = new IdxTreeModel(theconfig, prefs.idxFilterTreeDepth, idxTreeView); + m_idxtreemodel->populate(); + m_idxtreemodel->setHeaderData(0, Qt::Horizontal, QVariant(tr("Filter directories"))); + idxTreeView->setModel(m_idxtreemodel); + idxTreeView->setEditTriggers(QAbstractItemView::NoEditTriggers); + idxTreeView->setSelectionMode(QAbstractItemView::MultiSelection); +} + +void RclMain::clearDirFilter() +{ + idxTreeView->clearSelection(); +} + +std::vector RclMain::idxTreeGetDirs() +{ + std::vector out; + auto selmodel = idxTreeView->selectionModel(); + if (nullptr != selmodel) { + auto selected = selmodel->selectedIndexes(); + std::string clause; + for (int i = 0; i < selected.size(); i++) { + out.push_back(qs2path(selected[i].data(Qt::ToolTipRole).toString())); + } + } + return out; +} diff --git a/src/qtgui/rclm_wins.cpp b/src/qtgui/rclm_wins.cpp index a1195238..de1d370c 100644 --- a/src/qtgui/rclm_wins.cpp +++ b/src/qtgui/rclm_wins.cpp @@ -190,6 +190,7 @@ void RclMain::showIndexConfig(bool modal) if (created) { connect(new QShortcut(quitKeySeq, indexConfig->getDialog()), SIGNAL (activated()), this, SLOT (fileExit())); + connect(indexConfig, SIGNAL(idxConfigPossiblyChanged()), this, SLOT(populateFilters())); } } diff --git a/src/qtgui/rclmain.ui b/src/qtgui/rclmain.ui index beaf8421..334dfd3f 100644 --- a/src/qtgui/rclmain.ui +++ b/src/qtgui/rclmain.ui @@ -20,41 +20,36 @@ Recoll - - - 0 - - - 4 - - - 2 - - - 4 - - - 2 - + - - - - 0 - 0 - - - - - - - - - 2 - 0 - - - + + + + + + 0 + 0 + + + + + + + + Qt::Horizontal + + + + + + 2 + 0 + + + + + + diff --git a/src/qtgui/rclmain_w.cpp b/src/qtgui/rclmain_w.cpp index 966f8340..885d1c62 100644 --- a/src/qtgui/rclmain_w.cpp +++ b/src/qtgui/rclmain_w.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include "recoll.h" #include "log.h" @@ -71,10 +72,12 @@ #include "readfile.h" #include "moc_rclmain_w.cpp" #include "scbase.h" +#include "idxmodel.h" QString g_stringAllStem, g_stringNoStem; static const char *settingskey_toolarea="/Recoll/geometry/toolArea"; static const char *settingskey_resarea="/Recoll/geometry/resArea"; +static const char *settingskey_sidefilterssize = "/Recoll/geometry/sideFilters"; static Qt::ToolBarArea int2area(int in) { @@ -277,6 +280,18 @@ void RclMain::init() QSettings settings; restoreGeometry(settings.value("/Recoll/geometry/maingeom").toByteArray()); + QVariant saved = settings.value(settingskey_sidefilterssize); + if (saved != QVariant()) { + sideFiltersSPLT->restoreState(saved.toByteArray()); + } else { + QList sizes; + sizes << 200 << 600; + sideFiltersSPLT->setSizes(sizes); + } + + populateFilters(); + connect(idxTreeView, SIGNAL(clicked(const QModelIndex&)), this, SLOT(setFiltSpec())); + enableTrayIcon(prefs.showTrayIcon); fileRebuildIndexAction->setEnabled(false); @@ -692,6 +707,7 @@ void RclMain::fileExit() settings.setValue(settingskey_resarea, toolBarArea(m_resTB)); } restable->saveColState(); + settings.setValue(settingskey_sidefilterssize, sideFiltersSPLT->saveState()); if (prefs.ssearchTypSav) { prefs.ssearchTyp = sSearch->searchTypCMB->currentIndex(); @@ -769,13 +785,13 @@ class QueryThread : public QThread { public: QueryThread(std::shared_ptr source) : m_source(source) - { - } + { + } ~QueryThread() { } virtual void run() - { - cnt = m_source->getResCnt(); - } + { + cnt = m_source->getResCnt(); + } int cnt; }; @@ -1134,6 +1150,9 @@ void RclMain::setUIPrefs() if (!uiprefs) return; LOGDEB("Recollmain::setUIPrefs\n"); + if (nullptr != m_idxtreemodel && m_idxtreemodel->getDepth() != prefs.idxFilterTreeDepth) { + populateFilters(); + } emit uiPrefsChanged(); enbSynAction->setDisabled(prefs.synFile.isEmpty()); enbSynAction->setChecked(prefs.synFileEnable); @@ -1215,12 +1234,26 @@ void RclMain::setFiltSpec() if (fragbuts) { vector frags; fragbuts->getfrags(frags); - for (vector::const_iterator it = frags.begin(); - it != frags.end(); it++) { - m_filtspec.orCrit(DocSeqFiltSpec::DSFS_QLANG, *it); + for (const auto& frag : frags) { + m_filtspec.orCrit(DocSeqFiltSpec::DSFS_QLANG, frag); } } + auto treedirs = idxTreeGetDirs(); + bool first{true}; + const std::string prefix{"dir:"}; + std::string clause; + for (const auto& dir : treedirs) { + if (first) { + first = false; + } else { + clause += " OR "; + } + clause += prefix + makeCString(dir); + } + LOGDEB0("Sidefilter dir clause: [" << clause << "]\n"); + m_filtspec.orCrit(DocSeqFiltSpec::DSFS_QLANG, clause); + if (m_source) m_source->setFiltSpec(m_filtspec); initiateQuery(); diff --git a/src/qtgui/rclmain_w.h b/src/qtgui/rclmain_w.h index 6939c48c..8f76d773 100644 --- a/src/qtgui/rclmain_w.h +++ b/src/qtgui/rclmain_w.h @@ -51,6 +51,7 @@ class RclTrayIcon; class QShortcut; class QActionGroup; class ActSearchW; +class IdxTreeModel; #include "ui_rclmain.h" @@ -106,8 +107,7 @@ public slots: virtual void bumpIndexing(); virtual void rebuildIndex(); virtual void specialIndex(); - virtual void startSearch(std::shared_ptr sdata, - bool issimple); + virtual void startSearch(std::shared_ptr sdata, bool issimple); virtual void previewClosed(Preview *w); virtual void showAdvSearchDialog(); virtual void showSpellDialog(); @@ -144,6 +144,7 @@ public slots: virtual void startNativeViewer(Rcl::Doc, int pagenum = -1, QString term = QString()); virtual void openWith(Rcl::Doc, string); virtual void saveDocToFile(Rcl::Doc); + virtual void populateFilters(); virtual void previewNextInTab(Preview *, int sid, int docnum); virtual void previewPrevInTab(Preview *, int sid, int docnum); virtual void previewExposed(Preview *, int sid, int docnum); @@ -171,9 +172,11 @@ public slots: virtual void onSetDescription(QString); virtual void onNewShortcuts(); virtual void toggleTable(); + virtual void clearDirFilter(); virtual void hideToolTip(); virtual void zoomIn(); virtual void zoomOut(); + virtual void setFiltSpec(); private slots: virtual bool updateIdxStatus(); @@ -230,6 +233,7 @@ private: QShortcut *m_clearsearchsc{0}; QShortcut *m_toggletablesc{0}; QShortcut *m_actionssearchsc{0}; + QShortcut *m_cleardirfiltersc{0}; QFileSystemWatcher m_watcher; vector m_viewers; ExecCmd *m_idxproc{0}; // Indexing process @@ -257,7 +261,8 @@ private: RclTrayIcon *m_trayicon{0}; // We sometimes take the indexer lock (e.g.: when editing the webcache) Pidfile *m_pidfile{0}; - + IdxTreeModel *m_idxtreemodel{nullptr}; + // Menu for the button version of the top menu. QMenu *buttonTopMenu; // Entries/submenus for the top menu. @@ -288,8 +293,8 @@ private: virtual void updateIdxForDocs(vector&); virtual void initiateQuery(); virtual bool containerUpToDate(Rcl::Doc& doc); - virtual void setFiltSpec(); virtual bool checkIdxPaths(); + virtual std::vector idxTreeGetDirs(); }; #endif // RCLMAIN_W_H diff --git a/src/qtgui/recoll.pro.in b/src/qtgui/recoll.pro.in index b3c331ac..358e0a48 100644 --- a/src/qtgui/recoll.pro.in +++ b/src/qtgui/recoll.pro.in @@ -35,6 +35,7 @@ HEADERS += \ crontool.h \ firstidx.h \ fragbuts.h \ + idxmodel.h \ idxsched.h \ preview_load.h \ preview_plaintorich.h \ @@ -68,6 +69,7 @@ SOURCES += \ crontool.cpp \ fragbuts.cpp \ guiutils.cpp \ + idxmodel.cpp \ main.cpp \ multisave.cpp \ preview_load.cpp \ @@ -79,6 +81,7 @@ SOURCES += \ rclm_menus.cpp \ rclm_preview.cpp \ rclm_saveload.cpp \ + rclm_sidefilters.cpp \ rclm_view.cpp \ rclm_wins.cpp \ rclmain_w.cpp \ diff --git a/src/qtgui/uiprefs.ui b/src/qtgui/uiprefs.ui index 739fb322..dc81641e 100644 --- a/src/qtgui/uiprefs.ui +++ b/src/qtgui/uiprefs.ui @@ -7,7 +7,7 @@ 0 0 731 - 652 + 684 @@ -137,6 +137,52 @@ + + + + + + + 1 + 0 + + + + Depth of side filter directory tree + + + false + + + + + + + 0 + + + 99 + + + 2 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + @@ -1531,12 +1577,12 @@ May be slow for big documents. + + The bug causes a strange circle characters to be displayed inside highlighted Tamil words. The workaround inserts an additional space character which appears to fix the problem. + Work around Tamil QTBUG-78923 by inserting space before anchor text - - The bug causes a strange circle characters to be displayed inside highlighted Tamil words. The workaround inserts an additional space character which appears to fix the problem. - diff --git a/src/qtgui/uiprefs_w.cpp b/src/qtgui/uiprefs_w.cpp index 0ccd7458..04475cfd 100644 --- a/src/qtgui/uiprefs_w.cpp +++ b/src/qtgui/uiprefs_w.cpp @@ -79,28 +79,19 @@ void UIPrefsDialog::init() connect(synFilePB, SIGNAL(clicked()),this, SLOT(showSynFileDialog())); connect(resetSnipCssPB, SIGNAL(clicked()), this, SLOT(resetSnipCss())); - connect(idxLV, SIGNAL(itemSelectionChanged()), - this, SLOT(extradDbSelectChanged())); - connect(ptransPB, SIGNAL(clicked()), - this, SLOT(extraDbEditPtrans())); - connect(addExtraDbPB, SIGNAL(clicked()), - this, SLOT(addExtraDbPB_clicked())); - connect(delExtraDbPB, SIGNAL(clicked()), - this, SLOT(delExtraDbPB_clicked())); - connect(togExtraDbPB, SIGNAL(clicked()), - this, SLOT(togExtraDbPB_clicked())); - connect(actAllExtraDbPB, SIGNAL(clicked()), - this, SLOT(actAllExtraDbPB_clicked())); - connect(unacAllExtraDbPB, SIGNAL(clicked()), - this, SLOT(unacAllExtraDbPB_clicked())); + connect(idxLV, SIGNAL(itemSelectionChanged()), this, SLOT(extradDbSelectChanged())); + connect(ptransPB, SIGNAL(clicked()), this, SLOT(extraDbEditPtrans())); + connect(addExtraDbPB, SIGNAL(clicked()), this, SLOT(addExtraDbPB_clicked())); + connect(delExtraDbPB, SIGNAL(clicked()), this, SLOT(delExtraDbPB_clicked())); + connect(togExtraDbPB, SIGNAL(clicked()), this, SLOT(togExtraDbPB_clicked())); + connect(actAllExtraDbPB, SIGNAL(clicked()), this, SLOT(actAllExtraDbPB_clicked())); + connect(unacAllExtraDbPB, SIGNAL(clicked()), this, SLOT(unacAllExtraDbPB_clicked())); connect(CLEditPara, SIGNAL(clicked()), this, SLOT(editParaFormat())); connect(CLEditHeader, SIGNAL(clicked()), this, SLOT(editHeaderText())); connect(buttonOk, SIGNAL(clicked()), this, SLOT(accept())); connect(buttonCancel, SIGNAL(clicked()), this, SLOT(reject())); - connect(buildAbsCB, SIGNAL(toggled(bool)), - replAbsCB, SLOT(setEnabled(bool))); - connect(ssNoCompleteCB, SIGNAL(toggled(bool)), - ssSearchOnCompleteCB, SLOT(setDisabled(bool))); + connect(buildAbsCB, SIGNAL(toggled(bool)), replAbsCB, SLOT(setEnabled(bool))); + connect(ssNoCompleteCB, SIGNAL(toggled(bool)), ssSearchOnCompleteCB, SLOT(setDisabled(bool))); connect(resetscPB, SIGNAL(clicked()), this, SLOT(resetShortcuts())); (void)new HelpClient(this); @@ -117,6 +108,7 @@ void UIPrefsDialog::setFromPrefs() // Entries per result page spinbox pageLenSB->setValue(prefs.respagesize); + idxTreeDepthSB->setValue(prefs.idxFilterTreeDepth); maxHistSizeSB->setValue(prefs.historysize); collapseDupsCB->setChecked(prefs.collapseDuplicates); maxHLTSB->setValue(prefs.maxhltextkbs); @@ -384,6 +376,7 @@ void UIPrefsDialog::accept() m_mainWindow->setFilterCtlStyle(prefs.filterCtlStyle); prefs.respagesize = pageLenSB->value(); + prefs.idxFilterTreeDepth = idxTreeDepthSB->value(); prefs.historysize = maxHistSizeSB->value(); prefs.collapseDuplicates = collapseDupsCB->isChecked(); prefs.maxhltextkbs = maxHLTSB->value(); diff --git a/src/utils/smallut.cpp b/src/utils/smallut.cpp index 4299962d..9d800191 100644 --- a/src/utils/smallut.cpp +++ b/src/utils/smallut.cpp @@ -322,6 +322,30 @@ template void stringsToCSV(const T& tokens, string& s, char sep) s.pop_back(); } +template std::string commonprefix(const T& values) +{ + if (values.empty()) + return std::string(); + if (values.size() == 1) + return *values.begin(); + unsigned int i = 0; + for (;;i++) { + auto it = values.begin(); + if (it->size() <= i) { + goto out; + } + auto val = (*it)[i]; + it++; + for (;it < values.end(); it++) { + if (it->size() <= i || (*it)[i] != val) { + goto out; + } + } + } +out: + return values.begin()->substr(0, i); +} + #ifdef SMALLUT_EXTERNAL_INSTANTIATIONS #include "smallut_instantiate.h" #else @@ -342,8 +366,8 @@ template string stringsToString >(const vector&); template string stringsToString >(const set&); template string stringsToString >(const unordered_set&); template void stringsToCSV >(const list&, string&, char); -template void stringsToCSV >(const vector&, string&, - char); +template void stringsToCSV >(const vector&, string&, char); +template string commonprefix>(const vector&values); #endif void stringToTokens(const string& str, vector& tokens, @@ -571,7 +595,6 @@ string makeCString(const string& in) return out; } - // Substitute printf-like percent cmds inside a string bool pcSubst(const string& in, string& out, const map& subs) { diff --git a/src/utils/smallut.h b/src/utils/smallut.h index 04300a47..b3cd499a 100644 --- a/src/utils/smallut.h +++ b/src/utils/smallut.h @@ -136,6 +136,9 @@ template std::string stringsToString(const T& tokens); template void stringsToCSV(const T& tokens, std::string& s, char sep = ','); +/** Find longest common prefix for bunch of strings */ +template std::string commonprefix(const T& values); + /** * Split input string. No handling of quoting. */