KCalendarCore

incidence.cpp
Go to the documentation of this file.
1/*
2 This file is part of the kcalcore library.
3
4 SPDX-FileCopyrightText: 2001 Cornelius Schumacher <schumacher@kde.org>
5 SPDX-FileCopyrightText: 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
6
7 SPDX-License-Identifier: LGPL-2.0-or-later
8*/
9/**
10 @file
11 This file is part of the API for handling calendar data and
12 defines the Incidence class.
13
14 @brief
15 Provides the class common to non-FreeBusy (Events, To-dos, Journals)
16 calendar components known as incidences.
17
18 @author Cornelius Schumacher <schumacher@kde.org>
19 @author Reinhold Kainhofer <reinhold@kainhofer.com>
20*/
21
22#include "incidence.h"
23#include "incidence_p.h"
24#include "calformat.h"
25#include "kcalendarcore_debug.h"
26#include "utils_p.h"
27
28#include <math.h>
29
30#include <QStringList>
31#include <QTextDocument> // for .toHtmlEscaped() and Qt::mightBeRichText()
32#include <QTime>
33#include <QTimeZone>
34
35using namespace KCalendarCore;
36
37IncidencePrivate::IncidencePrivate() = default;
38
39IncidencePrivate::IncidencePrivate(const IncidencePrivate &p)
40 : IncidenceBasePrivate(p)
41 , mCreated(p.mCreated)
42 , mDescription(p.mDescription)
43 , mSummary(p.mSummary)
44 , mLocation(p.mLocation)
45 , mCategories(p.mCategories)
46 , mResources(p.mResources)
47 , mStatusString(p.mStatusString)
48 , mSchedulingID(p.mSchedulingID)
49 , mRelatedToUid(p.mRelatedToUid)
50 , mRecurrenceId(p.mRecurrenceId)
51 , mConferences(p.mConferences)
52 , mGeoLatitude(p.mGeoLatitude)
53 , mGeoLongitude(p.mGeoLongitude)
54 , mRecurrence(nullptr)
55 , mRevision(p.mRevision)
56 , mPriority(p.mPriority)
57 , mStatus(p.mStatus)
58 , mSecrecy(p.mSecrecy)
59 , mColor(p.mColor)
60 , mDescriptionIsRich(p.mDescriptionIsRich)
61 , mSummaryIsRich(p.mSummaryIsRich)
62 , mLocationIsRich(p.mLocationIsRich)
63 , mThisAndFuture(p.mThisAndFuture)
64 , mLocalOnly(false)
65{
66}
67
68IncidencePrivate::IncidencePrivate(const Incidence &other)
69 : IncidencePrivate(*other.d_func())
70{
71}
72
73void IncidencePrivate::clear()
74{
75 mAlarms.clear();
76 mAttachments.clear();
77 delete mRecurrence;
78 mRecurrence = nullptr;
79}
80
81void IncidencePrivate::init(Incidence *q, const IncidencePrivate &other)
82{
83 mRevision = other.mRevision;
84 mCreated = other.mCreated;
85 mDescription = other.mDescription;
86 mDescriptionIsRich = other.mDescriptionIsRich;
87 mSummary = other.mSummary;
88 mSummaryIsRich = other.mSummaryIsRich;
89 mCategories = other.mCategories;
90 mRelatedToUid = other.mRelatedToUid;
91 mResources = other.mResources;
92 mStatusString = other.mStatusString;
93 mStatus = other.mStatus;
94 mSecrecy = other.mSecrecy;
95 mPriority = other.mPriority;
96 mLocation = other.mLocation;
97 mLocationIsRich = other.mLocationIsRich;
98 mGeoLatitude = other.mGeoLatitude;
99 mGeoLongitude = other.mGeoLongitude;
100 mRecurrenceId = other.mRecurrenceId;
101 mConferences = other.mConferences;
102 mThisAndFuture = other.mThisAndFuture;
103 mLocalOnly = other.mLocalOnly;
104 mColor = other.mColor;
105
106 // Alarms and Attachments are stored in ListBase<...>, which is a QValueList<...*>.
107 // We need to really duplicate the objects stored therein, otherwise deleting
108 // i will also delete all attachments from this object (setAutoDelete...)
109 mAlarms.reserve(other.mAlarms.count());
110 for (const Alarm::Ptr &alarm : std::as_const(other.mAlarms)) {
111 Alarm::Ptr b(new Alarm(*alarm.data()));
112 b->setParent(q);
113 mAlarms.append(b);
114 }
115
116 mAttachments = other.mAttachments;
117 if (other.mRecurrence) {
118 mRecurrence = new Recurrence(*(other.mRecurrence));
119 mRecurrence->addObserver(q);
120 } else {
121 mRecurrence = nullptr;
122 }
123}
124
125bool IncidencePrivate::validStatus(Incidence::Status status)
126{
128}
129//@endcond
130
131Incidence::Incidence(IncidencePrivate *p)
132 : IncidenceBase(p)
133{
134 recreate();
136}
137
138Incidence::Incidence(const Incidence &other, IncidencePrivate *p)
139 : IncidenceBase(other, p)
140 , Recurrence::RecurrenceObserver()
141{
142 Q_D(Incidence);
143 d->init(this, *(other.d_func()));
145}
146
148{
149 // Alarm has a raw incidence pointer, so we must set it to 0
150 // so Alarm doesn't use it after Incidence is destroyed
151 Q_D(const Incidence);
152 for (const Alarm::Ptr &alarm : std::as_const(d->mAlarms)) {
153 alarm->setParent(nullptr);
154 }
155 delete d->mRecurrence;
156}
157
158//@cond PRIVATE
159// A string comparison that considers that null and empty are the same
160static bool stringCompare(const QString &s1, const QString &s2)
161{
162 return (s1.isEmpty() && s2.isEmpty()) || (s1 == s2);
163}
164
165//@endcond
167{
168 Q_D(Incidence);
169 if (&other != this) {
170 d->clear();
171 // TODO: should relations be cleared out, as in destructor???
173 const Incidence *i = static_cast<const Incidence *>(&other);
174 d->init(this, *(i->d_func()));
175 }
176
177 return *this;
178}
179
180bool Incidence::equals(const IncidenceBase &incidence) const
181{
182 if (!IncidenceBase::equals(incidence)) {
183 return false;
184 }
185
186 // If they weren't the same type IncidenceBase::equals would had returned false already
187 const Incidence *i2 = static_cast<const Incidence *>(&incidence);
188
189 const Alarm::List alarmList = alarms();
190 const Alarm::List otherAlarmsList = i2->alarms();
191 if (alarmList.count() != otherAlarmsList.count()) {
192 return false;
193 }
194
195 auto matchFunc = [](const Alarm::Ptr &a, const Alarm::Ptr &b) {
196 return *a == *b;
197 };
198
199 const auto [it1, it2] = std::mismatch(alarmList.cbegin(), alarmList.cend(), otherAlarmsList.cbegin(), otherAlarmsList.cend(), matchFunc);
200 // Checking the iterator from one list only, since both lists are the same size
201 if (it1 != alarmList.cend()) {
202 return false;
203 }
204
205 const Attachment::List attachmentList = attachments();
206 const Attachment::List otherAttachmentList = i2->attachments();
207 if (attachmentList.count() != otherAttachmentList.count()) {
208 return false;
209 }
210
211 const auto [at1, at2] = std::mismatch(attachmentList.cbegin(), attachmentList.cend(), otherAttachmentList.cbegin(), otherAttachmentList.cend());
212
213 // Checking the iterator from one list only, since both lists are the same size
214 if (at1 != attachmentList.cend()) {
215 return false;
216 }
217
218 Q_D(const Incidence);
219 bool recurrenceEqual = (d->mRecurrence == nullptr && i2->d_func()->mRecurrence == nullptr);
220 if (!recurrenceEqual) {
221 recurrence(); // create if doesn't exist
222 i2->recurrence(); // create if doesn't exist
223 recurrenceEqual = d->mRecurrence != nullptr && i2->d_func()->mRecurrence != nullptr
224 && *d->mRecurrence == *i2->d_func()->mRecurrence;
225 }
226
227 if (!qFuzzyCompare(d->mGeoLatitude, i2->d_func()->mGeoLatitude) || !qFuzzyCompare(d->mGeoLongitude, i2->d_func()->mGeoLongitude)) {
228 return false;
229 }
230 // clang-format off
231 return
232 recurrenceEqual
233 && created() == i2->created()
234 && stringCompare(description(), i2->description())
236 && stringCompare(summary(), i2->summary())
237 && summaryIsRich() == i2->summaryIsRich()
238 && categories() == i2->categories()
239 && stringCompare(relatedTo(), i2->relatedTo())
240 && resources() == i2->resources()
241 && d->mStatus == i2->d_func()->mStatus
242 && (d->mStatus == StatusNone || stringCompare(d->mStatusString, i2->d_func()->mStatusString))
243 && secrecy() == i2->secrecy()
244 && priority() == i2->priority()
245 && stringCompare(location(), i2->location())
246 && locationIsRich() == i2->locationIsRich()
247 && stringCompare(color(), i2->color())
248 && stringCompare(schedulingID(), i2->schedulingID())
249 && recurrenceId() == i2->recurrenceId()
250 && conferences() == i2->conferences()
251 && thisAndFuture() == i2->thisAndFuture();
252 // clang-format on
253}
254
256{
257 if (hasRecurrenceId()) {
258 return uid() + recurrenceId().toString(Qt::ISODate);
259 }
260 return uid();
261}
262
264{
266 setCreated(nowUTC);
267
269 setRevision(0);
270 setLastModified(nowUTC); // NOLINT false clang-analyzer-optin.cplusplus.VirtualCall
271}
272
274{
275 Q_D(const Incidence);
276 if (!d->mLocalOnly) {
278 }
279}
280
281void Incidence::setReadOnly(bool readOnly)
282{
283 Q_D(const Incidence);
285 if (d->mRecurrence) {
286 d->mRecurrence->setRecurReadOnly(readOnly);
287 }
288}
289
290void Incidence::setLocalOnly(bool localOnly)
291{
292 if (mReadOnly) {
293 return;
294 }
295 Q_D(Incidence);
296 d->mLocalOnly = localOnly;
297}
298
300{
301 Q_D(const Incidence);
302 return d->mLocalOnly;
303}
304
305void Incidence::setAllDay(bool allDay)
306{
307 if (mReadOnly) {
308 return;
309 }
310 Q_D(const Incidence);
311 if (d->mRecurrence) {
312 d->mRecurrence->setAllDay(allDay);
313 }
315}
316
318{
319 Q_D(Incidence);
320 if (mReadOnly || d->mLocalOnly) {
321 return;
322 }
323
324 update();
325 d->mCreated = created.toUTC();
326 const auto ct = d->mCreated.time();
327 // Remove milliseconds
328 d->mCreated.setTime(QTime(ct.hour(), ct.minute(), ct.second()));
330 updated();
331}
332
333QDateTime Incidence::created() const
334{
335 Q_D(const Incidence);
336 return d->mCreated;
337}
338
340{
341 Q_D(Incidence);
342 if (mReadOnly || d->mLocalOnly) {
343 return;
344 }
345
346 update();
347 d->mRevision = rev;
349 updated();
350}
351
353{
354 Q_D(const Incidence);
355 return d->mRevision;
356}
357
359{
360 Q_D(const Incidence);
362 if (d->mRecurrence && dirtyFields().contains(FieldDtStart)) {
363 d->mRecurrence->setStartDateTime(dt, allDay());
364 }
365}
366
367void Incidence::shiftTimes(const QTimeZone &oldZone, const QTimeZone &newZone)
368{
369 Q_D(const Incidence);
370 IncidenceBase::shiftTimes(oldZone, newZone);
371 if (d->mRecurrence) {
372 d->mRecurrence->shiftTimes(oldZone, newZone);
373 }
374 if (d->mAlarms.count() > 0) {
375 update();
376 for (auto alarm : d->mAlarms) {
377 alarm->shiftTimes(oldZone, newZone);
378 }
380 updated();
381 }
382}
383
384void Incidence::setDescription(const QString &description, bool isRich)
385{
386 if (mReadOnly) {
387 return;
388 }
389 update();
390 Q_D(Incidence);
391 d->mDescription = description;
392 d->mDescriptionIsRich = isRich;
394 updated();
395}
396
397void Incidence::setDescription(const QString &description)
398{
399 setDescription(description, Qt::mightBeRichText(description));
400}
401
402QString Incidence::description() const
403{
404 Q_D(const Incidence);
405 return d->mDescription;
406}
407
409{
410 Q_D(const Incidence);
411 if (descriptionIsRich()) {
412 return d->mDescription;
413 } else {
414 return d->mDescription.toHtmlEscaped().replace(QLatin1Char('\n'), QStringLiteral("<br/>"));
415 }
416}
417
419{
420 Q_D(const Incidence);
421 return d->mDescriptionIsRich;
422}
423
424void Incidence::setSummary(const QString &summary, bool isRich)
425{
426 if (mReadOnly) {
427 return;
428 }
429 Q_D(Incidence);
430 if (d->mSummary != summary || d->mSummaryIsRich != isRich) {
431 update();
432 d->mSummary = summary;
433 d->mSummaryIsRich = isRich;
435 updated();
436 }
437}
438
439void Incidence::setSummary(const QString &summary)
440{
441 setSummary(summary, Qt::mightBeRichText(summary));
442}
443
444QString Incidence::summary() const
445{
446 Q_D(const Incidence);
447 return d->mSummary;
448}
449
451{
452 Q_D(const Incidence);
453 if (summaryIsRich()) {
454 return d->mSummary;
455 } else {
456 return d->mSummary.toHtmlEscaped().replace(QLatin1Char('\n'), QStringLiteral("<br/>"));
457 }
458}
459
461{
462 Q_D(const Incidence);
463 return d->mSummaryIsRich;
464}
465
467{
468 if (mReadOnly) {
469 return;
470 }
471
472 Q_D(Incidence);
473 update();
474 d->mCategories = categories;
476 updated();
477}
478
480{
481 if (mReadOnly) {
482 return;
483 }
484 update();
486
487 Q_D(Incidence);
488 d->mCategories.clear();
489
490 if (catStr.isEmpty()) {
491 updated();
492 return;
493 }
494
495 d->mCategories = catStr.split(QLatin1Char(','));
496
497 for (auto &category : d->mCategories) {
498 category = category.trimmed();
499 }
500
501 updated();
502}
503
504QStringList Incidence::categories() const
505{
506 Q_D(const Incidence);
507 return d->mCategories;
508}
509
511{
512 Q_D(const Incidence);
513 return d->mCategories.join(QLatin1Char(','));
514}
515
516void Incidence::setRelatedTo(const QString &relatedToUid, RelType relType)
517{
518 // TODO: RFC says that an incidence can have more than one related-to field
519 // even for the same relType.
520
521 Q_D(Incidence);
522 if (d->mRelatedToUid[relType] != relatedToUid) {
523 update();
524 d->mRelatedToUid[relType] = relatedToUid;
526 updated();
527 }
528}
529
531{
532 Q_D(const Incidence);
533 return d->mRelatedToUid.value(relType);
534}
535
536void Incidence::setColor(const QString &colorName)
537{
538 if (mReadOnly) {
539 return;
540 }
541 Q_D(Incidence);
542 if (!stringCompare(d->mColor, colorName)) {
543 update();
544 d->mColor = colorName;
546 updated();
547 }
548}
549
551{
552 Q_D(const Incidence);
553 return d->mColor;
554}
555
556// %%%%%%%%%%%% Recurrence-related methods %%%%%%%%%%%%%%%%%%%%
557
559{
560 Q_D(const Incidence);
561 if (!d->mRecurrence) {
562 d->mRecurrence = new Recurrence();
563 d->mRecurrence->setStartDateTime(dateTime(RoleRecurrenceStart), allDay());
564 d->mRecurrence->setAllDay(allDay());
565 d->mRecurrence->setRecurReadOnly(mReadOnly);
566 d->mRecurrence->addObserver(const_cast<KCalendarCore::Incidence *>(this));
567 }
568
569 return d->mRecurrence;
570}
571
573{
574 Q_D(const Incidence);
575 delete d->mRecurrence;
576 d->mRecurrence = nullptr;
577}
578
580{
581 Q_D(const Incidence);
582 if (d->mRecurrence) {
583 return d->mRecurrence->recurrenceType();
584 } else {
585 return Recurrence::rNone;
586 }
587}
588
590{
591 Q_D(const Incidence);
592 if (d->mRecurrence) {
593 return d->mRecurrence->recurs();
594 } else {
595 return false;
596 }
597}
598
599bool Incidence::recursOn(const QDate &date, const QTimeZone &timeZone) const
600{
601 Q_D(const Incidence);
602 return d->mRecurrence && d->mRecurrence->recursOn(date, timeZone);
603}
604
605bool Incidence::recursAt(const QDateTime &qdt) const
606{
607 Q_D(const Incidence);
608 return d->mRecurrence && d->mRecurrence->recursAt(qdt);
609}
610
612{
613 QList<QDateTime> result;
614 if (!date.isValid()) {
615 qCWarning(KCALCORE_LOG) << "Invalid date encountered";
616 return result;
617 }
618
619 QDateTime start = dtStart();
620 QDateTime end = dateTime(RoleEndRecurrenceBase);
621
622 // TODO_Recurrence: Also work if only due date is given...
623 if (!start.isValid() && !end.isValid()) {
624 return result;
625 }
626
627 // if the incidence doesn't recur,
628 QDateTime kdate(date, {}, timeZone);
629 if (!recurs()) {
630 if (start.date() <= date && end.date() >= date) {
631 result << start;
632 }
633 return result;
634 }
635
636 qint64 days = start.daysTo(end);
637 // Account for possible recurrences going over midnight, while the original event doesn't
638 QDate tmpday(date.addDays(-days - 1));
639 QDateTime tmp;
640 while (tmpday <= date) {
641 if (recurrence()->recursOn(tmpday, timeZone)) {
642 const QList<QTime> times = recurrence()->recurTimesOn(tmpday, timeZone);
643 for (const QTime &time : times) {
644 tmp = QDateTime(tmpday, time, start.timeZone());
645 if (endDateForStart(tmp) >= kdate) {
646 result << tmp;
647 }
648 }
649 }
650 tmpday = tmpday.addDays(1);
651 }
652 return result;
653}
654
656{
657 QList<QDateTime> result;
658 if (!datetime.isValid()) {
659 qCWarning(KCALCORE_LOG) << "Invalid datetime encountered";
660 return result;
661 }
662
663 QDateTime start = dtStart();
664 QDateTime end = dateTime(RoleEndRecurrenceBase);
665
666 // TODO_Recurrence: Also work if only due date is given...
667 if (!start.isValid() && !end.isValid()) {
668 return result;
669 }
670
671 // if the incidence doesn't recur,
672 if (!recurs()) {
673 if (!(start > datetime || end < datetime)) {
674 result << start;
675 }
676 return result;
677 }
678
679 qint64 days = start.daysTo(end);
680 // Account for possible recurrences going over midnight, while the original event doesn't
681 QDate tmpday(datetime.date().addDays(-days - 1));
682 QDateTime tmp;
683 while (tmpday <= datetime.date()) {
684 if (recurrence()->recursOn(tmpday, datetime.timeZone())) {
685 // Get the times during the day (in start date's time zone) when recurrences happen
686 const QList<QTime> times = recurrence()->recurTimesOn(tmpday, start.timeZone());
687 for (const QTime &time : times) {
688 tmp = QDateTime(tmpday, time, start.timeZone());
689 if (!(tmp > datetime || endDateForStart(tmp) < datetime)) {
690 result << tmp;
691 }
692 }
693 }
694 tmpday = tmpday.addDays(1);
695 }
696 return result;
697}
698
700{
701 QDateTime start = dtStart();
702 QDateTime end = dateTime(RoleEndRecurrenceBase);
703 if (!end.isValid()) {
704 return start;
705 }
706 if (!start.isValid()) {
707 return end;
708 }
709
710 return startDt.addSecs(start.secsTo(end));
711}
712
714{
715 if (mReadOnly || attachment.isEmpty()) {
716 return;
717 }
718
719 Q_D(Incidence);
720 update();
721 d->mAttachments.append(attachment);
723 updated();
724}
725
727{
728 Q_D(Incidence);
729 auto it = std::remove_if(d->mAttachments.begin(), d->mAttachments.end(), [&mime](const Attachment &a) {
730 return a.mimeType() == mime;
731 });
732 if (it != d->mAttachments.end()) {
733 update();
734 d->mAttachments.erase(it, d->mAttachments.end());
736 updated();
737 }
738}
739
740Attachment::List Incidence::attachments() const
741{
742 Q_D(const Incidence);
743 return d->mAttachments;
744}
745
746Attachment::List Incidence::attachments(const QString &mime) const
747{
748 Q_D(const Incidence);
749 Attachment::List attachments;
750 for (const Attachment &attachment : std::as_const(d->mAttachments)) {
751 if (attachment.mimeType() == mime) {
752 attachments.append(attachment);
753 }
754 }
755 return attachments;
756}
757
759{
760 Q_D(Incidence);
761 update();
763 d->mAttachments.clear();
764 updated();
765}
766
768{
769 if (mReadOnly) {
770 return;
771 }
772
773 update();
774 Q_D(Incidence);
775 d->mResources = resources;
777 updated();
778}
779
781{
782 Q_D(const Incidence);
783 return d->mResources;
784}
785
786void Incidence::setPriority(int priority)
787{
788 if (mReadOnly) {
789 return;
790 }
791
792 if (priority < 0 || priority > 9) {
793 qCWarning(KCALCORE_LOG) << "Ignoring invalid priority" << priority;
794 return;
795 }
796
797 update();
798 Q_D(Incidence);
799 d->mPriority = priority;
801 updated();
802}
803
804int Incidence::priority() const
805{
806 Q_D(const Incidence);
807 return d->mPriority;
808}
809
811{
812 if (mReadOnly) {
813 qCWarning(KCALCORE_LOG) << "Attempt to set status of read-only incidence";
814 return;
815 }
816
817 Q_D(Incidence);
818 if (d->validStatus(status)) {
819 update();
820 d->mStatus = status;
821 d->mStatusString.clear();
823 updated();
824 } else {
825 qCWarning(KCALCORE_LOG) << "Ignoring invalid status" << status << "for" << typeStr();
826 }
827}
828
830{
831 if (mReadOnly) {
832 return;
833 }
834
835 update();
836 Q_D(Incidence);
837 d->mStatus = status.isEmpty() ? StatusNone : StatusX;
838 d->mStatusString = status;
840 updated();
841}
842
843Incidence::Status Incidence::status() const
844{
845 Q_D(const Incidence);
846 return d->mStatus;
847}
848
850{
851 Q_D(const Incidence);
852 if (d->mStatus == StatusX) {
853 return d->mStatusString;
854 } else {
855 return QString();
856 }
857}
858
860{
861 if (mReadOnly) {
862 return;
863 }
864
865 update();
866 Q_D(Incidence);
867 d->mSecrecy = secrecy;
869 updated();
870}
871
872Incidence::Secrecy Incidence::secrecy() const
873{
874 Q_D(const Incidence);
875 return d->mSecrecy;
876}
877
879{
880 Q_D(const Incidence);
881 return d->mAlarms;
882}
883
885{
886 Q_D(Incidence);
887 Alarm::Ptr alarm(new Alarm(this));
888 addAlarm(alarm);
889 return alarm;
890}
891
893{
894 Q_D(Incidence);
895 update();
896 d->mAlarms.append(alarm);
898 updated();
899}
900
902{
903 Q_D(Incidence);
904 const int index = d->mAlarms.indexOf(alarm);
905 if (index > -1) {
906 update();
907 d->mAlarms.remove(index);
909 updated();
910 }
911}
912
914{
915 update();
916 Q_D(Incidence);
917 d->mAlarms.clear();
919 updated();
920}
921
923{
924 Q_D(const Incidence);
925 return std::any_of(d->mAlarms.cbegin(), d->mAlarms.cend(), [](const Alarm::Ptr &alarm) {
926 return alarm->enabled();
927 });
928}
929
930Conference::List Incidence::conferences() const
931{
932 Q_D(const Incidence);
933 return d->mConferences;
934}
935
937{
938 update();
939 Q_D(Incidence);
940 d->mConferences.push_back(conference);
942 updated();
943}
944
946{
947 update();
948 Q_D(Incidence);
949 d->mConferences = conferences;
951 updated();
952}
953
955{
956 update();
957 Q_D(Incidence);
958 d->mConferences.clear();
960 updated();
961}
962
963void Incidence::setLocation(const QString &location, bool isRich)
964{
965 if (mReadOnly) {
966 return;
967 }
968
969 Q_D(Incidence);
970 if (d->mLocation != location || d->mLocationIsRich != isRich) {
971 update();
972 d->mLocation = location;
973 d->mLocationIsRich = isRich;
975 updated();
976 }
977}
978
979void Incidence::setLocation(const QString &location)
980{
981 setLocation(location, Qt::mightBeRichText(location));
982}
983
984QString Incidence::location() const
985{
986 Q_D(const Incidence);
987 return d->mLocation;
988}
989
991{
992 Q_D(const Incidence);
993 if (locationIsRich()) {
994 return d->mLocation;
995 } else {
996 return d->mLocation.toHtmlEscaped().replace(QLatin1Char('\n'), QStringLiteral("<br/>"));
997 }
998}
999
1001{
1002 Q_D(const Incidence);
1003 return d->mLocationIsRich;
1004}
1005
1006void Incidence::setSchedulingID(const QString &sid, const QString &uid)
1007{
1008 if (!uid.isEmpty()) {
1009 setUid(uid);
1010 }
1011 Q_D(Incidence);
1012 if (sid != d->mSchedulingID) {
1013 update();
1014 d->mSchedulingID = sid;
1016 updated();
1017 }
1018}
1019
1021{
1022 Q_D(const Incidence);
1023 if (d->mSchedulingID.isNull()) {
1024 // Nothing set, so use the normal uid
1025 return uid();
1026 }
1027 return d->mSchedulingID;
1028}
1029
1030bool Incidence::hasGeo() const
1031{
1032 Q_D(const Incidence);
1033 // For internal consistency, return false if either coordinate is invalid.
1034 return d->mGeoLatitude != INVALID_LATLON && d->mGeoLongitude != INVALID_LATLON;
1035}
1036
1037float Incidence::geoLatitude() const
1038{
1039 Q_D(const Incidence);
1040 // For internal consistency, both coordinates are considered invalid if either is.
1041 return (INVALID_LATLON == d->mGeoLongitude) ? INVALID_LATLON : d->mGeoLatitude;
1042}
1043
1044void Incidence::setGeoLatitude(float geolatitude)
1045{
1046 if (mReadOnly) {
1047 return;
1048 }
1049
1050 if (isnan(geolatitude)) {
1051 geolatitude = INVALID_LATLON;
1052 }
1053 if (geolatitude != INVALID_LATLON && (geolatitude < -90.0 || geolatitude > 90.0)) {
1054 qCWarning(KCALCORE_LOG) << "Ignoring invalid latitude" << geolatitude;
1055 return;
1056 }
1057
1058 Q_D(Incidence);
1059 update();
1060 d->mGeoLatitude = geolatitude;
1062 updated();
1063}
1064
1065float Incidence::geoLongitude() const
1066{
1067 Q_D(const Incidence);
1068 // For internal consistency, both coordinates are considered invalid if either is.
1069 return (INVALID_LATLON == d->mGeoLatitude) ? INVALID_LATLON : d->mGeoLongitude;
1070}
1071
1072void Incidence::setGeoLongitude(float geolongitude)
1073{
1074 if (mReadOnly) {
1075 return;
1076 }
1077
1078 if (isnan(geolongitude)) {
1079 geolongitude = INVALID_LATLON;
1080 }
1081 if (geolongitude != INVALID_LATLON && (geolongitude < -180.0 || geolongitude > 180.0)) {
1082 qCWarning(KCALCORE_LOG) << "Ignoring invalid longitude" << geolongitude;
1083 return;
1084 }
1085
1086 Q_D(Incidence);
1087 update();
1088 d->mGeoLongitude = geolongitude;
1090 updated();
1091}
1092
1094{
1095 Q_D(const Incidence);
1096 return (allDay() && d->mRecurrenceId.date().isValid()) || d->mRecurrenceId.isValid();
1097}
1098
1100{
1101 Q_D(const Incidence);
1102 return d->mRecurrenceId;
1103}
1104
1105void Incidence::setThisAndFuture(bool thisAndFuture)
1106{
1107 Q_D(Incidence);
1108 d->mThisAndFuture = thisAndFuture;
1109}
1110
1112{
1113 Q_D(const Incidence);
1114 return d->mThisAndFuture;
1115}
1116
1117void Incidence::setRecurrenceId(const QDateTime &recurrenceId)
1118{
1119 if (!mReadOnly) {
1120 update();
1121 Q_D(Incidence);
1122 d->mRecurrenceId = recurrenceId;
1124 updated();
1125 }
1126}
1127
1128/** Observer interface for the recurrence class. If the recurrence is changed,
1129 this method will be called for the incidence the recurrence object
1130 belongs to. */
1132{
1133 Q_D(const Incidence);
1134 if (recurrence == d->mRecurrence) {
1135 update();
1137 updated();
1138 }
1139}
1140
1141//@cond PRIVATE
1142#define ALT_DESC_FIELD "X-ALT-DESC"
1143#define ALT_DESC_PARAMETERS QStringLiteral("FMTTYPE=text/html")
1144//@endcond
1145
1147{
1148 const QString value = nonKDECustomProperty(ALT_DESC_FIELD);
1149 const QString parameter = nonKDECustomPropertyParameters(ALT_DESC_FIELD);
1150
1151 return parameter == ALT_DESC_PARAMETERS && !value.isEmpty();
1152}
1153
1154void Incidence::setAltDescription(const QString &altdescription)
1155{
1156 if (altdescription.isEmpty()) {
1157 removeNonKDECustomProperty(ALT_DESC_FIELD);
1158 } else {
1159 setNonKDECustomProperty(ALT_DESC_FIELD, altdescription, ALT_DESC_PARAMETERS);
1160 }
1161}
1162
1164{
1165 if (!hasAltDescription()) {
1166 return QString();
1167 } else {
1168 return nonKDECustomProperty(ALT_DESC_FIELD);
1169 }
1170}
1171
1172/** static */
1178
1180{
1181 Q_D(const Incidence);
1182 serializeQDateTimeAsKDateTime(out, d->mCreated);
1183 out << d->mRevision << d->mDescription << d->mDescriptionIsRich << d->mSummary << d->mSummaryIsRich << d->mLocation << d->mLocationIsRich << d->mCategories
1184 << d->mResources << d->mStatusString << d->mPriority << d->mSchedulingID << d->mGeoLatitude << d->mGeoLongitude
1185 << hasGeo(); // No longer used, but serialized/deserialized for compatibility.
1186 serializeQDateTimeAsKDateTime(out, d->mRecurrenceId);
1187 out << d->mThisAndFuture << d->mLocalOnly << d->mStatus << d->mSecrecy << (d->mRecurrence ? true : false) << (qint32)d->mAttachments.count()
1188 << (qint32)d->mAlarms.count() << (qint32)d->mConferences.count() << d->mRelatedToUid;
1189
1190 if (d->mRecurrence) {
1191 out << d->mRecurrence;
1192 }
1193
1194 for (const Attachment &attachment : std::as_const(d->mAttachments)) {
1195 out << attachment;
1196 }
1197
1198 for (const Alarm::Ptr &alarm : std::as_const(d->mAlarms)) {
1199 out << alarm;
1200 }
1201
1202 for (const Conference &conf : std::as_const(d->mConferences)) {
1203 out << conf;
1204 }
1205}
1206
1208{
1209 bool hasGeo; // No longer used, but serialized/deserialized for compatibility.
1210 quint32 status;
1211 quint32 secrecy;
1212 bool hasRecurrence;
1213 int attachmentCount;
1214 int alarmCount;
1215 int conferencesCount;
1216 QMap<int, QString> relatedToUid;
1217 Q_D(Incidence);
1218 deserializeKDateTimeAsQDateTime(in, d->mCreated);
1219 in >> d->mRevision >> d->mDescription >> d->mDescriptionIsRich >> d->mSummary >> d->mSummaryIsRich >> d->mLocation >> d->mLocationIsRich >> d->mCategories
1220 >> d->mResources >> d->mStatusString >> d->mPriority >> d->mSchedulingID >> d->mGeoLatitude >> d->mGeoLongitude >> hasGeo;
1221 deserializeKDateTimeAsQDateTime(in, d->mRecurrenceId);
1222 in >> d->mThisAndFuture >> d->mLocalOnly >> status >> secrecy >> hasRecurrence >> attachmentCount >> alarmCount >> conferencesCount >> relatedToUid;
1223
1224 if (hasRecurrence) {
1225 d->mRecurrence = new Recurrence();
1226 d->mRecurrence->addObserver(const_cast<KCalendarCore::Incidence *>(this));
1227 in >> d->mRecurrence;
1228 }
1229
1230 d->mAttachments.clear();
1231 d->mAlarms.clear();
1232 d->mConferences.clear();
1233
1234 d->mAttachments.reserve(attachmentCount);
1235 for (int i = 0; i < attachmentCount; ++i) {
1236 Attachment attachment;
1237 in >> attachment;
1238 d->mAttachments.append(attachment);
1239 }
1240
1241 d->mAlarms.reserve(alarmCount);
1242 for (int i = 0; i < alarmCount; ++i) {
1243 Alarm::Ptr alarm = Alarm::Ptr(new Alarm(this));
1244 in >> alarm;
1245 d->mAlarms.append(alarm);
1246 }
1247
1248 d->mConferences.reserve(conferencesCount);
1249 for (int i = 0; i < conferencesCount; ++i) {
1250 Conference conf;
1251 in >> conf;
1252 d->mConferences.push_back(conf);
1253 }
1254
1255 d->mStatus = static_cast<Incidence::Status>(status);
1256 d->mSecrecy = static_cast<Incidence::Secrecy>(secrecy);
1257
1258 d->mRelatedToUid.clear();
1259
1260 auto it = relatedToUid.cbegin();
1261 auto end = relatedToUid.cend();
1262 for (; it != end; ++it) {
1263 d->mRelatedToUid.insert(static_cast<Incidence::RelType>(it.key()), it.value());
1264 }
1265}
1266
1267#include "moc_incidence.cpp"
This file is part of the API for handling calendar data and defines the CalFormat abstract base class...
Represents an alarm notification.
Definition alarm.h:51
QSharedPointer< Alarm > Ptr
A shared pointer to an Alarm object.
Definition alarm.h:67
Represents information related to an attachment for a Calendar Incidence.
Definition attachment.h:47
static QString createUniqueId()
Creates a unique id string.
Definition calformat.cpp:88
Represents information related to a conference information of an Calendar Incidence,...
Definition conference.h:32
void setNonKDECustomProperty(const QByteArray &name, const QString &value, const QString &parameters=QString())
Create or modify a non-KDE or non-standard custom calendar property.
QString nonKDECustomPropertyParameters(const QByteArray &name) const
Return the parameters of a non-KDE or non-standard custom calendar property.
void removeNonKDECustomProperty(const QByteArray &name)
Delete a non-KDE or non-standard custom calendar property.
QString nonKDECustomProperty(const QByteArray &name) const
Return the value of a non-KDE or non-standard custom calendar property.
static QLatin1String eventMimeType()
Returns the Akonadi specific sub MIME type of a KCalendarCore::Event.
Definition event.cpp:335
An abstract class that provides a common base for all calendar incidence classes.
void updated()
Call this to notify the observers after the IncidenceBase object has changed.
virtual void setDtStart(const QDateTime &dtStart)
Sets the incidence's starting date/time with a QDateTime.
virtual void shiftTimes(const QTimeZone &oldZone, const QTimeZone &newZone)
Shift the times of the incidence so that they appear at the same clock time as before but in a new ti...
bool mReadOnly
Identifies a read-only incidence.
void update()
Call this to notify the observers after the IncidenceBase object will be changed.
QSet< IncidenceBase::Field > dirtyFields() const
Returns a QSet with all Fields that were changed since the incidence was created or resetDirtyFields(...
virtual void setReadOnly(bool readOnly)
Sets readonly status.
virtual void setLastModified(const QDateTime &lm)
Sets the time the incidence was last modified to lm.
@ FieldCreated
Field representing the CREATED component.
@ FieldRevision
Field representing the SEQUENCE component.
@ FieldDtStart
Field representing the DTSTART component.
@ FieldRelatedTo
Field representing the RELATED-TO component.
@ FieldGeoLongitude
Field representing the longitude part of the GEO component.
@ FieldDescription
Field representing the DESCRIPTION component.
@ FieldLocation
Field representing the LOCATION component.
@ FieldSummary
Field representing the SUMMARY component.
@ FieldRecurrenceId
Field representing the RECURRENCE-ID component.
@ FieldStatus
Field representing the STATUS component.
@ FieldConferences
Field representing the CONFERENCE component.
@ FieldPriority
Field representing the PRIORITY component.
@ FieldColor
Field representing the COLOR component.
@ FieldCategories
Field representing the CATEGORIES component.
@ FieldAlarms
Field representing the VALARM component.
@ FieldResources
Field representing the RESOURCES component.
@ FieldRecurrence
Field representing the EXDATE, EXRULE, RDATE, and RRULE components.
@ FieldAttachment
Field representing the ATTACH component.
@ FieldGeoLatitude
Field representing the latitude part of the GEO component.
@ FieldSecrecy
Field representing the CLASS component.
@ FieldSchedulingId
Field representing the X-KDE-LIBKCAL-ID component.
virtual IncidenceBase & assign(const IncidenceBase &other)
Provides polymorfic assignment.
virtual QByteArray typeStr() const =0
Prints the type of incidence as a string.
void setFieldDirty(IncidenceBase::Field field)
Marks Field field as dirty.
@ RoleRecurrenceStart
Role for determining the start of the recurrence.
void setUid(const QString &uid)
Sets the unique id for the incidence to uid.
void resetDirtyFields()
Resets dirty fields.
virtual void setAllDay(bool allDay)
Sets whether the incidence is all-day, i.e.
virtual QDateTime dateTime(DateTimeRole role) const =0
Returns a date/time corresponding to the specified DateTimeRole.
virtual bool equals(const IncidenceBase &incidenceBase) const
Provides polymorfic comparison for equality.
Provides the abstract base class common to non-FreeBusy (Events, To-dos, Journals) calendar component...
Definition incidence.h:60
QString schedulingID() const
Returns the incidence scheduling ID.
QString relatedTo(RelType relType=RelTypeParent) const
Returns a UID string for the incidence that is related to this one.
void setLocalOnly(bool localonly)
Set localOnly state of incidence.
void recurrenceUpdated(Recurrence *recurrence) override
Observer interface for the recurrence class.
~Incidence() override
Destroys an incidence.
void addConference(const Conference &conference)
Adds a conference to the incidence.
void setSchedulingID(const QString &sid, const QString &uid=QString())
Set the incidence scheduling ID.
void setAltDescription(const QString &altdescription)
Sets the incidence's alternative (=text/html) description.
RelType
The different types of RELTYPE values specified by the RFC.
Definition incidence.h:108
QString categoriesStr() const
Returns the incidence categories as a comma separated string.
bool locationIsRich() const
Returns true if incidence location contains RichText; false otherwise.
void setCustomStatus(const QString &status)
Sets the incidence Status to a non-standard status value.
void recreate()
Recreate incidence.
void setGeoLatitude(float geolatitude)
Set the incidence's geoLatitude.
void clearAlarms()
Removes all alarms.
void setRecurrenceId(const QDateTime &recurrenceId)
Set the incidences recurrenceId.
void removeAlarm(const Alarm::Ptr &alarm)
Removes the specified alarm from the incidence.
void clearAttachments()
Removes all attachments and frees the memory used by them.
virtual QList< QDateTime > startDateTimesForDate(const QDate &date, const QTimeZone &timeZone) const
Calculates the start date/time for all recurrences that happen at some time on the given date (might ...
virtual QList< QDateTime > startDateTimesForDateTime(const QDateTime &datetime) const
Calculates the start date/time for all recurrences that happen at the given time.
void setLastModified(const QDateTime &lm) override
Sets the time the incidence was last modified to lm.
bool hasAltDescription() const
Returns true if the alternative (=text/html) description is available.
int revision() const
Returns the number of revisions this incidence has seen.
void setColor(const QString &colorName)
Set the incidence color, as added in RFC7986.
void setLocation(const QString &location, bool isRich)
Sets the incidence location.
void setPriority(int priority)
Sets the incidences priority.
QString richLocation() const
Returns the incidence location in rich text format.
bool hasRecurrenceId() const
Returns true if the incidence has recurrenceId, otherwise return false.
void setGeoLongitude(float geolongitude)
Set the incidence's geoLongitude.
static QStringList mimeTypes()
Returns the list of possible mime types in an Incidence object: "text/calendar" "application/x-vnd....
bool hasEnabledAlarms() const
Returns true if any of the incidence alarms are enabled; false otherwise.
void setSummary(const QString &summary, bool isRich)
Sets the incidence summary.
QString customStatus() const
Returns the non-standard status value.
virtual bool recursOn(const QDate &date, const QTimeZone &timeZone) const
Returns true if the date specified is one on which the event will recur.
Status
The different types of overall incidence status or confirmation.
Definition incidence.h:80
@ StatusX
a non-standard status string
Definition incidence.h:90
Alarm::List alarms() const
Returns a list of all incidence alarms.
void setStatus(Status status)
Sets the incidence status to a standard Status value.
ushort recurrenceType() const
Returns the event's recurrence status.
Secrecy
The different types of incidence access classifications.
Definition incidence.h:97
bool recurs() const
Returns whether the event recurs at all.
virtual QDateTime endDateForStart(const QDateTime &startDt) const
Returns the end date/time of the incidence occurrence if it starts at specified date/time.
void setAllDay(bool allDay) override
Sets whether the incidence is all-day, i.e.
QStringList resources() const
Returns the incidence resources as a list of strings.
void setThisAndFuture(bool thisAndFuture)
Set to true if the exception also applies to all future occurrences.
bool descriptionIsRich() const
Returns true if incidence description contains RichText; false otherwise.
bool recursAt(const QDateTime &dt) const
Returns true if the date/time specified is one at which the event will recur.
void setResources(const QStringList &resources)
Sets a list of incidence resources.
void serialize(QDataStream &out) const override
Sub-type specific serialization.
QString altDescription() const
Returns the incidence alternative (=text/html) description.
void shiftTimes(const QTimeZone &oldZone, const QTimeZone &newZone) override
Shift the times of the incidence so that they appear at the same clock time as before but in a new ti...
Alarm::Ptr newAlarm()
Create a new incidence alarm.
void deleteAttachments(const QString &mime)
Removes all attachments of the specified MIME type from the incidence.
IncidenceBase & assign(const IncidenceBase &other) override
Provides polymorfic assignment.
QString richSummary() const
Returns the incidence summary in rich text format.
void clearConferences()
Removes all conferences from the incidence.
QString richDescription() const
Returns the incidence description in rich text format.
void setReadOnly(bool readonly) override
Set readonly state of incidence.
void setCreated(const QDateTime &dt)
Sets the incidence creation date/time.
void clearRecurrence()
Removes all recurrence and exception rules and dates.
bool equals(const IncidenceBase &incidence) const override
Compares this with Incidence incidence for equality.
QDateTime recurrenceId() const override
Returns the incidence recurrenceId.
bool localOnly() const
Get the localOnly status.
Recurrence * recurrence() const
Returns the recurrence rule associated with this incidence.
QString instanceIdentifier() const
Returns a unique identifier for a specific instance of an incidence.
void setDescription(const QString &description, bool isRich)
Sets the incidence description.
void setRevision(int rev)
Sets the number of revisions this incidence has seen.
bool summaryIsRich() const
Returns true if incidence summary contains RichText; false otherwise.
void setSecrecy(Secrecy secrecy)
Sets the incidence Secrecy.
void addAlarm(const Alarm::Ptr &alarm)
Adds an alarm to the incidence.
void setCategories(const QStringList &categories)
Sets the incidence category list.
void addAttachment(const Attachment &attachment)
Adds an attachment to the incidence.
void setConferences(const Conference::List &conferences)
Replaces all conferences in the incidence with given conferences.
void setDtStart(const QDateTime &dt) override
Sets the incidence starting date/time.
bool thisAndFuture() const
Returns true if the exception also applies to all future occurrences.
void setRelatedTo(const QString &uid, RelType relType=RelTypeParent)
Relates another incidence to this one, by UID.
void deserialize(QDataStream &in) override
Sub-type specific deserialization.
QString color() const
Returns the color, if any is defined, for this incidence.
static QLatin1String journalMimeType()
Returns the Akonadi specific sub MIME type of a KCalendarCore::Journal.
Definition journal.cpp:128
This class represents a recurrence rule for a calendar incidence.
Definition recurrence.h:77
TimeList recurTimesOn(const QDate &date, const QTimeZone &timeZone) const
Returns a list of the times on the specified date at which the recurrence will occur.
static QLatin1String todoMimeType()
Returns the Akonadi specific sub MIME type of a KCalendarCore::Todo.
Definition todo.cpp:612
Q_SCRIPTABLE CaptureState status()
Q_SCRIPTABLE Q_NOREPLY void start()
This file is part of the API for handling calendar data and defines the Incidence class.
Namespace for all KCalendarCore types.
Definition alarm.h:37
QDate addDays(qint64 ndays) const const
bool isValid(int year, int month, int day)
QDateTime addSecs(qint64 s) const const
QDateTime currentDateTimeUtc()
QDate date() const const
qint64 daysTo(const QDateTime &other) const const
bool isValid() const const
QTime time() const const
QTimeZone timeZone() const const
QString toString(QStringView format, QCalendar cal) const const
QDateTime toUTC() const const
void append(QList< T > &&value)
const_iterator cbegin() const const
const_iterator cend() const const
qsizetype count() const const
const_iterator cbegin() const const
const_iterator cend() const const
void clear()
bool isEmpty() const const
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
bool mightBeRichText(const QString &text)
Q_D(Todo)
Private class that helps to provide binary compatibility between releases.
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:08:40 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.