2 * SPDX-FileCopyrightText: 2025 Méven Car <meven@kde.org>
4 * SPDX-License-Identifier: GPL-2.0-or-later
7 #include "setfoldericonitemaction.h"
8 #include "../dolphindebug.h"
10 #include <KConfigGroup>
11 #include <KDesktopFile>
13 #include <KLocalizedString>
14 #include <KPluginFactory>
19 #include <QActionGroup>
22 #include <QFocusEvent>
25 #include <QPushButton>
27 #include <QWidgetAction>
29 K_PLUGIN_CLASS_WITH_JSON(SetFolderIconItemAction
, "setfoldericonitemaction.json")
33 bool isDefaultFolderIcon(const QString
&iconName
)
35 return iconName
.isEmpty() || iconName
== QLatin1String("folder") || iconName
== QLatin1String("inode-directory");
39 SetFolderIconItemAction::SetFolderIconItemAction(QObject
*parent
)
40 : KAbstractFileItemActionPlugin(parent
)
44 void SetFolderIconItemAction::setFolderIcon(bool check
)
46 QAction
*action
= qobject_cast
<QAction
*>(sender());
49 action
->setChecked(check
);
51 auto iconName
= action
->icon().name();
53 // Apply custom folder icon, if applicable.
54 const QString fileName
= m_localUrl
.toLocalFile() + QLatin1String("/.directory");
55 KDesktopFile desktopFile
{fileName
};
57 if (check
&& !isDefaultFolderIcon(iconName
)) {
58 desktopFile
.desktopGroup().writeEntry(QStringLiteral("Icon"), iconName
);
60 desktopFile
.desktopGroup().deleteEntry(QStringLiteral("Icon"));
61 if (desktopFile
.desktopGroup().entryMap().isEmpty() && QFile::exists(fileName
)) {
63 QFile::remove(fileName
);
68 org::kde::KDirNotify::emitFilesChanged({m_url
});
72 class ButtonsWithSubMenuWidgetAction
: public QWidgetAction
75 ButtonsWithSubMenuWidgetAction(QMenu
*subMenu
, QWidget
*parentWidget
)
76 : QWidgetAction(parentWidget
)
81 void setActions(const QList
<QAction
*> actions
)
86 bool eventFilter(QObject
*object
, QEvent
*event
) override
88 if (event
->type() == QEvent::KeyPress
) {
89 const QKeyEvent
*keyEvent
= static_cast<QKeyEvent
*>(event
);
90 auto widget
= qobject_cast
<QWidget
*>(object
);
92 if (keyEvent
->keyCombination() == QKeyCombination(Qt::Modifier::SHIFT
, Qt::Key_Backtab
) || keyEvent
->key() == Qt::Key_Left
93 || keyEvent
->key() == Qt::Key_Up
) {
94 auto previous
= widget
->previousInFocusChain();
95 if (previous
== widget
->parentWidget()) {
96 // the next object is the parent, let the focus bubble up
100 previous
->setFocus(Qt::BacktabFocusReason
);
105 if (keyEvent
->keyCombination() == QKeyCombination(Qt::Key_Tab
) || keyEvent
->key() == Qt::Key_Right
|| keyEvent
->key() == Qt::Key_Down
) {
106 auto next
= widget
->nextInFocusChain();
107 if (next
->parentWidget() != widget
->parentWidget()) {
108 // the next object is not a sibling, let the focus bubble up
112 next
->setFocus(Qt::TabFocusReason
);
118 // TODO implement proper SHIFT+TAB
119 // See https://bugreports.qt.io/browse/QTBUG-137298
124 QWidget
*createWidget(QWidget
*parent
) override
126 QWidget
*widget
= new QWidget(parent
);
127 auto layout
= new QHBoxLayout(widget
);
129 bool firstAction
= false;
130 for (const auto action
: std::as_const(m_actions
)) {
131 action
->setParent(widget
);
133 auto p
= new QPushButton(widget
);
135 p
->setIcon(action
->icon());
136 p
->setCheckable(true);
137 p
->setChecked(action
->isChecked());
138 p
->setToolTip(action
->toolTip());
139 p
->installEventFilter(this);
141 connect(p
, &QPushButton::clicked
, action
, &QAction::triggered
);
142 connect(action
, &QAction::toggled
, p
, &QPushButton::setChecked
);
144 layout
->addWidget(p
);
147 widget
->setFocusProxy(p
);
152 auto p
= new QPushButton(widget
);
153 p
->setText(i18nc("@action open a submeun with additional entries", "Other"));
154 p
->setToolTip(i18nc("@label", "Other folder icon options"));
155 p
->setMenu(m_subMenu
);
156 layout
->addWidget(p
);
157 p
->installEventFilter(this);
159 widget
->setFocusPolicy(Qt::StrongFocus
);
164 QList
<QAction
*> m_actions
;
168 QList
<QAction
*> SetFolderIconItemAction::actions(const KFileItemListProperties
&fileItemInfos
, QWidget
*parentWidget
)
170 if (fileItemInfos
.items().count() != 1) {
174 auto fileItem
= fileItemInfos
.items().at(0);
175 m_url
= fileItem
.url();
178 m_localUrl
= fileItem
.mostLocalUrl(&local
);
179 if (!local
|| !fileItemInfos
.supportsWriting() || !fileItem
.isWritable()) {
182 const short s_numberOfEntriesVisible
= 5;
184 using StringPair
= QPair
<KLocalizedString
, QString
>;
185 // keep in sync with kio/src/filewidgets/knewfilemenu.cpp
186 // default folder icon goes here.
187 const QList
<StringPair
> icons
= {// colors.
188 StringPair
{ki18nc("@label as in default folder color", "Red"), QStringLiteral("folder-red")},
189 StringPair
{ki18nc("@label as in default folder color", "Yellow"), QStringLiteral("folder-yellow")},
190 StringPair
{ki18nc("@label as in default folder color", "Orange"), QStringLiteral("folder-orange")},
191 StringPair
{ki18nc("@label as in default folder color", "Green"), QStringLiteral("folder-green")},
192 StringPair
{ki18nc("@label as in default folder color", "Cyan"), QStringLiteral("folder-cyan")},
193 // must match s_numberOfEntriesVisible
194 StringPair
{ki18nc("@label: as in default folder icon", "Default"), QStringLiteral("inode-directory")},
196 StringPair
{ki18nc("@label as in default folder color", "Blue"), QStringLiteral("folder-blue")},
197 StringPair
{ki18nc("@label as in default folder color", "Violet"), QStringLiteral("folder-violet")},
198 StringPair
{ki18nc("@label as in default folder color", "Brown"), QStringLiteral("folder-brown")},
199 StringPair
{ki18nc("@label as in default folder color", "Grey"), QStringLiteral("folder-grey")},
202 StringPair
{ki18nc("@label as in default folder color", "Bookmark"), QStringLiteral("folder-bookmark")},
203 StringPair
{ki18nc("@label as in default folder color", "Cloud"), QStringLiteral("folder-cloud")},
204 StringPair
{ki18nc("@label as in default folder color", "Development"), QStringLiteral("folder-development")},
205 StringPair
{ki18nc("@label as in default folder color", "Games"), QStringLiteral("folder-games")},
206 StringPair
{ki18nc("@label as in default folder color", "Mail"), QStringLiteral("folder-mail")},
207 StringPair
{ki18nc("@label as in default folder color", "Music"), QStringLiteral("folder-music")},
208 StringPair
{ki18nc("@label as in default folder color", "Print"), QStringLiteral("folder-print")},
209 StringPair
{ki18nc("@label as in default folder color", "Compressed"), QStringLiteral("folder-tar")},
210 StringPair
{ki18nc("@label as in default folder color", "Temporary"), QStringLiteral("folder-temp")},
211 StringPair
{ki18nc("@label as in default folder color", "Important"), QStringLiteral("folder-important")}};
213 QActionGroup
*actiongroup
= new QActionGroup(this);
214 actiongroup
->setExclusionPolicy(QActionGroup::ExclusionPolicy::ExclusiveOptional
);
216 QMenu
*subMenu
= new QMenu();
217 auto action
= new ButtonsWithSubMenuWidgetAction(subMenu
, parentWidget
);
220 QList
<QAction
*> actions
;
221 const auto fileIconName
= fileItem
.iconName();
222 for (const auto &[name
, iconName
] : icons
) {
223 auto icon
= QIcon::fromTheme(iconName
);
225 qCWarning(DolphinDebug
) << "SetFolderIconItemAction Missing icon:" << iconName
;
229 QAction
*folderIconAction
= new QAction(KLocalizedString(name
).toString(), parentWidget
);
230 folderIconAction
->setIcon(icon
);
231 folderIconAction
->setCheckable(true);
232 folderIconAction
->setChecked(fileIconName
== iconName
);
233 folderIconAction
->setToolTip(i18nc("@label %1 is a folder icon name (Red, Music...) etc", "Set folder icon to %1", folderIconAction
->iconText()));
234 actiongroup
->addAction(folderIconAction
);
236 connect(folderIconAction
, &QAction::triggered
, this, &SetFolderIconItemAction::setFolderIcon
);
237 connect(folderIconAction
, &QAction::triggered
, action
, &QAction::triggered
);
240 if (i
< s_numberOfEntriesVisible
+ 1) {
241 actions
.append(folderIconAction
);
243 folderIconAction
->setParent(subMenu
);
244 subMenu
->addAction(folderIconAction
);
247 action
->setActions(actions
);
252 #include "moc_setfoldericonitemaction.cpp"
253 #include "setfoldericonitemaction.moc"