diff options
| author | Kostiantin Korchuhanov <[email protected]> | 2025-08-15 16:29:34 +0300 |
|---|---|---|
| committer | Kostiantyn Korchuhanov <[email protected]> | 2025-09-22 06:51:19 +0000 |
| commit | f5818306d014fafaf35ef10ee83e286eebea9cd7 (patch) | |
| tree | 25151e3268583dbc4f503a043357a03469c622ab /src/itemactions/hidefileitemaction.cpp | |
| parent | 1d92f1736350664383217a7f30f4c3701ae14759 (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/hidefileitemaction.cpp')
| -rw-r--r-- | src/itemactions/hidefileitemaction.cpp | 173 |
1 files changed, 173 insertions, 0 deletions
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" |
