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 
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 
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 : std::as_const(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);
123  Incidence::Ptr lastInc(inc);
124  qint64 offset(0);
125  qint64 lastOffset(0);
126  QDateTime occurrenceStartDate;
127  for (const auto &recurrenceId : std::as_const(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  incidence = recurrenceIds.value(recurrenceId);
135  occurrenceStartDate = incidence->dtStart();
136  resetIncidence = !incidence->thisAndFuture();
137  offset = incidence->recurrenceId().secsTo(incidence->dtStart());
138  if (incidence->thisAndFuture()) {
139  lastInc = incidence;
140  lastOffset = offset;
141  }
142  } else if (inc != incidence) { // thisAndFuture exception is active
143  occurrenceStartDate = occurrenceStartDate.addSecs(offset);
144  }
145 
146  if (!occurrenceIsHidden(calendar, incidence, occurrenceStartDate)) {
147  const Period period = inc->recurrence()->rDateTimePeriod(occurrenceStartDate);
148  if (period.isValid()) {
149  occurrenceList << Private::Occurrence(incidence, recurrenceId, occurrenceStartDate, period.end());
150  } else {
151  occurrenceList << Private::Occurrence(incidence, recurrenceId, occurrenceStartDate, occurrenceEnd(incidence, occurrenceStartDate));
152  }
153  }
154 
155  if (resetIncidence) {
156  incidence = lastInc;
157  offset = lastOffset;
158  }
159  }
160  } else {
161  occurrenceList << Private::Occurrence(inc, {}, inc->dtStart(),
162  inc->dateTime(Incidence::RoleEnd));
163  }
164  }
165  occurrenceIt = QListIterator<Private::Occurrence>(occurrenceList);
166  }
167 };
168 //@endcond
169 
170 /**
171  * Right now there is little point in the iterator, but:
172  * With an iterator it should be possible to solve this more memory efficiently
173  * and with immediate results at the beginning of the selected timeframe.
174  * Either all events are iterated simoulatneously, resulting in occurrences
175  * of all events in parallel in the correct time-order, or incidence after
176  * incidence, which would be even more efficient.
177  *
178  * By making this class a friend of calendar, we could also use the internally
179  * available data structures.
180  */
182  : d(new KCalendarCore::OccurrenceIterator::Private(this))
183 {
184  d->start = start;
185  d->end = end;
186 
187  Event::List events = calendar.rawEvents(start.date(), end.date(), start.timeZone());
188  if (calendar.filter()) {
189  calendar.filter()->apply(&events);
190  }
191 
192  Todo::List todos = calendar.rawTodos(start.date(), end.date(), start.timeZone());
193  if (calendar.filter()) {
194  calendar.filter()->apply(&todos);
195  }
196 
197  Journal::List journals;
198  const Journal::List allJournals = calendar.rawJournals();
199  for (const KCalendarCore::Journal::Ptr &journal : allJournals) {
200  const QDate journalStart = journal->dtStart().toTimeZone(start.timeZone()).date();
201  if (journal->dtStart().isValid() && journalStart >= start.date() && journalStart <= end.date()) {
202  journals << journal;
203  }
204  }
205 
206  if (calendar.filter()) {
207  calendar.filter()->apply(&journals);
208  }
209 
210  const Incidence::List incidences = KCalendarCore::Calendar::mergeIncidenceList(events, todos, journals);
211  d->setupIterator(calendar, incidences);
212 }
213 
214 OccurrenceIterator::OccurrenceIterator(const Calendar &calendar, const Incidence::Ptr &incidence, const QDateTime &start, const QDateTime &end)
215  : d(new KCalendarCore::OccurrenceIterator::Private(this))
216 {
217  Q_ASSERT(incidence);
218  d->start = start;
219  d->end = end;
220  d->setupIterator(calendar, Incidence::List() << incidence);
221 }
222 
223 OccurrenceIterator::~OccurrenceIterator()
224 {
225 }
226 
227 bool OccurrenceIterator::hasNext() const
228 {
229  return d->occurrenceIt.hasNext();
230 }
231 
233 {
234  d->current = d->occurrenceIt.next();
235 }
236 
238 {
239  return d->current.incidence;
240 }
241 
243 {
244  return d->current.startDate;
245 }
246 
248 {
249  return d->current.endDate;
250 }
251 
253 {
254  return d->current.recurrenceId;
255 }
QDateTime addSecs(qint64 s) const const
const T value(const Key &key) const const
Represents a span of time measured in seconds or days.
Definition: duration.h:43
QTimeZone timeZone() const const
void apply(Event::List *eventList) const
Applies the filter to a list of Events.
Definition: calfilter.cpp:69
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
@ Seconds
duration is a number of seconds
Definition: duration.h:50
virtual Todo::List rawTodos(TodoSortField sortField=TodoSortUnsorted, SortDirection sortDirection=SortDirectionAscending) const =0
Returns a sorted, unfiltered list of all Todos for this Calendar.
Namespace for all KCalendarCore types.
Definition: alarm.h:36
QDateTime end() const
Returns when this period ends.
Definition: period.cpp:110
bool isValid() const
Returns true if the Period is not empty.
Definition: period.cpp:100
OccurrenceIterator(const Calendar &calendar, const QDateTime &start=QDateTime(), const QDateTime &end=QDateTime())
Creates iterator that iterates over all occurrences of all incidences between.
@ TypeTodo
Type is a to-do.
AKONADI_CALENDAR_EXPORT KCalendarCore::Incidence::Ptr incidence(const Akonadi::Item &item)
QHash::iterator insert(const Key &key, const T &value)
CalFilter * filter() const
Returns the calendar filter.
Definition: calendar.cpp:252
Represents the main calendar class.
Definition: calendar.h:132
Provides a To-do in the sense of RFC2445.
Definition: todo.h:33
Q_SCRIPTABLE Q_NOREPLY void start()
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:1164
QDateTime occurrenceStartDate() const
Returns the start date of the occurrence.
int criteria() const
Returns the inclusive filter criteria.
Definition: calfilter.cpp:185
virtual Event::List rawEvents(EventSortField sortField=EventSortUnsorted, SortDirection sortDirection=SortDirectionAscending) const =0
Returns a sorted, unfiltered list of all Events for this Calendar.
@ RoleEnd
Role for determining an incidence's dtEnd, will return an invalid QDateTime if the incidence does not...
QDateTime occurrenceEndDate() const
Returns the end date of the occurrence.
bool isValid() const const
@ RoleRecurrenceStart
Role for determining the start of the recurrence.
virtual Incidence::List instances(const Incidence::Ptr &incidence) const
Returns an unfiltered list of all exceptions of this recurring incidence.
Definition: calendar.cpp:290
Iterate over calendar items in a calendar.
QSharedPointer< X > staticCast() const const
bool contains(const Key &key) const const
virtual Todo::Ptr todo(const QString &uid, const QDateTime &recurrenceId={}) const =0
Returns the Todo associated with the given unique identifier.
void next()
Advance iterator to the next occurrence.
virtual Journal::List rawJournals(JournalSortField sortField=JournalSortUnsorted, SortDirection sortDirection=SortDirectionAscending) const =0
Returns a sorted, unfiltered list of all Journals for this Calendar.
AKONADI_CALENDAR_EXPORT KCalendarCore::Todo::Ptr todo(const Akonadi::Item &item)
const QList< QKeySequence > & end()
@ HideCompletedTodos
Remove completed to-dos.
Definition: calfilter.h:50
QDateTime recurrenceId() const
Returns the recurrence Id.
Incidence::Ptr incidence() const
Returns either main incidence or exception, depending on occurrence.
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Thu Sep 28 2023 03:53:12 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.