From 469c5449151665fddae10e4803c63d30e0ea28e1 Mon Sep 17 00:00:00 2001 From: Jean-Francois Dockes Date: Thu, 7 Jul 2011 11:11:02 +0200 Subject: [PATCH] GUI: allow setting the snippet separator inside abstract (now a real html ellipsis by default) --- src/doc/user/usermanual.sgml | 18 ++++++++---- src/qtgui/guiutils.cpp | 5 ++++ src/qtgui/guiutils.h | 2 ++ src/qtgui/reslist.cpp | 1 + src/qtgui/restable.cpp | 1 + src/qtgui/uiprefs.ui | 24 ++++++++++++++++ src/qtgui/uiprefs_w.cpp | 6 +++- src/query/docseq.h | 25 ++++++++++++++--- src/query/docseqdb.cpp | 19 ++++++------- src/query/docseqdb.h | 2 +- src/query/reslistpager.cpp | 23 ++++++++++------ src/query/reslistpager.h | 2 +- src/rcldb/rcldb.cpp | 53 +++++++++++++++++++++++++----------- src/rcldb/rcldb.h | 1 + src/rcldb/rcldb_p.h | 2 +- 15 files changed, 135 insertions(+), 49 deletions(-) diff --git a/src/doc/user/usermanual.sgml b/src/doc/user/usermanual.sgml index 0ad4f6fc..e22dfe11 100644 --- a/src/doc/user/usermanual.sgml +++ b/src/doc/user/usermanual.sgml @@ -1525,6 +1525,14 @@ fvwm described in its own section. + + Abstract snippet separator: + for synthetic abstracts built from index data, which are + usually made of several snippets from different parts of the + document, this defines the snippet separator, an ellipsis by + default. + + Maximum text size highlighted for preview Inserting highlights on search term inside the text before inserting it in the preview window involves @@ -1754,12 +1762,10 @@ fvwm preview link. - Due to the way the program handles right mouse clicks in the - result list, if the custom formatting results in multiple - paragraphs per result, right clicks will only work inside the first - one. - - + It is also possible to + + define the value of the snippet separator inside the abstract + section. diff --git a/src/qtgui/guiutils.cpp b/src/qtgui/guiutils.cpp index f745ad01..96b3375c 100644 --- a/src/qtgui/guiutils.cpp +++ b/src/qtgui/guiutils.cpp @@ -129,6 +129,11 @@ void rwSettings(bool writing) if (!writing && prefs.qtermcolor == "") prefs.qtermcolor = "blue"; + // Abstract snippet separator + SETTING_RW(prefs.abssep, "/Recoll/prefs/reslist/abssep", String,"…"); + if (!writing && prefs.abssep == "") + prefs.abssep = "…"; + SETTING_RW(prefs.reslistfontfamily, "/Recoll/prefs/reslist/fontFamily", String, ""); SETTING_RW(prefs.reslistfontsize, "/Recoll/prefs/reslist/fontSize", Int, diff --git a/src/qtgui/guiutils.h b/src/qtgui/guiutils.h index 9ee418d0..aa573150 100644 --- a/src/qtgui/guiutils.h +++ b/src/qtgui/guiutils.h @@ -48,6 +48,8 @@ class PrefsPack { // Result list format string QString reslistformat; string creslistformat; + // Abstract snippet separator + QString abssep; QString queryStemLang; int mainwidth; int mainheight; diff --git a/src/qtgui/reslist.cpp b/src/qtgui/reslist.cpp index 910ac759..9e1c72ef 100644 --- a/src/qtgui/reslist.cpp +++ b/src/qtgui/reslist.cpp @@ -76,6 +76,7 @@ public: virtual string pageTop(); virtual string iconPath(const string& mt); virtual void suggest(const vectoruterms, vector&sugg); + virtual string absSep() {return (const char *)(prefs.abssep.toUtf8());} private: ResList *m_parent; }; diff --git a/src/qtgui/restable.cpp b/src/qtgui/restable.cpp index 51f1ca8d..4198a3a4 100644 --- a/src/qtgui/restable.cpp +++ b/src/qtgui/restable.cpp @@ -76,6 +76,7 @@ public: virtual string trans(const string& in); virtual const string &parFormat(); virtual string iconPath(const string& mt); + virtual string absSep() {return (const char *)(prefs.abssep.toUtf8());} private: ResTable *m_parent; }; diff --git a/src/qtgui/uiprefs.ui b/src/qtgui/uiprefs.ui index d5463812..6809f1ab 100644 --- a/src/qtgui/uiprefs.ui +++ b/src/qtgui/uiprefs.ui @@ -174,6 +174,30 @@ + + + + + + Abstract snippet separator + + + false + + + + + + + + 30 + 0 + + + + + + diff --git a/src/qtgui/uiprefs_w.cpp b/src/qtgui/uiprefs_w.cpp index a86d6840..fcd75a43 100644 --- a/src/qtgui/uiprefs_w.cpp +++ b/src/qtgui/uiprefs_w.cpp @@ -102,7 +102,9 @@ void UIPrefsDialog::setFromPrefs() previewPlainPreCB->setChecked(prefs.previewPlainPre); // Query terms color qtermColorLE->setText(prefs.qtermcolor); - + // Abstract snippet separator string + abssepLE->setText(prefs.abssep); + // Result list font family and size reslistFontFamily = prefs.reslistfontfamily; reslistFontSize = prefs.reslistfontsize; @@ -178,6 +180,8 @@ void UIPrefsDialog::accept() prefs.maxhltextmbs = maxHLTSB->value(); prefs.qtermcolor = qtermColorLE->text(); + prefs.abssep = abssepLE->text(); + prefs.reslistfontfamily = reslistFontFamily; prefs.reslistfontsize = reslistFontSize; prefs.reslistformat = rlfTE->toPlainText(); diff --git a/src/query/docseq.h b/src/query/docseq.h index 1ed3e8c5..fc634c5e 100644 --- a/src/query/docseq.h +++ b/src/query/docseq.h @@ -93,10 +93,21 @@ class DocSequence { /** Get abstract for document. This is special because it may take time. * The default is to return the input doc's abstract fields, but some * sequences can compute a better value (ie: docseqdb) */ - virtual string getAbstract(Rcl::Doc& doc) { - return doc.meta[Rcl::Doc::keyabs]; + virtual bool getAbstract(Rcl::Doc& doc, vector& abs) { + abs.push_back(doc.meta[Rcl::Doc::keyabs]); + return true; + } + virtual string getAbstract(Rcl::Doc& doc) { + vector v; + getAbstract(doc, v); + string abstract; + for (vector::const_iterator it = v.begin(); + it != v.end(); it++) { + abstract += *it; + abstract += "... "; + } + return abstract; } - virtual bool getEnclosing(Rcl::Doc&, Rcl::Doc&) = 0; /** Get estimated total count in results */ @@ -152,7 +163,13 @@ public: {} virtual ~DocSeqModifier() {} - virtual string getAbstract(Rcl::Doc& doc) + virtual bool getAbstract(Rcl::Doc& doc, vector& abs) + { + if (m_seq.isNull()) + return false; + return m_seq->getAbstract(doc, abs); + } + virtual string getAbstract(Rcl::Doc& doc) { if (m_seq.isNull()) return ""; diff --git a/src/query/docseqdb.cpp b/src/query/docseqdb.cpp index f54a4dd6..e76e8ef2 100644 --- a/src/query/docseqdb.cpp +++ b/src/query/docseqdb.cpp @@ -71,20 +71,17 @@ int DocSequenceDb::getResCnt() return m_rescnt; } -string DocSequenceDb::getAbstract(Rcl::Doc &doc) +bool DocSequenceDb::getAbstract(Rcl::Doc &doc, vector& vabs) { setQuery(); - if (!m_q->whatDb()) - return doc.meta[Rcl::Doc::keyabs]; - string abstract; + if (m_q->whatDb() && + m_queryBuildAbstract && (doc.syntabs || m_queryReplaceAbstract)) { + m_q->whatDb()->makeDocAbstract(doc, m_q.getptr(), vabs); + } + if (vabs.empty()) + vabs.push_back(doc.meta[Rcl::Doc::keyabs]); - if (m_queryBuildAbstract && (doc.syntabs || m_queryReplaceAbstract)) { - m_q->whatDb()->makeDocAbstract(doc, m_q.getptr(), abstract); - } else { - abstract = doc.meta[Rcl::Doc::keyabs]; - } - - return abstract.empty() ? doc.meta[Rcl::Doc::keyabs] : abstract; + return true; } bool DocSequenceDb::getEnclosing(Rcl::Doc& doc, Rcl::Doc& pdoc) diff --git a/src/query/docseqdb.h b/src/query/docseqdb.h index f5194732..9f65bc2f 100644 --- a/src/query/docseqdb.h +++ b/src/query/docseqdb.h @@ -34,7 +34,7 @@ class DocSequenceDb : public DocSequence { vector >& groups, vector& gslks); virtual void getUTerms(vector& terms); - virtual string getAbstract(Rcl::Doc &doc); + virtual bool getAbstract(Rcl::Doc &doc, vector&); virtual bool getEnclosing(Rcl::Doc& doc, Rcl::Doc& pdoc); virtual string getDescription(); virtual list expand(Rcl::Doc &doc); diff --git a/src/query/reslistpager.cpp b/src/query/reslistpager.cpp index 7698c215..7c19e1d7 100644 --- a/src/query/reslistpager.cpp +++ b/src/query/reslistpager.cpp @@ -177,15 +177,22 @@ void ResListPager::displayDoc(RclConfig *config, sizebuf = displayableBytes(fsize); } - string abstract; - if (m_docSource.isNotNull()) - abstract = m_docSource->getAbstract(doc); + string richabst; + bool needabstract = parFormat().find("%A") != string::npos; + if (needabstract && m_docSource.isNotNull()) { + vector vabs; + m_docSource->getAbstract(doc, vabs); - // No need to call escapeHtml(), plaintorich handles it - list lr; - m_hiliter->set_inputhtml(false); - m_hiliter->plaintorich(abstract, lr, hdata); - string richabst = lr.front(); + for (vector::const_iterator it = vabs.begin(); + it != vabs.end(); it++) { + // No need to call escapeHtml(), plaintorich handles it + list lr; + m_hiliter->set_inputhtml(false); + m_hiliter->plaintorich(*it, lr, hdata); + richabst += lr.front(); + richabst += absSep(); + } + } // Links; ostringstream linksbuf; diff --git a/src/query/reslistpager.h b/src/query/reslistpager.h index d54811b9..517efd44 100644 --- a/src/query/reslistpager.h +++ b/src/query/reslistpager.h @@ -113,7 +113,7 @@ public: virtual void suggest(const vector, vector&sugg) { sugg.clear(); } - + virtual string absSep() {return "…";} private: int m_pagesize; int m_newpagesize; diff --git a/src/rcldb/rcldb.cpp b/src/rcldb/rcldb.cpp index c5048438..fce9a24b 100644 --- a/src/rcldb/rcldb.cpp +++ b/src/rcldb/rcldb.cpp @@ -73,6 +73,7 @@ namespace Rcl { #endif const string pathelt_prefix = "XP"; +static const string ellipsis("..."); string version_string(){ return string("Recoll ") + string(rclversionstr) + string(" + Xapian ") + @@ -245,7 +246,7 @@ static void listList(const string& what, const list&l) // // DatabaseModified and other general exceptions are catched and // possibly retried by our caller -string Db::Native::makeAbstract(Xapian::docid docid, Query *query) +vector Db::Native::makeAbstract(Xapian::docid docid, Query *query) { Chrono chron; LOGDEB2(("makeAbstract:%d: maxlen %d wWidth %d\n", chron.ms(), @@ -259,7 +260,7 @@ string Db::Native::makeAbstract(Xapian::docid docid, Query *query) noPrefixList(iterms, terms); if (terms.empty()) { LOGDEB(("makeAbstract::Empty term list\n")); - return string(); + return vector(); } } // listList("Match terms: ", terms); @@ -353,12 +354,11 @@ string Db::Native::makeAbstract(Xapian::docid docid, Query *query) // This can't happen, but would crash us if (totalweight == 0.0) { LOGERR(("makeAbstract: 0 totalweight!\n")); - return string(); + return vector(); } // This is used to mark positions overlapped by a multi-word match term const string occupiedmarker("?"); - const string ellipsis("..."); // Let's go populate for (multimap::reverse_iterator qit = byQ.rbegin(); @@ -439,7 +439,7 @@ string Db::Native::makeAbstract(Xapian::docid docid, Query *query) // This can happen if there are term occurences in the keywords // etc. but not elsewhere ? if (qtermposs.size() == 0) - return string(); + return vector(); // Walk all document's terms position lists and populate slots // around the query terms. We arbitrarily truncate the list to @@ -504,8 +504,8 @@ string Db::Native::makeAbstract(Xapian::docid docid, Query *query) LOGABS(("makeAbstract:%d: extracting\n", chron.millis())); // Finally build the abstract by walking the map (in order of position) - string abstract; - abstract.reserve(sparseDoc.size() * 10); + vector vabs; + string chunk; bool incjk = false; for (map::const_iterator it = sparseDoc.begin(); it != sparseDoc.end(); it++) { @@ -517,18 +517,24 @@ string Db::Native::makeAbstract(Xapian::docid docid, Query *query) if (TextSplit::isCJK(*uit)) newcjk = true; if (!incjk || (incjk && !newcjk)) - abstract += " "; + chunk += " "; incjk = newcjk; - abstract += it->second; + if (it->second == ellipsis) { + vabs.push_back(chunk); + chunk.clear(); + } else { + chunk += it->second; + } } - + if (!chunk.empty()) + vabs.push_back(chunk); // This happens for docs with no terms (only filename) indexed? I'll fix // one day (yeah) - if (!abstract.compare("... ")) - abstract.clear(); + if (vabs.size() == 1 && !vabs[0].compare("... ")) + vabs.clear(); LOGDEB2(("makeAbtract: done in %d mS\n", chron.millis())); - return abstract; + return vabs; } /* Rcl::Db methods ///////////////////////////////// */ @@ -1742,6 +1748,17 @@ bool Db::stemDiffers(const string& lang, const string& word, return true; } +bool Db::makeDocAbstract(Doc &doc, Query *query, vector& abstract) +{ + LOGDEB1(("Db::makeDocAbstract: exti %d\n", exti)); + if (!m_ndb || !m_ndb->m_isopen) { + LOGERR(("Db::makeDocAbstract: no db\n")); + return false; + } + XAPTRY(abstract = m_ndb->makeAbstract(doc.xdocid, query), + m_ndb->xrdb, m_reason); + return m_reason.empty() ? true : false; +} bool Db::makeDocAbstract(Doc &doc, Query *query, string& abstract) { @@ -1750,10 +1767,14 @@ bool Db::makeDocAbstract(Doc &doc, Query *query, string& abstract) LOGERR(("Db::makeDocAbstract: no db\n")); return false; } - - XAPTRY(abstract = m_ndb->makeAbstract(doc.xdocid, query), + vector vab; + XAPTRY(vab = m_ndb->makeAbstract(doc.xdocid, query), m_ndb->xrdb, m_reason); - + for (vector::const_iterator it = vab.begin(); + it != vab.end(); it++) { + abstract.append(*it); + abstract.append(ellipsis); + } return m_reason.empty() ? true : false; } diff --git a/src/rcldb/rcldb.h b/src/rcldb/rcldb.h index eff7b8ec..4586cd2d 100644 --- a/src/rcldb/rcldb.h +++ b/src/rcldb/rcldb.h @@ -201,6 +201,7 @@ class Db { /** Build synthetic abstract for document, extracting chunks relevant for * the input query. This uses index data only (no access to the file) */ bool makeDocAbstract(Doc &doc, Query *query, string& abstract); + bool makeDocAbstract(Doc &doc, Query *query, vector& abstract); /** Get document for given udi * diff --git a/src/rcldb/rcldb_p.h b/src/rcldb/rcldb_p.h index ab34820a..3a5ef8e8 100644 --- a/src/rcldb/rcldb_p.h +++ b/src/rcldb/rcldb_p.h @@ -91,7 +91,7 @@ class Db::Native { ~Native() { } - string makeAbstract(Xapian::docid id, Query *query); + vector makeAbstract(Xapian::docid id, Query *query); bool dbDataToRclDoc(Xapian::docid docid, std::string &data, Doc &doc);