Split fileitemactionplugin code into multiple files
This commit is contained in:
parent
882a2a5d35
commit
f901386478
|
@ -7,10 +7,18 @@ set(META_QT5_VERSION 5.8)
|
|||
|
||||
# add project files
|
||||
set(HEADER_FILES
|
||||
${META_PROJECT_NAME}.h
|
||||
syncthingmenuaction.h
|
||||
syncthinginfoaction.h
|
||||
syncthingdiractions.h
|
||||
syncthingfileitemactionstaticdata.h
|
||||
syncthingfileitemaction.h
|
||||
)
|
||||
set(SRC_FILES
|
||||
${META_PROJECT_NAME}.cpp
|
||||
syncthingmenuaction.cpp
|
||||
syncthinginfoaction.cpp
|
||||
syncthingdiractions.cpp
|
||||
syncthingfileitemactionstaticdata.cpp
|
||||
syncthingfileitemaction.cpp
|
||||
)
|
||||
|
||||
set(TS_FILES
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
#include "./syncthingdiractions.h"
|
||||
|
||||
#include "../model/syncthingicons.h"
|
||||
|
||||
#include "../connector/syncthingdir.h"
|
||||
#include "../connector/utils.h"
|
||||
|
||||
using namespace Data;
|
||||
|
||||
SyncthingDirActions::SyncthingDirActions(const SyncthingDir &dir, QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_dirId(dir.id)
|
||||
{
|
||||
m_infoAction.setSeparator(true);
|
||||
updateStatus(dir);
|
||||
}
|
||||
|
||||
void SyncthingDirActions::updateStatus(const std::vector<SyncthingDir> &dirs)
|
||||
{
|
||||
for (const SyncthingDir &dir : dirs) {
|
||||
if (updateStatus(dir)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
m_statusAction.setText(tr("Status: not available anymore"));
|
||||
m_statusAction.setIcon(statusIcons().disconnected);
|
||||
}
|
||||
|
||||
bool SyncthingDirActions::updateStatus(const SyncthingDir &dir)
|
||||
{
|
||||
if (dir.id != m_dirId) {
|
||||
return false;
|
||||
}
|
||||
m_infoAction.setText(tr("Directory info for %1").arg(dir.displayName()));
|
||||
m_infoAction.setIcon(QIcon::fromTheme(QStringLiteral("dialog-information")));
|
||||
m_statusAction.setText(tr("Status: ") + dir.statusString());
|
||||
if (dir.paused && dir.status != SyncthingDirStatus::OutOfSync) {
|
||||
m_statusAction.setIcon(statusIcons().pause);
|
||||
} else if (dir.isUnshared()) {
|
||||
m_statusAction.setIcon(statusIcons().disconnected);
|
||||
} else {
|
||||
switch (dir.status) {
|
||||
case SyncthingDirStatus::Unknown:
|
||||
m_statusAction.setIcon(statusIcons().disconnected);
|
||||
break;
|
||||
case SyncthingDirStatus::Idle:
|
||||
m_statusAction.setIcon(statusIcons().idling);
|
||||
break;
|
||||
case SyncthingDirStatus::Scanning:
|
||||
m_statusAction.setIcon(statusIcons().scanninig);
|
||||
break;
|
||||
case SyncthingDirStatus::Synchronizing:
|
||||
m_statusAction.setIcon(statusIcons().sync);
|
||||
break;
|
||||
case SyncthingDirStatus::OutOfSync:
|
||||
m_statusAction.setIcon(statusIcons().error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_globalStatusAction.setText(tr("Global: ") + directoryStatusString(dir.globalStats));
|
||||
m_localStatusAction.setText(tr("Local: ") + directoryStatusString(dir.localStats));
|
||||
m_lastScanAction.setText(tr("Last scan time: ") + agoString(dir.lastScanTime));
|
||||
m_lastScanAction.setIcon(QIcon::fromTheme(QStringLiteral("accept_time_event")));
|
||||
m_rescanIntervalAction.setText(tr("Rescan interval: %1 seconds").arg(dir.rescanInterval));
|
||||
if (!dir.pullErrorCount) {
|
||||
m_errorsAction.setVisible(false);
|
||||
} else {
|
||||
m_errorsAction.setVisible(true);
|
||||
m_errorsAction.setIcon(QIcon::fromTheme(QStringLiteral("dialog-error")));
|
||||
m_errorsAction.setText(tr("%1 item(s) out-of-sync", nullptr, trQuandity(dir.pullErrorCount)).arg(dir.pullErrorCount));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QList<QAction *> &operator<<(QList<QAction *> &actions, SyncthingDirActions &dirActions)
|
||||
{
|
||||
return actions << &dirActions.m_infoAction << &dirActions.m_statusAction << &dirActions.m_globalStatusAction << &dirActions.m_localStatusAction
|
||||
<< &dirActions.m_lastScanAction << &dirActions.m_rescanIntervalAction << &dirActions.m_errorsAction;
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
#ifndef SYNCTHINGDIRACTIONS_H
|
||||
#define SYNCTHINGDIRACTIONS_H
|
||||
|
||||
#include "../connector/syncthingdir.h"
|
||||
|
||||
#include "./syncthinginfoaction.h"
|
||||
|
||||
class SyncthingDirActions : public QObject {
|
||||
Q_OBJECT
|
||||
friend QList<QAction *> &operator<<(QList<QAction *> &, SyncthingDirActions &);
|
||||
|
||||
public:
|
||||
explicit SyncthingDirActions(const Data::SyncthingDir &dir, QObject *parent = nullptr);
|
||||
|
||||
public Q_SLOTS:
|
||||
void updateStatus(const std::vector<Data::SyncthingDir> &dirs);
|
||||
bool updateStatus(const Data::SyncthingDir &dir);
|
||||
|
||||
private:
|
||||
QString m_dirId;
|
||||
QAction m_infoAction;
|
||||
SyncthingInfoAction m_statusAction;
|
||||
SyncthingInfoAction m_globalStatusAction;
|
||||
SyncthingInfoAction m_localStatusAction;
|
||||
SyncthingInfoAction m_lastScanAction;
|
||||
SyncthingInfoAction m_rescanIntervalAction;
|
||||
SyncthingInfoAction m_errorsAction;
|
||||
};
|
||||
|
||||
QList<QAction *> &operator<<(QList<QAction *> &actions, SyncthingDirActions &dirActions);
|
||||
|
||||
#endif // SYNCTHINGDIRACTIONS_H
|
|
@ -1,38 +1,19 @@
|
|||
#include "./syncthingfileitemaction.h"
|
||||
#include "./syncthingdiractions.h"
|
||||
#include "./syncthinginfoaction.h"
|
||||
#include "./syncthingmenuaction.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 <qtutilities/resources/resources.h>
|
||||
|
||||
#include <c++utilities/application/argumentparser.h>
|
||||
|
||||
#include <KFileItem>
|
||||
#include <KPluginFactory>
|
||||
#include <KPluginLoader>
|
||||
|
||||
#include <QAction>
|
||||
#include <QDir>
|
||||
#include <QEvent>
|
||||
#include <QFileDialog>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
#include <QSettings>
|
||||
#include <QWidget>
|
||||
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
|
||||
#include "resources/config.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace Dialogs;
|
||||
using namespace Data;
|
||||
|
||||
K_PLUGIN_FACTORY(SyncthingFileItemActionFactory, registerPlugin<SyncthingFileItemAction>();)
|
||||
|
@ -56,295 +37,6 @@ SyncthingItem::SyncthingItem(const SyncthingDir *dir, const QString &path)
|
|||
}
|
||||
}
|
||||
|
||||
SyncthingMenuAction::SyncthingMenuAction(const KFileItemListProperties &properties, const QList<QAction *> &actions, QWidget *parentWidget)
|
||||
: QAction(parentWidget)
|
||||
, m_properties(properties)
|
||||
{
|
||||
if (!actions.isEmpty()) {
|
||||
auto *menu = new QMenu(parentWidget);
|
||||
menu->addActions(actions);
|
||||
setMenu(menu);
|
||||
}
|
||||
updateStatus(SyncthingFileItemAction::staticData().connection().status());
|
||||
}
|
||||
|
||||
void SyncthingMenuAction::updateStatus(SyncthingStatus status)
|
||||
{
|
||||
if (status != SyncthingStatus::Disconnected && status != SyncthingStatus::Reconnecting && status != SyncthingStatus::BeingDestroyed) {
|
||||
setText(tr("Syncthing"));
|
||||
setIcon(statusIcons().scanninig);
|
||||
if (!menu()) {
|
||||
const QList<QAction *> actions = SyncthingFileItemAction::createActions(m_properties, parentWidget());
|
||||
if (!actions.isEmpty()) {
|
||||
auto *menu = new QMenu(parentWidget());
|
||||
menu->addActions(actions);
|
||||
setMenu(menu);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (status != SyncthingStatus::Reconnecting) {
|
||||
SyncthingFileItemAction::staticData().connection().connect();
|
||||
}
|
||||
setText(tr("Syncthing - connecting"));
|
||||
setIcon(statusIcons().disconnected);
|
||||
if (QMenu *menu = this->menu()) {
|
||||
setMenu(nullptr);
|
||||
delete menu;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SyncthingInfoWidget::SyncthingInfoWidget(const SyncthingInfoAction *action, QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, m_textLabel(new QLabel(parent))
|
||||
, m_iconLabel(new QLabel(parent))
|
||||
{
|
||||
auto *const layout = new QHBoxLayout(parent);
|
||||
layout->setMargin(4);
|
||||
layout->setSpacing(5);
|
||||
m_iconLabel->setFixedWidth(16);
|
||||
m_iconLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
|
||||
layout->addWidget(m_iconLabel);
|
||||
layout->addWidget(m_textLabel);
|
||||
setLayout(layout);
|
||||
updateFromAction(action);
|
||||
connect(action, &QAction::changed, this, &SyncthingInfoWidget::updateFromSender);
|
||||
}
|
||||
|
||||
void SyncthingInfoWidget::updateFromSender()
|
||||
{
|
||||
updateFromAction(qobject_cast<const SyncthingInfoAction *>(QObject::sender()));
|
||||
}
|
||||
|
||||
void SyncthingInfoWidget::updateFromAction(const SyncthingInfoAction *action)
|
||||
{
|
||||
auto text(action->text());
|
||||
m_textLabel->setText(text.startsWith(QChar('&')) ? text.mid(1) : std::move(text));
|
||||
m_iconLabel->setPixmap(action->icon().pixmap(16));
|
||||
setVisible(action->isVisible());
|
||||
}
|
||||
|
||||
SyncthingInfoAction::SyncthingInfoAction(QObject *parent)
|
||||
: QWidgetAction(parent)
|
||||
{
|
||||
}
|
||||
|
||||
QWidget *SyncthingInfoAction::createWidget(QWidget *parent)
|
||||
{
|
||||
return new SyncthingInfoWidget(this, parent);
|
||||
}
|
||||
|
||||
SyncthingDirActions::SyncthingDirActions(const SyncthingDir &dir, QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_dirId(dir.id)
|
||||
{
|
||||
m_infoAction.setSeparator(true);
|
||||
updateStatus(dir);
|
||||
}
|
||||
|
||||
void SyncthingDirActions::updateStatus(const std::vector<SyncthingDir> &dirs)
|
||||
{
|
||||
for (const SyncthingDir &dir : dirs) {
|
||||
if (updateStatus(dir)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
m_statusAction.setText(tr("Status: not available anymore"));
|
||||
m_statusAction.setIcon(statusIcons().disconnected);
|
||||
}
|
||||
|
||||
bool SyncthingDirActions::updateStatus(const SyncthingDir &dir)
|
||||
{
|
||||
if (dir.id != m_dirId) {
|
||||
return false;
|
||||
}
|
||||
m_infoAction.setText(tr("Directory info for %1").arg(dir.displayName()));
|
||||
m_infoAction.setIcon(QIcon::fromTheme(QStringLiteral("dialog-information")));
|
||||
m_statusAction.setText(tr("Status: ") + dir.statusString());
|
||||
if (dir.paused && dir.status != SyncthingDirStatus::OutOfSync) {
|
||||
m_statusAction.setIcon(statusIcons().pause);
|
||||
} else if (dir.isUnshared()) {
|
||||
m_statusAction.setIcon(statusIcons().disconnected);
|
||||
} else {
|
||||
switch (dir.status) {
|
||||
case SyncthingDirStatus::Unknown:
|
||||
m_statusAction.setIcon(statusIcons().disconnected);
|
||||
break;
|
||||
case SyncthingDirStatus::Idle:
|
||||
m_statusAction.setIcon(statusIcons().idling);
|
||||
break;
|
||||
case SyncthingDirStatus::Scanning:
|
||||
m_statusAction.setIcon(statusIcons().scanninig);
|
||||
break;
|
||||
case SyncthingDirStatus::Synchronizing:
|
||||
m_statusAction.setIcon(statusIcons().sync);
|
||||
break;
|
||||
case SyncthingDirStatus::OutOfSync:
|
||||
m_statusAction.setIcon(statusIcons().error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_globalStatusAction.setText(tr("Global: ") + directoryStatusString(dir.globalStats));
|
||||
m_localStatusAction.setText(tr("Local: ") + directoryStatusString(dir.localStats));
|
||||
m_lastScanAction.setText(tr("Last scan time: ") + agoString(dir.lastScanTime));
|
||||
m_lastScanAction.setIcon(QIcon::fromTheme(QStringLiteral("accept_time_event")));
|
||||
m_rescanIntervalAction.setText(tr("Rescan interval: %1 seconds").arg(dir.rescanInterval));
|
||||
if (!dir.pullErrorCount) {
|
||||
m_errorsAction.setVisible(false);
|
||||
} else {
|
||||
m_errorsAction.setVisible(true);
|
||||
m_errorsAction.setIcon(QIcon::fromTheme(QStringLiteral("dialog-error")));
|
||||
m_errorsAction.setText(tr("%1 item(s) out-of-sync", nullptr, trQuandity(dir.pullErrorCount)).arg(dir.pullErrorCount));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QList<QAction *> &operator<<(QList<QAction *> &actions, SyncthingDirActions &dirActions)
|
||||
{
|
||||
return actions << &dirActions.m_infoAction << &dirActions.m_statusAction << &dirActions.m_globalStatusAction << &dirActions.m_localStatusAction
|
||||
<< &dirActions.m_lastScanAction << &dirActions.m_rescanIntervalAction << &dirActions.m_errorsAction;
|
||||
}
|
||||
|
||||
SyncthingFileItemActionStaticData::SyncthingFileItemActionStaticData()
|
||||
: m_initialized(false)
|
||||
{
|
||||
}
|
||||
|
||||
SyncthingFileItemActionStaticData::~SyncthingFileItemActionStaticData()
|
||||
{
|
||||
}
|
||||
|
||||
void SyncthingFileItemActionStaticData::initialize()
|
||||
{
|
||||
if (m_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOAD_QT_TRANSLATIONS;
|
||||
|
||||
// load settings
|
||||
const QSettings settingsFile(QSettings::IniFormat, QSettings::UserScope, QStringLiteral(PROJECT_NAME));
|
||||
|
||||
// determine path of Syncthing config file
|
||||
m_configFilePath = [&] {
|
||||
const QByteArray configPathFromEnv(qgetenv("KIO_SYNCTHING_CONFIG_PATH"));
|
||||
if (!configPathFromEnv.isEmpty()) {
|
||||
return QString::fromLocal8Bit(configPathFromEnv);
|
||||
}
|
||||
const QString configPathFromSettings = settingsFile.value(QStringLiteral("syncthingConfigPath")).toString();
|
||||
if (!configPathFromSettings.isEmpty()) {
|
||||
return configPathFromSettings;
|
||||
}
|
||||
return SyncthingConfig::locateConfigFile();
|
||||
}();
|
||||
applySyncthingConfiguration(m_configFilePath);
|
||||
|
||||
// prevent unnecessary API calls (for the purpose of the context menu)
|
||||
m_connection.disablePolling();
|
||||
|
||||
// connect Signals & Slots for logging
|
||||
connect(&m_connection, &SyncthingConnection::error, this, &SyncthingFileItemActionStaticData::logConnectionError);
|
||||
if (qEnvironmentVariableIsSet("KIO_SYNCTHING_LOG_STATUS")) {
|
||||
connect(&m_connection, &SyncthingConnection::statusChanged, this, &SyncthingFileItemActionStaticData::logConnectionStatus);
|
||||
}
|
||||
|
||||
m_initialized = true;
|
||||
}
|
||||
|
||||
void SyncthingFileItemActionStaticData::logConnectionStatus()
|
||||
{
|
||||
cerr << "Syncthing connection status changed to: " << m_connection.statusText().toLocal8Bit().data() << endl;
|
||||
}
|
||||
|
||||
void SyncthingFileItemActionStaticData::logConnectionError(const QString &errorMessage, SyncthingErrorCategory errorCategory)
|
||||
{
|
||||
switch (errorCategory) {
|
||||
case SyncthingErrorCategory::Parsing:
|
||||
case SyncthingErrorCategory::SpecificRequest:
|
||||
QMessageBox::critical(nullptr, tr("Syncthing connection error"), errorMessage);
|
||||
break;
|
||||
default:
|
||||
cerr << "Syncthing connection error: " << errorMessage.toLocal8Bit().data() << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void SyncthingFileItemActionStaticData::rescanDir(const QString &dirId, const QString &relpath)
|
||||
{
|
||||
m_connection.rescan(dirId, relpath);
|
||||
}
|
||||
|
||||
void SyncthingFileItemActionStaticData::showAboutDialog()
|
||||
{
|
||||
auto *aboutDialog = new AboutDialog(nullptr, QStringLiteral(APP_NAME), QStringLiteral(APP_AUTHOR "\nSyncthing icons from Syncthing project"),
|
||||
QStringLiteral(APP_VERSION), ApplicationUtilities::dependencyVersions2, 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->setAttribute(Qt::WA_DeleteOnClose);
|
||||
aboutDialog->show();
|
||||
}
|
||||
|
||||
void SyncthingFileItemActionStaticData::selectSyncthingConfig()
|
||||
{
|
||||
const auto configFilePath = QFileDialog::getOpenFileName(nullptr, tr("Select Syncthing config file") + QStringLiteral(" - " APP_NAME));
|
||||
if (!configFilePath.isEmpty() && applySyncthingConfiguration(configFilePath)) {
|
||||
QSettings(QSettings::IniFormat, QSettings::UserScope, QStringLiteral(PROJECT_NAME))
|
||||
.setValue(QStringLiteral("syncthingConfigPath"), m_configFilePath = configFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
bool SyncthingFileItemActionStaticData::applySyncthingConfiguration(const QString &syncthingConfigFilePath)
|
||||
{
|
||||
clearCurrentError();
|
||||
|
||||
// check for empty path
|
||||
if (syncthingConfigFilePath.isEmpty()) {
|
||||
setCurrentError(tr("Syncthing config file can not be automatically located"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// load Syncthing config
|
||||
SyncthingConfig config;
|
||||
if (!config.restore(syncthingConfigFilePath)) {
|
||||
auto errorMessage = tr("Unable to load Syncthing config from \"%1\"").arg(syncthingConfigFilePath);
|
||||
if (!m_configFilePath.isEmpty() && m_configFilePath != syncthingConfigFilePath) {
|
||||
errorMessage += QChar('\n');
|
||||
errorMessage += tr("(still using config from \"%1\")").arg(m_configFilePath);
|
||||
}
|
||||
setCurrentError(errorMessage);
|
||||
return false;
|
||||
}
|
||||
cerr << "Syncthing config loaded from \"" << syncthingConfigFilePath.toLocal8Bit().data() << "\"" << endl;
|
||||
|
||||
// make connection settings
|
||||
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;
|
||||
}
|
||||
m_connection.setAutoReconnectInterval(reconnectInterval);
|
||||
m_connection.reconnect(settings);
|
||||
return true;
|
||||
}
|
||||
|
||||
void SyncthingFileItemActionStaticData::setCurrentError(const QString ¤tError)
|
||||
{
|
||||
if (m_currentError == currentError) {
|
||||
return;
|
||||
}
|
||||
const bool hadError = hasError();
|
||||
m_currentError = currentError;
|
||||
if (hadError != hasError()) {
|
||||
emit hasErrorChanged(hasError());
|
||||
}
|
||||
emit currentErrorChanged(m_currentError);
|
||||
}
|
||||
|
||||
SyncthingFileItemActionStaticData SyncthingFileItemAction::s_data;
|
||||
|
||||
SyncthingFileItemAction::SyncthingFileItemAction(QObject *parent, const QVariantList &)
|
||||
|
|
|
@ -1,158 +1,12 @@
|
|||
#ifndef SYNCTHINGFILEITEMACTION_H
|
||||
#define SYNCTHINGFILEITEMACTION_H
|
||||
|
||||
#include "../connector/syncthingconnection.h"
|
||||
#include "./syncthingfileitemactionstaticdata.h"
|
||||
|
||||
#include <KAbstractFileItemActionPlugin>
|
||||
#include <KFileItemListProperties>
|
||||
|
||||
#include <QAction>
|
||||
#include <QWidget>
|
||||
#include <QWidgetAction>
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QLabel)
|
||||
|
||||
class KFileItemListProperties;
|
||||
|
||||
class SyncthingMenuAction : public QAction {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SyncthingMenuAction(const KFileItemListProperties &properties = KFileItemListProperties(),
|
||||
const QList<QAction *> &actions = QList<QAction *>(), QWidget *parentWidget = nullptr);
|
||||
|
||||
public Q_SLOTS:
|
||||
void updateStatus(Data::SyncthingStatus status);
|
||||
|
||||
private:
|
||||
KFileItemListProperties m_properties;
|
||||
};
|
||||
|
||||
class SyncthingInfoAction;
|
||||
|
||||
class SyncthingInfoWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SyncthingInfoWidget(const SyncthingInfoAction *action, QWidget *parent = nullptr);
|
||||
|
||||
private Q_SLOTS:
|
||||
void updateFromSender();
|
||||
void updateFromAction(const SyncthingInfoAction *action);
|
||||
|
||||
private:
|
||||
QLabel *const m_textLabel;
|
||||
QLabel *const m_iconLabel;
|
||||
};
|
||||
|
||||
class SyncthingInfoAction : public QWidgetAction {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SyncthingInfoAction(QObject *parent = nullptr);
|
||||
|
||||
protected:
|
||||
QWidget *createWidget(QWidget *parent) override;
|
||||
};
|
||||
|
||||
class SyncthingDirActions : public QObject {
|
||||
Q_OBJECT
|
||||
friend QList<QAction *> &operator<<(QList<QAction *> &, SyncthingDirActions &);
|
||||
|
||||
public:
|
||||
explicit SyncthingDirActions(const Data::SyncthingDir &dir, QObject *parent = nullptr);
|
||||
|
||||
public Q_SLOTS:
|
||||
void updateStatus(const std::vector<Data::SyncthingDir> &dirs);
|
||||
bool updateStatus(const Data::SyncthingDir &dir);
|
||||
|
||||
private:
|
||||
QString m_dirId;
|
||||
QAction m_infoAction;
|
||||
SyncthingInfoAction m_statusAction;
|
||||
SyncthingInfoAction m_globalStatusAction;
|
||||
SyncthingInfoAction m_localStatusAction;
|
||||
SyncthingInfoAction m_lastScanAction;
|
||||
SyncthingInfoAction m_rescanIntervalAction;
|
||||
SyncthingInfoAction m_errorsAction;
|
||||
};
|
||||
|
||||
QList<QAction *> &operator<<(QList<QAction *> &actions, SyncthingDirActions &dirActions);
|
||||
|
||||
class SyncthingFileItemActionStaticData : public QObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString configPath READ configPath)
|
||||
Q_PROPERTY(QString currentError READ currentError WRITE setCurrentError NOTIFY currentErrorChanged RESET clearCurrentError)
|
||||
Q_PROPERTY(bool hasError READ hasError NOTIFY hasErrorChanged)
|
||||
Q_PROPERTY(bool initialized READ isInitialized)
|
||||
|
||||
public:
|
||||
explicit SyncthingFileItemActionStaticData();
|
||||
~SyncthingFileItemActionStaticData();
|
||||
Data::SyncthingConnection &connection();
|
||||
const Data::SyncthingConnection &connection() const;
|
||||
const QString &configPath() const;
|
||||
const QString ¤tError() const;
|
||||
bool hasError() const;
|
||||
bool isInitialized() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
void initialize();
|
||||
bool applySyncthingConfiguration(const QString &syncthingConfigFilePath);
|
||||
void logConnectionStatus();
|
||||
void logConnectionError(const QString &errorMessage, Data::SyncthingErrorCategory errorCategory);
|
||||
void rescanDir(const QString &dirId, const QString &relpath = QString());
|
||||
static void showAboutDialog();
|
||||
void selectSyncthingConfig();
|
||||
void setCurrentError(const QString ¤tError);
|
||||
void clearCurrentError();
|
||||
|
||||
Q_SIGNALS:
|
||||
void currentErrorChanged(const QString &error);
|
||||
void hasErrorChanged(bool hasError);
|
||||
|
||||
private:
|
||||
Data::SyncthingConnection m_connection;
|
||||
QString m_configFilePath;
|
||||
QString m_currentError;
|
||||
bool m_initialized;
|
||||
};
|
||||
|
||||
inline Data::SyncthingConnection &SyncthingFileItemActionStaticData::connection()
|
||||
{
|
||||
return m_connection;
|
||||
}
|
||||
|
||||
inline const Data::SyncthingConnection &SyncthingFileItemActionStaticData::connection() const
|
||||
{
|
||||
return m_connection;
|
||||
}
|
||||
|
||||
inline const QString &SyncthingFileItemActionStaticData::configPath() const
|
||||
{
|
||||
return m_configFilePath;
|
||||
}
|
||||
|
||||
inline const QString &SyncthingFileItemActionStaticData::currentError() const
|
||||
{
|
||||
return m_currentError;
|
||||
}
|
||||
|
||||
inline bool SyncthingFileItemActionStaticData::hasError() const
|
||||
{
|
||||
return !currentError().isEmpty();
|
||||
}
|
||||
|
||||
inline bool SyncthingFileItemActionStaticData::isInitialized() const
|
||||
{
|
||||
return m_initialized;
|
||||
}
|
||||
|
||||
inline void SyncthingFileItemActionStaticData::clearCurrentError()
|
||||
{
|
||||
m_currentError.clear();
|
||||
}
|
||||
|
||||
class SyncthingFileItemAction : public KAbstractFileItemActionPlugin {
|
||||
Q_OBJECT
|
||||
|
||||
|
|
|
@ -0,0 +1,160 @@
|
|||
#include "./syncthingfileitemactionstaticdata.h"
|
||||
|
||||
#include "../model/syncthingicons.h"
|
||||
|
||||
#include "../connector/syncthingconfig.h"
|
||||
#include "../connector/syncthingconnection.h"
|
||||
#include "../connector/syncthingconnectionsettings.h"
|
||||
|
||||
#include <c++utilities/application/argumentparser.h>
|
||||
|
||||
#include <qtutilities/aboutdialog/aboutdialog.h>
|
||||
#include <qtutilities/resources/resources.h>
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QSettings>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "resources/config.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace Dialogs;
|
||||
using namespace Data;
|
||||
|
||||
SyncthingFileItemActionStaticData::SyncthingFileItemActionStaticData()
|
||||
: m_initialized(false)
|
||||
{
|
||||
}
|
||||
|
||||
void SyncthingFileItemActionStaticData::initialize()
|
||||
{
|
||||
if (m_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOAD_QT_TRANSLATIONS;
|
||||
|
||||
// load settings
|
||||
const QSettings settingsFile(QSettings::IniFormat, QSettings::UserScope, QStringLiteral(PROJECT_NAME));
|
||||
|
||||
// determine path of Syncthing config file
|
||||
m_configFilePath = [&] {
|
||||
const QByteArray configPathFromEnv(qgetenv("KIO_SYNCTHING_CONFIG_PATH"));
|
||||
if (!configPathFromEnv.isEmpty()) {
|
||||
return QString::fromLocal8Bit(configPathFromEnv);
|
||||
}
|
||||
const QString configPathFromSettings = settingsFile.value(QStringLiteral("syncthingConfigPath")).toString();
|
||||
if (!configPathFromSettings.isEmpty()) {
|
||||
return configPathFromSettings;
|
||||
}
|
||||
return SyncthingConfig::locateConfigFile();
|
||||
}();
|
||||
applySyncthingConfiguration(m_configFilePath);
|
||||
|
||||
// prevent unnecessary API calls (for the purpose of the context menu)
|
||||
m_connection.disablePolling();
|
||||
|
||||
// connect Signals & Slots for logging
|
||||
connect(&m_connection, &SyncthingConnection::error, this, &SyncthingFileItemActionStaticData::logConnectionError);
|
||||
if (qEnvironmentVariableIsSet("KIO_SYNCTHING_LOG_STATUS")) {
|
||||
connect(&m_connection, &SyncthingConnection::statusChanged, this, &SyncthingFileItemActionStaticData::logConnectionStatus);
|
||||
}
|
||||
|
||||
m_initialized = true;
|
||||
}
|
||||
|
||||
void SyncthingFileItemActionStaticData::logConnectionStatus()
|
||||
{
|
||||
cerr << "Syncthing connection status changed to: " << m_connection.statusText().toLocal8Bit().data() << endl;
|
||||
}
|
||||
|
||||
void SyncthingFileItemActionStaticData::logConnectionError(const QString &errorMessage, SyncthingErrorCategory errorCategory)
|
||||
{
|
||||
switch (errorCategory) {
|
||||
case SyncthingErrorCategory::Parsing:
|
||||
case SyncthingErrorCategory::SpecificRequest:
|
||||
QMessageBox::critical(nullptr, tr("Syncthing connection error"), errorMessage);
|
||||
break;
|
||||
default:
|
||||
cerr << "Syncthing connection error: " << errorMessage.toLocal8Bit().data() << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void SyncthingFileItemActionStaticData::rescanDir(const QString &dirId, const QString &relpath)
|
||||
{
|
||||
m_connection.rescan(dirId, relpath);
|
||||
}
|
||||
|
||||
void SyncthingFileItemActionStaticData::showAboutDialog()
|
||||
{
|
||||
auto *aboutDialog = new AboutDialog(nullptr, QStringLiteral(APP_NAME), QStringLiteral(APP_AUTHOR "\nSyncthing icons from Syncthing project"),
|
||||
QStringLiteral(APP_VERSION), ApplicationUtilities::dependencyVersions2, 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->setAttribute(Qt::WA_DeleteOnClose);
|
||||
aboutDialog->show();
|
||||
}
|
||||
|
||||
void SyncthingFileItemActionStaticData::selectSyncthingConfig()
|
||||
{
|
||||
const auto configFilePath = QFileDialog::getOpenFileName(nullptr, tr("Select Syncthing config file") + QStringLiteral(" - " APP_NAME));
|
||||
if (!configFilePath.isEmpty() && applySyncthingConfiguration(configFilePath)) {
|
||||
QSettings(QSettings::IniFormat, QSettings::UserScope, QStringLiteral(PROJECT_NAME))
|
||||
.setValue(QStringLiteral("syncthingConfigPath"), m_configFilePath = configFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
bool SyncthingFileItemActionStaticData::applySyncthingConfiguration(const QString &syncthingConfigFilePath)
|
||||
{
|
||||
clearCurrentError();
|
||||
|
||||
// check for empty path
|
||||
if (syncthingConfigFilePath.isEmpty()) {
|
||||
setCurrentError(tr("Syncthing config file can not be automatically located"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// load Syncthing config
|
||||
SyncthingConfig config;
|
||||
if (!config.restore(syncthingConfigFilePath)) {
|
||||
auto errorMessage = tr("Unable to load Syncthing config from \"%1\"").arg(syncthingConfigFilePath);
|
||||
if (!m_configFilePath.isEmpty() && m_configFilePath != syncthingConfigFilePath) {
|
||||
errorMessage += QChar('\n');
|
||||
errorMessage += tr("(still using config from \"%1\")").arg(m_configFilePath);
|
||||
}
|
||||
setCurrentError(errorMessage);
|
||||
return false;
|
||||
}
|
||||
cerr << "Syncthing config loaded from \"" << syncthingConfigFilePath.toLocal8Bit().data() << "\"" << endl;
|
||||
|
||||
// make connection settings
|
||||
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;
|
||||
}
|
||||
m_connection.setAutoReconnectInterval(reconnectInterval);
|
||||
m_connection.reconnect(settings);
|
||||
return true;
|
||||
}
|
||||
|
||||
void SyncthingFileItemActionStaticData::setCurrentError(const QString ¤tError)
|
||||
{
|
||||
if (m_currentError == currentError) {
|
||||
return;
|
||||
}
|
||||
const bool hadError = hasError();
|
||||
m_currentError = currentError;
|
||||
if (hadError != hasError()) {
|
||||
emit hasErrorChanged(hasError());
|
||||
}
|
||||
emit currentErrorChanged(m_currentError);
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
#ifndef SYNCTHINGFILEITEMACTIONSTATICDATA_H
|
||||
#define SYNCTHINGFILEITEMACTIONSTATICDATA_H
|
||||
|
||||
#include "../connector/syncthingconnection.h"
|
||||
|
||||
class SyncthingFileItemActionStaticData : public QObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString configPath READ configPath)
|
||||
Q_PROPERTY(QString currentError READ currentError WRITE setCurrentError NOTIFY currentErrorChanged RESET clearCurrentError)
|
||||
Q_PROPERTY(bool hasError READ hasError NOTIFY hasErrorChanged)
|
||||
Q_PROPERTY(bool initialized READ isInitialized)
|
||||
|
||||
public:
|
||||
explicit SyncthingFileItemActionStaticData();
|
||||
Data::SyncthingConnection &connection();
|
||||
const Data::SyncthingConnection &connection() const;
|
||||
const QString &configPath() const;
|
||||
const QString ¤tError() const;
|
||||
bool hasError() const;
|
||||
bool isInitialized() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
void initialize();
|
||||
bool applySyncthingConfiguration(const QString &syncthingConfigFilePath);
|
||||
void logConnectionStatus();
|
||||
void logConnectionError(const QString &errorMessage, Data::SyncthingErrorCategory errorCategory);
|
||||
void rescanDir(const QString &dirId, const QString &relpath = QString());
|
||||
static void showAboutDialog();
|
||||
void selectSyncthingConfig();
|
||||
void setCurrentError(const QString ¤tError);
|
||||
void clearCurrentError();
|
||||
|
||||
Q_SIGNALS:
|
||||
void currentErrorChanged(const QString &error);
|
||||
void hasErrorChanged(bool hasError);
|
||||
|
||||
private:
|
||||
Data::SyncthingConnection m_connection;
|
||||
QString m_configFilePath;
|
||||
QString m_currentError;
|
||||
bool m_initialized;
|
||||
};
|
||||
|
||||
inline Data::SyncthingConnection &SyncthingFileItemActionStaticData::connection()
|
||||
{
|
||||
return m_connection;
|
||||
}
|
||||
|
||||
inline const Data::SyncthingConnection &SyncthingFileItemActionStaticData::connection() const
|
||||
{
|
||||
return m_connection;
|
||||
}
|
||||
|
||||
inline const QString &SyncthingFileItemActionStaticData::configPath() const
|
||||
{
|
||||
return m_configFilePath;
|
||||
}
|
||||
|
||||
inline const QString &SyncthingFileItemActionStaticData::currentError() const
|
||||
{
|
||||
return m_currentError;
|
||||
}
|
||||
|
||||
inline bool SyncthingFileItemActionStaticData::hasError() const
|
||||
{
|
||||
return !currentError().isEmpty();
|
||||
}
|
||||
|
||||
inline bool SyncthingFileItemActionStaticData::isInitialized() const
|
||||
{
|
||||
return m_initialized;
|
||||
}
|
||||
|
||||
inline void SyncthingFileItemActionStaticData::clearCurrentError()
|
||||
{
|
||||
m_currentError.clear();
|
||||
}
|
||||
|
||||
#endif // SYNCTHINGFILEITEMACTIONSTATICDATA_H
|
|
@ -0,0 +1,45 @@
|
|||
#include "./syncthinginfoaction.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
|
||||
SyncthingInfoWidget::SyncthingInfoWidget(const SyncthingInfoAction *action, QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, m_textLabel(new QLabel(parent))
|
||||
, m_iconLabel(new QLabel(parent))
|
||||
{
|
||||
auto *const layout = new QHBoxLayout(parent);
|
||||
layout->setMargin(4);
|
||||
layout->setSpacing(5);
|
||||
m_iconLabel->setFixedWidth(16);
|
||||
m_iconLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
|
||||
layout->addWidget(m_iconLabel);
|
||||
layout->addWidget(m_textLabel);
|
||||
setLayout(layout);
|
||||
updateFromAction(action);
|
||||
connect(action, &QAction::changed, this, &SyncthingInfoWidget::updateFromSender);
|
||||
}
|
||||
|
||||
void SyncthingInfoWidget::updateFromSender()
|
||||
{
|
||||
updateFromAction(qobject_cast<const SyncthingInfoAction *>(QObject::sender()));
|
||||
}
|
||||
|
||||
void SyncthingInfoWidget::updateFromAction(const SyncthingInfoAction *action)
|
||||
{
|
||||
auto text(action->text());
|
||||
m_textLabel->setText(text.startsWith(QChar('&')) ? text.mid(1) : std::move(text));
|
||||
m_iconLabel->setPixmap(action->icon().pixmap(16));
|
||||
setVisible(action->isVisible());
|
||||
}
|
||||
|
||||
SyncthingInfoAction::SyncthingInfoAction(QObject *parent)
|
||||
: QWidgetAction(parent)
|
||||
{
|
||||
}
|
||||
|
||||
QWidget *SyncthingInfoAction::createWidget(QWidget *parent)
|
||||
{
|
||||
return new SyncthingInfoWidget(this, parent);
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
#ifndef SYNCTHINGINFOACTION_H
|
||||
#define SYNCTHINGINFOACTION_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QWidgetAction>
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QLabel)
|
||||
|
||||
class SyncthingInfoAction;
|
||||
|
||||
class SyncthingInfoWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SyncthingInfoWidget(const SyncthingInfoAction *action, QWidget *parent = nullptr);
|
||||
|
||||
private Q_SLOTS:
|
||||
void updateFromSender();
|
||||
void updateFromAction(const SyncthingInfoAction *action);
|
||||
|
||||
private:
|
||||
QLabel *const m_textLabel;
|
||||
QLabel *const m_iconLabel;
|
||||
};
|
||||
|
||||
class SyncthingInfoAction : public QWidgetAction {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SyncthingInfoAction(QObject *parent = nullptr);
|
||||
|
||||
protected:
|
||||
QWidget *createWidget(QWidget *parent) override;
|
||||
};
|
||||
|
||||
#endif // SYNCTHINGINFOACTION_H
|
|
@ -0,0 +1,49 @@
|
|||
#include "./syncthingmenuaction.h"
|
||||
#include "./syncthingfileitemaction.h"
|
||||
|
||||
#include "../model/syncthingicons.h"
|
||||
|
||||
#include "../connector/syncthingconnection.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QMenu>
|
||||
|
||||
using namespace Data;
|
||||
|
||||
SyncthingMenuAction::SyncthingMenuAction(const KFileItemListProperties &properties, const QList<QAction *> &actions, QWidget *parentWidget)
|
||||
: QAction(parentWidget)
|
||||
, m_properties(properties)
|
||||
{
|
||||
if (!actions.isEmpty()) {
|
||||
auto *menu = new QMenu(parentWidget);
|
||||
menu->addActions(actions);
|
||||
setMenu(menu);
|
||||
}
|
||||
updateStatus(SyncthingFileItemAction::staticData().connection().status());
|
||||
}
|
||||
|
||||
void SyncthingMenuAction::updateStatus(SyncthingStatus status)
|
||||
{
|
||||
if (status != SyncthingStatus::Disconnected && status != SyncthingStatus::Reconnecting && status != SyncthingStatus::BeingDestroyed) {
|
||||
setText(tr("Syncthing"));
|
||||
setIcon(statusIcons().scanninig);
|
||||
if (!menu()) {
|
||||
const QList<QAction *> actions = SyncthingFileItemAction::createActions(m_properties, parentWidget());
|
||||
if (!actions.isEmpty()) {
|
||||
auto *menu = new QMenu(parentWidget());
|
||||
menu->addActions(actions);
|
||||
setMenu(menu);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (status != SyncthingStatus::Reconnecting) {
|
||||
SyncthingFileItemAction::staticData().connection().connect();
|
||||
}
|
||||
setText(tr("Syncthing - connecting"));
|
||||
setIcon(statusIcons().disconnected);
|
||||
if (QMenu *menu = this->menu()) {
|
||||
setMenu(nullptr);
|
||||
delete menu;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
#ifndef SYNCTHINGMENUACTION_H
|
||||
#define SYNCTHINGMENUACTION_H
|
||||
|
||||
#include <KFileItemListProperties>
|
||||
|
||||
#include <QAction>
|
||||
|
||||
namespace Data {
|
||||
enum class SyncthingStatus;
|
||||
}
|
||||
|
||||
class SyncthingMenuAction : public QAction {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SyncthingMenuAction(const KFileItemListProperties &properties = KFileItemListProperties(),
|
||||
const QList<QAction *> &actions = QList<QAction *>(), QWidget *parentWidget = nullptr);
|
||||
|
||||
public Q_SLOTS:
|
||||
void updateStatus(Data::SyncthingStatus status);
|
||||
|
||||
private:
|
||||
KFileItemListProperties m_properties;
|
||||
};
|
||||
|
||||
#endif // SYNCTHINGMENUACTION_H
|
Loading…
Reference in New Issue