1 // SPDX-FileCopyrightText: 2021 Claudio Cambra <claudio.cambra@gmail.com>
2 // SPDX-License-Identifier: LGPL-2.1-or-later
3 
4 #include "kalendar_debug.h"
5 #include <KLocalizedString>
6 #include <QBitArray>
7 #include <QJSValue>
8 #include <incidencewrapper.h>
9 
IncidenceWrapper(QObject * parent)10 IncidenceWrapper::IncidenceWrapper(QObject *parent)
11     : QObject(parent)
12     , Akonadi::ItemMonitor()
13 {
14     // Change incidence pointer in remindersmodel if changed here
15     connect(this, &IncidenceWrapper::incidencePtrChanged, &m_remindersModel, [=](KCalendarCore::Incidence::Ptr incidencePtr) {
16         m_remindersModel.setIncidencePtr(incidencePtr);
17     });
18     connect(this, &IncidenceWrapper::incidencePtrChanged, &m_attendeesModel, [=](KCalendarCore::Incidence::Ptr incidencePtr) {
19         m_attendeesModel.setIncidencePtr(incidencePtr);
20     });
21     connect(this, &IncidenceWrapper::incidencePtrChanged, &m_recurrenceExceptionsModel, [=](KCalendarCore::Incidence::Ptr incidencePtr) {
22         m_recurrenceExceptionsModel.setIncidencePtr(incidencePtr);
23     });
24     connect(this, &IncidenceWrapper::incidencePtrChanged, &m_attachmentsModel, [=](KCalendarCore::Incidence::Ptr incidencePtr) {
25         m_attachmentsModel.setIncidencePtr(incidencePtr);
26     });
27 
28     Akonadi::ItemFetchScope scope;
29     scope.fetchFullPayload();
30     scope.fetchAllAttributes();
31     scope.setFetchRelations(true);
32     scope.setAncestorRetrieval(Akonadi::ItemFetchScope::Parent);
33     setFetchScope(scope);
34 
35     setNewEvent();
36 }
37 
38 IncidenceWrapper::~IncidenceWrapper() = default;
39 
notifyDataChanged()40 void IncidenceWrapper::notifyDataChanged()
41 {
42     Q_EMIT incidenceTypeChanged();
43     Q_EMIT incidenceTypeStrChanged();
44     Q_EMIT incidenceIconNameChanged();
45     Q_EMIT collectionIdChanged();
46     Q_EMIT summaryChanged();
47     Q_EMIT categoriesChanged();
48     Q_EMIT descriptionChanged();
49     Q_EMIT locationChanged();
50     Q_EMIT incidenceStartChanged();
51     Q_EMIT incidenceStartDateDisplayChanged();
52     Q_EMIT incidenceStartTimeDisplayChanged();
53     Q_EMIT incidenceEndChanged();
54     Q_EMIT incidenceEndDateDisplayChanged();
55     Q_EMIT incidenceEndTimeDisplayChanged();
56     Q_EMIT timeZoneChanged();
57     Q_EMIT startTimeZoneUTCOffsetMinsChanged();
58     Q_EMIT endTimeZoneUTCOffsetMinsChanged();
59     Q_EMIT allDayChanged();
60     Q_EMIT priorityChanged();
61     Q_EMIT remindersModelChanged();
62     Q_EMIT organizerChanged();
63     Q_EMIT attendeesModelChanged();
64     Q_EMIT recurrenceDataChanged();
65     Q_EMIT recurrenceExceptionsModelChanged();
66     Q_EMIT attachmentsModelChanged();
67     Q_EMIT todoCompletedChanged();
68     Q_EMIT todoCompletionDtChanged();
69     Q_EMIT todoPercentCompleteChanged();
70 }
71 
incidenceItem() const72 Akonadi::Item IncidenceWrapper::incidenceItem() const
73 {
74     return item();
75 }
76 
setIncidenceItem(const Akonadi::Item & incidenceItem)77 void IncidenceWrapper::setIncidenceItem(const Akonadi::Item &incidenceItem)
78 {
79     if (incidenceItem.hasPayload<KCalendarCore::Incidence::Ptr>()) {
80         setItem(incidenceItem);
81         setIncidencePtr(incidenceItem.payload<KCalendarCore::Incidence::Ptr>());
82         Q_EMIT incidenceItemChanged();
83         Q_EMIT collectionIdChanged();
84     } else {
85         qCWarning(KALENDAR_LOG) << "This is not an incidence item.";
86     }
87 }
88 
incidencePtr() const89 KCalendarCore::Incidence::Ptr IncidenceWrapper::incidencePtr() const
90 {
91     return m_incidence;
92 }
93 
setIncidencePtr(const KCalendarCore::Incidence::Ptr incidencePtr)94 void IncidenceWrapper::setIncidencePtr(const KCalendarCore::Incidence::Ptr incidencePtr)
95 {
96     m_incidence = incidencePtr;
97 
98     KCalendarCore::Incidence::Ptr originalIncidence(incidencePtr->clone());
99     m_originalIncidence = originalIncidence;
100 
101     Q_EMIT incidencePtrChanged(incidencePtr);
102     Q_EMIT originalIncidencePtrChanged();
103     notifyDataChanged();
104 }
105 
originalIncidencePtr()106 KCalendarCore::Incidence::Ptr IncidenceWrapper::originalIncidencePtr()
107 {
108     return m_originalIncidence;
109 }
110 
incidenceType() const111 int IncidenceWrapper::incidenceType() const
112 {
113     return m_incidence->type();
114 }
115 
incidenceTypeStr() const116 QString IncidenceWrapper::incidenceTypeStr() const
117 {
118     return m_incidence->type() == KCalendarCore::Incidence::TypeTodo ? i18n("Task") : i18n(m_incidence->typeStr());
119 }
120 
incidenceIconName() const121 QString IncidenceWrapper::incidenceIconName() const
122 {
123     return m_incidence->iconName();
124 }
125 
uid() const126 QString IncidenceWrapper::uid() const
127 {
128     return m_incidence->uid();
129 }
130 
collectionId() const131 qint64 IncidenceWrapper::collectionId() const
132 {
133     return m_collectionId < 0 ? item().parentCollection().id() : m_collectionId;
134 }
135 
setCollectionId(qint64 collectionId)136 void IncidenceWrapper::setCollectionId(qint64 collectionId)
137 {
138     m_collectionId = collectionId;
139     Q_EMIT collectionIdChanged();
140 }
141 
parent() const142 QString IncidenceWrapper::parent() const
143 {
144     return m_incidence->relatedTo();
145 }
146 
setParent(QString parent)147 void IncidenceWrapper::setParent(QString parent)
148 {
149     m_incidence->setRelatedTo(parent);
150     Q_EMIT parentChanged();
151 }
152 
summary() const153 QString IncidenceWrapper::summary() const
154 {
155     return m_incidence->summary();
156 }
157 
setSummary(const QString & summary)158 void IncidenceWrapper::setSummary(const QString &summary)
159 {
160     m_incidence->setSummary(summary);
161     Q_EMIT summaryChanged();
162 }
163 
categories()164 QStringList IncidenceWrapper::categories()
165 {
166     return m_incidence->categories();
167 }
168 
setCategories(QStringList categories)169 void IncidenceWrapper::setCategories(QStringList categories)
170 {
171     m_incidence->setCategories(categories);
172     Q_EMIT categoriesChanged();
173 }
174 
description() const175 QString IncidenceWrapper::description() const
176 {
177     return m_incidence->description();
178 }
179 
setDescription(const QString & description)180 void IncidenceWrapper::setDescription(const QString &description)
181 {
182     if (m_incidence->description() == description) {
183         return;
184     }
185     m_incidence->setDescription(description);
186     Q_EMIT descriptionChanged();
187 }
188 
location() const189 QString IncidenceWrapper::location() const
190 {
191     return m_incidence->location();
192 }
193 
setLocation(const QString & location)194 void IncidenceWrapper::setLocation(const QString &location)
195 {
196     m_incidence->setLocation(location);
197     Q_EMIT locationChanged();
198 }
199 
hasGeo() const200 bool IncidenceWrapper::hasGeo() const
201 {
202     return m_incidence->hasGeo();
203 }
204 
geoLatitude() const205 float IncidenceWrapper::geoLatitude() const
206 {
207     return m_incidence->geoLatitude();
208 }
209 
geoLongitude() const210 float IncidenceWrapper::geoLongitude() const
211 {
212     return m_incidence->geoLongitude();
213 }
214 
incidenceStart() const215 QDateTime IncidenceWrapper::incidenceStart() const
216 {
217     return m_incidence->dtStart();
218 }
219 
setIncidenceStart(const QDateTime & incidenceStart,bool respectTimeZone)220 void IncidenceWrapper::setIncidenceStart(const QDateTime &incidenceStart, bool respectTimeZone)
221 {
222     // When we receive dates from QML, these are all set to the local system timezone but
223     // have the dates and times we want. We need to preserve date and time but set the new
224     // QDateTime to have the correct timezone.
225 
226     // When we set the timeZone property, however, we invariably also set the incidence start and end.
227     // This object needs no change. We therefore need to make sure to preserve the entire QDateTime object here.
228     auto oldStart = this->incidenceStart();
229 
230     if (respectTimeZone) {
231         m_incidence->setDtStart(incidenceStart);
232         auto newTzEnd = incidenceEnd();
233         newTzEnd.setTimeZone(incidenceStart.timeZone());
234         setIncidenceEnd(newTzEnd, true);
235     } else {
236         const auto date = incidenceStart.date();
237         const auto time = incidenceStart.time();
238         QDateTime start;
239         start.setTimeZone(QTimeZone(timeZone()));
240         start.setDate(date);
241         start.setTime(time);
242         m_incidence->setDtStart(start);
243     }
244 
245     auto oldStartEndDifference = oldStart.secsTo(incidenceEnd());
246     auto newEnd = this->incidenceStart().addSecs(oldStartEndDifference);
247     setIncidenceEnd(newEnd);
248 
249     Q_EMIT incidenceStartChanged();
250     Q_EMIT incidenceStartDateDisplayChanged();
251     Q_EMIT incidenceStartTimeDisplayChanged();
252 }
253 
setIncidenceStartDate(int day,int month,int year)254 void IncidenceWrapper::setIncidenceStartDate(int day, int month, int year)
255 {
256     QDate date;
257     date.setDate(year, month, day);
258 
259     auto newStart = incidenceStart();
260     newStart.setDate(date);
261 
262     setIncidenceStart(newStart, true);
263 }
264 
setIncidenceStartTime(int hours,int minutes)265 void IncidenceWrapper::setIncidenceStartTime(int hours, int minutes)
266 {
267     QTime time;
268     time.setHMS(hours, minutes, 0);
269 
270     auto newStart = incidenceStart();
271     newStart.setTime(time);
272 
273     setIncidenceStart(newStart, true);
274 }
275 
incidenceStartDateDisplay() const276 QString IncidenceWrapper::incidenceStartDateDisplay() const
277 {
278     return QLocale::system().toString(incidenceStart().date(), QLocale::NarrowFormat);
279 }
280 
incidenceStartTimeDisplay() const281 QString IncidenceWrapper::incidenceStartTimeDisplay() const
282 {
283     return QLocale::system().toString(incidenceStart().time(), QLocale::NarrowFormat);
284 }
285 
incidenceEnd() const286 QDateTime IncidenceWrapper::incidenceEnd() const
287 {
288     if (m_incidence->type() == KCalendarCore::Incidence::IncidenceType::TypeEvent) {
289         KCalendarCore::Event::Ptr event = m_incidence.staticCast<KCalendarCore::Event>();
290         return event->dtEnd();
291     } else if (m_incidence->type() == KCalendarCore::Incidence::IncidenceType::TypeTodo) {
292         KCalendarCore::Todo::Ptr todo = m_incidence.staticCast<KCalendarCore::Todo>();
293         return todo->dtDue();
294     }
295     return {};
296 }
297 
setIncidenceEnd(const QDateTime & incidenceEnd,bool respectTimeZone)298 void IncidenceWrapper::setIncidenceEnd(const QDateTime &incidenceEnd, bool respectTimeZone)
299 {
300     QDateTime end;
301     if (respectTimeZone) {
302         end = incidenceEnd;
303     } else {
304         const auto date = incidenceEnd.date();
305         const auto time = incidenceEnd.time();
306         end.setTimeZone(QTimeZone(timeZone()));
307         end.setDate(date);
308         end.setTime(time);
309     }
310 
311     if (m_incidence->type() == KCalendarCore::Incidence::IncidenceType::TypeEvent) {
312         KCalendarCore::Event::Ptr event = m_incidence.staticCast<KCalendarCore::Event>();
313         event->setDtEnd(end);
314     } else if (m_incidence->type() == KCalendarCore::Incidence::IncidenceType::TypeTodo) {
315         KCalendarCore::Todo::Ptr todo = m_incidence.staticCast<KCalendarCore::Todo>();
316         todo->setDtDue(end);
317     } else {
318         qCWarning(KALENDAR_LOG) << "Unknown incidence type";
319     }
320     Q_EMIT incidenceEndChanged();
321     Q_EMIT incidenceEndDateDisplayChanged();
322     Q_EMIT incidenceEndTimeDisplayChanged();
323 }
324 
setIncidenceEndDate(int day,int month,int year)325 void IncidenceWrapper::setIncidenceEndDate(int day, int month, int year)
326 {
327     QDate date;
328     date.setDate(year, month, day);
329 
330     auto newEnd = incidenceEnd();
331     newEnd.setDate(date);
332 
333     setIncidenceEnd(newEnd, true);
334 }
335 
setIncidenceEndTime(int hours,int minutes)336 void IncidenceWrapper::setIncidenceEndTime(int hours, int minutes)
337 {
338     QTime time;
339     time.setHMS(hours, minutes, 0);
340 
341     auto newEnd = incidenceEnd();
342     newEnd.setTime(time);
343 
344     setIncidenceEnd(newEnd, true);
345 }
346 
incidenceEndDateDisplay() const347 QString IncidenceWrapper::incidenceEndDateDisplay() const
348 {
349     return QLocale::system().toString(incidenceEnd().date(), QLocale::NarrowFormat);
350 }
351 
incidenceEndTimeDisplay() const352 QString IncidenceWrapper::incidenceEndTimeDisplay() const
353 {
354     return QLocale::system().toString(incidenceEnd().time(), QLocale::NarrowFormat);
355 }
356 
timeZone() const357 QByteArray IncidenceWrapper::timeZone() const
358 {
359     return incidenceEnd().timeZone().id();
360 }
361 
setTimeZone(const QByteArray & timeZone)362 void IncidenceWrapper::setTimeZone(const QByteArray &timeZone)
363 {
364     QDateTime start(incidenceStart());
365     if (start.isValid()) {
366         start.setTimeZone(QTimeZone(timeZone));
367         setIncidenceStart(start, true);
368     }
369 
370     QDateTime end(incidenceEnd());
371     if (end.isValid()) {
372         end.setTimeZone(QTimeZone(timeZone));
373         setIncidenceEnd(end, true);
374     }
375 
376     Q_EMIT timeZoneChanged();
377     Q_EMIT startTimeZoneUTCOffsetMinsChanged();
378     Q_EMIT endTimeZoneUTCOffsetMinsChanged();
379 }
380 
startTimeZoneUTCOffsetMins()381 int IncidenceWrapper::startTimeZoneUTCOffsetMins()
382 {
383     return QTimeZone(timeZone()).offsetFromUtc(incidenceStart());
384 }
385 
endTimeZoneUTCOffsetMins()386 int IncidenceWrapper::endTimeZoneUTCOffsetMins()
387 {
388     return QTimeZone(timeZone()).offsetFromUtc(incidenceEnd());
389 }
390 
allDay() const391 bool IncidenceWrapper::allDay() const
392 {
393     return m_incidence->allDay();
394 }
395 
setAllDay(bool allDay)396 void IncidenceWrapper::setAllDay(bool allDay)
397 {
398     m_incidence->setAllDay(allDay);
399     Q_EMIT allDayChanged();
400 }
401 
priority() const402 int IncidenceWrapper::priority() const
403 {
404     return m_incidence->priority();
405 }
406 
setPriority(int priority)407 void IncidenceWrapper::setPriority(int priority)
408 {
409     m_incidence->setPriority(priority);
410     Q_EMIT priorityChanged();
411 }
412 
recurrence() const413 KCalendarCore::Recurrence *IncidenceWrapper::recurrence() const
414 {
415     KCalendarCore::Recurrence *recurrence = m_incidence->recurrence();
416     return recurrence;
417 }
418 
recurrenceData()419 QVariantMap IncidenceWrapper::recurrenceData()
420 {
421     QBitArray weekDaysBits = m_incidence->recurrence()->days();
422     QVector<bool> weekDaysBools(7);
423 
424     for (int i = 0; i < weekDaysBits.size(); i++) {
425         weekDaysBools[i] = weekDaysBits[i];
426     }
427 
428     QVariantList monthPositions;
429     const auto monthPositionsToConvert = m_incidence->recurrence()->monthPositions();
430     for (const auto &pos : monthPositionsToConvert) {
431         QVariantMap positionToAdd;
432         positionToAdd[QStringLiteral("day")] = pos.day();
433         positionToAdd[QStringLiteral("pos")] = pos.pos();
434         monthPositions.append(positionToAdd);
435     }
436 
437     // FYI: yearPositions() just calls monthPositions(), so we're cutting out the middleman
438     return QVariantMap{
439         {QStringLiteral("weekdays"), QVariant::fromValue(weekDaysBools)},
440         {QStringLiteral("duration"), m_incidence->recurrence()->duration()},
441         {QStringLiteral("frequency"), m_incidence->recurrence()->frequency()},
442         {QStringLiteral("startDateTime"), m_incidence->recurrence()->startDateTime()},
443         {QStringLiteral("startDateTimeDisplay"), QLocale::system().toString(m_incidence->recurrence()->startDateTime(), QLocale::NarrowFormat)},
444         {QStringLiteral("endDateTime"), m_incidence->recurrence()->endDateTime()},
445         {QStringLiteral("endDateTimeDisplay"), QLocale::system().toString(m_incidence->recurrence()->endDateTime(), QLocale::NarrowFormat)},
446         {QStringLiteral("allDay"), m_incidence->recurrence()->allDay()},
447         {QStringLiteral("type"), m_incidence->recurrence()->recurrenceType()},
448         {QStringLiteral("monthDays"), QVariant::fromValue(m_incidence->recurrence()->monthDays())},
449         {QStringLiteral("monthPositions"), monthPositions},
450         {QStringLiteral("yearDays"), QVariant::fromValue(m_incidence->recurrence()->yearDays())},
451         {QStringLiteral("yearDates"), QVariant::fromValue(m_incidence->recurrence()->yearDates())},
452         {QStringLiteral("yearMonths"), QVariant::fromValue(m_incidence->recurrence()->yearMonths())},
453     };
454 }
455 
setRecurrenceDataItem(const QString & key,const QVariant & value)456 void IncidenceWrapper::setRecurrenceDataItem(const QString &key, const QVariant &value)
457 {
458     QVariantMap map = recurrenceData();
459     if (map.contains(key)) {
460         if (key == QStringLiteral("weekdays") && value.canConvert<QJSValue>()) {
461             auto jsval = value.value<QJSValue>();
462 
463             if (!jsval.isArray()) {
464                 return;
465             }
466 
467             auto vlist = jsval.toVariant().value<QVariantList>();
468             QBitArray days(7);
469 
470             for (int i = 0; i < vlist.size(); i++) {
471                 days[i] = vlist[i].toBool();
472             }
473 
474             KCalendarCore::RecurrenceRule *rrule = m_incidence->recurrence()->defaultRRule();
475             QList<KCalendarCore::RecurrenceRule::WDayPos> positions;
476 
477             for (int i = 0; i < 7; ++i) {
478                 if (days.testBit(i)) {
479                     KCalendarCore::RecurrenceRule::WDayPos p(0, i + 1);
480                     positions.append(p);
481                 }
482             }
483 
484             rrule->setByDays(positions);
485             m_incidence->recurrence()->updated();
486 
487         } else if (key == QStringLiteral("duration")) {
488             m_incidence->recurrence()->setDuration(value.toInt());
489 
490         } else if (key == QStringLiteral("frequency")) {
491             m_incidence->recurrence()->setFrequency(value.toInt());
492 
493         } else if ((key == QStringLiteral("startDateTime") || key == QStringLiteral("endDateTime")) && value.toDateTime().isValid()) {
494             auto dt = value.toDateTime();
495             QDateTime adjustedDt;
496             adjustedDt.setTimeZone(incidenceEnd().timeZone());
497             adjustedDt.setDate(dt.date());
498             adjustedDt.setTime(dt.time());
499 
500             if (key == QStringLiteral("startDateTime")) {
501                 m_incidence->recurrence()->setStartDateTime(adjustedDt, false);
502 
503             } else if (key == QStringLiteral("endDateTime")) {
504                 m_incidence->recurrence()->setEndDateTime(adjustedDt);
505             }
506 
507         } else if (key == QStringLiteral("allDay")) {
508             m_incidence->recurrence()->setAllDay(value.toBool());
509 
510         } else if (key == QStringLiteral("monthDays") && value.canConvert<QList<int>>()) {
511             m_incidence->recurrence()->setMonthlyDate(value.value<QList<int>>());
512 
513         } else if (key == QStringLiteral("yearDays") && value.canConvert<QList<int>>()) {
514             m_incidence->recurrence()->setYearlyDay(value.value<QList<int>>());
515 
516         } else if (key == QStringLiteral("yearDates") && value.canConvert<QList<int>>()) {
517             m_incidence->recurrence()->setYearlyDate(value.value<QList<int>>());
518 
519         } else if (key == QStringLiteral("yearMonths") && value.canConvert<QList<int>>()) {
520             m_incidence->recurrence()->setYearlyMonth(value.value<QList<int>>());
521 
522         } else if (key == QStringLiteral("monthPositions") && value.canConvert<QList<QVariantMap>>()) {
523             QList<KCalendarCore::RecurrenceRule::WDayPos> newMonthPositions;
524             const auto values = value.value<QList<QVariantMap>>();
525             for (const auto &pos : values) {
526                 KCalendarCore::RecurrenceRule::WDayPos newPos;
527                 newPos.setDay(pos[QStringLiteral("day")].toInt());
528                 newPos.setPos(pos[QStringLiteral("pos")].toInt());
529                 newMonthPositions.append(newPos);
530             }
531 
532             m_incidence->recurrence()->setMonthlyPos(newMonthPositions);
533         }
534     }
535     Q_EMIT recurrenceDataChanged();
536 }
537 
organizer()538 QVariantMap IncidenceWrapper::organizer()
539 {
540     auto organizerPerson = m_incidence->organizer();
541     return QVariantMap{{QStringLiteral("name"), organizerPerson.name()},
542                        {QStringLiteral("email"), organizerPerson.email()},
543                        {QStringLiteral("fullName"), organizerPerson.fullName()}};
544 }
545 
attendees() const546 KCalendarCore::Attendee::List IncidenceWrapper::attendees() const
547 {
548     return m_incidence->attendees();
549 }
550 
remindersModel()551 RemindersModel *IncidenceWrapper::remindersModel()
552 {
553     return &m_remindersModel;
554 }
555 
attendeesModel()556 AttendeesModel *IncidenceWrapper::attendeesModel()
557 {
558     return &m_attendeesModel;
559 }
560 
recurrenceExceptionsModel()561 RecurrenceExceptionsModel *IncidenceWrapper::recurrenceExceptionsModel()
562 {
563     return &m_recurrenceExceptionsModel;
564 }
565 
attachmentsModel()566 AttachmentsModel *IncidenceWrapper::attachmentsModel()
567 {
568     return &m_attachmentsModel;
569 }
570 
todoCompleted()571 bool IncidenceWrapper::todoCompleted()
572 {
573     if (m_incidence->type() != KCalendarCore::IncidenceBase::TypeTodo) {
574         return false;
575     }
576 
577     auto todo = m_incidence.staticCast<KCalendarCore::Todo>();
578     return todo->isCompleted();
579 }
580 
setTodoCompleted(bool completed)581 void IncidenceWrapper::setTodoCompleted(bool completed)
582 {
583     if (m_incidence->type() != KCalendarCore::IncidenceBase::TypeTodo) {
584         return;
585     }
586 
587     auto todo = m_incidence.staticCast<KCalendarCore::Todo>();
588     todo->setCompleted(completed);
589 
590     Q_EMIT todoCompletionDtChanged();
591     Q_EMIT todoPercentCompleteChanged();
592     Q_EMIT incidenceIconNameChanged();
593     Q_EMIT todoCompletedChanged();
594 }
595 
todoCompletionDt()596 QDateTime IncidenceWrapper::todoCompletionDt()
597 {
598     if (m_incidence->type() != KCalendarCore::IncidenceBase::TypeTodo) {
599         return {};
600     }
601 
602     auto todo = m_incidence.staticCast<KCalendarCore::Todo>();
603     return todo->completed();
604 }
605 
todoPercentComplete()606 int IncidenceWrapper::todoPercentComplete()
607 {
608     if (m_incidence->type() != KCalendarCore::IncidenceBase::TypeTodo) {
609         return 0;
610     }
611 
612     auto todo = m_incidence.staticCast<KCalendarCore::Todo>();
613     return todo->percentComplete();
614 }
615 
setTodoPercentComplete(int todoPercentComplete)616 void IncidenceWrapper::setTodoPercentComplete(int todoPercentComplete)
617 {
618     if (m_incidence->type() != KCalendarCore::IncidenceBase::TypeTodo) {
619         return;
620     }
621 
622     auto todo = m_incidence.staticCast<KCalendarCore::Todo>();
623     todo->setPercentComplete(todoPercentComplete);
624 
625     Q_EMIT todoPercentCompleteChanged();
626 
627     if (todoPercentComplete < 100 && todoCompleted()) {
628         setTodoCompleted(false);
629     }
630 
631     Q_EMIT todoCompletedChanged();
632 }
633 
triggerEditMode()634 void IncidenceWrapper::triggerEditMode() // You edit a clone so that the original ptr isn't messed with
635 {
636     KCalendarCore::Incidence::Ptr clonedPtr(m_incidence->clone());
637     setIncidencePtr(clonedPtr);
638 }
639 
nearestQuarterHour(int secsSinceEpoch)640 static int nearestQuarterHour(int secsSinceEpoch)
641 {
642     const int quarterHourInSecs = 60 * 15;
643     return secsSinceEpoch + (quarterHourInSecs - secsSinceEpoch % quarterHourInSecs);
644 }
645 
setNewEvent()646 void IncidenceWrapper::setNewEvent()
647 {
648     auto event = KCalendarCore::Event::Ptr(new KCalendarCore::Event);
649     QDateTime start;
650     start.setSecsSinceEpoch(nearestQuarterHour(QDateTime::currentSecsSinceEpoch()));
651     event->setDtStart(start);
652     event->setDtEnd(start.addSecs(60 * 60));
653 
654     Akonadi::Item incidenceItem;
655     incidenceItem.setPayload<KCalendarCore::Event::Ptr>(event);
656     setIncidenceItem(incidenceItem);
657 }
658 
setNewTodo()659 void IncidenceWrapper::setNewTodo()
660 {
661     auto todo = KCalendarCore::Todo::Ptr(new KCalendarCore::Todo);
662     Akonadi::Item incidenceItem;
663     incidenceItem.setPayload<KCalendarCore::Todo::Ptr>(todo);
664     setIncidenceItem(incidenceItem);
665 }
666 
addAlarms(KCalendarCore::Alarm::List alarms)667 void IncidenceWrapper::addAlarms(KCalendarCore::Alarm::List alarms)
668 {
669     for (int i = 0; i < alarms.size(); i++) {
670         m_incidence->addAlarm(alarms[i]);
671     }
672 }
673 
setRegularRecurrence(IncidenceWrapper::RecurrenceIntervals interval,int freq)674 void IncidenceWrapper::setRegularRecurrence(IncidenceWrapper::RecurrenceIntervals interval, int freq)
675 {
676     switch (interval) {
677     case Daily:
678         m_incidence->recurrence()->setDaily(freq);
679         Q_EMIT recurrenceDataChanged();
680         return;
681     case Weekly:
682         m_incidence->recurrence()->setWeekly(freq);
683         Q_EMIT recurrenceDataChanged();
684         return;
685     case Monthly:
686         m_incidence->recurrence()->setMonthly(freq);
687         Q_EMIT recurrenceDataChanged();
688         return;
689     case Yearly:
690         m_incidence->recurrence()->setYearly(freq);
691         Q_EMIT recurrenceDataChanged();
692         return;
693     default:
694         qCWarning(KALENDAR_LOG) << "Unknown interval for recurrence" << interval;
695         return;
696     }
697 }
698 
setMonthlyPosRecurrence(short pos,int day)699 void IncidenceWrapper::setMonthlyPosRecurrence(short pos, int day)
700 {
701     QBitArray daysBitArray(7);
702     daysBitArray[day] = 1;
703     m_incidence->recurrence()->addMonthlyPos(pos, daysBitArray);
704 }
705 
setRecurrenceOccurrences(int occurrences)706 void IncidenceWrapper::setRecurrenceOccurrences(int occurrences)
707 {
708     m_incidence->recurrence()->setDuration(occurrences);
709     Q_EMIT recurrenceDataChanged();
710 }
711 
clearRecurrences()712 void IncidenceWrapper::clearRecurrences()
713 {
714     m_incidence->recurrence()->clear();
715     Q_EMIT recurrenceDataChanged();
716 }
717 
itemChanged(const Akonadi::Item & item)718 void IncidenceWrapper::itemChanged(const Akonadi::Item &item)
719 {
720     if (item.hasPayload<KCalendarCore::Incidence::Ptr>()) {
721         qCDebug(KALENDAR_LOG) << item.payload<KCalendarCore::Incidence::Ptr>()->summary() << item.parentCollection().id();
722         setIncidencePtr(item.payload<KCalendarCore::Incidence::Ptr>());
723     }
724 }
725 
726 Q_DECLARE_METATYPE(KCalendarCore::Incidence::Ptr);
727