Make Dolphin integration configurable
* Allow to select Syncthing config file manually * Show error if config file can not be located
This commit is contained in:
parent
ba4012fb36
commit
882a2a5d35
|
@ -18,10 +18,12 @@
|
|||
#include <QAction>
|
||||
#include <QDir>
|
||||
#include <QEvent>
|
||||
#include <QFileDialog>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
#include <QSettings>
|
||||
#include <QWidget>
|
||||
|
||||
#include <functional>
|
||||
|
@ -63,7 +65,7 @@ SyncthingMenuAction::SyncthingMenuAction(const KFileItemListProperties &properti
|
|||
menu->addActions(actions);
|
||||
setMenu(menu);
|
||||
}
|
||||
updateStatus(SyncthingFileItemAction::connection().status());
|
||||
updateStatus(SyncthingFileItemAction::staticData().connection().status());
|
||||
}
|
||||
|
||||
void SyncthingMenuAction::updateStatus(SyncthingStatus status)
|
||||
|
@ -81,7 +83,7 @@ void SyncthingMenuAction::updateStatus(SyncthingStatus status)
|
|||
}
|
||||
} else {
|
||||
if (status != SyncthingStatus::Reconnecting) {
|
||||
SyncthingFileItemAction::connection().connect();
|
||||
SyncthingFileItemAction::staticData().connection().connect();
|
||||
}
|
||||
setText(tr("Syncthing - connecting"));
|
||||
setIcon(statusIcons().disconnected);
|
||||
|
@ -92,6 +94,36 @@ void SyncthingMenuAction::updateStatus(SyncthingStatus status)
|
|||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -99,19 +131,7 @@ SyncthingInfoAction::SyncthingInfoAction(QObject *parent)
|
|||
|
||||
QWidget *SyncthingInfoAction::createWidget(QWidget *parent)
|
||||
{
|
||||
auto *container = new QWidget(parent);
|
||||
auto *layout = new QHBoxLayout(parent);
|
||||
layout->setMargin(4);
|
||||
layout->setSpacing(5);
|
||||
auto *iconLabel = new QLabel(parent);
|
||||
iconLabel->setPixmap(icon().pixmap(16));
|
||||
iconLabel->setFixedWidth(16);
|
||||
iconLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
|
||||
layout->addWidget(iconLabel);
|
||||
auto *textLabel = new QLabel(text(), parent);
|
||||
layout->addWidget(textLabel);
|
||||
container->setLayout(layout);
|
||||
return container;
|
||||
return new SyncthingInfoWidget(this, parent);
|
||||
}
|
||||
|
||||
SyncthingDirActions::SyncthingDirActions(const SyncthingDir &dir, QObject *parent)
|
||||
|
@ -185,85 +205,58 @@ QList<QAction *> &operator<<(QList<QAction *> &actions, SyncthingDirActions &dir
|
|||
<< &dirActions.m_lastScanAction << &dirActions.m_rescanIntervalAction << &dirActions.m_errorsAction;
|
||||
}
|
||||
|
||||
SyncthingConnection SyncthingFileItemAction::s_connection;
|
||||
|
||||
SyncthingFileItemAction::SyncthingFileItemAction(QObject *parent, const QVariantList &)
|
||||
: KAbstractFileItemActionPlugin(parent)
|
||||
SyncthingFileItemActionStaticData::SyncthingFileItemActionStaticData()
|
||||
: m_initialized(false)
|
||||
{
|
||||
// skip initialization if not the first instantiation
|
||||
if (!s_connection.apiKey().isEmpty()) {
|
||||
}
|
||||
|
||||
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
|
||||
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;
|
||||
m_configFilePath = [&] {
|
||||
const QByteArray configPathFromEnv(qgetenv("KIO_SYNCTHING_CONFIG_PATH"));
|
||||
if (!configPathFromEnv.isEmpty()) {
|
||||
return QString::fromLocal8Bit(configPathFromEnv);
|
||||
}
|
||||
return;
|
||||
}
|
||||
cerr << "Syncthing config loaded from \"" << configPath.toLocal8Bit().data() << "\"" << endl;
|
||||
SyncthingConnectionSettings settings;
|
||||
settings.syncthingUrl = config.syncthingUrl();
|
||||
settings.apiKey.append(config.guiApiKey);
|
||||
const QString configPathFromSettings = settingsFile.value(QStringLiteral("syncthingConfigPath")).toString();
|
||||
if (!configPathFromSettings.isEmpty()) {
|
||||
return configPathFromSettings;
|
||||
}
|
||||
return SyncthingConfig::locateConfigFile();
|
||||
}();
|
||||
applySyncthingConfiguration(m_configFilePath);
|
||||
|
||||
// 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);
|
||||
// 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(&s_connection, &SyncthingConnection::statusChanged, &SyncthingFileItemAction::logConnectionStatus);
|
||||
}
|
||||
}
|
||||
|
||||
QList<QAction *> SyncthingFileItemAction::actions(const KFileItemListProperties &fileItemInfo, QWidget *parentWidget)
|
||||
{
|
||||
// handle case when not connected yet
|
||||
if (!s_connection.isConnected()) {
|
||||
s_connection.connect();
|
||||
auto *menuAction = new SyncthingMenuAction(fileItemInfo, QList<QAction *>(), parentWidget);
|
||||
connect(&s_connection, &SyncthingConnection::statusChanged, menuAction, &SyncthingMenuAction::updateStatus);
|
||||
return QList<QAction *>() << menuAction;
|
||||
connect(&m_connection, &SyncthingConnection::statusChanged, this, &SyncthingFileItemActionStaticData::logConnectionStatus);
|
||||
}
|
||||
|
||||
const QList<QAction *> actions = createActions(fileItemInfo, parentWidget);
|
||||
// don't show anything if no relevant actions could be determined
|
||||
if (actions.isEmpty()) {
|
||||
return actions;
|
||||
}
|
||||
|
||||
return QList<QAction *>() << new SyncthingMenuAction(fileItemInfo, actions, parentWidget);
|
||||
m_initialized = true;
|
||||
}
|
||||
|
||||
SyncthingConnection &SyncthingFileItemAction::connection()
|
||||
void SyncthingFileItemActionStaticData::logConnectionStatus()
|
||||
{
|
||||
return s_connection;
|
||||
cerr << "Syncthing connection status changed to: " << m_connection.statusText().toLocal8Bit().data() << endl;
|
||||
}
|
||||
|
||||
void SyncthingFileItemAction::logConnectionStatus()
|
||||
{
|
||||
cerr << "Syncthing connection status changed to: " << s_connection.statusText().toLocal8Bit().data() << endl;
|
||||
}
|
||||
|
||||
void SyncthingFileItemAction::logConnectionError(const QString &errorMessage, SyncthingErrorCategory errorCategory)
|
||||
void SyncthingFileItemActionStaticData::logConnectionError(const QString &errorMessage, SyncthingErrorCategory errorCategory)
|
||||
{
|
||||
switch (errorCategory) {
|
||||
case SyncthingErrorCategory::Parsing:
|
||||
|
@ -275,12 +268,12 @@ void SyncthingFileItemAction::logConnectionError(const QString &errorMessage, Sy
|
|||
}
|
||||
}
|
||||
|
||||
void SyncthingFileItemAction::rescanDir(const QString &dirId, const QString &relpath)
|
||||
void SyncthingFileItemActionStaticData::rescanDir(const QString &dirId, const QString &relpath)
|
||||
{
|
||||
s_connection.rescan(dirId, relpath);
|
||||
m_connection.rescan(dirId, relpath);
|
||||
}
|
||||
|
||||
void SyncthingFileItemAction::showAboutDialog()
|
||||
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),
|
||||
|
@ -291,15 +284,100 @@ void SyncthingFileItemAction::showAboutDialog()
|
|||
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 &)
|
||||
: KAbstractFileItemActionPlugin(parent)
|
||||
{
|
||||
s_data.initialize();
|
||||
}
|
||||
|
||||
QList<QAction *> SyncthingFileItemAction::actions(const KFileItemListProperties &fileItemInfo, QWidget *parentWidget)
|
||||
{
|
||||
// handle case when not connected yet
|
||||
if (!s_data.connection().isConnected()) {
|
||||
s_data.connection().connect();
|
||||
auto *menuAction = new SyncthingMenuAction(fileItemInfo, QList<QAction *>(), parentWidget);
|
||||
connect(&s_data.connection(), &SyncthingConnection::statusChanged, menuAction, &SyncthingMenuAction::updateStatus);
|
||||
return QList<QAction *>() << menuAction;
|
||||
}
|
||||
|
||||
const QList<QAction *> actions = createActions(fileItemInfo, parentWidget);
|
||||
// don't show anything if no relevant actions could be determined
|
||||
if (actions.isEmpty()) {
|
||||
return actions;
|
||||
}
|
||||
|
||||
return QList<QAction *>({ new SyncthingMenuAction(fileItemInfo, actions, parentWidget) });
|
||||
}
|
||||
|
||||
QList<QAction *> SyncthingFileItemAction::createActions(const KFileItemListProperties &fileItemInfo, QWidget *parentWidget)
|
||||
{
|
||||
QList<QAction *> actions;
|
||||
|
||||
// check whether any directories are known
|
||||
const auto &dirs = s_connection.dirInfo();
|
||||
if (dirs.empty()) {
|
||||
return actions;
|
||||
}
|
||||
auto &data = s_data;
|
||||
auto &connection = data.connection();
|
||||
const auto &dirs = connection.dirInfo();
|
||||
|
||||
// get all paths
|
||||
QStringList paths;
|
||||
|
@ -307,7 +385,7 @@ QList<QAction *> SyncthingFileItemAction::createActions(const KFileItemListPrope
|
|||
for (const KFileItem &item : fileItemInfo.items()) {
|
||||
if (!item.isLocalFile()) {
|
||||
// don't show any actions when remote files are selected
|
||||
return QList<QAction *>();
|
||||
return actions;
|
||||
}
|
||||
paths << item.localPath();
|
||||
}
|
||||
|
@ -316,7 +394,7 @@ QList<QAction *> SyncthingFileItemAction::createActions(const KFileItemListPrope
|
|||
QList<const SyncthingDir *> detectedDirs;
|
||||
QList<const SyncthingDir *> containingDirs;
|
||||
QList<SyncthingItem> detectedItems;
|
||||
const SyncthingDir *lastDir;
|
||||
const SyncthingDir *lastDir = nullptr;
|
||||
for (const SyncthingDir &dir : dirs) {
|
||||
QStringRef dirPath(dir.pathWithoutTrailingSlash());
|
||||
for (const QString &path : paths) {
|
||||
|
@ -336,14 +414,15 @@ QList<QAction *> SyncthingFileItemAction::createActions(const KFileItemListPrope
|
|||
}
|
||||
|
||||
// add actions for the selected items itself
|
||||
actions.reserve(32);
|
||||
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()) {
|
||||
if (connection.isConnected()) {
|
||||
for (const SyncthingItem &item : detectedItems) {
|
||||
connect(actions.back(), &QAction::triggered, bind(&SyncthingFileItemAction::rescanDir, item.dir->id, item.path));
|
||||
connect(actions.back(), &QAction::triggered, bind(&SyncthingFileItemActionStaticData::rescanDir, &data, item.dir->id, item.path));
|
||||
}
|
||||
} else {
|
||||
actions.back()->setEnabled(false);
|
||||
|
@ -355,9 +434,9 @@ QList<QAction *> SyncthingFileItemAction::createActions(const KFileItemListPrope
|
|||
// rescan item
|
||||
actions << new QAction(QIcon::fromTheme(QStringLiteral("folder-sync")),
|
||||
detectedDirs.size() == 1 ? tr("Rescan %1").arg(detectedDirs.front()->displayName()) : tr("Rescan selected directories"), parentWidget);
|
||||
if (s_connection.isConnected()) {
|
||||
if (connection.isConnected()) {
|
||||
for (const SyncthingDir *dir : detectedDirs) {
|
||||
connect(actions.back(), &QAction::triggered, bind(&SyncthingFileItemAction::rescanDir, dir->id, QString()));
|
||||
connect(actions.back(), &QAction::triggered, bind(&SyncthingFileItemActionStaticData::rescanDir, &data, dir->id, QString()));
|
||||
containingDirs.removeAll(dir);
|
||||
}
|
||||
} else {
|
||||
|
@ -383,9 +462,9 @@ QList<QAction *> SyncthingFileItemAction::createActions(const KFileItemListPrope
|
|||
actions << new QAction(QIcon::fromTheme(QStringLiteral("media-playback-pause")),
|
||||
detectedDirs.size() == 1 ? tr("Pause %1").arg(detectedDirs.front()->displayName()) : tr("Pause selected directories"), parentWidget);
|
||||
}
|
||||
if (s_connection.isConnected()) {
|
||||
if (connection.isConnected()) {
|
||||
connect(actions.back(), &QAction::triggered,
|
||||
bind(isPaused ? &SyncthingConnection::resumeDirectories : &SyncthingConnection::pauseDirectories, &s_connection, ids));
|
||||
bind(isPaused ? &SyncthingConnection::resumeDirectories : &SyncthingConnection::pauseDirectories, &connection, ids));
|
||||
} else {
|
||||
actions.back()->setEnabled(false);
|
||||
}
|
||||
|
@ -397,9 +476,9 @@ QList<QAction *> SyncthingFileItemAction::createActions(const KFileItemListPrope
|
|||
actions << new QAction(QIcon::fromTheme(QStringLiteral("folder-sync")),
|
||||
containingDirs.size() == 1 ? tr("Rescan %1").arg(containingDirs.front()->displayName()) : tr("Rescan containing directories"),
|
||||
parentWidget);
|
||||
if (s_connection.isConnected()) {
|
||||
if (connection.isConnected()) {
|
||||
for (const SyncthingDir *dir : containingDirs) {
|
||||
connect(actions.back(), &QAction::triggered, bind(&SyncthingFileItemAction::rescanDir, dir->id, QString()));
|
||||
connect(actions.back(), &QAction::triggered, bind(&SyncthingFileItemActionStaticData::rescanDir, &data, dir->id, QString()));
|
||||
}
|
||||
} else {
|
||||
actions.back()->setEnabled(false);
|
||||
|
@ -425,35 +504,50 @@ QList<QAction *> SyncthingFileItemAction::createActions(const KFileItemListPrope
|
|||
containingDirs.size() == 1 ? tr("Pause %1").arg(containingDirs.front()->displayName()) : tr("Pause containing directories"),
|
||||
parentWidget);
|
||||
}
|
||||
if (s_connection.isConnected()) {
|
||||
if (connection.isConnected()) {
|
||||
connect(actions.back(), &QAction::triggered,
|
||||
bind(isPaused ? &SyncthingConnection::resumeDirectories : &SyncthingConnection::pauseDirectories, &s_connection, ids));
|
||||
bind(isPaused ? &SyncthingConnection::resumeDirectories : &SyncthingConnection::pauseDirectories, &connection, ids));
|
||||
} else {
|
||||
actions.back()->setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
// don't add any further actions if no relevant actions could be determined so far
|
||||
if (actions.isEmpty()) {
|
||||
return actions;
|
||||
}
|
||||
|
||||
// add actions to show further information about directory if the selection is only about one particular Syncthing dir
|
||||
if (detectedDirs.size() + containingDirs.size() == 1) {
|
||||
if (lastDir && detectedDirs.size() + containingDirs.size() == 1) {
|
||||
auto *statusActions = new SyncthingDirActions(*lastDir, parentWidget);
|
||||
connect(&s_connection, &SyncthingConnection::newDirs, statusActions,
|
||||
connect(&connection, &SyncthingConnection::newDirs, statusActions,
|
||||
static_cast<void (SyncthingDirActions::*)(const vector<SyncthingDir> &)>(&SyncthingDirActions::updateStatus));
|
||||
connect(&s_connection, &SyncthingConnection::dirStatusChanged, statusActions,
|
||||
connect(&connection, &SyncthingConnection::dirStatusChanged, statusActions,
|
||||
static_cast<bool (SyncthingDirActions::*)(const SyncthingDir &)>(&SyncthingDirActions::updateStatus));
|
||||
actions << *statusActions;
|
||||
}
|
||||
|
||||
// add separator
|
||||
if (!actions.isEmpty()) {
|
||||
QAction *const separator = new QAction(parentWidget);
|
||||
separator->setSeparator(true);
|
||||
actions << separator;
|
||||
}
|
||||
|
||||
// add error action
|
||||
QAction *const errorAction = new SyncthingInfoAction(parentWidget);
|
||||
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;
|
||||
|
||||
// add config file selection
|
||||
QAction *const configFileAction = new QAction(QIcon::fromTheme(QStringLiteral("settings-configure")), tr("Select Syncthing config ..."));
|
||||
connect(configFileAction, &QAction::triggered, &data, &SyncthingFileItemActionStaticData::selectSyncthingConfig);
|
||||
actions << configFileAction;
|
||||
|
||||
// about about action
|
||||
QAction *separator = new QAction(parentWidget);
|
||||
separator->setSeparator(true);
|
||||
QAction *aboutAction = new QAction(QIcon::fromTheme(QStringLiteral("help-about")), tr("About"));
|
||||
connect(aboutAction, &QAction::triggered, &SyncthingFileItemAction::showAboutDialog);
|
||||
actions << separator << aboutAction;
|
||||
QAction *const aboutAction = new QAction(QIcon::fromTheme(QStringLiteral("help-about")), tr("About"));
|
||||
connect(aboutAction, &QAction::triggered, &SyncthingFileItemActionStaticData::showAboutDialog);
|
||||
actions << aboutAction;
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
|
|
@ -6,9 +6,11 @@
|
|||
#include <KAbstractFileItemActionPlugin>
|
||||
#include <KFileItemListProperties>
|
||||
|
||||
#include <QAction>
|
||||
#include <QWidget>
|
||||
#include <QWidgetAction>
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QWidget)
|
||||
QT_FORWARD_DECLARE_CLASS(QLabel)
|
||||
|
||||
class KFileItemListProperties;
|
||||
|
||||
|
@ -16,8 +18,8 @@ class SyncthingMenuAction : public QAction {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SyncthingMenuAction(const KFileItemListProperties &properties = KFileItemListProperties(), const QList<QAction *> &actions = QList<QAction *>(),
|
||||
QWidget *parentWidget = nullptr);
|
||||
explicit SyncthingMenuAction(const KFileItemListProperties &properties = KFileItemListProperties(),
|
||||
const QList<QAction *> &actions = QList<QAction *>(), QWidget *parentWidget = nullptr);
|
||||
|
||||
public Q_SLOTS:
|
||||
void updateStatus(Data::SyncthingStatus status);
|
||||
|
@ -26,11 +28,28 @@ 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:
|
||||
SyncthingInfoAction(QObject *parent = nullptr);
|
||||
explicit SyncthingInfoAction(QObject *parent = nullptr);
|
||||
|
||||
protected:
|
||||
QWidget *createWidget(QWidget *parent) override;
|
||||
|
@ -41,7 +60,7 @@ class SyncthingDirActions : public QObject {
|
|||
friend QList<QAction *> &operator<<(QList<QAction *> &, SyncthingDirActions &);
|
||||
|
||||
public:
|
||||
SyncthingDirActions(const Data::SyncthingDir &dir, QObject *parent = nullptr);
|
||||
explicit SyncthingDirActions(const Data::SyncthingDir &dir, QObject *parent = nullptr);
|
||||
|
||||
public Q_SLOTS:
|
||||
void updateStatus(const std::vector<Data::SyncthingDir> &dirs);
|
||||
|
@ -60,23 +79,96 @@ private:
|
|||
|
||||
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
|
||||
|
||||
public:
|
||||
SyncthingFileItemAction(QObject *parent, const QVariantList &args);
|
||||
QList<QAction *> actions(const KFileItemListProperties &fileItemInfo, QWidget *parentWidget) override;
|
||||
static Data::SyncthingConnection &connection();
|
||||
static QList<QAction *> createActions(const KFileItemListProperties &fileItemInfo, QWidget *parentWidget);
|
||||
|
||||
private Q_SLOTS:
|
||||
static void logConnectionStatus();
|
||||
static void logConnectionError(const QString &errorMessage, Data::SyncthingErrorCategory errorCategory);
|
||||
static void rescanDir(const QString &dirId, const QString &relpath = QString());
|
||||
static void showAboutDialog();
|
||||
static SyncthingFileItemActionStaticData &staticData();
|
||||
|
||||
private:
|
||||
static Data::SyncthingConnection s_connection;
|
||||
static SyncthingFileItemActionStaticData s_data;
|
||||
};
|
||||
|
||||
inline SyncthingFileItemActionStaticData &SyncthingFileItemAction::staticData()
|
||||
{
|
||||
return s_data;
|
||||
}
|
||||
|
||||
#endif // SYNCTHINGFILEITEMACTION_H
|
||||
|
|
Loading…
Reference in New Issue