GUI: create separate popup menu entries for open parent and open folder

This commit is contained in:
Jean-Francois Dockes 2020-07-16 10:25:26 +02:00
parent b4306b71c0
commit 3948f9bd33
9 changed files with 160 additions and 132 deletions

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2005 J.F.Dockes
/* Copyright (C) 2005-2020 J.F.Dockes
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
@ -545,19 +545,18 @@ pair<int,int> ResList::parnumfromdocnum(int docnum)
LOGDEB("parnumfromdocnum: docnum " << docnum << "\n");
if (m_pager->pageNumber() < 0) {
LOGDEB("parnumfromdocnum: no page return -1,-1\n");
return pair<int,int>(-1,-1);
return {-1, -1};
}
int winfirst = pageFirstDocNum();
if (docnum - winfirst < 0) {
LOGDEB("parnumfromdocnum: docnum " << docnum << " < winfirst " <<
winfirst << " return -1,-1\n");
return pair<int,int>(-1,-1);
return {-1, -1};
}
docnum -= winfirst;
for (std::map<int,int>::iterator it = m_pageParaToReldocnums.begin();
it != m_pageParaToReldocnums.end(); it++) {
if (docnum == it->second) {
int first = it->first;
for (const auto& entry : m_pageParaToReldocnums.begin()) {
if (docnum == entry.second) {
int first = entry.first;
int last = first+1;
std::map<int,int>::iterator it1;
while ((it1 = m_pageParaToReldocnums.find(last)) !=
@ -565,11 +564,11 @@ pair<int,int> ResList::parnumfromdocnum(int docnum)
last++;
}
LOGDEB("parnumfromdocnum: return " << first << "," << last << "\n");
return pair<int,int>(first, last);
return {first, last};
}
}
LOGDEB("parnumfromdocnum: not found return -1,-1\n");
return pair<int,int>(-1,-1);
return {-1,-1};
}
#endif // TEXTBROWSER
@ -1180,8 +1179,23 @@ void ResList::menuPreviewParent()
void ResList::menuOpenParent()
{
Rcl::Doc doc;
if (getDoc(m_popDoc, doc) && m_source)
emit editRequested(ResultPopup::getParent(m_source, doc));
if (getDoc(m_popDoc, doc) && m_source) {
Rcl::Doc pdoc = ResultPopup::getParent(m_source, doc);
if (!pdoc.url.empty()) {
emit editRequested(pdoc);
}
}
}
void ResList::menuOpenFolder()
{
Rcl::Doc doc;
if (getDoc(m_popDoc, doc) && m_source) {
Rcl::Doc pdoc = ResultPopup::getFolder(m_source, doc);
if (!pdoc.url.empty()) {
emit editRequested(pdoc);
}
}
}
void ResList::menuShowSnippets()

View File

@ -85,6 +85,7 @@ public slots:
virtual void menuExpand();
virtual void menuPreviewParent();
virtual void menuOpenParent();
virtual void menuOpenFolder();
virtual void menuShowSnippets();
virtual void menuShowSubDocs();
virtual void previewExposed(int);

View File

@ -1,4 +1,5 @@
/*
/* Copyright (C) 2005-2020 J.F.Dockes
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
@ -43,34 +44,25 @@ QMenu *create(QWidget *me, int opts, std::shared_ptr<DocSequence> source,
string apptag;
doc.getmeta(Rcl::Doc::keyapptg, &apptag);
// Is this a top level file system file (accessible by regular utilities)?
bool isFsTop = doc.ipath.empty() && doc.isFsFile();
popup->addAction(QWidget::tr("&Preview"), me, SLOT(menuPreview()));
if (!theconfig->getMimeViewerDef(doc.mimetype, apptag, 0).empty()) {
popup->addAction(QWidget::tr("&Open"), me, SLOT(menuEdit()));
}
bool needopenwith = true;
if (!doc.ipath.empty())
needopenwith = false;
if (needopenwith) {
string backend;
doc.getmeta(Rcl::Doc::keybcknd, &backend);
if (!backend.empty() && backend.compare("FS"))
needopenwith = false;
}
if (needopenwith) {
vector<DesktopDb::AppDef> aps;
if (isFsTop) {
// Openable by regular program. Add "open with" entry.
vector<DesktopDb::AppDef> apps;
DesktopDb *ddb = DesktopDb::getDb();
if (ddb && ddb->appForMime(doc.mimetype, &aps) &&
!aps.empty()) {
if (ddb && ddb->appForMime(doc.mimetype, &apps) && !apps.empty()) {
QMenu *sub = popup->addMenu(QWidget::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()));
for (const auto& app : apps) {
QAction *act = new QAction(u8s2qs(app.name), me);
QVariant v(u8s2qs(app.command));
act->setData(v);
sub->addAction(act);
}
@ -81,16 +73,14 @@ QMenu *create(QWidget *me, int opts, std::shared_ptr<DocSequence> source,
// See if there are any desktop files in $RECOLL_CONFDIR/scripts
// and possibly create a "run script" menu.
aps.clear();
apps.clear();
ddb = new DesktopDb(path_cat(theconfig->getConfDir(), "scripts"));
if (ddb && ddb->allApps(&aps) && !aps.empty()) {
if (ddb && ddb->allApps(&apps) && !apps.empty()) {
QMenu *sub = popup->addMenu(QWidget::tr("Run Script"));
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()));
for (const auto& app : apps) {
QAction *act = new QAction(u8s2qs(app.name), me);
QVariant v(u8s2qs(app.command));
act->setData(v);
sub->addAction(act);
}
@ -101,10 +91,12 @@ QMenu *create(QWidget *me, int opts, std::shared_ptr<DocSequence> source,
delete ddb;
}
popup->addAction(QWidget::tr("Copy &File Name"), me, SLOT(menuCopyFN()));
if (doc.isFsFile()) {
popup->addAction(QWidget::tr("Copy &File Name"), me, SLOT(menuCopyFN()));
}
popup->addAction(QWidget::tr("Copy &URL"), me, SLOT(menuCopyURL()));
if ((opts&showSaveOne) && (!doc.isFsFile() || !doc.ipath.empty()))
if ((opts&showSaveOne) && !(isFsTop))
popup->addAction(QWidget::tr("&Write to File"), me,
SLOT(menuSaveToFile()));
@ -112,16 +104,21 @@ QMenu *create(QWidget *me, int opts, std::shared_ptr<DocSequence> source,
popup->addAction(QWidget::tr("Save selection to files"),
me, SLOT(menuSaveSelection()));
// We now separate preview/open parent, which only makes sense for
// an embedded doc, and open folder (which was previously done if
// the doc was a top level file and was not accessible else).
Rcl::Doc pdoc;
if (source && source->getEnclosing(doc, pdoc)) {
bool isEnclosed = source && source->getEnclosing(doc, pdoc);
if (isEnclosed) {
popup->addAction(QWidget::tr("Preview P&arent document/folder"),
me, SLOT(menuPreviewParent()));
}
// Open parent is useful even if there is no parent because we open
// the enclosing folder.
if (doc.isFsFile())
popup->addAction(QWidget::tr("&Open Parent document/folder"),
popup->addAction(QWidget::tr("&Open Parent document"),
me, SLOT(menuOpenParent()));
}
if (doc.isFsFile())
popup->addAction(QWidget::tr("&Open Parent Folder"),
me, SLOT(menuOpenFolder()));
if (opts & showExpand)
popup->addAction(QWidget::tr("Find &similar documents"),
@ -141,17 +138,22 @@ QMenu *create(QWidget *me, int opts, std::shared_ptr<DocSequence> source,
Rcl::Doc getParent(std::shared_ptr<DocSequence> source, Rcl::Doc& doc)
{
Rcl::Doc pdoc;
if (!source || !source->getEnclosing(doc, pdoc)) {
// No parent doc: show enclosing folder with app configured for
// directories
pdoc.url = url_parentfolder(doc.url);
pdoc.meta[Rcl::Doc::keychildurl] = doc.url;
pdoc.meta[Rcl::Doc::keyapptg] = "parentopen";
pdoc.mimetype = "inode/directory";
if (source) {
source->getEnclosing(doc, pdoc);
}
return pdoc;
}
Rcl::Doc getFolder(std::shared_ptr<DocSequence>, Rcl::Doc& doc)
{
Rcl::Doc pdoc;
pdoc.url = url_parentfolder(doc.url);
pdoc.meta[Rcl::Doc::keychildurl] = doc.url;
pdoc.meta[Rcl::Doc::keyapptg] = "parentopen";
pdoc.mimetype = "inode/directory";
return pdoc;
}
void copyFN(const Rcl::Doc &doc)
{
// Our urls currently always begin with "file://"

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2006 J.F.Dockes
/* Copyright (C) 2006-2020 J.F.Dockes
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
@ -19,15 +19,17 @@
#include "autoconfig.h"
namespace ResultPopup {
enum Options {showExpand = 0x1, showSubs = 0x2, isMain = 0x3,
showSaveOne = 0x4, showSaveSel = 0x8};
extern QMenu *create(QWidget *me, int opts,
std::shared_ptr<DocSequence> source,
Rcl::Doc& doc);
extern Rcl::Doc getParent(std::shared_ptr<DocSequence> source,
Rcl::Doc& doc);
extern void copyFN(const Rcl::Doc &doc);
extern void copyURL(const Rcl::Doc &doc);
enum Options {showExpand = 0x1, showSubs = 0x2, isMain = 0x3,
showSaveOne = 0x4, showSaveSel = 0x8};
extern QMenu *create(QWidget *me, int opts,
std::shared_ptr<DocSequence> source,
Rcl::Doc& doc);
extern Rcl::Doc getParent(std::shared_ptr<DocSequence> source,
Rcl::Doc& doc);
extern Rcl::Doc getFolder(std::shared_ptr<DocSequence> source,
Rcl::Doc& doc);
extern void copyFN(const Rcl::Doc &doc);
extern void copyURL(const Rcl::Doc &doc);
};
#endif /* _RESPOPUP_H_INCLUDED_ */

