From 5add2e23847aee74b1585771b083bbc06310f848 Mon Sep 17 00:00:00 2001 From: Jean-Francois Dockes Date: Fri, 12 Oct 2012 16:54:52 +0200 Subject: [PATCH] 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 --- src/doc/user/usermanual.sgml | 28 ++++++- src/filters/rclchm | 15 +++- src/filters/rclepub | 13 +++- src/filters/rclics | 13 +++- src/filters/rclinfo | 13 +++- src/filters/rclrar | 13 +++- src/filters/rcltar | 13 +++- src/filters/rclwar | 7 +- src/filters/rclzip | 12 ++- src/internfile/internfile.cpp | 10 +++ src/internfile/internfile.h | 3 + src/qtgui/rclmain_w.cpp | 138 +++++++++++++++++++++++----------- tests/chm/chm.txt | 3 +- 13 files changed, 222 insertions(+), 59 deletions(-) diff --git a/src/doc/user/usermanual.sgml b/src/doc/user/usermanual.sgml index 88fffbde..93dcde7d 100644 --- a/src/doc/user/usermanual.sgml +++ b/src/doc/user/usermanual.sgml @@ -215,7 +215,7 @@ Python programming interface, a KDE KIO slave module, and - a Ubuntu Unity Lens module. + a Ubuntu Unity Lens module. @@ -2825,8 +2825,29 @@ dir:recoll dir:src -dir:utils -dir:common Desktop integration Being independant of the desktop type has its drawbacks: &RCL; - desktop integration is minimal. Here follow a few things that may - help. + desktop integration is minimal. However there are a few tools + available: + + + The KDE KIO Slave was + described in a previous + section. + + + If you use a recent version of Ubuntu Linux, you may + find the Ubuntu Unity + Lens module useful. + + + There is also an independantly developed + + Krunner plugin. + + + + Here follow a few other things that may help. Hotkeying recoll @@ -2844,6 +2865,7 @@ dir:recoll dir:src -dir:utils -dir:common The KDE Kicker Recoll applet + This is probably obsolete now. Anyway: The &RCL; source tree contains the source code to the recoll_applet, a small application derived from the find_applet. This can be used to diff --git a/src/filters/rclchm b/src/filters/rclchm index dff3b853..39e415b0 100755 --- a/src/filters/rclchm +++ b/src/filters/rclchm @@ -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: diff --git a/src/filters/rclepub b/src/filters/rclepub index dac4293c..545b3763 100755 --- a/src/filters/rclepub +++ b/src/filters/rclepub @@ -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: diff --git a/src/filters/rclics b/src/filters/rclics index 866b8b33..6ad3f632 100755 --- a/src/filters/rclics +++ b/src/filters/rclics @@ -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) diff --git a/src/filters/rclinfo b/src/filters/rclinfo index 01ef47d9..c6b8a8b1 100755 --- a/src/filters/rclinfo +++ b/src/filters/rclinfo @@ -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) diff --git a/src/filters/rclrar b/src/filters/rclrar index aa10d602..b661f510 100755 --- a/src/filters/rclrar +++ b/src/filters/rclrar @@ -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) diff --git a/src/filters/rcltar b/src/filters/rcltar index 7e285a0f..14f98c0e 100755 --- a/src/filters/rcltar +++ b/src/filters/rcltar @@ -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) diff --git a/src/filters/rclwar b/src/filters/rclwar index f102d5b5..8fe46638 100755 --- a/src/filters/rclwar +++ b/src/filters/rclwar @@ -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") diff --git a/src/filters/rclzip b/src/filters/rclzip index d9ea904f..eebe0cc3 100755 --- a/src/filters/rclzip +++ b/src/filters/rclzip @@ -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) diff --git a/src/internfile/internfile.cpp b/src/internfile/internfile.cpp index cdbf1bae..c3d59698 100644 --- a/src/internfile/internfile.cpp +++ b/src/internfile/internfile.cpp @@ -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, diff --git a/src/internfile/internfile.h b/src/internfile/internfile.h index 9b801b8c..7abf9e95 100644 --- a/src/internfile/internfile.h +++ b/src/internfile/internfile.h @@ -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 */ diff --git a/src/qtgui/rclmain_w.cpp b/src/qtgui/rclmain_w.cpp index 04ee7b24..93c9d772 100644 --- a/src/qtgui/rclmain_w.cpp +++ b/src/qtgui/rclmain_w.cpp @@ -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 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() diff --git a/tests/chm/chm.txt b/tests/chm/chm.txt index 56f97cef..85c55d19 100644 --- a/tests/chm/chm.txt +++ b/tests/chm/chm.txt @@ -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