KIO

kdynamicjobtracker.cpp
1/*
2 SPDX-FileCopyrightText: 2008 Rob Scheepmaker <r.scheepmaker@student.utwente.nl>
3 SPDX-FileCopyrightText: 2010 Shaun Reich <shaun.reich@kdemail.net>
4 SPDX-FileCopyrightText: 2021 Kai Uwe Broulik <kde@broulik.de>
5
6 SPDX-License-Identifier: LGPL-2.1-or-later
7*/
8
9#include "kdynamicjobtracker_p.h"
10#include "kio_widgets_debug.h"
11#include "kuiserver_interface.h"
12
13#include <KJobTrackerInterface>
14#include <KUiServerJobTracker>
15#include <KUiServerV2JobTracker>
16#include <KWidgetJobTracker>
17#include <kio/jobtracker.h>
18
19#include <QApplication>
20#include <QDBusConnection>
21#include <QDBusConnectionInterface>
22#include <QDBusServiceWatcher>
23#include <QMap>
24#include <QXmlStreamReader>
25
26struct AllTrackers {
27 KUiServerJobTracker *kuiserverTracker;
28 KUiServerV2JobTracker *kuiserverV2Tracker;
29 KWidgetJobTracker *widgetTracker;
30};
31
32class KDynamicJobTrackerPrivate
33{
34public:
35 KDynamicJobTrackerPrivate()
36 {
37 }
38
39 ~KDynamicJobTrackerPrivate()
40 {
41 delete kuiserverTracker;
42 delete kuiserverV2Tracker;
43 delete widgetTracker;
44 }
45
46 static bool hasDBusInterface(const QString &introspectionData, const QString &interface)
47 {
48 QXmlStreamReader xml(introspectionData);
49 while (!xml.atEnd() && !xml.hasError()) {
50 xml.readNext();
51
52 if (xml.tokenType() == QXmlStreamReader::StartElement && xml.name() == QLatin1String("interface")) {
53 if (xml.attributes().value(QLatin1String("name")) == interface) {
54 return true;
55 }
56 }
57 }
58 return false;
59 }
60
61 KUiServerJobTracker *kuiserverTracker = nullptr;
62 KUiServerV2JobTracker *kuiserverV2Tracker = nullptr;
63 KWidgetJobTracker *widgetTracker = nullptr;
65
66 enum JobViewServerSupport {
67 NeedsChecking,
68 Error,
69 V2Supported,
70 V2NotSupported,
71 };
72 JobViewServerSupport jobViewServerSupport = NeedsChecking;
73 QDBusServiceWatcher *jobViewServerWatcher = nullptr;
74};
75
76KDynamicJobTracker::KDynamicJobTracker(QObject *parent)
77 : KJobTrackerInterface(parent)
78 , d(new KDynamicJobTrackerPrivate)
79{
80}
81
82KDynamicJobTracker::~KDynamicJobTracker() = default;
83
84void KDynamicJobTracker::registerJob(KJob *job)
85{
86 if (d->trackers.contains(job)) {
87 return;
88 }
89
90 // only interested in finished() signal,
91 // so catching ourselves instead of using KJobTrackerInterface::registerJob()
92 connect(job, &KJob::finished, this, &KDynamicJobTracker::unregisterJob);
93
94 const bool canHaveWidgets = (qobject_cast<QApplication *>(qApp) != nullptr);
95
96 // always add an entry, even with no trackers used at all,
97 // so unregisterJob() will work as normal
98 AllTrackers &trackers = d->trackers[job];
99 trackers.kuiserverTracker = nullptr;
100 trackers.kuiserverV2Tracker = nullptr;
101 trackers.widgetTracker = nullptr;
102
103 auto useWidgetsFallback = [this, canHaveWidgets, &trackers, job] {
104 if (canHaveWidgets) {
105 // fallback to widget tracker only!
106 if (!d->widgetTracker) {
107 d->widgetTracker = new KWidgetJobTracker();
108 }
109
110 trackers.widgetTracker = d->widgetTracker;
111 trackers.widgetTracker->registerJob(job);
112 }
113 };
114
115 // do not try to use kuiserver on Windows/macOS
116#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
117 useWidgetsFallback();
118 return;
119#endif
120
121 // do not try to query kuiserver if dbus is not available
122 if (!QDBusConnection::sessionBus().interface()) {
123 useWidgetsFallback();
124 return;
125 }
126
127 const QString kuiserverService = QStringLiteral("org.kde.kuiserver");
128
129 if (!d->jobViewServerWatcher) {
130 d->jobViewServerWatcher = new QDBusServiceWatcher(kuiserverService,
133 this);
134 connect(d->jobViewServerWatcher, &QDBusServiceWatcher::serviceOwnerChanged, this, [this] {
135 d->jobViewServerSupport = KDynamicJobTrackerPrivate::NeedsChecking;
136 });
137 }
138
139 if (d->jobViewServerSupport == KDynamicJobTrackerPrivate::NeedsChecking) {
140 // Unfortunately no DBus ObjectManager support in Qt DBus.
141 QDBusMessage msg = QDBusMessage::createMethodCall(kuiserverService,
142 QStringLiteral("/JobViewServer"),
143 QStringLiteral("org.freedesktop.DBus.Introspectable"),
144 QStringLiteral("Introspect"));
145 auto reply = QDBusConnection::sessionBus().call(msg);
146 if (reply.type() == QDBusMessage::ErrorMessage || reply.arguments().count() != 1) {
147 qCWarning(KIO_WIDGETS) << "Failed to check which JobView API is supported" << reply.errorMessage();
148 d->jobViewServerSupport = KDynamicJobTrackerPrivate::Error;
149 } else {
150 const QString introspectionData = reply.arguments().first().toString();
151
152 if (KDynamicJobTrackerPrivate::hasDBusInterface(introspectionData, QStringLiteral("org.kde.JobViewServerV2"))) {
153 d->jobViewServerSupport = KDynamicJobTrackerPrivate::V2Supported;
154 } else {
155 d->jobViewServerSupport = KDynamicJobTrackerPrivate::V2NotSupported;
156 }
157 }
158
159 org::kde::kuiserver interface(kuiserverService, QStringLiteral("/JobViewServer"), QDBusConnection::sessionBus(), this);
160
161 QDBusReply<bool> requiresTrackerReply = interface.requiresJobTracker();
162 if (!requiresTrackerReply.isValid() || requiresTrackerReply.value()) {
163 d->jobViewServerSupport = KDynamicJobTrackerPrivate::Error;
164 }
165
166 QDBusConnection::sessionBus().connect(kuiserverService,
167 QStringLiteral("/JobViewServer"),
168 QStringLiteral("org.kde.kuiserver"),
169 QStringLiteral("requiresJobTrackerChanged"),
170 this,
171 SLOT(handleRequiresJobTrackerChanged(bool)));
172 }
173
174 if (d->jobViewServerSupport == KDynamicJobTrackerPrivate::V2Supported) {
175 if (!d->kuiserverV2Tracker) {
176 d->kuiserverV2Tracker = new KUiServerV2JobTracker();
177 }
178
179 trackers.kuiserverV2Tracker = d->kuiserverV2Tracker;
180 trackers.kuiserverV2Tracker->registerJob(job);
181 return;
182 }
183
184 // No point in trying to set up V1 if calling the service above failed.
185 if (d->jobViewServerSupport != KDynamicJobTrackerPrivate::Error) {
186 if (!d->kuiserverTracker) {
187 d->kuiserverTracker = new KUiServerJobTracker();
188 }
189
190 trackers.kuiserverTracker = d->kuiserverTracker;
191 trackers.kuiserverTracker->registerJob(job);
192 }
193
194 // If kuiserver isn't available or it tells us a job tracker is required
195 // create a widget tracker.
196 if (d->jobViewServerSupport == KDynamicJobTrackerPrivate::Error) {
197 useWidgetsFallback();
198 }
199}
200
201void KDynamicJobTracker::unregisterJob(KJob *job)
202{
203 job->disconnect(this);
204
205 QMap<KJob *, AllTrackers>::Iterator it = d->trackers.find(job);
206
207 if (it == d->trackers.end()) {
208 qCWarning(KIO_WIDGETS) << "Tried to unregister a kio job that hasn't been registered.";
209 return;
210 }
211
212 const AllTrackers &trackers = it.value();
213 KUiServerJobTracker *kuiserverTracker = trackers.kuiserverTracker;
214 KUiServerV2JobTracker *kuiserverV2Tracker = trackers.kuiserverV2Tracker;
215 KWidgetJobTracker *widgetTracker = trackers.widgetTracker;
216
217 if (kuiserverTracker) {
218 kuiserverTracker->unregisterJob(job);
219 }
220
221 if (kuiserverV2Tracker) {
222 kuiserverV2Tracker->unregisterJob(job);
223 }
224
225 if (widgetTracker) {
226 widgetTracker->unregisterJob(job);
227 }
228
229 d->trackers.erase(it);
230}
231
232void KDynamicJobTracker::handleRequiresJobTrackerChanged(bool req)
233{
234 if (req) {
235 d->jobViewServerSupport = KDynamicJobTrackerPrivate::Error;
236 } else {
237 d->jobViewServerSupport = KDynamicJobTrackerPrivate::V2Supported;
238 }
239}
240
241Q_GLOBAL_STATIC(KDynamicJobTracker, globalJobTracker)
242
243// Simply linking to this library, creates a GUI job tracker for all KIO jobs
244static int registerDynamicJobTracker()
245{
246 KIO::setJobTracker(globalJobTracker());
247
248 return 0; // something
249}
250
251Q_CONSTRUCTOR_FUNCTION(registerDynamicJobTracker)
252
253#include "moc_kdynamicjobtracker_p.cpp"
void finished(KJob *job)
void unregisterJob(KJob *job) override
void registerJob(KJob *job) override
void registerJob(KJob *job) override
void unregisterJob(KJob *job) override
void unregisterJob(KJob *job) override
void registerJob(KJob *job) override
KIOCORE_EXPORT void setJobTracker(KJobTrackerInterface *tracker)
QDBusMessage call(const QDBusMessage &message, QDBus::CallMode mode, int timeout) const const
bool connect(const QString &service, const QString &path, const QString &interface, const QString &name, QObject *receiver, const char *slot)
QDBusConnection sessionBus()
QDBusMessage createMethodCall(const QString &service, const QString &path, const QString &interface, const QString &method)
bool isValid() const const
void serviceOwnerChanged(const QString &serviceName, const QString &oldOwner, const QString &newOwner)
T value(const Key &key, const T &defaultValue) const const
bool disconnect(const QMetaObject::Connection &connection)
QString first(qsizetype n) const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:16:28 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.