File indexing completed on 2024-05-05 17:53:45
0001 /* 0002 * SPDX-FileCopyrightText: 2009-2010 Peter Penz <peter.penz19@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "contextmenusettingspage.h" 0008 0009 #include "dolphin_contextmenusettings.h" 0010 #include "dolphin_versioncontrolsettings.h" 0011 #include "global.h" 0012 #include "settings/servicemodel.h" 0013 0014 #include <KDesktopFile> 0015 #include <KFileUtils> 0016 #include <KLocalizedString> 0017 #include <KMessageBox> 0018 #include <KPluginMetaData> 0019 #include <KService> 0020 #include <kiocore_export.h> 0021 #include <kservice_export.h> 0022 #include <kwidgetsaddons_version.h> 0023 0024 #include <KNSWidgets/Button> 0025 #include <QtGlobal> 0026 0027 #include <QApplication> 0028 #include <QGridLayout> 0029 #include <QLabel> 0030 #include <QLineEdit> 0031 #include <QListWidget> 0032 #include <QScroller> 0033 #include <QShowEvent> 0034 #include <QSortFilterProxyModel> 0035 0036 namespace 0037 { 0038 const bool ShowDeleteDefault = false; 0039 const char VersionControlServicePrefix[] = "_version_control_"; 0040 const char DeleteService[] = "_delete"; 0041 const char CopyToMoveToService[] = "_copy_to_move_to"; 0042 0043 bool laterSelected = false; 0044 } 0045 0046 ContextMenuSettingsPage::ContextMenuSettingsPage(QWidget *parent, const KActionCollection *actions, const QStringList &actionIds) 0047 : SettingsPageBase(parent) 0048 , m_initialized(false) 0049 , m_serviceModel(nullptr) 0050 , m_sortModel(nullptr) 0051 , m_listView(nullptr) 0052 , m_enabledVcsPlugins() 0053 , m_actions(actions) 0054 , m_actionIds(actionIds) 0055 { 0056 QVBoxLayout *topLayout = new QVBoxLayout(this); 0057 0058 QLabel *label = new QLabel(i18nc("@label:textbox", 0059 "Select which services should " 0060 "be shown in the context menu:"), 0061 this); 0062 label->setWordWrap(true); 0063 m_searchLineEdit = new QLineEdit(this); 0064 m_searchLineEdit->setPlaceholderText(i18nc("@label:textbox", "Search…")); 0065 connect(m_searchLineEdit, &QLineEdit::textChanged, this, [this](const QString &filter) { 0066 m_sortModel->setFilterFixedString(filter); 0067 }); 0068 0069 m_listView = new QListView(this); 0070 QScroller::grabGesture(m_listView->viewport(), QScroller::TouchGesture); 0071 0072 m_serviceModel = new ServiceModel(this); 0073 m_sortModel = new QSortFilterProxyModel(this); 0074 m_sortModel->setSourceModel(m_serviceModel); 0075 m_sortModel->setSortRole(Qt::DisplayRole); 0076 m_sortModel->setSortLocaleAware(true); 0077 m_sortModel->setFilterRole(Qt::DisplayRole); 0078 m_sortModel->setFilterCaseSensitivity(Qt::CaseInsensitive); 0079 m_listView->setModel(m_sortModel); 0080 m_listView->setVerticalScrollMode(QListView::ScrollPerPixel); 0081 connect(m_listView, &QListView::clicked, this, &ContextMenuSettingsPage::changed); 0082 0083 topLayout->addWidget(label); 0084 topLayout->addWidget(m_searchLineEdit); 0085 topLayout->addWidget(m_listView); 0086 0087 #ifndef Q_OS_WIN 0088 using NewStuffButton = KNSWidgets::Button; 0089 auto *downloadButton = new NewStuffButton(i18nc("@action:button", "Download New Services…"), QStringLiteral("servicemenu.knsrc"), this); 0090 connect(downloadButton, &NewStuffButton::dialogFinished, this, [this](const auto &changedEntries) { 0091 if (!changedEntries.isEmpty()) { 0092 m_serviceModel->clear(); 0093 loadServices(); 0094 } 0095 }); 0096 topLayout->addWidget(downloadButton); 0097 #endif // Q_OS_WIN 0098 0099 m_enabledVcsPlugins = VersionControlSettings::enabledPlugins(); 0100 std::sort(m_enabledVcsPlugins.begin(), m_enabledVcsPlugins.end()); 0101 } 0102 0103 ContextMenuSettingsPage::~ContextMenuSettingsPage() 0104 { 0105 } 0106 0107 bool ContextMenuSettingsPage::entryVisible(const QString &id) 0108 { 0109 if (id == "add_to_places") { 0110 return ContextMenuSettings::showAddToPlaces(); 0111 } else if (id == "sort") { 0112 return ContextMenuSettings::showSortBy(); 0113 } else if (id == "view_mode") { 0114 return ContextMenuSettings::showViewMode(); 0115 } else if (id == "open_in_new_tab") { 0116 return ContextMenuSettings::showOpenInNewTab(); 0117 } else if (id == "open_in_new_window") { 0118 return ContextMenuSettings::showOpenInNewWindow(); 0119 } else if (id == "open_in_split_view") { 0120 return ContextMenuSettings::showOpenInSplitView(); 0121 } else if (id == "copy_location") { 0122 return ContextMenuSettings::showCopyLocation(); 0123 } else if (id == "duplicate") { 0124 return ContextMenuSettings::showDuplicateHere(); 0125 } else if (id == "open_terminal_here") { 0126 return ContextMenuSettings::showOpenTerminal(); 0127 } else if (id == "copy_to_inactive_split_view") { 0128 return ContextMenuSettings::showCopyToOtherSplitView(); 0129 } else if (id == "move_to_inactive_split_view") { 0130 return ContextMenuSettings::showMoveToOtherSplitView(); 0131 } 0132 return false; 0133 } 0134 0135 void ContextMenuSettingsPage::setEntryVisible(const QString &id, bool visible) 0136 { 0137 if (id == "add_to_places") { 0138 ContextMenuSettings::setShowAddToPlaces(visible); 0139 } else if (id == "sort") { 0140 ContextMenuSettings::setShowSortBy(visible); 0141 } else if (id == "view_mode") { 0142 ContextMenuSettings::setShowViewMode(visible); 0143 } else if (id == "open_in_new_tab") { 0144 ContextMenuSettings::setShowOpenInNewTab(visible); 0145 } else if (id == "open_in_new_window") { 0146 ContextMenuSettings::setShowOpenInNewWindow(visible); 0147 } else if (id == "open_in_split_view") { 0148 return ContextMenuSettings::setShowOpenInSplitView(visible); 0149 } else if (id == "copy_location") { 0150 ContextMenuSettings::setShowCopyLocation(visible); 0151 } else if (id == "duplicate") { 0152 ContextMenuSettings::setShowDuplicateHere(visible); 0153 } else if (id == "open_terminal_here") { 0154 ContextMenuSettings::setShowOpenTerminal(visible); 0155 } else if (id == "copy_to_inactive_split_view") { 0156 ContextMenuSettings::setShowCopyToOtherSplitView(visible); 0157 } else if (id == "move_to_inactive_split_view") { 0158 ContextMenuSettings::setShowMoveToOtherSplitView(visible); 0159 } 0160 } 0161 0162 void ContextMenuSettingsPage::applySettings() 0163 { 0164 if (!m_initialized) { 0165 return; 0166 } 0167 0168 KConfig config(QStringLiteral("kservicemenurc"), KConfig::NoGlobals); 0169 KConfigGroup showGroup = config.group(QStringLiteral("Show")); 0170 0171 QStringList enabledPlugins; 0172 0173 for (int i = 0; i < m_serviceModel->rowCount(); ++i) { 0174 const QModelIndex index = m_serviceModel->index(i, 0); 0175 const QString service = m_serviceModel->data(index, ServiceModel::DesktopEntryNameRole).toString(); 0176 const bool checked = m_serviceModel->data(index, Qt::CheckStateRole).value<Qt::CheckState>() == Qt::Checked; 0177 0178 if (service.startsWith(VersionControlServicePrefix)) { 0179 if (checked) { 0180 enabledPlugins.append(m_serviceModel->data(index, Qt::DisplayRole).toString()); 0181 } 0182 } else if (service == QLatin1String(DeleteService)) { 0183 KSharedConfig::Ptr globalConfig = KSharedConfig::openConfig(QStringLiteral("kdeglobals"), KConfig::NoGlobals); 0184 KConfigGroup configGroup(globalConfig, QStringLiteral("KDE")); 0185 configGroup.writeEntry("ShowDeleteCommand", checked); 0186 configGroup.sync(); 0187 } else if (service == QLatin1String(CopyToMoveToService)) { 0188 ContextMenuSettings::setShowCopyMoveMenu(checked); 0189 ContextMenuSettings::self()->save(); 0190 } else if (m_actionIds.contains(service)) { 0191 setEntryVisible(service, checked); 0192 ContextMenuSettings::self()->save(); 0193 } else { 0194 showGroup.writeEntry(service, checked); 0195 } 0196 } 0197 0198 showGroup.sync(); 0199 0200 if (m_enabledVcsPlugins != enabledPlugins) { 0201 VersionControlSettings::setEnabledPlugins(enabledPlugins); 0202 VersionControlSettings::self()->save(); 0203 0204 if (!laterSelected) { 0205 KMessageBox::ButtonCode promptRestart = 0206 KMessageBox::questionTwoActions(window(), 0207 i18nc("@info", 0208 "Dolphin must be restarted to apply the " 0209 "updated version control system settings."), 0210 i18nc("@info", "Restart now?"), 0211 KGuiItem(QApplication::translate("KStandardGuiItem", "&Restart"), QStringLiteral("dialog-restart")), 0212 KGuiItem(QApplication::translate("KStandardGuiItem", "&Later"), QStringLiteral("dialog-later"))); 0213 if (promptRestart == KMessageBox::ButtonCode::PrimaryAction) { 0214 Dolphin::openNewWindow(); 0215 qApp->quit(); 0216 } else { 0217 laterSelected = true; 0218 } 0219 } 0220 } 0221 } 0222 0223 void ContextMenuSettingsPage::restoreDefaults() 0224 { 0225 for (int i = 0; i < m_serviceModel->rowCount(); ++i) { 0226 const QModelIndex index = m_serviceModel->index(i, 0); 0227 const QString service = m_serviceModel->data(index, ServiceModel::DesktopEntryNameRole).toString(); 0228 0229 const bool checked = 0230 !service.startsWith(VersionControlServicePrefix) && service != QLatin1String(DeleteService) && service != QLatin1String(CopyToMoveToService); 0231 m_serviceModel->setData(index, checked ? Qt::Checked : Qt::Unchecked, Qt::CheckStateRole); 0232 } 0233 } 0234 0235 void ContextMenuSettingsPage::showEvent(QShowEvent *event) 0236 { 0237 if (!event->spontaneous() && !m_initialized) { 0238 loadServices(); 0239 0240 loadVersionControlSystems(); 0241 0242 // Add "Show 'Delete' command" as service 0243 KSharedConfig::Ptr globalConfig = KSharedConfig::openConfig(QStringLiteral("kdeglobals"), KConfig::IncludeGlobals); 0244 KConfigGroup configGroup(globalConfig, QStringLiteral("KDE")); 0245 addRow(QStringLiteral("edit-delete"), i18nc("@option:check", "Delete"), DeleteService, configGroup.readEntry("ShowDeleteCommand", ShowDeleteDefault)); 0246 0247 // Add "Show 'Copy To' and 'Move To' commands" as service 0248 addRow(QStringLiteral("edit-copy"), 0249 i18nc("@option:check", "'Copy To' and 'Move To' commands"), 0250 CopyToMoveToService, 0251 ContextMenuSettings::showCopyMoveMenu()); 0252 0253 if (m_actions) { 0254 // Add other built-in actions 0255 for (const QString &id : m_actionIds) { 0256 const QAction *action = m_actions->action(id); 0257 if (action) { 0258 addRow(action->icon().name(), KLocalizedString::removeAcceleratorMarker(action->text()), id, entryVisible(id)); 0259 } 0260 } 0261 } 0262 0263 m_sortModel->sort(Qt::DisplayRole); 0264 0265 m_initialized = true; 0266 } 0267 SettingsPageBase::showEvent(event); 0268 } 0269 0270 void ContextMenuSettingsPage::loadServices() 0271 { 0272 const KConfig config(QStringLiteral("kservicemenurc"), KConfig::NoGlobals); 0273 const KConfigGroup showGroup = config.group(QStringLiteral("Show")); 0274 0275 // Load generic services 0276 const auto locations = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("kio/servicemenus"), QStandardPaths::LocateDirectory); 0277 QStringList files = KFileUtils::findAllUniqueFiles(locations); 0278 0279 for (const auto &file : std::as_const(files)) { 0280 const QList<KServiceAction> serviceActions = KService(file).actions(); 0281 0282 const KDesktopFile desktopFile(file); 0283 const QString subMenuName = desktopFile.desktopGroup().readEntry("X-KDE-Submenu"); 0284 0285 for (const KServiceAction &action : serviceActions) { 0286 const QString serviceName = action.name(); 0287 const bool addService = !action.noDisplay() && !action.isSeparator() && !isInServicesList(serviceName); 0288 0289 if (addService) { 0290 const QString itemName = subMenuName.isEmpty() ? action.text() : i18nc("@item:inmenu", "%1: %2", subMenuName, action.text()); 0291 const bool checked = showGroup.readEntry(serviceName, true); 0292 addRow(action.icon(), itemName, serviceName, checked); 0293 } 0294 } 0295 } 0296 0297 // Load JSON-based plugins that implement the KFileItemActionPlugin interface 0298 const auto jsonPlugins = KPluginMetaData::findPlugins(QStringLiteral("kf6/kfileitemaction")); 0299 0300 for (const auto &jsonMetadata : jsonPlugins) { 0301 const QString desktopEntryName = jsonMetadata.pluginId(); 0302 if (!isInServicesList(desktopEntryName)) { 0303 const bool checked = showGroup.readEntry(desktopEntryName, true); 0304 addRow(jsonMetadata.iconName(), jsonMetadata.name(), desktopEntryName, checked); 0305 } 0306 } 0307 0308 m_sortModel->sort(Qt::DisplayRole); 0309 m_searchLineEdit->setFocus(Qt::OtherFocusReason); 0310 } 0311 0312 void ContextMenuSettingsPage::loadVersionControlSystems() 0313 { 0314 const QStringList enabledPlugins = VersionControlSettings::enabledPlugins(); 0315 0316 // Create a checkbox for each available version control plugin 0317 QSet<QString> loadedPlugins; 0318 0319 const QVector<KPluginMetaData> plugins = KPluginMetaData::findPlugins(QStringLiteral("dolphin/vcs")); 0320 for (const auto &plugin : plugins) { 0321 const QString pluginName = plugin.name(); 0322 addRow(QStringLiteral("code-class"), pluginName, VersionControlServicePrefix + pluginName, enabledPlugins.contains(pluginName)); 0323 loadedPlugins += pluginName; 0324 } 0325 0326 m_sortModel->sort(Qt::DisplayRole); 0327 } 0328 0329 bool ContextMenuSettingsPage::isInServicesList(const QString &service) const 0330 { 0331 for (int i = 0; i < m_serviceModel->rowCount(); ++i) { 0332 const QModelIndex index = m_serviceModel->index(i, 0); 0333 if (m_serviceModel->data(index, ServiceModel::DesktopEntryNameRole).toString() == service) { 0334 return true; 0335 } 0336 } 0337 return false; 0338 } 0339 0340 void ContextMenuSettingsPage::addRow(const QString &icon, const QString &text, const QString &value, bool checked) 0341 { 0342 m_serviceModel->insertRow(0); 0343 0344 const QModelIndex index = m_serviceModel->index(0, 0); 0345 m_serviceModel->setData(index, icon, Qt::DecorationRole); 0346 m_serviceModel->setData(index, text, Qt::DisplayRole); 0347 m_serviceModel->setData(index, value, ServiceModel::DesktopEntryNameRole); 0348 m_serviceModel->setData(index, checked ? Qt::Checked : Qt::Unchecked, Qt::CheckStateRole); 0349 } 0350 0351 #include "moc_contextmenusettingspage.cpp"