mirror of
https://bitbucket.org/smil3y/kde-playground.git
synced 2025-02-24 10:52:52 +00:00
244 lines
8.3 KiB
C++
244 lines
8.3 KiB
C++
/*
|
|
This file is part of the kcalcore library.
|
|
|
|
Copyright (C) 2013 Christian Mollekopf <mollekopf@kolabsys.com>
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110-1301, USA.
|
|
*/
|
|
/**
|
|
@file
|
|
This file is part of the API for handling calendar data and
|
|
defines the OccurrenceIterator class.
|
|
|
|
@brief
|
|
This class provides an iterator to iterate over all occurrences of incidences.
|
|
|
|
@author Christian Mollekopf \<mollekopf@kolabsys.com\>
|
|
*/
|
|
|
|
#include "occurrenceiterator.h"
|
|
#include "calendar.h"
|
|
#include "calfilter.h"
|
|
|
|
#include <KDebug>
|
|
#include <QDate>
|
|
|
|
using namespace KCalCore;
|
|
|
|
/**
|
|
Private class that helps to provide binary compatibility between releases.
|
|
@internal
|
|
*/
|
|
//@cond PRIVATE
|
|
class KCalCore::OccurrenceIterator::Private
|
|
{
|
|
public:
|
|
Private(OccurrenceIterator *qq)
|
|
: q(qq),
|
|
occurrenceIt(occurrenceList)
|
|
{
|
|
}
|
|
|
|
OccurrenceIterator *q;
|
|
KDateTime start;
|
|
KDateTime end;
|
|
|
|
struct Occurrence
|
|
{
|
|
Occurrence()
|
|
{
|
|
}
|
|
|
|
Occurrence(const Incidence::Ptr &i, const KDateTime &d)
|
|
: incidence(i), date(d)
|
|
{
|
|
}
|
|
|
|
Incidence::Ptr incidence;
|
|
KDateTime date;
|
|
};
|
|
QList<Occurrence> occurrenceList;
|
|
QListIterator<Occurrence> occurrenceIt;
|
|
Occurrence current;
|
|
|
|
/*
|
|
* KCalCore::CalFilter can't handle individual occurrences.
|
|
* When filtering completed to-dos, the CalFilter doesn't hide
|
|
* them if it's a recurring to-do.
|
|
*/
|
|
bool occurrenceIsHidden(const Calendar &calendar,
|
|
const Incidence::Ptr &inc,
|
|
const KDateTime &occurrenceDate)
|
|
{
|
|
if ((inc->type() == Incidence::TypeTodo) &&
|
|
calendar.filter() &&
|
|
(calendar.filter()->criteria() & KCalCore::CalFilter::HideCompletedTodos)) {
|
|
if (inc->recurs()) {
|
|
const Todo::Ptr todo = inc.staticCast<Todo>();
|
|
if (todo && (occurrenceDate < todo->dtDue())) {
|
|
return true;
|
|
}
|
|
} else if (inc->hasRecurrenceId()) {
|
|
const Todo::Ptr mainTodo = calendar.todo(inc->uid());
|
|
if (mainTodo && mainTodo->isCompleted()) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void setupIterator(const Calendar &calendar, const Incidence::List &incidences)
|
|
{
|
|
foreach(const Incidence::Ptr &inc, incidences) {
|
|
if (inc->hasRecurrenceId()) {
|
|
continue;
|
|
}
|
|
if (inc->recurs()) {
|
|
QHash<KDateTime, Incidence::Ptr> recurrenceIds;
|
|
KDateTime incidenceRecStart = inc->dateTime(Incidence::RoleRecurrenceStart);
|
|
foreach(const Incidence::Ptr &exception, calendar.instances(inc)) {
|
|
if (incidenceRecStart.isValid())
|
|
recurrenceIds.insert(exception->recurrenceId().toTimeSpec(incidenceRecStart.timeSpec()), exception);
|
|
}
|
|
const bool isAllDay = inc->allDay();
|
|
const DateTimeList occurrences = inc->recurrence()->timesInInterval(start, end);
|
|
Incidence::Ptr incidence(inc);
|
|
qint64 offset(0);
|
|
foreach(KDateTime occurrenceDate, occurrences) { //krazy:exclude=foreach
|
|
//timesInInterval generates always date-times,
|
|
//which is not what we want for all-day events
|
|
occurrenceDate.setDateOnly(isAllDay);
|
|
|
|
bool resetIncidence = false;
|
|
if (recurrenceIds.contains(occurrenceDate)) {
|
|
// TODO: exclude exceptions where the start/end is not within
|
|
// (so the occurrence of the recurrence is omitted, but no exception is added)
|
|
if (recurrenceIds.value(occurrenceDate)->status() == Incidence::StatusCanceled)
|
|
continue;
|
|
|
|
incidence = recurrenceIds.value(occurrenceDate);
|
|
occurrenceDate = incidence->dtStart();
|
|
resetIncidence = !incidence->thisAndFuture();
|
|
offset = incidence->recurrenceId().secsTo_long(incidence->dtStart());
|
|
} else if (inc != incidence) { //thisAndFuture exception is active
|
|
occurrenceDate = occurrenceDate.addSecs(offset);
|
|
}
|
|
if (!occurrenceIsHidden(calendar, incidence, occurrenceDate)) {
|
|
occurrenceList << Private::Occurrence(incidence, occurrenceDate);
|
|
}
|
|
if (resetIncidence) {
|
|
incidence = inc;
|
|
offset = 0;
|
|
}
|
|
}
|
|
} else {
|
|
occurrenceList << Private::Occurrence(inc, inc->dtStart());
|
|
}
|
|
}
|
|
occurrenceIt = QListIterator<Private::Occurrence>(occurrenceList);
|
|
}
|
|
};
|
|
//@endcond
|
|
|
|
static uint qHash(const KDateTime &dt)
|
|
{
|
|
return qHash(dt.toString());
|
|
}
|
|
|
|
/**
|
|
* Right now there is little point in the iterator, but:
|
|
* With an iterator it should be possible to solve this more memory efficiently
|
|
* and with immediate results at the beginning of the selected timeframe.
|
|
* Either all events are iterated simoulatneously, resulting in occurrences
|
|
* of all events in parallel in the correct time-order, or incidence after
|
|
* incidence, which would be even more efficient.
|
|
*
|
|
* By making this class a friend of calendar, we could also use the internally
|
|
* available data structures.
|
|
*/
|
|
OccurrenceIterator::OccurrenceIterator(const Calendar &calendar,
|
|
const KDateTime &start,
|
|
const KDateTime &end)
|
|
: d(new KCalCore::OccurrenceIterator::Private(this))
|
|
{
|
|
d->start = start;
|
|
d->end = end;
|
|
|
|
Event::List events = calendar.rawEvents(start.date(), end.date(), start.timeSpec());
|
|
if (calendar.filter()) {
|
|
calendar.filter()->apply(&events);
|
|
}
|
|
|
|
Todo::List todos = calendar.rawTodos(start.date(), end.date(), start.timeSpec());
|
|
if (calendar.filter()) {
|
|
calendar.filter()->apply(&todos);
|
|
}
|
|
|
|
Journal::List journals;
|
|
const Journal::List allJournals = calendar.rawJournals();
|
|
foreach(const KCalCore::Journal::Ptr &journal, allJournals) {
|
|
const QDate journalStart = journal->dtStart().toTimeSpec(start.timeSpec()).date();
|
|
if (journal->dtStart().isValid() &&
|
|
journalStart >= start.date() &&
|
|
journalStart <= end.date())
|
|
journals << journal;
|
|
}
|
|
|
|
if (calendar.filter()) {
|
|
calendar.filter()->apply(&journals);
|
|
}
|
|
|
|
const Incidence::List incidences =
|
|
KCalCore::Calendar::mergeIncidenceList(events, todos, journals);
|
|
d->setupIterator(calendar, incidences);
|
|
}
|
|
|
|
OccurrenceIterator::OccurrenceIterator(const Calendar &calendar,
|
|
const Incidence::Ptr &incidence,
|
|
const KDateTime &start,
|
|
const KDateTime &end)
|
|
: d(new KCalCore::OccurrenceIterator::Private(this))
|
|
{
|
|
Q_ASSERT(incidence);
|
|
d->start = start;
|
|
d->end = end;
|
|
d->setupIterator(calendar, Incidence::List() << incidence);
|
|
}
|
|
|
|
OccurrenceIterator::~OccurrenceIterator()
|
|
{
|
|
}
|
|
|
|
bool OccurrenceIterator::hasNext() const
|
|
{
|
|
return d->occurrenceIt.hasNext();
|
|
}
|
|
|
|
void OccurrenceIterator::next()
|
|
{
|
|
d->current = d->occurrenceIt.next();
|
|
}
|
|
|
|
Incidence::Ptr OccurrenceIterator::incidence() const
|
|
{
|
|
return d->current.incidence;
|
|
}
|
|
|
|
KDateTime OccurrenceIterator::occurrenceStartDate() const
|
|
{
|
|
return d->current.date;
|
|
}
|