File indexing completed on 2024-04-21 03:52:46

0001 /*
0002   This file is part of the kcalcore library.
0003 
0004   SPDX-FileCopyrightText: 2001 Cornelius Schumacher <schumacher@kde.org>
0005 
0006   SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 /**
0009   @file
0010   This file is part of the API for handling calendar data and
0011   defines the Event class.
0012 
0013   @brief
0014   This class provides an Event in the sense of RFC2445.
0015 
0016   @author Cornelius Schumacher \<schumacher@kde.org\>
0017 */
0018 
0019 #include "event.h"
0020 #include "incidence_p.h"
0021 #include "kcalendarcore_debug.h"
0022 #include "utils_p.h"
0023 #include "visitor.h"
0024 
0025 #include <QDate>
0026 
0027 using namespace KCalendarCore;
0028 
0029 /**
0030   Private class that helps to provide binary compatibility between releases.
0031   @internal
0032 */
0033 //@cond PRIVATE
0034 class KCalendarCore::EventPrivate : public IncidencePrivate
0035 {
0036 public:
0037     EventPrivate() = default;
0038     explicit EventPrivate(const Incidence &);
0039     bool validStatus(Incidence::Status) override;
0040 
0041     QDateTime mDtEnd;
0042     Event::Transparency mTransparency = Event::Opaque;
0043     bool mMultiDayValid = false;
0044     bool mMultiDay = false;
0045 };
0046 
0047 // Copy IncidencePrivate and IncidenceBasePrivate members,
0048 // but default-initialize EventPrivate members.
0049 EventPrivate::EventPrivate(const Incidence &other)
0050     : IncidencePrivate(other)
0051 {
0052 }
0053 
0054 bool EventPrivate::validStatus(Incidence::Status status)
0055 {
0056     constexpr unsigned validSet
0057         = 1u << Incidence::StatusNone
0058         | 1u << Incidence::StatusTentative
0059         | 1u << Incidence::StatusConfirmed
0060         | 1u << Incidence::StatusCanceled;
0061     return validSet & (1u << status);
0062 }
0063 //@endcond
0064 
0065 Event::Event()
0066     : Incidence(new EventPrivate)
0067 {
0068 }
0069 
0070 Event::Event(const Event &other)
0071     : Incidence(other, new EventPrivate(*(other.d_func())))
0072 {
0073 }
0074 
0075 Event::Event(const Incidence &other)
0076     : Incidence(other, new EventPrivate(other))
0077 {
0078 }
0079 
0080 Event::~Event() = default;
0081 
0082 Event *Event::clone() const
0083 {
0084     return new Event(*this);
0085 }
0086 
0087 IncidenceBase &Event::assign(const IncidenceBase &other)
0088 {
0089     Q_D(Event);
0090     if (&other != this) {
0091         Incidence::assign(other);
0092         const auto o = static_cast<const Event *>(&other)->d_func();
0093         d->mDtEnd = o->mDtEnd;
0094         d->mTransparency = o->mTransparency;
0095         d->mMultiDayValid = o->mMultiDayValid;
0096         d->mMultiDay = o->mMultiDay;
0097     }
0098     return *this;
0099 }
0100 
0101 bool Event::equals(const IncidenceBase &event) const
0102 {
0103     if (!Incidence::equals(event)) {
0104         return false;
0105     } else {
0106         // If they weren't the same type IncidenceBase::equals would had returned false already
0107         const Event *e = static_cast<const Event *>(&event);
0108         return identical(dtEnd(), e->dtEnd()) && transparency() == e->transparency();
0109     }
0110 }
0111 
0112 Incidence::IncidenceType Event::type() const
0113 {
0114     return TypeEvent;
0115 }
0116 
0117 QByteArray Event::typeStr() const
0118 {
0119     return QByteArrayLiteral("Event");
0120 }
0121 
0122 void Event::setDtStart(const QDateTime &dt)
0123 {
0124     Q_D(Event);
0125     d->mMultiDayValid = false;
0126     Incidence::setDtStart(dt);
0127 }
0128 
0129 void Event::setDtEnd(const QDateTime &dtEnd)
0130 {
0131     if (mReadOnly) {
0132         return;
0133     }
0134 
0135     Q_D(Event);
0136     if (!identical(d->mDtEnd, dtEnd) || hasDuration() == dtEnd.isValid()) {
0137         update();
0138         d->mDtEnd = dtEnd;
0139         d->mMultiDayValid = false;
0140         setHasDuration(!dtEnd.isValid());
0141         setFieldDirty(FieldDtEnd);
0142         updated();
0143     }
0144 }
0145 
0146 QDateTime Event::dtEnd() const
0147 {
0148     Q_D(const Event);
0149     if (d->mDtEnd.isValid()) {
0150         return d->mDtEnd;
0151     }
0152 
0153     if (hasDuration()) {
0154         if (allDay()) {
0155             // For all day events, dtEnd is always inclusive
0156             QDateTime end = duration().end(dtStart().addDays(-1));
0157             return end >= dtStart() ? end : dtStart();
0158         } else {
0159             return duration().end(dtStart());
0160         }
0161     }
0162 
0163     // It is valid for a VEVENT to be without a DTEND. See RFC2445, Sect4.6.1.
0164     // Be careful to use Event::dateEnd() as appropriate due to this possibility.
0165     return dtStart();
0166 }
0167 
0168 QDate Event::dateEnd() const
0169 {
0170     QDateTime end = dtEnd().toTimeZone(dtStart().timeZone());
0171     if (allDay()) {
0172         return end.date();
0173     } else {
0174         return end.addSecs(-1).date();
0175     }
0176 }
0177 
0178 bool Event::hasEndDate() const
0179 {
0180     Q_D(const Event);
0181     return d->mDtEnd.isValid();
0182 }
0183 
0184 bool Event::isMultiDay(const QTimeZone &zone) const
0185 {
0186     Q_D(const Event);
0187     // First off, if spec's not valid, we can check for cache
0188     if (!zone.isValid() && d->mMultiDayValid) {
0189         return d->mMultiDay;
0190     }
0191 
0192     // Not in cache -> do it the hard way
0193     QDateTime start;
0194     QDateTime end;
0195 
0196     if (!zone.isValid()) {
0197         start = dtStart();
0198         end = dtEnd();
0199     } else {
0200         start = dtStart().toTimeZone(zone);
0201         end = dtEnd().toTimeZone(zone);
0202     }
0203 
0204     bool multi = (start < end && start.date() != end.date());
0205 
0206     // End date is non inclusive
0207     // If we have an incidence that duration is one day and ends with a start of a new day
0208     // than it is not a multiday event
0209     if (multi && end.time() == QTime(0, 0, 0)) {
0210         multi = start.daysTo(end) > 1;
0211     }
0212 
0213     // Update the cache
0214     // Also update Cache if spec is invalid
0215     {
0216         auto d = static_cast<EventPrivate *>(d_ptr);
0217         d->mMultiDayValid = true;
0218         d->mMultiDay = multi;
0219     }
0220     return multi;
0221 }
0222 
0223 void Event::shiftTimes(const QTimeZone &oldZone, const QTimeZone &newZone)
0224 {
0225     Q_D(Event);
0226     Incidence::shiftTimes(oldZone, newZone);
0227     if (d->mDtEnd.isValid()) {
0228         update();
0229         d->mDtEnd = d->mDtEnd.toTimeZone(oldZone);
0230         d->mDtEnd.setTimeZone(newZone);
0231         setFieldDirty(FieldDtEnd);
0232         updated();
0233     }
0234 }
0235 
0236 void Event::setTransparency(Event::Transparency transparency)
0237 {
0238     if (mReadOnly) {
0239         return;
0240     }
0241     update();
0242     Q_D(Event);
0243     d->mTransparency = transparency;
0244     setFieldDirty(FieldTransparency);
0245     updated();
0246 }
0247 
0248 Event::Transparency Event::transparency() const
0249 {
0250     Q_D(const Event);
0251     return d->mTransparency;
0252 }
0253 
0254 void Event::setDuration(const Duration &duration)
0255 {
0256     // These both call update()/updated() and setFieldDirty().
0257     setDtEnd(QDateTime());
0258     Incidence::setDuration(duration);
0259 }
0260 
0261 void Event::setAllDay(bool allday)
0262 {
0263     if (allday != allDay() && !mReadOnly) {
0264         update();
0265         setFieldDirty(FieldDtEnd);
0266         Incidence::setAllDay(allday);
0267         updated();
0268     }
0269 }
0270 
0271 bool Event::accept(Visitor &v, const IncidenceBase::Ptr &incidence)
0272 {
0273     return v.visit(incidence.staticCast<Event>());
0274 }
0275 
0276 QDateTime Event::dateTime(DateTimeRole role) const
0277 {
0278     switch (role) {
0279     case RoleRecurrenceStart:
0280     case RoleAlarmStartOffset:
0281     case RoleStartTimeZone:
0282     case RoleSort:
0283         return dtStart();
0284     case RoleCalendarHashing:
0285         return !recurs() && !isMultiDay() ? dtStart() : QDateTime();
0286     case RoleAlarmEndOffset:
0287     case RoleEndTimeZone:
0288     case RoleEndRecurrenceBase:
0289     case RoleEnd:
0290     case RoleDisplayEnd:
0291         return dtEnd();
0292     case RoleDisplayStart:
0293         return dtStart();
0294     case RoleAlarm:
0295         if (alarms().isEmpty()) {
0296             return QDateTime();
0297         } else {
0298             Alarm::Ptr alarm = alarms().at(0);
0299             return alarm->hasStartOffset() ? dtStart() : dtEnd();
0300         }
0301     default:
0302         return QDateTime();
0303     }
0304 }
0305 
0306 void Event::setDateTime(const QDateTime &dateTime, DateTimeRole role)
0307 {
0308     switch (role) {
0309     case RoleDnD: {
0310         const qint64 duration = dtStart().secsTo(dtEnd());
0311 
0312         setDtStart(dateTime);
0313         setDtEnd(dateTime.addSecs(duration <= 0 ? 3600 : duration));
0314         break;
0315     }
0316     case RoleEnd:
0317         setDtEnd(dateTime);
0318         break;
0319     default:
0320         qCDebug(KCALCORE_LOG) << "Unhandled role" << role;
0321     }
0322 }
0323 
0324 void Event::virtual_hook(VirtualHook id, void *data)
0325 {
0326     Q_UNUSED(id);
0327     Q_UNUSED(data);
0328 }
0329 
0330 QLatin1String KCalendarCore::Event::mimeType() const
0331 {
0332     return Event::eventMimeType();
0333 }
0334 
0335 QLatin1String Event::eventMimeType()
0336 {
0337     return QLatin1String("application/x-vnd.akonadi.calendar.event");
0338 }
0339 
0340 QLatin1String Event::iconName(const QDateTime &) const
0341 {
0342     return QLatin1String("view-calendar-day");
0343 }
0344 
0345 void Event::serialize(QDataStream &out) const
0346 {
0347     Q_D(const Event);
0348     Incidence::serialize(out);
0349     serializeQDateTimeAsKDateTime(out, d->mDtEnd);
0350     out << hasEndDate() << static_cast<quint32>(d->mTransparency) << d->mMultiDayValid << d->mMultiDay;
0351 }
0352 
0353 void Event::deserialize(QDataStream &in)
0354 {
0355     Q_D(Event);
0356     Incidence::deserialize(in);
0357     bool hasEndDateDummy = true;
0358     deserializeKDateTimeAsQDateTime(in, d->mDtEnd);
0359     in >> hasEndDateDummy;
0360     quint32 transp;
0361     in >> transp;
0362     d->mTransparency = static_cast<Transparency>(transp);
0363     in >> d->mMultiDayValid >> d->mMultiDay;
0364 }
0365 
0366 bool Event::supportsGroupwareCommunication() const
0367 {
0368     return true;
0369 }
0370 
0371 #include "moc_event.cpp"