File indexing completed on 2024-04-21 05:50:57

0001 /*
0002  * SPDX-FileCopyrightText: 2020 Dimitris Kardarakos <dimkard@posteo.net>
0003  *
0004  * SPDX-License-Identifier: GPL-3.0-or-later
0005  */
0006 
0007 #include "eventmodel.h"
0008 #include <KCalendarCore/CalFilter>
0009 #include <KLocalizedString>
0010 #include "settingscontroller.h"
0011 
0012 EventModel::EventModel(QObject *parent) :
0013     QAbstractListModel {parent},
0014     m_events {KCalendarCore::Event::List {}},
0015     m_filterdt {QDate {}},
0016     m_category {QString {}},
0017     m_local_calendar {nullptr},
0018     m_settings_controller {new SettingsController}
0019 {
0020     connect(this, &EventModel::filterdtChanged, this, &EventModel::loadEvents);
0021     connect(this, &EventModel::calendarChanged, this, &EventModel::loadEvents);
0022     connect(this, &EventModel::eventCategoryChanged, this, &EventModel::loadEvents);
0023 }
0024 
0025 QDate EventModel::filterdt() const
0026 {
0027     return m_filterdt;
0028 }
0029 
0030 void EventModel::setFilterdt(const QDate &filterDate)
0031 {
0032     m_filterdt = filterDate;
0033 
0034     Q_EMIT filterdtChanged();
0035 }
0036 
0037 LocalCalendar *EventModel::calendar() const
0038 {
0039     return m_local_calendar;
0040 }
0041 
0042 void EventModel::setCalendar(LocalCalendar *const calendarPtr)
0043 {
0044     m_local_calendar = calendarPtr;
0045 
0046     connect(m_local_calendar, &LocalCalendar::eventsChanged, this, &EventModel::loadEvents);
0047 
0048     Q_EMIT calendarChanged();
0049 }
0050 
0051 QHash<int, QByteArray> EventModel::roleNames() const
0052 {
0053     return {
0054         { Uid, "uid" },
0055         { EventStartDt, "eventStartDt" },
0056         { EventDt, "eventDt" },
0057         { ShiftedEventDt, "shiftedEventDt" },
0058         { ShiftedEventDtLocal, "shiftedEventDtLocal" },
0059         { ScheduleStartDt, "scheduleStartDt" },
0060         { AllDay, "allday" },
0061         { Description, "description" },
0062         { Summary, "summary" },
0063         { LastModified, "lastmodified" },
0064         { Location, "location" },
0065         { Categories, "categories" },
0066         { Priority, "priority" },
0067         { Created, "created" },
0068         { Secrecy, "secrecy" },
0069         { EventEndDt, "eventEndDt" },
0070         { ScheduleEndDt, "scheduleEndDt" },
0071         { Transparency, "transparency" },
0072         { RepeatPeriodType, "repeatType" },
0073         { RepeatEvery, "repeatEvery" },
0074         { RepeatStopAfter, "repeatStopAfter" },
0075         { IsRepeating, "isRepeating" },
0076         { EventCategories, "eventCategories" },
0077         { Url, "url" },
0078         { ShiftedStartEndDt, "shiftedStartEndDt" },
0079         { ShiftedStartEndDtLocal, "shiftedStartEndDtLocal" },
0080         { ShiftedStartEndTime, "shiftedStartEndTime" },
0081         { ShiftedStartEndTimeLocal, "shiftedStartEndTimeLocal" },
0082         { StartEndDt, "startEndDt" },
0083         { StartEndDtLocal, "startEndDtLocal" },
0084         { Overlapping, "overlapping" },
0085         { ConferenceTzId, "conferenceTzId" }
0086     };
0087 }
0088 
0089 static void applyTimeZone(QDateTime &dt, const QTimeZone &tz)
0090 {
0091     if (dt.timeSpec() == Qt::LocalTime) {
0092         dt.setTimeZone(tz);
0093     } else {
0094         dt = dt.toTimeZone(tz);
0095     }
0096 }
0097 
0098 QVariant EventModel::data(const QModelIndex &index, int role) const
0099 {
0100     if (!index.isValid()) {
0101         return QVariant {};
0102     }
0103 
0104     auto row = index.row();
0105     auto calendarTz = m_local_calendar->memorycalendar()->timeZone();
0106     auto startDtTime = m_events.at(row)->dtStart();
0107     auto endDtTime = m_events.at(row)->dtEnd();
0108     auto allDay = m_events.at(row)->allDay();
0109 
0110     switch (role) {
0111     case Uid :
0112         return m_events.at(row)->uid();
0113     case EventStartDt:
0114         return startDtTime;
0115     case EventDt:
0116         return startDtTime.toString("dddd d MMMM");
0117     case ShiftedEventDt: {
0118         applyTimeZone(startDtTime, calendarTz);
0119         return startDtTime.toString("dddd d MMMM");
0120     }
0121     case ShiftedEventDtLocal: {
0122         startDtTime = startDtTime.toTimeZone(QTimeZone::systemTimeZone());
0123         return startDtTime.toString("dddd d MMMM");
0124     }
0125     case ScheduleStartDt: {
0126         applyTimeZone(startDtTime, calendarTz);
0127         return startDtTime;
0128     }
0129     case AllDay:
0130         return allDay;
0131     case Description:
0132         return m_events.at(row)->description();
0133     case Summary:
0134         return m_events.at(row)->summary();
0135     case LastModified:
0136         return m_events.at(row)->lastModified();
0137     case Location:
0138         return m_events.at(row)->location();
0139     case Categories:
0140         return m_events.at(row)->categories();
0141     case Priority:
0142         return m_events.at(row)->priority();
0143     case Created:
0144         return m_events.at(row)->created();
0145     case Secrecy:
0146         return m_events.at(row)->secrecy();
0147     case EventEndDt:
0148         return endDtTime;
0149     case ScheduleEndDt: {
0150         applyTimeZone(endDtTime, calendarTz);
0151         return endDtTime;
0152     }
0153     case Transparency:
0154         return m_events.at(row)->transparency();
0155     case RepeatPeriodType:
0156         return repeatPeriodType(row);
0157     case RepeatEvery:
0158         return repeatEvery(row);
0159     case RepeatStopAfter:
0160         return repeatStopAfter(row);
0161     case IsRepeating:
0162         return m_events.at(row)->recurs();
0163     case EventCategories:
0164         return m_events.at(row)->categoriesStr();
0165     case Url:
0166         return m_events.at(row)->url();
0167     case ShiftedStartEndDt: {
0168         // Shift and convert time to the time zone of the conference
0169         applyTimeZone(startDtTime, calendarTz);
0170         applyTimeZone(endDtTime, calendarTz);
0171 
0172         return formatStartEndDt(startDtTime, endDtTime, allDay);
0173     }
0174     case ShiftedStartEndDtLocal: {
0175         // Shift time to the time zone of the conference and convert to the system time zone
0176         startDtTime = startDtTime.toTimeZone(QTimeZone::systemTimeZone());
0177         endDtTime = endDtTime.toTimeZone(QTimeZone::systemTimeZone());
0178 
0179         return formatStartEndDt(startDtTime, endDtTime, allDay);
0180     }
0181     case ShiftedStartEndTime: {
0182         // Shift time to the time zone of the conference
0183         applyTimeZone(startDtTime, calendarTz);
0184         applyTimeZone(endDtTime, calendarTz);
0185 
0186         return formatStartEndTime(startDtTime, endDtTime);
0187     }
0188     case ShiftedStartEndTimeLocal: {
0189         // Shift time to the conference time zone and convert time to the system time zone
0190         startDtTime = startDtTime.toTimeZone(QTimeZone::systemTimeZone());
0191         endDtTime = endDtTime.toTimeZone(QTimeZone::systemTimeZone());
0192 
0193         return formatStartEndTime(startDtTime, endDtTime);
0194     }
0195     case StartEndDt: {
0196         // Convert time to the time zone of the conference
0197         applyTimeZone(startDtTime, calendarTz);
0198         applyTimeZone(endDtTime, calendarTz);
0199 
0200         return formatStartEndDt(startDtTime, endDtTime, allDay);
0201     }
0202     case StartEndDtLocal: {
0203         //Convert time to the system time zone
0204         startDtTime = startDtTime.toTimeZone(QTimeZone::systemTimeZone());
0205         endDtTime = endDtTime.toTimeZone(QTimeZone::systemTimeZone());
0206 
0207         return formatStartEndDt(startDtTime, endDtTime, allDay);
0208     }
0209     case Overlapping:
0210         return overlappingEvents(row);
0211     case ConferenceTzId: {
0212         return calendarTz.id();
0213     }
0214     default:
0215         return QVariant {};
0216     }
0217 }
0218 
0219 int EventModel::rowCount(const QModelIndex &parent) const
0220 {
0221     if (parent.isValid()) {
0222         return 0;
0223     }
0224 
0225     return m_events.count();
0226 }
0227 
0228 void EventModel::loadEvents()
0229 {
0230     beginResetModel();
0231     m_events.clear();
0232 
0233     if (m_local_calendar != nullptr && m_local_calendar->memorycalendar() != nullptr && m_filterdt.isValid()) {
0234         auto filterTz = m_settings_controller->displayInLocalTimezone() ? QTimeZone::systemTimeZone() : m_local_calendar->memorycalendar()->timeZone();
0235         auto dayEvents = m_local_calendar->memorycalendar()->rawEvents(m_filterdt, m_filterdt, filterTz, true);
0236         m_events = KCalendarCore::Calendar::sortEvents(std::move(dayEvents), KCalendarCore::EventSortField::EventSortStartDate, KCalendarCore::SortDirection::SortDirectionAscending);
0237     }
0238 
0239     if (m_local_calendar != nullptr && m_local_calendar->memorycalendar() != nullptr && m_filterdt.isNull()) {
0240         m_events = m_local_calendar->memorycalendar()->rawEvents(KCalendarCore::EventSortStartDate, KCalendarCore::SortDirectionAscending);
0241     }
0242 
0243     if (m_local_calendar != nullptr && !(m_category.isEmpty())) {
0244         QStringList categories {m_category};
0245         KCalendarCore::CalFilter filter {};
0246         filter.setCategoryList(categories);
0247         filter.setCriteria(KCalendarCore::CalFilter::ShowCategories);
0248         filter.apply(&m_events);
0249     }
0250 
0251     endResetModel();
0252 }
0253 
0254 int EventModel::repeatEvery(const int idx) const
0255 {
0256     if (!(m_events.at(idx)->recurs())) {
0257         return 0;
0258     }
0259 
0260     return m_events.at(idx)->recurrence()->frequency();
0261 }
0262 
0263 int EventModel::repeatStopAfter(const int idx) const
0264 {
0265 
0266     if (!(m_events.at(idx)->recurs())) {
0267         return -1;
0268     }
0269 
0270     return m_events.at(idx)->recurrence()->duration();
0271 }
0272 
0273 ushort EventModel::repeatPeriodType(const int idx) const
0274 {
0275     if (!(m_events.at(idx)->recurs())) {
0276         return KCalendarCore::Recurrence::rNone;
0277     }
0278 
0279     return m_events.at(idx)->recurrence()->recurrenceType();
0280 }
0281 
0282 void EventModel::setEventCategory(const QString &category)
0283 {
0284     if (m_category != category) {
0285         m_category = category;
0286         Q_EMIT eventCategoryChanged();
0287     }
0288 }
0289 
0290 QString EventModel::eventCategory() const
0291 {
0292     return m_category;
0293 }
0294 
0295 int EventModel::overlappingEvents(const int idx) const
0296 {
0297     int cnt = 0;
0298 
0299     for (const auto &e : m_events) {
0300         if ((m_events.at(idx)->dtStart() < e->dtEnd()) && (m_events.at(idx)->dtEnd() > e->dtStart()) && (m_events.at(idx)->uid() != e->uid())) {
0301             ++cnt;
0302         }
0303     }
0304 
0305     return cnt;
0306 }
0307 
0308 QString EventModel::formatStartEndTime(const QDateTime &startDtTime, const QDateTime &endDtTime) const
0309 {
0310     if (startDtTime.date() == endDtTime.date()) {
0311         return QString {"%1 - %2"}.arg(startDtTime.time().toString("hh:mm"), endDtTime.time().toString("hh:mm"));
0312     }
0313 
0314     auto displayStartDtTime = QString {"%1 %2"}.arg(startDtTime.date().toString("ddd d MMM yyyy"), startDtTime.time().toString("hh:mm"));
0315     auto displayEndDtTime = QString {"%1 %2"}.arg(endDtTime.date().toString("ddd d MMM yyyy"), endDtTime.time().toString("hh:mm"));
0316 
0317     return QString {"%1 - %2 %3"}.arg(displayStartDtTime, displayEndDtTime, startDtTime.timeZoneAbbreviation());
0318 }
0319 
0320 QString EventModel::formatStartEndDt(const QDateTime &startDtTime, const QDateTime &endDtTime, bool allDay) const
0321 {
0322     if (allDay) {
0323         return startDtTime.date().toString("ddd d MMM yyyy");
0324     }
0325 
0326     if (startDtTime.date() == endDtTime.date()) {
0327 
0328         auto displayDt = startDtTime.date().toString("ddd d MMM yyyy");
0329         auto displayTime = QString {"%1 - %2"}.arg(startDtTime.time().toString("hh:mm"), endDtTime.time().toString("hh:mm"));
0330 
0331         return QString {"%1 %2 %3"}.arg(displayDt, displayTime, startDtTime.timeZoneAbbreviation());
0332     }
0333 
0334     auto displayStartDtTime = QString {"%1 %2"}.arg(startDtTime.date().toString("ddd d MMM yyyy"), startDtTime.time().toString("hh:mm"));
0335     auto displayEndDtTime = QString {"%1 %2"}.arg(endDtTime.date().toString("ddd d MMM yyyy"), endDtTime.time().toString("hh:mm"));
0336 
0337     return QString {"%1 - %2 %3"}.arg(displayStartDtTime, displayEndDtTime, startDtTime.timeZoneAbbreviation());
0338 }
0339 
0340 #include "moc_eventmodel.cpp"