diff --git a/src/qtgui/rclmain_w.cpp b/src/qtgui/rclmain_w.cpp index 3909b20c..d3cfce64 100644 --- a/src/qtgui/rclmain_w.cpp +++ b/src/qtgui/rclmain_w.cpp @@ -301,6 +301,8 @@ void RclMain::init() this, SLOT(startPreview(Rcl::Doc))); connect(restable, SIGNAL(editRequested(Rcl::Doc)), this, SLOT(startNativeViewer(Rcl::Doc))); + connect(restable, SIGNAL(openWithRequested(Rcl::Doc, string)), + this, SLOT(openWith(Rcl::Doc, string))); connect(restable, SIGNAL(docSaveToFileClicked(Rcl::Doc)), this, SLOT(saveDocToFile(Rcl::Doc))); connect(restable, SIGNAL(showSnippets(Rcl::Doc)), @@ -341,6 +343,8 @@ void RclMain::init() this, SLOT(saveDocToFile(Rcl::Doc))); connect(reslist, SIGNAL(editRequested(Rcl::Doc)), this, SLOT(startNativeViewer(Rcl::Doc))); + connect(reslist, SIGNAL(openWithRequested(Rcl::Doc, string)), + this, SLOT(openWith(Rcl::Doc, string))); connect(reslist, SIGNAL(docPreviewClicked(int, Rcl::Doc, int)), this, SLOT(startPreview(int, Rcl::Doc, int))); connect(reslist, SIGNAL(previewRequested(Rcl::Doc)), @@ -1641,6 +1645,39 @@ void RclMain::showSubDocs(Rcl::Doc doc) res->show(); } +void RclMain::openWith(Rcl::Doc doc, string cmdspec) +{ + LOGDEB(("RclMain::openWith: %s\n", cmdspec.c_str())); + + // Split the command line + vector lcmd; + if (!stringToStrings(cmdspec, lcmd)) { + QMessageBox::warning(0, "Recoll", + tr("Bad desktop app spec for %1: [%2]\n" + "Please check the desktop file") + .arg(QString::fromAscii(doc.mimetype.c_str())) + .arg(QString::fromLocal8Bit(cmdspec.c_str()))); + return; + } + + // Look for the command to execute in the exec path and the filters + // directory + string execname = lcmd.front(); + lcmd.erase(lcmd.begin()); + string url = doc.url; + string fn = fileurltolocalpath(doc.url); + + // Try to keep the letters used more or less consistent with the reslist + // paragraph format. + map subs; + subs["F"] = fn; + subs["f"] = fn; + subs["U"] = url; + subs["u"] = url; + + execViewer(subs, false, execname, lcmd, cmdspec, doc); +} + void RclMain::startNativeViewer(Rcl::Doc doc, int pagenum, QString term) { string apptag; @@ -1705,7 +1742,6 @@ void RclMain::startNativeViewer(Rcl::Doc doc, int pagenum, QString term) } } - // Command not found: start the user dialog to help find another one: if (execpath.empty()) { QString mt = QString::fromAscii(doc.mimetype.c_str()); @@ -1731,7 +1767,6 @@ 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()); - // Process the command arguments to determine if we need to create // a temporary file. @@ -1868,12 +1903,21 @@ void RclMain::startNativeViewer(Rcl::Doc doc, int pagenum, QString term) it != doc.meta.end(); it++) { subs[it->first] = it->second; } + execViewer(subs, istempfile, execpath, lcmd, cmd, doc); +} + +void RclMain::execViewer(const map& subs, bool istempfile, + const string& execpath, + const vector& _lcmd, const string& cmd, + Rcl::Doc doc) +{ string ncmd; - for (vector::iterator it = lcmd.begin(); - it != lcmd.end(); it++) { + vector lcmd; + for (vector::const_iterator it = _lcmd.begin(); + it != _lcmd.end(); it++) { pcSubst(*it, ncmd, subs); LOGDEB(("%s->%s\n", it->c_str(), ncmd.c_str())); - *it = ncmd; + lcmd.push_back(ncmd); } // Also substitute inside the unsplitted command line and display diff --git a/src/qtgui/rclmain_w.h b/src/qtgui/rclmain_w.h index 4eda40c9..525e0b00 100644 --- a/src/qtgui/rclmain_w.h +++ b/src/qtgui/rclmain_w.h @@ -133,6 +133,7 @@ public slots: virtual void startPreview(Rcl::Doc); virtual void startNativeViewer(Rcl::Doc, int pagenum = -1, QString term=QString()); + virtual void openWith(Rcl::Doc, string); virtual void saveDocToFile(Rcl::Doc); virtual void previewNextInTab(Preview *, int sid, int docnum); virtual void previewPrevInTab(Preview *, int sid, int docnum); @@ -197,6 +198,9 @@ private: virtual void init(); virtual void previewPrevOrNextInTab(Preview *, int sid, int docnum, bool next); + virtual void execViewer(const map& subs, bool istempfile, + const string& execpath, const vector& lcmd, + const string& cmd, Rcl::Doc doc); virtual void setStemLang(const QString& lang); virtual void onSortCtlChanged(); virtual void showIndexConfig(bool modal); diff --git a/src/qtgui/reslist.cpp b/src/qtgui/reslist.cpp index 3597ea3e..26a43d6d 100644 --- a/src/qtgui/reslist.cpp +++ b/src/qtgui/reslist.cpp @@ -1027,6 +1027,15 @@ void ResList::menuEdit() if (getDoc(m_popDoc, doc)) emit editRequested(doc); } +void ResList::menuOpenWith(QAction *act) +{ + if (act == 0) + return; + string cmd = qs2utf8s(act->data().toString()); + Rcl::Doc doc; + if (getDoc(m_popDoc, doc)) + emit openWithRequested(doc, cmd); +} void ResList::menuCopyFN() { diff --git a/src/qtgui/reslist.h b/src/qtgui/reslist.h index f67943ef..346c54ef 100644 --- a/src/qtgui/reslist.h +++ b/src/qtgui/reslist.h @@ -81,6 +81,7 @@ class ResList : public RESLIST_PARENTCLASS virtual void menuPreview(); virtual void menuSaveToFile(); virtual void menuEdit(); + virtual void menuOpenWith(QAction *); virtual void menuCopyFN(); virtual void menuCopyURL(); virtual void menuExpand(); @@ -104,6 +105,7 @@ class ResList : public RESLIST_PARENTCLASS void showSnippets(Rcl::Doc); void showSubDocs(Rcl::Doc); void editRequested(Rcl::Doc); + void openWithRequested(Rcl::Doc, string cmd); void docExpand(Rcl::Doc); void wordSelect(QString); void wordReplace(const QString&, const QString&); diff --git a/src/qtgui/respopup.cpp b/src/qtgui/respopup.cpp index 49137a67..da5c24cd 100644 --- a/src/qtgui/respopup.cpp +++ b/src/qtgui/respopup.cpp @@ -25,6 +25,7 @@ #include "recoll.h" #include "docseq.h" #include "respopup.h" +#include "appformime.h" namespace ResultPopup { @@ -47,6 +48,27 @@ QMenu *create(QWidget *me, int opts, RefCntr source, Rcl::Doc& doc) popup->addAction(me->tr("&Open"), me, SLOT(menuEdit())); } + if (doc.ipath.empty()) { + vector aps; + DesktopDb *ddb = DesktopDb::getDb(); + if (ddb && ddb->appForMime(doc.mimetype, &aps) && + !aps.empty()) { + QMenu *sub = popup->addMenu(me->tr("Open With")); + if (sub) { + for (vector::const_iterator it = aps.begin(); + it != aps.end(); it++) { + QAction *act = new + QAction(QString::fromUtf8(it->name.c_str()), me); + QVariant v(QString::fromUtf8(it->command.c_str())); + act->setData(v); + sub->addAction(act); + } + sub->connect(sub, SIGNAL(triggered(QAction *)), me, + SLOT(menuOpenWith(QAction *))); + } + } + } + popup->addAction(me->tr("Copy &File Name"), me, SLOT(menuCopyFN())); popup->addAction(me->tr("Copy &URL"), me, SLOT(menuCopyURL())); diff --git a/src/qtgui/restable.cpp b/src/qtgui/restable.cpp index 5c39b266..14320d5f 100644 --- a/src/qtgui/restable.cpp +++ b/src/qtgui/restable.cpp @@ -880,6 +880,14 @@ void ResTable::menuEdit() if (m_detaildocnum >= 0) emit editRequested(m_detaildoc); } +void ResTable::menuOpenWith(QAction *act) +{ + if (act == 0) + return; + string cmd = qs2utf8s(act->data().toString()); + if (m_detaildocnum >= 0) + emit openWithRequested(m_detaildoc, cmd); +} void ResTable::menuCopyFN() { diff --git a/src/qtgui/restable.h b/src/qtgui/restable.h index 685df1dd..cb88237e 100644 --- a/src/qtgui/restable.h +++ b/src/qtgui/restable.h @@ -137,6 +137,7 @@ public slots: virtual void menuSaveToFile(); virtual void menuSaveSelection(); virtual void menuEdit(); + virtual void menuOpenWith(QAction *); virtual void menuCopyFN(); virtual void menuCopyURL(); virtual void menuExpand(); @@ -157,6 +158,7 @@ signals: void docSaveToFileClicked(Rcl::Doc); void previewRequested(Rcl::Doc); void editRequested(Rcl::Doc); + void openWithRequested(Rcl::Doc, string cmd); void headerClicked(); void docExpand(Rcl::Doc); void showSubDocs(Rcl::Doc); diff --git a/src/utils/appformime.h b/src/utils/appformime.h index e6d6339b..5213c0d6 100644 --- a/src/utils/appformime.h +++ b/src/utils/appformime.h @@ -24,6 +24,7 @@ * code which would parse /usr/share/applications to return a list of * apps for a given mime type. So here goes. Note that the implementation * is very primitive for now (no use of cache file, no updating once built). + * Also, this is not thread-safe, but could be made so quite easily. */ class DesktopDb { public: @@ -49,7 +50,7 @@ public: * problem was detected. */ bool appForMime(const std::string& mime, vector *apps, - std::string *reason); + std::string *reason = 0); private: DesktopDb(); diff --git a/src/utils/smallut.cpp b/src/utils/smallut.cpp index 86d7a025..e3bfbb01 100644 --- a/src/utils/smallut.cpp +++ b/src/utils/smallut.cpp @@ -534,7 +534,7 @@ string escapeShell(const string &in) // Substitute printf-like percent cmds inside a string -bool pcSubst(const string& in, string& out, map& subs) +bool pcSubst(const string& in, string& out, const map& subs) { string::const_iterator it; for (it = in.begin(); it != in.end();it++) { @@ -547,7 +547,7 @@ bool pcSubst(const string& in, string& out, map& subs) out += '%'; continue; } - map::iterator tr; + map::const_iterator tr; if ((tr = subs.find(*it)) != subs.end()) { out += tr->second; } else { @@ -561,7 +561,7 @@ bool pcSubst(const string& in, string& out, map& subs) return true; } -bool pcSubst(const string& in, string& out, map& subs) +bool pcSubst(const string& in, string& out, const map& subs) { out.erase(); string::size_type i; @@ -592,7 +592,7 @@ bool pcSubst(const string& in, string& out, map& subs) } else { key = in[i]; } - map::iterator tr; + map::const_iterator tr; if ((tr = subs.find(key)) != subs.end()) { out += tr->second; } else { diff --git a/src/utils/smallut.h b/src/utils/smallut.h index 11a57b27..2b749175 100644 --- a/src/utils/smallut.h +++ b/src/utils/smallut.h @@ -143,9 +143,9 @@ string displayableBytes(off_t size); string breakIntoLines(const string& in, unsigned int ll = 100, unsigned int maxlines= 50); /** Small utility to substitute printf-like percents cmds in a string */ -bool pcSubst(const string& in, string& out, map& subs); +bool pcSubst(const string& in, string& out, const map& subs); /** Substitute printf-like percents and also %(key) */ -bool pcSubst(const string& in, string& out, map& subs); +bool pcSubst(const string& in, string& out, const map& subs); /** Append system error message */ void catstrerror(string *reason, const char *what, int _errno);