diff --git a/src/common/rclconfig.cpp b/src/common/rclconfig.cpp index 6d4380cf..c9da7487 100644 --- a/src/common/rclconfig.cpp +++ b/src/common/rclconfig.cpp @@ -576,6 +576,24 @@ string RclConfig::getMimeHandlerDef(const string &mtype, bool filtertypes) return hs; } +bool RclConfig::getGuiFilterNames(list& cats) +{ + if (!mimeconf) + return false; + cats = mimeconf->getNamesShallow("guifilters"); + return true; +} + +bool RclConfig::getGuiFilter(const string& catfiltername, string& frag) +{ + frag.clear(); + if (!mimeconf) + return false; + if (!mimeconf->get(catfiltername, frag, "guifilters")) + return false; + return true; +} + bool RclConfig::valueSplitAttributes(const string& whole, string& value, ConfSimple& attrs) { diff --git a/src/common/rclconfig.h b/src/common/rclconfig.h index 86b2637f..bf2ea680 100644 --- a/src/common/rclconfig.h +++ b/src/common/rclconfig.h @@ -202,6 +202,11 @@ class RclConfig { /** mimeconf: get list of mime types for category */ bool getMimeCatTypes(const string& cat, list&); + /** mimeconf: get list of gui filters (doc cats by default */ + bool getGuiFilterNames(list&); + /** mimeconf: get query lang frag for named filter */ + bool getGuiFilter(const string& filtername, string& frag); + /** fields: get field prefix from field name */ bool getFieldTraits(const string& fldname, const FieldTraits **); const set& getStoredFields() {return m_storedFields;} diff --git a/src/qtgui/rclmain_w.cpp b/src/qtgui/rclmain_w.cpp index 4fedc28c..8c73789e 100644 --- a/src/qtgui/rclmain_w.cpp +++ b/src/qtgui/rclmain_w.cpp @@ -176,7 +176,7 @@ void RclMain::init() connect(bgrp, SIGNAL(buttonClicked(int)), this, SLOT(catgFilter(int))); allRDB->setChecked(true); list cats; - theconfig->getMimeCategories(cats); + theconfig->getGuiFilterNames(cats); // Text for button 0 is not used. Next statement just avoids unused // variable compiler warning for catg_strings m_catgbutvec.push_back(catg_strings[0]); @@ -1626,7 +1626,7 @@ void RclMain::showDocHistory() new DocSequenceHistory(rcldb, g_dynconf, string(tr("Document history").toUtf8())); src->setDescription((const char *)tr("History data").toUtf8()); - DocSource *source = new DocSource(RefCntr(src)); + DocSource *source = new DocSource(theconfig, RefCntr(src)); m_source = RefCntr(source); m_source->setSortSpec(m_sortspec); m_source->setFiltSpec(m_filtspec); @@ -1695,7 +1695,8 @@ void RclMain::showQueryDetails() return; string oq = breakIntoLines(m_source->getDescription(), 100, 50); QString str; - QString desc = tr("Result count (est.)") + ": " + str.setNum(m_source->getResCnt()) + "
"; + QString desc = tr("Result count (est.)") + ": " + + str.setNum(m_source->getResCnt()) + "
"; desc += tr("Query details") + ": " + QString::fromUtf8(oq.c_str()); QMessageBox::information(this, tr("Query details"), desc); } @@ -1711,12 +1712,11 @@ void RclMain::catgFilter(int id) if (id != 0) { string catg = m_catgbutvec[id]; - list tps; - theconfig->getMimeCatTypes(catg, tps); - for (list::const_iterator it = tps.begin(); - it != tps.end(); it++) - m_filtspec.orCrit(DocSeqFiltSpec::DSFS_MIMETYPE, *it); + string frag; + theconfig->getGuiFilter(catg, frag); + m_filtspec.orCrit(DocSeqFiltSpec::DSFS_QLANG, frag); } + LOGDEB(("RclMain::catgFilter: calling setFiltSpec\n")); if (m_source.isNotNull()) m_source->setFiltSpec(m_filtspec); initiateQuery(); diff --git a/src/qtgui/reslist.cpp b/src/qtgui/reslist.cpp index dd3ba140..047dc3f2 100644 --- a/src/qtgui/reslist.cpp +++ b/src/qtgui/reslist.cpp @@ -315,7 +315,7 @@ extern "C" int XFlush(void *); void ResList::setDocSource(RefCntr nsource) { LOGDEB(("ResList::setDocSource()\n")); - m_source = RefCntr(new DocSource(nsource)); + m_source = RefCntr(new DocSource(theconfig, nsource)); } // A query was executed, or the filtering/sorting parameters changed, diff --git a/src/qtgui/restable.cpp b/src/qtgui/restable.cpp index ee62ad4e..6f99b942 100644 --- a/src/qtgui/restable.cpp +++ b/src/qtgui/restable.cpp @@ -298,7 +298,7 @@ void RecollModel::setDocSource(RefCntr nsource) if (nsource.isNull()) { m_source = RefCntr(); } else { - m_source = RefCntr(new DocSource(nsource)); + m_source = RefCntr(new DocSource(theconfig, nsource)); m_hdata.reset(); m_source->getTerms(m_hdata.terms, m_hdata.groups, m_hdata.gslks); } diff --git a/src/query/docseq.cpp b/src/query/docseq.cpp index 47639fec..b080c456 100644 --- a/src/query/docseq.cpp +++ b/src/query/docseq.cpp @@ -62,7 +62,7 @@ bool DocSource::buildStack() } else { if (m_fspec.isNotNull()) { m_seq = - RefCntr(new DocSeqFiltered(m_seq, m_fspec)); + RefCntr(new DocSeqFiltered(m_config, m_seq, m_fspec)); } } diff --git a/src/query/docseq.h b/src/query/docseq.h index c98ea694..e14e45c5 100644 --- a/src/query/docseq.h +++ b/src/query/docseq.h @@ -49,7 +49,7 @@ class DocSeqSortSpec { class DocSeqFiltSpec { public: DocSeqFiltSpec() {} - enum Crit {DSFS_MIMETYPE}; + enum Crit {DSFS_MIMETYPE, DSFS_QLANG, DSFS_PASSALL}; void orCrit(Crit crit, const string& value) { crits.push_back(crit); values.push_back(value); @@ -191,12 +191,13 @@ protected: RefCntr m_seq; }; +class RclConfig; // A DocSource can juggle docseqs of different kinds to implement // sorting and filtering in ways depending on the base seqs capabilities class DocSource : public DocSeqModifier { public: - DocSource(RefCntr iseq) - : DocSeqModifier(iseq) + DocSource(RclConfig *config, RefCntr iseq) + : DocSeqModifier(iseq), m_config(config) {} virtual bool canFilter() {return true;} virtual bool canSort() {return true;} @@ -218,6 +219,7 @@ public: private: bool buildStack(); void stripStack(); + RclConfig *m_config; DocSeqFiltSpec m_fspec; DocSeqSortSpec m_sspec; }; diff --git a/src/query/docseqdb.cpp b/src/query/docseqdb.cpp index e76e8ef2..95c66a9d 100644 --- a/src/query/docseqdb.cpp +++ b/src/query/docseqdb.cpp @@ -21,6 +21,7 @@ #include "rcldb.h" #include "debuglog.h" #include "internfile.h" +#include "wasatorcl.h" DocSequenceDb::DocSequenceDb(RefCntr q, const string &t, RefCntr sdata) @@ -126,6 +127,26 @@ bool DocSequenceDb::setFiltSpec(const DocSeqFiltSpec &fs) switch (fs.crits[i]) { case DocSeqFiltSpec::DSFS_MIMETYPE: m_fsdata->addFiletype(fs.values[i]); + break; + case DocSeqFiltSpec::DSFS_QLANG: + { + if (m_q.isNull()) + break; + + string reason; + Rcl::SearchData *sd = + wasaStringToRcl(m_q->whatDb()->getConf(), + fs.values[i], reason); + if (sd) { + Rcl::SearchDataClauseSub *cl1 = + new Rcl::SearchDataClauseSub(Rcl::SCLT_SUB, + RefCntr(sd)); + m_fsdata->addClause(cl1); + } + } + break; + default: + break; } } m_isFiltered = true; diff --git a/src/query/filtseq.cpp b/src/query/filtseq.cpp index c1824438..1bf16ee2 100644 --- a/src/query/filtseq.cpp +++ b/src/query/filtseq.cpp @@ -14,39 +14,87 @@ * Free Software Foundation, Inc., * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#include #include "debuglog.h" #include "filtseq.h" +#include "rclconfig.h" using std::string; static bool filter(const DocSeqFiltSpec& fs, const Rcl::Doc *x) { + LOGDEB2((" Filter: ncrits %d\n", fs.crits.size())); // Compare using each criterion in term. We're doing an or: // 1st ok ends for (unsigned int i = 0; i < fs.crits.size(); i++) { switch (fs.crits[i]) { case DocSeqFiltSpec::DSFS_MIMETYPE: - LOGDEB1((" MIMETYPE\n")); + LOGDEB2((" filter: MIMETYPE: me [%s] doc [%s]\n", + fs.values[i].c_str(), x->mimetype.c_str())); if (x->mimetype == fs.values[i]) - return 1; + return true; + break; + case DocSeqFiltSpec::DSFS_QLANG: + { + LOGDEB((" filter: QLANG [%s]!!\n", fs.values[i].c_str())); + } + break; + case DocSeqFiltSpec::DSFS_PASSALL: + return true; } } // Did all comparisons - return 0; + return false; } +DocSeqFiltered::DocSeqFiltered(RclConfig *conf, RefCntr iseq, + DocSeqFiltSpec &filtspec) + : DocSeqModifier(iseq), m_config(conf) +{ + setFiltSpec(filtspec); +} + bool DocSeqFiltered::setFiltSpec(DocSeqFiltSpec &filtspec) { - m_spec = filtspec; + LOGDEB0(("DocSeqFiltered::setFiltSpec\n")); + for (unsigned int i = 0; i < filtspec.crits.size(); i++) { + switch (filtspec.crits[i]) { + case DocSeqFiltSpec::DSFS_MIMETYPE: + m_spec.orCrit(filtspec.crits[i], filtspec.values[i]); + break; + case DocSeqFiltSpec::DSFS_QLANG: + { + // There are very few lang constructs that we can + // interpret. The default config uses rclcat:value + // only. That will be all for now... + string val = filtspec.values[i]; + if (val.find("rclcat:") == 0) { + string catg = val.substr(7); + list tps; + m_config->getMimeCatTypes(catg, tps); + for (list::const_iterator it = tps.begin(); + it != tps.end(); it++) { + LOGDEB2(("Adding mime: [%s]\n", it->c_str())); + m_spec.orCrit(DocSeqFiltSpec::DSFS_MIMETYPE, *it); + } + } + } + break; + default: + break; + } + } + // If m_spec ends up empty, pass everything, better than filtering all. + if (m_spec.crits.empty()) { + m_spec.orCrit(DocSeqFiltSpec::DSFS_PASSALL, ""); + } m_dbindices.clear(); return true; } bool DocSeqFiltered::getDoc(int idx, Rcl::Doc &doc, string *) { - LOGDEB1(("DocSeqFiltered: fetching %d\n", idx)); + LOGDEB2(("DocSeqFiltered::getDoc() fetching %d\n", idx)); if (idx >= (int)m_dbindices.size()) { // Have to fetch docs and filter until we get enough or diff --git a/src/query/filtseq.h b/src/query/filtseq.h index 30bba127..11282810 100644 --- a/src/query/filtseq.h +++ b/src/query/filtseq.h @@ -19,10 +19,13 @@ #include #include +using std::string; +using std::vector; #include "refcntr.h" #include "docseq.h" +class RclConfig; /** * A filtered sequence is created from another one by selecting entries @@ -30,17 +33,17 @@ */ class DocSeqFiltered : public DocSeqModifier { public: - DocSeqFiltered(RefCntr iseq, DocSeqFiltSpec &filtspec) - : DocSeqModifier(iseq), m_spec(filtspec) - {} + DocSeqFiltered(RclConfig *conf, RefCntr iseq, + DocSeqFiltSpec &filtspec); virtual ~DocSeqFiltered() {} virtual bool canFilter() {return true;} virtual bool setFiltSpec(DocSeqFiltSpec &filtspec); virtual bool getDoc(int num, Rcl::Doc &doc, string *sh = 0); virtual int getResCnt() {return m_seq->getResCnt();} private: - DocSeqFiltSpec m_spec; - vector m_dbindices; + RclConfig *m_config; + DocSeqFiltSpec m_spec; + vector m_dbindices; }; #endif /* _FILTSEQ_H_INCLUDED_ */ diff --git a/src/sampleconf/mimeconf b/src/sampleconf/mimeconf index d2ebf12d..9586e060 100644 --- a/src/sampleconf/mimeconf +++ b/src/sampleconf/mimeconf @@ -194,7 +194,12 @@ text/x-purple-html-log = pidgin text/x-python = text-x-python [categories] - +# Categories group mime types by "kind". They can be used from the query +# language as an "rclcat" clause. This is fully dynamic, you can change the +# names and groups as you wish, only the mime types are stored in the index. +# +# If you add/remove categories, you may also want to change the +# "guifilters" section below. text = \ application/msword \ application/pdf \ @@ -280,3 +285,26 @@ other = application/vnd.sun.xml.draw \ application/x-rar \ application/x-webarchive \ application/zip \ + +[guifilters] +# This defines the top level filters in the GUI (accessed by the the +# radiobuttons above the results area, or a toolbar combobox). +# Each entry defines a label and a query language fragment that will be +# applied to filter the current query if the option is activated. +# +# This does not really belong in mimeconf, but it does belong in the index +# config (not the GUI one), because it's not necessarily the same in all +# configs, it has to go somewhere, and it's not worth a separate config +# file... +# +# By default this filters by document category (see above), but any +# language fragment should be ok. Be aware though that the "document +# history" queries only know about simple "rclcat" filtering. + +text = rclcat:text +spreadsheet = rclcat:spreadsheet +presentation = rclcat:presentation +media = rclcat:media +message = rclcat:message +other = rclcat:other +