Provide basic Dolphin integration
This commit is contained in:
parent
6ec35fa203
commit
57f7d140e0
|
@ -17,6 +17,7 @@ project(${META_PROJECT_NAME})
|
|||
# options for partial build
|
||||
option(NO_CLI "specifies whether building CLI should be skipped" OFF)
|
||||
option(NO_TRAY "specifies whether building the tray should be skipped" OFF)
|
||||
option(NO_FILE_ITEM_ACTION_PLUGIN "specifies whether building the file item action plugin should be skipped" OFF)
|
||||
option(NO_MODEL "specifies whether building models should be skipped, implies NO_TRAY" OFF)
|
||||
|
||||
# add subdirectories
|
||||
|
@ -32,3 +33,6 @@ if(NOT NO_MODEL)
|
|||
add_subdirectory(tray)
|
||||
endif()
|
||||
endif()
|
||||
if(NOT NO_FILE_ITEM_ACTION_PLUGIN)
|
||||
add_subdirectory(fileitemactionplugin)
|
||||
endif()
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
# metadata
|
||||
set(META_PROJECT_NAME syncthingfileitemaction)
|
||||
set(META_APP_NAME "Syncthing KIO-Plugin")
|
||||
set(META_APP_DESCRIPTION "KIO file item action for Syncthing")
|
||||
set(META_PROJECT_TYPE qtplugin)
|
||||
|
||||
# add project files
|
||||
set(HEADER_FILES
|
||||
syncthingfileitemaction.h
|
||||
)
|
||||
set(SRC_FILES
|
||||
syncthingfileitemaction.cpp
|
||||
)
|
||||
|
||||
# find c++utilities
|
||||
find_package(c++utilities 4.6.0 REQUIRED)
|
||||
list(APPEND CMAKE_MODULE_PATH ${CPP_UTILITIES_MODULE_DIRS})
|
||||
|
||||
# find qtutilities
|
||||
find_package(qtutilities 5.0.0 REQUIRED)
|
||||
use_qt_utilities()
|
||||
#list(APPEND CMAKE_MODULE_PATH ${QT_UTILITIES_MODULE_DIRS})
|
||||
|
||||
# find backend libraries
|
||||
find_package(syncthingconnector ${META_APP_VERSION} REQUIRED)
|
||||
use_syncthingconnector()
|
||||
find_package(syncthingmodel ${META_APP_VERSION} REQUIRED)
|
||||
use_syncthingmodel()
|
||||
|
||||
# link also explicitely against the following Qt 5 modules
|
||||
list(APPEND ADDITIONAL_QT_MODULES Network)
|
||||
list(APPEND ADDITIONAL_KF_MODULES KIO)
|
||||
|
||||
# include modules to apply configuration
|
||||
include(BasicConfig)
|
||||
include(QtGuiConfig)
|
||||
include(QtConfig)
|
||||
include(WindowsResources)
|
||||
# KIO libs can not be determined automatically
|
||||
list(REMOVE_ITEM PRIVATE_LIBRARIES KF5::KIO)
|
||||
list(APPEND PRIVATE_LIBRARIES KF5::KIOCore KF5::KIOFileWidgets KF5::KIOWidgets KF5::KIONTLM)
|
||||
include(LibraryTarget)
|
||||
include(ConfigHeader)
|
|
@ -0,0 +1,27 @@
|
|||
// Created via CMake from template global.h.in
|
||||
// WARNING! Any changes to this file will be overwritten by the next CMake run!
|
||||
|
||||
#ifndef SYNCTHINGFILEITEMACTION_GLOBAL
|
||||
#define SYNCTHINGFILEITEMACTION_GLOBAL
|
||||
|
||||
#include <c++utilities/application/global.h>
|
||||
|
||||
#ifdef SYNCTHINGFILEITEMACTION_STATIC
|
||||
# define SYNCTHINGFILEITEMACTION_EXPORT
|
||||
# define SYNCTHINGFILEITEMACTION_IMPORT
|
||||
#else
|
||||
# define SYNCTHINGFILEITEMACTION_EXPORT LIB_EXPORT
|
||||
# define SYNCTHINGFILEITEMACTION_IMPORT LIB_IMPORT
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* \def SYNCTHINGFILEITEMACTION_EXPORT
|
||||
* \brief Marks the symbol to be exported by the syncthingfileitemaction library.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \def SYNCTHINGFILEITEMACTION_IMPORT
|
||||
* \brief Marks the symbol to be imported from the syncthingfileitemaction library.
|
||||
*/
|
||||
|
||||
#endif // SYNCTHINGFILEITEMACTION_GLOBAL
|
|
@ -0,0 +1,264 @@
|
|||
#include "./syncthingfileitemaction.h"
|
||||
#include "../model/syncthingicons.h"
|
||||
|
||||
#include "../connector/syncthingconfig.h"
|
||||
#include "../connector/syncthingconnectionsettings.h"
|
||||
#include "../connector/syncthingdir.h"
|
||||
#include "../connector/utils.h"
|
||||
|
||||
#include <qtutilities/aboutdialog/aboutdialog.h>
|
||||
|
||||
#include <KFileItemListProperties>
|
||||
#include <KFileItem>
|
||||
#include <KPluginFactory>
|
||||
#include <KPluginLoader>
|
||||
|
||||
#include <QMenu>
|
||||
#include <QAction>
|
||||
#include <QWidget>
|
||||
#include <QDir>
|
||||
#include <QEvent>
|
||||
|
||||
#include <iostream>
|
||||
#include <functional>
|
||||
|
||||
#include "resources/config.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace Dialogs;
|
||||
using namespace Data;
|
||||
|
||||
K_PLUGIN_FACTORY(SyncthingFileItemActionFactory, registerPlugin<SyncthingFileItemAction>();)
|
||||
|
||||
struct SyncthingItem
|
||||
{
|
||||
SyncthingItem(const SyncthingDir *dir, const QString &path);
|
||||
const SyncthingDir *dir;
|
||||
QString path;
|
||||
QString name;
|
||||
};
|
||||
|
||||
SyncthingItem::SyncthingItem(const SyncthingDir *dir, const QString &path) :
|
||||
dir(dir),
|
||||
path(path)
|
||||
{
|
||||
int lastSep = path.lastIndexOf(QChar('/'));
|
||||
if(lastSep > 0) {
|
||||
name = path.mid(lastSep + 1);
|
||||
} else {
|
||||
name = path;
|
||||
}
|
||||
}
|
||||
|
||||
SyncthingConnection SyncthingFileItemAction::s_connection;
|
||||
|
||||
SyncthingFileItemAction::SyncthingFileItemAction(QObject *parent, const QVariantList &) :
|
||||
KAbstractFileItemActionPlugin(parent)
|
||||
{
|
||||
if(s_connection.apiKey().isEmpty()) {
|
||||
// determine path of Syncthing config file
|
||||
const QByteArray configPathFromEnv(qgetenv("KIO_SYNCTHING_CONFIG_PATH"));
|
||||
const QString configPath = !configPathFromEnv.isEmpty()
|
||||
? QString::fromLocal8Bit(configPathFromEnv)
|
||||
: SyncthingConfig::locateConfigFile();
|
||||
if(configPath.isEmpty()) {
|
||||
cerr << "Unable to determine location of Syncthing config. Set KIO_SYNCTHING_CONFIG_PATH to specify location." << endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// load Syncthing config
|
||||
SyncthingConfig config;
|
||||
if(!config.restore(configPath)) {
|
||||
cerr << "Unable to load Syncthing config from \"" << configPath.toLocal8Bit().data() << "\"" << endl;
|
||||
if(configPathFromEnv.isEmpty()) {
|
||||
cerr << "Note: Set KIO_SYNCTHING_CONFIG_PATH to specify config file explicitely." << endl;
|
||||
}
|
||||
return;
|
||||
}
|
||||
cerr << "Syncthing config loaded from \"" << configPath.toLocal8Bit().data() << "\"" << endl;
|
||||
SyncthingConnectionSettings settings;
|
||||
settings.syncthingUrl = config.syncthingUrl();
|
||||
settings.apiKey.append(config.guiApiKey);
|
||||
|
||||
// establish connection
|
||||
bool ok;
|
||||
int reconnectInterval = qEnvironmentVariableIntValue("KIO_SYNCTHING_RECONNECT_INTERVAL", &ok);
|
||||
if(!ok || reconnectInterval < 0) {
|
||||
reconnectInterval = 10000;
|
||||
}
|
||||
s_connection.setAutoReconnectInterval(reconnectInterval);
|
||||
s_connection.reconnect(settings);
|
||||
connect(&s_connection, &SyncthingConnection::error, &SyncthingFileItemAction::logConnectionError);
|
||||
connect(&s_connection, &SyncthingConnection::statusChanged, &SyncthingFileItemAction::logConnectionStatus);
|
||||
}
|
||||
}
|
||||
|
||||
QList<QAction *> SyncthingFileItemAction::actions(const KFileItemListProperties &fileItemInfo, QWidget *parentWidget)
|
||||
{
|
||||
// check whether any directories are known
|
||||
const auto &dirs = s_connection.dirInfo();
|
||||
if(dirs.empty()) {
|
||||
return QList<QAction*>();
|
||||
}
|
||||
|
||||
// get all paths
|
||||
QStringList paths;
|
||||
paths.reserve(fileItemInfo.items().size());
|
||||
for(const KFileItem &item : fileItemInfo.items()) {
|
||||
if(!item.isLocalFile()) {
|
||||
// don't show any actions when remote files are selected
|
||||
return QList<QAction*>();
|
||||
}
|
||||
paths << item.localPath();
|
||||
}
|
||||
|
||||
// determine relevant Syncthing dirs
|
||||
QList<const SyncthingDir *> detectedDirs;
|
||||
QList<const SyncthingDir *> containingDirs;
|
||||
QList<SyncthingItem> detectedItems;
|
||||
const SyncthingDir *lastDir;
|
||||
for(const SyncthingDir &dir : dirs) {
|
||||
QStringRef dirPath(&dir.path);
|
||||
while(dirPath.endsWith(QChar('/'))) {
|
||||
dirPath.chop(1);
|
||||
}
|
||||
for(const QString &path : paths) {
|
||||
if(path == dirPath) {
|
||||
detectedDirs << (lastDir = &dir);
|
||||
} else if(path.startsWith(dir.path)) {
|
||||
detectedItems << SyncthingItem(&dir, path.mid(dir.path.size()));
|
||||
containingDirs << (lastDir = &dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add actions for explicitely selected Syncthing dirs
|
||||
QList<QAction*> actions;
|
||||
if(!detectedDirs.isEmpty()) {
|
||||
actions << new QAction(
|
||||
QIcon::fromTheme(QStringLiteral("folder-sync")),
|
||||
detectedDirs.size() == 1
|
||||
? tr("Rescan ") + detectedDirs.front()->displayName()
|
||||
: tr("Rescan selected directories"),
|
||||
parentWidget);
|
||||
if(s_connection.isConnected()) {
|
||||
for(const SyncthingDir *dir : detectedDirs) {
|
||||
connect(actions.back(), &QAction::triggered, bind(&SyncthingFileItemAction::rescanDir, dir->id, QString()));
|
||||
containingDirs.removeAll(dir);
|
||||
}
|
||||
} else {
|
||||
actions.back()->setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
// add actions for the Syncthing dirs containing selected items
|
||||
if(!containingDirs.isEmpty()) {
|
||||
actions << new QAction(
|
||||
QIcon::fromTheme(QStringLiteral("folder-sync")),
|
||||
containingDirs.size() == 1
|
||||
? tr("Rescan ") + containingDirs.front()->displayName()
|
||||
: tr("Rescan containing directories"),
|
||||
parentWidget);
|
||||
if(s_connection.isConnected()) {
|
||||
for(const SyncthingDir *dir : containingDirs) {
|
||||
connect(actions.back(), &QAction::triggered, bind(&SyncthingFileItemAction::rescanDir, dir->id, QString()));
|
||||
}
|
||||
} else {
|
||||
actions.back()->setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
// add actions for the selected items itself
|
||||
if(!detectedItems.isEmpty()) {
|
||||
actions << new QAction(
|
||||
QIcon::fromTheme(QStringLiteral("view-refresh")),
|
||||
detectedItems.size() == 1
|
||||
? tr("Rescan %1 (in %2)").arg(detectedItems.front().name, detectedItems.front().dir->displayName())
|
||||
: tr("Rescan selected items"),
|
||||
parentWidget);
|
||||
if(s_connection.isConnected()) {
|
||||
for(const SyncthingItem &item : detectedItems) {
|
||||
connect(actions.back(), &QAction::triggered, bind(&SyncthingFileItemAction::rescanDir, item.dir->id, item.path));
|
||||
}
|
||||
} else {
|
||||
actions.back()->setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
// don't show anything if relevant actions could be determined
|
||||
if(actions.isEmpty()) {
|
||||
return actions;
|
||||
}
|
||||
|
||||
// create the menu
|
||||
QAction *menuAction = new QAction(statusIcons().scanninig, tr("Syncthing"), this);
|
||||
QMenu *menu = new QMenu(parentWidget);
|
||||
menuAction->setMenu(menu);
|
||||
menu->addActions(actions);
|
||||
|
||||
// add action to show further information about directory if the selection is only about
|
||||
// one particular Syncthing dir
|
||||
if(detectedDirs.size() + containingDirs.size() == 1) {
|
||||
QAction *infoAction = menu->addSeparator();
|
||||
infoAction->setIcon(QIcon::fromTheme(QStringLiteral("dialog-information")));
|
||||
infoAction->setText(tr("Directory"));
|
||||
QAction *statusAction = menu->addAction(tr("Status: ") + statusString(lastDir->status));
|
||||
switch(lastDir->status) {
|
||||
case SyncthingDirStatus::Unknown:
|
||||
case SyncthingDirStatus::Unshared:
|
||||
statusAction->setIcon(statusIcons().disconnected);
|
||||
break;
|
||||
case SyncthingDirStatus::Idle:
|
||||
statusAction->setIcon(statusIcons().idling);
|
||||
break;
|
||||
case SyncthingDirStatus::Scanning:
|
||||
statusAction->setIcon(statusIcons().scanninig);
|
||||
break;
|
||||
case SyncthingDirStatus::Synchronizing:
|
||||
statusAction->setIcon(statusIcons().sync);
|
||||
break;
|
||||
case SyncthingDirStatus::Paused:
|
||||
statusAction->setIcon(statusIcons().pause);
|
||||
break;
|
||||
case SyncthingDirStatus::OutOfSync:
|
||||
statusAction->setIcon(statusIcons().error);
|
||||
break;
|
||||
}
|
||||
menu->addAction(QIcon::fromTheme(QStringLiteral("accept_time_event")),
|
||||
tr("Last scan time: ") + agoString(lastDir->lastScanTime))->setEnabled(false);
|
||||
menu->addAction(tr("Rescan interval: %1 seconds").arg(lastDir->rescanInterval))->setEnabled(false);
|
||||
}
|
||||
|
||||
// about about action
|
||||
menu->addSeparator();
|
||||
menu->addAction(QIcon::fromTheme(QStringLiteral("help-about")), tr("About"), &SyncthingFileItemAction::showAboutDialog);
|
||||
|
||||
return QList<QAction*>() << menuAction;
|
||||
}
|
||||
|
||||
void SyncthingFileItemAction::logConnectionStatus()
|
||||
{
|
||||
cerr << "Syncthing connection status changed to: " << s_connection.statusText().toLocal8Bit().data() << endl;
|
||||
}
|
||||
|
||||
void SyncthingFileItemAction::logConnectionError(const QString &errorMessage)
|
||||
{
|
||||
cerr << "Syncthing connection error: " << errorMessage.toLocal8Bit().data() << endl;
|
||||
}
|
||||
|
||||
void SyncthingFileItemAction::rescanDir(const QString &dirId, const QString &relpath)
|
||||
{
|
||||
s_connection.rescan(dirId, relpath);
|
||||
}
|
||||
|
||||
void SyncthingFileItemAction::showAboutDialog()
|
||||
{
|
||||
auto *aboutDialog = new AboutDialog(nullptr, QStringLiteral(APP_NAME), QStringLiteral(APP_AUTHOR), QStringLiteral(APP_VERSION), QStringLiteral(APP_URL), QStringLiteral(APP_DESCRIPTION), QImage(statusIcons().scanninig.pixmap(128).toImage()));
|
||||
aboutDialog->setWindowTitle(tr("About") + QStringLiteral(" - " APP_NAME));
|
||||
aboutDialog->setWindowIcon(QIcon::fromTheme(QStringLiteral("syncthingtray")));
|
||||
aboutDialog->setWindowFlags(static_cast<Qt::WindowFlags>(aboutDialog->windowFlags() | Qt::WA_DeleteOnClose));
|
||||
aboutDialog->show();
|
||||
}
|
||||
|
||||
|
||||
#include <syncthingfileitemaction.moc>
|
|
@ -0,0 +1,10 @@
|
|||
[Desktop Entry]
|
||||
Type=Service
|
||||
Name=Trigger Syncthing scan
|
||||
Name[de]="Syncthing-Rescan" im Servicemenü
|
||||
X-KDE-Library=syncthingfileitemaction
|
||||
X-KDE-Submenu=Syncthing
|
||||
|
||||
Icon=syncthingtray
|
||||
ServiceTypes=KFileItemAction/Plugin
|
||||
MimeType=application/octet-stream;inode/directory
|
|
@ -0,0 +1,31 @@
|
|||
#ifndef SYNCTHINGFILEITEMACTION_H
|
||||
#define SYNCTHINGFILEITEMACTION_H
|
||||
|
||||
#include "../connector/syncthingconnection.h"
|
||||
|
||||
#include <KAbstractFileItemActionPlugin>
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QAction)
|
||||
QT_FORWARD_DECLARE_CLASS(QWidget)
|
||||
|
||||
class KFileItemListProperties;
|
||||
|
||||
class SyncthingFileItemAction : public KAbstractFileItemActionPlugin
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SyncthingFileItemAction(QObject* parent, const QVariantList &args);
|
||||
QList<QAction *> actions(const KFileItemListProperties &fileItemInfo, QWidget *parentWidget) override;
|
||||
|
||||
private Q_SLOTS:
|
||||
static void logConnectionStatus();
|
||||
static void logConnectionError(const QString &errorMessage);
|
||||
static void rescanDir(const QString &dirId, const QString &relpath = QString());
|
||||
static void showAboutDialog();
|
||||
|
||||
private:
|
||||
static Data::SyncthingConnection s_connection;
|
||||
};
|
||||
|
||||
#endif // SYNCTHINGFILEITEMACTION_H
|
Loading…
Reference in New Issue