2017-02-20 18:37:11 +01:00
|
|
|
#include "./syncthingfileitemaction.h"
|
2018-11-28 20:57:48 +01:00
|
|
|
#include "./syncthingdiractions.h"
|
|
|
|
#include "./syncthinginfoaction.h"
|
|
|
|
#include "./syncthingmenuaction.h"
|
2017-02-20 18:37:11 +01:00
|
|
|
|
2018-11-28 20:57:48 +01:00
|
|
|
#include "../model/syncthingicons.h"
|
2018-05-08 19:19:27 +02:00
|
|
|
|
2017-02-20 18:37:11 +01:00
|
|
|
#include <KFileItem>
|
|
|
|
#include <KPluginFactory>
|
|
|
|
#include <KPluginLoader>
|
|
|
|
|
|
|
|
#include <QAction>
|
|
|
|
|
|
|
|
#include <functional>
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
using namespace Data;
|
|
|
|
|
|
|
|
K_PLUGIN_FACTORY(SyncthingFileItemActionFactory, registerPlugin<SyncthingFileItemAction>();)
|
|
|
|
|
2017-05-01 03:34:43 +02:00
|
|
|
struct SyncthingItem {
|
2017-02-20 18:37:11 +01:00
|
|
|
SyncthingItem(const SyncthingDir *dir, const QString &path);
|
|
|
|
const SyncthingDir *dir;
|
|
|
|
QString path;
|
|
|
|
QString name;
|
|
|
|
};
|
|
|
|
|
2017-05-01 03:34:43 +02:00
|
|
|
SyncthingItem::SyncthingItem(const SyncthingDir *dir, const QString &path)
|
|
|
|
: dir(dir)
|
|
|
|
, path(path)
|
2017-02-20 18:37:11 +01:00
|
|
|
{
|
2018-12-22 16:27:08 +01:00
|
|
|
const auto lastSep = path.lastIndexOf(QChar('/'));
|
|
|
|
if (lastSep >= 0) {
|
2017-02-20 18:37:11 +01:00
|
|
|
name = path.mid(lastSep + 1);
|
|
|
|
} else {
|
|
|
|
name = path;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-27 17:59:17 +01:00
|
|
|
SyncthingFileItemActionStaticData SyncthingFileItemAction::s_data;
|
|
|
|
|
|
|
|
SyncthingFileItemAction::SyncthingFileItemAction(QObject *parent, const QVariantList &)
|
|
|
|
: KAbstractFileItemActionPlugin(parent)
|
|
|
|
{
|
|
|
|
s_data.initialize();
|
|
|
|
}
|
|
|
|
|
|
|
|
QList<QAction *> SyncthingFileItemAction::actions(const KFileItemListProperties &fileItemInfo, QWidget *parentWidget)
|
|
|
|
{
|
2018-12-26 01:18:28 +01:00
|
|
|
Q_UNUSED(parentWidget)
|
2018-12-24 17:19:11 +01:00
|
|
|
|
2018-12-26 01:18:28 +01:00
|
|
|
// create actions
|
|
|
|
const QList<QAction *> subActions = createActions(fileItemInfo, this);
|
|
|
|
QList<QAction *> topLevelActions;
|
|
|
|
|
|
|
|
// don't show anything if no relevant actions could be determined but successfully connected
|
|
|
|
if (s_data.connection().isConnected() && !s_data.hasError() && subActions.isEmpty()) {
|
|
|
|
return topLevelActions;
|
2017-02-20 18:37:11 +01:00
|
|
|
}
|
|
|
|
|
2018-12-26 01:18:28 +01:00
|
|
|
topLevelActions << new SyncthingMenuAction(fileItemInfo, subActions, this);
|
|
|
|
return topLevelActions;
|
2018-11-27 17:59:17 +01:00
|
|
|
}
|
|
|
|
|
2018-12-26 01:18:28 +01:00
|
|
|
QList<QAction *> SyncthingFileItemAction::createActions(const KFileItemListProperties &fileItemInfo, QObject *parent)
|
2018-11-27 17:59:17 +01:00
|
|
|
{
|
|
|
|
QList<QAction *> actions;
|
|
|
|
auto &data = s_data;
|
|
|
|
auto &connection = data.connection();
|
|
|
|
const auto &dirs = connection.dirInfo();
|
|
|
|
|
2017-02-20 18:37:11 +01:00
|
|
|
// get all paths
|
|
|
|
QStringList paths;
|
|
|
|
paths.reserve(fileItemInfo.items().size());
|
2017-05-01 03:34:43 +02:00
|
|
|
for (const KFileItem &item : fileItemInfo.items()) {
|
|
|
|
if (!item.isLocalFile()) {
|
2017-02-20 18:37:11 +01:00
|
|
|
// don't show any actions when remote files are selected
|
2018-11-27 17:59:17 +01:00
|
|
|
return actions;
|
2017-02-20 18:37:11 +01:00
|
|
|
}
|
|
|
|
paths << item.localPath();
|
|
|
|
}
|
|
|
|
|
|
|
|
// determine relevant Syncthing dirs
|
|
|
|
QList<const SyncthingDir *> detectedDirs;
|
|
|
|
QList<const SyncthingDir *> containingDirs;
|
|
|
|
QList<SyncthingItem> detectedItems;
|
2018-11-27 17:59:17 +01:00
|
|
|
const SyncthingDir *lastDir = nullptr;
|
2017-05-01 03:34:43 +02:00
|
|
|
for (const SyncthingDir &dir : dirs) {
|
2017-03-29 23:17:53 +02:00
|
|
|
QStringRef dirPath(dir.pathWithoutTrailingSlash());
|
2017-05-01 03:34:43 +02:00
|
|
|
for (const QString &path : paths) {
|
|
|
|
if (path == dirPath) {
|
2017-02-25 18:35:27 +01:00
|
|
|
lastDir = &dir;
|
2017-05-01 03:34:43 +02:00
|
|
|
if (!detectedDirs.contains(lastDir)) {
|
2017-02-25 18:35:27 +01:00
|
|
|
detectedDirs << lastDir;
|
|
|
|
}
|
2017-05-01 03:34:43 +02:00
|
|
|
} else if (path.startsWith(dir.path)) {
|
2017-02-20 18:37:11 +01:00
|
|
|
detectedItems << SyncthingItem(&dir, path.mid(dir.path.size()));
|
2017-02-25 18:28:20 +01:00
|
|
|
lastDir = &dir;
|
2017-05-01 03:34:43 +02:00
|
|
|
if (!containingDirs.contains(lastDir)) {
|
2017-02-25 18:28:20 +01:00
|
|
|
containingDirs << lastDir;
|
|
|
|
}
|
2017-02-20 18:37:11 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-25 18:28:20 +01:00
|
|
|
// add actions for the selected items itself
|
2018-11-27 17:59:17 +01:00
|
|
|
actions.reserve(32);
|
2017-05-01 03:34:43 +02:00
|
|
|
if (!detectedItems.isEmpty()) {
|
2018-12-22 16:27:08 +01:00
|
|
|
QString rescanLabel;
|
|
|
|
if (detectedItems.size() > 1) {
|
|
|
|
rescanLabel = tr("Rescan selected items");
|
|
|
|
} else {
|
|
|
|
rescanLabel = tr("Rescan \"%1\"").arg(detectedItems.front().name);
|
|
|
|
}
|
2018-12-26 01:18:28 +01:00
|
|
|
actions << new QAction(QIcon::fromTheme(QStringLiteral("view-refresh")), rescanLabel, parent);
|
2018-11-27 17:59:17 +01:00
|
|
|
if (connection.isConnected()) {
|
2017-05-01 03:34:43 +02:00
|
|
|
for (const SyncthingItem &item : detectedItems) {
|
2018-11-27 17:59:17 +01:00
|
|
|
connect(actions.back(), &QAction::triggered, bind(&SyncthingFileItemActionStaticData::rescanDir, &data, item.dir->id, item.path));
|
2017-02-25 18:28:20 +01:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
actions.back()->setEnabled(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// add actions for explicitely selected Syncthing dirs
|
2017-05-01 03:34:43 +02:00
|
|
|
if (!detectedDirs.isEmpty()) {
|
2017-02-25 18:28:20 +01:00
|
|
|
// rescan item
|
2017-05-01 03:34:43 +02:00
|
|
|
actions << new QAction(QIcon::fromTheme(QStringLiteral("folder-sync")),
|
2018-12-26 01:18:28 +01:00
|
|
|
detectedDirs.size() == 1 ? tr("Rescan \"%1\"").arg(detectedDirs.front()->displayName()) : tr("Rescan selected directories"), parent);
|
2018-11-27 17:59:17 +01:00
|
|
|
if (connection.isConnected()) {
|
2017-05-01 03:34:43 +02:00
|
|
|
for (const SyncthingDir *dir : detectedDirs) {
|
2018-11-27 17:59:17 +01:00
|
|
|
connect(actions.back(), &QAction::triggered, bind(&SyncthingFileItemActionStaticData::rescanDir, &data, dir->id, QString()));
|
2017-02-20 18:37:11 +01:00
|
|
|
containingDirs.removeAll(dir);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
actions.back()->setEnabled(false);
|
|
|
|
}
|
2017-02-25 18:28:20 +01:00
|
|
|
|
|
|
|
// pause/resume item
|
|
|
|
QStringList ids;
|
|
|
|
ids.reserve(detectedDirs.size());
|
|
|
|
bool isPaused = false;
|
2017-05-01 03:34:43 +02:00
|
|
|
for (const SyncthingDir *dir : detectedDirs) {
|
2017-02-25 18:28:20 +01:00
|
|
|
ids << dir->id;
|
2017-05-01 03:34:43 +02:00
|
|
|
if (dir->paused) {
|
2017-02-25 18:28:20 +01:00
|
|
|
isPaused = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2017-05-01 03:34:43 +02:00
|
|
|
if (isPaused) {
|
|
|
|
actions << new QAction(QIcon::fromTheme(QStringLiteral("media-playback-start")),
|
2018-12-26 01:18:28 +01:00
|
|
|
detectedDirs.size() == 1 ? tr("Resume \"%1\"").arg(detectedDirs.front()->displayName()) : tr("Resume selected directories"), parent);
|
2017-02-25 18:28:20 +01:00
|
|
|
} else {
|
2017-05-01 03:34:43 +02:00
|
|
|
actions << new QAction(QIcon::fromTheme(QStringLiteral("media-playback-pause")),
|
2018-12-26 01:18:28 +01:00
|
|
|
detectedDirs.size() == 1 ? tr("Pause \"%1\"").arg(detectedDirs.front()->displayName()) : tr("Pause selected directories"), parent);
|
2017-02-25 18:28:20 +01:00
|
|
|
}
|
2018-11-27 17:59:17 +01:00
|
|
|
if (connection.isConnected()) {
|
2017-05-01 03:34:43 +02:00
|
|
|
connect(actions.back(), &QAction::triggered,
|
2018-11-27 17:59:17 +01:00
|
|
|
bind(isPaused ? &SyncthingConnection::resumeDirectories : &SyncthingConnection::pauseDirectories, &connection, ids));
|
2017-02-25 18:28:20 +01:00
|
|
|
} else {
|
|
|
|
actions.back()->setEnabled(false);
|
|
|
|
}
|
2017-02-20 18:37:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// add actions for the Syncthing dirs containing selected items
|
2017-05-01 03:34:43 +02:00
|
|
|
if (!containingDirs.isEmpty()) {
|
2017-02-25 18:28:20 +01:00
|
|
|
// rescan item
|
2017-05-01 03:34:43 +02:00
|
|
|
actions << new QAction(QIcon::fromTheme(QStringLiteral("folder-sync")),
|
2018-11-30 17:46:48 +01:00
|
|
|
containingDirs.size() == 1 ? tr("Rescan \"%1\"").arg(containingDirs.front()->displayName()) : tr("Rescan containing directories"),
|
2018-12-26 01:18:28 +01:00
|
|
|
parent);
|
2018-11-27 17:59:17 +01:00
|
|
|
if (connection.isConnected()) {
|
2017-05-01 03:34:43 +02:00
|
|
|
for (const SyncthingDir *dir : containingDirs) {
|
2018-11-27 17:59:17 +01:00
|
|
|
connect(actions.back(), &QAction::triggered, bind(&SyncthingFileItemActionStaticData::rescanDir, &data, dir->id, QString()));
|
2017-02-20 18:37:11 +01:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
actions.back()->setEnabled(false);
|
|
|
|
}
|
|
|
|
|
2017-02-25 18:28:20 +01:00
|
|
|
// pause/resume item
|
|
|
|
QStringList ids;
|
|
|
|
ids.reserve(containingDirs.size());
|
|
|
|
bool isPaused = false;
|
2017-05-01 03:34:43 +02:00
|
|
|
for (const SyncthingDir *dir : containingDirs) {
|
2017-02-25 18:28:20 +01:00
|
|
|
ids << dir->id;
|
2017-05-01 03:34:43 +02:00
|
|
|
if (dir->paused) {
|
2017-02-25 18:28:20 +01:00
|
|
|
isPaused = true;
|
|
|
|
break;
|
2017-02-20 18:37:11 +01:00
|
|
|
}
|
2017-02-25 18:28:20 +01:00
|
|
|
}
|
2017-05-01 03:34:43 +02:00
|
|
|
if (isPaused) {
|
|
|
|
actions << new QAction(QIcon::fromTheme(QStringLiteral("media-playback-start")),
|
2018-11-30 17:46:48 +01:00
|
|
|
containingDirs.size() == 1 ? tr("Resume \"%1\"").arg(containingDirs.front()->displayName()) : tr("Resume containing directories"),
|
2018-12-26 01:18:28 +01:00
|
|
|
parent);
|
2017-02-25 18:28:20 +01:00
|
|
|
} else {
|
2017-05-01 03:34:43 +02:00
|
|
|
actions << new QAction(QIcon::fromTheme(QStringLiteral("media-playback-pause")),
|
2018-11-30 17:46:48 +01:00
|
|
|
containingDirs.size() == 1 ? tr("Pause \"%1\"").arg(containingDirs.front()->displayName()) : tr("Pause containing directories"),
|
2018-12-26 01:18:28 +01:00
|
|
|
parent);
|
2017-02-25 18:28:20 +01:00
|
|
|
}
|
2018-11-27 17:59:17 +01:00
|
|
|
if (connection.isConnected()) {
|
2017-05-01 03:34:43 +02:00
|
|
|
connect(actions.back(), &QAction::triggered,
|
2018-11-27 17:59:17 +01:00
|
|
|
bind(isPaused ? &SyncthingConnection::resumeDirectories : &SyncthingConnection::pauseDirectories, &connection, ids));
|
2017-02-20 18:37:11 +01:00
|
|
|
} else {
|
|
|
|
actions.back()->setEnabled(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-13 00:40:48 +01:00
|
|
|
// add actions to show further information about directory if the selection is only about one particular Syncthing dir
|
2018-11-27 17:59:17 +01:00
|
|
|
if (lastDir && detectedDirs.size() + containingDirs.size() == 1) {
|
2020-02-18 20:46:57 +01:00
|
|
|
auto *statusActions = new SyncthingDirActions(*lastDir, &data, parent);
|
2018-11-27 17:59:17 +01:00
|
|
|
connect(&connection, &SyncthingConnection::newDirs, statusActions,
|
2017-05-01 03:34:43 +02:00
|
|
|
static_cast<void (SyncthingDirActions::*)(const vector<SyncthingDir> &)>(&SyncthingDirActions::updateStatus));
|
2018-11-27 17:59:17 +01:00
|
|
|
connect(&connection, &SyncthingConnection::dirStatusChanged, statusActions,
|
2017-05-01 03:34:43 +02:00
|
|
|
static_cast<bool (SyncthingDirActions::*)(const SyncthingDir &)>(&SyncthingDirActions::updateStatus));
|
2017-03-13 00:40:48 +01:00
|
|
|
actions << *statusActions;
|
2017-02-20 18:37:11 +01:00
|
|
|
}
|
|
|
|
|
2018-11-27 17:59:17 +01:00
|
|
|
// add separator
|
|
|
|
if (!actions.isEmpty()) {
|
2018-12-26 01:18:28 +01:00
|
|
|
QAction *const separator = new QAction(parent);
|
2018-11-27 17:59:17 +01:00
|
|
|
separator->setSeparator(true);
|
|
|
|
actions << separator;
|
|
|
|
}
|
|
|
|
|
|
|
|
// add error action
|
2018-12-26 01:18:28 +01:00
|
|
|
QAction *const errorAction = new SyncthingInfoAction(parent);
|
2018-11-27 17:59:17 +01:00
|
|
|
errorAction->setText(data.currentError());
|
|
|
|
errorAction->setIcon(QIcon::fromTheme(QStringLiteral("state-error")));
|
|
|
|
errorAction->setVisible(data.hasError());
|
|
|
|
connect(&data, &SyncthingFileItemActionStaticData::currentErrorChanged, errorAction, &QAction::setText);
|
|
|
|
connect(&data, &SyncthingFileItemActionStaticData::hasErrorChanged, errorAction, &QAction::setVisible);
|
|
|
|
connect(&data, &SyncthingFileItemActionStaticData::currentErrorChanged, errorAction, &QAction::changed);
|
|
|
|
actions << errorAction;
|
|
|
|
|
2020-02-18 20:46:57 +01:00
|
|
|
// add config items
|
|
|
|
QAction *const brightCustomColorsAction = new QAction(QIcon::fromTheme(QStringLiteral("color-profile")), tr("Use bright custom colors"), parent);
|
|
|
|
brightCustomColorsAction->setCheckable(true);
|
|
|
|
brightCustomColorsAction->setChecked(data.isUsingBrightCustomColors());
|
|
|
|
connect(brightCustomColorsAction, &QAction::triggered, &data, &SyncthingFileItemActionStaticData::handleBrightCustomColorsChanged);
|
|
|
|
actions << brightCustomColorsAction;
|
2018-12-26 01:18:28 +01:00
|
|
|
QAction *const configFileAction = new QAction(QIcon::fromTheme(QStringLiteral("settings-configure")), tr("Select Syncthing config ..."), parent);
|
2018-11-27 17:59:17 +01:00
|
|
|
connect(configFileAction, &QAction::triggered, &data, &SyncthingFileItemActionStaticData::selectSyncthingConfig);
|
|
|
|
actions << configFileAction;
|
|
|
|
|
2017-02-20 18:37:11 +01:00
|
|
|
// about about action
|
2018-12-26 01:18:28 +01:00
|
|
|
QAction *const aboutAction = new QAction(QIcon::fromTheme(QStringLiteral("help-about")), tr("About"), parent);
|
2018-11-27 17:59:17 +01:00
|
|
|
connect(aboutAction, &QAction::triggered, &SyncthingFileItemActionStaticData::showAboutDialog);
|
|
|
|
actions << aboutAction;
|
2017-02-20 18:37:11 +01:00
|
|
|
|
2017-03-11 19:57:49 +01:00
|
|
|
return actions;
|
2017-02-20 18:37:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#include <syncthingfileitemaction.moc>
|