┌   ┐
54
└   ┘

summaryrefslogtreecommitdiff
path: root/src/itemactions
diff options
context:
space:
mode:
authorKostiantin Korchuhanov <[email protected]>2025-08-15 16:29:34 +0300
committerKostiantyn Korchuhanov <[email protected]>2025-09-22 06:51:19 +0000
commitf5818306d014fafaf35ef10ee83e286eebea9cd7 (patch)
tree25151e3268583dbc4f503a043357a03469c622ab /src/itemactions
parent1d92f1736350664383217a7f30f4c3701ae14759 (diff)
itemactions: Add context menu actions to hide and unhide files using .hidden
Summary: This patch introduces context menu actions that allow users to hide or unhide selected files by editing the .hidden file in their parent directories. What this patch does: - Implemented HideFileItemAction plugin based on KAbstractFileItemActionPlugin - Plugin is disabled by default unless explicitly enabled in kservicemenurc via the hidefileitemaction key - "Hide" action adds selected file names to the .hidden file, avoiding duplicates - "Unhide" action removes selected file names from the .hidden file - Action visibility logic: - If only visible or only hidden files are selected, only the relevant action is shown - If both are selected, both "Hide" and "Unhide" actions are shown - Write permission checks ensure actions are disabled if the .hidden file cannot be modified - Optimized redundant path computations by calculating parent directory and .hidden file paths once and reusing them
Diffstat (limited to 'src/itemactions')
-rw-r--r--src/itemactions/CMakeLists.txt11
-rw-r--r--src/itemactions/hidefileitemaction.cpp173
-rw-r--r--src/itemactions/hidefileitemaction.h26
-rw-r--r--src/itemactions/hidefileitemaction.json10
4 files changed, 220 insertions, 0 deletions
diff --git a/src/itemactions/CMakeLists.txt b/src/itemactions/CMakeLists.txt
index 6610b0e4a..cd196eb36 100644
--- a/src/itemactions/CMakeLists.txt
+++ b/src/itemactions/CMakeLists.txt
@@ -11,6 +11,16 @@ target_link_libraries(movetonewfolderitemaction
KF6::KIOWidgets
KF6::KIOFileWidgets)
+#hidefileitemaction plugin
+kcoreaddons_add_plugin(hidefileitemaction
+ SOURCES hidefileitemaction.cpp hidefileitemaction.h
+ INSTALL_NAMESPACE "kf6/kfileitemaction")
+
+target_link_libraries(hidefileitemaction
+ KF6::CoreAddons
+ KF6::I18n
+ KF6::KIOCore
+ KF6::KIOWidgets)
if(NOT WIN32)
# setfoldericon plugin
@@ -24,3 +34,4 @@ if(NOT WIN32)
KF6::KIOCore
KF6::KIOWidgets)
endif()
+
diff --git a/src/itemactions/hidefileitemaction.cpp b/src/itemactions/hidefileitemaction.cpp
new file mode 100644
index 000000000..b01117838
--- /dev/null
+++ b/src/itemactions/hidefileitemaction.cpp
@@ -0,0 +1,173 @@
+/*
+ * SPDX-FileCopyrightText: 2025 Kostiantyn Korchuhanov <[email protected]>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "hidefileitemaction.h"
+#include "../dolphindebug.h"
+
+#ifdef QT_DBUS_LIB
+#include <KDirNotify>
+#endif
+#include <KConfigGroup>
+#include <KFileItem>
+#include <KFileItemListProperties>
+#include <KLocalizedString>
+#include <KPluginFactory>
+#include <KSharedConfig>
+
+#include <QAction>
+#include <QDir>
+#include <QFile>
+#include <QFileInfo>
+#include <QTextStream>
+#include <QUrl>
+
+K_PLUGIN_CLASS_WITH_JSON(HideFileItemAction, "hidefileitemaction.json");
+
+HideFileItemAction::HideFileItemAction(QObject *parent)
+ : KAbstractFileItemActionPlugin(parent)
+{
+}
+
+static bool pluginIsEnabled()
+{
+ KConfigGroup showGroup(KSharedConfig::openConfig(QStringLiteral("kservicemenurc")), QStringLiteral("Show"));
+ return showGroup.readEntry(QStringLiteral("hidefileitemaction"), false);
+}
+
+static QStringList readHiddenFileEntries(const QString &hiddenPath)
+{
+ QStringList entriesNames;
+ QFile readHiddenFile(hiddenPath);
+ if (readHiddenFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ QTextStream in(&readHiddenFile);
+ while (!in.atEnd()) {
+ entriesNames.append(in.readLine());
+ }
+ }
+ return entriesNames;
+}
+
+static bool fileAlreadyHidden(const KFileItem &item)
+{
+ QString fileName = item.name();
+ QString parentPath = QFileInfo(item.localPath()).absolutePath();
+ QString hiddenPath = QDir(parentPath).filePath(".hidden");
+
+ QStringList entriesNames = readHiddenFileEntries(hiddenPath);
+
+ return entriesNames.contains(fileName);
+}
+
+static void writeHiddenFileEntries(const QString &hiddenPath, const QStringList &entriesNames)
+{
+ QFile writeHiddenFile(hiddenPath);
+ if (writeHiddenFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
+ QTextStream out(&writeHiddenFile);
+ out << entriesNames.join("\n");
+ }
+}
+
+static void writeFileToHidden(const QString &hiddenPath, const QString &fileName)
+{
+ QStringList entriesNames = readHiddenFileEntries(hiddenPath);
+ if (!entriesNames.contains(fileName)) {
+ entriesNames.append(fileName);
+ writeHiddenFileEntries(hiddenPath, entriesNames);
+ }
+}
+
+static void deleteFileFromHidden(const QString &hiddenPath, const QString &fileName)
+{
+ QStringList entriesNames = readHiddenFileEntries(hiddenPath);
+ entriesNames.removeAll(fileName);
+ writeHiddenFileEntries(hiddenPath, entriesNames);
+}
+
+QList<QAction *> HideFileItemAction::actions(const KFileItemListProperties &fileItemInfos, QWidget *parentWidget)
+{
+ if (!pluginIsEnabled()) {
+ return {};
+ }
+ const KFileItemList selectedItems = fileItemInfos.items();
+ if (selectedItems.isEmpty()) {
+ return {};
+ }
+
+ const QUrl parentFileUrl = selectedItems.first().url().adjusted(QUrl::RemoveFilename);
+ const QString parentFilePath = parentFileUrl.toLocalFile();
+ const QString hiddenFilePath = QDir(parentFilePath).filePath(".hidden");
+
+ for (const KFileItem &item : selectedItems) {
+ if (item.url().adjusted(QUrl::RemoveFilename).toLocalFile() != parentFilePath) {
+ return {};
+ }
+ }
+
+ QFileInfo parentFileInfo(parentFilePath);
+ QFileInfo hiddenFileInfo(hiddenFilePath);
+ const bool canWrite = parentFileInfo.isWritable() && (!hiddenFileInfo.exists() || hiddenFileInfo.isWritable());
+
+ QAction *hideFolder = new QAction(i18nc("@action:inmenu", "Hide"), parentWidget);
+ hideFolder->setIcon(QIcon::fromTheme("hide_table_row"));
+ hideFolder->setEnabled(canWrite);
+
+ connect(hideFolder, &QAction::triggered, this, [selectedItems, hiddenFilePath, parentFilePath]() {
+ for (const KFileItem &item : selectedItems) {
+ if (item.name() == ".hidden") {
+ continue;
+ }
+
+ QString fileName = item.name();
+ writeFileToHidden(hiddenFilePath, fileName);
+
+#ifdef QT_DBUS_LIB
+ org::kde::KDirNotify::emitFilesChanged({QUrl::fromLocalFile(parentFilePath)});
+#endif
+ }
+ });
+
+ QAction *unhideFolder = new QAction(i18nc("@action:inmenu", "Unhide"), parentWidget);
+ unhideFolder->setIcon(QIcon::fromTheme("view-visible"));
+ unhideFolder->setEnabled(canWrite);
+
+ connect(unhideFolder, &QAction::triggered, this, [selectedItems, hiddenFilePath, parentFilePath]() {
+ for (const KFileItem &item : selectedItems) {
+ if (item.name() == ".hidden") {
+ continue;
+ }
+
+ QString fileName = item.name();
+ deleteFileFromHidden(hiddenFilePath, fileName);
+
+#ifdef QT_DBUS_LIB
+ org::kde::KDirNotify::emitFilesChanged({QUrl::fromLocalFile(parentFilePath)});
+#endif
+ }
+ });
+
+ bool anyFileHidden = false;
+ bool anyFileVisible = false;
+ for (const KFileItem &item : selectedItems) {
+ if (fileAlreadyHidden(item)) {
+ anyFileHidden = true;
+ } else {
+ anyFileVisible = true;
+ }
+ if (anyFileHidden && anyFileVisible) {
+ break;
+ }
+ }
+ if (anyFileHidden && anyFileVisible) {
+ return {hideFolder, unhideFolder};
+ }
+ if (anyFileVisible) {
+ return {hideFolder};
+ }
+ return {unhideFolder};
+}
+
+#include "hidefileitemaction.moc"
+#include "moc_hidefileitemaction.cpp"
diff --git a/src/itemactions/hidefileitemaction.h b/src/itemactions/hidefileitemaction.h
new file mode 100644
index 000000000..565f0d563
--- /dev/null
+++ b/src/itemactions/hidefileitemaction.h
@@ -0,0 +1,26 @@
+/*
+ * SPDX-FileCopyrightText: 2025 Kostiantyn Korchuhanov <[email protected]>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HIDEFILEITEMACTION_H
+#define HIDEFILEITEMACTION_H
+
+#include <KAbstractFileItemActionPlugin>
+#include <KFileItemListProperties>
+#include <QAction>
+#include <QList>
+#include <QWidget>
+
+class HideFileItemAction : public KAbstractFileItemActionPlugin
+{
+ Q_OBJECT
+
+public:
+ HideFileItemAction(QObject *parent);
+
+ QList<QAction *> actions(const KFileItemListProperties &fileItemInfos, QWidget *parentWidget) override;
+};
+
+#endif // HIDEFILEITEMACTION_H
diff --git a/src/itemactions/hidefileitemaction.json b/src/itemactions/hidefileitemaction.json
new file mode 100644
index 000000000..7b4627254
--- /dev/null
+++ b/src/itemactions/hidefileitemaction.json
@@ -0,0 +1,10 @@
+{
+ "KPlugin": {
+ "Icon": "hide_table_row",
+ "MimeTypes": [
+ "application/octet-stream"
+ ],
+ "Name": "Hide"
+ },
+ "X-KDE-Show-In-Submenu": true
+}