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"