Arrange so we can now open the parent of a document (e.g. chm file instead of temp copy of html page inside chm), even when the parent is itself embedded in an archive

This commit is contained in:
Jean-Francois Dockes 2012-10-12 16:54:52 +02:00
parent 382ae1f718
commit 5add2e2384
13 changed files with 222 additions and 59 deletions

View File

@ -215,7 +215,7 @@
<application>Python</application>
programming interface</link>, a <link linkend="rcl.search.kio">
<application>KDE</application> KIO slave module</link>, and
a <application>Ubuntu Unity Lens</application> module.
a <ulink url="http://bitbucket.org/medoc/recoll/wiki/UnityLens">Ubuntu Unity Lens</ulink> module.
</para>
</sect1>
@ -2825,8 +2825,29 @@ dir:recoll dir:src -dir:utils -dir:common
<title>Desktop integration</title>
<para>Being independant of the desktop type has its drawbacks: &RCL;
desktop integration is minimal. Here follow a few things that may
help.</para>
desktop integration is minimal. However there are a few tools
available:
<itemizedlist>
<listitem>
<para>The <application>KDE</application> KIO Slave was
described in a <link linkend="rcl.search.kio">previous
section</link>.</para>
</listitem>
<listitem>
<para>If you use a recent version of Ubuntu Linux, you may
find the <ulink
url="http://bitbucket.org/medoc/recoll/wiki/UnityLens">Ubuntu Unity
Lens</ulink> module useful.</para>
</listitem>
<listitem>
<para>There is also an independantly developed
<ulink
url="http://kde-apps.org/content/show.php/recollrunner?content=128203">
Krunner plugin</ulink>.</para>
</listitem>
</itemizedlist>
<para>Here follow a few other things that may help.</para>
<sect2 id="rcl.search.shortcut">
<title>Hotkeying recoll</title>
@ -2844,6 +2865,7 @@ dir:recoll dir:src -dir:utils -dir:common
<sect2 id="rcl.kicker-applet">
<title>The KDE Kicker Recoll applet</title>
<para>This is probably obsolete now. Anyway:</para>
<para>The &RCL; source tree contains the source code to the
<application>recoll_applet</application>, a small application derived
from the <application>find_applet</application>. This can be used to

View File

@ -190,7 +190,6 @@ class rclCHM:
def __init__(self, em):
self.contents = []
self.chm = chm.CHMFile()
self.currentindex = 0
self.em = em
if rclchm_catenate:
self.em.setmimetype("text/plain")
@ -240,7 +239,7 @@ class rclCHM:
"""Open the chm file and build the contents list by extracting and
parsing the Topics object"""
self.currentindex = 0
self.currentindex = -1
self.contents = []
filename = params["filename:"]
@ -248,8 +247,6 @@ class rclCHM:
self.em.rclog("LoadCHM failed")
return False
self.sfn = os.path.basename(filename)
#self.em.rclog("home [%s] topics [%s] title [%s]" %
# (self.chm.home, self.chm.topics, self.chm.title))
@ -293,6 +290,16 @@ class rclCHM:
else:
return (False, "", "", rclexecm.RclExecM.eofnow)
if self.currentindex == -1:
# Return "self" doc
self.currentindex = 0
self.em.setmimetype('text/plain')
if len(self.contents) == 0:
eof = rclexecm.RclExecM.eofnext
else:
eof = rclexecm.RclExecM.noteof
return (True, "", "", eof)
if self.currentindex >= len(self.contents):
return (False, "", "", rclexecm.RclExecM.eofnow)
else:

View File

@ -48,7 +48,7 @@ class rclEPUB:
def openfile(self, params):
"""Open the EPUB file, create a contents array"""
self.currentindex = 0
self.currentindex = -1
self.contents = []
try:
self.book = epub.open(params["filename:"])
@ -65,6 +65,17 @@ class rclEPUB:
return self.extractone(params["ipath:"])
def getnext(self, params):
if self.currentindex == -1:
# Return "self" doc
self.currentindex = 0
self.em.setmimetype('text/plain')
if len(self.contents) == 0:
eof = rclexecm.RclExecM.eofnext
else:
eof = rclexecm.RclExecM.noteof
return (True, "", "", eof)
if self.currentindex >= len(self.contents):
return (False, "", "", rclexecm.RclExecM.eofnow)
else:

