KCalendarCore

occurrenceiterator.cpp
Go to the documentation of this file.
1 /*
2  This file is part of the kcalcore library.
3 
4  SPDX-FileCopyrightText: 2013 Christian Mollekopf <[email protected]>
5 
6  SPDX-License-Identifier: LGPL-2.0-or-later
7 */
8 /**
9  @file
10  This file is part of the API for handling calendar data and
11  defines the OccurrenceIterator class.
12 
13  @brief
14  This class provides an iterator to iterate over all occurrences of incidences.
15 
16  @author Christian Mollekopf <[email protected]>
17  */
18 
19 #include "occurrenceiterator.h"
20 #include "calendar.h"
21 #include "calfilter.h"
22 
23 #include <QDate>
24 
25 using namespace KCalendarCore;
26 
27 /**
28  Private class that helps to provide binary compatibility between releases.
29  @internal
30 */
31 //@cond PRIVATE
32 class Q_DECL_HIDDEN KCalendarCore::OccurrenceIterator::Private
33 {
34 public:
35  Private(OccurrenceIterator *qq)
36  : q(qq)
37  , occurrenceIt(occurrenceList)
38  {
39  }
40 
42  QDateTime start;
43  QDateTime end;
44 
45  struct Occurrence {
46  Occurrence()
47  {
48  }
49 
50  Occurrence(const Incidence::Ptr &i, const QDateTime &recurrenceId,
51  const QDateTime &startDate, const QDateTime &endDate)
52  : incidence(i)
53  , recurrenceId(recurrenceId)
54  , startDate(startDate)
55  , endDate(endDate)
56  {
57  }
58 
59  Incidence::Ptr incidence;
60  QDateTime recurrenceId;
61  QDateTime startDate;
62  QDateTime endDate;
63  };
64  QList<Occurrence> occurrenceList;
65  QListIterator<Occurrence> occurrenceIt;
66  Occurrence current;
67 
68  /*
69  * KCalendarCore::CalFilter can't handle individual occurrences.
70  * When filtering completed to-dos, the CalFilter doesn't hide
71  * them if it's a recurring to-do.
72  */
73  bool occurrenceIsHidden(const Calendar &calendar, const Incidence::Ptr &inc, const QDateTime &occurrenceDate)
74  {
75  if ((inc->type() == Incidence::TypeTodo) && calendar.filter() && (calendar.filter()->criteria() & KCalendarCore::CalFilter::HideCompletedTodos)) {
76  if (inc->recurs()) {
77  const Todo::Ptr todo = inc.staticCast<Todo>();
78  if (todo && (occurrenceDate < todo->dtDue())) {
79  return true;
80  }
81  } else if (inc->hasRecurrenceId()) {
82  const Todo::Ptr mainTodo = calendar.todo(inc->uid());
83  if (mainTodo && mainTodo->isCompleted()) {
84  return true;
85  }
86  }
87  }
88  return false;
89  }
90 
91  QDateTime occurrenceEnd(const Incidence::Ptr &inc, const QDateTime &start)
92  {
93  if (inc->hasDuration()) {
94  return inc->duration().end(start);
95  } else {
96  const QDateTime end = inc->dateTime(Incidence::RoleEnd);
97  if (end.isValid()) {
98  const Duration elapsed(inc->dtStart(), end, Duration::Seconds);
99  return elapsed.end(start);
100  }
101  }
102  return QDateTime();
103  }
104 
105  void setupIterator(const Calendar &calendar, const Incidence::List &incidences)
106  {
107  for (const Incidence::Ptr &inc : qAsConst(incidences)) {
108  if (inc->hasRecurrenceId()) {
109  continue;
110  }
111  if (inc->recurs()) {
112  QHash<QDateTime, Incidence::Ptr> recurrenceIds;
113  QDateTime incidenceRecStart = inc->dateTime(Incidence::RoleRecurrenceStart);
114  // const bool isAllDay = inc->allDay();
115  const auto lstInstances = calendar.instances(inc);
116  for (const Incidence::Ptr &exception : lstInstances) {
117  if (incidenceRecStart.isValid()) {
118  recurrenceIds.insert(exception->recurrenceId().toTimeZone(incidenceRecStart.timeZone()), exception);
119  }
120  }
121  const auto occurrences = inc->recurrence()->timesInInterval(start, end);
122  Incidence::Ptr incidence(inc);
123  Incidence::Ptr lastInc(inc);
124  qint64 offset(0);
125  qint64 lastOffset(0);
126  QDateTime occurrenceStartDate;
127  for (const auto &recurrenceId : qAsConst(occurrences)) {
128  occurrenceStartDate = recurrenceId;
129 
130  bool resetIncidence = false;
131  if (recurrenceIds.contains(recurrenceId)) {
132  // TODO: exclude exceptions where the start/end is not within
133  // (so the occurrence of the recurrence is omitted, but no exception is added)
134  if (recurrenceIds.value(recurrenceId)->status() == Incidence::StatusCanceled) {
135  continue;
136  }
137 
138  incidence = recurrenceIds.value(recurrenceId);
139  occurrenceStartDate = incidence->dtStart();
140  resetIncidence = !incidence->thisAndFuture();
141  offset = incidence->recurrenceId().secsTo(incidence->dtStart());
142  if (incidence->thisAndFuture()) {
143  lastInc = incidence;
144  lastOffset = offset;
145  }
146  } else if (inc != incidence) { // thisAndFuture exception is active
147  occurrenceStartDate = occurrenceStartDate.addSecs(offset);
148  }
149 
150  if (!occurrenceIsHidden(calendar, incidence, occurrenceStartDate)) {
151  const Period period = inc->recurrence()->rDateTimePeriod(occurrenceStartDate);
152  if (period.isValid()) {
153  occurrenceList << Private::Occurrence(incidence, recurrenceId, occurrenceStartDate, period.end());
154  } else {
155  occurrenceList << Private::Occurrence(incidence, recurrenceId, occurrenceStartDate, occurrenceEnd(incidence, occurrenceStartDate));
156  }
157  }
158 
159  if (resetIncidence) {
160  incidence = lastInc;
161  offset = lastOffset;
162  }
163  }
164  } else {
165  occurrenceList << Private::Occurrence(inc, {}, inc->dtStart(),
166  inc->dateTime(Incidence::RoleEnd));
167  }
168  }
169  occurrenceIt = QListIterator<Private::Occurrence>(occurrenceList);
170  }
171 };
172 //@endcond
173 
174 /**
175  * Right now there is little point in the iterator, but:
176  * With an iterator it should be possible to solve this more memory efficiently
177  * and with immediate results at the beginning of the selected timeframe.
178  * Either all events are iterated simoulatneously, resulting in occurrences
179  * of all events in parallel in the correct time-order, or incidence after
180  * incidence, which would be even more efficient.
181  *
182  * By making this class a friend of calendar, we could also use the internally
183  * available data structures.
184  */
185 OccurrenceIterator::OccurrenceIterator(const Calendar &calendar, const QDateTime &start, const QDateTime &end)
186  : d(new KCalendarCore::OccurrenceIterator::Private(this))
187 {
188  d->start = start;
189  d->end = end;
190 
191  Event::List events = calendar.rawEvents(start.date(), end.date(), start.timeZone());
192  if (calendar.filter()) {
193  calendar.filter()->apply(&events);
194  }
195 
196  Todo::List todos = calendar.rawTodos(start.date(), end.date(), start.timeZone());
197  if (calendar.filter()) {
198  calendar.filter()->apply(&todos);
199  }
200 
201  Journal::List journals;
202  const Journal::List allJournals = calendar.rawJournals();
203  for (const KCalendarCore::Journal::Ptr &journal : allJournals) {
204  const QDate journalStart = journal->dtStart().toTimeZone(start.timeZone()).date();
205  if (journal->dtStart().isValid() && journalStart >= start.date() && journalStart <= end.date()) {
206  journals << journal;
207  }
208  }
209 
210  if (calendar.filter()) {
211  calendar.filter()->apply(&journals);
212  }
213 
214  const Incidence::List incidences = KCalendarCore::Calendar::mergeIncidenceList(events, todos, journals);
215  d->setupIterator(calendar, incidences);
216 }
217 
219  : d(new KCalendarCore::OccurrenceIterator::Private(this))
220 {
221  Q_ASSERT(incidence);
222  d->start = start;
223  d->end = end;
224  d->setupIterator(calendar, Incidence::List() << incidence);
225 }
226 
227 OccurrenceIterator::~OccurrenceIterator()
228 {
229 }
230 
231 bool OccurrenceIterator::hasNext() const
232 {
233  return d->occurrenceIt.hasNext();
234 }
235 
237 {
238  d->current = d->occurrenceIt.next();
239 }
240 
242 {
243  return d->current.incidence;
244 }
245 
247 {
248  return d->current.startDate;
249 }
250 
252 {
253  return d->current.endDate;
254 }
255 
257 {
258  return d->current.recurrenceId;
259 }
int criteria() const
Returns the inclusive filter criteria.
Definition: calfilter.cpp:200
QHash::iterator insert(const Key &key, const T &value)
QDateTime recurrenceId() const
Returns the recurrence Id.
Represents the main calendar class.
Definition: calendar.h:129
This file is part of the API for handling calendar data and defines the OccurrenceIterator class...
CalFilter * filter() const
Returns the calendar filter.
Definition: calendar.cpp:251
QDateTime end() const
Returns when this period ends.
Definition: period.cpp:109
virtual Todo::Ptr todo(const QString &uid, const QDateTime &recurrenceId={}) const =0
Returns the Todo associated with the given unique identifier.
QDateTime end(const QDateTime &start) const
Computes a duration end time by adding the number of seconds or days in the duration to the specified...
Definition: duration.cpp:171
OccurrenceIterator(const Calendar &calendar, const QDateTime &start=QDateTime(), const QDateTime &end=QDateTime())
Creates iterator that iterates over all occurrences of all incidences between.
virtual Todo::List rawTodos(TodoSortField sortField=TodoSortUnsorted, SortDirection sortDirection=SortDirectionAscending) const =0
Returns a sorted, unfiltered list of all Todos for this Calendar.
Role for determining the start of the recurrence.
Incidence::Ptr incidence() const
Returns either main incidence or exception, depending on occurrence.
Represents a span of time measured in seconds or days.
Definition: duration.h:43
The period can be defined by either a start time and an end time or by a start time and a duration...
Definition: period.h:37
This file is part of the API for handling calendar data and defines the Calendar class.
QDateTime occurrenceStartDate() const
Returns the start date of the occurrence.
Provides a To-do in the sense of RFC2445.
Definition: todo.h:30
static Incidence::List mergeIncidenceList(const Event::List &events, const Todo::List &todos, const Journal::List &journals)
Create a merged list of Events, Todos, and Journals.
Definition: calendar.cpp:1175
bool isValid() const
Returns true if the Period is not empty.
Definition: period.cpp:99
void apply(Event::List *eventList) const
Applies the filter to a list of Events.
Definition: calfilter.cpp:69
const T value(const Key &key) const const
bool isValid() const const
const QList< QKeySequence > & end()
Iterate over calendar items in a calendar.
QDate date() const const
duration is a number of seconds
Definition: duration.h:50
event or to-do canceled; journal removed
Definition: incidence.h:83
qint64 secsTo(const QDateTime &other) const const
virtual Incidence::List instances(const Incidence::Ptr &incidence) const
Returns an unfiltered list of all exceptions of this recurring incidence.
Definition: calendar.cpp:289
Role for determining an incidence&#39;s dtEnd, will return an invalid QDateTime if the incidence does not...
bool contains(const Key &key) const const
virtual Event::List rawEvents(EventSortField sortField=EventSortUnsorted, SortDirection sortDirection=SortDirectionAscending) const =0
Returns a sorted, unfiltered list of all Events for this Calendar.
This file is part of the API for handling calendar data and defines the CalFilter class...
QDateTime addSecs(qint64 s) const const
QSharedPointer< X > staticCast() const const
virtual Journal::List rawJournals(JournalSortField sortField=JournalSortUnsorted, SortDirection sortDirection=SortDirectionAscending) const =0
Returns a sorted, unfiltered list of all Journals for this Calendar.
void next()
Advance iterator to the next occurrence.
Namespace for all KCalendarCore types.
Definition: alarm.h:36
QTimeZone timeZone() const const
QDateTime occurrenceEndDate() const
Returns the end date of the occurrence.
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Sun Sep 26 2021 22:51:52 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.