GUI: allow setting the snippet separator inside abstract (now a real html ellipsis by default)
This commit is contained in:
parent
cb0794e92c
commit
469c544915
@ -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>
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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 "";
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -113,7 +113,7 @@ public:
|
||||
virtual void suggest(const vector<string>, vector<string>&sugg) {
|
||||
sugg.clear();
|
||||
}
|
||||
|
||||
virtual string absSep() {return "…";}
|
||||
private:
|
||||
int m_pagesize;
|
||||
int m_newpagesize;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
*
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user