322 lines
8.6 KiB
C++
322 lines
8.6 KiB
C++
#ifndef lint
|
|
static char rcsid[] = "@(#$Id: reslistpager.cpp,v 1.4 2008-12-02 13:14:01 dockes Exp $ (C) 2007 J.F.Dockes";
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
|
|
#include "reslistpager.h"
|
|
#include "debuglog.h"
|
|
#include "rclconfig.h"
|
|
#include "smallut.h"
|
|
#include "plaintorich.h"
|
|
#include "mimehandler.h"
|
|
|
|
// This should be passed as an input object to the pager instead
|
|
class PlainToRichHtReslist : public PlainToRich {
|
|
public:
|
|
virtual ~PlainToRichHtReslist() {}
|
|
virtual string startMatch() {return string("<font color=\"blue\">");}
|
|
virtual string endMatch() {return string("</font>");}
|
|
};
|
|
// IDEM
|
|
struct Prefs {
|
|
bool queryBuildAbstract;
|
|
bool queryReplaceAbstract;
|
|
};
|
|
Prefs prefs = {true, true};
|
|
|
|
void ResListPager::resultPageNext()
|
|
{
|
|
if (m_docSource.isNull()) {
|
|
LOGDEB(("ResListPager::displayPage: null source\n"));
|
|
return;
|
|
}
|
|
|
|
int resCnt = m_docSource->getResCnt();
|
|
LOGDEB(("ResListPager::resultPageNext: rescnt %d, winfirst %d\n",
|
|
resCnt, m_winfirst));
|
|
|
|
if (m_winfirst < 0) {
|
|
m_winfirst = 0;
|
|
} else {
|
|
m_winfirst += m_pagesize;
|
|
}
|
|
// Get the next page of results.
|
|
vector<ResListEntry> npage;
|
|
int pagelen = m_docSource->getSeqSlice(m_winfirst, m_pagesize, npage);
|
|
|
|
// If page was truncated, there is no next
|
|
m_hasNext = (pagelen == m_pagesize);
|
|
|
|
if (pagelen <= 0) {
|
|
// No results ? This can only happen on the first page or if the
|
|
// actual result list size is a multiple of the page pref (else
|
|
// there would have been no Next on the last page)
|
|
if (m_winfirst > 0) {
|
|
// Have already results. Let them show, just disable the
|
|
// Next button. We'd need to remove the Next link from the page
|
|
// too.
|
|
// Restore the m_winfirst value, let the current result vector alone
|
|
m_winfirst -= m_pagesize;
|
|
} else {
|
|
// No results at all (on first page)
|
|
m_winfirst = -1;
|
|
}
|
|
return;
|
|
}
|
|
m_respage = npage;
|
|
}
|
|
|
|
void ResListPager::displayPage()
|
|
{
|
|
if (m_docSource.isNull()) {
|
|
LOGDEB(("ResListPager::displayPage: null source\n"));
|
|
return;
|
|
}
|
|
string chunk;
|
|
|
|
// Display list header
|
|
// We could use a <title> but the textedit doesnt display
|
|
// it prominently
|
|
// Note: have to append text in chunks that make sense
|
|
// html-wise. If we break things up too much, the editor
|
|
// gets confused. Hence the use of the 'chunk' text
|
|
// accumulator
|
|
// Also note that there can be results beyond the estimated resCnt.
|
|
chunk = "<html><head>"
|
|
"<meta http-equiv=\"content-type\""
|
|
"content=\"text/html; charset=utf-8\">"
|
|
"</head><body>";
|
|
chunk += pageTop();
|
|
chunk += "<p><font size=+1><b>";
|
|
chunk += m_docSource->title();
|
|
chunk += "</b></font>"
|
|
" ";
|
|
|
|
if (pageEmpty()) {
|
|
chunk += tr("<p><b>No results found</b> for ");
|
|
} else {
|
|
unsigned int resCnt = m_docSource->getResCnt();
|
|
if (m_winfirst + m_respage.size() < resCnt) {
|
|
string f1 = tr("Documents <b>%d-%d</b> out of at least <b>%d</b> for ");
|
|
char buf[1024];
|
|
snprintf(buf, 1023, f1.c_str(), m_winfirst+1,
|
|
m_winfirst + m_respage.size(), resCnt);
|
|
chunk += buf;
|
|
} else {
|
|
string f1 = tr("Documents <b>%d-%d</b> for ");
|
|
char buf[1024];
|
|
snprintf(buf, 1023, f1.c_str(), m_winfirst + 1,
|
|
m_winfirst + m_respage.size());
|
|
chunk += buf;
|
|
}
|
|
}
|
|
chunk += detailsLink();
|
|
if (hasPrev() || hasNext()) {
|
|
chunk += " ";
|
|
if (hasPrev()) {
|
|
chunk += "<a href=\"" + prevUrl() + "\"><b>";
|
|
chunk += tr("Previous");
|
|
chunk += "</b></a> ";
|
|
}
|
|
if (hasNext()) {
|
|
chunk += "<a href=\""+ nextUrl() + "\"><b>";
|
|
chunk += tr("Next");
|
|
chunk += "</b></a>";
|
|
}
|
|
}
|
|
chunk += "</p>";
|
|
|
|
append(chunk);
|
|
if (pageEmpty())
|
|
return;
|
|
|
|
|
|
HiliteData hdata;
|
|
m_docSource->getTerms(hdata.terms, hdata.groups, hdata.gslks);
|
|
|
|
// Emit data for result entry paragraph. Do it in chunks that make sense
|
|
// html-wise, else our client may get confused
|
|
for (int i = 0; i < (int)m_respage.size(); i++) {
|
|
|
|
Rcl::Doc &doc(m_respage[i].doc);
|
|
string& sh(m_respage[i].subHeader);
|
|
int percent;
|
|
if (doc.pc == -1) {
|
|
percent = 0;
|
|
// Document not available, maybe other further, will go on.
|
|
doc.meta[Rcl::Doc::keyabs] = string(tr("Unavailable document"));
|
|
} else {
|
|
percent = doc.pc;
|
|
}
|
|
// Percentage of 'relevance'
|
|
char perbuf[10];
|
|
sprintf(perbuf, "%3d%% ", percent);
|
|
|
|
// Determine icon to display if any
|
|
string iconpath;
|
|
RclConfig::getMainConfig()->getMimeIconName(doc.mimetype, &iconpath);
|
|
iconpath = string("file://") + iconpath;
|
|
|
|
// Printable url: either utf-8 if transcoding succeeds, or url-encoded
|
|
string url;
|
|
printableUrl(RclConfig::getMainConfig()->getDefCharset(), doc.url, url);
|
|
|
|
// Make title out of file name if none yet
|
|
if (doc.meta[Rcl::Doc::keytt].empty()) {
|
|
doc.meta[Rcl::Doc::keytt] = path_getsimple(url);
|
|
}
|
|
|
|
// Result number
|
|
char numbuf[20];
|
|
int docnumforlinks = m_winfirst + 1 + i;
|
|
sprintf(numbuf, "%d", docnumforlinks);
|
|
|
|
// Document date: either doc or file modification time
|
|
char datebuf[100];
|
|
datebuf[0] = 0;
|
|
if (!doc.dmtime.empty() || !doc.fmtime.empty()) {
|
|
time_t mtime = doc.dmtime.empty() ?
|
|
atol(doc.fmtime.c_str()) : atol(doc.dmtime.c_str());
|
|
struct tm *tm = localtime(&mtime);
|
|
#ifndef sun
|
|
strftime(datebuf, 99, " %Y-%m-%d %H:%M:%S %z", tm);
|
|
#else
|
|
strftime(datebuf, 99, " %Y-%m-%d %H:%M:%S %Z", tm);
|
|
#endif
|
|
}
|
|
|
|
// Size information. We print both doc and file if they differ a lot
|
|
long fsize = -1, dsize = -1;
|
|
if (!doc.dbytes.empty())
|
|
dsize = atol(doc.dbytes.c_str());
|
|
if (!doc.fbytes.empty())
|
|
fsize = atol(doc.fbytes.c_str());
|
|
string sizebuf;
|
|
if (dsize > 0) {
|
|
sizebuf = displayableBytes(dsize);
|
|
if (fsize > 10 * dsize && fsize - dsize > 1000)
|
|
sizebuf += string(" / ") + displayableBytes(fsize);
|
|
} else if (fsize >= 0) {
|
|
sizebuf = displayableBytes(fsize);
|
|
}
|
|
|
|
string abstract;
|
|
if (prefs.queryBuildAbstract &&
|
|
(doc.syntabs || prefs.queryReplaceAbstract)) {
|
|
abstract = m_docSource->getAbstract(doc);
|
|
} else {
|
|
abstract = doc.meta[Rcl::Doc::keyabs];
|
|
}
|
|
// No need to call escapeHtml(), plaintorich handles it
|
|
list<string> lr;
|
|
PlainToRichHtReslist ptr;
|
|
ptr.plaintorich(abstract, lr, hdata);
|
|
string richabst = lr.front();
|
|
|
|
// Links;
|
|
string linksbuf;
|
|
char vlbuf[100];
|
|
if (canIntern(doc.mimetype, RclConfig::getMainConfig())) {
|
|
sprintf(vlbuf, "\"P%d\"", docnumforlinks);
|
|
linksbuf += string("<a href=") + vlbuf + ">" + "Preview" + "</a>"
|
|
+ " ";
|
|
}
|
|
if (!RclConfig::getMainConfig()->getMimeViewerDef(doc.mimetype).empty()) {
|
|
sprintf(vlbuf, "E%d", docnumforlinks);
|
|
linksbuf += string("<a href=") + vlbuf + ">" + "Open" + "</a>";
|
|
}
|
|
|
|
// Build the result list paragraph:
|
|
chunk = "";
|
|
|
|
// Subheader: this is used by history
|
|
if (!sh.empty())
|
|
chunk += "<p><b>" + sh + "</p>\n<p>";
|
|
else
|
|
chunk += "<p>";
|
|
|
|
// Configurable stuff
|
|
map<char,string> subs;
|
|
subs['A'] = !richabst.empty() ? richabst + "<br>" : "";
|
|
subs['D'] = datebuf;
|
|
subs['I'] = iconpath;
|
|
subs['K'] = !doc.meta[Rcl::Doc::keykw].empty() ?
|
|
escapeHtml(doc.meta[Rcl::Doc::keykw]) + "<br>" : "";
|
|
subs['L'] = linksbuf;
|
|
subs['N'] = numbuf;
|
|
subs['M'] = doc.mimetype;
|
|
subs['R'] = perbuf;
|
|
subs['S'] = sizebuf;
|
|
subs['T'] = escapeHtml(doc.meta[Rcl::Doc::keytt]);
|
|
subs['U'] = url;
|
|
|
|
string formatted;
|
|
pcSubst(parFormat(), formatted, subs);
|
|
chunk += formatted;
|
|
|
|
chunk += "</p>\n";
|
|
|
|
LOGDEB2(("Chunk: [%s]\n", (const char *)chunk.c_str()));
|
|
append(chunk);
|
|
}
|
|
|
|
// Footer
|
|
chunk = "<p align=\"center\">";
|
|
if (hasPrev() || hasNext()) {
|
|
if (hasPrev()) {
|
|
chunk += "<a href=\"" + prevUrl() + "\"><b>";
|
|
chunk += tr("Previous");
|
|
chunk += "</b></a> ";
|
|
}
|
|
if (hasNext()) {
|
|
chunk += "<a href=\""+ nextUrl() + "\"><b>";
|
|
chunk += tr("Next");
|
|
chunk += "</b></a>";
|
|
}
|
|
}
|
|
chunk += "</p>\n";
|
|
chunk += "</body></html>\n";
|
|
append(chunk);
|
|
}
|
|
|
|
string ResListPager::nextUrl()
|
|
{
|
|
return "n-1";
|
|
}
|
|
string ResListPager::prevUrl()
|
|
{
|
|
return "p-1";
|
|
}
|
|
|
|
// Default implementations for things that should be re-implemented by our user.
|
|
bool ResListPager::append(const string& data)
|
|
{
|
|
fprintf(stderr, "%s", data.c_str());
|
|
return true;
|
|
}
|
|
|
|
string ResListPager::tr(const string& in)
|
|
{
|
|
return in;
|
|
}
|
|
|
|
string ResListPager::detailsLink()
|
|
{
|
|
string chunk = "<a href=\"H-1\">";
|
|
chunk += tr("(show query)") + "</a>";
|
|
return chunk;
|
|
}
|
|
|
|
const string &ResListPager::parFormat()
|
|
{
|
|
const static string format("<img src=\"%I\" align=\"left\">"
|
|
"%R %S %L <b>%T</b><br>"
|
|
"%M %D <i>%U</i><br>"
|
|
"%A %K");
|
|
return format;
|
|
}
|
|
|