View File

@ -60,7 +60,7 @@ class IcalExtractor:
self.em.rclog("Openfile: open: %s" % str(e))
return False
self.currentindex = 0
self.currentindex = -1
if usemodule == 'internal':
self.contents = ICalSimpleSplitter().splitcalendar(calstr)
@ -96,6 +96,17 @@ class IcalExtractor:
return self.extractone(index)
def getnext(self, params):
if self.currentindex == -1:
# Return "self" doc
self.currentindex = 0
self.em.setmimetype('text/plain')
if len(self.contents) == 0:
eof = rclexecm.RclExecM.eofnext
else:
eof = rclexecm.RclExecM.noteof
return (True, "", "", eof)
if self.currentindex >= len(self.contents):
self.em.rclog("getnext: EOF hit")
return (False, "", "", rclexecm.RclExecM.eofnow)

View File

@ -73,7 +73,7 @@ class InfoExtractor:
sys.exit(1);
self.currentindex = 0
self.currentindex = -1
self.contents = InfoSimpleSplitter().splitinfo(self.file, infostream)
@ -90,6 +90,17 @@ class InfoExtractor:
# Extract next in list
def getnext(self, params):
if self.currentindex == -1:
# Return "self" doc
self.currentindex = 0
self.em.setmimetype('text/plain')
if len(self.contents) == 0:
eof = rclexecm.RclExecM.eofnext
else:
eof = rclexecm.RclExecM.noteof
return (True, "", "", eof)
if self.currentindex >= len(self.contents):
self.em.rclog("getnext: EOF hit")
return (False, "", "", rclexecm.RclExecM.eofnow)

View File

@ -73,7 +73,7 @@ class RarExtractor:
###### File type handler api, used by rclexecm ---------->
def openfile(self, params):
self.currentindex = 0
self.currentindex = -1
try:
self.rar = RarFile(params["filename:"])
return True
@ -93,6 +93,17 @@ class RarExtractor:
return (ok, data, ipath, eof)
def getnext(self, params):
if self.currentindex == -1:
# Return "self" doc
self.currentindex = 0
self.em.setmimetype('text/plain')
if len(self.rar.namelist()) == 0:
eof = rclexecm.RclExecM.eofnext
else:
eof = rclexecm.RclExecM.noteof
return (True, "", "", eof)
if self.currentindex >= len(self.rar.namelist()):
#self.em.rclog("getnext: EOF hit")
return (False, "", "", rclexecm.RclExecM.eofnow)

View File

@ -43,7 +43,7 @@ class TarExtractor:
return (ok, docdata, ipath, iseof)
def openfile(self, params):
self.currentindex = 0
self.currentindex = -1
try:
self.tar = open(name=params["filename:"],mode='r')
self.namen = [ y.name for y in filter(lambda z:z.isfile(),self.tar.getmembers())]
@ -63,6 +63,17 @@ class TarExtractor:
return (ok, data, ipath, eof)
def getnext(self, params):
if self.currentindex == -1:
# Return "self" doc
self.currentindex = 0
self.em.setmimetype('text/plain')
if len(self.namen) == 0:
eof = rclexecm.RclExecM.eofnext
else:
eof = rclexecm.RclExecM.noteof
return (True, "", "", eof)
if self.currentindex >= len(self.namen):
self.namen=[]
return (False, "", "", rclexecm.RclExecM.eofnow)

View File

