File indexing completed on 2025-01-05 04:49:48

0001 /*
0002  * SPDX-FileCopyrightText: 2016 Daniel Vrátil <dvratil@kde.org>
0003  *
0004  * SPDX-License-Identifier: GPL-2.0-or-later
0005  *
0006  */
0007 
0008 #include "eventdatavisitor.h"
0009 #include "pimdatasource.h"
0010 #include "pimeventsplugin_debug.h"
0011 
0012 BaseEventDataVisitor::BaseEventDataVisitor(PimDataSource *dataSource, QDate start, QDate end)
0013     : mDataSource(dataSource)
0014     , mStart(start)
0015     , mEnd(end)
0016 {
0017 }
0018 
0019 BaseEventDataVisitor::~BaseEventDataVisitor() = default;
0020 
0021 bool BaseEventDataVisitor::act(const KCalendarCore::Incidence::Ptr &incidence)
0022 {
0023     return incidence->accept(*this, incidence);
0024 }
0025 
0026 bool BaseEventDataVisitor::act(const KCalendarCore::Event::List &events)
0027 {
0028     bool ok = false;
0029     for (const KCalendarCore::Event::Ptr &event : events) {
0030         ok = event.staticCast<KCalendarCore::IncidenceBase>()->accept(*this, event) || ok;
0031     }
0032     return ok;
0033 }
0034 
0035 bool BaseEventDataVisitor::act(const KCalendarCore::Todo::List &todos)
0036 {
0037     bool ok = false;
0038     for (const KCalendarCore::Todo::Ptr &todo : todos) {
0039         ok = todo.staticCast<KCalendarCore::IncidenceBase>()->accept(*this, todo) || ok;
0040     }
0041     return ok;
0042 }
0043 
0044 bool BaseEventDataVisitor::isInRange(QDate start, QDate end) const
0045 {
0046     if (!mStart.isValid() || !mEnd.isValid()) {
0047         return true;
0048     }
0049 
0050     if (!end.isValid() && start >= mStart && start <= mEnd) {
0051         return true;
0052     } else if (start < mStart) {
0053         return end >= mStart;
0054     } else if (end > mEnd) {
0055         return start <= mEnd;
0056     } else {
0057         return true;
0058     }
0059 }
0060 
0061 QString BaseEventDataVisitor::generateUid(const KCalendarCore::Incidence::Ptr &incidence, const QDateTime &recurrenceId) const
0062 {
0063     // Get a corresponding Akonadi Item: Akonadi ID is the only reliably unique
0064     // and persistent identifier when dealing with incidences from multiple
0065     // calendars
0066     const qint64 itemId = mDataSource->akonadiIdForIncidence(incidence);
0067     if (itemId <= 0) {
0068         // Can this happen? What do we do now?!
0069         return {};
0070     }
0071 
0072     if (recurrenceId.isValid()) {
0073         return QStringLiteral("Akonadi-%1-%2").arg(itemId).arg(recurrenceId.toString(QStringLiteral("yyyyMMddThhmmsst")));
0074     } else {
0075         return QStringLiteral("Akonadi-%1").arg(itemId);
0076     }
0077 }
0078 
0079 QList<CalendarEvents::EventData>
0080 BaseEventDataVisitor::explodeIncidenceOccurences(const CalendarEvents::EventData &ed, const KCalendarCore::Incidence::Ptr &incidence, bool &ok)
0081 {
0082     Q_ASSERT(incidence->recurs());
0083 
0084     const qint64 duration = ed.startDateTime().secsTo(ed.endDateTime());
0085 
0086     QDateTime rec(mStart.addDays(-1), QTime(0, 0, 0));
0087     rec = incidence->recurrence()->getNextDateTime(rec);
0088     QList<CalendarEvents::EventData> results;
0089     while (rec.isValid() && rec.date() <= mEnd) {
0090         CalendarEvents::EventData copy = ed;
0091         QDateTime dt;
0092         if (incidence->allDay()) {
0093             dt = QDateTime(rec.date(), QTime(0, 0, 0), Qt::LocalTime);
0094         } else {
0095             dt = rec.toLocalTime();
0096         }
0097         copy.setStartDateTime(dt);
0098         copy.setEndDateTime(dt.addSecs(duration));
0099         copy.setUid(generateUid(incidence, rec));
0100 
0101         results.push_back(copy);
0102 
0103         rec = incidence->recurrence()->getNextDateTime(rec);
0104     }
0105 
0106     ok = true;
0107     return results;
0108 }
0109 
0110 EventDataVisitor::EventDataVisitor(PimDataSource *dataSource, const QDate &start, const QDate &end)
0111     : BaseEventDataVisitor(dataSource, start, end)
0112 {
0113 }
0114 
0115 EventDataVisitor::~EventDataVisitor() = default;
0116 
0117 const QMultiHash<QDate, CalendarEvents::EventData> &EventDataVisitor::results() const
0118 {
0119     return mResults;
0120 }
0121 
0122 bool EventDataVisitor::visit(const KCalendarCore::Incidence::Ptr &incidence, CalendarEvents::EventData::EventType type)
0123 {
0124     CalendarEvents::EventData data = incidenceData(incidence);
0125     data.setEventType(type);
0126     if (incidence->recurs()) {
0127         bool ok = false;
0128         const auto list = explodeIncidenceOccurences(data, incidence, ok);
0129         if (ok) {
0130             for (const auto &dataCalendar : list) {
0131                 insertResult(dataCalendar);
0132             }
0133         }
0134         return ok;
0135     } else if (isInRange(data.startDateTime().date(), data.endDateTime().date())) {
0136         insertResult(data);
0137         return true;
0138     }
0139 
0140     return false;
0141 }
0142 
0143 bool EventDataVisitor::visit(const KCalendarCore::Event::Ptr &event)
0144 {
0145     return visit(event, CalendarEvents::EventData::Event);
0146 }
0147 
0148 bool EventDataVisitor::visit(const KCalendarCore::Todo::Ptr &todo)
0149 {
0150     return visit(todo, CalendarEvents::EventData::Todo);
0151 }
0152 
0153 void EventDataVisitor::insertResult(const CalendarEvents::EventData &result)
0154 {
0155     QDate d = result.startDateTime().date();
0156     const QDate end = result.endDateTime().date();
0157 
0158     // Agenda without start date will be placed at the end (due) date
0159     if (!d.isValid()) {
0160         mResults.insert(end, result);
0161         return;
0162     }
0163 
0164     while (d <= end) {
0165         mResults.insert(d, result);
0166         d = d.addDays(1);
0167     }
0168 }
0169 
0170 CalendarEvents::EventData EventDataVisitor::incidenceData(const KCalendarCore::Incidence::Ptr &incidence) const
0171 {
0172     CalendarEvents::EventData data;
0173     data.setTitle(incidence->summary());
0174     data.setDescription(incidence->description());
0175     data.setIsAllDay(incidence->allDay());
0176     data.setIsMinor(false);
0177     data.setUid(generateUid(incidence));
0178     data.setStartDateTime(incidence->dtStart().toLocalTime());
0179     data.setEndDateTime(incidence->dateTime(KCalendarCore::Incidence::RoleEnd).toLocalTime());
0180     data.setEventColor(mDataSource->calendarColorForIncidence(incidence));
0181     return data;
0182 }
0183 
0184 EventDataIdVisitor::EventDataIdVisitor(PimDataSource *dataSource, QDate start, QDate end)
0185     : BaseEventDataVisitor(dataSource, start, end)
0186 {
0187 }
0188 
0189 const QStringList &EventDataIdVisitor::results() const
0190 {
0191     return mResults;
0192 }
0193 
0194 bool EventDataIdVisitor::visit(const KCalendarCore::Event::Ptr &event)
0195 {
0196     return visit(event.staticCast<KCalendarCore::Incidence>());
0197 }
0198 
0199 bool EventDataIdVisitor::visit(const KCalendarCore::Todo::Ptr &todo)
0200 {
0201     return visit(todo.staticCast<KCalendarCore::Incidence>());
0202 }
0203 
0204 bool EventDataIdVisitor::visit(const KCalendarCore::Incidence::Ptr &incidence)
0205 {
0206     if (incidence->recurs()) {
0207         CalendarEvents::EventData ed;
0208         bool ok = false;
0209         const auto list = explodeIncidenceOccurences(ed, incidence, ok);
0210         if (ok) {
0211             for (const auto &data : list) {
0212                 mResults.push_back(data.uid());
0213             }
0214         }
0215         return ok;
0216     } else {
0217         mResults.push_back(generateUid(incidence, incidence->recurrenceId()));
0218     }
0219     return true;
0220 }