kcmkded.cpp 9.37 KB
Newer Older
1
2
3
// vim: noexpandtab shiftwidth=4 tabstop=4
/* This file is part of the KDE project
   Copyright (C) 2002 Daniel Molkentin <molkentin@kde.org>
4
   Copyright (C) 2020 Kai Uwe Broulik <kde@broulik.de>
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; see the file COPYING.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.
*/

#include "kcmkded.h"

24
#include "debug.h"
25

26
27
28
29
30
31
32
#include <QDBusConnection>
#include <QDBusConnectionInterface>
#include <QDBusPendingCall>
#include <QDBusPendingCallWatcher>

#include <KAboutData>
#include <KConfig>
33
#include <KConfigGroup>
34
35
36
#include <KPluginFactory>
#include <KLocalizedString>

37
#include <algorithm>
38

39
40
#include "modulesmodel.h"
#include "filterproxymodel.h"
41

42
#include "kded_interface.h"
43

44
K_PLUGIN_FACTORY_WITH_JSON(KCMStyleFactory, "kcmkded.json", registerPlugin<KDEDConfig>();)
45

46
static const QString s_kdedServiceName = QStringLiteral("org.kde.kded5");
47

48
49
50
51
52
53
54
55
56
57
KDEDConfig::KDEDConfig(QObject* parent, const QVariantList &args)
	: KQuickAddons::ConfigModule(parent, args)
    , m_model(new ModulesModel(this))
    , m_filteredModel(new FilterProxyModel(this))
    , m_kdedInterface(new org::kde::kded5(s_kdedServiceName,
                                          QStringLiteral("/kded"),
                                          QDBusConnection::sessionBus()))
    , m_kdedWatcher(new QDBusServiceWatcher(s_kdedServiceName,
                                            QDBusConnection::sessionBus(),
                                            QDBusServiceWatcher::WatchForOwnerChange, this))
58
{
59
60
61
62
63
64
65
66
67
    qmlRegisterUncreatableType<KDEDConfig>("org.kde.private.kcms.style", 1, 0, "KCM", QStringLiteral("Cannot create instances of KCM"));
    // FIXME Qt 5.14 qmlRegisterAnonymousType
    qmlRegisterType<ModulesModel>();
    qmlRegisterType<FilterProxyModel>();

    KAboutData *about = new KAboutData(QStringLiteral("kcm5_kded"), i18n("Background Services"),
        QStringLiteral("2.0"), QString(), KAboutLicense::GPL,
        i18n("(c) 2002 Daniel Molkentin, (c) 2020 Kai Uwe Broulik")
    );
68
	about->addAuthor(i18n("Daniel Molkentin"), QString(),QStringLiteral("molkentin@kde.org"));
69
70
71
72
73
74
75
    about->addAuthor(i18n("Kai Uwe Broulik"), QString(),QStringLiteral("kde@broulik.de"));
    setAboutData(about);
    setButtons(Apply|Default|Help);

    m_filteredModel->setSourceModel(m_model);

    connect(m_model, &ModulesModel::autoloadedModulesChanged, this, [this] {
76
77
        setNeedsSave(m_model->needsSave());
        setRepresentsDefaults(m_model->representsDefault());
78
79
80
81
82
83
84
85
86
    });

    connect(m_kdedWatcher, &QDBusServiceWatcher::serviceOwnerChanged, this,
        [this](const QString &service, const QString &oldOwner, const QString &newOwner) {
        Q_UNUSED(service)
        Q_UNUSED(oldOwner)
        setKdedRunning(!newOwner.isEmpty());
    });
    setKdedRunning(QDBusConnection::sessionBus().interface()->isServiceRegistered(s_kdedServiceName));
87
88
}

89
ModulesModel *KDEDConfig::model() const
90
{
91
    return m_model;
92
93
}

94
FilterProxyModel *KDEDConfig::filteredModel() const
95
{
96
    return m_filteredModel;
97
98
}

99
bool KDEDConfig::kdedRunning() const
100
{
101
    return m_kdedRunning;
102
103
}

104
void KDEDConfig::setKdedRunning(bool kdedRunning)
105
{
106
107
108
109
110
111
112
113
114
115
116
117
    if (m_kdedRunning == kdedRunning) {
        return;
    }

    m_kdedRunning = kdedRunning;
    emit kdedRunningChanged();

    if (kdedRunning) {
        getModuleStatus();
    } else {
        m_model->setRunningModulesKnown(false);
    }
118
119
}

120
void KDEDConfig::startModule(const QString &moduleName)
121
{
122
    startOrStopModule(moduleName, Running);
123
124
}

125
void KDEDConfig::stopModule(const QString &moduleName)
126
{
127
    startOrStopModule(moduleName, NotRunning);
128
129
}