@ -22,7 +22,7 @@ class WarExtractor:
###### File type handler api, used by rclexecm ---------->
def openfile(self, params):
self.currentindex = 0
self.currentindex = -1
try:
self.tar = tarfile.open(params["filename:"])
return True
@ -40,6 +40,11 @@ class WarExtractor:
return self.extractone(tarinfo)
def getnext(self, params):
if self.currentindex == -1:
# Return "self" doc
self.currentindex = 0
return (True, "", "", rclexecm.RclExecM.noteof)
tarinfo = self.tar.next()
if tarinfo is None:
#self.em.rclog("getnext: EOF hit")

View File

@ -67,7 +67,7 @@ class ZipExtractor:
###### File type handler api, used by rclexecm ---------->
def openfile(self, params):
self.currentindex = 0
self.currentindex = -1
try:
self.zip = ZipFile(params["filename:"])
return True
@ -87,6 +87,16 @@ class ZipExtractor:
return (ok, data, ipath, eof)
def getnext(self, params):
if self.currentindex == -1:
# Return "self" doc
self.currentindex = 0
self.em.setmimetype('text/plain')
if len(self.zip.namelist()) == 0:
eof = rclexecm.RclExecM.eofnext
else:
eof = rclexecm.RclExecM.noteof
return (True, "", "", eof)
if self.currentindex >= len(self.zip.namelist()):
#self.em.rclog("getnext: EOF hit")
return (False, "", "", rclexecm.RclExecM.eofnow)

View File

