mirror-kde-plasma-desktop/kcms/solid_actions/SolidActions.cpp
Akseli Lahtinen ee5f80dfd0
SolidActions: Use absolute path for getting the template file
Due to changes in KDesktopFile, namely
https://invent.kde.org/frameworks/kconfig/-/merge_requests/430 ,
we can end up in situation where we can't locate the
template file, since we're searching for it from user files.

Instead, locate the absolute filepath for the template
in addAction and use it directly.

BUG: 518317
2026-03-30 17:02:31 +03:00

216 lines
7.3 KiB
C++

/*
SPDX-FileCopyrightText: 2009 Ben Cooksley <ben@eclipse.endoftheinternet.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "SolidActions.h"
#include "ActionItem.h"
#include <KAboutData>
#include <KBuildSycocaProgressDialog>
#include <KConfigGroup>
#include <KDesktopFile>
#include <KMessageBox>
#include <KPluginFactory>
#include <KStandardGuiItem>
#include <KIO/Global>
#include <QComboBox>
#include <QDebug>
#include <QPushButton>
#include <Solid/DeviceInterface>
#include <Solid/Predicate>
K_PLUGIN_CLASS_WITH_JSON(SolidActions, "kcm_solid_actions.json")
SolidActions::SolidActions(QObject *parent, const KPluginMetaData &data)
: KCModule(parent, data)
{
setButtons(KCModule::Help);
// Prepare main display dialog
actionModel = new ActionModel(this);
mainUi.setupUi(widget());
mainUi.TvActions->setModel(actionModel);
mainUi.TvActions->setHeaderHidden(true);
mainUi.TvActions->setRootIsDecorated(false);
mainUi.TvActions->setSelectionMode(QAbstractItemView::SingleSelection);
KStandardGuiItem::assign(mainUi.PbAddAction, KStandardGuiItem::Add);
mainUi.PbEditAction->setIcon(QIcon::fromTheme(QStringLiteral("document-edit")));
connect(mainUi.PbAddAction, &QPushButton::clicked, this, &SolidActions::slotShowAddDialog);
connect(mainUi.PbEditAction, &QPushButton::clicked, this, &SolidActions::editAction);
connect(mainUi.PbDeleteAction, &QPushButton::clicked, this, &SolidActions::deleteAction);
connect(mainUi.TvActions->selectionModel(), &QItemSelectionModel::currentChanged, this, &SolidActions::toggleEditDelete);
connect(mainUi.TvActions, &QTreeView::doubleClicked, this, &SolidActions::editAction);
// Prepare + connect up with Edit dialog
editUi = new ActionEditor(widget());
connect(editUi, &ActionEditor::accepted, this, &SolidActions::acceptActionChanges);
// Prepare + connect up add action dialog
addDialog = new QDialog(widget());
addUi.setupUi(addDialog);
addDialog->resize(QSize(300, 100)); // Set a sensible default size
slotTextChanged(addUi.LeActionName->text());
connect(addUi.LeActionName, &QLineEdit::textChanged, this, &SolidActions::slotTextChanged);
connect(addUi.buttonBox, &QDialogButtonBox::accepted, this, &SolidActions::addAction);
connect(addUi.buttonBox, &QDialogButtonBox::rejected, addDialog, &QDialog::reject);
}
void SolidActions::slotShowAddDialog()
{
addDialog->show();
addUi.LeActionName->setFocus();
addUi.LeActionName->clear();
}
void SolidActions::slotTextChanged(const QString &text)
{
addUi.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!text.isEmpty());
}
void SolidActions::load()
{
fillActionsList();
}
void SolidActions::defaults()
{
}
void SolidActions::save()
{
}
void SolidActions::addAction()
{
const QString enteredName = addUi.LeActionName->text();
constexpr auto resourceType = QStandardPaths::GenericDataLocation;
const auto absolutePath = QStandardPaths::locate(resourceType, QStringLiteral("kcmsolidactions/solid-action-template.desktop"));
KDesktopFile templateDesktop(resourceType, absolutePath); // Lets get the template
// Lets get a desktop file
QString internalName = enteredName; // copy the name the user entered -> we will be making mods
internalName.replace(QChar(' '), QChar('-'), Qt::CaseSensitive); // replace spaces with dashes
internalName = KIO::encodeFileName(internalName);
QString filePath = QStandardPaths::writableLocation(resourceType) + "/solid/actions/"; // Get the location on disk for "data"
if (!QDir().exists(filePath)) {
QDir().mkpath(filePath);
}
filePath += internalName + ".desktop";
// Fill in an initial template
KDesktopFile *newDesktop = templateDesktop.copyTo(filePath);
newDesktop->actionGroup(QStringLiteral("open")).writeEntry("Name", enteredName); // ditto
delete newDesktop; // Force file to be written
// Prepare to open the editDialog
fillActionsList();
const QList<ActionItem *> actionList = actionModel->actionList();
QModelIndex newAction;
for (ActionItem *newItem : actionList) { // Lets find our new action
if (newItem->desktopMasterPath == filePath) {
const int position = actionList.indexOf(newItem);
newAction = actionModel->index(position, 0); // Grab it
break;
}
}
mainUi.TvActions->setCurrentIndex(newAction); // Set it as currently active
addDialog->hide();
editAction(); // Open the edit dialog
}
void SolidActions::editAction()
{
ActionItem *selectedItem = selectedAction();
if (!selectedItem) {
return;
}
// We should error out here if we have to
if (!selectedItem->predicate().isValid()) {
KMessageBox::error(widget(), i18n("It appears that the predicate for this action is not valid."), i18n("Error Parsing Device Conditions"));
return;
}
// Display us!
editUi->setActionToEdit(selectedItem);
editUi->setWindowIcon(widget()->windowIcon());
editUi->show();
}
void SolidActions::deleteAction()
{
ActionItem *action = selectedAction();
if (action->isUserSupplied()) { // Is the action user supplied?
QFile::remove(action->desktopMasterPath); // Remove the main desktop file then
}
QFile::remove(action->desktopWritePath); // Remove the modified desktop file now
fillActionsList(); // Update the list of actions
}
ActionItem *SolidActions::selectedAction() const
{
QModelIndex action = mainUi.TvActions->currentIndex();
ActionItem *actionItem = actionModel->data(action, Qt::UserRole).value<ActionItem *>();
return actionItem;
}
void SolidActions::fillActionsList()
{
mainUi.TvActions->clearSelection();
actionModel->buildActionList();
mainUi.TvActions->header()->setSectionResizeMode(0, QHeaderView::Stretch);
mainUi.TvActions->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
toggleEditDelete();
}
void SolidActions::acceptActionChanges()
{
// Re-read the actions list to ensure changes are reflected
KBuildSycocaProgressDialog::rebuildKSycoca(widget());
fillActionsList();
}
void SolidActions::toggleEditDelete()
{
bool toggle = true;
if (!mainUi.TvActions->currentIndex().isValid()) { // Is an action selected?
mainUi.PbDeleteAction->setText(i18n("No Action Selected")); // Set a friendly disabled text
mainUi.PbDeleteAction->setIcon(QIcon());
toggle = false;
}
mainUi.PbEditAction->setEnabled(toggle); // Change them to the new state
mainUi.PbDeleteAction->setEnabled(toggle); // Ditto
if (!toggle) {
return;
}
// What functionality do we need to change?
if (selectedAction()->isUserSupplied()) {
// We are able to directly delete it, enable full delete functionality
KStandardGuiItem::assign(mainUi.PbDeleteAction, KStandardGuiItem::Remove);
} else if (QFile::exists(selectedAction()->desktopWritePath)) { // Does the write file exist?
// We are able to revert, lets show it
KStandardGuiItem::assign(mainUi.PbDeleteAction, KStandardGuiItem::Discard);
} else {
// We cannot do anything then, disable delete functionality
mainUi.PbDeleteAction->setText(i18n("Cannot be deleted"));
mainUi.PbDeleteAction->setIcon(QIcon());
mainUi.PbDeleteAction->setEnabled(false);
}
}
#include "SolidActions.moc"
#include "moc_SolidActions.cpp"