Akonadi

resourcescheduler.cpp
1 /*
2  SPDX-FileCopyrightText: 2007 Volker Krause <[email protected]>
3 
4  SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "resourcescheduler_p.h"
8 
9 #include "recursivemover_p.h"
10 #include <QDBusConnection>
11 
12 #include "akonadiagentbase_debug.h"
13 #include "private/instance_p.h"
14 #include <KLocalizedString>
15 
16 #include <QDBusInterface>
17 #include <QTimer>
18 
19 using namespace Akonadi;
20 using namespace std::chrono_literals;
21 qint64 ResourceScheduler::Task::latestSerial = 0;
22 static QDBusAbstractInterface *s_resourcetracker = nullptr;
23 
24 /// @cond PRIVATE
25 
26 ResourceScheduler::ResourceScheduler(QObject *parent)
27  : QObject(parent)
28 {
29 }
30 
31 void ResourceScheduler::scheduleFullSync()
32 {
33  Task t;
34  t.type = SyncAll;
35  TaskList &queue = queueForTaskType(t.type);
36  if (queue.contains(t) || mCurrentTask == t) {
37  return;
38  }
39  queue << t;
40  signalTaskToTracker(t, "SyncAll");
41  scheduleNext();
42 }
43 
44 void ResourceScheduler::scheduleCollectionTreeSync()
45 {
46  Task t;
47  t.type = SyncCollectionTree;
48  TaskList &queue = queueForTaskType(t.type);
49  if (queue.contains(t) || mCurrentTask == t) {
50  return;
51  }
52  queue << t;
53  signalTaskToTracker(t, "SyncCollectionTree");
54  scheduleNext();
55 }
56 
57 void ResourceScheduler::scheduleTagSync()
58 {
59  Task t;
60  t.type = SyncTags;
61  TaskList &queue = queueForTaskType(t.type);
62  if (queue.contains(t) || mCurrentTask == t) {
63  return;
64  }
65  queue << t;
66  signalTaskToTracker(t, "SyncTags");
67  scheduleNext();
68 }
69 
70 void ResourceScheduler::scheduleRelationSync()
71 {
72  Task t;
73  t.type = SyncRelations;
74  TaskList &queue = queueForTaskType(t.type);
75  if (queue.contains(t) || mCurrentTask == t) {
76  return;
77  }
78  queue << t;
79  signalTaskToTracker(t, "SyncRelations");
80  scheduleNext();
81 }
82 
83 void ResourceScheduler::scheduleSync(const Collection &col)
84 {
85  Task t;
86  t.type = SyncCollection;
87  t.collection = col;
88  TaskList &queue = queueForTaskType(t.type);
89  if (queue.contains(t) || mCurrentTask == t) {
90  return;
91  }
92  queue << t;
93  signalTaskToTracker(t, "SyncCollection", QString::number(col.id()));
94  scheduleNext();
95 }
96 
97 void ResourceScheduler::scheduleAttributesSync(const Collection &collection)
98 {
99  Task t;
100  t.type = SyncCollectionAttributes;
101  t.collection = collection;
102 
103  TaskList &queue = queueForTaskType(t.type);
104  if (queue.contains(t) || mCurrentTask == t) {
105  return;
106  }
107  queue << t;
108  signalTaskToTracker(t, "SyncCollectionAttributes", QString::number(collection.id()));
109  scheduleNext();
110 }
111 
112 void ResourceScheduler::scheduleItemFetch(const Akonadi::Item &item, const QSet<QByteArray> &parts, const QList<QDBusMessage> &msgs, qint64 parentId)
113 
114 {
115  Task t;
116  t.type = FetchItem;
117  t.items << item;
118  t.itemParts = parts;
119  t.dbusMsgs = msgs;
120  t.argument = parentId;
121 
122  TaskList &queue = queueForTaskType(t.type);
123  queue << t;
124 
125  signalTaskToTracker(t, "FetchItem", QString::number(item.id()));
126  scheduleNext();
127 }
128 
129 void ResourceScheduler::scheduleItemsFetch(const Item::List &items, const QSet<QByteArray> &parts, const QDBusMessage &msg)
130 {
131  Task t;
132  t.type = FetchItems;
133  t.items = items;
134  t.itemParts = parts;
135 
136  // if the current task does already fetch the requested item, break here but
137  // keep the dbus message, so we can send the reply later on
138  if (mCurrentTask == t) {
139  mCurrentTask.dbusMsgs << msg;
140  return;
141  }
142 
143  // If this task is already in the queue, merge with it.
144  TaskList &queue = queueForTaskType(t.type);
145  const int idx = queue.indexOf(t);
146  if (idx != -1) {
147  queue[idx].dbusMsgs << msg;
148  return;
149  }
150 
151  t.dbusMsgs << msg;
152  queue << t;
153 
154  QStringList ids;
155  ids.reserve(items.size());
156  for (const auto &item : items) {
157  ids.push_back(QString::number(item.id()));
158  }
159  signalTaskToTracker(t, "FetchItems", ids.join(QLatin1String(", ")));
160  scheduleNext();
161 }
162 
163 void ResourceScheduler::scheduleResourceCollectionDeletion()
164 {
165  Task t;
166  t.type = DeleteResourceCollection;
167  TaskList &queue = queueForTaskType(t.type);
168  if (queue.contains(t) || mCurrentTask == t) {
169  return;
170  }
171  queue << t;
172  signalTaskToTracker(t, "DeleteResourceCollection");
173  scheduleNext();
174 }
175 
176 void ResourceScheduler::scheduleCacheInvalidation(const Collection &collection)
177 {
178  Task t;
179  t.type = InvalideCacheForCollection;
180  t.collection = collection;
181  TaskList &queue = queueForTaskType(t.type);
182  if (queue.contains(t) || mCurrentTask == t) {
183  return;
184  }
185  queue << t;
186  signalTaskToTracker(t, "InvalideCacheForCollection", QString::number(collection.id()));
187  scheduleNext();
188 }
189 
190 void ResourceScheduler::scheduleChangeReplay()
191 {
192  Task t;
193  t.type = ChangeReplay;
194  TaskList &queue = queueForTaskType(t.type);
195  // see ResourceBase::changeProcessed() for why we do not check for mCurrentTask == t here like in the other tasks
196  if (queue.contains(t)) {
197  return;
198  }
199  queue << t;
200  signalTaskToTracker(t, "ChangeReplay");
201  scheduleNext();
202 }
203 
204 void ResourceScheduler::scheduleMoveReplay(const Collection &movedCollection, RecursiveMover *mover)
205 {
206  Task t;
207  t.type = RecursiveMoveReplay;
208  t.collection = movedCollection;
209  t.argument = QVariant::fromValue(mover);
210  TaskList &queue = queueForTaskType(t.type);
211 
212  if (queue.contains(t) || mCurrentTask == t) {
213  return;
214  }
215 
216  queue << t;
217  signalTaskToTracker(t, "RecursiveMoveReplay", QString::number(t.collection.id()));
218  scheduleNext();
219 }
220 
221 void Akonadi::ResourceScheduler::scheduleFullSyncCompletion()
222 {
223  Task t;
224  t.type = SyncAllDone;
225  TaskList &queue = queueForTaskType(t.type);
226  // no compression here, all this does is emitting a D-Bus signal anyway, and compression can trigger races on the receiver side with the signal being lost
227  queue << t;
228  signalTaskToTracker(t, "SyncAllDone");
229  scheduleNext();
230 }
231 
232 void Akonadi::ResourceScheduler::scheduleCollectionTreeSyncCompletion()
233 {
234  Task t;
235  t.type = SyncCollectionTreeDone;
236  TaskList &queue = queueForTaskType(t.type);
237  // no compression here, all this does is emitting a D-Bus signal anyway, and compression can trigger races on the receiver side with the signal being lost
238  queue << t;
239  signalTaskToTracker(t, "SyncCollectionTreeDone");
240  scheduleNext();
241 }
242 
243 void Akonadi::ResourceScheduler::scheduleCustomTask(QObject *receiver,
244  const char *methodName,
245  const QVariant &argument,
247 {
248  Task t;
249  t.type = Custom;
250  t.receiver = receiver;
251  t.methodName = methodName;
252  t.argument = argument;
253  QueueType queueType = GenericTaskQueue;
254  if (priority == ResourceBase::AfterChangeReplay) {
255  queueType = AfterChangeReplayQueue;
256  } else if (priority == ResourceBase::Prepend) {
257  queueType = PrependTaskQueue;
258  }
259  TaskList &queue = mTaskList[queueType];
260 
261  if (queue.contains(t)) {
262  return;
263  }
264 
265  switch (priority) {
267  queue.prepend(t);
268  break;
269  default:
270  queue.append(t);
271  break;
272  }
273 
274  signalTaskToTracker(t, "Custom-" + t.methodName);
275  scheduleNext();
276 }
277 
278 void ResourceScheduler::taskDone()
279 {
280  if (isEmpty()) {
281  Q_EMIT status(AgentBase::Idle, i18nc("@info:status Application ready for work", "Ready"));
282  }
283 
284  if (s_resourcetracker) {
285  const QList<QVariant> argumentList = {QString::number(mCurrentTask.serial), QString()};
286  s_resourcetracker->asyncCallWithArgumentList(QStringLiteral("jobEnded"), argumentList);
287  }
288 
289  mCurrentTask = Task();
290  mCurrentTasksQueue = -1;
291  scheduleNext();
292 }
293 
294 void ResourceScheduler::itemFetchDone(const QString &msg)
295 {
296  Q_ASSERT(mCurrentTask.type == FetchItem);
297 
298  TaskList &queue = queueForTaskType(mCurrentTask.type);
299 
300  const qint64 parentId = mCurrentTask.argument.toLongLong();
301  // msg is empty, there was no error
302  if (msg.isEmpty() && !queue.isEmpty()) {
303  Task &nextTask = queue[0];
304  // If the next task is FetchItem too...
305  if (nextTask.type != mCurrentTask.type || nextTask.argument.toLongLong() != parentId) {
306  // If the next task is not FetchItem or the next FetchItem task has
307  // different parentId then this was the last task in the series, so
308  // send the DBus replies.
309  mCurrentTask.sendDBusReplies(msg);
310  }
311  } else {
312  // msg was not empty, there was an error.
313  // remove all subsequent FetchItem tasks with the same parentId
314  auto iter = queue.begin();
315  while (iter != queue.end()) {
316  if (iter->type != mCurrentTask.type || iter->argument.toLongLong() == parentId) {
317  iter = queue.erase(iter);
318  continue;
319  } else {
320  break;
321  }
322  }
323 
324  // ... and send DBus reply with the error message
325  mCurrentTask.sendDBusReplies(msg);
326  }
327 
328  taskDone();
329 }
330 
331 void ResourceScheduler::deferTask()
332 {
333  if (mCurrentTask.type == Invalid) {
334  return;
335  }
336 
337  if (s_resourcetracker) {
338  const QList<QVariant> argumentList = {QString::number(mCurrentTask.serial), QString()};
339  s_resourcetracker->asyncCallWithArgumentList(QStringLiteral("jobEnded"), argumentList);
340  }
341 
342  Task t = mCurrentTask;
343  mCurrentTask = Task();
344 
345  Q_ASSERT(mCurrentTasksQueue >= 0 && mCurrentTasksQueue < NQueueCount);
346  mTaskList[mCurrentTasksQueue].prepend(t);
347  mCurrentTasksQueue = -1;
348 
349  signalTaskToTracker(t, "DeferedTask");
350 
351  scheduleNext();
352 }
353 
354 bool ResourceScheduler::isEmpty()
355 {
356  for (int i = 0; i < NQueueCount; ++i) {
357  if (!mTaskList[i].isEmpty()) {
358  return false;
359  }
360  }
361  return true;
362 }
363 
364 void ResourceScheduler::scheduleNext()
365 {
366  if (mCurrentTask.type != Invalid || isEmpty() || !mOnline) {
367  return;
368  }
369  QTimer::singleShot(0s, this, &ResourceScheduler::executeNext);
370 }
371 
372 void ResourceScheduler::executeNext()
373 {
374  if (mCurrentTask.type != Invalid || isEmpty()) {
375  return;
376  }
377 
378  for (int i = 0; i < NQueueCount; ++i) {
379  if (!mTaskList[i].isEmpty()) {
380  mCurrentTask = mTaskList[i].takeFirst();
381  mCurrentTasksQueue = i;
382  break;
383  }
384  }
385 
386  if (s_resourcetracker) {
387  const QList<QVariant> argumentList = {QString::number(mCurrentTask.serial)};
388  s_resourcetracker->asyncCallWithArgumentList(QStringLiteral("jobStarted"), argumentList);
389  }
390 
391  switch (mCurrentTask.type) {
392  case SyncAll:
393  Q_EMIT executeFullSync();
394  break;
395  case SyncCollectionTree:
396  Q_EMIT executeCollectionTreeSync();
397  break;
398  case SyncCollection:
399  Q_EMIT executeCollectionSync(mCurrentTask.collection);
400  break;
401  case SyncCollectionAttributes:
402  Q_EMIT executeCollectionAttributesSync(mCurrentTask.collection);
403  break;
404  case SyncTags:
405  Q_EMIT executeTagSync();
406  break;
407  case FetchItem:
408  Q_EMIT executeItemFetch(mCurrentTask.items.at(0), mCurrentTask.itemParts);
409  break;
410  case FetchItems:
411  Q_EMIT executeItemsFetch(mCurrentTask.items, mCurrentTask.itemParts);
412  break;
413  case DeleteResourceCollection:
414  Q_EMIT executeResourceCollectionDeletion();
415  break;
416  case InvalideCacheForCollection:
417  Q_EMIT executeCacheInvalidation(mCurrentTask.collection);
418  break;
419  case ChangeReplay:
420  Q_EMIT executeChangeReplay();
421  break;
422  case RecursiveMoveReplay:
423  Q_EMIT executeRecursiveMoveReplay(mCurrentTask.argument.value<RecursiveMover *>());
424  break;
425  case SyncAllDone:
426  Q_EMIT fullSyncComplete();
427  break;
428  case SyncCollectionTreeDone:
429  Q_EMIT collectionTreeSyncComplete();
430  break;
431  case SyncRelations:
432  Q_EMIT executeRelationSync();
433  break;
434  case Custom: {
435  const QByteArray methodSig = mCurrentTask.methodName + QByteArray("(QVariant)");
436  const bool hasSlotWithVariant = mCurrentTask.receiver->metaObject()->indexOfMethod(methodSig.constData()) != -1;
437  bool success = false;
438  if (hasSlotWithVariant) {
439  success = QMetaObject::invokeMethod(mCurrentTask.receiver, mCurrentTask.methodName.constData(), Q_ARG(QVariant, mCurrentTask.argument));
440  Q_ASSERT_X(success || !mCurrentTask.argument.isValid(),
441  "ResourceScheduler::executeNext",
442  "Valid argument was provided but the method wasn't found");
443  }
444  if (!success) {
445  success = QMetaObject::invokeMethod(mCurrentTask.receiver, mCurrentTask.methodName.constData());
446  }
447 
448  if (!success) {
449  qCCritical(AKONADIAGENTBASE_LOG) << "Could not invoke slot" << mCurrentTask.methodName << "on" << mCurrentTask.receiver << "with argument"
450  << mCurrentTask.argument;
451  }
452  break;
453  }
454  default: {
455  qCCritical(AKONADIAGENTBASE_LOG) << "Unhandled task type" << mCurrentTask.type;
456  dump();
457  Q_ASSERT(false);
458  }
459  }
460 }
461 
462 ResourceScheduler::Task ResourceScheduler::currentTask() const
463 {
464  return mCurrentTask;
465 }
466 
467 ResourceScheduler::Task &ResourceScheduler::currentTask()
468 {
469  return mCurrentTask;
470 }
471 
472 void ResourceScheduler::setOnline(bool state)
473 {
474  if (mOnline == state) {
475  return;
476  }
477  mOnline = state;
478  if (mOnline) {
479  scheduleNext();
480  } else {
481  if (mCurrentTask.type != Invalid) {
482  // abort running task
483  queueForTaskType(mCurrentTask.type).prepend(mCurrentTask);
484  mCurrentTask = Task();
485  mCurrentTasksQueue = -1;
486  }
487  // abort pending synchronous tasks, might take longer until the resource goes online again
488  TaskList &itemFetchQueue = queueForTaskType(FetchItem);
489  qint64 parentId = -1;
490  Task lastTask;
491  for (QList<Task>::iterator it = itemFetchQueue.begin(); it != itemFetchQueue.end();) {
492  if ((*it).type == FetchItem) {
493  qint64 idx = it->argument.toLongLong();
494  if (parentId == -1) {
495  parentId = idx;
496  }
497  if (idx != parentId) {
498  // Only emit the DBus reply once we reach the last taskwith the
499  // same "idx"
500  lastTask.sendDBusReplies(i18nc("@info", "Job canceled."));
501  parentId = idx;
502  }
503  lastTask = (*it);
504  it = itemFetchQueue.erase(it);
505  if (s_resourcetracker) {
506  const QList<QVariant> argumentList = {QString::number(mCurrentTask.serial), i18nc("@info", "Job canceled.")};
507  s_resourcetracker->asyncCallWithArgumentList(QStringLiteral("jobEnded"), argumentList);
508  }
509  } else {
510  ++it;
511  }
512  }
513  }
514 }
515 
516 void ResourceScheduler::signalTaskToTracker(const Task &task, const QByteArray &taskType, const QString &debugString)
517 {
518  // if there's a job tracer running, tell it about the new job
519  if (!s_resourcetracker) {
520  const QString suffix = Akonadi::Instance::identifier().isEmpty() ? QString() : QLatin1Char('-') + Akonadi::Instance::identifier();
521  if (QDBusConnection::sessionBus().interface()->isServiceRegistered(QStringLiteral("org.kde.akonadiconsole") + suffix)) {
522  s_resourcetracker = new QDBusInterface(QLatin1String("org.kde.akonadiconsole") + suffix,
523  QStringLiteral("/resourcesJobtracker"),
524  QStringLiteral("org.freedesktop.Akonadi.JobTracker"),
526  nullptr);
527  }
528  }
529 
530  if (s_resourcetracker) {
531  const QList<QVariant> argumentList = QList<QVariant>() << static_cast<AgentBase *>(parent())->identifier() // "session" (in our case resource)
532  << QString::number(task.serial) // "job"
533  << QString() // "parent job"
534  << QString::fromLatin1(taskType) // "job type"
535  << debugString // "job debugging string"
536  ;
537  s_resourcetracker->asyncCallWithArgumentList(QStringLiteral("jobCreated"), argumentList);
538  }
539 }
540 
541 void ResourceScheduler::collectionRemoved(const Akonadi::Collection &collection)
542 {
543  if (!collection.isValid()) { // should not happen, but you never know...
544  return;
545  }
546  TaskList &queue = queueForTaskType(SyncCollection);
547  for (QList<Task>::iterator it = queue.begin(); it != queue.end();) {
548  if ((*it).type == SyncCollection && (*it).collection == collection) {
549  it = queue.erase(it);
550  qCDebug(AKONADIAGENTBASE_LOG) << " erasing";
551  } else {
552  ++it;
553  }
554  }
555 }
556 
557 void ResourceScheduler::Task::sendDBusReplies(const QString &errorMsg)
558 {
559  for (const QDBusMessage &msg : std::as_const(dbusMsgs)) {
560  qCDebug(AKONADIAGENTBASE_LOG) << "Sending dbus reply for method" << methodName << "with error" << errorMsg;
561  QDBusMessage reply;
562  if (!errorMsg.isEmpty()) {
563  reply = msg.createErrorReply(QDBusError::Failed, errorMsg);
564  } else if (msg.member() == QLatin1String("requestItemDelivery")) {
565  reply = msg.createReply();
566  } else if (msg.member().isEmpty()) {
567  continue; // unittest calls scheduleItemFetch with empty QDBusMessage
568  } else {
569  qCCritical(AKONADIAGENTBASE_LOG) << "ResourceScheduler: got unexpected method name :" << msg.member();
570  }
572  }
573 }
574 
575 ResourceScheduler::QueueType ResourceScheduler::queueTypeForTaskType(TaskType type)
576 {
577  switch (type) {
578  case ChangeReplay:
579  case RecursiveMoveReplay:
580  return ChangeReplayQueue;
581  case FetchItem:
582  case FetchItems:
583  case SyncCollectionAttributes:
584  return UserActionQueue;
585  default:
586  return GenericTaskQueue;
587  }
588 }
589 
590 ResourceScheduler::TaskList &ResourceScheduler::queueForTaskType(TaskType type)
591 {
592  const QueueType qt = queueTypeForTaskType(type);
593  return mTaskList[qt];
594 }
595 
596 void ResourceScheduler::dump() const
597 {
598  qCDebug(AKONADIAGENTBASE_LOG) << dumpToString();
599 }
600 
601 QString ResourceScheduler::dumpToString() const
602 {
603  QString ret;
604  QTextStream str(&ret);
605  str << "ResourceScheduler: " << (mOnline ? "Online" : "Offline") << '\n';
606  str << " current task: " << mCurrentTask << '\n';
607  for (int i = 0; i < NQueueCount; ++i) {
608  const TaskList &queue = mTaskList[i];
609  if (queue.isEmpty()) {
610  str << " queue " << i << " is empty" << '\n';
611  } else {
612  str << " queue " << i << " " << queue.size() << " tasks:\n";
613  const QList<Task>::const_iterator queueEnd(queue.constEnd());
614  for (QList<Task>::const_iterator it = queue.constBegin(); it != queueEnd; ++it) {
615  str << " " << (*it) << '\n';
616  }
617  }
618  }
619  str.flush();
620  return ret;
621 }
622 
623 void ResourceScheduler::clear()
624 {
625  qCDebug(AKONADIAGENTBASE_LOG) << "Clearing ResourceScheduler queues:";
626  for (int i = 0; i < NQueueCount; ++i) {
627  TaskList &queue = mTaskList[i];
628  queue.clear();
629  }
630  mCurrentTask = Task();
631  mCurrentTasksQueue = -1;
632 }
633 
634 void Akonadi::ResourceScheduler::cancelQueues()
635 {
636  for (int i = 0; i < NQueueCount; ++i) {
637  TaskList &queue = mTaskList[i];
638  if (s_resourcetracker) {
639  for (const Task &t : queue) {
640  QList<QVariant> argumentList{QString::number(t.serial), QString()};
641  s_resourcetracker->asyncCallWithArgumentList(QStringLiteral("jobEnded"), argumentList);
642  }
643  }
644  queue.clear();
645  }
646 }
647 
648 static const char s_taskTypes[][27] = {"Invalid (no task)",
649  "SyncAll",
650  "SyncCollectionTree",
651  "SyncCollection",
652  "SyncCollectionAttributes",
653  "SyncTags",
654  "FetchItem",
655  "FetchItems",
656  "ChangeReplay",
657  "RecursiveMoveReplay",
658  "DeleteResourceCollection",
659  "InvalideCacheForCollection",
660  "SyncAllDone",
661  "SyncCollectionTreeDone",
662  "SyncRelations",
663  "Custom"};
664 
665 QTextStream &Akonadi::operator<<(QTextStream &d, const ResourceScheduler::Task &task)
666 {
667  d << task.serial << " " << s_taskTypes[task.type] << " ";
668  if (task.type != ResourceScheduler::Invalid) {
669  if (task.collection.isValid()) {
670  d << "collection " << task.collection.id() << " ";
671  }
672  if (!task.items.isEmpty()) {
673  QStringList ids;
674  ids.reserve(task.items.size());
675  for (const auto &item : std::as_const(task.items)) {
676  ids.push_back(QString::number(item.id()));
677  }
678  d << "items " << ids.join(QLatin1String(", ")) << " ";
679  }
680  if (!task.methodName.isEmpty()) {
681  d << task.methodName << " " << task.argument.toString();
682  }
683  }
684  return d;
685 }
686 
687 QDebug Akonadi::operator<<(QDebug d, const ResourceScheduler::Task &task)
688 {
689  QString s;
690  QTextStream str(&s);
691  str << task;
692  d << s;
693  return d;
694 }
695 
696 /// @endcond
697 
698 #include "moc_resourcescheduler_p.cpp"
@ Idle
The agent does currently nothing.
Definition: agentbase.h:422
QString number(int n, int base)
@ Prepend
The task will be executed as soon as the current task has finished.
Definition: resourcebase.h:758
QVariant fromValue(const T &value)
void clear()
Represents a collection of PIM items.
Definition: collection.h:61
void push_back(const T &value)
Definition: item.h:32
QVector< Item > List
Describes a list of items.
Definition: item.h:115
bool send(const QDBusMessage &message) const const
void reserve(int alloc)
QDBusConnection sessionBus()
SchedulePriority
Describes the scheduling priority of a task that has been queued for execution.
Definition: resourcebase.h:757
bool isEmpty() const const
NETWORKMANAGERQT_EXPORT NetworkManager::Status status()
QDBusPendingCall asyncCallWithArgumentList(const QString &method, const QList< QVariant > &args)
QString join(const QString &separator) const const
The base class for all Akonadi agents and resources.
Definition: agentbase.h:72
Id id() const
Returns the unique identifier of the item.
Definition: item.cpp:63
const char * constData() const const
QString fromLatin1(const char *str, int size)
bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType type, QGenericReturnArgument ret, QGenericArgument val0, QGenericArgument val1, QGenericArgument val2, QGenericArgument val3, QGenericArgument val4, QGenericArgument val5, QGenericArgument val6, QGenericArgument val7, QGenericArgument val8, QGenericArgument val9)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
@ AfterChangeReplay
The task is scheduled after the last ChangeReplay task in the queue.
Definition: resourcebase.h:759
Represents a PIM item stored in Akonadi storage.
Definition: item.h:104
Helper integration between Akonadi and Qt.
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Sat Jul 2 2022 06:41:49 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.