1/*
2 SPDX-FileCopyrightText: 1999 Matthias Hoelzer-Kluepfel <hoelzer@kde.org>
3 SPDX-FileCopyrightText: 2000 Matthias Elter <elter@kde.org>
4 SPDX-FileCopyrightText: 2004 Frans Englich <frans.englich@telia.com>
5 SPDX-FileCopyrightText: 2023 Alexander Lohnau <alexander.lohnau@gmx.de>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8
9*/
10
11#include <QApplication>
12#include <QCommandLineOption>
13#include <QCommandLineParser>
14#include <QDebug>
15#include <QIcon>
16#include <QRegularExpression>
17#include <QStandardPaths>
18
19#include <KAboutData>
20#include <KAuthorized>
21#include <KCModule>
22#include <KCMultiDialog>
23#include <KLocalizedString>
24#include <KPageDialog>
25#include <KPluginMetaData>
26#include <KShell>
27#include <kcmutils_debug.h>
28
29#if __has_include(<KStartupInfo>)
30#include <KStartupInfo>
31#include <private/qtx11extras_p.h>
32#define HAVE_X11 1
33#endif
34
35#if !defined(Q_OS_WINDOWS) && !defined(Q_OS_MAC)
36#include <QDBusConnection>
37#include <QDBusConnectionInterface>
38#endif
39
40#include <algorithm>
41#include <iostream>
42
43inline QList<KPluginMetaData> findKCMsMetaData()
44{
45 QList<KPluginMetaData> metaDataList = KPluginMetaData::findPlugins(QStringLiteral("plasma/kcms"));
46 metaDataList << KPluginMetaData::findPlugins(QStringLiteral("plasma/kcms/systemsettings"));
47 metaDataList << KPluginMetaData::findPlugins(QStringLiteral("plasma/kcms/systemsettings_qwidgets"));
48 metaDataList << KPluginMetaData::findPlugins(QStringLiteral("plasma/kcms/kinfocenter"));
49 return metaDataList;
50}
51
52class KCMShellMultiDialog : public KCMultiDialog
53{
54 Q_OBJECT
55
56public:
57 /**
58 * Constructor. Parameter @p dialogFace is passed to KCMultiDialog
59 * unchanged.
60 */
61 explicit KCMShellMultiDialog(KPageDialog::FaceType dialogFace)
62 : KCMultiDialog()
63 {
64 setFaceType(dialogFace);
65 setModal(false);
66
67#if !defined(Q_OS_WINDOWS) && !defined(Q_OS_MAC)
68 connect(sender: this, signal: &KCMShellMultiDialog::currentPageChanged, context: this, slot: [](KPageWidgetItem *newPage) {
69 if (KCModule *activeModule = newPage->widget()->findChild<KCModule *>()) {
70 if (QDBusConnection::sessionBus().isConnected()
71 && QDBusConnection::sessionBus().interface()->isServiceRegistered(QStringLiteral("org.kde.ActivityManager"))) {
72 QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.ActivityManager"),
73 QStringLiteral("/ActivityManager/Resources"),
74 QStringLiteral("org.kde.ActivityManager.Resources"),
75 QStringLiteral("RegisterResourceEvent"));
76
77 const QString appId = QStringLiteral("org.kde.systemsettings");
78 const uint winId = 0;
79 const QString url = QLatin1String("kcm:") + activeModule->metaData().pluginId();
80 const uint eventType = 0; // Accessed
81
82 msg.setArguments({appId, winId, url, eventType});
83
84 QDBusConnection::sessionBus().asyncCall(message: msg);
85 }
86 }
87 });
88#endif
89 }
90};
91
92int main(int argc, char *argv[])
93{
94 const bool qpaVariable = qEnvironmentVariableIsSet(varName: "QT_QPA_PLATFORM");
95 QApplication app(argc, argv);
96 if (!qpaVariable) {
97 // don't leak the env variable to processes we start
98 qunsetenv(varName: "QT_QPA_PLATFORM");
99 }
100 KLocalizedString::setApplicationDomain("kcmshell6");
101 KAboutData aboutData(QStringLiteral("kcmshell6"),
102 QString(),
103 QLatin1String(PROJECT_VERSION),
104 i18n("A tool to start single system settings modules"),
105 KAboutLicense::GPL,
106 i18n("(c) 1999-2023, The KDE Developers"));
107
108 aboutData.addAuthor(i18n("Frans Englich"), i18n("Maintainer"), QStringLiteral("frans.englich@kde.org"));
109 aboutData.addAuthor(i18n("Daniel Molkentin"), task: QString(), QStringLiteral("molkentin@kde.org"));
110 aboutData.addAuthor(i18n("Matthias Hoelzer-Kluepfel"), task: QString(), QStringLiteral("hoelzer@kde.org"));
111 aboutData.addAuthor(i18n("Matthias Elter"), task: QString(), QStringLiteral("elter@kde.org"));
112 aboutData.addAuthor(i18n("Matthias Ettrich"), task: QString(), QStringLiteral("ettrich@kde.org"));
113 aboutData.addAuthor(i18n("Waldo Bastian"), task: QString(), QStringLiteral("bastian@kde.org"));
114 KAboutData::setApplicationData(aboutData);
115
116 QCommandLineParser parser;
117 aboutData.setupCommandLine(&parser);
118
119 parser.addOption(commandLineOption: QCommandLineOption(QStringLiteral("list"), i18n("List all possible modules")));
120 parser.addPositionalArgument(QStringLiteral("module"), i18n("Configuration module to open"));
121 parser.addOption(commandLineOption: QCommandLineOption(QStringLiteral("args"), i18n("Space separated arguments for the module"), QLatin1String("arguments")));
122 parser.addOption(commandLineOption: QCommandLineOption(QStringLiteral("icon"), i18n("Use a specific icon for the window"), QLatin1String("icon")));
123 parser.addOption(commandLineOption: QCommandLineOption(QStringLiteral("caption"), i18n("Use a specific caption for the window"), QLatin1String("caption")));
124 parser.addOption(commandLineOption: QCommandLineOption(QStringLiteral("highlight"), i18n("Show an indicator when settings have changed from their default value")));
125
126 parser.parse(arguments: app.arguments());
127 aboutData.processCommandLine(parser: &parser);
128
129 parser.process(app);
130
131 if (parser.isSet(QStringLiteral("list"))) {
132 std::cout << i18n("The following modules are available:").toLocal8Bit().constData() << '\n';
133
134 QList<KPluginMetaData> plugins = findKCMsMetaData();
135 int maxLen = 0;
136
137 for (const auto &plugin : plugins) {
138 const int len = plugin.pluginId().size();
139 maxLen = std::max(a: maxLen, b: len);
140 }
141
142 for (const auto &plugin : plugins) {
143 QString comment = plugin.description();
144 if (comment.isEmpty()) {
145 comment = i18n("No description available");
146 }
147
148 const QString entry = QStringLiteral("%1 - %2").arg(args: plugin.pluginId().leftJustified(width: maxLen, fill: QLatin1Char(' ')), args&: comment);
149
150 std::cout << entry.toLocal8Bit().constData() << '\n';
151 }
152
153 std::cout << std::endl;
154
155 return 0;
156 }
157
158 if (parser.positionalArguments().isEmpty()) {
159 parser.showHelp();
160 return -1;
161 }
162
163 QList<KPluginMetaData> metaDataList;
164
165 QStringList args = parser.positionalArguments();
166 args.removeDuplicates();
167 for (const QString &arg : args) {
168 if (KPluginMetaData data(arg, KPluginMetaData::AllowEmptyMetaData); data.isValid()) {
169 metaDataList << data;
170 } else {
171 // Look in the namespaces for systemsettings/kinfocenter
172 const static auto knownKCMs = findKCMsMetaData();
173 const QStringList possibleIds{arg, QStringLiteral("kcm_") + arg, QStringLiteral("kcm") + arg};
174 bool found = std::any_of(first: knownKCMs.begin(), last: knownKCMs.end(), pred: [&possibleIds, &metaDataList](const KPluginMetaData &data) {
175 bool idMatches = possibleIds.contains(str: data.pluginId());
176 if (idMatches) {
177 metaDataList << data;
178 }
179 return idMatches;
180 });
181 if (!found) {
182 metaDataList << KPluginMetaData(arg); // So that we show an error message in the dialog
183 qCWarning(KCMUTILS_LOG) << "Could not find KCM with given Id" << arg;
184 }
185 }
186 }
187 if (metaDataList.isEmpty()) {
188 return -1;
189 }
190
191 const bool multipleKCMs = metaDataList.size() > 1;
192 KPageDialog::FaceType ftype = multipleKCMs ? KPageDialog::List : KPageDialog::Plain;
193 auto dlg = new KCMShellMultiDialog(ftype);
194 dlg->setAttribute(Qt::WA_DeleteOnClose);
195
196 if (parser.isSet(QStringLiteral("caption"))) {
197 dlg->setWindowTitle(parser.value(QStringLiteral("caption")));
198 } else if (!multipleKCMs) { // We will have the "Configure" window title set by KCMultiDialog
199 dlg->setWindowTitle(metaDataList.constFirst().name());
200 }
201
202 const QStringList argSplit = KShell::splitArgs(cmd: parser.value(QStringLiteral("args")));
203 QVariantList pluginArgs(argSplit.begin(), argSplit.end());
204 if (metaDataList.size() == 1) {
205 KPageWidgetItem *item = dlg->addModule(metaData: *metaDataList.cbegin(), args: pluginArgs);
206 // This makes sure the content area is focused by default
207 item->widget()->setFocus(Qt::MouseFocusReason);
208 } else {
209 for (const KPluginMetaData &m : std::as_const(t&: metaDataList)) {
210 dlg->addModule(metaData: m, args: pluginArgs);
211 }
212 }
213
214 if (parser.isSet(QStringLiteral("icon"))) {
215 dlg->setWindowIcon(QIcon::fromTheme(name: parser.value(QStringLiteral("icon"))));
216 } else {
217 dlg->setWindowIcon(QIcon::fromTheme(name: metaDataList.constFirst().iconName()));
218 }
219
220 if (parser.isSet(QStringLiteral("highlight"))) {
221 dlg->setDefaultsIndicatorsVisible(true);
222 }
223
224 if (app.desktopFileName() == QLatin1String("org.kde.kcmshell6")) {
225 const QString path = metaDataList.constFirst().fileName();
226
227 if (path.endsWith(s: QLatin1String(".desktop"))) {
228 app.setDesktopFileName(path);
229 } else {
230 app.setDesktopFileName(metaDataList.constFirst().pluginId());
231 }
232 }
233
234 dlg->show();
235
236 app.exec();
237
238 return 0;
239}
240
241#include "main.moc"
242

source code of kcmutils/src/kcmshell/main.cpp