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

KDE's Doxygen guidelines are available online.