1 | /* |
2 | This file is part of the kcalcore library. |
3 | |
4 | Copyright (c) 1998 Preston Brown <pbrown@kde.org> |
5 | Copyright (c) 2001,2003,2004 Cornelius Schumacher <schumacher@kde.org> |
6 | Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com> |
7 | |
8 | This library is free software; you can redistribute it and/or |
9 | modify it under the terms of the GNU Library General Public |
10 | License as published by the Free Software Foundation; either |
11 | version 2 of the License, or (at your option) any later version. |
12 | |
13 | This library is distributed in the hope that it will be useful, |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
16 | Library General Public License for more details. |
17 | |
18 | You should have received a copy of the GNU Library General Public License |
19 | along with this library; see the file COPYING.LIB. If not, write to |
20 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
21 | Boston, MA 02110-1301, USA. |
22 | */ |
23 | /** |
24 | @file |
25 | This file is part of the API for handling calendar data and |
26 | defines the MemoryCalendar class. |
27 | |
28 | @brief |
29 | This class provides a calendar stored as a local file. |
30 | |
31 | @author Preston Brown \<pbrown@kde.org\> |
32 | @author Cornelius Schumacher \<schumacher@kde.org\> |
33 | */ |
34 | |
35 | #include "memorycalendar.h" |
36 | |
37 | #include <KDebug> |
38 | #include <QDate> |
39 | #include <QDataStream> |
40 | #include <KDateTime> |
41 | |
42 | using namespace KCalCore; |
43 | |
44 | /** |
45 | Private class that helps to provide binary compatibility between releases. |
46 | @internal |
47 | */ |
48 | //@cond PRIVATE |
49 | class KCalCore::MemoryCalendar::Private |
50 | { |
51 | public: |
52 | Private(MemoryCalendar *qq) |
53 | : q(qq), mFormat(0) |
54 | { |
55 | } |
56 | ~Private() |
57 | { |
58 | } |
59 | |
60 | MemoryCalendar *q; |
61 | CalFormat *mFormat; // calendar format |
62 | QString mIncidenceBeingUpdated; // Instance identifier of Incidence currently being updated |
63 | |
64 | /** |
65 | * List of all incidences. |
66 | * First indexed by incidence->type(), then by incidence->uid(); |
67 | */ |
68 | QMap<IncidenceBase::IncidenceType, QMultiHash<QString, Incidence::Ptr> > mIncidences; |
69 | |
70 | /** |
71 | * Has all incidences, indexed by identifier. |
72 | */ |
73 | QHash<QString,KCalCore::Incidence::Ptr> mIncidencesByIdentifier; |
74 | |
75 | /** |
76 | * List of all deleted incidences. |
77 | * First indexed by incidence->type(), then by incidence->uid(); |
78 | */ |
79 | QMap<IncidenceBase::IncidenceType, QMultiHash<QString, Incidence::Ptr> > mDeletedIncidences; |
80 | |
81 | /** |
82 | * Contains incidences ( to-dos; non-recurring, non-multiday events; journals; ) |
83 | * indexed by start/due date. |
84 | * |
85 | * The QMap key is the incidence->type(). |
86 | * The QMultiHash key is the dtStart/dtDue().toString() |
87 | * |
88 | * Note: We had 3 variables, mJournalsForDate, mTodosForDate and mEventsForDate |
89 | * but i merged them into one (indexed by type) because it simplifies code using |
90 | * it. No need to if else based on type. |
91 | */ |
92 | QMap<IncidenceBase::IncidenceType, QMultiHash<QString, IncidenceBase::Ptr> > mIncidencesForDate; |
93 | |
94 | void insertIncidence(const Incidence::Ptr &incidence); |
95 | |
96 | Incidence::Ptr incidence(const QString &uid, |
97 | const IncidenceBase::IncidenceType type, |
98 | const KDateTime &recurrenceId = KDateTime()) const; |
99 | |
100 | Incidence::Ptr deletedIncidence(const QString &uid, |
101 | const KDateTime &recurrenceId, |
102 | const IncidenceBase::IncidenceType type) const; |
103 | |
104 | void deleteAllIncidences(const IncidenceBase::IncidenceType type); |
105 | |
106 | }; |
107 | //@endcond |
108 | |
109 | MemoryCalendar::MemoryCalendar(const KDateTime::Spec &timeSpec) |
110 | : Calendar(timeSpec), |
111 | d(new KCalCore::MemoryCalendar::Private(this)) |
112 | { |
113 | } |
114 | |
115 | MemoryCalendar::MemoryCalendar(const QString &timeZoneId) |
116 | : Calendar(timeZoneId), |
117 | d(new KCalCore::MemoryCalendar::Private(this)) |
118 | { |
119 | } |
120 | |
121 | MemoryCalendar::~MemoryCalendar() |
122 | { |
123 | close(); |
124 | delete d; |
125 | } |
126 | |
127 | void MemoryCalendar::close() |
128 | { |
129 | setObserversEnabled(false); |
130 | |
131 | // Don't call the virtual function deleteEvents() etc, the base class might have |
132 | // other ways of deleting the data. |
133 | d->deleteAllIncidences(Incidence::TypeEvent); |
134 | d->deleteAllIncidences(Incidence::TypeTodo); |
135 | d->deleteAllIncidences(Incidence::TypeJournal); |
136 | |
137 | d->mIncidencesByIdentifier.clear(); |
138 | d->mDeletedIncidences.clear(); |
139 | |
140 | setModified(false); |
141 | |
142 | setObserversEnabled(true); |
143 | } |
144 | |
145 | bool MemoryCalendar::deleteIncidence(const Incidence::Ptr &incidence) |
146 | { |
147 | // Handle orphaned children |
148 | // relations is an Incidence's property, not a Todo's, so |
149 | // we remove relations in deleteIncidence, not in deleteTodo. |
150 | removeRelations(incidence); |
151 | const Incidence::IncidenceType type = incidence->type(); |
152 | const QString uid = incidence->uid(); |
153 | if (d->mIncidences[type].remove(uid, incidence)) { |
154 | d->mIncidencesByIdentifier.remove(incidence->instanceIdentifier()); |
155 | setModified(true); |
156 | notifyIncidenceDeleted(incidence); |
157 | if (deletionTracking()) |
158 | d->mDeletedIncidences[type].insert(uid, incidence); |
159 | |
160 | const KDateTime dt = incidence->dateTime(Incidence::RoleCalendarHashing); |
161 | if (dt.isValid()) { |
162 | d->mIncidencesForDate[type].remove(dt.date().toString(), incidence); |
163 | } |
164 | // Delete child-incidences. |
165 | if (!incidence->hasRecurrenceId()) { |
166 | deleteIncidenceInstances(incidence); |
167 | } |
168 | return true; |
169 | } else { |
170 | kWarning() << incidence->typeStr() << " not found. uid=" << uid; |
171 | return false; |
172 | } |
173 | } |
174 | |
175 | bool MemoryCalendar::deleteIncidenceInstances(const Incidence::Ptr &incidence) |
176 | { |
177 | const Incidence::IncidenceType type = incidence->type(); |
178 | QList<Incidence::Ptr> values = d->mIncidences[type].values(incidence->uid()); |
179 | QList<Incidence::Ptr>::const_iterator it; |
180 | for (it = values.constBegin(); it != values.constEnd(); ++it) { |
181 | Incidence::Ptr i = *it; |
182 | if (i->hasRecurrenceId()) { |
183 | kDebug() << "deleting child" |
184 | << ", type=" << int(type) |
185 | << ", uid=" << i->uid() |
186 | << ", start=" << i->dtStart() |
187 | << " from calendar" ; |
188 | deleteIncidence(i); |
189 | } |
190 | } |
191 | |
192 | return true; |
193 | } |
194 | |
195 | //@cond PRIVATE |
196 | void MemoryCalendar::Private::deleteAllIncidences(const Incidence::IncidenceType incidenceType) |
197 | { |
198 | QHashIterator<QString, Incidence::Ptr>i(mIncidences[incidenceType]); |
199 | while (i.hasNext()) { |
200 | i.next(); |
201 | q->notifyIncidenceDeleted(i.value()); |
202 | i.value()->unRegisterObserver(q); |
203 | } |
204 | mIncidences[incidenceType].clear(); |
205 | mIncidencesForDate[incidenceType].clear(); |
206 | } |
207 | |
208 | Incidence::Ptr MemoryCalendar::Private::incidence(const QString &uid, |
209 | const Incidence::IncidenceType type, |
210 | const KDateTime &recurrenceId) const |
211 | { |
212 | QList<Incidence::Ptr> values = mIncidences[type].values(uid); |
213 | QList<Incidence::Ptr>::const_iterator it; |
214 | for (it = values.constBegin(); it != values.constEnd(); ++it) { |
215 | Incidence::Ptr i = *it; |
216 | if (recurrenceId.isNull()) { |
217 | if (!i->hasRecurrenceId()) { |
218 | return i; |
219 | } |
220 | } else { |
221 | if (i->hasRecurrenceId() && i->recurrenceId() == recurrenceId) { |
222 | return i; |
223 | } |
224 | } |
225 | } |
226 | return Incidence::Ptr(); |
227 | } |
228 | |
229 | Incidence::Ptr |
230 | MemoryCalendar::Private::deletedIncidence(const QString &uid, |
231 | const KDateTime &recurrenceId, |
232 | const IncidenceBase::IncidenceType type) const |
233 | { |
234 | if (!q->deletionTracking()) { |
235 | return Incidence::Ptr(); |
236 | } |
237 | |
238 | QList<Incidence::Ptr> values = mDeletedIncidences[type].values(uid); |
239 | QList<Incidence::Ptr>::const_iterator it; |
240 | for (it = values.constBegin(); it != values.constEnd(); ++it) { |
241 | Incidence::Ptr i = *it; |
242 | if (recurrenceId.isNull()) { |
243 | if (!i->hasRecurrenceId()) { |
244 | return i; |
245 | } |
246 | } else { |
247 | if (i->hasRecurrenceId() && i->recurrenceId() == recurrenceId) { |
248 | return i; |
249 | } |
250 | } |
251 | } |
252 | return Incidence::Ptr(); |
253 | } |
254 | |
255 | void MemoryCalendar::Private::insertIncidence(const Incidence::Ptr &incidence) |
256 | { |
257 | const QString uid = incidence->uid(); |
258 | const Incidence::IncidenceType type = incidence->type(); |
259 | if (!mIncidences[type].contains(uid, incidence)) { |
260 | mIncidences[type].insert(uid, incidence); |
261 | mIncidencesByIdentifier.insert(incidence->instanceIdentifier(), incidence); |
262 | const KDateTime dt = incidence->dateTime(Incidence::RoleCalendarHashing); |
263 | if (dt.isValid()) { |
264 | mIncidencesForDate[type].insert(dt.date().toString(), incidence); |
265 | } |
266 | |
267 | } else { |
268 | #ifndef NDEBUG |
269 | // if we already have an to-do with this UID, it must be the same incidence, |
270 | // otherwise something's really broken |
271 | Q_ASSERT(mIncidences[type].value(uid) == incidence); |
272 | #endif |
273 | } |
274 | } |
275 | //@endcond |
276 | |
277 | bool MemoryCalendar::addIncidence(const Incidence::Ptr &incidence) |
278 | { |
279 | d->insertIncidence(incidence); |
280 | |
281 | notifyIncidenceAdded(incidence); |
282 | |
283 | incidence->registerObserver(this); |
284 | |
285 | setupRelations(incidence); |
286 | |
287 | setModified(true); |
288 | |
289 | return true; |
290 | } |
291 | |
292 | bool MemoryCalendar::addEvent(const Event::Ptr &event) |
293 | { |
294 | return addIncidence(event); |
295 | } |
296 | |
297 | bool MemoryCalendar::deleteEvent(const Event::Ptr &event) |
298 | { |
299 | return deleteIncidence(event); |
300 | } |
301 | |
302 | bool MemoryCalendar::deleteEventInstances(const Event::Ptr &event) |
303 | { |
304 | return deleteIncidenceInstances(event); |
305 | } |
306 | |
307 | void MemoryCalendar::deleteAllEvents() |
308 | { |
309 | d->deleteAllIncidences(Incidence::TypeEvent); |
310 | } |
311 | |
312 | Event::Ptr MemoryCalendar::event(const QString &uid, |
313 | const KDateTime &recurrenceId) const |
314 | { |
315 | return d->incidence(uid, Incidence::TypeEvent, recurrenceId).staticCast<Event>(); |
316 | } |
317 | |
318 | Event::Ptr MemoryCalendar::deletedEvent(const QString &uid, const KDateTime &recurrenceId) const |
319 | { |
320 | return d->deletedIncidence(uid, recurrenceId, Incidence::TypeEvent).staticCast<Event>(); |
321 | } |
322 | |
323 | bool MemoryCalendar::addTodo(const Todo::Ptr &todo) |
324 | { |
325 | return addIncidence(todo); |
326 | } |
327 | |
328 | bool MemoryCalendar::deleteTodo(const Todo::Ptr &todo) |
329 | { |
330 | return deleteIncidence(todo); |
331 | } |
332 | |
333 | bool MemoryCalendar::deleteTodoInstances(const Todo::Ptr &todo) |
334 | { |
335 | return deleteIncidenceInstances(todo); |
336 | } |
337 | |
338 | void MemoryCalendar::deleteAllTodos() |
339 | { |
340 | d->deleteAllIncidences(Incidence::TypeTodo); |
341 | } |
342 | |
343 | Todo::Ptr MemoryCalendar::todo(const QString &uid, |
344 | const KDateTime &recurrenceId) const |
345 | { |
346 | return d->incidence(uid, Incidence::TypeTodo, recurrenceId).staticCast<Todo>(); |
347 | } |
348 | |
349 | Todo::Ptr MemoryCalendar::deletedTodo(const QString &uid, |
350 | const KDateTime &recurrenceId) const |
351 | { |
352 | return d->deletedIncidence(uid, recurrenceId, Incidence::TypeTodo).staticCast<Todo>(); |
353 | } |
354 | |
355 | Todo::List MemoryCalendar::rawTodos(TodoSortField sortField, |
356 | SortDirection sortDirection) const |
357 | { |
358 | Todo::List todoList; |
359 | QHashIterator<QString, Incidence::Ptr>i(d->mIncidences[Incidence::TypeTodo]); |
360 | while (i.hasNext()) { |
361 | i.next(); |
362 | todoList.append(i.value().staticCast<Todo>()); |
363 | } |
364 | return Calendar::sortTodos(todoList, sortField, sortDirection); |
365 | } |
366 | |
367 | Todo::List MemoryCalendar::deletedTodos(TodoSortField sortField, |
368 | SortDirection sortDirection) const |
369 | { |
370 | if (!deletionTracking()) { |
371 | return Todo::List(); |
372 | } |
373 | |
374 | Todo::List todoList; |
375 | QHashIterator<QString, Incidence::Ptr >i(d->mDeletedIncidences[Incidence::TypeTodo]); |
376 | while (i.hasNext()) { |
377 | i.next(); |
378 | todoList.append(i.value().staticCast<Todo>()); |
379 | } |
380 | return Calendar::sortTodos(todoList, sortField, sortDirection); |
381 | } |
382 | |
383 | Todo::List MemoryCalendar::todoInstances(const Incidence::Ptr &todo, |
384 | TodoSortField sortField, |
385 | SortDirection sortDirection) const |
386 | { |
387 | Todo::List list; |
388 | |
389 | QList<Incidence::Ptr > values = d->mIncidences[Incidence::TypeTodo].values(todo->uid()); |
390 | QList<Incidence::Ptr>::const_iterator it; |
391 | for (it = values.constBegin(); it != values.constEnd(); ++it) { |
392 | Todo::Ptr t = (*it).staticCast<Todo>(); |
393 | if (t->hasRecurrenceId()) { |
394 | list.append(t); |
395 | } |
396 | } |
397 | return Calendar::sortTodos(list, sortField, sortDirection); |
398 | } |
399 | |
400 | Todo::List MemoryCalendar::rawTodosForDate(const QDate &date) const |
401 | { |
402 | Todo::List todoList; |
403 | Todo::Ptr t; |
404 | |
405 | KDateTime::Spec ts = timeSpec(); |
406 | const QString dateStr = date.toString(); |
407 | QMultiHash<QString, IncidenceBase::Ptr >::const_iterator it = |
408 | d->mIncidencesForDate[Incidence::TypeTodo].constFind(dateStr); |
409 | while (it != d->mIncidencesForDate[Incidence::TypeTodo].constEnd() && it.key() == dateStr) { |
410 | t = it.value().staticCast<Todo>(); |
411 | todoList.append(t); |
412 | ++it; |
413 | } |
414 | |
415 | // Iterate over all todos. Look for recurring todoss that occur on this date |
416 | QHashIterator<QString, Incidence::Ptr >i(d->mIncidences[Incidence::TypeTodo]); |
417 | while (i.hasNext()) { |
418 | i.next(); |
419 | t = i.value().staticCast<Todo>(); |
420 | if (t->recurs()) { |
421 | if (t->recursOn(date, ts)) { |
422 | todoList.append(t); |
423 | } |
424 | } |
425 | } |
426 | |
427 | return todoList; |
428 | } |
429 | |
430 | Todo::List MemoryCalendar::rawTodos(const QDate &start, |
431 | const QDate &end, |
432 | const KDateTime::Spec ×pec, |
433 | bool inclusive) const |
434 | { |
435 | Q_UNUSED(inclusive); // use only exact dtDue/dtStart, not dtStart and dtEnd |
436 | |
437 | Todo::List todoList; |
438 | KDateTime::Spec ts = timespec.isValid() ? timespec : timeSpec(); |
439 | KDateTime st(start, ts); |
440 | KDateTime nd(end, ts); |
441 | |
442 | // Get todos |
443 | QHashIterator<QString, Incidence::Ptr >i(d->mIncidences[Incidence::TypeTodo]); |
444 | Todo::Ptr todo; |
445 | while (i.hasNext()) { |
446 | i.next(); |
447 | todo = i.value().staticCast<Todo>(); |
448 | if (!isVisible(todo)) { |
449 | continue; |
450 | } |
451 | |
452 | KDateTime rStart = todo->hasDueDate() ? todo->dtDue() : |
453 | todo->hasStartDate() ? todo->dtStart() : KDateTime(); |
454 | if (!rStart.isValid()) { |
455 | continue; |
456 | } |
457 | |
458 | if (!todo->recurs()) { // non-recurring todos |
459 | if (nd.isValid() && nd < rStart) { |
460 | continue; |
461 | } |
462 | if (st.isValid() && rStart < st) { |
463 | continue; |
464 | } |
465 | } else { // recurring events |
466 | switch (todo->recurrence()->duration()) { |
467 | case -1: // infinite |
468 | break; |
469 | case 0: // end date given |
470 | default: // count given |
471 | KDateTime rEnd(todo->recurrence()->endDate(), ts); |
472 | if (!rEnd.isValid()) { |
473 | continue; |
474 | } |
475 | if (st.isValid() && rEnd < st) { |
476 | continue; |
477 | } |
478 | break; |
479 | } // switch(duration) |
480 | } //if(recurs) |
481 | |
482 | todoList.append(todo); |
483 | } |
484 | |
485 | return todoList; |
486 | } |
487 | |
488 | Alarm::List MemoryCalendar::alarmsTo(const KDateTime &to) const |
489 | { |
490 | return alarms(KDateTime(QDate(1900, 1, 1)), to); |
491 | } |
492 | |
493 | Alarm::List MemoryCalendar::alarms(const KDateTime &from, const KDateTime &to) const |
494 | { |
495 | Alarm::List alarmList; |
496 | QHashIterator<QString, Incidence::Ptr>ie(d->mIncidences[Incidence::TypeEvent]); |
497 | Event::Ptr e; |
498 | while (ie.hasNext()) { |
499 | ie.next(); |
500 | e = ie.value().staticCast<Event>(); |
501 | if (e->recurs()) { |
502 | appendRecurringAlarms(alarmList, e, from, to); |
503 | } else { |
504 | appendAlarms(alarmList, e, from, to); |
505 | } |
506 | } |
507 | |
508 | QHashIterator<QString, Incidence::Ptr>it(d->mIncidences[Incidence::TypeTodo]); |
509 | Todo::Ptr t; |
510 | while (it.hasNext()) { |
511 | it.next(); |
512 | t = it.value().staticCast<Todo>(); |
513 | |
514 | if (!t->isCompleted()) { |
515 | appendAlarms(alarmList, t, from, to); |
516 | if (t->recurs()) { |
517 | appendRecurringAlarms(alarmList, t, from, to); |
518 | } else { |
519 | appendAlarms(alarmList, t, from, to); |
520 | } |
521 | } |
522 | } |
523 | |
524 | return alarmList; |
525 | } |
526 | |
527 | void MemoryCalendar::incidenceUpdate(const QString &uid, const KDateTime &recurrenceId) |
528 | { |
529 | Incidence::Ptr inc = incidence(uid, recurrenceId); |
530 | |
531 | if (inc) { |
532 | if (!d->mIncidenceBeingUpdated.isEmpty()) { |
533 | kWarning() << "Incidence::update() called twice without an updated() call in between." ; |
534 | } |
535 | |
536 | // Save it so we can detect changes to uid or recurringId. |
537 | d->mIncidenceBeingUpdated = inc->instanceIdentifier(); |
538 | |
539 | const KDateTime dt = inc->dateTime(Incidence::RoleCalendarHashing); |
540 | if (dt.isValid()) { |
541 | const Incidence::IncidenceType type = inc->type(); |
542 | d->mIncidencesForDate[type].remove(dt.date().toString(), inc); |
543 | } |
544 | } |
545 | } |
546 | |
547 | void MemoryCalendar::incidenceUpdated(const QString &uid, const KDateTime &recurrenceId) |
548 | { |
549 | Incidence::Ptr inc = incidence(uid, recurrenceId); |
550 | |
551 | if (inc) { |
552 | |
553 | if (d->mIncidenceBeingUpdated.isEmpty()) { |
554 | kWarning() << "Incidence::updated() called twice without an update() call in between." ; |
555 | } else if (inc->instanceIdentifier() != d->mIncidenceBeingUpdated) { |
556 | // Instance identifier changed, update our hash table |
557 | d->mIncidencesByIdentifier.remove(d->mIncidenceBeingUpdated); |
558 | d->mIncidencesByIdentifier.insert(inc->instanceIdentifier(), inc); |
559 | } |
560 | |
561 | d->mIncidenceBeingUpdated = QString(); |
562 | |
563 | inc->setLastModified(KDateTime::currentUtcDateTime()); |
564 | // we should probably update the revision number here, |
565 | // or internally in the Event itself when certain things change. |
566 | // need to verify with ical documentation. |
567 | |
568 | const KDateTime dt = inc->dateTime(Incidence::RoleCalendarHashing); |
569 | if (dt.isValid()) { |
570 | const Incidence::IncidenceType type = inc->type(); |
571 | d->mIncidencesForDate[type].insert(dt.date().toString(), inc); |
572 | } |
573 | |
574 | notifyIncidenceChanged(inc); |
575 | |
576 | setModified(true); |
577 | } |
578 | } |
579 | |
580 | Event::List MemoryCalendar::rawEventsForDate(const QDate &date, |
581 | const KDateTime::Spec ×pec, |
582 | EventSortField sortField, |
583 | SortDirection sortDirection) const |
584 | { |
585 | Event::List eventList; |
586 | |
587 | if (!date.isValid()) { |
588 | // There can't be events on invalid dates |
589 | return eventList; |
590 | } |
591 | |
592 | Event::Ptr ev; |
593 | |
594 | // Find the hash for the specified date |
595 | const QString dateStr = date.toString(); |
596 | QMultiHash<QString, IncidenceBase::Ptr >::const_iterator it = |
597 | d->mIncidencesForDate[Incidence::TypeEvent].constFind(dateStr); |
598 | // Iterate over all non-recurring, single-day events that start on this date |
599 | KDateTime::Spec ts = timespec.isValid() ? timespec : timeSpec(); |
600 | KDateTime kdt(date, ts); |
601 | while (it != d->mIncidencesForDate[Incidence::TypeEvent].constEnd() && it.key() == dateStr) { |
602 | ev = it.value().staticCast<Event>(); |
603 | KDateTime end(ev->dtEnd().toTimeSpec(ev->dtStart())); |
604 | if (ev->allDay()) { |
605 | end.setDateOnly(true); |
606 | } else { |
607 | end = end.addSecs(-1); |
608 | } |
609 | if (end >= kdt) { |
610 | eventList.append(ev); |
611 | } |
612 | ++it; |
613 | } |
614 | |
615 | // Iterate over all events. Look for recurring events that occur on this date |
616 | QHashIterator<QString, Incidence::Ptr>i(d->mIncidences[Incidence::TypeEvent]); |
617 | while (i.hasNext()) { |
618 | i.next(); |
619 | ev = i.value().staticCast<Event>(); |
620 | if (ev->recurs()) { |
621 | if (ev->isMultiDay()) { |
622 | int = ev->dtStart().date().daysTo(ev->dtEnd().date()); |
623 | for (int i = 0; i <= extraDays; ++i) { |
624 | if (ev->recursOn(date.addDays(-i), ts)) { |
625 | eventList.append(ev); |
626 | break; |
627 | } |
628 | } |
629 | } else { |
630 | if (ev->recursOn(date, ts)) { |
631 | eventList.append(ev); |
632 | } |
633 | } |
634 | } else { |
635 | if (ev->isMultiDay()) { |
636 | if (ev->dtStart().date() <= date && ev->dtEnd().date() >= date) { |
637 | eventList.append(ev); |
638 | } |
639 | } |
640 | } |
641 | } |
642 | |
643 | return Calendar::sortEvents(eventList, sortField, sortDirection); |
644 | } |
645 | |
646 | Event::List MemoryCalendar::rawEvents(const QDate &start, |
647 | const QDate &end, |
648 | const KDateTime::Spec ×pec, |
649 | bool inclusive) const |
650 | { |
651 | Event::List eventList; |
652 | KDateTime::Spec ts = timespec.isValid() ? timespec : timeSpec(); |
653 | KDateTime st(start, ts); |
654 | KDateTime nd(end, ts); |
655 | KDateTime yesterStart = st.addDays(-1); |
656 | |
657 | // Get non-recurring events |
658 | QHashIterator<QString, Incidence::Ptr>i(d->mIncidences[Incidence::TypeEvent]); |
659 | Event::Ptr event; |
660 | while (i.hasNext()) { |
661 | i.next(); |
662 | event = i.value().staticCast<Event>(); |
663 | KDateTime rStart = event->dtStart(); |
664 | if (nd < rStart) { |
665 | continue; |
666 | } |
667 | if (inclusive && rStart < st) { |
668 | continue; |
669 | } |
670 | |
671 | if (!event->recurs()) { // non-recurring events |
672 | KDateTime rEnd = event->dtEnd(); |
673 | if (rEnd < st) { |
674 | continue; |
675 | } |
676 | if (inclusive && nd < rEnd) { |
677 | continue; |
678 | } |
679 | } else { // recurring events |
680 | switch (event->recurrence()->duration()) { |
681 | case -1: // infinite |
682 | if (inclusive) { |
683 | continue; |
684 | } |
685 | break; |
686 | case 0: // end date given |
687 | default: // count given |
688 | KDateTime rEnd(event->recurrence()->endDate(), ts); |
689 | if (!rEnd.isValid()) { |
690 | continue; |
691 | } |
692 | if (rEnd < st) { |
693 | continue; |
694 | } |
695 | if (inclusive && nd < rEnd) { |
696 | continue; |
697 | } |
698 | break; |
699 | } // switch(duration) |
700 | } //if(recurs) |
701 | |
702 | eventList.append(event); |
703 | } |
704 | |
705 | return eventList; |
706 | } |
707 | |
708 | Event::List MemoryCalendar::rawEventsForDate(const KDateTime &kdt) const |
709 | { |
710 | return rawEventsForDate(kdt.date(), kdt.timeSpec()); |
711 | } |
712 | |
713 | Event::List MemoryCalendar::rawEvents(EventSortField sortField, |
714 | SortDirection sortDirection) const |
715 | { |
716 | Event::List eventList; |
717 | QHashIterator<QString, Incidence::Ptr> i(d->mIncidences[Incidence::TypeEvent]); |
718 | while (i.hasNext()) { |
719 | i.next(); |
720 | eventList.append(i.value().staticCast<Event>()); |
721 | } |
722 | return Calendar::sortEvents(eventList, sortField, sortDirection); |
723 | } |
724 | |
725 | Event::List MemoryCalendar::deletedEvents(EventSortField sortField, |
726 | SortDirection sortDirection) const |
727 | { |
728 | if (!deletionTracking()) { |
729 | return Event::List(); |
730 | } |
731 | |
732 | Event::List eventList; |
733 | QHashIterator<QString, Incidence::Ptr>i(d->mDeletedIncidences[Incidence::TypeEvent]); |
734 | while (i.hasNext()) { |
735 | i.next(); |
736 | eventList.append(i.value().staticCast<Event>()); |
737 | } |
738 | return Calendar::sortEvents(eventList, sortField, sortDirection); |
739 | } |
740 | |
741 | Event::List MemoryCalendar::eventInstances(const Incidence::Ptr &event, |
742 | EventSortField sortField, |
743 | SortDirection sortDirection) const |
744 | { |
745 | Event::List list; |
746 | |
747 | QList<Incidence::Ptr> values = d->mIncidences[Incidence::TypeEvent].values(event->uid()); |
748 | QList<Incidence::Ptr>::const_iterator it; |
749 | for (it = values.constBegin(); it != values.constEnd(); ++it) { |
750 | Event::Ptr ev = (*it).staticCast<Event>(); |
751 | if (ev->hasRecurrenceId()) { |
752 | list.append(ev); |
753 | } |
754 | } |
755 | return Calendar::sortEvents(list, sortField, sortDirection); |
756 | } |
757 | |
758 | bool MemoryCalendar::addJournal(const Journal::Ptr &journal) |
759 | { |
760 | return addIncidence(journal); |
761 | } |
762 | |
763 | bool MemoryCalendar::deleteJournal(const Journal::Ptr &journal) |
764 | { |
765 | return deleteIncidence(journal); |
766 | } |
767 | |
768 | bool MemoryCalendar::deleteJournalInstances(const Journal::Ptr &journal) |
769 | { |
770 | return deleteIncidenceInstances(journal); |
771 | } |
772 | |
773 | void MemoryCalendar::deleteAllJournals() |
774 | { |
775 | d->deleteAllIncidences(Incidence::TypeJournal); |
776 | } |
777 | |
778 | Journal::Ptr MemoryCalendar::journal(const QString &uid, |
779 | const KDateTime &recurrenceId) const |
780 | { |
781 | return d->incidence(uid, Incidence::TypeJournal, recurrenceId).staticCast<Journal>(); |
782 | } |
783 | |
784 | Journal::Ptr MemoryCalendar::deletedJournal(const QString &uid, |
785 | const KDateTime &recurrenceId) const |
786 | { |
787 | return d->deletedIncidence(uid, recurrenceId, Incidence::TypeJournal).staticCast<Journal>(); |
788 | } |
789 | |
790 | Journal::List MemoryCalendar::rawJournals(JournalSortField sortField, |
791 | SortDirection sortDirection) const |
792 | { |
793 | Journal::List journalList; |
794 | QHashIterator<QString, Incidence::Ptr>i(d->mIncidences[Incidence::TypeJournal]); |
795 | while (i.hasNext()) { |
796 | i.next(); |
797 | journalList.append(i.value().staticCast<Journal>()); |
798 | } |
799 | return Calendar::sortJournals(journalList, sortField, sortDirection); |
800 | } |
801 | |
802 | Journal::List MemoryCalendar::deletedJournals(JournalSortField sortField, |
803 | SortDirection sortDirection) const |
804 | { |
805 | if (!deletionTracking()) { |
806 | return Journal::List(); |
807 | } |
808 | |
809 | Journal::List journalList; |
810 | QHashIterator<QString, Incidence::Ptr>i(d->mDeletedIncidences[Incidence::TypeJournal]); |
811 | while (i.hasNext()) { |
812 | i.next(); |
813 | journalList.append(i.value().staticCast<Journal>()); |
814 | } |
815 | return Calendar::sortJournals(journalList, sortField, sortDirection); |
816 | } |
817 | |
818 | Journal::List MemoryCalendar::journalInstances(const Incidence::Ptr &journal, |
819 | JournalSortField sortField, |
820 | SortDirection sortDirection) const |
821 | { |
822 | Journal::List list; |
823 | |
824 | QList<Incidence::Ptr> values = d->mIncidences[Incidence::TypeJournal].values(journal->uid()); |
825 | QList<Incidence::Ptr>::const_iterator it; |
826 | for (it = values.constBegin(); it != values.constEnd(); ++it) { |
827 | Journal::Ptr j = (*it).staticCast<Journal>(); |
828 | if (j->hasRecurrenceId()) { |
829 | list.append(j); |
830 | } |
831 | } |
832 | return Calendar::sortJournals(list, sortField, sortDirection); |
833 | } |
834 | |
835 | Journal::List MemoryCalendar::rawJournalsForDate(const QDate &date) const |
836 | { |
837 | Journal::List journalList; |
838 | Journal::Ptr j; |
839 | |
840 | QString dateStr = date.toString(); |
841 | QMultiHash<QString, IncidenceBase::Ptr >::const_iterator it = |
842 | d->mIncidencesForDate[Incidence::TypeJournal].constFind(dateStr); |
843 | |
844 | while (it != d->mIncidencesForDate[Incidence::TypeJournal].constEnd() && it.key() == dateStr) { |
845 | j = it.value().staticCast<Journal>(); |
846 | journalList.append(j); |
847 | ++it; |
848 | } |
849 | return journalList; |
850 | } |
851 | |
852 | Incidence::Ptr MemoryCalendar::instance(const QString &identifier) const |
853 | { |
854 | return d->mIncidencesByIdentifier.value(identifier); |
855 | } |
856 | |
857 | void MemoryCalendar::virtual_hook(int id, void *data) |
858 | { |
859 | Q_UNUSED(id); |
860 | Q_UNUSED(data); |
861 | Q_ASSERT(false); |
862 | } |
863 | |