GUI: add "Open With" entry in reslist/table popups to let the user choose the app
This commit is contained in:
parent
b0b372e350
commit
c897a09cd3
@ -301,6 +301,8 @@ void RclMain::init()
|
|||||||
this, SLOT(startPreview(Rcl::Doc)));
|
this, SLOT(startPreview(Rcl::Doc)));
|
||||||
connect(restable, SIGNAL(editRequested(Rcl::Doc)),
|
connect(restable, SIGNAL(editRequested(Rcl::Doc)),
|
||||||
this, SLOT(startNativeViewer(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)),
|
connect(restable, SIGNAL(docSaveToFileClicked(Rcl::Doc)),
|
||||||
this, SLOT(saveDocToFile(Rcl::Doc)));
|
this, SLOT(saveDocToFile(Rcl::Doc)));
|
||||||
connect(restable, SIGNAL(showSnippets(Rcl::Doc)),
|
connect(restable, SIGNAL(showSnippets(Rcl::Doc)),
|
||||||
@ -341,6 +343,8 @@ void RclMain::init()
|
|||||||
this, SLOT(saveDocToFile(Rcl::Doc)));
|
this, SLOT(saveDocToFile(Rcl::Doc)));
|
||||||
connect(reslist, SIGNAL(editRequested(Rcl::Doc)),
|
connect(reslist, SIGNAL(editRequested(Rcl::Doc)),
|
||||||
this, SLOT(startNativeViewer(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)),
|
connect(reslist, SIGNAL(docPreviewClicked(int, Rcl::Doc, int)),
|
||||||
this, SLOT(startPreview(int, Rcl::Doc, int)));
|
this, SLOT(startPreview(int, Rcl::Doc, int)));
|
||||||
connect(reslist, SIGNAL(previewRequested(Rcl::Doc)),
|
connect(reslist, SIGNAL(previewRequested(Rcl::Doc)),
|
||||||
@ -1641,6 +1645,39 @@ void RclMain::showSubDocs(Rcl::Doc doc)
|
|||||||
res->show();
|
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)
|
void RclMain::startNativeViewer(Rcl::Doc doc, int pagenum, QString term)
|
||||||
{
|
{
|
||||||
string apptag;
|
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:
|
// Command not found: start the user dialog to help find another one:
|
||||||
if (execpath.empty()) {
|
if (execpath.empty()) {
|
||||||
QString mt = QString::fromAscii(doc.mimetype.c_str());
|
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]
|
// Get rid of the command name. lcmd is now argv[1...n]
|
||||||
lcmd.erase(lcmd.begin());
|
lcmd.erase(lcmd.begin());
|
||||||
|
|
||||||
|
|
||||||
// Process the command arguments to determine if we need to create
|
// Process the command arguments to determine if we need to create
|
||||||
// a temporary file.
|
// a temporary file.
|
||||||
|
|
||||||
@ -1868,12 +1903,21 @@ void RclMain::startNativeViewer(Rcl::Doc doc, int pagenum, QString term)
|
|||||||
it != doc.meta.end(); it++) {
|
it != doc.meta.end(); it++) {
|
||||||
subs[it->first] = it->second;
|
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;
|
string ncmd;
|
||||||
for (vector<string>::iterator it = lcmd.begin();
|
vector<string> lcmd;
|
||||||
it != lcmd.end(); it++) {
|
for (vector<string>::const_iterator it = _lcmd.begin();
|
||||||
|
it != _lcmd.end(); it++) {
|
||||||
pcSubst(*it, ncmd, subs);
|
pcSubst(*it, ncmd, subs);
|
||||||
LOGDEB(("%s->%s\n", it->c_str(), ncmd.c_str()));
|
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
|
// Also substitute inside the unsplitted command line and display
|
||||||
|
|||||||
@ -133,6 +133,7 @@ public slots:
|
|||||||
virtual void startPreview(Rcl::Doc);
|
virtual void startPreview(Rcl::Doc);
|
||||||
virtual void startNativeViewer(Rcl::Doc, int pagenum = -1,
|
virtual void startNativeViewer(Rcl::Doc, int pagenum = -1,
|
||||||
QString term=QString());
|
QString term=QString());
|
||||||
|
virtual void openWith(Rcl::Doc, string);
|
||||||
virtual void saveDocToFile(Rcl::Doc);
|
virtual void saveDocToFile(Rcl::Doc);
|
||||||
virtual void previewNextInTab(Preview *, int sid, int docnum);
|
virtual void previewNextInTab(Preview *, int sid, int docnum);
|
||||||
virtual void previewPrevInTab(Preview *, int sid, int docnum);
|
virtual void previewPrevInTab(Preview *, int sid, int docnum);
|
||||||
@ -197,6 +198,9 @@ private:
|
|||||||
virtual void init();
|
virtual void init();
|
||||||
virtual void previewPrevOrNextInTab(Preview *, int sid, int docnum,
|
virtual void previewPrevOrNextInTab(Preview *, int sid, int docnum,
|
||||||
bool next);
|
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 setStemLang(const QString& lang);
|
||||||
virtual void onSortCtlChanged();
|
virtual void onSortCtlChanged();
|
||||||
virtual void showIndexConfig(bool modal);
|
virtual void showIndexConfig(bool modal);
|
||||||
|
|||||||
@ -1027,6 +1027,15 @@ void ResList::menuEdit()
|
|||||||
if (getDoc(m_popDoc, doc))
|
if (getDoc(m_popDoc, doc))
|
||||||
emit editRequested(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()
|
void ResList::menuCopyFN()
|
||||||
{
|
{
|
||||||
|
|||||||
@ -81,6 +81,7 @@ class ResList : public RESLIST_PARENTCLASS
|
|||||||
virtual void menuPreview();
|
virtual void menuPreview();
|
||||||
virtual void menuSaveToFile();
|
virtual void menuSaveToFile();
|
||||||
virtual void menuEdit();
|
virtual void menuEdit();
|
||||||
|
virtual void menuOpenWith(QAction *);
|
||||||
virtual void menuCopyFN();
|
virtual void menuCopyFN();
|
||||||
virtual void menuCopyURL();
|
virtual void menuCopyURL();
|
||||||
virtual void menuExpand();
|
virtual void menuExpand();
|
||||||
@ -104,6 +105,7 @@ class ResList : public RESLIST_PARENTCLASS
|
|||||||
void showSnippets(Rcl::Doc);
|
void showSnippets(Rcl::Doc);
|
||||||
void showSubDocs(Rcl::Doc);
|
void showSubDocs(Rcl::Doc);
|
||||||
void editRequested(Rcl::Doc);
|
void editRequested(Rcl::Doc);
|
||||||
|
void openWithRequested(Rcl::Doc, string cmd);
|
||||||
void docExpand(Rcl::Doc);
|
void docExpand(Rcl::Doc);
|
||||||
void wordSelect(QString);
|
void wordSelect(QString);
|
||||||
void wordReplace(const QString&, const QString&);
|
void wordReplace(const QString&, const QString&);
|
||||||
|
|||||||
@ -25,6 +25,7 @@
|
|||||||
#include "recoll.h"
|
#include "recoll.h"
|
||||||
#include "docseq.h"
|
#include "docseq.h"
|
||||||
#include "respopup.h"
|
#include "respopup.h"
|
||||||
|
#include "appformime.h"
|
||||||
|
|
||||||
namespace ResultPopup {
|
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()));
|
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 &File Name"), me, SLOT(menuCopyFN()));
|
||||||
popup->addAction(me->tr("Copy &URL"), me, SLOT(menuCopyURL()));
|
popup->addAction(me->tr("Copy &URL"), me, SLOT(menuCopyURL()));
|
||||||
|
|
||||||
|
|||||||
@ -880,6 +880,14 @@ void ResTable::menuEdit()
|
|||||||
if (m_detaildocnum >= 0)
|
if (m_detaildocnum >= 0)
|
||||||
emit editRequested(m_detaildoc);
|
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()
|
void ResTable::menuCopyFN()
|
||||||
{
|
{
|
||||||
|
|||||||
@ -137,6 +137,7 @@ public slots:
|
|||||||
virtual void menuSaveToFile();
|
virtual void menuSaveToFile();
|
||||||
virtual void menuSaveSelection();
|
virtual void menuSaveSelection();
|
||||||
virtual void menuEdit();
|
virtual void menuEdit();
|
||||||
|
virtual void menuOpenWith(QAction *);
|
||||||
virtual void menuCopyFN();
|
virtual void menuCopyFN();
|
||||||
virtual void menuCopyURL();
|
virtual void menuCopyURL();
|
||||||
virtual void menuExpand();
|
virtual void menuExpand();
|
||||||
@ -157,6 +158,7 @@ signals:
|
|||||||
void docSaveToFileClicked(Rcl::Doc);
|
void docSaveToFileClicked(Rcl::Doc);
|
||||||
void previewRequested(Rcl::Doc);
|
void previewRequested(Rcl::Doc);
|
||||||
void editRequested(Rcl::Doc);
|
void editRequested(Rcl::Doc);
|
||||||
|
void openWithRequested(Rcl::Doc, string cmd);
|
||||||
void headerClicked();
|
void headerClicked();
|
||||||
void docExpand(Rcl::Doc);
|
void docExpand(Rcl::Doc);
|
||||||
void showSubDocs(Rcl::Doc);
|
void showSubDocs(Rcl::Doc);
|
||||||
|
|||||||
@ -24,6 +24,7 @@
|
|||||||
* code which would parse /usr/share/applications to return a list of
|
* 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
|
* 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).
|
* 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 {
|
class DesktopDb {
|
||||||
public:
|
public:
|
||||||
@ -49,7 +50,7 @@ public:
|
|||||||
* problem was detected.
|
* problem was detected.
|
||||||
*/
|
*/
|
||||||
bool appForMime(const std::string& mime, vector<AppDef> *apps,
|
bool appForMime(const std::string& mime, vector<AppDef> *apps,
|
||||||
std::string *reason);
|
std::string *reason = 0);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DesktopDb();
|
DesktopDb();
|
||||||
|
|||||||
@ -534,7 +534,7 @@ string escapeShell(const string &in)
|
|||||||
|
|
||||||
|
|
||||||
// Substitute printf-like percent cmds inside a string
|
// 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;
|
string::const_iterator it;
|
||||||
for (it = in.begin(); it != in.end();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 += '%';
|
out += '%';
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
map<char,string>::iterator tr;
|
map<char,string>::const_iterator tr;
|
||||||
if ((tr = subs.find(*it)) != subs.end()) {
|
if ((tr = subs.find(*it)) != subs.end()) {
|
||||||
out += tr->second;
|
out += tr->second;
|
||||||
} else {
|
} else {
|
||||||
@ -561,7 +561,7 @@ bool pcSubst(const string& in, string& out, map<char, string>& subs)
|
|||||||
return true;
|
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();
|
out.erase();
|
||||||
string::size_type i;
|
string::size_type i;
|
||||||
@ -592,7 +592,7 @@ bool pcSubst(const string& in, string& out, map<string, string>& subs)
|
|||||||
} else {
|
} else {
|
||||||
key = in[i];
|
key = in[i];
|
||||||
}
|
}
|
||||||
map<string,string>::iterator tr;
|
map<string,string>::const_iterator tr;
|
||||||
if ((tr = subs.find(key)) != subs.end()) {
|
if ((tr = subs.find(key)) != subs.end()) {
|
||||||
out += tr->second;
|
out += tr->second;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -143,9 +143,9 @@ string displayableBytes(off_t size);
|
|||||||
string breakIntoLines(const string& in, unsigned int ll = 100,
|
string breakIntoLines(const string& in, unsigned int ll = 100,
|
||||||
unsigned int maxlines= 50);
|
unsigned int maxlines= 50);
|
||||||
/** Small utility to substitute printf-like percents cmds in a string */
|
/** 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) */
|
/** 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 */
|
/** Append system error message */
|
||||||
void catstrerror(string *reason, const char *what, int _errno);
|
void catstrerror(string *reason, const char *what, int _errno);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user