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> described in its own section.</link></para>
</listitem> </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 <listitem><para><guilabel>Maximum text size highlighted for
preview</guilabel> Inserting highlights on search term inside preview</guilabel> Inserting highlights on search term inside
the text before inserting it in the preview window involves the text before inserting it in the preview window involves
@ -1754,12 +1762,10 @@ fvwm
preview link. preview link.
</para> </para>
<para>Due to the way the program handles right mouse clicks in the <para>It is also possible to
result list, if the custom formatting results in multiple <link linkend="rcl.search.custom.abssep">
paragraphs per result, right clicks will only work inside the first define the value of the snippet separator inside the abstract
one.</para> section</link>.</para>
</sect3> </sect3>
</sect2> </sect2>

View File

@ -129,6 +129,11 @@ void rwSettings(bool writing)
if (!writing && prefs.qtermcolor == "") if (!writing && prefs.qtermcolor == "")
prefs.qtermcolor = "blue"; 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.reslistfontfamily, "/Recoll/prefs/reslist/fontFamily", String,
""); "");
SETTING_RW(prefs.reslistfontsize, "/Recoll/prefs/reslist/fontSize", Int, SETTING_RW(prefs.reslistfontsize, "/Recoll/prefs/reslist/fontSize", Int,

View File

@ -48,6 +48,8 @@ class PrefsPack {
// Result list format string // Result list format string
QString reslistformat; QString reslistformat;
string creslistformat; string creslistformat;
// Abstract snippet separator
QString abssep;
QString queryStemLang; QString queryStemLang;
int mainwidth; int mainwidth;
int mainheight; int mainheight;

View File

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

View File

@ -76,6 +76,7 @@ public:
virtual string trans(const string& in); virtual string trans(const string& in);
virtual const string &parFormat(); virtual const string &parFormat();
virtual string iconPath(const string& mt); virtual string iconPath(const string& mt);
virtual string absSep() {return (const char *)(prefs.abssep.toUtf8());}
private: private:
ResTable *m_parent; ResTable *m_parent;
}; };

View File

@ -174,6 +174,30 @@
</item> </item>
</layout> </layout>
</item> </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> <item>
<layout class="QHBoxLayout"> <layout class="QHBoxLayout">
<item> <item>

View File

@ -102,6 +102,8 @@ void UIPrefsDialog::setFromPrefs()
previewPlainPreCB->setChecked(prefs.previewPlainPre); previewPlainPreCB->setChecked(prefs.previewPlainPre);
// Query terms color // Query terms color
qtermColorLE->setText(prefs.qtermcolor); qtermColorLE->setText(prefs.qtermcolor);
// Abstract snippet separator string
abssepLE->setText(prefs.abssep);
// Result list font family and size // Result list font family and size
reslistFontFamily = prefs.reslistfontfamily; reslistFontFamily = prefs.reslistfontfamily;
@ -178,6 +180,8 @@ void UIPrefsDialog::accept()
prefs.maxhltextmbs = maxHLTSB->value(); prefs.maxhltextmbs = maxHLTSB->value();
prefs.qtermcolor = qtermColorLE->text(); prefs.qtermcolor = qtermColorLE->text();
prefs.abssep = abssepLE->text();
prefs.reslistfontfamily = reslistFontFamily; prefs.reslistfontfamily = reslistFontFamily;
prefs.reslistfontsize = reslistFontSize; prefs.reslistfontsize = reslistFontSize;
prefs.reslistformat = rlfTE->toPlainText(); prefs.reslistformat = rlfTE->toPlainText();

View File

@ -93,10 +93,21 @@ class DocSequence {
/** Get abstract for document. This is special because it may take time. /** 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 * The default is to return the input doc's abstract fields, but some
* sequences can compute a better value (ie: docseqdb) */ * sequences can compute a better value (ie: docseqdb) */
virtual string getAbstract(Rcl::Doc& doc) { virtual bool getAbstract(Rcl::Doc& doc, vector<string>& abs) {
return doc.meta[Rcl::Doc::keyabs]; 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; virtual bool getEnclosing(Rcl::Doc&, Rcl::Doc&) = 0;
/** Get estimated total count in results */ /** Get estimated total count in results */
@ -152,6 +163,12 @@ public:
{} {}
virtual ~DocSeqModifier() {} virtual ~DocSeqModifier() {}
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) virtual string getAbstract(Rcl::Doc& doc)
{ {
if (m_seq.isNull()) if (m_seq.isNull())

View File

@ -71,20 +71,17 @@ int DocSequenceDb::getResCnt()
return m_rescnt; return m_rescnt;
} }
string DocSequenceDb::getAbstract(Rcl::Doc &doc) bool DocSequenceDb::getAbstract(Rcl::Doc &doc, vector<string>& vabs)
{ {
setQuery(); setQuery();
if (!m_q->whatDb()) if (m_q->whatDb() &&
return doc.meta[Rcl::Doc::keyabs]; m_queryBuildAbstract && (doc.syntabs || m_queryReplaceAbstract)) {
string abstract; m_q->whatDb()->makeDocAbstract(doc, m_q.getptr(), vabs);
if (m_queryBuildAbstract && (doc.syntabs || m_queryReplaceAbstract)) {
m_q->whatDb()->makeDocAbstract(doc, m_q.getptr(), abstract);
} else {
abstract = doc.meta[Rcl::Doc::keyabs];
} }
if (vabs.empty())
vabs.push_back(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) bool DocSequenceDb::getEnclosing(Rcl::Doc& doc, Rcl::Doc& pdoc)

View File

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

View File

@ -177,15 +177,22 @@ void ResListPager::displayDoc(RclConfig *config,
sizebuf = displayableBytes(fsize); sizebuf = displayableBytes(fsize);
} }
string abstract; string richabst;
if (m_docSource.isNotNull()) bool needabstract = parFormat().find("%A") != string::npos;
abstract = m_docSource->getAbstract(doc); if (needabstract && m_docSource.isNotNull()) {
vector<string> vabs;
m_docSource->getAbstract(doc, vabs);
// No need to call escapeHtml(), plaintorich handles it for (vector<string>::const_iterator it = vabs.begin();
list<string> lr; it != vabs.end(); it++) {
m_hiliter->set_inputhtml(false); // No need to call escapeHtml(), plaintorich handles it
m_hiliter->plaintorich(abstract, lr, hdata); list<string> lr;
string richabst = lr.front(); m_hiliter->set_inputhtml(false);
m_hiliter->plaintorich(*it, lr, hdata);
richabst += lr.front();
richabst += absSep();
}
}
// Links; // Links;
ostringstream linksbuf; ostringstream linksbuf;

View File

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

View File

@ -73,6 +73,7 @@ namespace Rcl {
#endif #endif
const string pathelt_prefix = "XP"; const string pathelt_prefix = "XP";
static const string ellipsis("...");
string version_string(){ string version_string(){
return string("Recoll ") + string(rclversionstr) + string(" + Xapian ") + 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 // DatabaseModified and other general exceptions are catched and
// possibly retried by our caller // 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; Chrono chron;
LOGDEB2(("makeAbstract:%d: maxlen %d wWidth %d\n", chron.ms(), 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); noPrefixList(iterms, terms);
if (terms.empty()) { if (terms.empty()) {
LOGDEB(("makeAbstract::Empty term list\n")); LOGDEB(("makeAbstract::Empty term list\n"));
return string(); return vector<string>();
} }
} }
// listList("Match terms: ", terms); // 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 // This can't happen, but would crash us
if (totalweight == 0.0) { if (totalweight == 0.0) {
LOGERR(("makeAbstract: 0 totalweight!\n")); LOGERR(("makeAbstract: 0 totalweight!\n"));
return string(); return vector<string>();
} }
// This is used to mark positions overlapped by a multi-word match term // This is used to mark positions overlapped by a multi-word match term
const string occupiedmarker("?"); const string occupiedmarker("?");
const string ellipsis("...");
// Let's go populate // Let's go populate
for (multimap<double, string>::reverse_iterator qit = byQ.rbegin(); 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 // This can happen if there are term occurences in the keywords
// etc. but not elsewhere ? // etc. but not elsewhere ?
if (qtermposs.size() == 0) if (qtermposs.size() == 0)
return string(); return vector<string>();
// Walk all document's terms position lists and populate slots // Walk all document's terms position lists and populate slots
// around the query terms. We arbitrarily truncate the list to // 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())); LOGABS(("makeAbstract:%d: extracting\n", chron.millis()));
// Finally build the abstract by walking the map (in order of position) // Finally build the abstract by walking the map (in order of position)
string abstract; vector<string> vabs;
abstract.reserve(sparseDoc.size() * 10); string chunk;
bool incjk = false; bool incjk = false;
for (map<unsigned int, string>::const_iterator it = sparseDoc.begin(); for (map<unsigned int, string>::const_iterator it = sparseDoc.begin();
it != sparseDoc.end(); it++) { it != sparseDoc.end(); it++) {
@ -517,18 +517,24 @@ string Db::Native::makeAbstract(Xapian::docid docid, Query *query)
if (TextSplit::isCJK(*uit)) if (TextSplit::isCJK(*uit))
newcjk = true; newcjk = true;
if (!incjk || (incjk && !newcjk)) if (!incjk || (incjk && !newcjk))
abstract += " "; chunk += " ";
incjk = newcjk; 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 // This happens for docs with no terms (only filename) indexed? I'll fix
// one day (yeah) // one day (yeah)
if (!abstract.compare("... ")) if (vabs.size() == 1 && !vabs[0].compare("... "))
abstract.clear(); vabs.clear();
LOGDEB2(("makeAbtract: done in %d mS\n", chron.millis())); LOGDEB2(("makeAbtract: done in %d mS\n", chron.millis()));
return abstract; return vabs;
} }
/* Rcl::Db methods ///////////////////////////////// */ /* Rcl::Db methods ///////////////////////////////// */
@ -1742,6 +1748,17 @@ bool Db::stemDiffers(const string& lang, const string& word,
return true; 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) 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")); LOGERR(("Db::makeDocAbstract: no db\n"));
return false; return false;
} }
vector<string> vab;
XAPTRY(abstract = m_ndb->makeAbstract(doc.xdocid, query), XAPTRY(vab = m_ndb->makeAbstract(doc.xdocid, query),
m_ndb->xrdb, m_reason); 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; return m_reason.empty() ? true : false;
} }

View File

@ -201,6 +201,7 @@ class Db {
/** Build synthetic abstract for document, extracting chunks relevant for /** Build synthetic abstract for document, extracting chunks relevant for
* the input query. This uses index data only (no access to the file) */ * 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, string& abstract);
bool makeDocAbstract(Doc &doc, Query *query, vector<string>& abstract);
/** Get document for given udi /** Get document for given udi
* *

View File

@ -91,7 +91,7 @@ class Db::Native {
~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); bool dbDataToRclDoc(Xapian::docid docid, std::string &data, Doc &doc);