GUI: allow setting the snippet separator inside abstract (now a real html ellipsis by default)

This commit is contained in:
Jean-Francois Dockes 2011-07-07 11:11:02 +02:00
parent cb0794e92c
commit 469c544915
15 changed files with 135 additions and 49 deletions

View File

@ -1525,6 +1525,14 @@ fvwm
described in its own section.</link></para>
</listitem>
<listitem><anchor id="rcl.search.custom.abssep">
<para><guilabel>Abstract snippet separator</guilabel>:
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. </para>
</listitem>
<listitem><para><guilabel>Maximum text size highlighted for
preview</guilabel> Inserting highlights on search term inside
the text before inserting it in the preview window involves
@ -1754,12 +1762,10 @@ fvwm
preview link.
</para>
<para>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.</para>
<para>It is also possible to
<link linkend="rcl.search.custom.abssep">
define the value of the snippet separator inside the abstract
section</link>.</para>
</sect3>
</sect2>

View File

@ -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,"&hellip;");
if (!writing && prefs.abssep == "")
prefs.abssep = "&hellip;";
SETTING_RW(prefs.reslistfontfamily, "/Recoll/prefs/reslist/fontFamily", String,
"");
SETTING_RW(prefs.reslistfontsize, "/Recoll/prefs/reslist/fontSize", Int,

View File

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

View File

@ -76,6 +76,7 @@ public:
virtual string pageTop();
virtual string iconPath(const string& mt);
virtual void suggest(const vector<string>uterms, vector<string>&sugg);
virtual string absSep() {return (const char *)(prefs.abssep.toUtf8());}
private:
ResList *m_parent;
};

View File

@ -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;
};

View File

@ -174,6 +174,30 @@
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout">
<item>
<widget class="QLabel" name="textLabelAbss">
<property name="text">
<string>Abstract snippet separator</string>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="abssepLE">
<property name="minimumSize">
<size>
<width>30</width>
<height>0</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout">
<item>

View File

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

View File

@ -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<string>& abs) {
abs.push_back(doc.meta[Rcl::Doc::keyabs]);
return true;
}
virtual string getAbstract(Rcl::Doc& doc) {
vector<string> v;
getAbstract(doc, v);
string abstract;
for (vector<string>::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<string>& abs)
{
if (m_seq.isNull())
return false;
return m_seq->getAbstract(doc, abs);
}
virtual string getAbstract(Rcl::Doc& doc)
{
if (m_seq.isNull())
return "";

View File

@ -71,20 +71,17 @@ int DocSequenceDb::getResCnt()
return m_rescnt;
}
string DocSequenceDb::getAbstract(Rcl::Doc &doc)
bool DocSequenceDb::getAbstract(Rcl::Doc &doc, vector<string>& 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)

View File

@ -34,7 +34,7 @@ class DocSequenceDb : public DocSequence {
vector<vector<string> >& groups,
vector<int>& gslks);
virtual void getUTerms(vector<string>& terms);
virtual string getAbstract(Rcl::Doc &doc);
virtual bool getAbstract(Rcl::Doc &doc, vector<string>&);
virtual bool getEnclosing(Rcl::Doc& doc, Rcl::Doc& pdoc);
virtual string getDescription();
virtual list<string> expand(Rcl::Doc &doc);

View File

@ -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<string> vabs;
m_docSource->getAbstract(doc, vabs);
// No need to call escapeHtml(), plaintorich handles it
list<string> lr;
m_hiliter->set_inputhtml(false);
m_hiliter->plaintorich(abstract, lr, hdata);
string richabst = lr.front();
for (vector<string>::const_iterator it = vabs.begin();
it != vabs.end(); it++) {
// No need to call escapeHtml(), plaintorich handles it
list<string> lr;
m_hiliter->set_inputhtml(false);
m_hiliter->plaintorich(*it, lr, hdata);
richabst += lr.front();
richabst += absSep();
}
}
// Links;
ostringstream linksbuf;

View File

@ -113,7 +113,7 @@ public:
virtual void suggest(const vector<string>, vector<string>&sugg) {
sugg.clear();
}
virtual string absSep() {return "&hellip;";}
private:
int m_pagesize;
int m_newpagesize;

View File

@ -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<string>&l)
//
// DatabaseModified and other general exceptions are catched and
// possibly retried by our caller
string Db::Native::makeAbstract(Xapian::docid docid, Query *query)
vector<string> 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<string>();
}
}
// 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<string>();
}
// 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<double, string>::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<string>();
// 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<string> vabs;
string chunk;
bool incjk = false;
for (map<unsigned int, string>::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<string>& 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<string> vab;
XAPTRY(vab = m_ndb->makeAbstract(doc.xdocid, query),
m_ndb->xrdb, m_reason);
for (vector<string>::const_iterator it = vab.begin();
it != vab.end(); it++) {
abstract.append(*it);
abstract.append(ellipsis);
}
return m_reason.empty() ? true : false;
}

View File

@ -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<string>& abstract);
/** Get document for given udi
*

View File

@ -91,7 +91,7 @@ class Db::Native {
~Native() {
}
string makeAbstract(Xapian::docid id, Query *query);
vector<string> makeAbstract(Xapian::docid id, Query *query);
bool dbDataToRclDoc(Xapian::docid docid, std::string &data, Doc &doc);