MauiKit Calendar

incidencewrapper.cpp
1// SPDX-FileCopyrightText: 2021 Claudio Cambra <claudio.cambra@gmail.com>
2// SPDX-License-Identifier: LGPL-2.1-or-later
3
4#include <KLocalizedString>
5#include <QBitArray>
6#include <QJSValue>
7#include <incidencewrapper.h>
8#include <QDebug>
9
10IncidenceWrapper::IncidenceWrapper(QObject *parent)
11: QObject(parent)
12, Akonadi::ItemMonitor()
13{
14// connect(this, &IncidenceWrapper::incidencePtrChanged, &m_attendeesModel, [=](KCalendarCore::Incidence::Ptr incidencePtr) {
15// m_attendeesModel.setIncidencePtr(incidencePtr);
16// });
17// connect(this, &IncidenceWrapper::incidencePtrChanged, &m_recurrenceExceptionsModel, [=](KCalendarCore::Incidence::Ptr incidencePtr) {
18// m_recurrenceExceptionsModel.setIncidencePtr(incidencePtr);
19// });
20// connect(this, &IncidenceWrapper::incidencePtrChanged, &m_attachmentsModel, [=](KCalendarCore::Incidence::Ptr incidencePtr) {
21// m_attachmentsModel.setIncidencePtr(incidencePtr);
22// });
23//
24 // While generally we know of the relationship an incidence has regarding its parent,
25 // from the POV of an incidence, we have no idea of its relationship to its children.
26 // This is a limitation of KCalendarCore, which only supports one type of relationship
27 // type per incidence and throughout the PIM infrastructure it is always the 'parent'
28 // relationship that is used.
29
30 // We therefore need to rely on the ETMCalendar for this information. Since the ETMCalendar
31 // does not provide us with any specific information about the incidences changed when it
32 // updates, we unfortunately have to this the coarse way and just update everything when
33 // things change.
34 connect(CalendarManager::instance(), &CalendarManager::calendarChanged, this, &IncidenceWrapper::resetChildIncidences);
35
37 scope.fetchFullPayload();
38 scope.fetchAllAttributes();
40 setFetchScope(scope);
41
42 setNewEvent();
43}
44
45IncidenceWrapper::~IncidenceWrapper()
46{
47 cleanupChildIncidences();
48}
49
50void IncidenceWrapper::notifyDataChanged()
51{
52 Q_EMIT incidenceTypeChanged();
53 Q_EMIT incidenceTypeStrChanged();
54 Q_EMIT incidenceIconNameChanged();
55 Q_EMIT collectionIdChanged();
56 Q_EMIT parentChanged();
57 Q_EMIT parentIncidenceChanged();
58 Q_EMIT childIncidencesChanged();
59 Q_EMIT summaryChanged();
60 Q_EMIT categoriesChanged();
61 Q_EMIT descriptionChanged();
62 Q_EMIT locationChanged();
63 Q_EMIT incidenceStartChanged();
64 Q_EMIT incidenceStartDateDisplayChanged();
65 Q_EMIT incidenceStartTimeDisplayChanged();
66 Q_EMIT incidenceEndChanged();
67 Q_EMIT incidenceEndDateDisplayChanged();
68 Q_EMIT incidenceEndTimeDisplayChanged();
69 Q_EMIT timeZoneChanged();
70 Q_EMIT startTimeZoneUTCOffsetMinsChanged();
71 Q_EMIT endTimeZoneUTCOffsetMinsChanged();
72 Q_EMIT durationChanged();
73 Q_EMIT durationDisplayStringChanged();
74 Q_EMIT allDayChanged();
75 Q_EMIT priorityChanged();
76 Q_EMIT organizerChanged();
77 // Q_EMIT attendeesModelChanged();
78 Q_EMIT recurrenceDataChanged();
79 // Q_EMIT recurrenceExceptionsModelChanged();
80 // Q_EMIT attachmentsModelChanged();
81 Q_EMIT todoCompletedChanged();
82 Q_EMIT todoCompletionDtChanged();
83 Q_EMIT todoPercentCompleteChanged();
84 Q_EMIT googleConferenceUrlChanged();
85}
86
87Akonadi::Item IncidenceWrapper::incidenceItem() const
88{
89 return item();
90}
91
92void IncidenceWrapper::setIncidenceItem(const Akonadi::Item &incidenceItem)
93{
94 if (incidenceItem.hasPayload<KCalendarCore::Incidence::Ptr>()) {
95 setItem(incidenceItem);
96 setIncidencePtr(incidenceItem.payload<KCalendarCore::Incidence::Ptr>());
97
98 Q_EMIT incidenceItemChanged();
99 Q_EMIT collectionIdChanged();
100 } else {
101 qWarning() << "This is not an incidence item.";
102 }
103}
104
105KCalendarCore::Incidence::Ptr IncidenceWrapper::incidencePtr() const
106{
107 return m_incidence;
108}
109
110void IncidenceWrapper::setIncidencePtr(const KCalendarCore::Incidence::Ptr incidencePtr)
111{
112 m_incidence = incidencePtr;
113
114 KCalendarCore::Incidence::Ptr originalIncidence(incidencePtr->clone());
115 m_originalIncidence = originalIncidence;
116
117 Q_EMIT incidencePtrChanged(incidencePtr);
118 Q_EMIT originalIncidencePtrChanged();
119 notifyDataChanged();
120}
121
122KCalendarCore::Incidence::Ptr IncidenceWrapper::originalIncidencePtr()
123{
124 return m_originalIncidence;
125}
126
127int IncidenceWrapper::incidenceType() const
128{
129 return m_incidence->type();
130}
131
132QString IncidenceWrapper::incidenceTypeStr() const
133{
134 return m_incidence->type() == KCalendarCore::Incidence::TypeTodo ? i18n("Task") : i18n(m_incidence->typeStr().constData());
135}
136
137QString IncidenceWrapper::incidenceIconName() const
138{
139 return m_incidence->iconName();
140}
141
142QString IncidenceWrapper::uid() const
143{
144 return m_incidence->uid();
145}
146
147qint64 IncidenceWrapper::collectionId() const
148{
149 return m_collectionId < 0 ? item().parentCollection().id() : m_collectionId;
150}
151
152void IncidenceWrapper::setCollectionId(qint64 collectionId)
153{
154 m_collectionId = collectionId;
155 Q_EMIT collectionIdChanged();
156}
157
158QString IncidenceWrapper::parent() const
159{
160 return m_incidence->relatedTo();
161}
162
163void IncidenceWrapper::setParent(QString parent)
164{
165 m_incidence->setRelatedTo(parent);
166 updateParentIncidence();
167 Q_EMIT parentChanged();
168}
169
170IncidenceWrapper *IncidenceWrapper::parentIncidence()
171{
172 updateParentIncidence();
173 return m_parentIncidence.data();
174}
175
176QVariantList IncidenceWrapper::childIncidences()
177{
178 resetChildIncidences();
179 return m_childIncidences;
180}
181
182QString IncidenceWrapper::summary() const
183{
184 return m_incidence->summary();
185}
186
187void IncidenceWrapper::setSummary(const QString &summary)
188{
189 m_incidence->setSummary(summary);
190 Q_EMIT summaryChanged();
191}
192
193QStringList IncidenceWrapper::categories()
194{
195 return m_incidence->categories();
196}
197
198void IncidenceWrapper::setCategories(QStringList categories)
199{
200 m_incidence->setCategories(categories);
201 Q_EMIT categoriesChanged();
202}
203
204QString IncidenceWrapper::description() const
205{
206 return m_incidence->description();
207}
208
209void IncidenceWrapper::setDescription(const QString &description)
210{
211 if (m_incidence->description() == description) {
212 return;
213 }
214 m_incidence->setDescription(description);
215 Q_EMIT descriptionChanged();
216}
217
218QString IncidenceWrapper::location() const
219{
220 return m_incidence->location();
221}
222
223void IncidenceWrapper::setLocation(const QString &location)
224{
225 m_incidence->setLocation(location);
226 Q_EMIT locationChanged();
227}
228
229bool IncidenceWrapper::hasGeo() const
230{
231 return m_incidence->hasGeo();
232}
233
234float IncidenceWrapper::geoLatitude() const
235{
236 return m_incidence->geoLatitude();
237}
238
239float IncidenceWrapper::geoLongitude() const
240{
241 return m_incidence->geoLongitude();
242}
243
244QDateTime IncidenceWrapper::incidenceStart() const
245{
246 return m_incidence->dtStart();
247}
248
249void IncidenceWrapper::setIncidenceStart(const QDateTime &incidenceStart, bool respectTimeZone)
250{
251 // When we receive dates from QML, these are all set to the local system timezone but
252 // have the dates and times we want. We need to preserve date and time but set the new
253 // QDateTime to have the correct timezone.
254
255 // When we set the timeZone property, however, we invariably also set the incidence start and end.
256 // This object needs no change. We therefore need to make sure to preserve the entire QDateTime object here.
257 auto oldStart = this->incidenceStart();
258
259 if (respectTimeZone) {
260 m_incidence->setDtStart(incidenceStart);
261 auto newTzEnd = incidenceEnd();
262 newTzEnd.setTimeZone(incidenceStart.timeZone());
263 setIncidenceEnd(newTzEnd, true);
264 } else {
265 const auto date = incidenceStart.date();
266 const auto time = incidenceStart.time();
268 start.setTimeZone(QTimeZone(timeZone()));
269 start.setDate(date);
270 start.setTime(time);
271 m_incidence->setDtStart(start);
272 }
273
274 auto oldStartEndDifference = oldStart.secsTo(incidenceEnd());
275 auto newEnd = this->incidenceStart().addSecs(oldStartEndDifference);
276 setIncidenceEnd(newEnd);
277
278 Q_EMIT incidenceStartChanged();
279 Q_EMIT incidenceStartDateDisplayChanged();
280 Q_EMIT incidenceStartTimeDisplayChanged();
281 Q_EMIT durationChanged();
282 Q_EMIT durationDisplayStringChanged();
283}
284
285void IncidenceWrapper::setIncidenceStartDate(int day, int month, int year)
286{
287 QDate date;
288 date.setDate(year, month, day);
289
290 auto newStart = incidenceStart();
291 newStart.setDate(date);
292
293 setIncidenceStart(newStart, true);
294}
295
296void IncidenceWrapper::setIncidenceStartTime(int hours, int minutes)
297{
298 QTime time;
299 time.setHMS(hours, minutes, 0);
300
301 auto newStart = incidenceStart();
302 newStart.setTime(time);
303
304 setIncidenceStart(newStart, true);
305}
306
307QString IncidenceWrapper::incidenceStartDateDisplay() const
308{
309 return QLocale::system().toString(incidenceStart().date(), QLocale::NarrowFormat);
310}
311
312QString IncidenceWrapper::incidenceStartTimeDisplay() const
313{
314 return QLocale::system().toString(incidenceStart().time(), QLocale::NarrowFormat);
315}
316
317QDateTime IncidenceWrapper::incidenceEnd() const
318{
319 if (m_incidence->type() == KCalendarCore::Incidence::IncidenceType::TypeEvent) {
321 return event->dtEnd();
322 } else if (m_incidence->type() == KCalendarCore::Incidence::IncidenceType::TypeTodo) {
324 return todo->dtDue();
325 }
326 return {};
327}
328
329void IncidenceWrapper::setIncidenceEnd(const QDateTime &incidenceEnd, bool respectTimeZone)
330{
332 if (respectTimeZone) {
333 end = incidenceEnd;
334 } else {
335 const auto date = incidenceEnd.date();
336 const auto time = incidenceEnd.time();
337 end.setTimeZone(QTimeZone(timeZone()));
338 end.setDate(date);
339 end.setTime(time);
340 }
341
342 if (m_incidence->type() == KCalendarCore::Incidence::IncidenceType::TypeEvent) {
344 event->setDtEnd(end);
345 } else if (m_incidence->type() == KCalendarCore::Incidence::IncidenceType::TypeTodo) {
347 todo->setDtDue(end);
348 } else {
349 qWarning() << "Unknown incidence type";
350 }
351 Q_EMIT incidenceEndChanged();
352 Q_EMIT incidenceEndDateDisplayChanged();
353 Q_EMIT incidenceEndTimeDisplayChanged();
354 Q_EMIT durationChanged();
355 Q_EMIT durationDisplayStringChanged();
356}
357
358void IncidenceWrapper::setIncidenceEndDate(int day, int month, int year)
359{
360 QDate date;
361 date.setDate(year, month, day);
362
363 auto newEnd = incidenceEnd();
364 newEnd.setDate(date);
365
366 setIncidenceEnd(newEnd, true);
367}
368
369void IncidenceWrapper::setIncidenceEndTime(int hours, int minutes)
370{
371 QTime time;
372 time.setHMS(hours, minutes, 0);
373
374 auto newEnd = incidenceEnd();
375 newEnd.setTime(time);
376
377 setIncidenceEnd(newEnd, true);
378}
379
380QString IncidenceWrapper::incidenceEndDateDisplay() const
381{
382 return QLocale::system().toString(incidenceEnd().date(), QLocale::NarrowFormat);
383}
384
385QString IncidenceWrapper::incidenceEndTimeDisplay() const
386{
387 return QLocale::system().toString(incidenceEnd().time(), QLocale::NarrowFormat);
388}
389
390void IncidenceWrapper::setIncidenceTimeToNearestQuarterHour(bool setStartTime, bool setEndTime)
391{
392 const int now = QDateTime::currentSecsSinceEpoch();
393 const int quarterHourInSecs = 15 * 60;
394 const int secsToSet = now + (quarterHourInSecs - now % quarterHourInSecs);
396 startTime.setSecsSinceEpoch(secsToSet);
397 if (setStartTime) {
398 setIncidenceStart(startTime, true);
399 }
400 if (setEndTime) {
401 setIncidenceEnd(startTime.addSecs(3600), true);
402 }
403}
404
405QByteArray IncidenceWrapper::timeZone() const
406{
407 return incidenceEnd().timeZone().id();
408}
409
410void IncidenceWrapper::setTimeZone(const QByteArray &timeZone)
411{
412 QDateTime start(incidenceStart());
413 if (start.isValid()) {
414 start.setTimeZone(QTimeZone(timeZone));
415 setIncidenceStart(start, true);
416 }
417
418 QDateTime end(incidenceEnd());
419 if (end.isValid()) {
420 end.setTimeZone(QTimeZone(timeZone));
421 setIncidenceEnd(end, true);
422 }
423
424 Q_EMIT timeZoneChanged();
425 Q_EMIT startTimeZoneUTCOffsetMinsChanged();
426 Q_EMIT endTimeZoneUTCOffsetMinsChanged();
427}
428
429int IncidenceWrapper::startTimeZoneUTCOffsetMins()
430{
431 return QTimeZone(timeZone()).offsetFromUtc(incidenceStart());
432}
433
434int IncidenceWrapper::endTimeZoneUTCOffsetMins()
435{
436 return QTimeZone(timeZone()).offsetFromUtc(incidenceEnd());
437}
438
439KCalendarCore::Duration IncidenceWrapper::duration() const
440{
441 return m_incidence->duration();
442}
443
444static QString formatSpelloutDuration(const KCalendarCore::Duration &duration, const KFormat &format, bool allDay)
445{
446 if (duration.asSeconds() == 0) {
447 return QString();
448 } else {
449 if (allDay) {
450 return format.formatSpelloutDuration(duration.asSeconds() * 1000 + 24 * 60 * 60 * 1000);
451 } else {
452 return format.formatSpelloutDuration(duration.asSeconds() * 1000);
453 }
454 }
455}
456
457QString IncidenceWrapper::durationDisplayString() const
458{
459 return formatSpelloutDuration(duration(), m_format, allDay());
460}
461
462bool IncidenceWrapper::allDay() const
463{
464 return m_incidence->allDay();
465}
466
467void IncidenceWrapper::setAllDay(bool allDay)
468{
469 m_incidence->setAllDay(allDay);
470 Q_EMIT allDayChanged();
471}
472
473int IncidenceWrapper::priority() const
474{
475 return m_incidence->priority();
476}
477
478void IncidenceWrapper::setPriority(int priority)
479{
480 m_incidence->setPriority(priority);
481 Q_EMIT priorityChanged();
482}
483
484KCalendarCore::Recurrence *IncidenceWrapper::recurrence() const
485{
486 KCalendarCore::Recurrence *recurrence = m_incidence->recurrence();
487 return recurrence;
488}
489
490QVariantMap IncidenceWrapper::recurrenceData()
491{
492 QBitArray weekDaysBits = m_incidence->recurrence()->days();
493 QVector<bool> weekDaysBools(7);
494
495 for (int i = 0; i < weekDaysBits.size(); i++) {
496 weekDaysBools[i] = weekDaysBits[i];
497 }
498
499 QVariantList monthPositions;
500 const auto monthPositionsToConvert = m_incidence->recurrence()->monthPositions();
501 for (const auto &pos : monthPositionsToConvert) {
502 QVariantMap positionToAdd;
503 positionToAdd[QStringLiteral("day")] = pos.day();
504 positionToAdd[QStringLiteral("pos")] = pos.pos();
505 monthPositions.append(positionToAdd);
506 }
507
508 // FYI: yearPositions() just calls monthPositions(), so we're cutting out the middleman
509 return QVariantMap{
510 {QStringLiteral("weekdays"), QVariant::fromValue(weekDaysBools)},
511 {QStringLiteral("duration"), m_incidence->recurrence()->duration()},
512 {QStringLiteral("frequency"), m_incidence->recurrence()->frequency()},
513 {QStringLiteral("startDateTime"), m_incidence->recurrence()->startDateTime()},
514 {QStringLiteral("startDateTimeDisplay"), QLocale::system().toString(m_incidence->recurrence()->startDateTime(), QLocale::NarrowFormat)},
515 {QStringLiteral("endDateTime"), m_incidence->recurrence()->endDateTime()},
516 {QStringLiteral("endDateTimeDisplay"), QLocale::system().toString(m_incidence->recurrence()->endDateTime(), QLocale::NarrowFormat)},
517 {QStringLiteral("allDay"), m_incidence->recurrence()->allDay()},
518 {QStringLiteral("type"), m_incidence->recurrence()->recurrenceType()},
519 {QStringLiteral("monthDays"), QVariant::fromValue(m_incidence->recurrence()->monthDays())},
520 {QStringLiteral("monthPositions"), monthPositions},
521 {QStringLiteral("yearDays"), QVariant::fromValue(m_incidence->recurrence()->yearDays())},
522 {QStringLiteral("yearDates"), QVariant::fromValue(m_incidence->recurrence()->yearDates())},
523 {QStringLiteral("yearMonths"), QVariant::fromValue(m_incidence->recurrence()->yearMonths())},
524 };
525}
526
527void IncidenceWrapper::setRecurrenceDataItem(const QString &key, const QVariant &value)
528{
529 QVariantMap map = recurrenceData();
530 if (map.contains(key)) {
531 if (key == QStringLiteral("weekdays") && value.canConvert<QJSValue>()) {
532 auto jsval = value.value<QJSValue>();
533
534 if (!jsval.isArray()) {
535 return;
536 }
537
538 auto vlist = jsval.toVariant().value<QVariantList>();
539 QBitArray days(7);
540
541 for (int i = 0; i < vlist.size(); i++) {
542 days[i] = vlist[i].toBool();
543 }
544
545 KCalendarCore::RecurrenceRule *rrule = m_incidence->recurrence()->defaultRRule();
547
548 for (int i = 0; i < 7; ++i) {
549 if (days.testBit(i)) {
551 positions.append(p);
552 }
553 }
554
555 rrule->setByDays(positions);
556 m_incidence->recurrence()->updated();
557
558 } else if (key == QStringLiteral("duration")) {
559 m_incidence->recurrence()->setDuration(value.toInt());
560
561 } else if (key == QStringLiteral("frequency")) {
562 m_incidence->recurrence()->setFrequency(value.toInt());
563
564 } else if ((key == QStringLiteral("startDateTime") || key == QStringLiteral("endDateTime")) && value.toDateTime().isValid()) {
565 auto dt = value.toDateTime();
566 QDateTime adjustedDt;
567 adjustedDt.setTimeZone(incidenceEnd().timeZone());
568 adjustedDt.setDate(dt.date());
569 adjustedDt.setTime(dt.time());
570
571 if (key == QStringLiteral("startDateTime")) {
572 m_incidence->recurrence()->setStartDateTime(adjustedDt, false);
573
574 } else if (key == QStringLiteral("endDateTime")) {
575 m_incidence->recurrence()->setEndDateTime(adjustedDt);
576 }
577
578 } else if (key == QStringLiteral("allDay")) {
579 m_incidence->recurrence()->setAllDay(value.toBool());
580
581 } else if (key == QStringLiteral("monthDays") && value.canConvert<QList<int>>()) {
582 m_incidence->recurrence()->setMonthlyDate(value.value<QList<int>>());
583
584 } else if (key == QStringLiteral("yearDays") && value.canConvert<QList<int>>()) {
585 m_incidence->recurrence()->setYearlyDay(value.value<QList<int>>());
586
587 } else if (key == QStringLiteral("yearDates") && value.canConvert<QList<int>>()) {
588 m_incidence->recurrence()->setYearlyDate(value.value<QList<int>>());
589
590 } else if (key == QStringLiteral("yearMonths") && value.canConvert<QList<int>>()) {
591 m_incidence->recurrence()->setYearlyMonth(value.value<QList<int>>());
592
593 } else if (key == QStringLiteral("monthPositions") && value.canConvert<QList<QVariantMap>>()) {
595 const auto values = value.value<QList<QVariantMap>>();
596 for (const auto &pos : values) {
598 newPos.setDay(pos[QStringLiteral("day")].toInt());
599 newPos.setPos(pos[QStringLiteral("pos")].toInt());
600 newMonthPositions.append(newPos);
601 }
602
603 m_incidence->recurrence()->setMonthlyPos(newMonthPositions);
604 }
605 }
606 Q_EMIT recurrenceDataChanged();
607}
608
609QString IncidenceWrapper::googleConferenceUrl()
610{
611 return m_incidence->customProperty("LIBKGAPI", "EventHangoutLink");
612}
613
614QVariantMap IncidenceWrapper::organizer()
615{
616 auto organizerPerson = m_incidence->organizer();
617 return QVariantMap{{QStringLiteral("name"), organizerPerson.name()},
618 {QStringLiteral("email"), organizerPerson.email()},
619 {QStringLiteral("fullName"), organizerPerson.fullName()}};
620}
621
622// KCalendarCore::Attendee::List IncidenceWrapper::attendees() const
623// {
624// return m_incidence->attendees();
625// }
626//
627// AttendeesModel *IncidenceWrapper::attendeesModel()
628// {
629// return &m_attendeesModel;
630// }
631
632// RecurrenceExceptionsModel *IncidenceWrapper::recurrenceExceptionsModel()
633// {
634// return &m_recurrenceExceptionsModel;
635// }
636
637// AttachmentsModel *IncidenceWrapper::attachmentsModel()
638// {
639// return &m_attachmentsModel;
640// }
641
642bool IncidenceWrapper::todoCompleted()
643{
644 if (m_incidence->type() != KCalendarCore::IncidenceBase::TypeTodo) {
645 return false;
646 }
647
648 auto todo = m_incidence.staticCast<KCalendarCore::Todo>();
649 return todo->isCompleted();
650}
651
652void IncidenceWrapper::setTodoCompleted(bool completed)
653{
654 if (m_incidence->type() != KCalendarCore::IncidenceBase::TypeTodo) {
655 return;
656 }
657
658 auto todo = m_incidence.staticCast<KCalendarCore::Todo>();
659 todo->setCompleted(completed);
660
661 Q_EMIT todoCompletionDtChanged();
662 Q_EMIT todoPercentCompleteChanged();
663 Q_EMIT incidenceIconNameChanged();
664 Q_EMIT todoCompletedChanged();
665}
666
667QDateTime IncidenceWrapper::todoCompletionDt()
668{
669 if (m_incidence->type() != KCalendarCore::IncidenceBase::TypeTodo) {
670 return {};
671 }
672
673 auto todo = m_incidence.staticCast<KCalendarCore::Todo>();
674 return todo->completed();
675}
676
677int IncidenceWrapper::todoPercentComplete()
678{
679 if (m_incidence->type() != KCalendarCore::IncidenceBase::TypeTodo) {
680 return 0;
681 }
682
683 auto todo = m_incidence.staticCast<KCalendarCore::Todo>();
684 return todo->percentComplete();
685}
686
687void IncidenceWrapper::setTodoPercentComplete(int todoPercentComplete)
688{
689 if (m_incidence->type() != KCalendarCore::IncidenceBase::TypeTodo) {
690 return;
691 }
692
693 auto todo = m_incidence.staticCast<KCalendarCore::Todo>();
694 todo->setPercentComplete(todoPercentComplete);
695
696 Q_EMIT todoPercentCompleteChanged();
697
698 if (todoPercentComplete < 100 && todoCompleted()) {
699 setTodoCompleted(false);
700 }
701
702 Q_EMIT todoCompletedChanged();
703}
704
705void IncidenceWrapper::triggerEditMode() // You edit a clone so that the original ptr isn't messed with
706{
707 auto itemToEdit = item();
708 KCalendarCore::Incidence::Ptr clonedPtr(m_incidence->clone());
709 itemToEdit.setPayload<KCalendarCore::Incidence::Ptr>(clonedPtr);
710 setIncidenceItem(itemToEdit);
711}
712
713static int nearestQuarterHour(int secsSinceEpoch)
714{
715 const int quarterHourInSecs = 60 * 15;
716 return secsSinceEpoch + (quarterHourInSecs - secsSinceEpoch % quarterHourInSecs);
717}
718
719void IncidenceWrapper::setNewEvent()
720{
724 event->setDtStart(start);
725 event->setDtEnd(start.addSecs(60 * 60));
726
728 alarm->setEnabled(true);
729 alarm->setType(KCalendarCore::Alarm::Display);
730 alarm->setStartOffset(-1 * 15 * 60); // 15 minutes
731
732 event->addAlarm(alarm);
733
734 setNewIncidence(event);
735}
736
737void IncidenceWrapper::setNewTodo()
738{
740 setNewIncidence(todo);
741}
742
743void IncidenceWrapper::setNewIncidence(KCalendarCore::Incidence::Ptr incidence)
744{
745 Akonadi::Item incidenceItem;
747 setIncidenceItem(incidenceItem);
748}
749
750// We need to be careful when we call updateParentIncidence and resetChildIncidences.
751// For instance, we always call them on-demand based on access to the properties and not
752// upon object construction on upon setting the incidence pointer.
753
754// Calling them both recursively down a family tree can cause a cascade of infinite
755// new IncidenceWrappers being created. Say we create a new incidence wrapper here and
756// call this new incidence's updateParentIncidence and resetChildIncidences, creating
757// a new child wrapper, creating more wrappers there, and so on.
758
759void IncidenceWrapper::updateParentIncidence()
760{
761 if (!m_incidence) {
762 return;
763 }
764
765 if (!parent().isEmpty() && (!m_parentIncidence || m_parentIncidence->uid() != parent())) {
766 m_parentIncidence.reset(new IncidenceWrapper);
767 m_parentIncidence->setIncidenceItem(CalendarManager::instance()->incidenceItem(parent()));
768 Q_EMIT parentIncidenceChanged();
769 }
770}
771
772void IncidenceWrapper::resetChildIncidences()
773{
774 cleanupChildIncidences();
775
776 if (!m_incidence) {
777 return;
778 }
779
780 const auto incidences = CalendarManager::instance()->childIncidences(uid());
781 QVariantList wrappedIncidences;
782
783 for (const auto &incidence : incidences) {
784 const auto wrappedIncidence = new IncidenceWrapper;
785 wrappedIncidence->setIncidenceItem(CalendarManager::instance()->incidenceItem(incidence));
786 wrappedIncidences.append(QVariant::fromValue(wrappedIncidence));
787 }
788
789 m_childIncidences = wrappedIncidences;
790 Q_EMIT childIncidencesChanged();
791}
792
793void IncidenceWrapper::cleanupChildIncidences()
794{
795 while (!m_childIncidences.isEmpty()) {
796 const auto incidence = m_childIncidences.takeFirst();
797 const auto incidencePtr = incidence.value<IncidenceWrapper *>();
798
799 delete incidencePtr;
800 }
801}
802
803void IncidenceWrapper::addAlarms(KCalendarCore::Alarm::List alarms)
804{
805 for (int i = 0; i < alarms.size(); i++) {
806 m_incidence->addAlarm(alarms[i]);
807 }
808}
809
810void IncidenceWrapper::setRegularRecurrence(IncidenceWrapper::RecurrenceIntervals interval, int freq)
811{
812 switch (interval) {
813 case Daily:
814 m_incidence->recurrence()->setDaily(freq);
815 Q_EMIT recurrenceDataChanged();
816 return;
817 case Weekly:
818 m_incidence->recurrence()->setWeekly(freq);
819 Q_EMIT recurrenceDataChanged();
820 return;
821 case Monthly:
822 m_incidence->recurrence()->setMonthly(freq);
823 Q_EMIT recurrenceDataChanged();
824 return;
825 case Yearly:
826 m_incidence->recurrence()->setYearly(freq);
827 Q_EMIT recurrenceDataChanged();
828 return;
829 default:
830 qWarning() << "Unknown interval for recurrence" << interval;
831 return;
832 }
833}
834
835void IncidenceWrapper::setMonthlyPosRecurrence(short pos, int day)
836{
837 QBitArray daysBitArray(7);
838 daysBitArray[day] = 1;
839 m_incidence->recurrence()->addMonthlyPos(pos, daysBitArray);
840}
841
842void IncidenceWrapper::setRecurrenceOccurrences(int occurrences)
843{
844 m_incidence->recurrence()->setDuration(occurrences);
845 Q_EMIT recurrenceDataChanged();
846}
847
848void IncidenceWrapper::clearRecurrences()
849{
850 m_incidence->recurrence()->clear();
851 Q_EMIT recurrenceDataChanged();
852}
853
854void IncidenceWrapper::itemChanged(const Akonadi::Item &item)
855{
857 qDebug()<< item.payload<KCalendarCore::Incidence::Ptr>()->summary() << item.parentCollection().id();
858 setIncidenceItem(item);
859 }
860}
861
862// TODO remove with 22.08, won't be needed anymore
863void IncidenceWrapper::setCollection(const Akonadi::Collection &collection)
864{
865 setCollectionId(collection.id());
866}
867
868#ifndef UNITY_CMAKE_SUPPORT
869Q_DECLARE_METATYPE(KCalendarCore::Incidence::Ptr)
870#endif
void setAncestorRetrieval(AncestorRetrieval ancestorDepth)
void fetchAllAttributes(bool fetch=true)
void fetchFullPayload(bool fetch=true)
void setItem(const Item &item)
void setPayload(const T &p)
Collection & parentCollection()
bool hasPayload() const
T payload() const
This class is a wrapper for a KCalendarCore::Incidence::Ptr object.
void setDtEnd(const QDateTime &dtEnd)
QSharedPointer< Event > Ptr
virtual QDateTime dtEnd() const
QSharedPointer< Todo > Ptr
QString formatSpelloutDuration(quint64 msecs) const
Q_SCRIPTABLE Q_NOREPLY void start()
QString i18n(const char *text, const TYPE &arg...)
AKONADI_CALENDAR_EXPORT KCalendarCore::Incidence::Ptr incidence(const Akonadi::Item &item)
AKONADI_CALENDAR_EXPORT KCalendarCore::Todo::Ptr todo(const Akonadi::Item &item)
const QList< QKeySequence > & end()
qsizetype size() const const
bool setDate(int year, int month, int day)
QDateTime addSecs(qint64 s) const const
QDateTime currentDateTime()
qint64 currentSecsSinceEpoch()
QDate date() const const
bool isValid() const const
void setDate(QDate date)
void setSecsSinceEpoch(qint64 secs)
void setTime(QTime time)
void setTimeZone(const QTimeZone &toZone)
QTime time() const const
QTimeZone timeZone() const const
QVariant toVariant() const const
void append(QList< T > &&value)
qsizetype size() const const
QLocale system()
QString toString(QDate date, FormatType format) const const
Q_EMITQ_EMIT
virtual bool event(QEvent *e)
T * data() const const
QSharedPointer< X > staticCast() const const
QFuture< void > map(Iterator begin, Iterator end, MapFunctor &&function)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
bool setHMS(int h, int m, int s, int ms)
QByteArray id() const const
int offsetFromUtc(const QDateTime &atDateTime) const const
bool canConvert() const const
QVariant fromValue(T &&value)
bool toBool() const const
QDateTime toDateTime() const const
int toInt(bool *ok) const const
T value() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:15:37 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.