@ -126,6 +126,16 @@ bool FileInterner::getEnclosing(const string &url, const string &ipath,
return true;
}
string FileInterner::getLastIpathElt(const string& ipath)
{
string::size_type sep;
if ((sep = ipath.find_last_of(cstr_isep)) != string::npos) {
return ipath.substr(sep + 1);
} else {
return ipath;
}
}
// Uncompress input file into a temporary one, by executing the appropriate
// script.
static bool uncompressfile(RclConfig *conf, const string& ifn,

View File

@ -83,6 +83,9 @@ class FileInterner {
static bool getEnclosing(const string &url, const string &ipath,
string &eurl, string &eipath, string& udi);
/** Return last element in ipath, like basename */
static std::string getLastIpathElt(const std::string& ipath);
/** Constructors take the initial step to preprocess the data object and
* create the top filter */

View File

@ -1501,13 +1501,17 @@ static bool lookForHtmlBrowser(string &exefile)
void RclMain::startNativeViewer(Rcl::Doc doc, int pagenum, QString term)
{
LOGDEB(("RclMain::startNativeViewer: page %d\n", pagenum));
// Look for appropriate viewer
string apptag;
doc.getmeta(Rcl::Doc::keyapptg, &apptag);
LOGDEB(("RclMain::startNativeViewer: mtype [%s] apptag [%s] page %d "
"term [%s] url [%s] ipath [%s]\n",
doc.mimetype.c_str(), apptag.c_str(), pagenum,
(const char *)(term.toUtf8()), doc.url.c_str(), doc.ipath.c_str()
));
// Look for appropriate viewer
string cmdplusattr = theconfig->getMimeViewerDef(doc.mimetype, apptag,
prefs.useDesktopOpen);
if (cmdplusattr.empty()) {
QMessageBox::warning(0, "Recoll",
tr("No external viewer configured for mime type [")
@ -1515,25 +1519,12 @@ void RclMain::startNativeViewer(Rcl::Doc doc, int pagenum, QString term)
return;
}
if (pagenum == -1) {
pagenum = 1;
string lterm;
if (m_source.isNotNull())
pagenum = m_source->getFirstMatchPage(doc, lterm);
if (pagenum == -1)
pagenum = 1;
else
term = QString::fromUtf8(lterm.c_str());
}
char cpagenum[20];
sprintf(cpagenum, "%d", pagenum);
// Extract possible viewer attributes
ConfSimple attrs;
// Separate command string and viewer attributes (if any)
ConfSimple viewerattrs;
string cmd;
theconfig->valueSplitAttributes(cmdplusattr, cmd, attrs);
theconfig->valueSplitAttributes(cmdplusattr, cmd, viewerattrs);
bool ignoreipath = false;
if (attrs.get("ignoreipath", cmdplusattr))
if (viewerattrs.get("ignoreipath", cmdplusattr))
ignoreipath = stringToBool(cmdplusattr);
// Split the command line
@ -1541,7 +1532,7 @@ void RclMain::startNativeViewer(Rcl::Doc doc, int pagenum, QString term)
if (!stringToStrings(cmd, lcmd)) {
QMessageBox::warning(0, "Recoll",
tr("Bad viewer command line for %1: [%2]\n"
"Please check the mimeconf file")
"Please check the mimeview file")
.arg(QString::fromAscii(doc.mimetype.c_str()))
.arg(QString::fromLocal8Bit(cmd.c_str())));
return;
@ -1549,23 +1540,23 @@ void RclMain::startNativeViewer(Rcl::Doc doc, int pagenum, QString term)
// Look for the command to execute in the exec path and the filters
// directory
string cmdpath;
if (!ExecCmd::which(lcmd.front(), cmdpath)) {
cmdpath = theconfig->findFilter(lcmd.front());
string execpath;
if (!ExecCmd::which(lcmd.front(), execpath)) {
execpath = theconfig->findFilter(lcmd.front());
// findFilter returns its input param if the filter is not in
// the normal places. As we already looked in the path, we
// have no use for a simple command name here (as opposed to
// mimehandler which will just let execvp do its thing). Erase
// cmdpath so that the user dialog will be started further
// execpath so that the user dialog will be started further
// down.
if (!cmdpath.compare(lcmd.front()))
cmdpath.erase();
if (!execpath.compare(lcmd.front()))
execpath.erase();
// Specialcase text/html because of the help browser need
if (cmdpath.empty() && !doc.mimetype.compare("text/html")) {
if (lookForHtmlBrowser(cmdpath)) {
if (execpath.empty() && !doc.mimetype.compare("text/html")) {
if (lookForHtmlBrowser(execpath)) {
lcmd.clear();
lcmd.push_back(cmdpath);
lcmd.push_back(execpath);
lcmd.push_back("%u");
}
}
@ -1573,7 +1564,7 @@ void RclMain::startNativeViewer(Rcl::Doc doc, int pagenum, QString term)
// Command not found: start the user dialog to help find another one:
if (cmdpath.empty()) {
if (execpath.empty()) {
QString mt = QString::fromAscii(doc.mimetype.c_str());
QString message = tr("The viewer specified in mimeview for %1: %2"
" is not found.\nDo you want to start the "
@ -1594,22 +1585,68 @@ void RclMain::startNativeViewer(Rcl::Doc doc, int pagenum, QString term)
// new command.
return;
}
// Get rid of the command name. lcmd is now argv[1...n]
lcmd.erase(lcmd.begin());
// We may need a temp file, or not, depending on the command
// arguments and the fact that this is a subdoc or not.
bool wantsipath = (cmd.find("%i") != string::npos) || ignoreipath;
// Process the command arguments to determine if we need to create
// a temporary file.
// If the command has a %i parameter it will manage the
// un-embedding. Else if ipath is not empty, we need a temp file.
// This can be overridden with the "ignoreipath" attribute
bool groksipath = (cmd.find("%i") != string::npos) || ignoreipath;
// wantsfile: do we actually need a local file ? The only other
// case here is an url %u (ie: for web history).
bool wantsfile = cmd.find("%f") != string::npos;
bool istempfile = false;
string fn = fileurltolocalpath(doc.url);
string orgfn = fn;
bool wantsparentfile = cmd.find("%F") != string::npos;
if (wantsfile && wantsparentfile) {
QMessageBox::warning(0, "Recoll",
tr("Viewer command line for %1 specifies both "
"file and parent file value: unsupported")
.arg(QString::fromAscii(doc.mimetype.c_str())));
return;
}
string url = doc.url;
string fn = fileurltolocalpath(doc.url);
Rcl::Doc pdoc;
if (wantsparentfile) {
// We want the path for the parent document. For example to
// open the chm file, not the internal page. Note that we just
// override the other file name in this case.
if (m_source.isNull() || !m_source->getEnclosing(doc, pdoc)) {
QMessageBox::warning(0, "Recoll",
tr("Cannot find parent document"));
return;
}
// Override fn with the parent's :
fn = fileurltolocalpath(pdoc.url);
// If the parent document has an ipath too, we need to create
// a temp file even if the command takes an ipath
// parameter. We have no viewer which could handle a double
// embedding. Will have to change if such a one appears.
if (!pdoc.ipath.empty()) {
groksipath = false;
}
}
bool istempfile = false;
LOGDEB(("RclMain::startNV: groksipath %d wantsf %d wantsparentf %d\n",
groksipath, wantsfile, wantsparentfile));
// If the command wants a file but this is not a file url, or
// there is an ipath that it won't understand, we need a temp file:
theconfig->setKeyDir(path_getfather(fn));
if ((wantsfile && fn.empty()) || (!wantsipath && !doc.ipath.empty())) {
if (((wantsfile || wantsparentfile) && fn.empty()) ||
(!groksipath && !doc.ipath.empty())) {
TempFile temp;
if (!FileInterner::idocToFile(temp, string(), theconfig, doc)) {
Rcl::Doc& thedoc = wantsparentfile ? pdoc : doc;
if (!FileInterner::idocToFile(temp, string(), theconfig, thedoc)) {
QMessageBox::warning(0, "Recoll",
tr("Cannot extract document or create "
"temporary file"));
@ -1647,8 +1684,21 @@ void RclMain::startNativeViewer(Rcl::Doc doc, int pagenum, QString term)
}
}
// Get rid of the command name. lcmd is now argv[1...n]
lcmd.erase(lcmd.begin());
// If we are not called with a page number (which would happen for a call
// from the snippets window), see if we can compute a page number anyway.
if (pagenum == -1) {
pagenum = 1;
string lterm;
if (m_source.isNotNull())
pagenum = m_source->getFirstMatchPage(doc, lterm);
if (pagenum == -1)
pagenum = 1;
else // We get the match term used to compute the page
term = QString::fromUtf8(lterm.c_str());
}
char cpagenum[20];
sprintf(cpagenum, "%d", pagenum);
// Substitute %xx inside arguments
string efftime;
@ -1662,8 +1712,8 @@ void RclMain::startNativeViewer(Rcl::Doc doc, int pagenum, QString term)
map<string, string> subs;
subs["D"] = efftime;
subs["f"] = fn;
subs["F"] = orgfn;
subs["i"] = doc.ipath;
subs["F"] = fn;
subs["i"] = FileInterner::getLastIpathElt(doc.ipath);
subs["M"] = doc.mimetype;
subs["p"] = cpagenum;
subs["s"] = (const char*)term.toLocal8Bit();
@ -1705,7 +1755,7 @@ void RclMain::startNativeViewer(Rcl::Doc doc, int pagenum, QString term)
// We keep pushing back and never deleting. This can't be good...
ExecCmd *ecmd = new ExecCmd;
m_viewers.push_back(ecmd);
ecmd->startExec(cmdpath, lcmd, false, false);
ecmd->startExec(execpath, lcmd, false, false);
}
void RclMain::startManual()

View File

@ -1,7 +1,8 @@
1 results
text/html [file:///home/dockes/projets/fulltext/testrecoll/chm/Nokia_Nseries_Help_jpn.chm] [デジタル著作権管理(DRM)] 2948 bytes
1 results
2 results
text/html [file:///home/dockes/projets/fulltext/testrecoll/chm/soundrec.chm] [Superposer (mixer) des fichiers son] 2279 bytes
text/html [file:///home/dockes/projets/fulltext/testrecoll/chm/zippedsoundrec.zip] [Superposer (mixer) des fichiers son] 2279 bytes
1 results
text/html [file:///home/dockes/projets/fulltext/testrecoll/chm/Django-1.1a1-r9905.chm] [User authentication in Django] 115179 bytes
1 results