1/*
2 Copyright (c) 1999 Matthias Hoelzer-Kluepfel <hoelzer@kde.org>
3 Copyright (c) 2000 Matthias Elter <elter@kde.org>
4 Copyright (c) 2004 Frans Englich <frans.englich@telia.com>
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
20*/
21
22#include "main.h"
23
24#include <iostream>
25
26#include <QtDBus/QtDBus>
27
28#include <KAboutData>
29#include <KApplication>
30#include <KAuthorized>
31#include <KCmdLineArgs>
32#include <KCModuleInfo>
33#include <KCMultiDialog>
34#include <KDebug>
35#include <KLocale>
36#include <KServiceTypeTrader>
37#include <KStartupInfo>
38#include <KGlobal>
39#include <KIcon>
40#include <kdeversion.h>
41
42#include "main.moc"
43
44using namespace std;
45
46KService::List m_modules;
47
48static int debugArea() {
49 static int s_area = KDebug::registerArea("kcmshell");
50 return s_area;
51}
52
53static bool caseInsensitiveLessThan(const KService::Ptr s1, const KService::Ptr s2)
54{
55 const int compare = QString::compare(s1->desktopEntryName(),
56 s2->desktopEntryName(),
57 Qt::CaseInsensitive);
58 return (compare < 0);
59}
60
61static void listModules()
62{
63 const KService::List services = KServiceTypeTrader::self()->query( "KCModule", "[X-KDE-ParentApp] == 'kcontrol' or [X-KDE-ParentApp] == 'kinfocenter'" );
64 for( KService::List::const_iterator it = services.begin();
65 it != services.end(); ++it)
66 {
67 const KService::Ptr s = (*it);
68 if (!KAuthorized::authorizeControlModule(s->menuId()))
69 continue;
70 m_modules.append(s);
71 }
72
73 qStableSort(m_modules.begin(), m_modules.end(), caseInsensitiveLessThan);
74}
75
76static KService::Ptr locateModule(const QString& module)
77{
78 QString path = module;
79
80 if (!path.endsWith(QLatin1String(".desktop")))
81 path += ".desktop";
82
83 KService::Ptr service = KService::serviceByStorageId( path );
84 if (!service) {
85 return KService::Ptr();
86 }
87
88 if (!service->hasServiceType("KCModule")) {
89 // Not a KCModule. E.g. "kcmshell4 akonadi" finds services/kresources/kabc/akonadi.desktop, unrelated.
90 return KService::Ptr();
91 }
92
93 if ( service->noDisplay() ) {
94 kDebug(debugArea()) << module << " should not be loaded.";
95 return KService::Ptr();
96 }
97
98 return service;
99}
100
101bool KCMShell::isRunning()
102{
103 QString owner = QDBusConnection::sessionBus().interface()->serviceOwner(m_serviceName);
104 if( owner == QDBusConnection::sessionBus().baseService() )
105 return false; // We are the one and only.
106
107 kDebug(debugArea()) << "kcmshell4 with modules '" <<
108 m_serviceName << "' is already running." << endl;
109
110 QDBusInterface iface(m_serviceName, "/KCModule/dialog", "org.kde.KCMShellMultiDialog");
111 QDBusReply<void> reply = iface.call("activate", kapp->startupId());
112 if (!reply.isValid())
113 {
114 kDebug(debugArea()) << "Calling D-Bus function dialog::activate() failed.";
115 return false; // Error, we have to do it ourselves.
116 }
117
118 return true;
119}
120
121KCMShellMultiDialog::KCMShellMultiDialog(KPageDialog::FaceType dialogFace, QWidget *parent)
122 : KCMultiDialog(parent)
123{
124 setFaceType(dialogFace);
125 setModal(true);
126
127 QDBusConnection::sessionBus().registerObject("/KCModule/dialog", this, QDBusConnection::ExportScriptableSlots);
128}
129
130void KCMShellMultiDialog::activate( const QByteArray& asn_id )
131{
132 kDebug(debugArea()) ;
133
134#ifdef Q_WS_X11
135 KStartupInfo::setNewStartupId( this, asn_id );
136#endif
137}
138
139void KCMShell::setServiceName(const QString &dbusName )
140{
141 m_serviceName = QLatin1String( "org.kde.kcmshell_" ) + dbusName;
142 QDBusConnection::sessionBus().registerService(m_serviceName);
143}
144
145void KCMShell::waitForExit()
146{
147 kDebug(debugArea());
148
149 QDBusServiceWatcher *watcher = new QDBusServiceWatcher(this);
150 watcher->setConnection(QDBusConnection::sessionBus());
151 watcher->setWatchMode(QDBusServiceWatcher::WatchForOwnerChange);
152 watcher->addWatchedService(m_serviceName);
153 connect(watcher, SIGNAL(serviceOwnerChanged(QString,QString,QString)),
154 SLOT(appExit(QString,QString,QString)));
155 exec();
156}
157
158void KCMShell::appExit(const QString &appId, const QString &oldName, const QString &newName)
159{
160 Q_UNUSED(appId);
161 Q_UNUSED(newName);
162 kDebug(debugArea());
163
164 if (!oldName.isEmpty())
165 {
166 kDebug(debugArea()) << "'" << appId << "' closed, dereferencing.";
167 KGlobal::deref();
168 }
169}
170
171extern "C" KDE_EXPORT int kdemain(int _argc, char *_argv[])
172{
173 KAboutData aboutData( "kcmshell", 0, ki18n("KDE Control Module"),
174 KDE_VERSION_STRING,
175 ki18n("A tool to start single KDE control modules"),
176 KAboutData::License_GPL,
177 ki18n("(c) 1999-2004, The KDE Developers") );
178
179 aboutData.addAuthor(ki18n("Frans Englich"), ki18n("Maintainer"), "frans.englich@kde.org");
180 aboutData.addAuthor(ki18n("Daniel Molkentin"), KLocalizedString(), "molkentin@kde.org");
181 aboutData.addAuthor(ki18n("Matthias Hoelzer-Kluepfel"),KLocalizedString(), "hoelzer@kde.org");
182 aboutData.addAuthor(ki18n("Matthias Elter"),KLocalizedString(), "elter@kde.org");
183 aboutData.addAuthor(ki18n("Matthias Ettrich"),KLocalizedString(), "ettrich@kde.org");
184 aboutData.addAuthor(ki18n("Waldo Bastian"),KLocalizedString(), "bastian@kde.org");
185
186 KCmdLineArgs::init(_argc, _argv, &aboutData);
187
188 KCmdLineOptions options;
189 options.add("list", ki18n("List all possible modules"));
190 options.add("+module", ki18n("Configuration module to open"));
191 options.add("lang <language>", ki18n("Specify a particular language"));
192 options.add("silent", ki18n("Do not display main window"));
193 options.add("args <arguments>", ki18n("Arguments for the module"));
194 KCmdLineArgs::addCmdLineOptions( options ); // Add our own options.
195 KCMShell app;
196
197 const KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
198
199 const QString lang = args->getOption("lang");
200 if( !lang.isEmpty() ) {
201 KGlobal::setLocale(new KLocale(aboutData.catalogName(), lang));
202 }
203
204 if (args->isSet("list"))
205 {
206 cout << i18n("The following modules are available:").toLocal8Bit().data() << endl;
207
208 listModules();
209
210 int maxLen=0;
211
212 for( KService::List::ConstIterator it = m_modules.constBegin(); it != m_modules.constEnd(); ++it)
213 {
214 int len = (*it)->desktopEntryName().length();
215 if (len > maxLen)
216 maxLen = len;
217 }
218
219 for( KService::List::ConstIterator it = m_modules.constBegin(); it != m_modules.constEnd(); ++it)
220 {
221 QString entry("%1 - %2");
222
223 entry = entry.arg((*it)->desktopEntryName().leftJustified(maxLen, ' '))
224 .arg(!(*it)->comment().isEmpty() ? (*it)->comment()
225 : i18n("No description available"));
226
227 cout << entry.toLocal8Bit().data() << endl;
228 }
229 return 0;
230 }
231
232 if (args->count() < 1)
233 {
234 args->usage();
235 return -1;
236 }
237
238 QString serviceName;
239 KService::List modules;
240 for (int i = 0; i < args->count(); i++)
241 {
242 const QString arg = args->arg(i);
243 KService::Ptr service = locateModule(arg);
244 if (!service) {
245 service = locateModule("kcm_" + arg);
246 }
247 if (!service) {
248 service = locateModule("kcm" + arg);
249 }
250
251 if (service) {
252 modules.append(service);
253 if( !serviceName.isEmpty() )
254 serviceName += '_';
255 serviceName += args->arg(i);
256 } else {
257 fprintf(stderr, "%s\n", i18n("Could not find module '%1'. See kcmshell4 --list for the full list of modules.", arg).toLocal8Bit().constData());
258 }
259 }
260
261 /* Check if this particular module combination is already running */
262 app.setServiceName(serviceName);
263 if( app.isRunning() ) {
264 app.waitForExit();
265 return 0;
266 }
267
268 KPageDialog::FaceType ftype = KPageDialog::Plain;
269
270 if (modules.count() < 1) {
271 return 0;
272 } else if (modules.count() > 1) {
273 ftype = KPageDialog::List;
274 }
275
276 QStringList moduleArgs;
277 QString x = args->getOption("args");
278 moduleArgs << x.split(QRegExp(" +"));
279
280 KCMShellMultiDialog *dlg = new KCMShellMultiDialog(ftype);
281 KCmdLineArgs *kdeargs = KCmdLineArgs::parsedArgs("kde");
282 if (kdeargs && kdeargs->isSet("caption")) {
283 dlg->setCaption(QString());
284 kdeargs->clear();
285 } else if (modules.count() == 1) {
286 dlg->setCaption(modules.first()->name());
287 }
288
289 for (KService::List::ConstIterator it = modules.constBegin(); it != modules.constEnd(); ++it)
290 dlg->addModule(*it, 0, moduleArgs);
291
292 if ( !args->isSet( "icon" ) && modules.count() == 1)
293 {
294 QString iconName = KCModuleInfo(modules.first()).icon();
295 dlg->setWindowIcon( KIcon(iconName) );
296 }
297 dlg->exec();
298 delete dlg;
299
300 return 0;
301}
302// vim: sw=4 et sts=4
303