Akonadi

core/agentmanager.cpp
1 /*
2  SPDX-FileCopyrightText: 2006-2008 Tobias Koenig <[email protected]>
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 
21 using namespace Akonadi;
22 using namespace AkRanges;
23 
24 // @cond PRIVATE
25 
26 AgentInstance 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 
36 void 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 
67 void 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 
77 void 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 
100 void 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 
110 void 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 
123 void 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 
138 void 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 
148 void 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 
158 void 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 
169 void 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 
181 void 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 
196 void 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 
211 AgentType 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 
225 void AgentManagerPrivate::setName(const AgentInstance &instance, const QString &name)
226 {
227  mManager->setAgentInstanceName(instance.identifier(), name);
228 }
229 
230 void AgentManagerPrivate::setOnline(const AgentInstance &instance, bool state)
231 {
232  mManager->setAgentInstanceOnline(instance.identifier(), state);
233 }
234 
235 void 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 
245 void AgentManagerPrivate::synchronize(const AgentInstance &instance)
246 {
247  mManager->agentInstanceSynchronize(instance.identifier());
248 }
249 
250 void AgentManagerPrivate::synchronizeCollectionTree(const AgentInstance &instance)
251 {
252  mManager->agentInstanceSynchronizeCollectionTree(instance.identifier());
253 }
254 
255 void AgentManagerPrivate::synchronizeTags(const AgentInstance &instance)
256 {
257  mManager->agentInstanceSynchronizeTags(instance.identifier());
258 }
259 
260 void AgentManagerPrivate::synchronizeRelations(const AgentInstance &instance)
261 {
262  mManager->agentInstanceSynchronizeRelations(instance.identifier());
263 }
264 
265 AgentInstance 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 
285 AgentInstance 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 
298 void 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 
326 AgentManager *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 
353 AgentManager::~AgentManager() = default;
354 
355 AgentManager *AgentManager::self()
356 {
357  if (!AgentManagerPrivate::mSelf) {
358  AgentManagerPrivate::mSelf = new AgentManager();
359  }
360 
361  return AgentManagerPrivate::mSelf;
362 }
363 
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 
375 AgentType AgentManager::type(const QString &identifier) const
376 {
377  return d->mTypes.value(identifier);
378 }
379 
381 {
382  return d->mInstances | Views::values | Actions::toQVector;
383 }
384 
386 {
387  return d->mInstances.value(identifier);
388 }
389 
391 {
392  d->mManager->removeAgentInstance(instance.identifier());
393 }
394 
396 {
397  synchronizeCollection(collection, false);
398 }
399 
400 void 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"
QString identifier() const
Returns the unique identifier of the agent instance.
bool isValid() const
Returns whether the agent type is valid.
QWidget * window() const const
void synchronizeCollection(const Collection &collection)
Trigger a synchronization of the given collection by its owning resource agent.
~AgentManager() override
Destroys the agent manager.
A representation of an agent type.
static QString serviceName(ServiceType serviceType)
Returns the namespaced D-Bus service name for serviceType.
Represents a collection of PIM items.
Definition: collection.h:61
AgentManager(bool verbose, QObject *parent=nullptr)
Creates a new agent manager.
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QStringList types(Mode mode=Writing)
AgentInstance instance(const QString &identifier) const
Returns the agent instance with the given identifier or an invalid agent instance if the identifier d...
void removeInstance(const AgentInstance &instance)
Removes the given agent instance.
Provides an interface to retrieve agent types and manage agent instances.
QDBusConnection sessionBus()
bool isEmpty() const const
Q_SCRIPTABLE CaptureState status()
void serviceRegistered(const QString &serviceName)
AgentType type(const QString &identifier) const
Returns the agent type with the given identifier or an invalid agent type if the identifier does not ...
VehicleSection::Type type(QStringView coachNumber, QStringView coachClassification)
QString name(StandardShortcut id)
WId winId() const const
AgentInstance::List instances() const
Returns the list of all available agent instances.
bool isValid() const
Returns whether the agent instance object is valid.
A representation of an agent instance.
T value(int i) const const
AgentType::List types() const
Returns the list of all available agent types.
Helper integration between Akonadi and Qt.
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Tue Nov 28 2023 03:52:30 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.