File indexing completed on 2024-04-21 03:52:44
0001 /* 0002 This file is part of the kcalcore library. 0003 0004 SPDX-FileCopyrightText: 1998 Preston Brown <pbrown@kde.org> 0005 SPDX-FileCopyrightText: 2000-2004 Cornelius Schumacher <schumacher@kde.org> 0006 SPDX-FileCopyrightText: 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com> 0007 SPDX-FileCopyrightText: 2006 David Jarvie <djarvie@kde.org> 0008 SPDX-FileCopyrightText: 2021 Boris Shmarin <b.shmarin@omp.ru> 0009 0010 SPDX-License-Identifier: LGPL-2.0-or-later 0011 */ 0012 /** 0013 @file 0014 This file is part of the API for handling calendar data and 0015 defines the Calendar class. 0016 0017 @brief 0018 Represents the main calendar class. 0019 0020 @author Preston Brown \<pbrown@kde.org\> 0021 @author Cornelius Schumacher \<schumacher@kde.org\> 0022 @author Reinhold Kainhofer \<reinhold@kainhofer.com\> 0023 @author David Jarvie \<djarvie@kde.org\> 0024 */ 0025 #include "calendar.h" 0026 #include "calendar_p.h" 0027 #include "calfilter.h" 0028 #include "icaltimezones_p.h" 0029 #include "sorting.h" 0030 #include "visitor.h" 0031 0032 #include "kcalendarcore_debug.h" 0033 0034 0035 extern "C" { 0036 #include <libical/icaltimezone.h> 0037 } 0038 0039 #include <algorithm> 0040 #include <set> 0041 0042 using namespace KCalendarCore; 0043 0044 /** 0045 Template for a class that implements a visitor for adding an Incidence 0046 to a resource supporting addEvent(), addTodo() and addJournal() calls. 0047 */ 0048 template<class T> 0049 class AddVisitor : public Visitor 0050 { 0051 public: 0052 AddVisitor(T *r) 0053 : mResource(r) 0054 { 0055 } 0056 0057 bool visit(const Event::Ptr &e) override 0058 { 0059 return mResource->addEvent(e); 0060 } 0061 bool visit(const Todo::Ptr &t) override 0062 { 0063 return mResource->addTodo(t); 0064 } 0065 bool visit(const Journal::Ptr &j) override 0066 { 0067 return mResource->addJournal(j); 0068 } 0069 bool visit(const FreeBusy::Ptr &) override 0070 { 0071 return false; 0072 } 0073 0074 private: 0075 T *mResource; 0076 }; 0077 0078 /** 0079 Template for a class that implements a visitor for deleting an Incidence 0080 from a resource supporting deleteEvent(), deleteTodo() and deleteJournal() 0081 calls. 0082 */ 0083 template<class T> 0084 class DeleteVisitor : public Visitor 0085 { 0086 public: 0087 DeleteVisitor(T *r) 0088 : mResource(r) 0089 { 0090 } 0091 0092 bool visit(const Event::Ptr &e) override 0093 { 0094 mResource->deleteEvent(e); 0095 return true; 0096 } 0097 bool visit(const Todo::Ptr &t) override 0098 { 0099 mResource->deleteTodo(t); 0100 return true; 0101 } 0102 bool visit(const Journal::Ptr &j) override 0103 { 0104 mResource->deleteJournal(j); 0105 return true; 0106 } 0107 bool visit(const FreeBusy::Ptr &) override 0108 { 0109 return false; 0110 } 0111 0112 private: 0113 T *mResource; 0114 }; 0115 //@endcond 0116 0117 Calendar::Calendar(const QTimeZone &timeZone) 0118 : d(new KCalendarCore::Calendar::Private) 0119 { 0120 if (timeZone.isValid()) { 0121 d->mTimeZone = timeZone; 0122 } else { 0123 d->mTimeZone = QTimeZone::systemTimeZone(); 0124 } 0125 } 0126 0127 Calendar::Calendar(const QByteArray &timeZoneId) 0128 : d(new KCalendarCore::Calendar::Private) 0129 { 0130 setTimeZoneId(timeZoneId); 0131 } 0132 0133 Calendar::~Calendar() 0134 { 0135 delete d; 0136 } 0137 0138 Person Calendar::owner() const 0139 { 0140 return d->mOwner; 0141 } 0142 0143 void Calendar::setOwner(const Person &owner) 0144 { 0145 if (owner != d->mOwner) { 0146 d->mOwner = owner; 0147 setModified(true); 0148 Q_EMIT ownerChanged(); 0149 } 0150 } 0151 0152 void Calendar::setTimeZone(const QTimeZone &timeZone) 0153 { 0154 if (timeZone.isValid()) { 0155 d->mTimeZone = timeZone; 0156 } else { 0157 d->mTimeZone = QTimeZone::systemTimeZone(); 0158 } 0159 0160 doSetTimeZone(d->mTimeZone); 0161 } 0162 0163 QTimeZone Calendar::timeZone() const 0164 { 0165 return d->mTimeZone; 0166 } 0167 0168 void Calendar::setTimeZoneId(const QByteArray &timeZoneId) 0169 { 0170 d->mTimeZone = d->timeZoneIdSpec(timeZoneId); 0171 0172 doSetTimeZone(d->mTimeZone); // NOLINT false clang-analyzer-optin.cplusplus.VirtualCall 0173 } 0174 0175 //@cond PRIVATE 0176 QTimeZone Calendar::Private::timeZoneIdSpec(const QByteArray &timeZoneId) 0177 { 0178 if (timeZoneId == QByteArrayLiteral("UTC")) { 0179 return QTimeZone::utc(); 0180 } 0181 auto tz = QTimeZone(timeZoneId); 0182 if (tz.isValid()) { 0183 return tz; 0184 } 0185 return QTimeZone::systemTimeZone(); 0186 } 0187 //@endcond 0188 0189 QByteArray Calendar::timeZoneId() const 0190 { 0191 return d->mTimeZone.id(); 0192 } 0193 0194 void Calendar::shiftTimes(const QTimeZone &oldZone, const QTimeZone &newZone) 0195 { 0196 setTimeZone(newZone); 0197 0198 int i; 0199 int end; 0200 Event::List ev = events(); 0201 for (i = 0, end = ev.count(); i < end; ++i) { 0202 ev[i]->shiftTimes(oldZone, newZone); 0203 } 0204 0205 Todo::List to = todos(); 0206 for (i = 0, end = to.count(); i < end; ++i) { 0207 to[i]->shiftTimes(oldZone, newZone); 0208 } 0209 0210 Journal::List jo = journals(); 0211 for (i = 0, end = jo.count(); i < end; ++i) { 0212 jo[i]->shiftTimes(oldZone, newZone); 0213 } 0214 } 0215 0216 void Calendar::setFilter(CalFilter *filter) 0217 { 0218 if (filter) { 0219 d->mFilter = filter; 0220 } else { 0221 d->mFilter = d->mDefaultFilter; 0222 } 0223 Q_EMIT filterChanged(); 0224 } 0225 0226 CalFilter *Calendar::filter() const 0227 { 0228 return d->mFilter; 0229 } 0230 0231 QStringList Calendar::categories() const 0232 { 0233 const Incidence::List rawInc = rawIncidences(); 0234 QStringList uniqueCategories; 0235 QStringList thisCats; 0236 // @TODO: For now just iterate over all incidences. In the future, 0237 // the list of categories should be built when reading the file. 0238 for (const Incidence::Ptr &inc : rawInc) { 0239 thisCats = inc->categories(); 0240 for (const auto &cat : std::as_const(thisCats)) { 0241 if (!uniqueCategories.contains(cat)) { 0242 uniqueCategories.append(cat); 0243 } 0244 } 0245 } 0246 return uniqueCategories; 0247 } 0248 0249 Incidence::List Calendar::incidences(const QDate &date) const 0250 { 0251 return mergeIncidenceList(events(date), todos(date), journals(date)); 0252 } 0253 0254 Incidence::List Calendar::incidences() const 0255 { 0256 return mergeIncidenceList(events(), todos(), journals()); 0257 } 0258 0259 Incidence::List Calendar::rawIncidences() const 0260 { 0261 return mergeIncidenceList(rawEvents(), rawTodos(), rawJournals()); 0262 } 0263 0264 Incidence::List Calendar::instances(const Incidence::Ptr &incidence) const 0265 { 0266 if (incidence) { 0267 Event::List elist; 0268 Todo::List tlist; 0269 Journal::List jlist; 0270 0271 if (incidence->type() == Incidence::TypeEvent) { 0272 elist = eventInstances(incidence); 0273 } else if (incidence->type() == Incidence::TypeTodo) { 0274 tlist = todoInstances(incidence); 0275 } else if (incidence->type() == Incidence::TypeJournal) { 0276 jlist = journalInstances(incidence); 0277 } 0278 return mergeIncidenceList(elist, tlist, jlist); 0279 } else { 0280 return Incidence::List(); 0281 } 0282 } 0283 0284 Event::List Calendar::sortEvents(Event::List &&eventList, EventSortField sortField, SortDirection sortDirection) 0285 { 0286 switch (sortField) { 0287 case EventSortUnsorted: 0288 break; 0289 0290 case EventSortStartDate: 0291 if (sortDirection == SortDirectionAscending) { 0292 std::sort(eventList.begin(), eventList.end(), Events::startDateLessThan); 0293 } else { 0294 std::sort(eventList.begin(), eventList.end(), Events::startDateMoreThan); 0295 } 0296 break; 0297 0298 case EventSortEndDate: 0299 if (sortDirection == SortDirectionAscending) { 0300 std::sort(eventList.begin(), eventList.end(), Events::endDateLessThan); 0301 } else { 0302 std::sort(eventList.begin(), eventList.end(), Events::endDateMoreThan); 0303 } 0304 break; 0305 0306 case EventSortSummary: 0307 if (sortDirection == SortDirectionAscending) { 0308 std::sort(eventList.begin(), eventList.end(), Events::summaryLessThan); 0309 } else { 0310 std::sort(eventList.begin(), eventList.end(), Events::summaryMoreThan); 0311 } 0312 break; 0313 } 0314 0315 return eventList; 0316 } 0317 0318 Event::List Calendar::events(const QDate &date, const QTimeZone &timeZone, EventSortField sortField, SortDirection sortDirection) const 0319 { 0320 Event::List el = rawEventsForDate(date, timeZone, sortField, sortDirection); 0321 d->mFilter->apply(&el); 0322 return el; 0323 } 0324 0325 Event::List Calendar::events(const QDateTime &dt) const 0326 { 0327 Event::List el = rawEventsForDate(dt.date(), dt.timeZone()); 0328 d->mFilter->apply(&el); 0329 return el; 0330 } 0331 0332 Event::List Calendar::events(const QDate &start, const QDate &end, const QTimeZone &timeZone, bool inclusive) const 0333 { 0334 Event::List el = rawEvents(start, end, timeZone, inclusive); 0335 d->mFilter->apply(&el); 0336 return el; 0337 } 0338 0339 Event::List Calendar::events(EventSortField sortField, SortDirection sortDirection) const 0340 { 0341 Event::List el = rawEvents(sortField, sortDirection); 0342 d->mFilter->apply(&el); 0343 return el; 0344 } 0345 0346 bool Calendar::addIncidence(const Incidence::Ptr &incidence) 0347 { 0348 if (!incidence) { 0349 return false; 0350 } 0351 0352 AddVisitor<Calendar> v(this); 0353 return incidence->accept(v, incidence); 0354 } 0355 0356 bool Calendar::deleteIncidence(const Incidence::Ptr &incidence) 0357 { 0358 if (!incidence) { 0359 return false; 0360 } 0361 0362 if (beginChange(incidence)) { 0363 DeleteVisitor<Calendar> v(this); 0364 const bool result = incidence->accept(v, incidence); 0365 endChange(incidence); 0366 return result; 0367 } else { 0368 return false; 0369 } 0370 } 0371 0372 Incidence::Ptr Calendar::createException(const Incidence::Ptr &incidence, const QDateTime &recurrenceId, bool thisAndFuture) 0373 { 0374 Q_ASSERT(recurrenceId.isValid()); 0375 if (!incidence || !incidence->recurs() || !recurrenceId.isValid()) { 0376 return Incidence::Ptr(); 0377 } 0378 0379 Incidence::Ptr newInc(incidence->clone()); 0380 const QDateTime current = QDateTime::currentDateTimeUtc(); 0381 newInc->setCreated(current); 0382 newInc->setLastModified(current); 0383 newInc->setRevision(0); 0384 // Recurring exceptions are not support for now 0385 newInc->clearRecurrence(); 0386 0387 newInc->setRecurrenceId(recurrenceId); 0388 newInc->setThisAndFuture(thisAndFuture); 0389 newInc->setDtStart(recurrenceId); 0390 0391 // Calculate and set the new end of the incidence 0392 QDateTime end = incidence->dateTime(IncidenceBase::RoleEnd); 0393 0394 if (end.isValid()) { 0395 if (incidence->allDay()) { 0396 qint64 offset = incidence->dtStart().daysTo(recurrenceId); 0397 end = end.addDays(offset); 0398 } else { 0399 qint64 offset = incidence->dtStart().secsTo(recurrenceId); 0400 end = end.addSecs(offset); 0401 } 0402 newInc->setDateTime(end, IncidenceBase::RoleEnd); 0403 } 0404 return newInc; 0405 } 0406 0407 Incidence::Ptr Calendar::incidence(const QString &uid, const QDateTime &recurrenceId) const 0408 { 0409 Incidence::Ptr i = event(uid, recurrenceId); 0410 if (i) { 0411 return i; 0412 } 0413 0414 i = todo(uid, recurrenceId); 0415 if (i) { 0416 return i; 0417 } 0418 0419 i = journal(uid, recurrenceId); 0420 return i; 0421 } 0422 0423 Incidence::List Calendar::incidencesFromSchedulingID(const QString &sid) const 0424 { 0425 Incidence::List result; 0426 const Incidence::List incidences = rawIncidences(); 0427 std::copy_if(incidences.cbegin(), incidences.cend(), std::back_inserter(result), [&sid](const Incidence::Ptr &in) { 0428 return in->schedulingID() == sid; 0429 }); 0430 return result; 0431 } 0432 0433 Incidence::Ptr Calendar::incidenceFromSchedulingID(const QString &uid) const 0434 { 0435 const Incidence::List incidences = rawIncidences(); 0436 const auto itEnd = incidences.cend(); 0437 auto it = std::find_if(incidences.cbegin(), itEnd, [&uid](const Incidence::Ptr &in) { 0438 return in->schedulingID() == uid; 0439 }); 0440 0441 return it != itEnd ? *it : Incidence::Ptr(); 0442 } 0443 0444 Todo::List Calendar::sortTodos(Todo::List &&todoList, TodoSortField sortField, SortDirection sortDirection) 0445 { 0446 // Note that To-dos may not have Start DateTimes nor due DateTimes. 0447 switch (sortField) { 0448 case TodoSortUnsorted: 0449 break; 0450 0451 case TodoSortStartDate: 0452 if (sortDirection == SortDirectionAscending) { 0453 std::sort(todoList.begin(), todoList.end(), Todos::startDateLessThan); 0454 } else { 0455 std::sort(todoList.begin(), todoList.end(), Todos::startDateMoreThan); 0456 } 0457 break; 0458 0459 case TodoSortDueDate: 0460 if (sortDirection == SortDirectionAscending) { 0461 std::sort(todoList.begin(), todoList.end(), Todos::dueDateLessThan); 0462 } else { 0463 std::sort(todoList.begin(), todoList.end(), Todos::dueDateMoreThan); 0464 } 0465 break; 0466 0467 case TodoSortPriority: 0468 if (sortDirection == SortDirectionAscending) { 0469 std::sort(todoList.begin(), todoList.end(), Todos::priorityLessThan); 0470 } else { 0471 std::sort(todoList.begin(), todoList.end(), Todos::priorityMoreThan); 0472 } 0473 break; 0474 0475 case TodoSortPercentComplete: 0476 if (sortDirection == SortDirectionAscending) { 0477 std::sort(todoList.begin(), todoList.end(), Todos::percentLessThan); 0478 } else { 0479 std::sort(todoList.begin(), todoList.end(), Todos::percentMoreThan); 0480 } 0481 break; 0482 0483 case TodoSortSummary: 0484 if (sortDirection == SortDirectionAscending) { 0485 std::sort(todoList.begin(), todoList.end(), Todos::summaryLessThan); 0486 } else { 0487 std::sort(todoList.begin(), todoList.end(), Todos::summaryMoreThan); 0488 } 0489 break; 0490 0491 case TodoSortCreated: 0492 if (sortDirection == SortDirectionAscending) { 0493 std::sort(todoList.begin(), todoList.end(), Todos::createdLessThan); 0494 } else { 0495 std::sort(todoList.begin(), todoList.end(), Todos::createdMoreThan); 0496 } 0497 break; 0498 0499 case TodoSortCategories: 0500 if (sortDirection == SortDirectionAscending) { 0501 std::sort(todoList.begin(), todoList.end(), Incidences::categoriesLessThan); 0502 } else { 0503 std::sort(todoList.begin(), todoList.end(), Incidences::categoriesMoreThan); 0504 } 0505 break; 0506 } 0507 0508 return todoList; 0509 } 0510 0511 Todo::List Calendar::todos(TodoSortField sortField, SortDirection sortDirection) const 0512 { 0513 Todo::List tl = rawTodos(sortField, sortDirection); 0514 d->mFilter->apply(&tl); 0515 return tl; 0516 } 0517 0518 Todo::List Calendar::todos(const QDate &date) const 0519 { 0520 Todo::List el = rawTodosForDate(date); 0521 d->mFilter->apply(&el); 0522 return el; 0523 } 0524 0525 Todo::List Calendar::todos(const QDate &start, const QDate &end, const QTimeZone &timeZone, bool inclusive) const 0526 { 0527 Todo::List tl = rawTodos(start, end, timeZone, inclusive); 0528 d->mFilter->apply(&tl); 0529 return tl; 0530 } 0531 0532 Journal::List Calendar::sortJournals(Journal::List &&journalList, JournalSortField sortField, SortDirection sortDirection) 0533 { 0534 switch (sortField) { 0535 case JournalSortUnsorted: 0536 break; 0537 0538 case JournalSortDate: 0539 if (sortDirection == SortDirectionAscending) { 0540 std::sort(journalList.begin(), journalList.end(), Journals::dateLessThan); 0541 } else { 0542 std::sort(journalList.begin(), journalList.end(), Journals::dateMoreThan); 0543 } 0544 break; 0545 0546 case JournalSortSummary: 0547 if (sortDirection == SortDirectionAscending) { 0548 std::sort(journalList.begin(), journalList.end(), Journals::summaryLessThan); 0549 } else { 0550 std::sort(journalList.begin(), journalList.end(), Journals::summaryMoreThan); 0551 } 0552 break; 0553 } 0554 0555 return journalList; 0556 } 0557 0558 Journal::List Calendar::journals(JournalSortField sortField, SortDirection sortDirection) const 0559 { 0560 Journal::List jl = rawJournals(sortField, sortDirection); 0561 d->mFilter->apply(&jl); 0562 return jl; 0563 } 0564 0565 Journal::List Calendar::journals(const QDate &date) const 0566 { 0567 Journal::List el = rawJournalsForDate(date); 0568 d->mFilter->apply(&el); 0569 return el; 0570 } 0571 0572 Calendar::CalendarObserver::~CalendarObserver() 0573 { 0574 } 0575 0576 void Calendar::CalendarObserver::calendarModified(bool modified, Calendar *calendar) 0577 { 0578 Q_UNUSED(modified); 0579 Q_UNUSED(calendar); 0580 } 0581 0582 void Calendar::CalendarObserver::calendarIncidenceAdded(const Incidence::Ptr &incidence) 0583 { 0584 Q_UNUSED(incidence); 0585 } 0586 0587 void Calendar::CalendarObserver::calendarIncidenceChanged(const Incidence::Ptr &incidence) 0588 { 0589 Q_UNUSED(incidence); 0590 } 0591 0592 void Calendar::CalendarObserver::calendarIncidenceAboutToBeDeleted(const Incidence::Ptr &incidence) 0593 { 0594 Q_UNUSED(incidence); 0595 } 0596 0597 void Calendar::CalendarObserver::calendarIncidenceDeleted(const Incidence::Ptr &incidence, const Calendar *calendar) 0598 { 0599 Q_UNUSED(incidence); 0600 Q_UNUSED(calendar); 0601 } 0602 0603 void Calendar::CalendarObserver::calendarIncidenceAdditionCanceled(const Incidence::Ptr &incidence) 0604 { 0605 Q_UNUSED(incidence); 0606 } 0607 0608 void Calendar::registerObserver(CalendarObserver *observer) 0609 { 0610 if (!observer) { 0611 return; 0612 } 0613 0614 if (!d->mObservers.contains(observer)) { 0615 d->mObservers.append(observer); 0616 } else { 0617 d->mNewObserver = true; 0618 } 0619 } 0620 0621 void Calendar::unregisterObserver(CalendarObserver *observer) 0622 { 0623 if (!observer) { 0624 return; 0625 } else { 0626 d->mObservers.removeAll(observer); 0627 } 0628 } 0629 0630 void Calendar::setModified(bool modified) 0631 { 0632 if (modified != d->mModified || d->mNewObserver) { 0633 d->mNewObserver = false; 0634 for (CalendarObserver *observer : std::as_const(d->mObservers)) { 0635 observer->calendarModified(modified, this); 0636 } 0637 d->mModified = modified; 0638 } 0639 } 0640 0641 bool Calendar::isModified() const 0642 { 0643 return d->mModified; 0644 } 0645 0646 void Calendar::incidenceUpdated(const QString &uid, const QDateTime &recurrenceId) 0647 { 0648 Incidence::Ptr inc = incidence(uid, recurrenceId); 0649 0650 if (!inc) { 0651 return; 0652 } 0653 0654 inc->setLastModified(QDateTime::currentDateTimeUtc()); 0655 // we should probably update the revision number here, 0656 // or internally in the Event itself when certain things change. 0657 // need to verify with ical documentation. 0658 0659 notifyIncidenceChanged(inc); 0660 0661 setModified(true); 0662 } 0663 0664 void Calendar::doSetTimeZone(const QTimeZone &timeZone) 0665 { 0666 Q_UNUSED(timeZone); 0667 } 0668 0669 void Calendar::notifyIncidenceAdded(const Incidence::Ptr &incidence) 0670 { 0671 if (!incidence) { 0672 return; 0673 } 0674 0675 if (!d->mObserversEnabled) { 0676 return; 0677 } 0678 0679 for (CalendarObserver *observer : std::as_const(d->mObservers)) { 0680 observer->calendarIncidenceAdded(incidence); 0681 } 0682 0683 for (auto role : {IncidenceBase::RoleStartTimeZone, IncidenceBase::RoleEndTimeZone}) { 0684 const auto dt = incidence->dateTime(role); 0685 if (dt.isValid() && dt.timeZone() != QTimeZone::utc()) { 0686 if (!d->mTimeZones.contains(dt.timeZone())) { 0687 d->mTimeZones.push_back(dt.timeZone()); 0688 } 0689 } 0690 } 0691 } 0692 0693 void Calendar::notifyIncidenceChanged(const Incidence::Ptr &incidence) 0694 { 0695 if (!incidence) { 0696 return; 0697 } 0698 0699 if (!d->mObserversEnabled) { 0700 return; 0701 } 0702 0703 for (CalendarObserver *observer : std::as_const(d->mObservers)) { 0704 observer->calendarIncidenceChanged(incidence); 0705 } 0706 } 0707 0708 void Calendar::notifyIncidenceAboutToBeDeleted(const Incidence::Ptr &incidence) 0709 { 0710 if (!incidence) { 0711 return; 0712 } 0713 0714 if (!d->mObserversEnabled) { 0715 return; 0716 } 0717 0718 for (CalendarObserver *observer : std::as_const(d->mObservers)) { 0719 observer->calendarIncidenceAboutToBeDeleted(incidence); 0720 } 0721 } 0722 0723 void Calendar::notifyIncidenceDeleted(const Incidence::Ptr &incidence) 0724 { 0725 if (!incidence) { 0726 return; 0727 } 0728 0729 if (!d->mObserversEnabled) { 0730 return; 0731 } 0732 0733 for (CalendarObserver *observer : std::as_const(d->mObservers)) { 0734 observer->calendarIncidenceDeleted(incidence, this); 0735 } 0736 } 0737 0738 void Calendar::notifyIncidenceAdditionCanceled(const Incidence::Ptr &incidence) 0739 { 0740 if (!incidence) { 0741 return; 0742 } 0743 0744 if (!d->mObserversEnabled) { 0745 return; 0746 } 0747 0748 for (CalendarObserver *observer : std::as_const(d->mObservers)) { 0749 observer->calendarIncidenceAdditionCanceled(incidence); 0750 } 0751 } 0752 0753 void Calendar::customPropertyUpdated() 0754 { 0755 setModified(true); 0756 } 0757 0758 void Calendar::setProductId(const QString &id) 0759 { 0760 d->mProductId = id; 0761 } 0762 0763 QString Calendar::productId() const 0764 { 0765 return d->mProductId; 0766 } 0767 0768 /** static */ 0769 Incidence::List Calendar::mergeIncidenceList(const Event::List &events, const Todo::List &todos, const Journal::List &journals) 0770 { 0771 Incidence::List incidences; 0772 incidences.reserve(events.count() + todos.count() + journals.count()); 0773 0774 int i; 0775 int end; 0776 for (i = 0, end = events.count(); i < end; ++i) { 0777 incidences.append(events[i]); 0778 } 0779 0780 for (i = 0, end = todos.count(); i < end; ++i) { 0781 incidences.append(todos[i]); 0782 } 0783 0784 for (i = 0, end = journals.count(); i < end; ++i) { 0785 incidences.append(journals[i]); 0786 } 0787 0788 return incidences; 0789 } 0790 0791 bool Calendar::beginChange(const Incidence::Ptr &incidence) 0792 { 0793 Q_UNUSED(incidence); 0794 return true; 0795 } 0796 0797 bool Calendar::endChange(const Incidence::Ptr &incidence) 0798 { 0799 Q_UNUSED(incidence); 0800 return true; 0801 } 0802 0803 void Calendar::setObserversEnabled(bool enabled) 0804 { 0805 d->mObserversEnabled = enabled; 0806 } 0807 0808 void Calendar::appendAlarms(Alarm::List &alarms, const Incidence::Ptr &incidence, const QDateTime &from, const QDateTime &to) const 0809 { 0810 QDateTime preTime = from.addSecs(-1); 0811 0812 Alarm::List alarmlist = incidence->alarms(); 0813 for (int i = 0, iend = alarmlist.count(); i < iend; ++i) { 0814 if (alarmlist[i]->enabled()) { 0815 QDateTime dt = alarmlist[i]->nextRepetition(preTime); 0816 if (dt.isValid() && dt <= to) { 0817 qCDebug(KCALCORE_LOG) << incidence->summary() << "':" << dt.toString(); 0818 alarms.append(alarmlist[i]); 0819 } 0820 } 0821 } 0822 } 0823 0824 void Calendar::appendRecurringAlarms(Alarm::List &alarms, const Incidence::Ptr &incidence, const QDateTime &from, const QDateTime &to) const 0825 { 0826 QDateTime dt; 0827 bool endOffsetValid = false; 0828 Duration endOffset(0); 0829 Duration period(from, to); 0830 0831 Alarm::List alarmlist = incidence->alarms(); 0832 for (int i = 0, iend = alarmlist.count(); i < iend; ++i) { 0833 Alarm::Ptr a = alarmlist[i]; 0834 if (a->enabled()) { 0835 if (a->hasTime()) { 0836 // The alarm time is defined as an absolute date/time 0837 dt = a->nextRepetition(from.addSecs(-1)); 0838 if (!dt.isValid() || dt > to) { 0839 continue; 0840 } 0841 } else { 0842 // Alarm time is defined by an offset from the event start or end time. 0843 // Find the offset from the event start time, which is also used as the 0844 // offset from the recurrence time. 0845 Duration offset(0); 0846 if (a->hasStartOffset()) { 0847 offset = a->startOffset(); 0848 } else if (a->hasEndOffset()) { 0849 offset = a->endOffset(); 0850 if (!endOffsetValid) { 0851 endOffset = Duration(incidence->dtStart(), incidence->dateTime(Incidence::RoleAlarmEndOffset)); 0852 endOffsetValid = true; 0853 } 0854 } 0855 0856 // Find the incidence's earliest alarm 0857 QDateTime alarmStart = offset.end(a->hasEndOffset() ? incidence->dateTime(Incidence::RoleAlarmEndOffset) : incidence->dtStart()); 0858 if (alarmStart > to) { 0859 continue; 0860 } 0861 QDateTime baseStart = incidence->dtStart(); 0862 if (from > alarmStart) { 0863 alarmStart = from; // don't look earlier than the earliest alarm 0864 baseStart = (-offset).end((-endOffset).end(alarmStart)); 0865 } 0866 0867 // Adjust the 'alarmStart' date/time and find the next recurrence at or after it. 0868 // Treat the two offsets separately in case one is daily and the other not. 0869 dt = incidence->recurrence()->getNextDateTime(baseStart.addSecs(-1)); 0870 if (!dt.isValid() || (dt = endOffset.end(offset.end(dt))) > to) { // adjust 'dt' to get the alarm time 0871 // The next recurrence is too late. 0872 if (!a->repeatCount()) { 0873 continue; 0874 } 0875 0876 // The alarm has repetitions, so check whether repetitions of previous 0877 // recurrences fall within the time period. 0878 bool found = false; 0879 Duration alarmDuration = a->duration(); 0880 for (QDateTime base = baseStart; (dt = incidence->recurrence()->getPreviousDateTime(base)).isValid(); base = dt) { 0881 if (a->duration().end(dt) < base) { 0882 break; // this recurrence's last repetition is too early, so give up 0883 } 0884 0885 // The last repetition of this recurrence is at or after 'alarmStart' time. 0886 // Check if a repetition occurs between 'alarmStart' and 'to'. 0887 int snooze = a->snoozeTime().value(); // in seconds or days 0888 if (a->snoozeTime().isDaily()) { 0889 Duration toFromDuration(dt, base); 0890 int toFrom = toFromDuration.asDays(); 0891 if (a->snoozeTime().end(from) <= to || (toFromDuration.isDaily() && toFrom % snooze == 0) 0892 || (toFrom / snooze + 1) * snooze <= toFrom + period.asDays()) { 0893 found = true; 0894 #ifndef NDEBUG 0895 // for debug output 0896 dt = offset.end(dt).addDays(((toFrom - 1) / snooze + 1) * snooze); 0897 #endif 0898 break; 0899 } 0900 } else { 0901 int toFrom = dt.secsTo(base); 0902 if (period.asSeconds() >= snooze || toFrom % snooze == 0 || (toFrom / snooze + 1) * snooze <= toFrom + period.asSeconds()) { 0903 found = true; 0904 #ifndef NDEBUG 0905 // for debug output 0906 dt = offset.end(dt).addSecs(((toFrom - 1) / snooze + 1) * snooze); 0907 #endif 0908 break; 0909 } 0910 } 0911 } 0912 if (!found) { 0913 continue; 0914 } 0915 } 0916 } 0917 qCDebug(KCALCORE_LOG) << incidence->summary() << "':" << dt.toString(); 0918 alarms.append(a); 0919 } 0920 } 0921 } 0922 0923 void Calendar::startBatchAdding() 0924 { 0925 d->batchAddingInProgress = true; 0926 } 0927 0928 void Calendar::endBatchAdding() 0929 { 0930 d->batchAddingInProgress = false; 0931 } 0932 0933 bool Calendar::batchAdding() const 0934 { 0935 return d->batchAddingInProgress; 0936 } 0937 0938 Alarm::List Calendar::alarmsTo(const QDateTime &to) const 0939 { 0940 return alarms(QDateTime(QDate(1900, 1, 1), QTime(0, 0, 0)), to); 0941 } 0942 0943 void Calendar::virtual_hook(int id, void *data) 0944 { 0945 Q_UNUSED(id); 0946 Q_UNUSED(data); 0947 Q_ASSERT(false); 0948 } 0949 0950 QString Calendar::id() const 0951 { 0952 return d->mId; 0953 } 0954 0955 void Calendar::setId(const QString &id) 0956 { 0957 if (d->mId != id) { 0958 d->mId = id; 0959 Q_EMIT idChanged(); 0960 } 0961 } 0962 0963 QString Calendar::name() const 0964 { 0965 return d->mName; 0966 } 0967 0968 void Calendar::setName(const QString &name) 0969 { 0970 if (d->mName != name) { 0971 d->mName = name; 0972 Q_EMIT nameChanged(); 0973 } 0974 } 0975 0976 QIcon Calendar::icon() const 0977 { 0978 return d->mIcon; 0979 } 0980 0981 void Calendar::setIcon(const QIcon &icon) 0982 { 0983 d->mIcon = icon; 0984 Q_EMIT iconChanged(); 0985 } 0986 0987 AccessMode Calendar::accessMode() const 0988 { 0989 return d->mAccessMode; 0990 } 0991 0992 void Calendar::setAccessMode(const AccessMode mode) 0993 { 0994 if (d->mAccessMode != mode) { 0995 d->mAccessMode = mode; 0996 Q_EMIT accessModeChanged(); 0997 } 0998 } 0999 1000 bool Calendar::isLoading() const 1001 { 1002 return d->mIsLoading; 1003 } 1004 1005 void Calendar::setIsLoading(bool isLoading) 1006 { 1007 if (d->mIsLoading == isLoading) { 1008 return; 1009 } 1010 1011 d->mIsLoading = isLoading; 1012 Q_EMIT isLoadingChanged(); 1013 } 1014 1015 #include "moc_calendar.cpp"