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>();
344 if (!item.isValid()) {
345 return false;
346 }
347
349 if (!todo) {
350 qCWarning(AKONADICALENDAR_LOG) << "TodoModel::setData() called, bug item doesn't have payload";
351 return false;
352 }
353
354 const auto parentCol = Akonadi::EntityTreeModel::updatedCollection(d->etm(), item.parentCollection());
355 if (parentCol.rights() & Akonadi::Collection::CanChangeItem) {
356 KCalendarCore::Todo::Ptr oldTodo(todo->clone());
357 if (role == Qt::CheckStateRole && index.column() == 0) {
358 const bool checked = static_cast<Qt::CheckState>(value.toInt()) == Qt::Checked;
359 if (checked) {
360 todo->setCompleted(QDateTime::currentDateTimeUtc()); // Because it calls Todo::recurTodo()
361 } else {
362 todo->setCompleted(false);
363 }
364 }
365
366 if (role == Qt::EditRole) {
367 switch (index.column()) {
368 case SummaryColumn:
369 if (!value.toString().isEmpty()) {
370 todo->setSummary(value.toString());
371 }
372 break;
373 case PriorityColumn:
374 todo->setPriority(value.toInt());
375 break;
376 case PercentColumn:
377 todo->setPercentComplete(value.toInt());
378 break;
379 case StartDateColumn: {
380 QDateTime tmp = todo->dtStart();
381 tmp.setDate(value.toDate());
382 todo->setDtStart(tmp);
383 break;
384 }
385 case DueDateColumn: {
386 QDateTime tmp = todo->dtDue();
387 tmp.setDate(value.toDate());
388 todo->setDtDue(tmp);
389 break;
390 }
391 case CategoriesColumn:
392 todo->setCategories(value.toStringList());
393 break;
394 case DescriptionColumn:
395 todo->setDescription(value.toString());
396 break;
397 }
398 }
399
400 if (!todo->dirtyFields().isEmpty()) {
401 d->m_changer->modifyIncidence(item, oldTodo);
402 // modifyIncidence will eventually call the view's
403 // changeIncidenceDisplay method, which in turn
404 // will call processChange. processChange will then emit
405 // dataChanged to the view, so we don't have to
406 // do it here
407 }
408
409 return true;
410 } else {
411 if (!(role == Qt::CheckStateRole && index.column() == 0)) {
412 // KOHelper::showSaveIncidenceErrorMsg( 0, todo ); //TODO pass parent
413 qCCritical(AKONADICALENDAR_LOG) << "Unable to modify incidence";
414 }
415 return false;
416 }
417}
418
419int TodoModel::columnCount(const QModelIndex &) const
420{
421 return ColumnCount;
422}
423
424void TodoModel::setSourceModel(QAbstractItemModel *model)
425{
426 if (model == sourceModel()) {
427 return;
428 }
429
431
432 if (sourceModel()) {
434 }
435
437
438 if (sourceModel()) {
439 connect(sourceModel(), &QAbstractItemModel::dataChanged, this, [this](const auto &begin, const auto &end) {
440 d->onDataChanged(begin, end);
441 });
442 }
443
445}
446
447void TodoModel::setIncidenceChanger(Akonadi::IncidenceChanger *changer)
448{
449 d->m_changer = changer;
450}
451
452QVariant TodoModel::headerData(int column, Qt::Orientation orientation, int role) const
453{
454 if (orientation != Qt::Horizontal) {
455 return {};
456 }
457
458 if (role == Qt::DisplayRole) {
459 switch (column) {
460 case SummaryColumn:
461 return QVariant(i18n("Summary"));
462 case RecurColumn:
463 return QVariant(i18n("Recurs"));
464 case PriorityColumn:
465 return QVariant(i18n("Priority"));
466 case PercentColumn:
467 return QVariant(i18nc("@title:column percent complete", "Complete"));
468 case StartDateColumn:
469 return QVariant(i18n("Start Date"));
470 case DueDateColumn:
471 return QVariant(i18n("Due Date"));
472 case CompletedDateColumn:
473 return QVariant(i18nc("@title:column date completed", "Completed"));
474 case CategoriesColumn:
475 return QVariant(i18n("Tags"));
476 case DescriptionColumn:
477 return QVariant(i18n("Description"));
478 case CalendarColumn:
479 return QVariant(i18n("Calendar"));
480 }
481 }
482
483 if (role == Qt::TextAlignmentRole) {
484 switch (column) {
485 // If you change this, change data() too.
486 case RecurColumn:
487 case PriorityColumn:
488 case PercentColumn:
489 case StartDateColumn:
490 case DueDateColumn:
491 case CategoriesColumn:
492 case CalendarColumn:
493 return {Qt::AlignHCenter};
494 }
495 return {};
496 }
497 return {};
498}
499
500void TodoModel::setCalendar(const Akonadi::ETMCalendar::Ptr &calendar)
501{
502 Q_UNUSED(calendar);
503 // Deprecated, no longer does anything
504}
505
506Qt::DropActions TodoModel::supportedDropActions() const
507{
508 // Qt::CopyAction not supported yet
509 return Qt::MoveAction;
510}
511
512QStringList TodoModel::mimeTypes() const
513{
514 static QStringList list;
515 if (list.isEmpty()) {
517 }
518 return list;
519}
520
521QMimeData *TodoModel::mimeData(const QModelIndexList &indexes) const
522{
524 for (const QModelIndex &index : indexes) {
525 const auto item = this->data(index, Akonadi::EntityTreeModel::ItemRole).value<Akonadi::Item>();
526 if (item.isValid() && !items.contains(item)) {
527 items.push_back(item);
528 }
529 }
531}
532
533bool TodoModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
534{
535 Q_UNUSED(row)
536 Q_UNUSED(column)
537
538 if (action != Qt::MoveAction) {
539 qCWarning(AKONADICALENDAR_LOG) << "No action other than MoveAction currently supported!"; // TODO
540 return false;
541 }
542
543 if (d->m_changer && (KCalUtils::ICalDrag::canDecode(data) || KCalUtils::VCalDrag::canDecode(data))) {
544 // DndFactory only needs a valid calendar for drag event, not for drop event.
545 KCalUtils::DndFactory dndFactory(KCalendarCore::Calendar::Ptr{});
546 KCalendarCore::Todo::Ptr t = dndFactory.createDropTodo(data);
547 KCalendarCore::Event::Ptr e = dndFactory.createDropEvent(data);
548
549 if (t) {
550 // we don't want to change the created todo, but the one which is already
551 // stored in our calendar / tree
552 const Akonadi::Item item = d->findItemByUid(t->uid(), QModelIndex());
555 if (parent.isValid()) {
556 const auto parentItem = this->data(parent, Akonadi::EntityTreeModel::ItemRole).value<Akonadi::Item>();
557 if (parentItem.isValid()) {
558 destTodo = Akonadi::CalendarUtils::todo(parentItem);
559 }
560
561 auto tmpParent = parent;
562 while (tmpParent.isValid()) {
563 const auto parentItem = this->data(parent, Akonadi::EntityTreeModel::ItemRole).value<Akonadi::Item>();
564 if (!parentItem.isValid()) {
565 break;
566 }
567 const auto parentTodo = Akonadi::CalendarUtils::todo(parentItem);
568 if (!parentTodo) {
569 break;
570 }
571
572 if (parentTodo->uid() == todo->uid()) { // correct, don't use instanceIdentifier() here
574 return false;
575 }
576
577 tmpParent = tmpParent.parent();
578 }
579 }
580
581 if (!destTodo || !destTodo->hasRecurrenceId()) {
583 // destTodo is empty when we drag a to-do out of a relationship
584 todo->setRelatedTo(destTodo ? destTodo->uid() : QString());
585 d->m_changer->modifyIncidence(item, oldTodo);
586
587 // again, no need to Q_EMIT dataChanged, that's done by processChange
588 return true;
589 } else {
590 qCDebug(AKONADICALENDAR_LOG) << "Todo's with recurring id can't have child todos yet.";
591 return false;
592 }
593 } else if (e) {
594 // TODO: Implement dropping an event onto a to-do: Generate a relationship to the event!
595 } else {
596 if (!parent.isValid()) {
597 // TODO we should create a new todo with the data in the drop object
598 qCDebug(AKONADICALENDAR_LOG) << "TODO: Create a new todo with the given data";
599 return false;
600 }
601
602 const auto parentItem = this->data(parent, Akonadi::EntityTreeModel::ItemRole).value<Akonadi::Item>();
604
605 if (data->hasText()) {
606 QString text = data->text();
607
608 KCalendarCore::Todo::Ptr oldTodo = KCalendarCore::Todo::Ptr(destTodo->clone());
609
610 if (text.startsWith("file:"_L1)) {
611 destTodo->addAttachment(KCalendarCore::Attachment(text));
612 } else {
613 QStringList emails = KEmailAddress::splitAddressList(text);
614 for (QStringList::ConstIterator it = emails.constBegin(); it != emails.constEnd(); ++it) {
615 QString name;
616 QString email;
617 QString comment;
618 if (KEmailAddress::splitAddress(*it, name, email, comment) == KEmailAddress::AddressOk) {
619 destTodo->addAttendee(KCalendarCore::Attendee(name, email));
620 }
621 }
622 }
623 d->m_changer->modifyIncidence(parentItem, oldTodo);
624 return true;
625 }
626 }
627 }
628
629 return false;
630}
631
632Qt::ItemFlags TodoModel::flags(const QModelIndex &index) const
633{
634 if (!index.isValid()) {
635 return Qt::NoItemFlags;
636 }
637
638 const auto item = data(index, Akonadi::EntityTreeModel::ItemRole).value<Akonadi::Item>();
639 if (!item.isValid()) {
640 return Qt::NoItemFlags;
641 }
642
645
647
648 const auto parentCol = Akonadi::EntityTreeModel::updatedCollection(d->etm(), item.parentCollection());
649 if (parentCol.rights() & Akonadi::Collection::CanChangeItem) {
650 // the following columns are editable:
651 switch (index.column()) {
652 case SummaryColumn:
653 case PriorityColumn:
654 case PercentColumn:
655 case StartDateColumn:
656 case DueDateColumn:
657 case CategoriesColumn:
658 ret |= Qt::ItemIsEditable;
659 break;
660 case DescriptionColumn:
661 if (!todo->descriptionIsRich()) {
662 ret |= Qt::ItemIsEditable;
663 }
664 break;
665 }
666 }
667
668 if (index.column() == 0) {
669 // whole rows should have checkboxes, so append the flag for the
670 // first item of every row only. Also, only the first item of every
671 // row should be used as a target for a drag and drop operation.
673 }
674 return ret;
675}
676
677QHash<int, QByteArray> TodoModel::roleNames() const
678{
679 return {
680 {SummaryRole, QByteArrayLiteral("summary")},
681 {RecurRole, QByteArrayLiteral("recur")},
682 {PriorityRole, QByteArrayLiteral("priority")},
683 {PercentRole, QByteArrayLiteral("percent")},
684 {StartDateRole, QByteArrayLiteral("startDate")},
685 {DueDateRole, QByteArrayLiteral("dueDate")},
686 {CategoriesRole, QByteArrayLiteral("categories")},
687 {DescriptionRole, QByteArrayLiteral("description")},
688 {CalendarRole, QByteArrayLiteral("calendar")},
689 {Qt::CheckStateRole, QByteArrayLiteral("checked")},
690 };
691}
692
693#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 *)
QAction * end(const QObject *recvr, const char *slot, QObject *parent)
QString name(const QVariant &location)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
const QList< QKeySequence > & begin()
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 Apr 11 2025 11:53:15 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.