Akonadi

core/agentmanager.cpp
1/*
2 SPDX-FileCopyrightText: 2006-2008 Tobias Koenig <tokoe@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "agentmanager.h"
8#include "agentmanager_p.h"
9
10#include "agentinstance_p.h"
11#include "agenttype_p.h"
12#include "collection.h"
13#include "servermanager.h"
14#include <QDBusConnection>
15
16#include "shared/akranges.h"
17
18#include <QDBusServiceWatcher>
19#include <QWidget>
20
21using namespace Akonadi;
22using namespace AkRanges;
23
24// @cond PRIVATE
25
26AgentInstance AgentManagerPrivate::createInstance(const AgentType &type)
27{
28 const QString &identifier = mManager->createAgentInstance(type.identifier());
29 if (identifier.isEmpty()) {
30 return AgentInstance();
31 }
32
33 return fillAgentInstanceLight(identifier);
34}
35
36void AgentManagerPrivate::agentTypeAdded(const QString &identifier)
37{
38 // Ignore agent types we already know about, for example because we called
39 // readAgentTypes before.
40 if (mTypes.contains(identifier)) {
41 return;
42 }
43
44 if (mTypes.isEmpty()) {
45 // The Akonadi ServerManager assumes that the server is up and running as soon
46 // as it knows about at least one agent type.
47 // If we Q_EMIT the typeAdded() signal here, it therefore thinks the server is
48 // running. However, the AgentManager does not know about all agent types yet,
49 // as the server might still have pending agentTypeAdded() signals, even though
50 // it internally knows all agent types already.
51 // This can cause situations where the client gets told by the ServerManager that
52 // the server is running, yet the client will encounter an error because the
53 // AgentManager doesn't know all types yet.
54 //
55 // Therefore, we read all agent types from the server here so they are known.
56 readAgentTypes();
57 }
58
59 const AgentType type = fillAgentType(identifier);
60 if (type.isValid()) {
61 mTypes.insert(identifier, type);
62
63 Q_EMIT mParent->typeAdded(type);
64 }
65}
66
67void AgentManagerPrivate::agentTypeRemoved(const QString &identifier)
68{
69 if (!mTypes.contains(identifier)) {
70 return;
71 }
72
73 const AgentType type = mTypes.take(identifier);
74 Q_EMIT mParent->typeRemoved(type);
75}
76
77void AgentManagerPrivate::agentInstanceAdded(const QString &identifier)
78{
79 const AgentInstance instance = fillAgentInstance(identifier);
80 if (instance.isValid()) {
81 // It is possible that this function is called when the instance is already
82 // in our list we filled initially in the constructor.
83 // This happens when the constructor is called during Akonadi startup, when
84 // the agent processes are not fully loaded and have no D-Bus interface yet.
85 // The server-side agent manager then emits the instance added signal when
86 // the D-Bus interface for the agent comes up.
87 // In this case, we simply notify that the instance status has changed.
88 const bool newAgentInstance = !mInstances.contains(identifier);
89 if (newAgentInstance) {
90 mInstances.insert(identifier, instance);
91 Q_EMIT mParent->instanceAdded(instance);
92 } else {
93 mInstances.remove(identifier);
94 mInstances.insert(identifier, instance);
95 Q_EMIT mParent->instanceStatusChanged(instance);
96 }
97 }
98}
99
100void AgentManagerPrivate::agentInstanceRemoved(const QString &identifier)
101{
102 if (!mInstances.contains(identifier)) {
103 return;
104 }
105
106 const AgentInstance instance = mInstances.take(identifier);
107 Q_EMIT mParent->instanceRemoved(instance);
108}
109
110void AgentManagerPrivate::agentInstanceStatusChanged(const QString &identifier, int status, const QString &msg)
111{
112 if (!mInstances.contains(identifier)) {
113 return;
114 }
115
116 AgentInstance &instance = mInstances[identifier];
117 instance.d->mStatus = status;
118 instance.d->mStatusMessage = msg;
119
120 Q_EMIT mParent->instanceStatusChanged(instance);
121}
122
123void AgentManagerPrivate::agentInstanceProgressChanged(const QString &identifier, uint progress, const QString &msg)
124{
125 if (!mInstances.contains(identifier)) {
126 return;
127 }
128
129 AgentInstance &instance = mInstances[identifier];
130 instance.d->mProgress = progress;
131 if (!msg.isEmpty()) {
132 instance.d->mStatusMessage = msg;
133 }
134
135 Q_EMIT mParent->instanceProgressChanged(instance);
136}
137
138void AgentManagerPrivate::agentInstanceWarning(const QString &identifier, const QString &msg)
139{
140 if (!mInstances.contains(identifier)) {
141 return;
142 }
143
144 AgentInstance &instance = mInstances[identifier];
145 Q_EMIT mParent->instanceWarning(instance, msg);
146}
147
148void AgentManagerPrivate::agentInstanceError(const QString &identifier, const QString &msg)
149{
150 if (!mInstances.contains(identifier)) {
151 return;
152 }
153
154 AgentInstance &instance = mInstances[identifier];
155 Q_EMIT mParent->instanceError(instance, msg);
156}
157
158void AgentManagerPrivate::agentInstanceOnlineChanged(const QString &identifier, bool state)
159{
160 if (!mInstances.contains(identifier)) {
161 return;
162 }
163
164 AgentInstance &instance = mInstances[identifier];
165 instance.d->mIsOnline = state;
166 Q_EMIT mParent->instanceOnline(instance, state);
167}
168
169void AgentManagerPrivate::agentInstanceNameChanged(const QString &identifier, const QString &name)
170{
171 if (!mInstances.contains(identifier)) {
172 return;
173 }
174
175 AgentInstance &instance = mInstances[identifier];
176 instance.d->mName = name;
177
178 Q_EMIT mParent->instanceNameChanged(instance);
179}
180
181void AgentManagerPrivate::readAgentTypes()
182{
183 const QDBusReply<QStringList> types = mManager->agentTypes();
184 if (types.isValid()) {
185 const QStringList lst = types.value();
186 for (const QString &type : lst) {
187 const AgentType agentType = fillAgentType(type);
188 if (agentType.isValid()) {
189 mTypes.insert(type, agentType);
190 Q_EMIT mParent->typeAdded(agentType);
191 }
192 }
193 }
194}
195
196void AgentManagerPrivate::readAgentInstances()
197{
198 const QDBusReply<QStringList> instances = mManager->agentInstances();
199 if (instances.isValid()) {
200 const QStringList lst = instances.value();
201 for (const QString &instance : lst) {
202 const AgentInstance agentInstance = fillAgentInstance(instance);
203 if (agentInstance.isValid()) {
204 mInstances.insert(instance, agentInstance);
205 Q_EMIT mParent->instanceAdded(agentInstance);
206 }
207 }
208 }
209}
210
211AgentType AgentManagerPrivate::fillAgentType(const QString &identifier) const
212{
213 AgentType type;
214 type.d->mIdentifier = identifier;
215 type.d->mName = mManager->agentName(identifier);
216 type.d->mDescription = mManager->agentComment(identifier);
217 type.d->mIconName = mManager->agentIcon(identifier);
218 type.d->mMimeTypes = mManager->agentMimeTypes(identifier);
219 type.d->mCapabilities = mManager->agentCapabilities(identifier);
220 type.d->mCustomProperties = mManager->agentCustomProperties(identifier);
221
222 return type;
223}
224
225void AgentManagerPrivate::setName(const AgentInstance &instance, const QString &name)
226{
227 mManager->setAgentInstanceName(instance.identifier(), name);
228}
229
230void AgentManagerPrivate::setOnline(const AgentInstance &instance, bool state)
231{
232 mManager->setAgentInstanceOnline(instance.identifier(), state);
233}
234
235void AgentManagerPrivate::configure(const AgentInstance &instance, QWidget *parent)
236{
237 qlonglong winId = 0;
238 if (parent) {
239 winId = static_cast<qlonglong>(parent->window()->winId());
240 }
241
242 mManager->agentInstanceConfigure(instance.identifier(), winId);
243}
244
245void AgentManagerPrivate::synchronize(const AgentInstance &instance)
246{
247 mManager->agentInstanceSynchronize(instance.identifier());
248}
249
250void AgentManagerPrivate::synchronizeCollectionTree(const AgentInstance &instance)
251{
252 mManager->agentInstanceSynchronizeCollectionTree(instance.identifier());
253}
254
255void AgentManagerPrivate::synchronizeTags(const AgentInstance &instance)
256{
257 mManager->agentInstanceSynchronizeTags(instance.identifier());
258}
259
260void AgentManagerPrivate::synchronizeRelations(const AgentInstance &instance)
261{
262 mManager->agentInstanceSynchronizeRelations(instance.identifier());
263}
264
265AgentInstance AgentManagerPrivate::fillAgentInstance(const QString &identifier) const
266{
267 AgentInstance instance;
268
269 const QString agentTypeIdentifier = mManager->agentInstanceType(identifier);
270 if (!mTypes.contains(agentTypeIdentifier)) {
271 return instance;
272 }
273
274 instance.d->mType = mTypes.value(agentTypeIdentifier);
275 instance.d->mIdentifier = identifier;
276 instance.d->mName = mManager->agentInstanceName(identifier);
277 instance.d->mStatus = mManager->agentInstanceStatus(identifier);
278 instance.d->mStatusMessage = mManager->agentInstanceStatusMessage(identifier);
279 instance.d->mProgress = mManager->agentInstanceProgress(identifier);
280 instance.d->mIsOnline = mManager->agentInstanceOnline(identifier);
281
282 return instance;
283}
284
285AgentInstance AgentManagerPrivate::fillAgentInstanceLight(const QString &identifier) const
286{
287 AgentInstance instance;
288
289 const QString agentTypeIdentifier = mManager->agentInstanceType(identifier);
290 Q_ASSERT_X(mTypes.contains(agentTypeIdentifier), "fillAgentInstanceLight", "Requests non-existing agent type");
291
292 instance.d->mType = mTypes.value(agentTypeIdentifier);
293 instance.d->mIdentifier = identifier;
294
295 return instance;
296}
297
298void AgentManagerPrivate::createDBusInterface()
299{
300 mTypes.clear();
301 mInstances.clear();
302
303 using AgentManagerIface = org::freedesktop::Akonadi::AgentManager;
304 mManager = std::make_unique<AgentManagerIface>(ServerManager::serviceName(ServerManager::Control),
305 QStringLiteral("/AgentManager"),
307 mParent);
308
309 connect(mManager.get(), &AgentManagerIface::agentTypeAdded, this, &AgentManagerPrivate::agentTypeAdded);
310 connect(mManager.get(), &AgentManagerIface::agentTypeRemoved, this, &AgentManagerPrivate::agentTypeRemoved);
311 connect(mManager.get(), &AgentManagerIface::agentInstanceAdded, this, &AgentManagerPrivate::agentInstanceAdded);
312 connect(mManager.get(), &AgentManagerIface::agentInstanceRemoved, this, &AgentManagerPrivate::agentInstanceRemoved);
313 connect(mManager.get(), &AgentManagerIface::agentInstanceStatusChanged, this, &AgentManagerPrivate::agentInstanceStatusChanged);
314 connect(mManager.get(), &AgentManagerIface::agentInstanceProgressChanged, this, &AgentManagerPrivate::agentInstanceProgressChanged);
315 connect(mManager.get(), &AgentManagerIface::agentInstanceNameChanged, this, &AgentManagerPrivate::agentInstanceNameChanged);
316 connect(mManager.get(), &AgentManagerIface::agentInstanceWarning, this, &AgentManagerPrivate::agentInstanceWarning);
317 connect(mManager.get(), &AgentManagerIface::agentInstanceError, this, &AgentManagerPrivate::agentInstanceError);
318 connect(mManager.get(), &AgentManagerIface::agentInstanceOnlineChanged, this, &AgentManagerPrivate::agentInstanceOnlineChanged);
319
320 if (mManager->isValid()) {
321 readAgentTypes();
322 readAgentInstances();
323 }
324}
325
326AgentManager *AgentManagerPrivate::mSelf = nullptr;
327
329 : QObject(nullptr)
330 , d(new AgentManagerPrivate(this))
331{
332 // needed for queued connections on our signals
333 qRegisterMetaType<Akonadi::AgentType>();
334 qRegisterMetaType<Akonadi::AgentInstance>();
335
336 d->createDBusInterface();
337
338 d->mServiceWatcher = std::make_unique<QDBusServiceWatcher>(ServerManager::serviceName(ServerManager::Control),
341 connect(d->mServiceWatcher.get(), &QDBusServiceWatcher::serviceRegistered, this, [this]() {
342 if (d->mTypes.isEmpty()) { // just to be safe
343 d->readAgentTypes();
344 }
345 if (d->mInstances.isEmpty()) {
346 d->readAgentInstances();
347 }
348 });
349}
350
351/// @endcond
352
354
355AgentManager *AgentManager::self()
356{
357 if (!AgentManagerPrivate::mSelf) {
358 AgentManagerPrivate::mSelf = new AgentManager();
359 }
360
361 return AgentManagerPrivate::mSelf;
362}
363
364AgentType::List AgentManager::types() const
365{
366 // Maybe the Control process is up and ready but we haven't been to the event loop yet so
367 // QDBusServiceWatcher hasn't notified us yet.
368 // In that case make sure to do it here, to avoid going into Broken state.
369 if (d->mTypes.isEmpty()) {
370 d->readAgentTypes();
371 }
372 return d->mTypes | Views::values | Actions::toQVector;
373}
374
375AgentType AgentManager::type(const QString &identifier) const
376{
377 return d->mTypes.value(identifier);
378}
379
380AgentInstance::List AgentManager::instances() const
381{
382 return d->mInstances | Views::values | Actions::toQVector;
383}
384
385AgentInstance AgentManager::instance(const QString &identifier) const
386{
387 return d->mInstances.value(identifier);
388}
389
390void AgentManager::removeInstance(const AgentInstance &instance)
391{
392 d->mManager->removeAgentInstance(instance.identifier());
393}
394
395void AgentManager::synchronizeCollection(const Collection &collection)
396{
397 synchronizeCollection(collection, false);
398}
399
400void AgentManager::synchronizeCollection(const Collection &collection, bool recursive)
401{
402 const QString resId = collection.resource();
403 Q_ASSERT(!resId.isEmpty());
404 d->mManager->agentInstanceSynchronizeCollection(resId, collection.id(), recursive);
405}
406
407#include "moc_agentmanager.cpp"
408
409#include "moc_agentmanager_p.cpp"
Represents one agent instance and takes care of communication with it.
QString identifier() const
Set/get the unique identifier of this AgentInstance.
The agent manager has knowledge about all available agents (it scans for .desktop files in the agent ...
AgentManager(bool verbose, QObject *parent=nullptr)
Creates a new agent manager.
~AgentManager() override
Destroys the agent manager.
A representation of an agent instance.
QString identifier() const
Returns the unique identifier of the agent instance.
Provides an interface to retrieve agent types and manage agent instances.
A representation of an agent type.
Represents a collection of PIM items.
Definition collection.h:62
Q_SCRIPTABLE CaptureState status()
Helper integration between Akonadi and Qt.
VehicleSection::Type type(QStringView coachNumber, QStringView coachClassification)
QString name(StandardAction id)
QDBusConnection sessionBus()
bool isValid() const const
void serviceRegistered(const QString &serviceName)
T qobject_cast(QObject *object)
bool isEmpty() const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
WId winId() const const
QWidget * window() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri May 17 2024 11:54:40 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.