]> cloud.milkyroute.net Git - dolphin.git/blob - src/settings/contextmenu/contextmenusettingspage.cpp
Add options to hide some context menu entries
[dolphin.git] / src / settings / contextmenu / contextmenusettingspage.cpp
1 /*
2 * SPDX-FileCopyrightText: 2009-2010 Peter Penz <peter.penz19@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7 #include "contextmenusettingspage.h"
8
9 #include "dolphin_generalsettings.h"
10 #include "dolphin_versioncontrolsettings.h"
11 #include "dolphin_contextmenusettings.h"
12 #include "settings/serviceitemdelegate.h"
13 #include "settings/servicemodel.h"
14
15 #include <KDesktopFile>
16 #include <KLocalizedString>
17 #include <KMessageBox>
18 #include <KNS3/Button>
19 #include <KPluginMetaData>
20 #include <KService>
21 #include <KServiceTypeTrader>
22 #include <KDesktopFileActions>
23
24 #include <QGridLayout>
25 #include <QLabel>
26 #include <QListWidget>
27 #include <QScroller>
28 #include <QShowEvent>
29 #include <QSortFilterProxyModel>
30 #include <QLineEdit>
31
32 namespace
33 {
34 const bool ShowDeleteDefault = false;
35 const char VersionControlServicePrefix[] = "_version_control_";
36 const char DeleteService[] = "_delete";
37 const char CopyToMoveToService[] ="_copy_to_move_to";
38 const char AddToPlacesService[] = "_add_to_places";
39 const char SortByService[] = "_sort_by";
40 const char ViewModeService[] = "_view_mode";
41 const char OpenInNewTabService[] = "_open_in_new_tab";
42 const char OpenInNewWindowService[] = "_open_in_new_window";
43 const char CopyLocationService[] = "_copy_location";
44 const char DuplicateHereService[] = "_duplicate_here";
45 }
46
47 ContextMenuSettingsPage::ContextMenuSettingsPage(QWidget* parent) :
48 SettingsPageBase(parent),
49 m_initialized(false),
50 m_serviceModel(nullptr),
51 m_sortModel(nullptr),
52 m_listView(nullptr),
53 m_enabledVcsPlugins()
54 {
55 QVBoxLayout* topLayout = new QVBoxLayout(this);
56
57 QLabel* label = new QLabel(i18nc("@label:textbox",
58 "Select which services should "
59 "be shown in the context menu:"), this);
60 label->setWordWrap(true);
61 m_searchLineEdit = new QLineEdit(this);
62 m_searchLineEdit->setPlaceholderText(i18nc("@label:textbox", "Search..."));
63 connect(m_searchLineEdit, &QLineEdit::textChanged, this, [this](const QString &filter){
64 m_sortModel->setFilterFixedString(filter);
65 });
66
67 m_listView = new QListView(this);
68 QScroller::grabGesture(m_listView->viewport(), QScroller::TouchGesture);
69
70 auto *delegate = new ServiceItemDelegate(m_listView, m_listView);
71 m_serviceModel = new ServiceModel(this);
72 m_sortModel = new QSortFilterProxyModel(this);
73 m_sortModel->setSourceModel(m_serviceModel);
74 m_sortModel->setSortRole(Qt::DisplayRole);
75 m_sortModel->setSortLocaleAware(true);
76 m_sortModel->setFilterRole(Qt::DisplayRole);
77 m_sortModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
78 m_listView->setModel(m_sortModel);
79 m_listView->setItemDelegate(delegate);
80 m_listView->setVerticalScrollMode(QListView::ScrollPerPixel);
81 connect(m_listView, &QListView::clicked, this, &ContextMenuSettingsPage::changed);
82
83 #ifndef Q_OS_WIN
84 auto *downloadButton = new KNS3::Button(i18nc("@action:button", "Download New Services..."),
85 QStringLiteral("servicemenu.knsrc"),
86 this);
87 connect(downloadButton, &KNS3::Button::dialogFinished, this, [this](const KNS3::Entry::List &changedEntries) {
88 if (!changedEntries.isEmpty()) {
89 m_serviceModel->clear();
90 loadServices();
91 }
92 });
93
94 #endif
95
96 topLayout->addWidget(label);
97 topLayout->addWidget(m_searchLineEdit);
98 topLayout->addWidget(m_listView);
99 #ifndef Q_OS_WIN
100 topLayout->addWidget(downloadButton);
101 #endif
102
103 m_enabledVcsPlugins = VersionControlSettings::enabledPlugins();
104 std::sort(m_enabledVcsPlugins.begin(), m_enabledVcsPlugins.end());
105 }
106
107 ContextMenuSettingsPage::~ContextMenuSettingsPage() {
108 }
109
110 void ContextMenuSettingsPage::applySettings()
111 {
112 if (!m_initialized) {
113 return;
114 }
115
116 KConfig config(QStringLiteral("kservicemenurc"), KConfig::NoGlobals);
117 KConfigGroup showGroup = config.group("Show");
118
119 QStringList enabledPlugins;
120
121 const QAbstractItemModel *model = m_listView->model();
122 for (int i = 0; i < model->rowCount(); ++i) {
123 const QModelIndex index = model->index(i, 0);
124 const QString service = model->data(index, ServiceModel::DesktopEntryNameRole).toString();
125 const bool checked = model->data(index, Qt::CheckStateRole).toBool();
126
127 if (service.startsWith(VersionControlServicePrefix)) {
128 if (checked) {
129 enabledPlugins.append(model->data(index, Qt::DisplayRole).toString());
130 }
131 } else if (service == QLatin1String(DeleteService)) {
132 KSharedConfig::Ptr globalConfig = KSharedConfig::openConfig(QStringLiteral("kdeglobals"), KConfig::NoGlobals);
133 KConfigGroup configGroup(globalConfig, "KDE");
134 configGroup.writeEntry("ShowDeleteCommand", checked);
135 configGroup.sync();
136 } else if (service == QLatin1String(CopyToMoveToService)) {
137 ContextMenuSettings::setShowCopyMoveMenu(checked);
138 ContextMenuSettings::self()->save();
139 } else if (service == QLatin1String(AddToPlacesService)) {
140 ContextMenuSettings::setShowAddToPlaces(checked);
141 ContextMenuSettings::self()->save();
142 } else if (service == QLatin1String(SortByService)) {
143 ContextMenuSettings::setShowSortBy(checked);
144 ContextMenuSettings::self()->save();
145 } else if (service == QLatin1String(ViewModeService)) {
146 ContextMenuSettings::setShowViewMode(checked);
147 ContextMenuSettings::self()->save();
148 } else if (service == QLatin1String(OpenInNewTabService)) {
149 ContextMenuSettings::setShowOpenInNewTab(checked);
150 ContextMenuSettings::self()->save();
151 } else if (service == QLatin1String(OpenInNewWindowService)) {
152 ContextMenuSettings::setShowOpenInNewWindow(checked);
153 ContextMenuSettings::self()->save();
154 } else if (service == QLatin1String(CopyLocationService)) {
155 ContextMenuSettings::setShowCopyLocation(checked);
156 ContextMenuSettings::self()->save();
157 } else if (service == QLatin1String(DuplicateHereService)) {
158 ContextMenuSettings::setShowDuplicateHere(checked);
159 ContextMenuSettings::self()->save();
160 } else {
161 showGroup.writeEntry(service, checked);
162 }
163 }
164
165 showGroup.sync();
166
167 if (m_enabledVcsPlugins != enabledPlugins) {
168 VersionControlSettings::setEnabledPlugins(enabledPlugins);
169 VersionControlSettings::self()->save();
170
171 KMessageBox::information(window(),
172 i18nc("@info", "Dolphin must be restarted to apply the "
173 "updated version control systems settings."),
174 QString(), // default title
175 QStringLiteral("ShowVcsRestartInformation"));
176 }
177 }
178
179 void ContextMenuSettingsPage::restoreDefaults()
180 {
181 QAbstractItemModel* model = m_listView->model();
182 for (int i = 0; i < model->rowCount(); ++i) {
183 const QModelIndex index = model->index(i, 0);
184 const QString service = model->data(index, ServiceModel::DesktopEntryNameRole).toString();
185
186 const bool checked = !service.startsWith(VersionControlServicePrefix)
187 && service != QLatin1String(DeleteService)
188 && service != QLatin1String(CopyToMoveToService);
189 model->setData(index, checked, Qt::CheckStateRole);
190 }
191 }
192
193 void ContextMenuSettingsPage::showEvent(QShowEvent* event)
194 {
195 if (!event->spontaneous() && !m_initialized) {
196 loadServices();
197
198 loadVersionControlSystems();
199
200 // Add "Show 'Delete' command" as service
201 KSharedConfig::Ptr globalConfig = KSharedConfig::openConfig(QStringLiteral("kdeglobals"), KConfig::IncludeGlobals);
202 KConfigGroup configGroup(globalConfig, "KDE");
203 addRow(QStringLiteral("edit-delete"),
204 i18nc("@option:check", "Delete"),
205 DeleteService,
206 configGroup.readEntry("ShowDeleteCommand", ShowDeleteDefault));
207
208 // Add "Show 'Copy To' and 'Move To' commands" as service
209 addRow(QStringLiteral("edit-copy"),
210 i18nc("@option:check", "'Copy To' and 'Move To' commands"),
211 CopyToMoveToService,
212 ContextMenuSettings::showCopyMoveMenu());
213
214 // Add other built-in actions
215 addRow(QStringLiteral("bookmark-new"),
216 i18nc("@option:check", "Add to Places"),
217 AddToPlacesService,
218 ContextMenuSettings::showAddToPlaces());
219 addRow(QStringLiteral("view-sort"),
220 i18nc("@option:check", "Sort By"),
221 SortByService,
222 ContextMenuSettings::showSortBy());
223 addRow(QStringLiteral("view-list-icons"),
224 i18nc("@option:check", "View Mode"),
225 ViewModeService,
226 ContextMenuSettings::showViewMode());
227 addRow(QStringLiteral("folder-new"),
228 i18nc("@option:check", "'Open in New Tab' and 'Open in New Tabs'"),
229 OpenInNewTabService,
230 ContextMenuSettings::showOpenInNewTab());
231 addRow(QStringLiteral("window-new"),
232 i18nc("@option:check", "Open in New Window"),
233 OpenInNewWindowService,
234 ContextMenuSettings::showOpenInNewWindow());
235 addRow(QStringLiteral("edit-copy"),
236 i18nc("@option:check", "Copy Location"),
237 CopyLocationService,
238 ContextMenuSettings::showCopyLocation());
239 addRow(QStringLiteral("edit-copy"),
240 i18nc("@option:check", "Duplicate Here"),
241 DuplicateHereService,
242 ContextMenuSettings::showDuplicateHere());
243
244 m_sortModel->sort(Qt::DisplayRole);
245
246 m_initialized = true;
247 }
248 SettingsPageBase::showEvent(event);
249 }
250
251 void ContextMenuSettingsPage::loadServices()
252 {
253 const KConfig config(QStringLiteral("kservicemenurc"), KConfig::NoGlobals);
254 const KConfigGroup showGroup = config.group("Show");
255
256 // Load generic services
257 const KService::List entries = KServiceTypeTrader::self()->query(QStringLiteral("KonqPopupMenu/Plugin"));
258 for (const KService::Ptr &service : entries) {
259 const QString file = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kservices5/" % service->entryPath());
260 const QList<KServiceAction> serviceActions = KDesktopFileActions::userDefinedServices(file, true);
261
262 const KDesktopFile desktopFile(file);
263 const QString subMenuName = desktopFile.desktopGroup().readEntry("X-KDE-Submenu");
264
265 for (const KServiceAction &action : serviceActions) {
266 const QString serviceName = action.name();
267 const bool addService = !action.noDisplay() && !action.isSeparator() && !isInServicesList(serviceName);
268
269 if (addService) {
270 const QString itemName = subMenuName.isEmpty()
271 ? action.text()
272 : i18nc("@item:inmenu", "%1: %2", subMenuName, action.text());
273 const bool checked = showGroup.readEntry(serviceName, true);
274 addRow(action.icon(), itemName, serviceName, checked);
275 }
276 }
277 }
278
279 // Load service plugins that implement the KFileItemActionPlugin interface
280 const KService::List pluginServices = KServiceTypeTrader::self()->query(QStringLiteral("KFileItemAction/Plugin"));
281 for (const KService::Ptr &service : pluginServices) {
282 const QString desktopEntryName = service->desktopEntryName();
283 if (!isInServicesList(desktopEntryName)) {
284 const bool checked = showGroup.readEntry(desktopEntryName, true);
285 addRow(service->icon(), service->name(), desktopEntryName, checked);
286 }
287 }
288
289 // Load JSON-based plugins that implement the KFileItemActionPlugin interface
290 const auto jsonPlugins = KPluginLoader::findPlugins(QStringLiteral("kf5/kfileitemaction"), [](const KPluginMetaData& metaData) {
291 return metaData.serviceTypes().contains(QLatin1String("KFileItemAction/Plugin"));
292 });
293
294 for (const auto &jsonMetadata : jsonPlugins) {
295 const QString desktopEntryName = jsonMetadata.pluginId();
296 if (!isInServicesList(desktopEntryName)) {
297 const bool checked = showGroup.readEntry(desktopEntryName, true);
298 addRow(jsonMetadata.iconName(), jsonMetadata.name(), desktopEntryName, checked);
299 }
300 }
301
302 m_sortModel->sort(Qt::DisplayRole);
303 m_searchLineEdit->setFocus(Qt::OtherFocusReason);
304 }
305
306 void ContextMenuSettingsPage::loadVersionControlSystems()
307 {
308 const QStringList enabledPlugins = VersionControlSettings::enabledPlugins();
309
310 // Create a checkbox for each available version control plugin
311 const KService::List pluginServices = KServiceTypeTrader::self()->query(QStringLiteral("FileViewVersionControlPlugin"));
312 for (const auto &plugin : pluginServices) {
313 const QString pluginName = plugin->name();
314 addRow(QStringLiteral("code-class"),
315 pluginName,
316 VersionControlServicePrefix + pluginName,
317 enabledPlugins.contains(pluginName));
318 }
319
320 m_sortModel->sort(Qt::DisplayRole);
321 }
322
323 bool ContextMenuSettingsPage::isInServicesList(const QString &service) const
324 {
325 for (int i = 0; i < m_serviceModel->rowCount(); ++i) {
326 const QModelIndex index = m_serviceModel->index(i, 0);
327 if (m_serviceModel->data(index, ServiceModel::DesktopEntryNameRole).toString() == service) {
328 return true;
329 }
330 }
331 return false;
332 }
333
334 void ContextMenuSettingsPage::addRow(const QString &icon,
335 const QString &text,
336 const QString &value,
337 bool checked)
338 {
339 m_serviceModel->insertRow(0);
340
341 const QModelIndex index = m_serviceModel->index(0, 0);
342 m_serviceModel->setData(index, icon, Qt::DecorationRole);
343 m_serviceModel->setData(index, text, Qt::DisplayRole);
344 m_serviceModel->setData(index, value, ServiceModel::DesktopEntryNameRole);
345 m_serviceModel->setData(index, checked, Qt::CheckStateRole);
346 }