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>
|
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>
|
||||||
|
|
||||||
|
|||||||
@ -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,"…");
|
||||||
|
if (!writing && prefs.abssep == "")
|
||||||
|
prefs.abssep = "…";
|
||||||
|
|
||||||
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,
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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();
|
||||||
|
|||||||
@ -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())
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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 "…";}
|
||||||
private:
|
private:
|
||||||
int m_pagesize;
|
int m_pagesize;
|
||||||
int m_newpagesize;
|
int m_newpagesize;
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
*
|
*
|
||||||
|
|||||||
@ -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);
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user