130
void KDEDConfig::startOrStopModule(const QString &moduleName, ModuleStatus status)
131
{
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
    auto call = (status == NotRunning ? m_kdedInterface->unloadModule(moduleName)
                                      : m_kdedInterface->loadModule(moduleName));

    QDBusPendingCallWatcher *callWatcher = new QDBusPendingCallWatcher(call, this);
    connect(callWatcher, &QDBusPendingCallWatcher::finished, this, [this, moduleName, status](QDBusPendingCallWatcher *watcher) {
        QDBusPendingReply<bool> reply = *watcher;
        watcher->deleteLater();

        if (reply.isError()) {
            if (status == NotRunning) {
                emit errorMessage(i18n("Failed to stop service: %1", reply.error().message()));
            } else {
                emit errorMessage(i18n("Failed to start service: %1", reply.error().message()));
            }
            return;
        }

        if (!reply.value()) {
            if (status == NotRunning) {
                emit errorMessage(i18n("Failed to stop service."));
            } else {
                emit errorMessage(i18n("Failed to start service."));
            }
            return;
        }

        qCDebug(KCM_KDED) << "Successfully" << (status == Running ? "started" : "stopped") << moduleName;
        if (status == Running) {
            m_lastStartedModule = moduleName;
        } else {
            m_lastStartedModule.clear();
        }
        getModuleStatus();
    });
166
167
}

168
void KDEDConfig::getModuleStatus()
169
{
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
    auto call = m_kdedInterface->loadedModules();

    QDBusPendingCallWatcher *callWatcher = new QDBusPendingCallWatcher(call, this);
    connect(callWatcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) {
        QDBusPendingReply<QStringList> reply = *watcher;
        watcher->deleteLater();

        if (reply.isError()) {
            qCWarning(KCM_KDED) << "Failed to get loaded modules" << reply.error().name() << reply.error().message();
            return;
        }

        QStringList runningModules = reply.value();
        m_model->setRunningModules(runningModules);
        m_model->setRunningModulesKnown(true);

        // Check if the user just tried starting a module that then disabled itself again.
        // Some kded modules disable themselves on start when they deem themselves unnecessary
        // based on some configuration independ of kded or the current environment.
        // At least provide some feedback and not leave the user wondering why the service doesn't start.
        if (!m_lastStartedModule.isEmpty() && !runningModules.contains(m_lastStartedModule)) {
            emit showSelfDisablingModulesHint();
        }
        m_lastStartedModule.clear();

        // Check if any modules got started/stopped as a result of reloading kded
        if (!m_runningModulesBeforeReconfigure.isEmpty()) {
            std::sort(m_runningModulesBeforeReconfigure.begin(), m_runningModulesBeforeReconfigure.end());
            std::sort(runningModules.begin(), runningModules.end());

            if (m_runningModulesBeforeReconfigure != runningModules) {
                emit showRunningModulesChangedAfterSaveHint();
            }
        }
        m_runningModulesBeforeReconfigure.clear();
    });
206
207
}

208
void KDEDConfig::load()
209
{
210
    m_model->load();
211

212
    setNeedsSave(false);
213
    setRepresentsDefaults(m_model->representsDefault());
214
215
}

216
void KDEDConfig::save()
217
{
218
	KConfig kdedrc(QStringLiteral("kded5rc"), KConfig::NoGlobals);
219

220
221
    for (int i = 0; i < m_model->rowCount(); ++i) {
        const QModelIndex idx = m_model->index(i, 0);
222

223
224
225
226
        const auto type = static_cast<ModuleType>(idx.data(ModulesModel::TypeRole).toInt());
        if (type != AutostartType) {
            continue;
        }
227

228
        const QString moduleName = idx.data(ModulesModel::ModuleNameRole).toString();
229

230
231
232
233
        const bool autoloadEnabled = idx.data(ModulesModel::AutoloadEnabledRole).toBool();
        KConfigGroup cg(&kdedrc, QStringLiteral("Module-%1").arg(moduleName));
        cg.writeEntry("autoload", autoloadEnabled);
    }
234

235
    kdedrc.sync();
236
    m_model->refreshAutoloadEnabledSavedState();
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
    setNeedsSave(false);

    m_runningModulesBeforeReconfigure = m_model->runningModules();

    // Is all of this really necessary? I would also think it to be fire and forget...
    // Only if changing autoload for a module may load/unload it, otherwise there's no point.
    // Autoload doesn't affect a running session and reloading the running modules is also useless then.
    auto call = m_kdedInterface->reconfigure();
    QDBusPendingCallWatcher *callWatcher = new QDBusPendingCallWatcher(call, this);
    connect(callWatcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) {
        QDBusPendingReply<void> reply = *watcher;
        watcher->deleteLater();

        if (reply.isError()) {
            emit errorMessage(i18n("Failed to notify KDE Service Manager (kded5) of saved changed: %1", reply.error().message()));
            return;
        }

        qCDebug(KCM_KDED) << "Successfully reconfigured kded";
        getModuleStatus();
    });
258
259
}

260
void KDEDConfig::defaults()
261
{
262
263
    for (int i = 0; i < m_model->rowCount(); ++i) {
        const QModelIndex idx = m_model->index(i, 0);
264
        m_model->setData(idx, true, ModulesModel::AutoloadEnabledRole);
265
    }
266
267
268
}

#include "kcmkded.moc"