1 | /* |
2 | This file is part of the kcalcore library. |
3 | |
4 | Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> |
5 | |
6 | This library is free software; you can redistribute it and/or |
7 | modify it under the terms of the GNU Library General Public |
8 | License as published by the Free Software Foundation; either |
9 | version 2 of the License, or (at your option) any later version. |
10 | |
11 | This library is distributed in the hope that it will be useful, |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | Library General Public License for more details. |
15 | |
16 | You should have received a copy of the GNU Library General Public License |
17 | along with this library; see the file COPYING.LIB. If not, write to |
18 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
19 | Boston, MA 02110-1301, USA. |
20 | */ |
21 | /** |
22 | @file |
23 | This file is part of the API for handling calendar data and |
24 | defines the Event class. |
25 | |
26 | @brief |
27 | This class provides an Event in the sense of RFC2445. |
28 | |
29 | @author Cornelius Schumacher \<schumacher@kde.org\> |
30 | */ |
31 | |
32 | #include "event.h" |
33 | #include "visitor.h" |
34 | |
35 | #include <KDebug> |
36 | |
37 | #include <QDate> |
38 | |
39 | using namespace KCalCore; |
40 | |
41 | /** |
42 | Private class that helps to provide binary compatibility between releases. |
43 | @internal |
44 | */ |
45 | //@cond PRIVATE |
46 | class KCalCore::Event::Private |
47 | { |
48 | public: |
49 | Private() |
50 | : mHasEndDate(false), |
51 | mTransparency(Opaque), |
52 | mMultiDayValid(false), |
53 | mMultiDay(false) |
54 | {} |
55 | Private(const KCalCore::Event::Private &other) |
56 | : mDtEnd(other.mDtEnd), |
57 | mHasEndDate(other.mHasEndDate), |
58 | mTransparency(other.mTransparency), |
59 | mMultiDayValid(false), |
60 | mMultiDay(false) |
61 | {} |
62 | |
63 | KDateTime mDtEnd; |
64 | bool mHasEndDate; |
65 | Transparency mTransparency; |
66 | bool mMultiDayValid; |
67 | bool mMultiDay; |
68 | }; |
69 | //@endcond |
70 | |
71 | Event::Event() |
72 | : d(new KCalCore::Event::Private) |
73 | { |
74 | } |
75 | |
76 | Event::Event(const Event &other) |
77 | : Incidence(other), d(new KCalCore::Event::Private(*other.d)) |
78 | { |
79 | } |
80 | |
81 | Event::~Event() |
82 | { |
83 | delete d; |
84 | } |
85 | |
86 | Event *Event::clone() const |
87 | { |
88 | return new Event(*this); |
89 | } |
90 | |
91 | IncidenceBase &Event::assign(const IncidenceBase &other) |
92 | { |
93 | if (&other != this) { |
94 | Incidence::assign(other); |
95 | const Event *e = static_cast<const Event*>(&other); |
96 | *d = *(e->d); |
97 | } |
98 | return *this; |
99 | } |
100 | |
101 | bool Event::equals(const IncidenceBase &event) const |
102 | { |
103 | if (!Incidence::equals(event)) { |
104 | return false; |
105 | } else { |
106 | // If they weren't the same type IncidenceBase::equals would had returned false already |
107 | const Event *e = static_cast<const Event*>(&event); |
108 | return |
109 | ((dtEnd() == e->dtEnd()) || |
110 | (!dtEnd().isValid() && !e->dtEnd().isValid())) && |
111 | hasEndDate() == e->hasEndDate() && |
112 | transparency() == e->transparency(); |
113 | } |
114 | } |
115 | |
116 | Incidence::IncidenceType Event::type() const |
117 | { |
118 | return TypeEvent; |
119 | } |
120 | |
121 | QByteArray Event::typeStr() const |
122 | { |
123 | return "Event" ; |
124 | } |
125 | |
126 | void Event::setDtStart(const KDateTime &dt) |
127 | { |
128 | d->mMultiDayValid = false; |
129 | Incidence::setDtStart(dt); |
130 | } |
131 | |
132 | void Event::setDtEnd(const KDateTime &dtEnd) |
133 | { |
134 | if (mReadOnly) { |
135 | return; |
136 | } |
137 | |
138 | update(); |
139 | |
140 | d->mDtEnd = dtEnd; |
141 | d->mMultiDayValid = false; |
142 | d->mHasEndDate = dtEnd.isValid(); |
143 | if (d->mHasEndDate) { |
144 | setHasDuration(false); |
145 | } |
146 | setFieldDirty(FieldDtEnd); |
147 | updated(); |
148 | } |
149 | |
150 | KDateTime Event::dtEnd() const |
151 | { |
152 | if (hasEndDate()) { |
153 | return d->mDtEnd; |
154 | } |
155 | |
156 | if (hasDuration()) { |
157 | if (allDay()) { |
158 | // For all day events, dtEnd is always inclusive |
159 | KDateTime end = duration().end(dtStart()).addDays(-1); |
160 | return end >= dtStart() ? end : dtStart(); |
161 | } else { |
162 | return duration().end(dtStart()); |
163 | } |
164 | } |
165 | |
166 | // It is valid for a VEVENT to be without a DTEND. See RFC2445, Sect4.6.1. |
167 | // Be careful to use Event::dateEnd() as appropriate due to this possibility. |
168 | return dtStart(); |
169 | } |
170 | |
171 | QDate Event::dateEnd() const |
172 | { |
173 | KDateTime end = dtEnd().toTimeSpec(dtStart()); |
174 | if (allDay()) { |
175 | return end.date(); |
176 | } else { |
177 | return end.addSecs(-1).date(); |
178 | } |
179 | } |
180 | |
181 | void Event::setHasEndDate(bool b) |
182 | { |
183 | d->mHasEndDate = b; |
184 | setFieldDirty(FieldDtEnd); |
185 | } |
186 | |
187 | bool Event::hasEndDate() const |
188 | { |
189 | return d->mHasEndDate; |
190 | } |
191 | |
192 | bool Event::isMultiDay(const KDateTime::Spec &spec) const |
193 | { |
194 | // First off, if spec's not valid, we can check for cache |
195 | if (!spec.isValid() && d->mMultiDayValid) { |
196 | return d->mMultiDay; |
197 | } |
198 | |
199 | // Not in cache -> do it the hard way |
200 | KDateTime start, end; |
201 | |
202 | if (!spec.isValid()) { |
203 | start = dtStart(); |
204 | end = dtEnd(); |
205 | } else { |
206 | start = dtStart().toTimeSpec(spec); |
207 | end = dtEnd().toTimeSpec(spec); |
208 | } |
209 | |
210 | // End date is non inclusive, so subtract 1 second... except if we |
211 | // got the event from some braindead implementation which gave us |
212 | // start == end one (those do happen) |
213 | if (start != end) { |
214 | end = end.addSecs(-1); |
215 | } |
216 | |
217 | const bool multi = (start.date() != end.date() && start <= end); |
218 | |
219 | // Update the cache |
220 | if (spec.isValid()) { |
221 | d->mMultiDayValid = true; |
222 | d->mMultiDay = multi; |
223 | } |
224 | return multi; |
225 | } |
226 | |
227 | void Event::shiftTimes(const KDateTime::Spec &oldSpec, |
228 | const KDateTime::Spec &newSpec) |
229 | { |
230 | Incidence::shiftTimes(oldSpec, newSpec); |
231 | if (hasEndDate()) { |
232 | d->mDtEnd = d->mDtEnd.toTimeSpec(oldSpec); |
233 | d->mDtEnd.setTimeSpec(newSpec); |
234 | } |
235 | } |
236 | |
237 | void Event::setTransparency(Event::Transparency transparency) |
238 | { |
239 | if (mReadOnly) { |
240 | return; |
241 | } |
242 | update(); |
243 | d->mTransparency = transparency; |
244 | setFieldDirty(FieldTransparency); |
245 | updated(); |
246 | } |
247 | |
248 | Event::Transparency Event::transparency() const |
249 | { |
250 | return d->mTransparency; |
251 | } |
252 | |
253 | void Event::setDuration(const Duration &duration) |
254 | { |
255 | setDtEnd(KDateTime()); |
256 | Incidence::setDuration(duration); |
257 | } |
258 | |
259 | void Event::setAllDay(bool allday) |
260 | { |
261 | if (allday != allDay() && !mReadOnly) { |
262 | setFieldDirty(FieldDtEnd); |
263 | Incidence::setAllDay(allday); |
264 | } |
265 | } |
266 | |
267 | bool Event::accept(Visitor &v, IncidenceBase::Ptr incidence) |
268 | { |
269 | return v.visit(incidence.staticCast<Event>()); |
270 | } |
271 | |
272 | KDateTime Event::dateTime(DateTimeRole role) const |
273 | { |
274 | switch (role) { |
275 | case RoleRecurrenceStart: |
276 | case RoleAlarmStartOffset: |
277 | case RoleStartTimeZone: |
278 | case RoleSort: |
279 | return dtStart(); |
280 | case RoleCalendarHashing: |
281 | return !recurs() && !isMultiDay() ? dtStart() : |
282 | KDateTime(); |
283 | case RoleAlarmEndOffset: |
284 | case RoleEndTimeZone: |
285 | case RoleEndRecurrenceBase: |
286 | case RoleEnd: |
287 | case RoleDisplayEnd: |
288 | return dtEnd(); |
289 | case RoleDisplayStart: |
290 | return dtStart(); |
291 | case RoleAlarm: |
292 | if (alarms().isEmpty()) { |
293 | return KDateTime(); |
294 | } else { |
295 | Alarm::Ptr alarm = alarms().first(); |
296 | return alarm->hasStartOffset() ? dtStart() : dtEnd(); |
297 | } |
298 | break; |
299 | default: |
300 | return KDateTime(); |
301 | } |
302 | } |
303 | |
304 | void Event::setDateTime(const KDateTime &dateTime, DateTimeRole role) |
305 | { |
306 | switch (role) { |
307 | case RoleDnD: |
308 | { |
309 | const int duration = dtStart().secsTo(dtEnd()); |
310 | |
311 | setDtStart(dateTime); |
312 | setDtEnd(dateTime.addSecs(duration <= 0 ? 3600 : duration)); |
313 | break; |
314 | } |
315 | case RoleEnd: |
316 | setDtEnd(dateTime); |
317 | break; |
318 | default: |
319 | kDebug() << "Unhandled role" << role; |
320 | } |
321 | } |
322 | |
323 | void Event::virtual_hook(int id, void *data) |
324 | { |
325 | switch (static_cast<IncidenceBase::VirtualHook>(id)) { |
326 | case IncidenceBase::SerializerHook: |
327 | serialize(*reinterpret_cast<QDataStream*>(data)); |
328 | break; |
329 | case IncidenceBase::DeserializerHook: |
330 | deserialize(*reinterpret_cast<QDataStream*>(data)); |
331 | break; |
332 | default: |
333 | Q_ASSERT(false); |
334 | } |
335 | } |
336 | |
337 | QLatin1String KCalCore::Event::mimeType() const |
338 | { |
339 | return Event::eventMimeType(); |
340 | } |
341 | |
342 | QLatin1String Event::eventMimeType() |
343 | { |
344 | return QLatin1String("application/x-vnd.akonadi.calendar.event" ); |
345 | } |
346 | |
347 | QLatin1String Event::iconName(const KDateTime &) const |
348 | { |
349 | return QLatin1String("view-calendar-day" ); |
350 | } |
351 | |
352 | void Event::serialize(QDataStream &out) |
353 | { |
354 | Incidence::serialize(out); |
355 | out << d->mDtEnd << d->mHasEndDate << static_cast<quint32>(d->mTransparency) << d->mMultiDayValid << d->mMultiDay; |
356 | } |
357 | |
358 | void Event::deserialize(QDataStream &in) |
359 | { |
360 | Incidence::deserialize(in); |
361 | in >> d->mDtEnd >> d->mHasEndDate; |
362 | quint32 transp; |
363 | in >> transp; |
364 | d->mTransparency = static_cast<Transparency>(transp); |
365 | in >> d->mMultiDayValid >> d->mMultiDay; |
366 | } |
367 | |