GUI: add "Open With" entry in reslist/table popups to let the user choose the app

This commit is contained in:
Jean-Francois Dockes 2014-05-01 09:28:46 +02:00
parent b0b372e350
commit c897a09cd3
10 changed files with 104 additions and 12 deletions

View File

@ -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<string> 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<string, string> 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<string, string>& subs, bool istempfile,
const string& execpath,
const vector<string>& _lcmd, const string& cmd,
Rcl::Doc doc)
{
string ncmd;
for (vector<string>::iterator it = lcmd.begin();
it != lcmd.end(); it++) {
vector<string> lcmd;
for (vector<string>::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

View File

@ -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<string, string>& subs, bool istempfile,
const string& execpath, const vector<string>& lcmd,
const string& cmd, Rcl::Doc doc);
virtual void setStemLang(const QString& lang);
virtual void onSortCtlChanged();
virtual void showIndexConfig(bool modal);

View File

@ -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()
{

View File

@ -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&);

View File

@ -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<DocSequence> source, Rcl::Doc& doc)
popup->addAction(me->tr("&Open"), me, SLOT(menuEdit()));
}
if (doc.ipath.empty()) {
vector<DesktopDb::AppDef> 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<DesktopDb::AppDef>::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()));

View File

@ -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()
{

View File

@ -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);

View File

@ -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<AppDef> *apps,
std::string *reason);
std::string *reason = 0);
private:
DesktopDb();

View File

@ -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<char, string>& subs)
bool pcSubst(const string& in, string& out, const map<char, string>& 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<char, string>& subs)
out += '%';
continue;
}
map<char,string>::iterator tr;
map<char,string>::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<char, string>& subs)
return true;
}
bool pcSubst(const string& in, string& out, map<string, string>& subs)
bool pcSubst(const string& in, string& out, const map<string, string>& subs)
{
out.erase();
string::size_type i;
@ -592,7 +592,7 @@ bool pcSubst(const string& in, string& out, map<string, string>& subs)
} else {
key = in[i];
}
map<string,string>::iterator tr;
map<string,string>::const_iterator tr;
if ((tr = subs.find(key)) != subs.end()) {
out += tr->second;
} else {

View File

@ -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<char, string>& subs);
bool pcSubst(const string& in, string& out, const map<char, string>& subs);
/** Substitute printf-like percents and also %(key) */
bool pcSubst(const string& in, string& out, map<string, string>& subs);
bool pcSubst(const string& in, string& out, const map<string, string>& subs);
/** Append system error message */
void catstrerror(string *reason, const char *what, int _errno);