2015-09-12 11:38:44 +01:00
|
|
|
|
/**
|
2020-08-17 10:48:10 +01:00
|
|
|
|
* SPDX-FileCopyrightText: 2015 David Edmundson <davidedmundson@kde.org>
|
2015-09-12 11:38:44 +01:00
|
|
|
|
*
|
2020-08-17 10:48:10 +01:00
|
|
|
|
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
2015-09-12 11:38:44 +01:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "runcommand_config.h"
|
|
|
|
|
|
2022-09-10 22:23:52 +01:00
|
|
|
|
#include <QDebug>
|
2015-09-12 11:38:44 +01:00
|
|
|
|
#include <QHBoxLayout>
|
2015-09-12 12:51:09 +01:00
|
|
|
|
#include <QHeaderView>
|
2022-09-10 22:23:52 +01:00
|
|
|
|
#include <QJsonDocument>
|
2023-03-30 00:08:03 +01:00
|
|
|
|
#include <QJsonArray>
|
|
|
|
|
#include <QFileDialog>
|
2018-03-24 18:57:40 +00:00
|
|
|
|
#include <QMenu>
|
2022-09-10 22:23:52 +01:00
|
|
|
|
#include <QPushButton>
|
2015-09-12 11:38:44 +01:00
|
|
|
|
#include <QStandardItemModel>
|
2020-07-05 14:45:28 +01:00
|
|
|
|
#include <QStandardPaths>
|
2022-09-10 22:23:52 +01:00
|
|
|
|
#include <QTableView>
|
|
|
|
|
#include <QUuid>
|
2015-09-12 11:38:44 +01:00
|
|
|
|
|
|
|
|
|
#include <KLocalizedString>
|
|
|
|
|
#include <KPluginFactory>
|
|
|
|
|
|
2019-06-09 16:28:49 +01:00
|
|
|
|
#include <dbushelper.h>
|
2018-08-01 10:10:14 +01:00
|
|
|
|
|
2015-09-12 11:38:44 +01:00
|
|
|
|
K_PLUGIN_FACTORY(ShareConfigFactory, registerPlugin<RunCommandConfig>();)
|
|
|
|
|
|
2022-09-10 22:23:52 +01:00
|
|
|
|
RunCommandConfig::RunCommandConfig(QWidget *parent, const QVariantList &args)
|
2022-03-21 17:03:45 +00:00
|
|
|
|
: KdeConnectPluginKcm(parent, args, QStringLiteral("kdeconnect_runcommand"))
|
2015-09-12 11:38:44 +01:00
|
|
|
|
{
|
2020-07-05 14:52:17 +01:00
|
|
|
|
// The qdbus executable name is different on some systems
|
2020-07-05 14:45:28 +01:00
|
|
|
|
QString qdbusExe = QStringLiteral("qdbus-qt5");
|
|
|
|
|
if (QStandardPaths::findExecutable(qdbusExe).isEmpty()) {
|
|
|
|
|
qdbusExe = QStringLiteral("qdbus");
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-10 22:23:52 +01:00
|
|
|
|
QMenu *defaultMenu = new QMenu(this);
|
2021-06-12 11:52:31 +01:00
|
|
|
|
|
|
|
|
|
#ifdef Q_OS_WIN
|
2021-06-13 08:56:51 +01:00
|
|
|
|
addSuggestedCommand(defaultMenu, i18n("Schedule a shutdown"), QStringLiteral("shutdown /s /t 60"));
|
2021-06-12 11:52:31 +01:00
|
|
|
|
addSuggestedCommand(defaultMenu, i18n("Shutdown now"), QStringLiteral("shutdown /s /t 0"));
|
|
|
|
|
addSuggestedCommand(defaultMenu, i18n("Cancel last shutdown"), QStringLiteral("shutdown /a"));
|
|
|
|
|
addSuggestedCommand(defaultMenu, i18n("Schedule a reboot"), QStringLiteral("shutdown /r /t 60"));
|
|
|
|
|
addSuggestedCommand(defaultMenu, i18n("Suspend"), QStringLiteral("rundll32.exe powrprof.dll,SetSuspendState 0,1,0"));
|
|
|
|
|
addSuggestedCommand(defaultMenu, i18n("Lock Screen"), QStringLiteral("rundll32.exe user32.dll,LockWorkStation"));
|
2022-09-10 22:23:52 +01:00
|
|
|
|
addSuggestedCommand(
|
|
|
|
|
defaultMenu,
|
|
|
|
|
i18n("Say Hello"),
|
|
|
|
|
QStringLiteral("PowerShell -Command \"Add-Type –AssemblyName System.Speech; (New-Object System.Speech.Synthesis.SpeechSynthesizer).Speak('hello');\""));
|
2021-06-12 11:52:31 +01:00
|
|
|
|
#else
|
2023-03-26 23:12:43 +01:00
|
|
|
|
addSuggestedCommand(defaultMenu, i18n("Shutdown"), QStringLiteral("systemctl poweroff"));
|
|
|
|
|
addSuggestedCommand(defaultMenu, i18n("Reboot"), QStringLiteral("systemctl reboot"));
|
|
|
|
|
addSuggestedCommand(defaultMenu, i18n("Suspend"), QStringLiteral("systemctl suspend"));
|
2022-09-10 22:23:52 +01:00
|
|
|
|
addSuggestedCommand(
|
|
|
|
|
defaultMenu,
|
|
|
|
|
i18n("Maximum Brightness"),
|
|
|
|
|
QStringLiteral("%0 org.kde.Solid.PowerManagement /org/kde/Solid/PowerManagement/Actions/BrightnessControl "
|
|
|
|
|
"org.kde.Solid.PowerManagement.Actions.BrightnessControl.setBrightness `%0 org.kde.Solid.PowerManagement "
|
|
|
|
|
"/org/kde/Solid/PowerManagement/Actions/BrightnessControl org.kde.Solid.PowerManagement.Actions.BrightnessControl.brightnessMax`")
|
|
|
|
|
.arg(qdbusExe));
|
2018-05-26 22:05:04 +01:00
|
|
|
|
addSuggestedCommand(defaultMenu, i18n("Lock Screen"), QStringLiteral("loginctl lock-session"));
|
|
|
|
|
addSuggestedCommand(defaultMenu, i18n("Unlock Screen"), QStringLiteral("loginctl unlock-session"));
|
2020-07-05 14:45:28 +01:00
|
|
|
|
addSuggestedCommand(defaultMenu, i18n("Close All Vaults"), QStringLiteral("%0 org.kde.kded5 /modules/plasmavault closeAllVaults").arg(qdbusExe));
|
2022-09-10 22:23:52 +01:00
|
|
|
|
addSuggestedCommand(defaultMenu,
|
|
|
|
|
i18n("Forcefully Close All Vaults"),
|
|
|
|
|
QStringLiteral("%0 org.kde.kded5 /modules/plasmavault forceCloseAllVaults").arg(qdbusExe));
|
2021-06-12 11:52:31 +01:00
|
|
|
|
#endif
|
2018-03-24 18:57:40 +00:00
|
|
|
|
|
2022-09-10 22:23:52 +01:00
|
|
|
|
QTableView *table = new QTableView(this);
|
2015-09-12 12:51:09 +01:00
|
|
|
|
table->horizontalHeader()->setStretchLastSection(true);
|
|
|
|
|
table->verticalHeader()->setVisible(false);
|
2022-09-10 22:23:52 +01:00
|
|
|
|
QVBoxLayout *layout = new QVBoxLayout(this);
|
2015-09-12 11:38:44 +01:00
|
|
|
|
layout->addWidget(table);
|
2022-09-10 22:23:52 +01:00
|
|
|
|
QPushButton *button = new QPushButton(QIcon::fromTheme(QStringLiteral("list-add")), i18n("Sample commands"), this);
|
2018-03-24 18:57:40 +00:00
|
|
|
|
button->setMenu(defaultMenu);
|
|
|
|
|
layout->addWidget(button);
|
|
|
|
|
|
2023-03-30 00:08:03 +01:00
|
|
|
|
QHBoxLayout *importExportLayout = new QHBoxLayout(this);
|
|
|
|
|
QPushButton *exportButton = new QPushButton(i18n("Export"), this);
|
|
|
|
|
importExportLayout->addWidget(exportButton);
|
|
|
|
|
connect(exportButton, &QPushButton::clicked, this, &RunCommandConfig::exportCommands);
|
|
|
|
|
QPushButton *importButton = new QPushButton(i18n("Import"), this);
|
|
|
|
|
importExportLayout->addWidget(importButton);
|
|
|
|
|
connect(importButton, &QPushButton::clicked, this, &RunCommandConfig::importCommands);
|
|
|
|
|
layout->addLayout(importExportLayout);
|
|
|
|
|
|
2015-09-12 11:38:44 +01:00
|
|
|
|
setLayout(layout);
|
|
|
|
|
|
|
|
|
|
m_entriesModel = new QStandardItemModel(this);
|
|
|
|
|
table->setModel(m_entriesModel);
|
|
|
|
|
|
|
|
|
|
m_entriesModel->setHorizontalHeaderLabels(QStringList() << i18n("Name") << i18n("Command"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RunCommandConfig::~RunCommandConfig()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-30 00:08:03 +01:00
|
|
|
|
void RunCommandConfig::exportCommands()
|
|
|
|
|
{
|
|
|
|
|
QString filePath = QFileDialog::getSaveFileName(this, i18n("Export Commands"), QDir::homePath(), QStringLiteral("JSON (*.json)"));
|
|
|
|
|
if (filePath.isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
QFile file(filePath);
|
|
|
|
|
if (!file.open(QFile::WriteOnly | QFile::Text)) {
|
|
|
|
|
qWarning() << "Could not write to file:" << filePath;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QJsonArray jsonArray;
|
|
|
|
|
for (int i = 0; i < m_entriesModel->rowCount(); i++) {
|
|
|
|
|
QJsonObject jsonObj;
|
|
|
|
|
jsonObj[QStringLiteral("name")] = m_entriesModel->index(i, 0).data().toString();
|
|
|
|
|
jsonObj[QStringLiteral("command")] = m_entriesModel->index(i, 1).data().toString();
|
|
|
|
|
jsonArray.append(jsonObj);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QJsonDocument jsonDocument(jsonArray);
|
|
|
|
|
file.write(jsonDocument.toJson());
|
|
|
|
|
file.close();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RunCommandConfig::importCommands()
|
|
|
|
|
{
|
|
|
|
|
QString filePath = QFileDialog::getOpenFileName(this, i18n("Import Commands"), QDir::homePath(), QStringLiteral("JSON (*.json)"));
|
|
|
|
|
if (filePath.isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
QFile file(filePath);
|
|
|
|
|
if (!file.open(QFile::ReadOnly | QFile::Text)) {
|
|
|
|
|
qWarning() << "Could not read file:" << filePath;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QByteArray jsonData = file.readAll();
|
|
|
|
|
file.close();
|
|
|
|
|
|
|
|
|
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData);
|
|
|
|
|
if (jsonDoc.isNull() || !jsonDoc.isArray()) {
|
|
|
|
|
qWarning() << "Invalid JSON format.";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Clear the current command list
|
|
|
|
|
m_entriesModel->removeRows(0, m_entriesModel->rowCount());
|
|
|
|
|
|
|
|
|
|
// Populate the model with the imported commands
|
|
|
|
|
QJsonArray jsonArray = jsonDoc.array();
|
|
|
|
|
for (const QJsonValue &jsonValue : jsonArray) {
|
|
|
|
|
QJsonObject jsonObj = jsonValue.toObject();
|
|
|
|
|
QString name = jsonObj.value(QStringLiteral("name")).toString();
|
|
|
|
|
QString command = jsonObj.value(QStringLiteral("command")).toString();
|
|
|
|
|
insertRow(m_entriesModel->rowCount(), name, command);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Q_EMIT changed(true);
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-10 22:23:52 +01:00
|
|
|
|
void RunCommandConfig::addSuggestedCommand(QMenu *menu, const QString &name, const QString &command)
|
2018-03-24 18:57:40 +00:00
|
|
|
|
{
|
|
|
|
|
auto action = new QAction(name);
|
|
|
|
|
connect(action, &QAction::triggered, action, [this, name, command]() {
|
|
|
|
|
insertRow(0, name, command);
|
|
|
|
|
Q_EMIT changed(true);
|
|
|
|
|
});
|
|
|
|
|
menu->addAction(action);
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-12 11:38:44 +01:00
|
|
|
|
void RunCommandConfig::defaults()
|
|
|
|
|
{
|
|
|
|
|
KCModule::defaults();
|
2022-09-10 22:23:52 +01:00
|
|
|
|
m_entriesModel->removeRows(0, m_entriesModel->rowCount());
|
2015-09-12 11:38:44 +01:00
|
|
|
|
|
|
|
|
|
Q_EMIT changed(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RunCommandConfig::load()
|
|
|
|
|
{
|
|
|
|
|
KCModule::load();
|
|
|
|
|
|
2019-10-27 17:08:51 +00:00
|
|
|
|
QJsonDocument jsonDocument = QJsonDocument::fromJson(config()->getByteArray(QStringLiteral("commands"), "{}"));
|
2015-09-12 11:38:44 +01:00
|
|
|
|
QJsonObject jsonConfig = jsonDocument.object();
|
2017-07-20 15:14:07 +01:00
|
|
|
|
const QStringList keys = jsonConfig.keys();
|
2022-09-10 22:23:52 +01:00
|
|
|
|
for (const QString &key : keys) {
|
2015-09-12 11:38:44 +01:00
|
|
|
|
const QJsonObject entry = jsonConfig[key].toObject();
|
2016-11-26 14:38:08 +00:00
|
|
|
|
const QString name = entry[QStringLiteral("name")].toString();
|
|
|
|
|
const QString command = entry[QStringLiteral("command")].toString();
|
2015-09-12 11:38:44 +01:00
|
|
|
|
|
2022-09-10 22:23:52 +01:00
|
|
|
|
QStandardItem *newName = new QStandardItem(name);
|
2015-09-12 11:38:44 +01:00
|
|
|
|
newName->setEditable(true);
|
|
|
|
|
newName->setData(key);
|
2022-09-10 22:23:52 +01:00
|
|
|
|
QStandardItem *newCommand = new QStandardItem(command);
|
2015-09-12 11:38:44 +01:00
|
|
|
|
newName->setEditable(true);
|
|
|
|
|
|
2022-09-10 22:23:52 +01:00
|
|
|
|
m_entriesModel->appendRow(QList<QStandardItem *>() << newName << newCommand);
|
2015-09-12 11:38:44 +01:00
|
|
|
|
}
|
2016-09-30 11:55:43 +01:00
|
|
|
|
|
|
|
|
|
m_entriesModel->sort(0);
|
|
|
|
|
|
2015-09-12 11:38:44 +01:00
|
|
|
|
insertEmptyRow();
|
|
|
|
|
connect(m_entriesModel, &QAbstractItemModel::dataChanged, this, &RunCommandConfig::onDataChanged);
|
|
|
|
|
|
|
|
|
|
Q_EMIT changed(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RunCommandConfig::save()
|
|
|
|
|
{
|
|
|
|
|
QJsonObject jsonConfig;
|
2022-09-10 22:23:52 +01:00
|
|
|
|
for (int i = 0; i < m_entriesModel->rowCount(); i++) {
|
2015-09-12 11:38:44 +01:00
|
|
|
|
QString key = m_entriesModel->item(i, 0)->data().toString();
|
|
|
|
|
const QString name = m_entriesModel->item(i, 0)->text();
|
|
|
|
|
const QString command = m_entriesModel->item(i, 1)->text();
|
|
|
|
|
|
|
|
|
|
if (name.isEmpty() || command.isEmpty()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (key.isEmpty()) {
|
|
|
|
|
key = QUuid::createUuid().toString();
|
2019-08-14 16:36:19 +01:00
|
|
|
|
DBusHelper::filterNonExportableCharacters(key);
|
2015-09-12 11:38:44 +01:00
|
|
|
|
}
|
|
|
|
|
QJsonObject entry;
|
2016-11-26 14:38:08 +00:00
|
|
|
|
entry[QStringLiteral("name")] = name;
|
|
|
|
|
entry[QStringLiteral("command")] = command;
|
2015-09-12 11:38:44 +01:00
|
|
|
|
jsonConfig[key] = entry;
|
|
|
|
|
}
|
|
|
|
|
QJsonDocument document;
|
|
|
|
|
document.setObject(jsonConfig);
|
2017-03-17 11:25:47 +00:00
|
|
|
|
config()->set(QStringLiteral("commands"), document.toJson(QJsonDocument::Compact));
|
2015-09-12 11:38:44 +01:00
|
|
|
|
|
|
|
|
|
KCModule::save();
|
|
|
|
|
|
|
|
|
|
Q_EMIT changed(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RunCommandConfig::insertEmptyRow()
|
|
|
|
|
{
|
2018-03-24 18:57:40 +00:00
|
|
|
|
insertRow(m_entriesModel->rowCount(), {}, {});
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-10 22:23:52 +01:00
|
|
|
|
void RunCommandConfig::insertRow(int i, const QString &name, const QString &command)
|
2018-03-24 18:57:40 +00:00
|
|
|
|
{
|
2022-09-10 22:23:52 +01:00
|
|
|
|
QStandardItem *newName = new QStandardItem(name);
|
2015-09-12 11:38:44 +01:00
|
|
|
|
newName->setEditable(true);
|
2022-09-10 22:23:52 +01:00
|
|
|
|
QStandardItem *newCommand = new QStandardItem(command);
|
2015-09-12 11:38:44 +01:00
|
|
|
|
newName->setEditable(true);
|
|
|
|
|
|
2022-09-10 22:23:52 +01:00
|
|
|
|
m_entriesModel->insertRow(i, QList<QStandardItem *>() << newName << newCommand);
|
2015-09-12 11:38:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
2022-09-10 22:23:52 +01:00
|
|
|
|
void RunCommandConfig::onDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
|
2015-09-12 11:38:44 +01:00
|
|
|
|
{
|
2018-11-26 00:53:55 +00:00
|
|
|
|
Q_EMIT changed(true);
|
2015-09-12 11:38:44 +01:00
|
|
|
|
Q_UNUSED(topLeft);
|
|
|
|
|
if (bottomRight.row() == m_entriesModel->rowCount() - 1) {
|
2022-09-10 22:23:52 +01:00
|
|
|
|
// TODO check both entries are still empty
|
2015-09-12 11:38:44 +01:00
|
|
|
|
insertEmptyRow();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#include "runcommand_config.moc"
|