View File

@ -1,4 +1,4 @@
/*
/* Copyright (C) 2005-2020 J.F.Dockes
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
@ -247,11 +247,10 @@ RecollModel::RecollModel(const QStringList fields, ResTable *tb,
// could be protected to be done only once, but it's no real
// problem
if (theconfig) {
const set<string>& stored = theconfig->getStoredFields();
for (set<string>::const_iterator it = stored.begin();
it != stored.end(); it++) {
if (o_displayableFields.find(*it) == o_displayableFields.end()) {
o_displayableFields[*it] = QString::fromUtf8(it->c_str());
const auto& stored = theconfig->getStoredFields();
for (const auto& field : stored) {
if (o_displayableFields.find(field) == o_displayableFields.end()) {
o_displayableFields[field] = u8s2qs(field);
}
}
}
@ -1003,9 +1002,24 @@ void ResTable::menuPreviewParent()
void ResTable::menuOpenParent()
{
if (m_detaildocnum >= 0 && m_model && m_model->getDocSource())
emit editRequested(
ResultPopup::getParent(m_model->getDocSource(), m_detaildoc));
if (m_detaildocnum >= 0 && m_model && m_model->getDocSource()) {
Rcl::Doc pdoc =
ResultPopup::getParent(m_model->getDocSource(), m_detaildoc);
if (!pdoc.url.empty()) {
emit editRequested(pdoc);
}
}
}
void ResTable::menuOpenFolder()
{
if (m_detaildocnum >= 0 && m_model && m_model->getDocSource()) {
Rcl::Doc pdoc =
ResultPopup::getFolder(m_model->getDocSource(), m_detaildoc);
if (!pdoc.url.empty()) {
emit editRequested(pdoc);
}
}
}
void ResTable::menuEdit()
@ -1085,12 +1099,11 @@ void ResTable::createHeaderPopupMenu(const QPoint& pos)
popup->addSeparator();
QAction *act;
for (map<string, QString>::const_iterator it = allfields.begin();
it != allfields.end(); it++) {
if (std::find(fields.begin(), fields.end(), it->first) != fields.end())
for (const auto& field : allfields) {
if (std::find(fields.begin(), fields.end(), field.first) != fields.end())
continue;
act = new QAction(tr("Add \"%1\" column").arg(it->second), popup);
act->setData(QString::fromUtf8(it->first.c_str()));
act = new QAction(tr("Add \"%1\" column").arg(field.second), popup);
act->setData(u8s2qs(field.first));
connect(act, SIGNAL(triggered(bool)), this , SLOT(addColumn()));
popup->addAction(act);
}

View File

@ -148,6 +148,7 @@ public slots:
virtual void menuExpand();
virtual void menuPreviewParent();
virtual void menuOpenParent();
virtual void menuOpenFolder();
virtual void menuShowSnippets();
virtual void menuShowSubDocs();
virtual void createHeaderPopupMenu(const QPoint&);

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2005 J.F.Dockes
/* Copyright (C) 2005-2020 J.F.Dockes
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
@ -30,11 +30,11 @@ int DocSequence::getSeqSlice(int offs, int cnt, vector<ResListEntry>& result)
{
int ret = 0;
for (int num = offs; num < offs + cnt; num++, ret++) {
result.push_back(ResListEntry());
if (!getDoc(num, result.back().doc, &result.back().subHeader)) {
result.pop_back();
return ret;
}
result.push_back(ResListEntry());
if (!getDoc(num, result.back().doc, &result.back().subHeader)) {
result.pop_back();
return ret;
}
}
return ret;
}
@ -43,8 +43,8 @@ bool DocSequence::getEnclosing(Rcl::Doc& doc, Rcl::Doc& pdoc)
{
std::shared_ptr<Rcl::Db> db = getDb();
if (!db) {
LOGERR("DocSequence::getEnclosing: no db\n" );
return false;
LOGERR("DocSequence::getEnclosing: no db\n" );
return false;
}
std::unique_lock<std::mutex> locker(o_dblock);
string udi;
@ -59,9 +59,9 @@ bool DocSequence::getEnclosing(Rcl::Doc& doc, Rcl::Doc& pdoc)
void DocSource::stripStack()
{
if (!m_seq)
return;
return;
while (m_seq->getSourceSeq()) {
m_seq = m_seq->getSourceSeq();
m_seq = m_seq->getSourceSeq();
}
}
@ -71,29 +71,29 @@ bool DocSource::buildStack()
stripStack();
if (!m_seq)
return false;
return false;
// Filtering must be done before sorting, (which may
// truncates the original list)
if (m_seq->canFilter()) {
if (!m_seq->setFiltSpec(m_fspec)) {
LOGERR("DocSource::buildStack: setfiltspec failed\n" );
}
if (!m_seq->setFiltSpec(m_fspec)) {
LOGERR("DocSource::buildStack: setfiltspec failed\n" );
}
} else {
if (m_fspec.isNotNull()) {
m_seq =
std::shared_ptr<DocSequence>(new DocSeqFiltered(m_config, m_seq, m_fspec));
}
if (m_fspec.isNotNull()) {
m_seq =
std::shared_ptr<DocSequence>(new DocSeqFiltered(m_config, m_seq, m_fspec));
}
}
if (m_seq->canSort()) {
if (!m_seq->setSortSpec(m_sspec)) {
LOGERR("DocSource::buildStack: setsortspec failed\n" );
}
if (!m_seq->setSortSpec(m_sspec)) {
LOGERR("DocSource::buildStack: setsortspec failed\n" );
}
} else {
if (m_sspec.isNotNull()) {
m_seq = std::shared_ptr<DocSequence>(new DocSeqSorted(m_seq, m_sspec));
}
if (m_sspec.isNotNull()) {
m_seq = std::shared_ptr<DocSequence>(new DocSeqSorted(m_seq, m_sspec));
}
}
return true;
}
@ -101,14 +101,14 @@ bool DocSource::buildStack()
string DocSource::title()
{
if (!m_seq)
return string();
return string();
string qual;
if (m_fspec.isNotNull() && !m_sspec.isNotNull())
qual = string(" (") + o_filt_trans + string(")");
qual = string(" (") + o_filt_trans + string(")");
else if (!m_fspec.isNotNull() && m_sspec.isNotNull())
qual = string(" (") + o_sort_trans + string(")");
qual = string(" (") + o_sort_trans + string(")");
else if (m_fspec.isNotNull() && m_sspec.isNotNull())
qual = string(" (") + o_sort_trans + string(",") + o_filt_trans + string(")");
qual = string(" (") + o_sort_trans + string(",") + o_filt_trans + string(")");
return m_seq->title() + qual;
}
@ -127,5 +127,3 @@ bool DocSource::setSortSpec(const DocSeqSortSpec &s)
buildStack();
return true;
}

View File

@ -114,39 +114,37 @@ public:
return false;
}
/** For an embedded document: get the immediately enclosing doc
* (e.g., for an attachment, the message it is attached to. Only
* makes sense is ipath is not empty. */
virtual bool getEnclosing(Rcl::Doc&, Rcl::Doc&);
/** Get estimated total count in results */
virtual int getResCnt() = 0;
/** Get title for result list */
virtual std::string title()
{
return m_title;
}
virtual std::string title() {
return m_title;
}
/** Can do snippets ? */
virtual bool snippetsCapable()
{
return false;
}
virtual bool snippetsCapable() {
return false;
}
/** Get description for underlying query */
virtual std::string getDescription() = 0;
/** Get search terms (for highlighting abstracts). Some sequences
* may have no associated search terms. Implement this for them. */
virtual void getTerms(HighlightData& hld)
{
hld.clear();
}
virtual std::list<std::string> expand(Rcl::Doc &)
{
return std::list<std::string>();
}
virtual std::string getReason()
{
return m_reason;
}
virtual void getTerms(HighlightData& hld) {
hld.clear();
}
virtual std::list<std::string> expand(Rcl::Doc &) {
return std::list<std::string>();
}
virtual std::string getReason() {
return m_reason;
}
/** Optional functionality. */
virtual bool canFilter() {return false;}
virtual bool canSort() {return false;}

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2006 J.F.Dockes
/* Copyright (C) 2006-2020 J.F.Dockes
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
@ -205,9 +205,8 @@ public:
return true;
}
/* Is this document stored as a regular filesystem file ?
* (as opposed to e.g. a webcache file), not a subdoc,
*/
/* Is this document stored in a regular filesystem file ?
* (as opposed to e.g. a webcache file). */
bool isFsFile() {
std::string backend;
getmeta(keybcknd, &backend);