1 /*
2 This file is part of the kcalcore library.
3
4 SPDX-FileCopyrightText: 1998 Preston Brown <pbrown@kde.org>
5 SPDX-FileCopyrightText: 2000-2004 Cornelius Schumacher <schumacher@kde.org>
6 SPDX-FileCopyrightText: 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
7 SPDX-FileCopyrightText: 2006 David Jarvie <djarvie@kde.org>
8 SPDX-FileCopyrightText: 2021 Boris Shmarin <b.shmarin@omp.ru>
9
10 SPDX-License-Identifier: LGPL-2.0-or-later
11 */
12 /**
13 @file
14 This file is part of the API for handling calendar data and
15 defines the Calendar class.
16
17 @brief
18 Represents the main calendar class.
19
20 @author Preston Brown \<pbrown@kde.org\>
21 @author Cornelius Schumacher \<schumacher@kde.org\>
22 @author Reinhold Kainhofer \<reinhold@kainhofer.com\>
23 @author David Jarvie \<djarvie@kde.org\>
24 */
25 #include "calendar.h"
26 #include "calendar_p.h"
27 #include "calfilter.h"
28 #include "icaltimezones_p.h"
29 #include "sorting.h"
30 #include "visitor.h"
31
32 #include "kcalendarcore_debug.h"
33
34
35 extern "C" {
36 #include <libical/icaltimezone.h>
37 }
38
39 #include <algorithm>
40 #include <set>
41
42 using namespace KCalendarCore;
43
44 /**
45 Make a QHash::value that returns a QVector.
46 */
47 template<typename K, typename V>
values(const QMultiHash<K,V> & c)48 QVector<V> values(const QMultiHash<K, V> &c)
49 {
50 QVector<V> v;
51 v.reserve(c.size());
52 for (typename QMultiHash<K, V>::const_iterator it = c.begin(), end = c.end(); it != end; ++it) {
53 v.push_back(it.value());
54 }
55 return v;
56 }
57
58 template<typename K, typename V>
values(const QMultiHash<K,V> & c,const K & x)59 QVector<V> values(const QMultiHash<K, V> &c, const K &x)
60 {
61 QVector<V> v;
62 typename QMultiHash<K, V>::const_iterator it = c.find(x);
63 while (it != c.end() && it.key() == x) {
64 v.push_back(it.value());
65 ++it;
66 }
67 return v;
68 }
69
70 /**
71 Template for a class that implements a visitor for adding an Incidence
72 to a resource supporting addEvent(), addTodo() and addJournal() calls.
73 */
74 template<class T>
75 class AddVisitor : public Visitor
76 {
77 public:
AddVisitor(T * r)78 AddVisitor(T *r)
79 : mResource(r)
80 {
81 }
82
visit(const Event::Ptr & e)83 bool visit(const Event::Ptr &e) override
84 {
85 return mResource->addEvent(e);
86 }
visit(const Todo::Ptr & t)87 bool visit(const Todo::Ptr &t) override
88 {
89 return mResource->addTodo(t);
90 }
visit(const Journal::Ptr & j)91 bool visit(const Journal::Ptr &j) override
92 {
93 return mResource->addJournal(j);
94 }
visit(const FreeBusy::Ptr &)95 bool visit(const FreeBusy::Ptr &) override
96 {
97 return false;
98 }
99
100 private:
101 T *mResource;
102 };
103
104 /**
105 Template for a class that implements a visitor for deleting an Incidence
106 from a resource supporting deleteEvent(), deleteTodo() and deleteJournal()
107 calls.
108 */
109 template<class T>
110 class DeleteVisitor : public Visitor
111 {
112 public:
DeleteVisitor(T * r)113 DeleteVisitor(T *r)
114 : mResource(r)
115 {
116 }
117
visit(const Event::Ptr & e)118 bool visit(const Event::Ptr &e) override
119 {
120 mResource->deleteEvent(e);
121 return true;
122 }
visit(const Todo::Ptr & t)123 bool visit(const Todo::Ptr &t) override
124 {
125 mResource->deleteTodo(t);
126 return true;
127 }
visit(const Journal::Ptr & j)128 bool visit(const Journal::Ptr &j) override
129 {
130 mResource->deleteJournal(j);
131 return true;
132 }
visit(const FreeBusy::Ptr &)133 bool visit(const FreeBusy::Ptr &) override
134 {
135 return false;
136 }
137
138 private:
139 T *mResource;
140 };
141 //@endcond
142
Calendar(const QTimeZone & timeZone)143 Calendar::Calendar(const QTimeZone &timeZone)
144 : d(new KCalendarCore::Calendar::Private)
145 {
146 if (timeZone.isValid()) {
147 d->mTimeZone = timeZone;
148 } else {
149 d->mTimeZone = QTimeZone::systemTimeZone();
150 }
151 }
152
Calendar(const QByteArray & timeZoneId)153 Calendar::Calendar(const QByteArray &timeZoneId)
154 : d(new KCalendarCore::Calendar::Private)
155 {
156 setTimeZoneId(timeZoneId);
157 }
158
~Calendar()159 Calendar::~Calendar()
160 {
161 delete d;
162 }
163
owner() const164 Person Calendar::owner() const
165 {
166 return d->mOwner;
167 }
168
setOwner(const Person & owner)169 void Calendar::setOwner(const Person &owner)
170 {
171 if (owner != d->mOwner) {
172 d->mOwner = owner;
173 setModified(true);
174 Q_EMIT ownerChanged();
175 }
176 }
177
setTimeZone(const QTimeZone & timeZone)178 void Calendar::setTimeZone(const QTimeZone &timeZone)
179 {
180 if (timeZone.isValid()) {
181 d->mTimeZone = timeZone;
182 } else {
183 d->mTimeZone = QTimeZone::systemTimeZone();
184 }
185
186 doSetTimeZone(d->mTimeZone);
187 }
188
timeZone() const189 QTimeZone Calendar::timeZone() const
190 {
191 return d->mTimeZone;
192 }
193
setTimeZoneId(const QByteArray & timeZoneId)194 void Calendar::setTimeZoneId(const QByteArray &timeZoneId)
195 {
196 d->mTimeZone = d->timeZoneIdSpec(timeZoneId);
197
198 doSetTimeZone(d->mTimeZone); // NOLINT false clang-analyzer-optin.cplusplus.VirtualCall
199 }
200
201 //@cond PRIVATE
timeZoneIdSpec(const QByteArray & timeZoneId)202 QTimeZone Calendar::Private::timeZoneIdSpec(const QByteArray &timeZoneId)
203 {
204 if (timeZoneId == QByteArrayLiteral("UTC")) {
205 return QTimeZone::utc();
206 }
207 auto tz = QTimeZone(timeZoneId);
208 if (tz.isValid()) {
209 return tz;
210 }
211 return QTimeZone::systemTimeZone();
212 }
213 //@endcond
214
timeZoneId() const215 QByteArray Calendar::timeZoneId() const
216 {
217 return d->mTimeZone.id();
218 }
219
shiftTimes(const QTimeZone & oldZone,const QTimeZone & newZone)220 void Calendar::shiftTimes(const QTimeZone &oldZone, const QTimeZone &newZone)
221 {
222 setTimeZone(newZone);
223
224 int i;
225 int end;
226 Event::List ev = events();
227 for (i = 0, end = ev.count(); i < end; ++i) {
228 ev[i]->shiftTimes(oldZone, newZone);
229 }
230
231 Todo::List to = todos();
232 for (i = 0, end = to.count(); i < end; ++i) {
233 to[i]->shiftTimes(oldZone, newZone);
234 }
235
236 Journal::List jo = journals();
237 for (i = 0, end = jo.count(); i < end; ++i) {
238 jo[i]->shiftTimes(oldZone, newZone);
239 }
240 }
241
setFilter(CalFilter * filter)242 void Calendar::setFilter(CalFilter *filter)
243 {
244 if (filter) {
245 d->mFilter = filter;
246 } else {
247 d->mFilter = d->mDefaultFilter;
248 }
249 Q_EMIT filterChanged();
250 }
251
filter() const252 CalFilter *Calendar::filter() const
253 {
254 return d->mFilter;
255 }
256
categories() const257 QStringList Calendar::categories() const
258 {
259 const Incidence::List rawInc = rawIncidences();
260 QStringList uniqueCategories;
261 QStringList thisCats;
262 // @TODO: For now just iterate over all incidences. In the future,
263 // the list of categories should be built when reading the file.
264 for (const Incidence::Ptr &inc : rawInc) {
265 thisCats = inc->categories();
266 for (const auto &cat : std::as_const(thisCats)) {
267 if (!uniqueCategories.contains(cat)) {
268 uniqueCategories.append(cat);
269 }
270 }
271 }
272 return uniqueCategories;
273 }
274
incidences(const QDate & date) const275 Incidence::List Calendar::incidences(const QDate &date) const
276 {
277 return mergeIncidenceList(events(date), todos(date), journals(date));
278 }
279
incidences() const280 Incidence::List Calendar::incidences() const
281 {
282 return mergeIncidenceList(events(), todos(), journals());
283 }
284
rawIncidences() const285 Incidence::List Calendar::rawIncidences() const
286 {
287 return mergeIncidenceList(rawEvents(), rawTodos(), rawJournals());
288 }
289
instances(const Incidence::Ptr & incidence) const290 Incidence::List Calendar::instances(const Incidence::Ptr &incidence) const
291 {
292 if (incidence) {
293 Event::List elist;
294 Todo::List tlist;
295 Journal::List jlist;
296
297 if (incidence->type() == Incidence::TypeEvent) {
298 elist = eventInstances(incidence);
299 } else if (incidence->type() == Incidence::TypeTodo) {
300 tlist = todoInstances(incidence);
301 } else if (incidence->type() == Incidence::TypeJournal) {
302 jlist = journalInstances(incidence);
303 }
304 return mergeIncidenceList(elist, tlist, jlist);
305 } else {
306 return Incidence::List();
307 }
308 }
309
duplicates(const Incidence::Ptr & incidence)310 Incidence::List Calendar::duplicates(const Incidence::Ptr &incidence)
311 {
312 if (!incidence) {
313 return {};
314 }
315
316 Incidence::List list;
317 const Incidence::List vals = values(d->mNotebookIncidences);
318 std::copy_if(vals.cbegin(), vals.cend(), std::back_inserter(list), [&](const Incidence::Ptr &in) {
319 return (incidence->dtStart() == in->dtStart() || (!incidence->dtStart().isValid() && !in->dtStart().isValid()))
320 && incidence->summary() == in->summary();
321 });
322 return list;
323 }
324
addNotebook(const QString & notebook,bool isVisible)325 bool Calendar::addNotebook(const QString ¬ebook, bool isVisible)
326 {
327 if (d->mNotebooks.contains(notebook)) {
328 return false;
329 } else {
330 d->mNotebooks.insert(notebook, isVisible);
331 return true;
332 }
333 }
334
updateNotebook(const QString & notebook,bool isVisible)335 bool Calendar::updateNotebook(const QString ¬ebook, bool isVisible)
336 {
337 if (!d->mNotebooks.contains(notebook)) {
338 return false;
339 } else {
340 d->mNotebooks.insert(notebook, isVisible);
341 for (auto noteIt = d->mNotebookIncidences.cbegin(); noteIt != d->mNotebookIncidences.cend(); ++noteIt) {
342 const Incidence::Ptr &incidence = noteIt.value();
343 auto visibleIt = d->mIncidenceVisibility.find(incidence);
344 if (visibleIt != d->mIncidenceVisibility.end()) {
345 *visibleIt = isVisible;
346 }
347 }
348 return true;
349 }
350 }
351
deleteNotebook(const QString & notebook)352 bool Calendar::deleteNotebook(const QString ¬ebook)
353 {
354 if (!d->mNotebooks.contains(notebook)) {
355 return false;
356 } else {
357 return d->mNotebooks.remove(notebook);
358 }
359 }
360
setDefaultNotebook(const QString & notebook)361 bool Calendar::setDefaultNotebook(const QString ¬ebook)
362 {
363 if (!d->mNotebooks.contains(notebook)) {
364 return false;
365 } else {
366 d->mDefaultNotebook = notebook;
367 return true;
368 }
369 }
370
defaultNotebook() const371 QString Calendar::defaultNotebook() const
372 {
373 return d->mDefaultNotebook;
374 }
375
hasValidNotebook(const QString & notebook) const376 bool Calendar::hasValidNotebook(const QString ¬ebook) const
377 {
378 return d->mNotebooks.contains(notebook);
379 }
380
isVisible(const Incidence::Ptr & incidence) const381 bool Calendar::isVisible(const Incidence::Ptr &incidence) const
382 {
383 if (d->mIncidenceVisibility.contains(incidence)) {
384 return d->mIncidenceVisibility[incidence];
385 }
386 const QString nuid = notebook(incidence);
387 bool rv;
388 if (d->mNotebooks.contains(nuid)) {
389 rv = d->mNotebooks.value(nuid);
390 } else {
391 // NOTE returns true also for nonexisting notebooks for compatibility
392 rv = true;
393 }
394 d->mIncidenceVisibility[incidence] = rv;
395 return rv;
396 }
397
isVisible(const QString & notebook) const398 bool Calendar::isVisible(const QString ¬ebook) const
399 {
400 QHash<QString, bool>::ConstIterator it = d->mNotebooks.constFind(notebook);
401 return (it != d->mNotebooks.constEnd()) ? *it : true;
402 }
403
clearNotebookAssociations()404 void Calendar::clearNotebookAssociations()
405 {
406 d->mNotebookIncidences.clear();
407 d->mUidToNotebook.clear();
408 d->mIncidenceVisibility.clear();
409 }
410
setNotebook(const Incidence::Ptr & inc,const QString & notebook)411 bool Calendar::setNotebook(const Incidence::Ptr &inc, const QString ¬ebook)
412 {
413 if (!inc) {
414 return false;
415 }
416
417 if (!notebook.isEmpty() && !incidence(inc->uid(), inc->recurrenceId())) {
418 qCWarning(KCALCORE_LOG) << "cannot set notebook until incidence has been added";
419 return false;
420 }
421
422 if (d->mUidToNotebook.contains(inc->uid())) {
423 QString old = d->mUidToNotebook.value(inc->uid());
424 if (!old.isEmpty() && notebook != old) {
425 if (inc->hasRecurrenceId()) {
426 qCWarning(KCALCORE_LOG) << "cannot set notebook for child incidences";
427 return false;
428 }
429 // Move all possible children also.
430 const Incidence::List list = instances(inc);
431 for (const auto &incidence : list) {
432 d->mNotebookIncidences.remove(old, incidence);
433 d->mNotebookIncidences.insert(notebook, incidence);
434 }
435 notifyIncidenceChanged(inc); // for removing from old notebook
436 // don not remove from mUidToNotebook to keep deleted incidences
437 d->mNotebookIncidences.remove(old, inc);
438 }
439 }
440 if (!notebook.isEmpty()) {
441 d->mUidToNotebook.insert(inc->uid(), notebook);
442 d->mNotebookIncidences.insert(notebook, inc);
443 qCDebug(KCALCORE_LOG) << "setting notebook" << notebook << "for" << inc->uid();
444 notifyIncidenceChanged(inc); // for inserting into new notebook
445 }
446
447 return true;
448 }
449
notebook(const Incidence::Ptr & incidence) const450 QString Calendar::notebook(const Incidence::Ptr &incidence) const
451 {
452 if (incidence) {
453 return d->mUidToNotebook.value(incidence->uid());
454 } else {
455 return QString();
456 }
457 }
458
notebook(const QString & uid) const459 QString Calendar::notebook(const QString &uid) const
460 {
461 return d->mUidToNotebook.value(uid);
462 }
463
notebooks() const464 QStringList Calendar::notebooks() const
465 {
466 return d->mNotebookIncidences.uniqueKeys();
467 }
468
incidences(const QString & notebook) const469 Incidence::List Calendar::incidences(const QString ¬ebook) const
470 {
471 if (notebook.isEmpty()) {
472 return values(d->mNotebookIncidences);
473 } else {
474 return values(d->mNotebookIncidences, notebook);
475 }
476 }
477
478 /** static */
sortEvents(const Event::List & eventList,EventSortField sortField,SortDirection sortDirection)479 Event::List Calendar::sortEvents(const Event::List &eventList, EventSortField sortField, SortDirection sortDirection)
480 {
481 if (eventList.isEmpty()) {
482 return Event::List();
483 }
484
485 Event::List eventListSorted;
486
487 // Notice we alphabetically presort Summaries first.
488 // We do this so comparison "ties" stay in a nice order.
489 eventListSorted = eventList;
490 switch (sortField) {
491 case EventSortUnsorted:
492 break;
493
494 case EventSortStartDate:
495 if (sortDirection == SortDirectionAscending) {
496 std::sort(eventListSorted.begin(), eventListSorted.end(), Events::startDateLessThan);
497 } else {
498 std::sort(eventListSorted.begin(), eventListSorted.end(), Events::startDateMoreThan);
499 }
500 break;
501
502 case EventSortEndDate:
503 if (sortDirection == SortDirectionAscending) {
504 std::sort(eventListSorted.begin(), eventListSorted.end(), Events::endDateLessThan);
505 } else {
506 std::sort(eventListSorted.begin(), eventListSorted.end(), Events::endDateMoreThan);
507 }
508 break;
509
510 case EventSortSummary:
511 if (sortDirection == SortDirectionAscending) {
512 std::sort(eventListSorted.begin(), eventListSorted.end(), Events::summaryLessThan);
513 } else {
514 std::sort(eventListSorted.begin(), eventListSorted.end(), Events::summaryMoreThan);
515 }
516 break;
517 }
518
519 return eventListSorted;
520 }
521
events(const QDate & date,const QTimeZone & timeZone,EventSortField sortField,SortDirection sortDirection) const522 Event::List Calendar::events(const QDate &date, const QTimeZone &timeZone, EventSortField sortField, SortDirection sortDirection) const
523 {
524 Event::List el = rawEventsForDate(date, timeZone, sortField, sortDirection);
525 d->mFilter->apply(&el);
526 return el;
527 }
528
events(const QDateTime & dt) const529 Event::List Calendar::events(const QDateTime &dt) const
530 {
531 Event::List el = rawEventsForDate(dt);
532 d->mFilter->apply(&el);
533 return el;
534 }
535
events(const QDate & start,const QDate & end,const QTimeZone & timeZone,bool inclusive) const536 Event::List Calendar::events(const QDate &start, const QDate &end, const QTimeZone &timeZone, bool inclusive) const
537 {
538 Event::List el = rawEvents(start, end, timeZone, inclusive);
539 d->mFilter->apply(&el);
540 return el;
541 }
542
events(EventSortField sortField,SortDirection sortDirection) const543 Event::List Calendar::events(EventSortField sortField, SortDirection sortDirection) const
544 {
545 Event::List el = rawEvents(sortField, sortDirection);
546 d->mFilter->apply(&el);
547 return el;
548 }
549
addIncidence(const Incidence::Ptr & incidence)550 bool Calendar::addIncidence(const Incidence::Ptr &incidence)
551 {
552 if (!incidence) {
553 return false;
554 }
555
556 AddVisitor<Calendar> v(this);
557 return incidence->accept(v, incidence);
558 }
559
deleteIncidence(const Incidence::Ptr & incidence)560 bool Calendar::deleteIncidence(const Incidence::Ptr &incidence)
561 {
562 if (!incidence) {
563 return false;
564 }
565
566 if (beginChange(incidence)) {
567 DeleteVisitor<Calendar> v(this);
568 const bool result = incidence->accept(v, incidence);
569 endChange(incidence);
570 return result;
571 } else {
572 return false;
573 }
574 }
575
createException(const Incidence::Ptr & incidence,const QDateTime & recurrenceId,bool thisAndFuture)576 Incidence::Ptr Calendar::createException(const Incidence::Ptr &incidence, const QDateTime &recurrenceId, bool thisAndFuture)
577 {
578 Q_ASSERT(recurrenceId.isValid());
579 if (!incidence || !incidence->recurs() || !recurrenceId.isValid()) {
580 return Incidence::Ptr();
581 }
582
583 Incidence::Ptr newInc(incidence->clone());
584 const QDateTime current = QDateTime::currentDateTimeUtc();
585 newInc->setCreated(current);
586 newInc->setLastModified(current);
587 newInc->setRevision(0);
588 // Recurring exceptions are not support for now
589 newInc->clearRecurrence();
590
591 newInc->setRecurrenceId(recurrenceId);
592 newInc->setThisAndFuture(thisAndFuture);
593 newInc->setDtStart(recurrenceId);
594
595 // Calculate and set the new end of the incidence
596 QDateTime end = incidence->dateTime(IncidenceBase::RoleEnd);
597
598 if (end.isValid()) {
599 if (incidence->allDay()) {
600 qint64 offset = incidence->dtStart().daysTo(recurrenceId);
601 end = end.addDays(offset);
602 } else {
603 qint64 offset = incidence->dtStart().secsTo(recurrenceId);
604 end = end.addSecs(offset);
605 }
606 newInc->setDateTime(end, IncidenceBase::RoleEnd);
607 }
608 return newInc;
609 }
610
incidence(const QString & uid,const QDateTime & recurrenceId) const611 Incidence::Ptr Calendar::incidence(const QString &uid, const QDateTime &recurrenceId) const
612 {
613 Incidence::Ptr i = event(uid, recurrenceId);
614 if (i) {
615 return i;
616 }
617
618 i = todo(uid, recurrenceId);
619 if (i) {
620 return i;
621 }
622
623 i = journal(uid, recurrenceId);
624 return i;
625 }
626
deleted(const QString & uid,const QDateTime & recurrenceId) const627 Incidence::Ptr Calendar::deleted(const QString &uid, const QDateTime &recurrenceId) const
628 {
629 Incidence::Ptr i = deletedEvent(uid, recurrenceId);
630 if (i) {
631 return i;
632 }
633
634 i = deletedTodo(uid, recurrenceId);
635 if (i) {
636 return i;
637 }
638
639 i = deletedJournal(uid, recurrenceId);
640 return i;
641 }
642
incidencesFromSchedulingID(const QString & sid) const643 Incidence::List Calendar::incidencesFromSchedulingID(const QString &sid) const
644 {
645 Incidence::List result;
646 const Incidence::List incidences = rawIncidences();
647 std::copy_if(incidences.cbegin(), incidences.cend(), std::back_inserter(result), [&sid](const Incidence::Ptr &in) {
648 return in->schedulingID() == sid;
649 });
650 return result;
651 }
652
incidenceFromSchedulingID(const QString & uid) const653 Incidence::Ptr Calendar::incidenceFromSchedulingID(const QString &uid) const
654 {
655 const Incidence::List incidences = rawIncidences();
656 const auto itEnd = incidences.cend();
657 auto it = std::find_if(incidences.cbegin(), itEnd, [&uid](const Incidence::Ptr &in) {
658 return in->schedulingID() == uid;
659 });
660
661 return it != itEnd ? *it : Incidence::Ptr();
662 }
663
664 /** static */
sortTodos(const Todo::List & todoList,TodoSortField sortField,SortDirection sortDirection)665 Todo::List Calendar::sortTodos(const Todo::List &todoList, TodoSortField sortField, SortDirection sortDirection)
666 {
667 if (todoList.isEmpty()) {
668 return Todo::List();
669 }
670
671 Todo::List todoListSorted;
672
673 // Notice we alphabetically presort Summaries first.
674 // We do this so comparison "ties" stay in a nice order.
675
676 // Note that To-dos may not have Start DateTimes nor due DateTimes.
677
678 todoListSorted = todoList;
679 switch (sortField) {
680 case TodoSortUnsorted:
681 break;
682
683 case TodoSortStartDate:
684 if (sortDirection == SortDirectionAscending) {
685 std::sort(todoListSorted.begin(), todoListSorted.end(), Todos::startDateLessThan);
686 } else {
687 std::sort(todoListSorted.begin(), todoListSorted.end(), Todos::startDateMoreThan);
688 }
689 break;
690
691 case TodoSortDueDate:
692 if (sortDirection == SortDirectionAscending) {
693 std::sort(todoListSorted.begin(), todoListSorted.end(), Todos::dueDateLessThan);
694 } else {
695 std::sort(todoListSorted.begin(), todoListSorted.end(), Todos::dueDateMoreThan);
696 }
697 break;
698
699 case TodoSortPriority:
700 if (sortDirection == SortDirectionAscending) {
701 std::sort(todoListSorted.begin(), todoListSorted.end(), Todos::priorityLessThan);
702 } else {
703 std::sort(todoListSorted.begin(), todoListSorted.end(), Todos::priorityMoreThan);
704 }
705 break;
706
707 case TodoSortPercentComplete:
708 if (sortDirection == SortDirectionAscending) {
709 std::sort(todoListSorted.begin(), todoListSorted.end(), Todos::percentLessThan);
710 } else {
711 std::sort(todoListSorted.begin(), todoListSorted.end(), Todos::percentMoreThan);
712 }
713 break;
714
715 case TodoSortSummary:
716 if (sortDirection == SortDirectionAscending) {
717 std::sort(todoListSorted.begin(), todoListSorted.end(), Todos::summaryLessThan);
718 } else {
719 std::sort(todoListSorted.begin(), todoListSorted.end(), Todos::summaryMoreThan);
720 }
721 break;
722
723 case TodoSortCreated:
724 if (sortDirection == SortDirectionAscending) {
725 std::sort(todoListSorted.begin(), todoListSorted.end(), Todos::createdLessThan);
726 } else {
727 std::sort(todoListSorted.begin(), todoListSorted.end(), Todos::createdMoreThan);
728 }
729 break;
730
731 case TodoSortCategories:
732 if (sortDirection == SortDirectionAscending) {
733 std::sort(todoListSorted.begin(), todoListSorted.end(), Incidences::categoriesLessThan);
734 } else {
735 std::sort(todoListSorted.begin(), todoListSorted.end(), Incidences::categoriesMoreThan);
736 }
737 break;
738 }
739
740 return todoListSorted;
741 }
742
todos(TodoSortField sortField,SortDirection sortDirection) const743 Todo::List Calendar::todos(TodoSortField sortField, SortDirection sortDirection) const
744 {
745 Todo::List tl = rawTodos(sortField, sortDirection);
746 d->mFilter->apply(&tl);
747 return tl;
748 }
749
todos(const QDate & date) const750 Todo::List Calendar::todos(const QDate &date) const
751 {
752 Todo::List el = rawTodosForDate(date);
753 d->mFilter->apply(&el);
754 return el;
755 }
756
todos(const QDate & start,const QDate & end,const QTimeZone & timeZone,bool inclusive) const757 Todo::List Calendar::todos(const QDate &start, const QDate &end, const QTimeZone &timeZone, bool inclusive) const
758 {
759 Todo::List tl = rawTodos(start, end, timeZone, inclusive);
760 d->mFilter->apply(&tl);
761 return tl;
762 }
763
764 /** static */
sortJournals(const Journal::List & journalList,JournalSortField sortField,SortDirection sortDirection)765 Journal::List Calendar::sortJournals(const Journal::List &journalList, JournalSortField sortField, SortDirection sortDirection)
766 {
767 if (journalList.isEmpty()) {
768 return Journal::List();
769 }
770
771 Journal::List journalListSorted = journalList;
772
773 switch (sortField) {
774 case JournalSortUnsorted:
775 break;
776
777 case JournalSortDate:
778 if (sortDirection == SortDirectionAscending) {
779 std::sort(journalListSorted.begin(), journalListSorted.end(), Journals::dateLessThan);
780 } else {
781 std::sort(journalListSorted.begin(), journalListSorted.end(), Journals::dateMoreThan);
782 }
783 break;
784
785 case JournalSortSummary:
786 if (sortDirection == SortDirectionAscending) {
787 std::sort(journalListSorted.begin(), journalListSorted.end(), Journals::summaryLessThan);
788 } else {
789 std::sort(journalListSorted.begin(), journalListSorted.end(), Journals::summaryMoreThan);
790 }
791 break;
792 }
793
794 return journalListSorted;
795 }
796
journals(JournalSortField sortField,SortDirection sortDirection) const797 Journal::List Calendar::journals(JournalSortField sortField, SortDirection sortDirection) const
798 {
799 Journal::List jl = rawJournals(sortField, sortDirection);
800 d->mFilter->apply(&jl);
801 return jl;
802 }
803
journals(const QDate & date) const804 Journal::List Calendar::journals(const QDate &date) const
805 {
806 Journal::List el = rawJournalsForDate(date);
807 d->mFilter->apply(&el);
808 return el;
809 }
810
811 // When this is called, the to-dos have already been added to the calendar.
812 // This method is only about linking related to-dos.
setupRelations(const Incidence::Ptr & forincidence)813 void Calendar::setupRelations(const Incidence::Ptr &forincidence)
814 {
815 if (!forincidence) {
816 return;
817 }
818
819 const QString uid = forincidence->uid();
820
821 // First, go over the list of orphans and see if this is their parent
822 Incidence::List l = values(d->mOrphans, uid);
823 d->mOrphans.remove(uid);
824 if (!l.isEmpty()) {
825 Incidence::List &relations = d->mIncidenceRelations[uid];
826 relations.reserve(relations.count() + l.count());
827 for (int i = 0, end = l.count(); i < end; ++i) {
828 relations.append(l[i]);
829 d->mOrphanUids.remove(l[i]->uid());
830 }
831 }
832
833 // Now see about this incidences parent
834 if (!forincidence->relatedTo().isEmpty()) {
835 // Incidence has a uid it is related to but is not registered to it yet.
836 // Try to find it
837 Incidence::Ptr parent = incidence(forincidence->relatedTo());
838 if (parent) {
839 // Found it
840
841 // look for hierarchy loops
842 if (isAncestorOf(forincidence, parent)) {
843 forincidence->setRelatedTo(QString());
844 qCWarning(KCALCORE_LOG) << "hierarchy loop between " << forincidence->uid() << " and " << parent->uid();
845 } else {
846 d->mIncidenceRelations[parent->uid()].append(forincidence);
847 }
848 } else {
849 // Not found, put this in the mOrphans list
850 // Note that the mOrphans dict might contain multiple entries with the
851 // same key! which are multiple children that wait for the parent
852 // incidence to be inserted.
853 d->mOrphans.insert(forincidence->relatedTo(), forincidence);
854 d->mOrphanUids.insert(forincidence->uid(), forincidence);
855 }
856 }
857 }
858
859 // If a to-do with sub-to-dos is deleted, move it's sub-to-dos to the orphan list
removeRelations(const Incidence::Ptr & incidence)860 void Calendar::removeRelations(const Incidence::Ptr &incidence)
861 {
862 if (!incidence) {
863 qCDebug(KCALCORE_LOG) << "Warning: incidence is 0";
864 return;
865 }
866
867 const QString uid = incidence->uid();
868
869 for (const Incidence::Ptr &i : qAsConst(d->mIncidenceRelations[uid])) {
870 if (!d->mOrphanUids.contains(i->uid())) {
871 d->mOrphans.insert(uid, i);
872 d->mOrphanUids.insert(i->uid(), i);
873 i->setRelatedTo(uid);
874 }
875 }
876
877 const QString parentUid = incidence->relatedTo();
878
879 // If this incidence is related to something else, tell that about it
880 if (!parentUid.isEmpty()) {
881 Incidence::List &relations = d->mIncidenceRelations[parentUid];
882 relations.erase(std::remove(relations.begin(), relations.end(), incidence), relations.end());
883 }
884
885 // Remove this one from the orphans list
886 if (d->mOrphanUids.remove(uid)) {
887 // This incidence is located in the orphans list - it should be removed
888 // Since the mOrphans dict might contain the same key (with different
889 // child incidence pointers!) multiple times, take care that we remove
890 // the correct one. So we need to remove all items with the given
891 // parent UID, and re-add those that are not for this item. Also, there
892 // might be other entries with different UID that point to this
893 // incidence (this might happen when the relatedTo of the item is
894 // changed before its parent is inserted. This might happen with
895 // groupware servers....). Remove them, too
896 QStringList relatedToUids;
897
898 // First, create a list of all keys in the mOrphans list which point
899 // to the removed item
900 relatedToUids << incidence->relatedTo();
901 for (auto it = d->mOrphans.cbegin(); it != d->mOrphans.cend(); ++it) {
902 if (it.value()->uid() == uid) {
903 relatedToUids << it.key();
904 }
905 }
906
907 // now go through all uids that have one entry that point to the incidence
908 for (const auto &relUid : std::as_const(relatedToUids)) {
909 // Remove all to get access to the remaining entries
910 Incidence::List lst = values(d->mOrphans, relUid);
911 d->mOrphans.remove(relUid);
912 lst.erase(std::remove(lst.begin(), lst.end(), incidence), lst.end());
913
914 // Re-add those that point to a different orphan incidence
915 for (const auto &in : std::as_const(lst)) {
916 d->mOrphans.insert(relUid, in);
917 }
918 }
919 }
920
921 // Make sure the deleted incidence doesn't relate to a non-deleted incidence,
922 // since that would cause trouble in MemoryCalendar::close(), as the deleted
923 // incidences are destroyed after the non-deleted incidences. The destructor
924 // of the deleted incidences would then try to access the already destroyed
925 // non-deleted incidence, which would segfault.
926 //
927 // So in short: Make sure dead incidences don't point to alive incidences
928 // via the relation.
929 //
930 // This crash is tested in MemoryCalendarTest::testRelationsCrash().
931 // incidence->setRelatedTo( Incidence::Ptr() );
932 }
933
isAncestorOf(const Incidence::Ptr & ancestor,const Incidence::Ptr & incidence) const934 bool Calendar::isAncestorOf(const Incidence::Ptr &ancestor, const Incidence::Ptr &incidence) const
935 {
936 if (!incidence || incidence->relatedTo().isEmpty()) {
937 return false;
938 } else if (incidence->relatedTo() == ancestor->uid()) {
939 return true;
940 } else {
941 return isAncestorOf(ancestor, this->incidence(incidence->relatedTo()));
942 }
943 }
944
relations(const QString & uid) const945 Incidence::List Calendar::relations(const QString &uid) const
946 {
947 return d->mIncidenceRelations[uid];
948 }
949
~CalendarObserver()950 Calendar::CalendarObserver::~CalendarObserver()
951 {
952 }
953
calendarModified(bool modified,Calendar * calendar)954 void Calendar::CalendarObserver::calendarModified(bool modified, Calendar *calendar)
955 {
956 Q_UNUSED(modified);
957 Q_UNUSED(calendar);
958 }
959
calendarIncidenceAdded(const Incidence::Ptr & incidence)960 void Calendar::CalendarObserver::calendarIncidenceAdded(const Incidence::Ptr &incidence)
961 {
962 Q_UNUSED(incidence);
963 }
964
calendarIncidenceChanged(const Incidence::Ptr & incidence)965 void Calendar::CalendarObserver::calendarIncidenceChanged(const Incidence::Ptr &incidence)
966 {
967 Q_UNUSED(incidence);
968 }
969
calendarIncidenceAboutToBeDeleted(const Incidence::Ptr & incidence)970 void Calendar::CalendarObserver::calendarIncidenceAboutToBeDeleted(const Incidence::Ptr &incidence)
971 {
972 Q_UNUSED(incidence);
973 }
974
calendarIncidenceDeleted(const Incidence::Ptr & incidence,const Calendar * calendar)975 void Calendar::CalendarObserver::calendarIncidenceDeleted(const Incidence::Ptr &incidence, const Calendar *calendar)
976 {
977 Q_UNUSED(incidence);
978 Q_UNUSED(calendar);
979 }
980
calendarIncidenceAdditionCanceled(const Incidence::Ptr & incidence)981 void Calendar::CalendarObserver::calendarIncidenceAdditionCanceled(const Incidence::Ptr &incidence)
982 {
983 Q_UNUSED(incidence);
984 }
985
registerObserver(CalendarObserver * observer)986 void Calendar::registerObserver(CalendarObserver *observer)
987 {
988 if (!observer) {
989 return;
990 }
991
992 if (!d->mObservers.contains(observer)) {
993 d->mObservers.append(observer);
994 } else {
995 d->mNewObserver = true;
996 }
997 }
998
unregisterObserver(CalendarObserver * observer)999 void Calendar::unregisterObserver(CalendarObserver *observer)
1000 {
1001 if (!observer) {
1002 return;
1003 } else {
1004 d->mObservers.removeAll(observer);
1005 }
1006 }
1007
isSaving() const1008 bool Calendar::isSaving() const
1009 {
1010 return false;
1011 }
1012
setModified(bool modified)1013 void Calendar::setModified(bool modified)
1014 {
1015 if (modified != d->mModified || d->mNewObserver) {
1016 d->mNewObserver = false;
1017 for (CalendarObserver *observer : qAsConst(d->mObservers)) {
1018 observer->calendarModified(modified, this);
1019 }
1020 d->mModified = modified;
1021 }
1022 }
1023
isModified() const1024 bool Calendar::isModified() const
1025 {
1026 return d->mModified;
1027 }
1028
save()1029 bool Calendar::save()
1030 {
1031 return true;
1032 }
1033
reload()1034 bool Calendar::reload()
1035 {
1036 return true;
1037 }
1038
incidenceUpdated(const QString & uid,const QDateTime & recurrenceId)1039 void Calendar::incidenceUpdated(const QString &uid, const QDateTime &recurrenceId)
1040 {
1041 Incidence::Ptr inc = incidence(uid, recurrenceId);
1042
1043 if (!inc) {
1044 return;
1045 }
1046
1047 inc->setLastModified(QDateTime::currentDateTimeUtc());
1048 // we should probably update the revision number here,
1049 // or internally in the Event itself when certain things change.
1050 // need to verify with ical documentation.
1051
1052 notifyIncidenceChanged(inc);
1053
1054 setModified(true);
1055 }
1056
doSetTimeZone(const QTimeZone & timeZone)1057 void Calendar::doSetTimeZone(const QTimeZone &timeZone)
1058 {
1059 Q_UNUSED(timeZone);
1060 }
1061
notifyIncidenceAdded(const Incidence::Ptr & incidence)1062 void Calendar::notifyIncidenceAdded(const Incidence::Ptr &incidence)
1063 {
1064 if (!incidence) {
1065 return;
1066 }
1067
1068 if (!d->mObserversEnabled) {
1069 return;
1070 }
1071
1072 for (CalendarObserver *observer : qAsConst(d->mObservers)) {
1073 observer->calendarIncidenceAdded(incidence);
1074 }
1075
1076 for (auto role : {IncidenceBase::RoleStartTimeZone, IncidenceBase::RoleEndTimeZone}) {
1077 const auto dt = incidence->dateTime(role);
1078 if (dt.isValid() && dt.timeZone() != QTimeZone::utc()) {
1079 if (!d->mTimeZones.contains(dt.timeZone())) {
1080 d->mTimeZones.push_back(dt.timeZone());
1081 }
1082 }
1083 }
1084 }
1085
notifyIncidenceChanged(const Incidence::Ptr & incidence)1086 void Calendar::notifyIncidenceChanged(const Incidence::Ptr &incidence)
1087 {
1088 if (!incidence) {
1089 return;
1090 }
1091
1092 if (!d->mObserversEnabled) {
1093 return;
1094 }
1095
1096 for (CalendarObserver *observer : qAsConst(d->mObservers)) {
1097 observer->calendarIncidenceChanged(incidence);
1098 }
1099 }
1100
notifyIncidenceAboutToBeDeleted(const Incidence::Ptr & incidence)1101 void Calendar::notifyIncidenceAboutToBeDeleted(const Incidence::Ptr &incidence)
1102 {
1103 if (!incidence) {
1104 return;
1105 }
1106
1107 if (!d->mObserversEnabled) {
1108 return;
1109 }
1110
1111 for (CalendarObserver *observer : qAsConst(d->mObservers)) {
1112 observer->calendarIncidenceAboutToBeDeleted(incidence);
1113 }
1114 }
1115
notifyIncidenceDeleted(const Incidence::Ptr & incidence)1116 void Calendar::notifyIncidenceDeleted(const Incidence::Ptr &incidence)
1117 {
1118 if (!incidence) {
1119 return;
1120 }
1121
1122 if (!d->mObserversEnabled) {
1123 return;
1124 }
1125
1126 for (CalendarObserver *observer : qAsConst(d->mObservers)) {
1127 observer->calendarIncidenceDeleted(incidence, this);
1128 }
1129 }
1130
notifyIncidenceAdditionCanceled(const Incidence::Ptr & incidence)1131 void Calendar::notifyIncidenceAdditionCanceled(const Incidence::Ptr &incidence)
1132 {
1133 if (!incidence) {
1134 return;
1135 }
1136
1137 if (!d->mObserversEnabled) {
1138 return;
1139 }
1140
1141 for (CalendarObserver *observer : qAsConst(d->mObservers)) {
1142 observer->calendarIncidenceAdditionCanceled(incidence);
1143 }
1144 }
1145
customPropertyUpdated()1146 void Calendar::customPropertyUpdated()
1147 {
1148 setModified(true);
1149 }
1150
setProductId(const QString & id)1151 void Calendar::setProductId(const QString &id)
1152 {
1153 d->mProductId = id;
1154 }
1155
productId() const1156 QString Calendar::productId() const
1157 {
1158 return d->mProductId;
1159 }
1160
1161 /** static */
mergeIncidenceList(const Event::List & events,const Todo::List & todos,const Journal::List & journals)1162 Incidence::List Calendar::mergeIncidenceList(const Event::List &events, const Todo::List &todos, const Journal::List &journals)
1163 {
1164 Incidence::List incidences;
1165 incidences.reserve(events.count() + todos.count() + journals.count());
1166
1167 int i;
1168 int end;
1169 for (i = 0, end = events.count(); i < end; ++i) {
1170 incidences.append(events[i]);
1171 }
1172
1173 for (i = 0, end = todos.count(); i < end; ++i) {
1174 incidences.append(todos[i]);
1175 }
1176
1177 for (i = 0, end = journals.count(); i < end; ++i) {
1178 incidences.append(journals[i]);
1179 }
1180
1181 return incidences;
1182 }
1183
beginChange(const Incidence::Ptr & incidence)1184 bool Calendar::beginChange(const Incidence::Ptr &incidence)
1185 {
1186 Q_UNUSED(incidence);
1187 return true;
1188 }
1189
endChange(const Incidence::Ptr & incidence)1190 bool Calendar::endChange(const Incidence::Ptr &incidence)
1191 {
1192 Q_UNUSED(incidence);
1193 return true;
1194 }
1195
setObserversEnabled(bool enabled)1196 void Calendar::setObserversEnabled(bool enabled)
1197 {
1198 d->mObserversEnabled = enabled;
1199 }
1200
appendAlarms(Alarm::List & alarms,const Incidence::Ptr & incidence,const QDateTime & from,const QDateTime & to) const1201 void Calendar::appendAlarms(Alarm::List &alarms, const Incidence::Ptr &incidence, const QDateTime &from, const QDateTime &to) const
1202 {
1203 QDateTime preTime = from.addSecs(-1);
1204
1205 Alarm::List alarmlist = incidence->alarms();
1206 for (int i = 0, iend = alarmlist.count(); i < iend; ++i) {
1207 if (alarmlist[i]->enabled()) {
1208 QDateTime dt = alarmlist[i]->nextRepetition(preTime);
1209 if (dt.isValid() && dt <= to) {
1210 qCDebug(KCALCORE_LOG) << incidence->summary() << "':" << dt.toString();
1211 alarms.append(alarmlist[i]);
1212 }
1213 }
1214 }
1215 }
1216
appendRecurringAlarms(Alarm::List & alarms,const Incidence::Ptr & incidence,const QDateTime & from,const QDateTime & to) const1217 void Calendar::appendRecurringAlarms(Alarm::List &alarms, const Incidence::Ptr &incidence, const QDateTime &from, const QDateTime &to) const
1218 {
1219 QDateTime dt;
1220 bool endOffsetValid = false;
1221 Duration endOffset(0);
1222 Duration period(from, to);
1223
1224 Alarm::List alarmlist = incidence->alarms();
1225 for (int i = 0, iend = alarmlist.count(); i < iend; ++i) {
1226 Alarm::Ptr a = alarmlist[i];
1227 if (a->enabled()) {
1228 if (a->hasTime()) {
1229 // The alarm time is defined as an absolute date/time
1230 dt = a->nextRepetition(from.addSecs(-1));
1231 if (!dt.isValid() || dt > to) {
1232 continue;
1233 }
1234 } else {
1235 // Alarm time is defined by an offset from the event start or end time.
1236 // Find the offset from the event start time, which is also used as the
1237 // offset from the recurrence time.
1238 Duration offset(0);
1239 if (a->hasStartOffset()) {
1240 offset = a->startOffset();
1241 } else if (a->hasEndOffset()) {
1242 offset = a->endOffset();
1243 if (!endOffsetValid) {
1244 endOffset = Duration(incidence->dtStart(), incidence->dateTime(Incidence::RoleAlarmEndOffset));
1245 endOffsetValid = true;
1246 }
1247 }
1248
1249 // Find the incidence's earliest alarm
1250 QDateTime alarmStart = offset.end(a->hasEndOffset() ? incidence->dateTime(Incidence::RoleAlarmEndOffset) : incidence->dtStart());
1251 if (alarmStart > to) {
1252 continue;
1253 }
1254 QDateTime baseStart = incidence->dtStart();
1255 if (from > alarmStart) {
1256 alarmStart = from; // don't look earlier than the earliest alarm
1257 baseStart = (-offset).end((-endOffset).end(alarmStart));
1258 }
1259
1260 // Adjust the 'alarmStart' date/time and find the next recurrence at or after it.
1261 // Treat the two offsets separately in case one is daily and the other not.
1262 dt = incidence->recurrence()->getNextDateTime(baseStart.addSecs(-1));
1263 if (!dt.isValid() || (dt = endOffset.end(offset.end(dt))) > to) { // adjust 'dt' to get the alarm time
1264 // The next recurrence is too late.
1265 if (!a->repeatCount()) {
1266 continue;
1267 }
1268
1269 // The alarm has repetitions, so check whether repetitions of previous
1270 // recurrences fall within the time period.
1271 bool found = false;
1272 Duration alarmDuration = a->duration();
1273 for (QDateTime base = baseStart; (dt = incidence->recurrence()->getPreviousDateTime(base)).isValid(); base = dt) {
1274 if (a->duration().end(dt) < base) {
1275 break; // this recurrence's last repetition is too early, so give up
1276 }
1277
1278 // The last repetition of this recurrence is at or after 'alarmStart' time.
1279 // Check if a repetition occurs between 'alarmStart' and 'to'.
1280 int snooze = a->snoozeTime().value(); // in seconds or days
1281 if (a->snoozeTime().isDaily()) {
1282 Duration toFromDuration(dt, base);
1283 int toFrom = toFromDuration.asDays();
1284 if (a->snoozeTime().end(from) <= to || (toFromDuration.isDaily() && toFrom % snooze == 0)
1285 || (toFrom / snooze + 1) * snooze <= toFrom + period.asDays()) {
1286 found = true;
1287 #ifndef NDEBUG
1288 // for debug output
1289 dt = offset.end(dt).addDays(((toFrom - 1) / snooze + 1) * snooze);
1290 #endif
1291 break;
1292 }
1293 } else {
1294 int toFrom = dt.secsTo(base);
1295 if (period.asSeconds() >= snooze || toFrom % snooze == 0 || (toFrom / snooze + 1) * snooze <= toFrom + period.asSeconds()) {
1296 found = true;
1297 #ifndef NDEBUG
1298 // for debug output
1299 dt = offset.end(dt).addSecs(((toFrom - 1) / snooze + 1) * snooze);
1300 #endif
1301 break;
1302 }
1303 }
1304 }
1305 if (!found) {
1306 continue;
1307 }
1308 }
1309 }
1310 qCDebug(KCALCORE_LOG) << incidence->summary() << "':" << dt.toString();
1311 alarms.append(a);
1312 }
1313 }
1314 }
1315
startBatchAdding()1316 void Calendar::startBatchAdding()
1317 {
1318 d->batchAddingInProgress = true;
1319 }
1320
endBatchAdding()1321 void Calendar::endBatchAdding()
1322 {
1323 d->batchAddingInProgress = false;
1324 }
1325
batchAdding() const1326 bool Calendar::batchAdding() const
1327 {
1328 return d->batchAddingInProgress;
1329 }
1330
setDeletionTracking(bool enable)1331 void Calendar::setDeletionTracking(bool enable)
1332 {
1333 d->mDeletionTracking = enable;
1334 }
1335
deletionTracking() const1336 bool Calendar::deletionTracking() const
1337 {
1338 return d->mDeletionTracking;
1339 }
1340
alarmsTo(const QDateTime & to) const1341 Alarm::List Calendar::alarmsTo(const QDateTime &to) const
1342 {
1343 return alarms(QDateTime(QDate(1900, 1, 1), QTime(0, 0, 0)), to);
1344 }
1345
virtual_hook(int id,void * data)1346 void Calendar::virtual_hook(int id, void *data)
1347 {
1348 Q_UNUSED(id);
1349 Q_UNUSED(data);
1350 Q_ASSERT(false);
1351 }
1352
id() const1353 QString Calendar::id() const
1354 {
1355 return d->mId;
1356 }
1357
setId(const QString & id)1358 void Calendar::setId(const QString &id)
1359 {
1360 if (d->mId != id) {
1361 d->mId = id;
1362 Q_EMIT idChanged();
1363 }
1364 }
1365
name() const1366 QString Calendar::name() const
1367 {
1368 return d->mName;
1369 }
1370
setName(const QString & name)1371 void Calendar::setName(const QString &name)
1372 {
1373 if (d->mName != name) {
1374 d->mName = name;
1375 Q_EMIT nameChanged();
1376 }
1377 }
1378
icon() const1379 QIcon Calendar::icon() const
1380 {
1381 return d->mIcon;
1382 }
1383
setIcon(const QIcon & icon)1384 void Calendar::setIcon(const QIcon &icon)
1385 {
1386 d->mIcon = icon;
1387 Q_EMIT iconChanged();
1388 }
1389
accessMode() const1390 AccessMode Calendar::accessMode() const
1391 {
1392 return d->mAccessMode;
1393 }
1394
setAccessMode(const AccessMode mode)1395 void Calendar::setAccessMode(const AccessMode mode)
1396 {
1397 if (d->mAccessMode != mode) {
1398 d->mAccessMode = mode;
1399 Q_EMIT accessModeChanged();
1400 }
1401 }
1402