kioslave sort of works
This commit is contained in:
parent
be8f0a2b4e
commit
31f704f564
@ -1,6 +1,4 @@
|
|||||||
|
|
||||||
(this used to work but last time I tried with konqueror (kde3, 11-2007), it failed with "protocol not authorized" or such. Didn't investigate more).
|
|
||||||
|
|
||||||
This is a small experiment with a recoll kio_slave
|
This is a small experiment with a recoll kio_slave
|
||||||
|
|
||||||
A kio_slave was implemented, supporting the "get" operation. Ie, you type
|
A kio_slave was implemented, supporting the "get" operation. Ie, you type
|
||||||
@ -15,25 +13,38 @@ quite useful in this case.
|
|||||||
Implementation notes:
|
Implementation notes:
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
|
- There are two main ways to do this:
|
||||||
|
- a-la kio_beagle, using listDir() to list entries pointing to the
|
||||||
|
different operations or objects (help, status, search result
|
||||||
|
entries, bookmarks, whatever...). The nice thing is that the
|
||||||
|
results really look like file object in a directory (probably,
|
||||||
|
didn't try it actually), no need for look and feel, it's provided by kde
|
||||||
|
|
||||||
|
- Or a la strigi: all interactions are through html pages and get()
|
||||||
|
operations. Looks less like a normal konqueror file-system
|
||||||
|
listing, and needs more html coding but all in all probably
|
||||||
|
simpler.
|
||||||
|
|
||||||
|
Recoll is currently doing the html thing. As far as I understand, the
|
||||||
|
way to trigger a listdir is to have a inode/directory default mime
|
||||||
|
type in the protocol file, and return inode/directory when
|
||||||
|
appropriate in mimetype() (look at kio_beagle). Some kde daemon needs
|
||||||
|
to be restarted when doing this (the protocol file is cached
|
||||||
|
somewhere).
|
||||||
|
|
||||||
|
Also would need a page header, configuration polish etc... Not done for
|
||||||
|
the same reason, this is a proof of concept.
|
||||||
|
|
||||||
|
KDE3 notes
|
||||||
- Not using libtool. Probably should. compilation flags in the Makefile
|
- Not using libtool. Probably should. compilation flags in the Makefile
|
||||||
were copy-pasted from a kdebase compilation tree on FreeBSD (kio/man).
|
were copy-pasted from a kdebase compilation tree on FreeBSD (kio/man).
|
||||||
|
|
||||||
- You MUST install a kio_recoll.la in lib/kde3 along with kio_recoll.so,
|
- You MUST install a kio_recoll.la in lib/kde3 along with kio_recoll.so,
|
||||||
else kdeinit won't be able to load the lib (probably uses the libltdl
|
else kdeinit won't be able to load the lib (probably uses the libltdl
|
||||||
thingy?). The one in this directory was duplicated/adjusted from
|
thingy?). The one in this directory was duplicated/adjusted from
|
||||||
kio_man.la. The contents don't seem too critical, just needs to exist.
|
kio_man.la. The contents don't seem too critical, just needs to exist.
|
||||||
|
|
||||||
- Currently retrieves all results on one page. Need to add state and
|
|
||||||
previous/next buttons. As I didn't find this thing to be particularly
|
|
||||||
useful, I didn't bothered to.
|
|
||||||
|
|
||||||
- Also would need a page header, configuration polish etc... Not done for
|
|
||||||
the same reason, this is a proof of concept.
|
|
||||||
|
|
||||||
- If you want to try, compile, then install kio_recoll.la kio_recoll.so
|
- If you want to try, compile, then install kio_recoll.la kio_recoll.so
|
||||||
wherever kde keeps its plugins (ie: lib/kde3), and recoll.protocol in the
|
wherever kde keeps its plugins (ie: lib/kde3), and recoll.protocol in the
|
||||||
services directory (share/services ? look for other .protocol file).
|
services directory (share/services ? look for other .protocol file).
|
||||||
|
|
||||||
- I saw after doing the build/config mockup that kdevelop can generate a
|
- I saw after doing the build/config mockup that kdevelop can generate a
|
||||||
kio_slave project. This might be the next thing to do. otoh would need to
|
kio_slave project. This might be the next thing to do. otoh would need to
|
||||||
separate the kio from the main source to avoid having to distribute 2megs
|
separate the kio from the main source to avoid having to distribute 2megs
|
||||||
|
|||||||
@ -7,40 +7,44 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake/modules )
|
|||||||
|
|
||||||
include(InstallRequiredSystemLibraries)
|
include(InstallRequiredSystemLibraries)
|
||||||
|
|
||||||
#set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Recoll KIO Slave")
|
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Recoll KIO Slave")
|
||||||
#set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README")
|
#set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README")
|
||||||
#set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/COPYING")
|
#set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/COPYING")
|
||||||
#set(CPACK_PACKAGE_VERSION "${VERSION}")
|
set(CPACK_PACKAGE_VERSION "${VERSION}")
|
||||||
#set(CPACK_GENERATOR TGZ)
|
set(CPACK_GENERATOR TGZ)
|
||||||
#set(CPACK_SOURCE_PACKAGE_FILE_NAME "${PROJECT_NAME}-${VERSION}" CACHE INTERNAL "
|
set(CPACK_SOURCE_PACKAGE_FILE_NAME "${PROJECT_NAME}-${VERSION}" CACHE INTERNAL "tarball basename")
|
||||||
#tarball basename")
|
SET(CPACK_PACKAGE_INSTALL_DIRECTORY "${PROJECT_NAME} ${CPACK_PACKAGE_VERSION}")
|
||||||
#SET(CPACK_PACKAGE_INSTALL_DIRECTORY "${PROJECT_NAME} ${CPACK_PACKAGE_VERSION}")
|
include(CPack)
|
||||||
#include(CPack)
|
|
||||||
|
|
||||||
# search packages used by KDE
|
# search packages used by KDE
|
||||||
find_package(KDE4 REQUIRED)
|
find_package(KDE4 REQUIRED)
|
||||||
include (UsePkgConfig)
|
include(UsePkgConfig)
|
||||||
include (KDE4Defaults)
|
include(KDE4Defaults)
|
||||||
include (MacroLibrary)
|
include(MacroLibrary)
|
||||||
include(MacroOptionalAddSubdirectory)
|
include(MacroOptionalAddSubdirectory)
|
||||||
|
|
||||||
add_definitions (${QT_DEFINITIONS} ${KDE4_DEFINITIONS})
|
add_definitions(${QT_DEFINITIONS} ${KDE4_DEFINITIONS})
|
||||||
|
add_definitions(-DKDE_DEFAULT_DEBUG_AREA=7130)
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${KDE4_ENABLE_EXCEPTIONS}")
|
||||||
|
|
||||||
set(depth ../../../)
|
set(depth ../../../)
|
||||||
|
set(rcltop ${CMAKE_CURRENT_SOURCE_DIR}/${depth})
|
||||||
|
|
||||||
include_directories (${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${KDE4_INCLUDES}
|
include_directories (${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${KDE4_INCLUDES}
|
||||||
${depth}/utils ${depth}/rcldb ${depth}/query ${depth}/unac
|
${rcltop}/utils ${rcltop}/rcldb ${rcltop}/query ${rcltop}/unac
|
||||||
${depth}/common ${depth}/internfile ${depth}/index ${depth}/bincimapmime
|
${rcltop}/common ${rcltop}/internfile ${rcltop}/index ${rcltop}/bincimapmime
|
||||||
)
|
)
|
||||||
|
|
||||||
set(kio_recoll_SRCS
|
set(kio_recoll_SRCS kio_recoll.cpp)
|
||||||
kio_recoll.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
kde4_add_plugin(kio_recoll ${kio_recoll_SRCS})
|
kde4_add_plugin(kio_recoll ${kio_recoll_SRCS})
|
||||||
link_directories(/home/dockes/projets/fulltext/recoll/src/lib)
|
|
||||||
target_link_libraries(kio_recoll rcl xapian ${KDE4_KIO_LIBS})
|
|
||||||
|
|
||||||
|
target_link_libraries(kio_recoll
|
||||||
|
${rcltop}/lib/librcl.a
|
||||||
|
${rcltop}/bincimapmime/libmime.a
|
||||||
|
xapian ${KDE4_KIO_LIBS})
|
||||||
|
|
||||||
install(TARGETS kio_recoll DESTINATION ${PLUGIN_INSTALL_DIR})
|
install(TARGETS kio_recoll DESTINATION ${PLUGIN_INSTALL_DIR})
|
||||||
install(FILES recoll.protocol DESTINATION ${SERVICES_INSTALL_DIR} )
|
install(FILES recoll.protocol DESTINATION ${SERVICES_INSTALL_DIR})
|
||||||
|
install(FILES data/welcome.html
|
||||||
|
DESTINATION ${DATA_INSTALL_DIR}/kio_recoll)
|
||||||
|
|||||||
20
src/kde/kioslave/recoll/data/welcome.html
Normal file
20
src/kde/kioslave/recoll/data/welcome.html
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||||
|
<title>Recoll Search</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h3>Recoll search</h3>
|
||||||
|
|
||||||
|
<p><form method="get" action="recoll://search/query">
|
||||||
|
Query type:<br>
|
||||||
|
<input type="radio" name="qtp" value="l" checked>Query language<br>
|
||||||
|
<input type="radio" name="qtp" value="a">All terms<br>
|
||||||
|
<input type="radio" name="qtp" value="o">Any term<br>
|
||||||
|
|
||||||
|
Enter search string: <input type="text" name="q" size="40" value="%Q">
|
||||||
|
<input type="submit" value="Search"></p>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@ -1,5 +1,5 @@
|
|||||||
#ifndef lint
|
#ifndef lint
|
||||||
static char rcsid[] = "@(#$Id: kio_recoll.cpp,v 1.14 2008-11-19 12:28:59 dockes Exp $ (C) 2005 J.F.Dockes";
|
static char rcsid[] = "@(#$Id: kio_recoll.cpp,v 1.15 2008-11-20 13:10:23 dockes Exp $ (C) 2005 J.F.Dockes";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@ -26,6 +26,7 @@ using namespace std;
|
|||||||
|
|
||||||
//#include <kinstance.h>
|
//#include <kinstance.h>
|
||||||
#include <kcomponentdata.h>
|
#include <kcomponentdata.h>
|
||||||
|
#include <kstandarddirs.h>
|
||||||
|
|
||||||
#include "rclconfig.h"
|
#include "rclconfig.h"
|
||||||
#include "rcldb.h"
|
#include "rcldb.h"
|
||||||
@ -36,9 +37,65 @@ using namespace std;
|
|||||||
#include "wasastringtoquery.h"
|
#include "wasastringtoquery.h"
|
||||||
#include "wasatorcl.h"
|
#include "wasatorcl.h"
|
||||||
#include "kio_recoll.h"
|
#include "kio_recoll.h"
|
||||||
|
#include "docseqdb.h"
|
||||||
|
#include "readfile.h"
|
||||||
|
#include "smallut.h"
|
||||||
|
|
||||||
using namespace KIO;
|
using namespace KIO;
|
||||||
|
|
||||||
|
bool RecollKioPager::append(const string& data)
|
||||||
|
{
|
||||||
|
if (!m_parent) return false;
|
||||||
|
m_parent->data(QByteArray(data.c_str()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
string RecollKioPager::detailsLink()
|
||||||
|
{
|
||||||
|
string chunk = "<a href=\"recoll://command/QueryDetails\">";
|
||||||
|
chunk += tr("(show query)") + "</a>";
|
||||||
|
return chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
const static string parformat =
|
||||||
|
"<a href=\"%U\"><img src=\"%I\" align=\"left\"></a>"
|
||||||
|
"%R %S "
|
||||||
|
"<a href=\"%U\">Open</a> <b>%T</b><br>"
|
||||||
|
"%M %D <i>%U</i><br>"
|
||||||
|
"%A %K";
|
||||||
|
const string &RecollKioPager::parFormat()
|
||||||
|
{
|
||||||
|
return parformat;
|
||||||
|
}
|
||||||
|
|
||||||
|
string RecollKioPager::pageTop()
|
||||||
|
{
|
||||||
|
return "<p align=\"center\"><a href=\"recoll://welcome\">New Search</a></p>";
|
||||||
|
}
|
||||||
|
|
||||||
|
string RecollKioPager::nextUrl()
|
||||||
|
{
|
||||||
|
int pagenum = pageNumber();
|
||||||
|
if (pagenum < 0)
|
||||||
|
pagenum = 0;
|
||||||
|
else
|
||||||
|
pagenum++;
|
||||||
|
char buf[100];
|
||||||
|
sprintf(buf, "recoll://command/Page%d", pagenum);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
string RecollKioPager::prevUrl()
|
||||||
|
{
|
||||||
|
int pagenum = pageNumber();
|
||||||
|
if (pagenum <= 0)
|
||||||
|
pagenum = 0;
|
||||||
|
else
|
||||||
|
pagenum--;
|
||||||
|
char buf[100];
|
||||||
|
sprintf(buf, "recoll://command/Page%d", pagenum);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
static RclConfig *rclconfig;
|
static RclConfig *rclconfig;
|
||||||
RclConfig *RclConfig::getMainConfig()
|
RclConfig *RclConfig::getMainConfig()
|
||||||
{
|
{
|
||||||
@ -49,6 +106,7 @@ RecollProtocol::RecollProtocol(const QByteArray &pool, const QByteArray &app)
|
|||||||
: SlaveBase("recoll", pool, app), m_initok(false),
|
: SlaveBase("recoll", pool, app), m_initok(false),
|
||||||
m_rclconfig(0), m_rcldb(0)
|
m_rclconfig(0), m_rcldb(0)
|
||||||
{
|
{
|
||||||
|
m_pager.setParent(this);
|
||||||
string reason;
|
string reason;
|
||||||
rclconfig = m_rclconfig = recollinit(0, 0, m_reason);
|
rclconfig = m_rclconfig = recollinit(0, 0, m_reason);
|
||||||
if (!m_rclconfig || !m_rclconfig->ok()) {
|
if (!m_rclconfig || !m_rclconfig->ok()) {
|
||||||
@ -99,9 +157,86 @@ bool RecollProtocol::maybeOpenDb(string &reason)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static string welcomedata;
|
||||||
|
|
||||||
|
void RecollProtocol::welcomePage()
|
||||||
|
{
|
||||||
|
kDebug() << endl;
|
||||||
|
if (welcomedata.empty()) {
|
||||||
|
QString location =
|
||||||
|
KStandardDirs::locate("data", "kio_recoll/welcome.html");
|
||||||
|
string reason;
|
||||||
|
if (location.isEmpty() ||
|
||||||
|
!file_to_string((const char *)location.toUtf8(),
|
||||||
|
welcomedata, &reason)) {
|
||||||
|
welcomedata = "<html><head><title>Recoll Error</title></head>"
|
||||||
|
"<body><p>Could not locate Recoll welcome.html file: ";
|
||||||
|
welcomedata += reason;
|
||||||
|
welcomedata += "</p></body></html>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
string tmp;
|
||||||
|
map<char, string> subs;
|
||||||
|
subs['Q'] = "";
|
||||||
|
pcSubst(welcomedata, tmp, subs);
|
||||||
|
data(tmp.c_str());
|
||||||
|
kDebug() << "WelcomePage done" << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecollProtocol::doSearch(const QString& q, char opt)
|
||||||
|
{
|
||||||
|
string qs = (const char *)q.toUtf8();
|
||||||
|
Rcl::SearchData *sd = 0;
|
||||||
|
if (opt != 'l') {
|
||||||
|
Rcl::SearchDataClause *clp = 0;
|
||||||
|
if (opt == 'f') {
|
||||||
|
clp = new Rcl::SearchDataClauseFilename(qs);
|
||||||
|
} else {
|
||||||
|
// If there is no white space inside the query, then the user
|
||||||
|
// certainly means it as a phrase.
|
||||||
|
bool isreallyaphrase = false;
|
||||||
|
if (qs.find_first_of(" \t") == string::npos)
|
||||||
|
isreallyaphrase = true;
|
||||||
|
clp = isreallyaphrase ?
|
||||||
|
new Rcl::SearchDataClauseDist(Rcl::SCLT_PHRASE, qs, 0) :
|
||||||
|
new Rcl::SearchDataClauseSimple(opt == 'o' ?
|
||||||
|
Rcl::SCLT_OR : Rcl::SCLT_AND,
|
||||||
|
qs);
|
||||||
|
}
|
||||||
|
sd = new Rcl::SearchData(Rcl::SCLT_OR);
|
||||||
|
if (sd && clp)
|
||||||
|
sd->addClause(clp);
|
||||||
|
} else {
|
||||||
|
kDebug() << "Parsing query";
|
||||||
|
sd = wasaStringToRcl(qs, m_reason);
|
||||||
|
}
|
||||||
|
if (!sd) {
|
||||||
|
m_reason = "Internal Error: cant allocate new query";
|
||||||
|
outputError(m_reason.c_str());
|
||||||
|
finished();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RefCntr<Rcl::SearchData> sdata(sd);
|
||||||
|
sdata->setStemlang("english");
|
||||||
|
RefCntr<Rcl::Query>query(new Rcl::Query(m_rcldb));
|
||||||
|
if (!query->setQuery(sdata)) {
|
||||||
|
m_reason = "Internal Error: setQuery failed";
|
||||||
|
outputError(m_reason.c_str());
|
||||||
|
finished();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DocSequenceDb *src =
|
||||||
|
new DocSequenceDb(RefCntr<Rcl::Query>(query), "Query results",
|
||||||
|
sdata);
|
||||||
|
|
||||||
|
m_pager.setDocSource(RefCntr<DocSequence>(src));
|
||||||
|
m_pager.resultPageNext();
|
||||||
|
}
|
||||||
|
|
||||||
void RecollProtocol::get(const KUrl & url)
|
void RecollProtocol::get(const KUrl & url)
|
||||||
{
|
{
|
||||||
kDebug() << "RecollProtocol::get:" << url << endl;
|
kDebug() << url << endl;
|
||||||
|
|
||||||
mimeType("text/html");
|
mimeType("text/html");
|
||||||
|
|
||||||
@ -111,98 +246,75 @@ void RecollProtocol::get(const KUrl & url)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
string iconsdir;
|
QString host = url.host();
|
||||||
m_rclconfig->getConfParam("iconsdir", iconsdir);
|
|
||||||
if (iconsdir.empty()) {
|
|
||||||
iconsdir = path_cat("/usr/local/share/recoll", "images");
|
|
||||||
} else {
|
|
||||||
iconsdir = path_tildexpand(iconsdir);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString path = url.path();
|
QString path = url.path();
|
||||||
kDebug() << "RecollProtocol::get:path:" << path << endl;
|
kDebug() << "host:" << host << " path:" << path;
|
||||||
QByteArray u8 = path.toUtf8();
|
if (host.isEmpty() || !host.compare("welcome")) {
|
||||||
|
kDebug() << "Host is empty";
|
||||||
|
if (path.isEmpty() || !path.compare("/")) {
|
||||||
|
kDebug() << "Path is empty or strange";
|
||||||
|
// Display welcome page
|
||||||
|
welcomePage();
|
||||||
|
finished();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Ie: "recoll: some search string"
|
||||||
|
doSearch(path);
|
||||||
|
} else if (!host.compare("search")) {
|
||||||
|
if (path.compare("/query")) {
|
||||||
|
finished(); return;
|
||||||
|
}
|
||||||
|
// Decode the forms' arguments
|
||||||
|
QString query = url.queryItem("q");
|
||||||
|
if (query.isEmpty()) {
|
||||||
|
finished(); return;
|
||||||
|
}
|
||||||
|
QString opt = url.queryItem("qtp");
|
||||||
|
if (opt.isEmpty()) {
|
||||||
|
opt = "l";
|
||||||
|
}
|
||||||
|
doSearch(query, opt.toUtf8().at(0));
|
||||||
|
} else if (!host.compare("command")) {
|
||||||
|
if (path.isEmpty()) {
|
||||||
|
finished();return;
|
||||||
|
} else if (path.indexOf("/Page") == 0) {
|
||||||
|
int newpage = 0;
|
||||||
|
sscanf(path.toUtf8(), "/Page%d", &newpage);
|
||||||
|
if (newpage > m_pager.pageNumber()) {
|
||||||
|
int npages = newpage - m_pager.pageNumber();
|
||||||
|
for (int i = 0; i < npages; i++)
|
||||||
|
m_pager.resultPageNext();
|
||||||
|
} else if (newpage < m_pager.pageNumber()) {
|
||||||
|
int npages = m_pager.pageNumber() - newpage;
|
||||||
|
for (int i = 0; i < npages; i++)
|
||||||
|
m_pager.resultPageBack();
|
||||||
|
}
|
||||||
|
} else if (path.indexOf("/QueryDetails") == 0) {
|
||||||
|
QByteArray array;
|
||||||
|
QTextStream os(&array, QIODevice::WriteOnly);
|
||||||
|
|
||||||
RefCntr<Rcl::SearchData> sdata = wasaStringToRcl((const char*)u8, m_reason);
|
os << "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">" << endl;
|
||||||
sdata->setStemlang("english");
|
os << "<title>" << "Recoll query details" << "</title>\n" << endl;
|
||||||
|
os << "</head>" << endl;
|
||||||
RefCntr<Rcl::Query>query(new Rcl::Query(m_rcldb));
|
os << "<body><h3>Query details:</h3>" << endl;
|
||||||
if (!query->setQuery(sdata)) {
|
os << "<p>" << m_pager.queryDescription().c_str() <<"</p>"<< endl;
|
||||||
m_reason = "Internal Error: setQuery failed";
|
os << "<p><a href=\"recoll://command/Page" <<
|
||||||
outputError(m_reason.c_str());
|
m_pager.pageNumber() << "\">Return to results</a>" << endl;
|
||||||
finished();
|
os << "</body></html>" << endl;
|
||||||
return;
|
data(array);
|
||||||
|
finished();
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// Unknown //command/???
|
||||||
|
finished(); return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Unknown 'host' //??? value
|
||||||
|
finished(); return;
|
||||||
}
|
}
|
||||||
|
|
||||||
string explain = sdata->getDescription();
|
m_pager.displayPage();
|
||||||
|
|
||||||
QByteArray output;
|
|
||||||
QTextStream os(&output, QIODevice::ReadWrite);
|
|
||||||
|
|
||||||
os << "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">" << endl;
|
|
||||||
os << "<html><head>" << endl;
|
|
||||||
os << "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">" << endl;
|
|
||||||
os << "<meta http-equiv=\"Pragma\" content=\"no-cache\">" << endl;
|
|
||||||
os << "<title>Recoll: query results</title>" << endl;
|
|
||||||
os << "</head><body>" << endl;
|
|
||||||
|
|
||||||
os << "<p><b>Actual query performed: </b>" << endl;
|
|
||||||
os << explain.c_str() << "</p>";
|
|
||||||
|
|
||||||
Rcl::Doc doc;
|
|
||||||
int cnt = query->getResCnt();
|
|
||||||
for (int i = 0; i < cnt; i++) {
|
|
||||||
string sh;
|
|
||||||
doc.erase();
|
|
||||||
|
|
||||||
if (!query->getDoc(i, doc)) {
|
|
||||||
// This may very well happen for history if the doc has
|
|
||||||
// been removed since. So don't treat it as fatal.
|
|
||||||
doc.meta[Rcl::Doc::keykw] = string("Unavailable document");
|
|
||||||
}
|
|
||||||
|
|
||||||
string iconname = m_rclconfig->getMimeIconName(doc.mimetype);
|
|
||||||
if (iconname.empty())
|
|
||||||
iconname = "document";
|
|
||||||
string imgfile = iconsdir + "/" + iconname + ".png";
|
|
||||||
|
|
||||||
string result;
|
|
||||||
if (!sh.empty())
|
|
||||||
result += string("<p><b>") + sh + "</p>\n<p>";
|
|
||||||
else
|
|
||||||
result = "<p>";
|
|
||||||
|
|
||||||
char perbuf[10];
|
|
||||||
sprintf(perbuf, "%3d%%", doc.pc);
|
|
||||||
if (doc.meta[Rcl::Doc::keytt].empty())
|
|
||||||
doc.meta[Rcl::Doc::keytt] = path_getsimple(doc.url);
|
|
||||||
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);
|
|
||||||
strftime(datebuf, 99,
|
|
||||||
"<i>Modified:</i> %Y-%m-%d %H:%M:%S", tm);
|
|
||||||
}
|
|
||||||
result += "<a href=\"" + doc.url + "\">" +
|
|
||||||
"<img src=\"file://" + imgfile + "\" align=\"left\">" + "</a>";
|
|
||||||
string abst = escapeHtml(doc.meta[Rcl::Doc::keyabs]);
|
|
||||||
result += string(perbuf) + " <b>" + doc.meta[Rcl::Doc::keytt] + "</b><br>" +
|
|
||||||
doc.mimetype + " " +
|
|
||||||
(datebuf[0] ? string(datebuf) + "<br>" : string("<br>")) +
|
|
||||||
(!abst.empty() ? abst + "<br>" : string("")) +
|
|
||||||
(!doc.meta[Rcl::Doc::keytt].empty() ? doc.meta[Rcl::Doc::keykw] +
|
|
||||||
"<br>" : string("")) +
|
|
||||||
"<a href=\"" + doc.url + "\">" + doc.url + "</a><br></p>\n";
|
|
||||||
|
|
||||||
QString str = QString::fromUtf8(result.c_str(), result.length());
|
|
||||||
os << str;
|
|
||||||
}
|
|
||||||
|
|
||||||
os << "</body></html>";
|
|
||||||
|
|
||||||
data(output);
|
|
||||||
kDebug() << "call finished" << endl;
|
kDebug() << "call finished" << endl;
|
||||||
finished();
|
finished();
|
||||||
}
|
}
|
||||||
@ -221,6 +333,8 @@ void RecollProtocol::outputError(const QString& errmsg)
|
|||||||
data(array);
|
data(array);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Note: KDE_EXPORT is actually needed on Unix when building with
|
// Note: KDE_EXPORT is actually needed on Unix when building with
|
||||||
// cmake. Says something like __attribute__(visibility(defautl))
|
// cmake. Says something like __attribute__(visibility(defautl))
|
||||||
// (cmake apparently sets all symbols to not exported)
|
// (cmake apparently sets all symbols to not exported)
|
||||||
@ -228,9 +342,6 @@ extern "C" {KDE_EXPORT int kdemain(int argc, char **argv);}
|
|||||||
|
|
||||||
int kdemain(int argc, char **argv)
|
int kdemain(int argc, char **argv)
|
||||||
{
|
{
|
||||||
FILE*mf = fopen("/tmp/toto","w");
|
|
||||||
fprintf(mf, "KIORECOLL\n");
|
|
||||||
fclose(mf);
|
|
||||||
#ifdef KDE_VERSION_3
|
#ifdef KDE_VERSION_3
|
||||||
KInstance instance("kio_recoll");
|
KInstance instance("kio_recoll");
|
||||||
#else
|
#else
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#ifndef _RECOLL_H
|
#ifndef _RECOLL_H
|
||||||
/* @(#$Id: kio_recoll.h,v 1.5 2008-11-17 14:51:38 dockes Exp $ (C) 2005 J.F.Dockes */
|
/* @(#$Id: kio_recoll.h,v 1.6 2008-11-20 13:10:23 dockes Exp $ (C) 2005 J.F.Dockes */
|
||||||
#define _RECOLL_H
|
#define _RECOLL_H
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
@ -17,6 +17,25 @@ using std::string;
|
|||||||
#include <kio/global.h>
|
#include <kio/global.h>
|
||||||
#include <kio/slavebase.h>
|
#include <kio/slavebase.h>
|
||||||
|
|
||||||
|
#include "reslistpager.h"
|
||||||
|
|
||||||
|
class RecollProtocol;
|
||||||
|
|
||||||
|
class RecollKioPager : public ResListPager {
|
||||||
|
public:
|
||||||
|
RecollKioPager() : m_parent(0) {}
|
||||||
|
void setParent(RecollProtocol *proto) {m_parent = proto;}
|
||||||
|
|
||||||
|
virtual bool append(const string& data);
|
||||||
|
virtual string detailsLink();
|
||||||
|
virtual const string &parFormat();
|
||||||
|
virtual string nextUrl();
|
||||||
|
virtual string prevUrl();
|
||||||
|
virtual string pageTop();
|
||||||
|
private:
|
||||||
|
RecollProtocol *m_parent;
|
||||||
|
};
|
||||||
|
|
||||||
class RecollProtocol : public KIO::SlaveBase {
|
class RecollProtocol : public KIO::SlaveBase {
|
||||||
public:
|
public:
|
||||||
RecollProtocol(const QByteArray &pool, const QByteArray &app );
|
RecollProtocol(const QByteArray &pool, const QByteArray &app );
|
||||||
@ -25,13 +44,18 @@ class RecollProtocol : public KIO::SlaveBase {
|
|||||||
virtual void get(const KUrl & url );
|
virtual void get(const KUrl & url );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_initok;
|
|
||||||
RclConfig *m_rclconfig;
|
|
||||||
Rcl::Db *m_rcldb;
|
|
||||||
std::string m_dbdir;
|
|
||||||
std::string m_reason;
|
|
||||||
bool maybeOpenDb(string &reason);
|
bool maybeOpenDb(string &reason);
|
||||||
void outputError(const QString& errmsg);
|
void outputError(const QString& errmsg);
|
||||||
|
void doSearch(const QString& q, char opt = 'l');
|
||||||
|
void welcomePage();
|
||||||
|
|
||||||
|
bool m_initok;
|
||||||
|
RclConfig *m_rclconfig;
|
||||||
|
Rcl::Db *m_rcldb;
|
||||||
|
std::string m_dbdir;
|
||||||
|
std::string m_reason;
|
||||||
|
RecollKioPager m_pager;
|
||||||
|
|
||||||
};
|
};
|
||||||
extern "C" {int kdemain(int, char**);}
|
extern "C" {int kdemain(int, char**);}
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,11 @@
|
|||||||
#ifndef lint
|
#ifndef lint
|
||||||
static char rcsid[] = "@(#$Id: reslistpager.cpp,v 1.1 2008-11-19 12:19:40 dockes Exp $ (C) 2007 J.F.Dockes";
|
static char rcsid[] = "@(#$Id: reslistpager.cpp,v 1.2 2008-11-20 13:10:23 dockes Exp $ (C) 2007 J.F.Dockes";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
#include "reslistpager.h"
|
#include "reslistpager.h"
|
||||||
#include "debuglog.h"
|
#include "debuglog.h"
|
||||||
#include "rclconfig.h"
|
#include "rclconfig.h"
|
||||||
@ -63,31 +67,9 @@ void ResListPager::resultPageNext()
|
|||||||
m_respage = npage;
|
m_respage = npage;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ResListPager::append(const string& data)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "%s", data.c_str());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
string ResListPager::tr(const string& in)
|
|
||||||
{
|
|
||||||
return in;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ResListPager::displayPage()
|
void ResListPager::displayPage()
|
||||||
{
|
{
|
||||||
string chunk;
|
string chunk;
|
||||||
if (pageEmpty()) {
|
|
||||||
chunk = "<html><head></head><body>"
|
|
||||||
"<p><font size=+1><b>";
|
|
||||||
chunk += m_docSource->title();
|
|
||||||
chunk += "</b></font>";
|
|
||||||
chunk += "<a href=\"H-1\">";
|
|
||||||
chunk += tr("Show query details");
|
|
||||||
chunk += "</a><br>";
|
|
||||||
append(chunk);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display list header
|
// Display list header
|
||||||
// We could use a <title> but the textedit doesnt display
|
// We could use a <title> but the textedit doesnt display
|
||||||
@ -97,8 +79,12 @@ void ResListPager::displayPage()
|
|||||||
// gets confused. Hence the use of the 'chunk' text
|
// gets confused. Hence the use of the 'chunk' text
|
||||||
// accumulator
|
// accumulator
|
||||||
// Also note that there can be results beyond the estimated resCnt.
|
// Also note that there can be results beyond the estimated resCnt.
|
||||||
chunk = "<qt><head></head><body><p>"
|
chunk = "<html><head>"
|
||||||
"<font size=+1><b>";
|
"<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 += m_docSource->title();
|
||||||
chunk += "</b></font>"
|
chunk += "</b></font>"
|
||||||
" ";
|
" ";
|
||||||
@ -121,9 +107,21 @@ void ResListPager::displayPage()
|
|||||||
chunk += buf;
|
chunk += buf;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
chunk += "<a href=\"H-1\">";
|
chunk += detailsLink();
|
||||||
chunk += tr("(show query)");
|
if (hasPrev() || hasNext()) {
|
||||||
chunk += "</a></p>";
|
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);
|
append(chunk);
|
||||||
if (pageEmpty())
|
if (pageEmpty())
|
||||||
@ -154,6 +152,7 @@ void ResListPager::displayPage()
|
|||||||
// Determine icon to display if any
|
// Determine icon to display if any
|
||||||
string iconpath;
|
string iconpath;
|
||||||
RclConfig::getMainConfig()->getMimeIconName(doc.mimetype, &iconpath);
|
RclConfig::getMainConfig()->getMimeIconName(doc.mimetype, &iconpath);
|
||||||
|
iconpath = string("file://") + iconpath;
|
||||||
|
|
||||||
// Printable url: either utf-8 if transcoding succeeds, or url-encoded
|
// Printable url: either utf-8 if transcoding succeeds, or url-encoded
|
||||||
string url;
|
string url;
|
||||||
@ -249,7 +248,7 @@ void ResListPager::displayPage()
|
|||||||
subs['U'] = url;
|
subs['U'] = url;
|
||||||
|
|
||||||
string formatted;
|
string formatted;
|
||||||
pcSubst(m_parformat, formatted, subs);
|
pcSubst(parFormat(), formatted, subs);
|
||||||
chunk += formatted;
|
chunk += formatted;
|
||||||
|
|
||||||
chunk += "</p>\n";
|
chunk += "</p>\n";
|
||||||
@ -262,12 +261,12 @@ void ResListPager::displayPage()
|
|||||||
chunk = "<p align=\"center\">";
|
chunk = "<p align=\"center\">";
|
||||||
if (hasPrev() || hasNext()) {
|
if (hasPrev() || hasNext()) {
|
||||||
if (hasPrev()) {
|
if (hasPrev()) {
|
||||||
chunk += "<a href=\"p-1\"><b>";
|
chunk += "<a href=\"" + prevUrl() + "\"><b>";
|
||||||
chunk += tr("Previous");
|
chunk += tr("Previous");
|
||||||
chunk += "</b></a> ";
|
chunk += "</b></a> ";
|
||||||
}
|
}
|
||||||
if (hasNext()) {
|
if (hasNext()) {
|
||||||
chunk += "<a href=\"n-1\"><b>";
|
chunk += "<a href=\""+ nextUrl() + "\"><b>";
|
||||||
chunk += tr("Next");
|
chunk += tr("Next");
|
||||||
chunk += "</b></a>";
|
chunk += "</b></a>";
|
||||||
}
|
}
|
||||||
@ -276,3 +275,41 @@ void ResListPager::displayPage()
|
|||||||
chunk += "</body></html>\n";
|
chunk += "</body></html>\n";
|
||||||
append(chunk);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
#ifndef _reslistpager_h_included_
|
#ifndef _reslistpager_h_included_
|
||||||
#define _reslistpager_h_included_
|
#define _reslistpager_h_included_
|
||||||
/* @(#$Id: reslistpager.h,v 1.1 2008-11-19 12:19:40 dockes Exp $ (C) 2007 J.F.Dockes */
|
/* @(#$Id: reslistpager.h,v 1.2 2008-11-20 13:10:23 dockes Exp $ (C) 2007 J.F.Dockes */
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
using std::vector;
|
using std::vector;
|
||||||
@ -13,12 +13,25 @@ using std::vector;
|
|||||||
*/
|
*/
|
||||||
class ResListPager {
|
class ResListPager {
|
||||||
public:
|
public:
|
||||||
ResListPager(RefCntr<DocSequence> src, int pagesize,
|
ResListPager() : m_pagesize(10), m_hasNext(false) {}
|
||||||
const string& parformat)
|
void setDocSource(RefCntr<DocSequence> src)
|
||||||
: m_docSource(src), m_pagesize(pagesize), m_parformat(parformat),
|
{
|
||||||
m_hasNext(false)
|
m_winfirst = -1;
|
||||||
|
m_docSource = src;
|
||||||
|
m_hasNext = false;
|
||||||
|
m_respage.clear();
|
||||||
|
}
|
||||||
|
ResListPager(RefCntr<DocSequence> src, int pagesize)
|
||||||
|
: m_winfirst(-1), m_docSource(src), m_pagesize(pagesize),
|
||||||
|
m_hasNext(false)
|
||||||
{}
|
{}
|
||||||
virtual ~ResListPager() {}
|
virtual ~ResListPager() {}
|
||||||
|
int pageNumber()
|
||||||
|
{
|
||||||
|
if (m_winfirst < 0 || m_pagesize <= 0)
|
||||||
|
return -1;
|
||||||
|
return m_winfirst / m_pagesize;
|
||||||
|
}
|
||||||
void pageNext();
|
void pageNext();
|
||||||
bool hasNext() {return m_hasNext;}
|
bool hasNext() {return m_hasNext;}
|
||||||
bool hasPrev() {return m_winfirst > 0;}
|
bool hasPrev() {return m_winfirst > 0;}
|
||||||
@ -35,14 +48,24 @@ public:
|
|||||||
void resultPageNext();
|
void resultPageNext();
|
||||||
void displayPage();
|
void displayPage();
|
||||||
bool pageEmpty() {return m_respage.size() == 0;}
|
bool pageEmpty() {return m_respage.size() == 0;}
|
||||||
|
|
||||||
|
string queryDescription() {return m_docSource.isNull() ? "" :
|
||||||
|
m_docSource->getDescription();}
|
||||||
|
|
||||||
|
// Things that need to be reimplemented in the subclass:
|
||||||
virtual bool append(const string& data);
|
virtual bool append(const string& data);
|
||||||
virtual string tr(const string& in);
|
virtual string tr(const string& in);
|
||||||
|
virtual string detailsLink();
|
||||||
|
virtual const string &parFormat();
|
||||||
|
virtual string nextUrl();
|
||||||
|
virtual string prevUrl();
|
||||||
|
virtual string pageTop() {return string();}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// First docnum (from docseq) in current page
|
// First docnum (from docseq) in current page
|
||||||
int m_winfirst;
|
int m_winfirst;
|
||||||
RefCntr<DocSequence> m_docSource;
|
RefCntr<DocSequence> m_docSource;
|
||||||
int m_pagesize;
|
int m_pagesize;
|
||||||
string m_parformat;
|
|
||||||
|
|
||||||
bool m_hasNext;
|
bool m_hasNext;
|
||||||
vector<ResListEntry> m_respage;
|
vector<ResListEntry> m_respage;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user