KCalendarCore

freebusy.cpp
Go to the documentation of this file.
1 /*
2  This file is part of the kcalcore library.
3 
4  SPDX-FileCopyrightText: 2001 Cornelius Schumacher <[email protected]>
5  SPDX-FileCopyrightText: 2004 Reinhold Kainhofer <[email protected]>
6 
7  SPDX-License-Identifier: LGPL-2.0-or-later
8 */
9 /**
10  @file
11  This file is part of the API for handling calendar data and
12  defines the FreeBusy class.
13 
14  @brief
15  Provides information about the free/busy time of a calendar user.
16 
17  @author Cornelius Schumacher <[email protected]>
18  @author Reinhold Kainhofer <[email protected]>
19 */
20 #include "freebusy.h"
21 #include "utils_p.h"
22 #include "visitor.h"
23 
24 #include "icalformat.h"
25 
26 #include "incidencebase_p.h"
27 #include "kcalendarcore_debug.h"
28 #include <QTime>
29 
30 using namespace KCalendarCore;
31 
32 //@cond PRIVATE
33 class KCalendarCore::FreeBusyPrivate : public IncidenceBasePrivate
34 {
35 public:
36  FreeBusyPrivate() = default;
37  FreeBusyPrivate(const FreeBusyPrivate &other) = default;
38 
39  FreeBusyPrivate(const FreeBusyPeriod::List &busyPeriods)
40  : IncidenceBasePrivate()
41  , mBusyPeriods(busyPeriods)
42  {
43  }
44 
45  void init(const FreeBusyPrivate &other);
46  void init(const Event::List &events, const QDateTime &start, const QDateTime &end);
47 
48  QDateTime mDtEnd; // end datetime
49  FreeBusyPeriod::List mBusyPeriods; // list of periods
50 
51  // This is used for creating a freebusy object for the current user
52  bool addLocalPeriod(const QDateTime &eventStart, const QDateTime &eventEnd);
53 
54  void sortBusyPeriods()
55  {
56  std::sort(mBusyPeriods.begin(), mBusyPeriods.end());
57  }
58 };
59 
60 void FreeBusyPrivate::init(const FreeBusyPrivate &other)
61 {
62  mDtEnd = other.mDtEnd;
63  mBusyPeriods = other.mBusyPeriods;
64 }
65 //@endcond
66 
68  : IncidenceBase(new FreeBusyPrivate())
69 {
70 }
71 
73  : IncidenceBase(other, new FreeBusyPrivate(*other.d_func()))
74 {
75 }
76 
78  : FreeBusy()
79 {
80  setDtStart(start); // NOLINT false clang-analyzer-optin.cplusplus.VirtualCall
81  setDtEnd(end); // NOLINT false clang-analyzer-optin.cplusplus.VirtualCall
82 }
83 
84 FreeBusy::FreeBusy(const Event::List &events, const QDateTime &start, const QDateTime &end)
85  : FreeBusy()
86 {
87  setDtStart(start); // NOLINT false clang-analyzer-optin.cplusplus.VirtualCall
88  setDtEnd(end); // NOLINT false clang-analyzer-optin.cplusplus.VirtualCall
89 
90  Q_D(FreeBusy);
91  d->init(events, start, end);
92 }
93 
94 //@cond PRIVATE
95 void FreeBusyPrivate::init(const Event::List &eventList, const QDateTime &start, const QDateTime &end)
96 {
97  const qint64 duration = start.daysTo(end);
98  QDate day;
99  QDateTime tmpStart;
100  QDateTime tmpEnd;
101 
102  // Loops through every event in the calendar
103  for (auto event : eventList) {
104  // If this event is transparent it shouldn't be in the freebusy list.
105  if (event->transparency() == Event::Transparent) {
106  continue;
107  }
108 
109  // The code below can not handle all-day events. Fixing this resulted
110  // in a lot of duplicated code. Instead, make a copy of the event and
111  // set the period to the full day(s). This trick works for recurring,
112  // multiday, and single day all-day events.
113  Event::Ptr allDayEvent;
114  if (event->allDay()) {
115  // addDay event. Do the hack
116  qCDebug(KCALCORE_LOG) << "All-day event";
117  allDayEvent = Event::Ptr(new Event(*event));
118 
119  // Set the start and end times to be on midnight
120  QDateTime st = allDayEvent->dtStart();
121  st.setTime(QTime(0, 0));
122  QDateTime nd = allDayEvent->dtEnd();
123  nd.setTime(QTime(23, 59, 59, 999));
124  allDayEvent->setAllDay(false);
125  allDayEvent->setDtStart(st);
126  allDayEvent->setDtEnd(nd);
127 
128  qCDebug(KCALCORE_LOG) << "Use:" << st.toString() << "to" << nd.toString();
129  // Finally, use this event for the setting below
130  event = allDayEvent;
131  }
132 
133  // This whole for loop is for recurring events, it loops through
134  // each of the days of the freebusy request
135 
136  for (qint64 i = 0; i <= duration; ++i) {
137  day = start.addDays(i).date();
138  tmpStart.setDate(day);
139  tmpEnd.setDate(day);
140 
141  if (event->recurs()) {
142  if (event->isMultiDay()) {
143  // FIXME: This doesn't work for sub-daily recurrences or recurrences with
144  // a different time than the original event.
145  const qint64 extraDays = event->dtStart().daysTo(event->dtEnd());
146  for (qint64 x = 0; x <= extraDays; ++x) {
147  if (event->recursOn(day.addDays(-x), start.timeZone())) {
148  tmpStart.setDate(day.addDays(-x));
149  tmpStart.setTime(event->dtStart().time());
150  tmpEnd = event->duration().end(tmpStart);
151 
152  addLocalPeriod(tmpStart, tmpEnd);
153  break;
154  }
155  }
156  } else {
157  if (event->recursOn(day, start.timeZone())) {
158  tmpStart.setTime(event->dtStart().time());
159  tmpEnd.setTime(event->dtEnd().time());
160 
161  addLocalPeriod(tmpStart, tmpEnd);
162  }
163  }
164  }
165  }
166 
167  // Non-recurring events
168  addLocalPeriod(event->dtStart(), event->dtEnd());
169  }
170 
171  sortBusyPeriods();
172 }
173 //@endcond
174 
175 FreeBusy::FreeBusy(const Period::List &busyPeriods)
176  : IncidenceBase(new FreeBusyPrivate())
177 {
179 }
180 
182  : IncidenceBase(new FreeBusyPrivate(busyPeriods))
183 {
184 }
185 
186 FreeBusy::~FreeBusy() = default;
187 
189 {
190  return TypeFreeBusy;
191 }
192 
194 {
195  return QByteArrayLiteral("FreeBusy");
196 }
197 
199 {
201 }
202 
204 {
205  Q_D(FreeBusy);
206  update();
207  d->mDtEnd = end;
209  updated();
210 }
211 
213 {
214  Q_D(const FreeBusy);
215  return d->mDtEnd;
216 }
217 
219 {
220  Period::List res;
221 
222  Q_D(const FreeBusy);
223  res.reserve(d->mBusyPeriods.count());
224  for (const FreeBusyPeriod &p : std::as_const(d->mBusyPeriods)) {
225  res << p;
226  }
227 
228  return res;
229 }
230 
232 {
233  Q_D(const FreeBusy);
234  return d->mBusyPeriods;
235 }
236 
238 {
239  Q_D(FreeBusy);
240  d->sortBusyPeriods();
241 }
242 
244 {
245  Q_D(FreeBusy);
246  d->mBusyPeriods.reserve(d->mBusyPeriods.count() + list.count());
247  for (const Period &p : std::as_const(list)) {
248  d->mBusyPeriods << FreeBusyPeriod(p);
249  }
250  sortList();
251 }
252 
254 {
255  Q_D(FreeBusy);
256  d->mBusyPeriods += list;
257  sortList();
258 }
259 
261 {
262  Q_D(FreeBusy);
263  d->mBusyPeriods.append(FreeBusyPeriod(start, end));
264  sortList();
265 }
266 
267 void FreeBusy::addPeriod(const QDateTime &start, const Duration &duration)
268 {
269  Q_D(FreeBusy);
270  d->mBusyPeriods.append(FreeBusyPeriod(start, duration));
271  sortList();
272 }
273 
274 void FreeBusy::merge(const FreeBusy::Ptr &freeBusy)
275 {
276  if (freeBusy->dtStart() < dtStart()) {
277  setDtStart(freeBusy->dtStart());
278  }
279 
280  if (freeBusy->dtEnd() > dtEnd()) {
281  setDtEnd(freeBusy->dtEnd());
282  }
283 
284  Q_D(FreeBusy);
285  const Period::List periods = freeBusy->busyPeriods();
286  d->mBusyPeriods.reserve(d->mBusyPeriods.count() + periods.count());
287  for (const auto &p : periods) {
288  d->mBusyPeriods.append(FreeBusyPeriod(p.start(), p.end()));
289  }
290  sortList();
291 }
292 
293 void FreeBusy::shiftTimes(const QTimeZone &oldZone, const QTimeZone &newZone)
294 {
295  Q_D(FreeBusy);
296  if (oldZone.isValid() && newZone.isValid() && oldZone != newZone) {
297  IncidenceBase::shiftTimes(oldZone, newZone);
298  update();
299  d->mDtEnd = d->mDtEnd.toTimeZone(oldZone);
300  d->mDtEnd.setTimeZone(newZone);
301  for (FreeBusyPeriod p : std::as_const(d->mBusyPeriods)) {
302  p.shiftTimes(oldZone, newZone);
303  }
305  updated();
306  }
307 }
308 
310 {
311  Q_D(FreeBusy);
312  if (&other != this) {
313  IncidenceBase::assign(other);
314  const FreeBusy *f = static_cast<const FreeBusy *>(&other);
315  d->init(*(f->d_func()));
316  }
317  return *this;
318 }
319 
320 bool FreeBusy::equals(const IncidenceBase &freeBusy) const
321 {
322  if (!IncidenceBase::equals(freeBusy)) {
323  return false;
324  } else {
325  Q_D(const FreeBusy);
326  // If they weren't the same type IncidenceBase::equals would had returned false already
327  const FreeBusy *fb = static_cast<const FreeBusy *>(&freeBusy);
328  return identical(dtEnd(), fb->dtEnd()) && d->mBusyPeriods == fb->d_func()->mBusyPeriods;
329  }
330 }
331 
332 bool FreeBusy::accept(Visitor &v, const IncidenceBase::Ptr &incidence)
333 {
334  return v.visit(incidence.staticCast<FreeBusy>());
335 }
336 
338 {
339  Q_UNUSED(role);
340  // No roles affecting freeBusy yet
341  return QDateTime();
342 }
343 
344 void FreeBusy::setDateTime(const QDateTime &dateTime, DateTimeRole role)
345 {
346  Q_UNUSED(dateTime);
347  Q_UNUSED(role);
348 }
349 
350 void FreeBusy::virtual_hook(VirtualHook id, void *data)
351 {
352  Q_UNUSED(id);
353  Q_UNUSED(data);
354  Q_ASSERT(false);
355 }
356 
357 //@cond PRIVATE
358 bool FreeBusyPrivate::addLocalPeriod(const QDateTime &eventStart, const QDateTime &eventEnd)
359 {
360  QDateTime tmpStart;
361  QDateTime tmpEnd;
362 
363  // Check to see if the start *or* end of the event is
364  // between the start and end of the freebusy dates.
365  QDateTime start = mDtStart;
366  if (!(((start.secsTo(eventStart) >= 0) && (eventStart.secsTo(mDtEnd) >= 0)) || ((start.secsTo(eventEnd) >= 0) && (eventEnd.secsTo(mDtEnd) >= 0)))) {
367  return false;
368  }
369 
370  if (eventStart.secsTo(start) >= 0) {
371  tmpStart = start;
372  } else {
373  tmpStart = eventStart;
374  }
375 
376  if (eventEnd.secsTo(mDtEnd) <= 0) {
377  tmpEnd = mDtEnd;
378  } else {
379  tmpEnd = eventEnd;
380  }
381 
382  FreeBusyPeriod p(tmpStart, tmpEnd);
383  mBusyPeriods.append(p);
384 
385  return true;
386 }
387 //@endcond
388 
390 {
392 }
393 
395 {
396  return QLatin1String("application/x-vnd.akonadi.calendar.freebusy");
397 }
398 
400 {
402  QString data = format.createScheduleMessage(freebusy, iTIPPublish);
403  return stream << data;
404 }
405 
407 {
408  QString freeBusyVCal;
409  stream >> freeBusyVCal;
410 
412  freebusy = format.parseFreeBusy(freeBusyVCal);
413 
414  if (!freebusy) {
415  qCDebug(KCALCORE_LOG) << "Error parsing free/busy";
416  qCDebug(KCALCORE_LOG) << freeBusyVCal;
417  }
418 
419  return stream;
420 }
QLatin1String mimeType() const override
Definition: freebusy.cpp:389
static QLatin1String freeBusyMimeType()
Returns the Akonadi specific sub MIME type of a KCalendarCore::FreeBusy.
Definition: freebusy.cpp:394
void merge(const FreeBusy::Ptr &freebusy)
Merges another free/busy into this free/busy.
Definition: freebusy.cpp:274
KCALENDARCORE_EXPORT QDataStream & operator<<(QDataStream &out, const KCalendarCore::Alarm::Ptr &)
Alarm serializer.
Definition: alarm.cpp:820
Represents a span of time measured in seconds or days.
Definition: duration.h:43
void setDtEnd(const QDateTime &end)
Sets the end datetime for the free/busy.
Definition: freebusy.cpp:203
~FreeBusy() override
Destroys a free/busy.
virtual bool visit(const Event::Ptr &event)
Reimplement this function in your concrete subclass of IncidenceBase::Visitor to perform actions on a...
Definition: visitor.cpp:25
DateTimeRole
The different types of incidence date/times roles.
QVector::iterator begin()
QCA_EXPORT void init()
int count(const T &value) const const
IncidenceType type() const override
Definition: freebusy.cpp:188
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
virtual void setDtStart(const QDateTime &dtStart)
Sets the incidence's starting date/time with a QDateTime.
bool equals(const IncidenceBase &freebusy) const override
Compare this with freebusy for equality.
Definition: freebusy.cpp:320
Namespace for all KCalendarCore types.
Definition: alarm.h:36
void setDtStart(const QDateTime &start) override
Sets the start date/time for the free/busy.
Definition: freebusy.cpp:198
void update()
Call this to notify the observers after the IncidenceBase object will be changed.
KCALENDARCORE_EXPORT bool identical(QDateTime, QDateTime)
Compare two QDateTimes for extended equality.
virtual IncidenceBase & assign(const IncidenceBase &other)
Provides polymorfic assignment.
KCALENDARCORE_EXPORT QDataStream & operator>>(QDataStream &in, const KCalendarCore::Alarm::Ptr &)
Alarm deserializer.
Definition: alarm.cpp:833
IncidenceBase & assign(const IncidenceBase &other) override
Definition: freebusy.cpp:309
FreeBusyPeriod::List fullBusyPeriods() const
Returns the list of all periods within the free/busy.
Definition: freebusy.cpp:231
FreeBusy::Ptr parseFreeBusy(const QString &string)
Converts a QString into a FreeBusy object.
Definition: icalformat.cpp:475
void addPeriod(const QDateTime &start, const QDateTime &end)
Adds a period to the freebusy list and sorts the list.
Definition: freebusy.cpp:260
QString createScheduleMessage(const IncidenceBase::Ptr &incidence, iTIPMethod method)
Creates a scheduling message string for an Incidence.
Definition: icalformat.cpp:429
FreeBusy()
Constructs an free/busy without any periods.
Definition: freebusy.cpp:67
void setDateTime(const QDateTime &dateTime, DateTimeRole role) override
Definition: freebusy.cpp:344
void setDate(const QDate &date)
void sortList()
Sorts the list of free/busy periods into ascending order.
Definition: freebusy.cpp:237
An abstract class that provides a common base for all calendar incidence classes.
Definition: incidencebase.h:98
@ iTIPPublish
Event, to-do, journal or freebusy posting.
Q_SCRIPTABLE Q_NOREPLY void start()
void updated()
Call this to notify the observers after the IncidenceBase object has changed.
@ Transparent
Event does not appear in free/busy time.
Definition: event.h:43
qint64 secsTo(const QDateTime &other) const const
Provides information about the free/busy time of a calendar.
Definition: freebusy.h:42
QDateTime dateTime(DateTimeRole role) const override
Definition: freebusy.cpp:337
bool isValid() const const
QByteArray typeStr() const override
Definition: freebusy.cpp:193
AKONADI_CALENDAR_EXPORT KCalendarCore::Event::Ptr event(const Akonadi::Item &item)
QDate addDays(qint64 ndays) const const
This class provides the interface for a visitor of calendar components.
Definition: visitor.h:30
void reserve(int size)
iCalendar format implementation.
Definition: icalformat.h:44
QVector::iterator end()
Period::List busyPeriods() const
Returns the list of all periods within the free/busy.
Definition: freebusy.cpp:218
IncidenceType
The different types of incidences, per RFC2445.
virtual void shiftTimes(const QTimeZone &oldZone, const QTimeZone &newZone)
Shift the times of the incidence so that they appear at the same clock time as before but in a new ti...
@ TypeFreeBusy
Type is a free/busy.
int count(const T &value) const const
void virtual_hook(VirtualHook id, void *data) override
Definition: freebusy.cpp:350
virtual bool equals(const IncidenceBase &incidenceBase) const
Provides polymorfic comparison for equality.
QSharedPointer< X > staticCast() const const
QString toString(Qt::DateFormat format) const const
void shiftTimes(const QTimeZone &oldZone, const QTimeZone &newZone) override
Definition: freebusy.cpp:293
void setFieldDirty(IncidenceBase::Field field)
Marks Field field as dirty.
@ FieldDtEnd
Field representing the DTEND component.
The period can be defined by either a start time and an end time or by a start time and a duration.
Duration duration() const
Returns the length of the incidence duration.
void setTime(const QTime &time)
Q_D(Todo)
Private class that helps to provide binary compatibility between releases.
void addPeriods(const Period::List &list)
Adds a list of periods to the freebusy object and then sorts that list.
Definition: freebusy.cpp:243
virtual QDateTime dtEnd() const
Returns the end datetime for the free/busy.
Definition: freebusy.cpp:212
QSharedPointer< Event > Ptr
A shared pointer to an Event object.
Definition: event.h:50
This class provides an Event in the sense of RFC2445.
Definition: event.h:32
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.