Akonadi Calendar

todomodel.cpp
1/*
2 SPDX-FileCopyrightText: 2008 Thomas Thrainer <tom_t@gmx.at>
3 SPDX-FileCopyrightText: 2012 Sérgio Martins <iamsergio@gmail.com>
4
5 SPDX-License-Identifier: GPL-2.0-or-later WITH Qt-Commercial-exception-1.0
6*/
7
8#include "todomodel.h"
9using namespace Qt::Literals::StringLiterals;
10
11#include <KCalendarCore/Attachment>
12#include <KCalendarCore/Event>
13#include <KEmailAddress>
14#include <KLocalizedString>
15
16#include <Akonadi/CalendarUtils>
17#include <Akonadi/IncidenceTreeModel>
18#include <Akonadi/TagCache>
19
20#include <KCalUtils/DndFactory>
21#include <KCalUtils/ICalDrag>
22#include <KCalUtils/IncidenceFormatter>
23#include <KCalUtils/VCalDrag>
24
25#include "akonadicalendar_debug.h"
26
27#include <QIcon>
28#include <QMimeData>
29
30namespace Akonadi
31{
32class TodoModelPrivate
33{
34public:
35 TodoModelPrivate(TodoModel *qq);
36
37 Akonadi::EntityTreeModel *etm()
38 {
39 if (!mEtm) {
40 auto *model = q->sourceModel();
41 while (model) {
42 if (auto *proxy = qobject_cast<QAbstractProxyModel *>(model); proxy) {
43 model = proxy->sourceModel();
44 } else if (auto *etm = qobject_cast<Akonadi::EntityTreeModel *>(model); etm) {
45 mEtm = etm;
46 break;
47 } else {
48 return nullptr;
49 }
50 }
51 }
52
53 return mEtm;
54 }
55
56 // TODO: O(N) complexity, see if the profiler complains about this
57 Akonadi::Item findItemByUid(const QString &uid, const QModelIndex &parent) const;
58
59 Akonadi::IncidenceChanger *m_changer = nullptr;
60
61 void onDataChanged(const QModelIndex &begin, const QModelIndex &end);
62
63 TodoModel *const q;
64 Akonadi::EntityTreeModel *mEtm = nullptr;
65};
66}
67
68using namespace Akonadi;
69
70TodoModelPrivate::TodoModelPrivate(TodoModel *qq)
71 : q(qq)
72{
73}
74
75Akonadi::Item TodoModelPrivate::findItemByUid(const QString &uid, const QModelIndex &parent) const
76{
77 Q_ASSERT(!uid.isEmpty());
78 auto treeModel = qobject_cast<Akonadi::IncidenceTreeModel *>(q->sourceModel());
79 if (treeModel) { // O(1) Shortcut
80 return treeModel->item(uid);
81 }
82
83 Akonadi::Item item;
84 const int count = q->rowCount(parent);
85 for (int i = 0; i < count; ++i) {
86 const QModelIndex currentIndex = q->index(i, 0, parent);
87 Q_ASSERT(currentIndex.isValid());
88 item = q->data(currentIndex, Akonadi::EntityTreeModel::ItemRole).value<Akonadi::Item>();
89 if (item.isValid()) {
90 return item;
91 } else {
92 item = findItemByUid(uid, currentIndex);
93 if (item.isValid()) {
94 return item;
95 }
96 }
97 }
98
99 return item;
100}
101
102void TodoModelPrivate::onDataChanged(const QModelIndex &begin, const QModelIndex &end)
103{
104 Q_ASSERT(begin.isValid());
105 Q_ASSERT(end.isValid());
106 const QModelIndex proxyBegin = q->mapFromSource(begin);
107 Q_ASSERT(proxyBegin.column() == 0);
108 const QModelIndex proxyEnd = q->mapFromSource(end);
109 Q_EMIT q->dataChanged(proxyBegin, proxyEnd.sibling(proxyEnd.row(), TodoModel::ColumnCount - 1));
110}
111
112TodoModel::TodoModel(QObject *parent)
113 : KExtraColumnsProxyModel(parent)
114 , d(new TodoModelPrivate(this))
115{
116 setObjectName("TodoModel"_L1);
117}
118
119TodoModel::~TodoModel() = default;
120
121QVariant TodoModel::data(const QModelIndex &index, int role) const
122{
123 Q_ASSERT(index.isValid());
124 if (!index.isValid()) {
125 return {};
126 }
127
128 const QModelIndex sourceIndex = mapToSource(index.sibling(index.row(), 0));
129 if (!sourceIndex.isValid()) {
130 return {};
131 }
132 Q_ASSERT(sourceIndex.isValid());
133 const auto item = sourceIndex.data(Akonadi::EntityTreeModel::ItemRole).value<Akonadi::Item>();
134 if (!item.isValid()) {
135 qCWarning(AKONADICALENDAR_LOG) << "Invalid index: " << sourceIndex;
136 // Q_ASSERT( false );
137 return {};
138 }
140 if (!todo) {
141 qCCritical(AKONADICALENDAR_LOG) << "item.hasPayload()" << item.hasPayload();
144 if (incidence) {
145 qCCritical(AKONADICALENDAR_LOG) << "It's actually " << incidence->type();
146 }
147 }
148
149 Q_ASSERT(!"There's no to-do.");
150 return {};
151 }
152
153 switch (role) {
154 case SummaryRole:
155 return todo->summary();
156 case RecurRole:
157 if (todo->recurs()) {
158 if (todo->hasRecurrenceId()) {
159 return i18nc("yes, an exception to a recurring to-do", "Exception");
160 } else {
161 return i18nc("yes, recurring to-do", "Yes");
162 }
163 } else {
164 return i18nc("no, not a recurring to-do", "No");
165 }
166 case PriorityRole:
167 if (todo->priority() == 0) {
168 return QStringLiteral("--");
169 }
170 return todo->priority();
171 case PercentRole:
172 return todo->percentComplete();
173 case StartDateRole:
174 return todo->hasStartDate() ? QLocale().toString(todo->dtStart().toLocalTime().date(), QLocale::ShortFormat) : QString();
175 case DueDateRole:
176 return todo->hasDueDate() ? QLocale().toString(todo->dtDue().toLocalTime().date(), QLocale::ShortFormat) : QString();
177 case CategoriesRole:
178 return todo->categories().join(i18nc("delimiter for joining category/tag names", ","));
179 case DescriptionRole:
180 return todo->description();
181 case CalendarRole:
183 default:
184 break; // column based model handling
185 }
186
187 if (role == Qt::DisplayRole) {
188 switch (index.column()) {
189 case SummaryColumn:
190 return QVariant(todo->summary());
191 case RecurColumn:
192 if (todo->recurs()) {
193 if (todo->hasRecurrenceId()) {
194 return i18nc("yes, an exception to a recurring to-do", "Exception");
195 } else {
196 return i18nc("yes, recurring to-do", "Yes");
197 }
198 } else {
199 return i18nc("no, not a recurring to-do", "No");
200 }
201 case PriorityColumn:
202 if (todo->priority() == 0) {
203 return QVariant(QStringLiteral("--"));
204 }
205 return {todo->priority()};
206 case PercentColumn:
207 return {todo->percentComplete()};
208 case StartDateColumn:
209 return todo->hasStartDate() ? QLocale().toString(todo->dtStart().toLocalTime().date(), QLocale::ShortFormat) : QVariant(QString());
210 case DueDateColumn:
211 return todo->hasDueDate() ? QLocale().toString(todo->dtDue().toLocalTime().date(), QLocale::ShortFormat) : QVariant(QString());
212 case CompletedDateColumn:
213 return todo->hasCompletedDate() ? QLocale().toString(todo->completed().toLocalTime().date(), QLocale::ShortFormat) : QVariant(QString());
214 case CategoriesColumn: {
215 QString categories = todo->categories().join(i18nc("delimiter for joining category/tag names", ","));
216 return QVariant(categories);
217 }
218 case DescriptionColumn:
219 return QVariant(todo->description());
220 case CalendarColumn:
221 return QVariant(Akonadi::CalendarUtils::displayName(d->etm(), item.parentCollection()));
222 }
223 return {};
224 }
225
226 if (role == Qt::EditRole) {
227 switch (index.column()) {
228 case SummaryColumn:
229 return QVariant(todo->summary());
230 case RecurColumn:
231 return {todo->recurs()};
232 case PriorityColumn:
233 return {todo->priority()};
234 case PercentColumn:
235 return {todo->percentComplete()};
236 case StartDateColumn:
237 return QVariant(todo->dtStart().date());
238 case DueDateColumn:
239 return QVariant(todo->dtDue().date());
240 case CompletedDateColumn:
241 return QVariant(todo->completed().date());
242 case CategoriesColumn:
243 return QVariant(todo->categories());
244 case DescriptionColumn:
245 return QVariant(todo->description());
246 case CalendarColumn:
247 return QVariant(Akonadi::CalendarUtils::displayName(d->etm(), item.parentCollection()));
248 }
249 return {};
250 }
251
252 // indicate if a row is checked (=completed) only in the first column
253 if (role == Qt::CheckStateRole && index.column() == 0) {
254 if (hasChildren(index) && !index.parent().isValid()) {
255 return {};
256 }
257
258 if (todo->isCompleted()) {
259 return {Qt::Checked};
260 } else {
261 return {Qt::Unchecked};
262 }
263 }
264
265 // icon for recurring todos
266 // It's in the summary column so you don't accidentally click
267 // the checkbox ( which increments the next occurrence date ).
268 // category colour
269 if (role == Qt::DecorationRole && index.column() == SummaryColumn) {
270 if (todo->recurs()) {
271 return QVariant(QIcon::fromTheme(QStringLiteral("task-recurring")));
272 }
273 const QStringList categories = todo->categories();
274 return categories.isEmpty() ? QVariant() : QVariant(Akonadi::TagCache::instance()->tagColor(categories.first()));
275 } else if (role == Qt::DecorationRole) {
276 return {};
277 }
278
279 if (role == TodoRole) {
280 return QVariant::fromValue(item);
281 }
282
283 if (role == TodoPtrRole) {
284 return QVariant::fromValue(todo);
285 }
286
287 if (role == IsRichTextRole) {
288 if (index.column() == SummaryColumn) {
289 return {todo->summaryIsRich()};
290 } else if (index.column() == DescriptionColumn) {
291 return {todo->descriptionIsRich()};
292 } else {
293 return {};
294 }
295 }
296
297 if (role == Qt::TextAlignmentRole) {
298 switch (index.column()) {
299 // If you change this, change headerData() too.
300 case RecurColumn:
301 case PriorityColumn:
302 case PercentColumn:
303 case StartDateColumn:
304 case DueDateColumn:
305 case CategoriesColumn:
306 case CalendarColumn:
308 }
310 }
311
312 if (sourceModel()) {
313 return sourceModel()->data(mapToSource(index.sibling(index.row(), 0)), role);
314 }
315
316 return {};
317}
318
319QVariant TodoModel::extraColumnData(const QModelIndex &parent, int row, int extraColumn, int role) const
320{
321 // we customize all columns, not just the extra ones, and thus do all that in ::data()
322 Q_UNUSED(parent);
323 Q_UNUSED(row);
324 Q_UNUSED(extraColumn);
325 Q_UNUSED(role);
326 return {};
327}
328
329bool TodoModel::setData(const QModelIndex &index, const QVariant &value, int role)
330{
331 Q_ASSERT(index.isValid());
332 if (!d->m_changer) {
333 return false;
334 }
335 const QVariant oldValue = data(index, role);
336
337 if (oldValue == value) {
338 // Nothing changed, the user used one of the QStyledDelegate's editors but seted the old value
339 // Lets just skip this then and avoid a roundtrip to akonadi, and avoid sending invitations
340 return true;
341 }
342
343 const auto item = data(index, Akonadi::EntityTreeModel::ItemRole).value<Akonadi::Item>();
345
346 if (!item.isValid() || !todo) {
347 qCWarning(AKONADICALENDAR_LOG) << "TodoModel::setData() called, bug item is invalid or doesn't have payload";
348 Q_ASSERT(false);
349 return false;
350 }
351
352 const auto parentCol = Akonadi::EntityTreeModel::updatedCollection(d->etm(), item.parentCollection());
353 if (parentCol.rights() & Akonadi::Collection::CanChangeItem) {
354 KCalendarCore::Todo::Ptr oldTodo(todo->clone());
355 if (role == Qt::CheckStateRole && index.column() == 0) {
356 const bool checked = static_cast<Qt::CheckState>(value.toInt()) == Qt::Checked;
357 if (checked) {
358 todo->setCompleted(QDateTime::currentDateTimeUtc()); // Because it calls Todo::recurTodo()
359 } else {
360 todo->setCompleted(false);
361 }
362 }
363
364 if (role == Qt::EditRole) {
365 switch (index.column()) {
366 case SummaryColumn:
367 if (!value.toString().isEmpty()) {
368 todo->setSummary(value.toString());
369 }
370 break;
371 case PriorityColumn:
372 todo->setPriority(value.toInt());
373 break;
374 case PercentColumn:
375 todo->setPercentComplete(value.toInt());
376 break;
377 case StartDateColumn: {
378 QDateTime tmp = todo->dtStart();
379 tmp.setDate(value.toDate());
380 todo->setDtStart(tmp);
381 break;
382 }
383 case DueDateColumn: {
384 QDateTime tmp = todo->dtDue();
385 tmp.setDate(value.toDate());
386 todo->setDtDue(tmp);
387 break;
388 }
389 case CategoriesColumn:
390 todo->setCategories(value.toStringList());
391 break;
392 case DescriptionColumn:
393 todo->setDescription(value.toString());
394 break;
395 }
396 }
397
398 if (!todo->dirtyFields().isEmpty()) {
399 d->m_changer->modifyIncidence(item, oldTodo);
400 // modifyIncidence will eventually call the view's
401 // changeIncidenceDisplay method, which in turn
402 // will call processChange. processChange will then emit
403 // dataChanged to the view, so we don't have to
404 // do it here
405 }
406
407 return true;
408 } else {
409 if (!(role == Qt::CheckStateRole && index.column() == 0)) {
410 // KOHelper::showSaveIncidenceErrorMsg( 0, todo ); //TODO pass parent
411 qCCritical(AKONADICALENDAR_LOG) << "Unable to modify incidence";
412 }
413 return false;
414 }
415}
416
417int TodoModel::columnCount(const QModelIndex &) const
418{
419 return ColumnCount;
420}
421
422void TodoModel::setSourceModel(QAbstractItemModel *model)
423{
424 if (model == sourceModel()) {
425 return;
426 }
427
429
430 if (sourceModel()) {
432 }
433
435
436 if (sourceModel()) {
437 connect(sourceModel(), &QAbstractItemModel::dataChanged, this, [this](const auto &begin, const auto &end) {
438 d->onDataChanged(begin, end);
439 });
440 }
441
443}
444
445void TodoModel::setIncidenceChanger(Akonadi::IncidenceChanger *changer)
446{
447 d->m_changer = changer;
448}
449
450QVariant TodoModel::headerData(int column, Qt::Orientation orientation, int role) const
451{
452 if (orientation != Qt::Horizontal) {
453 return {};
454 }
455
456 if (role == Qt::DisplayRole) {
457 switch (column) {
458 case SummaryColumn:
459 return QVariant(i18n("Summary"));
460 case RecurColumn:
461 return QVariant(i18n("Recurs"));
462 case PriorityColumn:
463 return QVariant(i18n("Priority"));
464 case PercentColumn:
465 return QVariant(i18nc("@title:column percent complete", "Complete"));
466 case StartDateColumn:
467 return QVariant(i18n("Start Date"));
468 case DueDateColumn:
469 return QVariant(i18n("Due Date"));
470 case CompletedDateColumn:
471 return QVariant(i18nc("@title:column date completed", "Completed"));
472 case CategoriesColumn:
473 return QVariant(i18n("Tags"));
474 case DescriptionColumn:
475 return QVariant(i18n("Description"));
476 case CalendarColumn:
477 return QVariant(i18n("Calendar"));
478 }
479 }
480
481 if (role == Qt::TextAlignmentRole) {
482 switch (column) {
483 // If you change this, change data() too.
484 case RecurColumn:
485 case PriorityColumn:
486 case PercentColumn:
487 case StartDateColumn:
488 case DueDateColumn:
489 case CategoriesColumn:
490 case CalendarColumn:
491 return {Qt::AlignHCenter};
492 }
493 return {};
494 }
495 return {};
496}
497
498void TodoModel::setCalendar(const Akonadi::ETMCalendar::Ptr &calendar)
499{
500 Q_UNUSED(calendar);
501 // Deprecated, no longer does anything
502}
503
504Qt::DropActions TodoModel::supportedDropActions() const
505{
506 // Qt::CopyAction not supported yet
507 return Qt::MoveAction;
508}
509
510QStringList TodoModel::mimeTypes() const
511{
512 static QStringList list;
513 if (list.isEmpty()) {
515 }
516 return list;
517}
518
519QMimeData *TodoModel::mimeData(const QModelIndexList &indexes) const
520{
522 for (const QModelIndex &index : indexes) {
523 const auto item = this->data(index, Akonadi::EntityTreeModel::ItemRole).value<Akonadi::Item>();
524 if (item.isValid() && !items.contains(item)) {
525 items.push_back(item);
526 }
527 }
529}
530
531bool TodoModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
532{
533 Q_UNUSED(row)
534 Q_UNUSED(column)
535
536 if (action != Qt::MoveAction) {
537 qCWarning(AKONADICALENDAR_LOG) << "No action other than MoveAction currently supported!"; // TODO
538 return false;
539 }
540
541 if (d->m_changer && (KCalUtils::ICalDrag::canDecode(data) || KCalUtils::VCalDrag::canDecode(data))) {
542 // DndFactory only needs a valid calendar for drag event, not for drop event.
543 KCalUtils::DndFactory dndFactory(KCalendarCore::Calendar::Ptr{});
544 KCalendarCore::Todo::Ptr t = dndFactory.createDropTodo(data);
545 KCalendarCore::Event::Ptr e = dndFactory.createDropEvent(data);
546
547 if (t) {
548 // we don't want to change the created todo, but the one which is already
549 // stored in our calendar / tree
550 const Akonadi::Item item = d->findItemByUid(t->uid(), QModelIndex());
553 if (parent.isValid()) {
554 const auto parentItem = this->data(parent, Akonadi::EntityTreeModel::ItemRole).value<Akonadi::Item>();
555 if (parentItem.isValid()) {
556 destTodo = Akonadi::CalendarUtils::todo(parentItem);
557 }
558
559 auto tmpParent = parent;
560 while (tmpParent.isValid()) {
561 const auto parentItem = this->data(parent, Akonadi::EntityTreeModel::ItemRole).value<Akonadi::Item>();
562 if (!parentItem.isValid()) {
563 break;
564 }
565 const auto parentTodo = Akonadi::CalendarUtils::todo(parentItem);
566 if (!parentTodo) {
567 break;
568 }
569
570 if (parentTodo->uid() == todo->uid()) { // correct, don't use instanceIdentifier() here
572 return false;
573 }
574
575 tmpParent = tmpParent.parent();
576 }
577 }
578
579 if (!destTodo || !destTodo->hasRecurrenceId()) {
581 // destTodo is empty when we drag a to-do out of a relationship
582 todo->setRelatedTo(destTodo ? destTodo->uid() : QString());
583 d->m_changer->modifyIncidence(item, oldTodo);
584
585 // again, no need to Q_EMIT dataChanged, that's done by processChange
586 return true;
587 } else {
588 qCDebug(AKONADICALENDAR_LOG) << "Todo's with recurring id can't have child todos yet.";
589 return false;
590 }
591 } else if (e) {
592 // TODO: Implement dropping an event onto a to-do: Generate a relationship to the event!
593 } else {
594 if (!parent.isValid()) {
595 // TODO we should create a new todo with the data in the drop object
596 qCDebug(AKONADICALENDAR_LOG) << "TODO: Create a new todo with the given data";
597 return false;
598 }
599
600 const auto parentItem = this->data(parent, Akonadi::EntityTreeModel::ItemRole).value<Akonadi::Item>();
602
603 if (data->hasText()) {
604 QString text = data->text();
605
606 KCalendarCore::Todo::Ptr oldTodo = KCalendarCore::Todo::Ptr(destTodo->clone());
607
608 if (text.startsWith("file:"_L1)) {
609 destTodo->addAttachment(KCalendarCore::Attachment(text));
610 } else {
611 QStringList emails = KEmailAddress::splitAddressList(text);
612 for (QStringList::ConstIterator it = emails.constBegin(); it != emails.constEnd(); ++it) {
613 QString name;
614 QString email;
615 QString comment;
616 if (KEmailAddress::splitAddress(*it, name, email, comment) == KEmailAddress::AddressOk) {
617 destTodo->addAttendee(KCalendarCore::Attendee(name, email));
618 }
619 }
620 }
621 d->m_changer->modifyIncidence(parentItem, oldTodo);
622 return true;
623 }
624 }
625 }
626
627 return false;
628}
629
630Qt::ItemFlags TodoModel::flags(const QModelIndex &index) const
631{
632 if (!index.isValid()) {
633 return Qt::NoItemFlags;
634 }
635
637
638 const auto item = data(index, Akonadi::EntityTreeModel::ItemRole).value<Akonadi::Item>();
639
640 if (!item.isValid()) {
641 Q_ASSERT(mapToSource(index).isValid());
642 qCWarning(AKONADICALENDAR_LOG) << "Item is invalid " << index;
643 Q_ASSERT(false);
644 return Qt::NoItemFlags;
645 }
646
648
650
651 const auto parentCol = Akonadi::EntityTreeModel::updatedCollection(d->etm(), item.parentCollection());
652 if (parentCol.rights() & Akonadi::Collection::CanChangeItem) {
653 // the following columns are editable:
654 switch (index.column()) {
655 case SummaryColumn:
656 case PriorityColumn:
657 case PercentColumn:
658 case StartDateColumn:
659 case DueDateColumn:
660 case CategoriesColumn:
661 ret |= Qt::ItemIsEditable;
662 break;
663 case DescriptionColumn:
664 if (!todo->descriptionIsRich()) {
665 ret |= Qt::ItemIsEditable;
666 }
667 break;
668 }
669 }
670
671 if (index.column() == 0) {
672 // whole rows should have checkboxes, so append the flag for the
673 // first item of every row only. Also, only the first item of every
674 // row should be used as a target for a drag and drop operation.
676 }
677 return ret;
678}
679
680QHash<int, QByteArray> TodoModel::roleNames() const
681{
682 return {
683 {SummaryRole, QByteArrayLiteral("summary")},
684 {RecurRole, QByteArrayLiteral("recur")},
685 {PriorityRole, QByteArrayLiteral("priority")},
686 {PercentRole, QByteArrayLiteral("percent")},
687 {StartDateRole, QByteArrayLiteral("startDate")},
688 {DueDateRole, QByteArrayLiteral("dueDate")},
689 {CategoriesRole, QByteArrayLiteral("categories")},
690 {DescriptionRole, QByteArrayLiteral("description")},
691 {CalendarRole, QByteArrayLiteral("calendar")},
692 {Qt::CheckStateRole, QByteArrayLiteral("checked")},
693 };
694}
695
696#include "moc_todomodel.cpp"
static Collection updatedCollection(const QAbstractItemModel *model, qint64 collectionId)
Collection & parentCollection()
bool hasPayload() const
T payload() const
bool isValid() const
QList< Item > List
Expands an IncidenceTreeModel by additional columns for showing todos.
Definition todomodel.h:31
void dropOnSelfRejected()
Emitted when dropMimeData() rejected a drop on the same item or any of its children.
QSharedPointer< Calendar > Ptr
QSharedPointer< Event > Ptr
QSharedPointer< Incidence > Ptr
QSharedPointer< Todo > Ptr
QModelIndex parent(const QModelIndex &child) const override
void setSourceModel(QAbstractItemModel *model) override
QModelIndex mapToSource(const QModelIndex &proxyIndex) const override
bool hasChildren(const QModelIndex &index) const override
Qt::ItemFlags flags(const QModelIndex &index) const override
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
KCODECS_EXPORT QStringList splitAddressList(const QString &aStr)
KCODECS_EXPORT EmailParseResult splitAddress(const QByteArray &address, QByteArray &displayName, QByteArray &addrSpec, QByteArray &comment)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
AKONADI_CALENDAR_EXPORT KCalendarCore::Incidence::Ptr incidence(const Akonadi::Item &item)
Returns the incidence from an Akonadi item, or a null pointer if the item has no such payload.
AKONADI_CALENDAR_EXPORT KCalendarCore::Todo::Ptr todo(const Akonadi::Item &item)
Returns the todo from an Akonadi item, or a null pointer if the item has no such payload.
AKONADI_CALENDAR_EXPORT QString displayName(Akonadi::ETMCalendar *calendar, const Akonadi::Collection &collection)
Returns a suitable display name for the calendar (or calendar folder) collection.
AKONADI_CALENDAR_EXPORT QMimeData * createMimeData(const Akonadi::Item::List &items)
Creates a MIME data object for dragging Akonadi items containing calendar incidences.
FreeBusyManager::Singleton.
KCALUTILS_EXPORT QString mimeType()
KCALUTILS_EXPORT bool canDecode(const QMimeData *)
KCALUTILS_EXPORT QString mimeType()
KCALUTILS_EXPORT bool canDecode(const QMimeData *)
bool isValid(QStringView ifopt)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
QString name(StandardAction id)
const QList< QKeySequence > & begin()
const QList< QKeySequence > & end()
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList< int > &roles)
QDateTime currentDateTimeUtc()
void setDate(QDate date)
QIcon fromTheme(const QString &name)
typedef ConstIterator
const_iterator constBegin() const const
const_iterator constEnd() const const
bool contains(const AT &value) const const
bool isEmpty() const const
void push_back(parameter_type value)
int column() const const
QVariant data(int role) const const
bool isValid() const const
int row() const const
QModelIndex sibling(int row, int column) const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
bool isEmpty() const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
AlignHCenter
typedef DropActions
DisplayRole
typedef ItemFlags
Orientation
QVariant fromValue(T &&value)
QDate toDate() const const
int toInt(bool *ok) const const
QString toString() const const
QStringList toStringList() const const
T value() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Jan 24 2025 11:53:46 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.