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);