Warning, file /frameworks/kcalendarcore/src/incidencebase.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 This file is part of the kcalcore library. 0003 0004 SPDX-FileCopyrightText: 2001,2004 Cornelius Schumacher <schumacher@kde.org> 0005 SPDX-FileCopyrightText: 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com> 0006 SPDX-FileCopyrightText: 2009 Nokia Corporation and/or its subsidiary(-ies). All rights reserved. 0007 SPDX-FileContributor: Alvaro Manera <alvaro.manera@nokia.com> 0008 0009 SPDX-License-Identifier: LGPL-2.0-or-later 0010 */ 0011 /** 0012 @file 0013 This file is part of the API for handling calendar data and 0014 defines the IncidenceBase class. 0015 0016 @brief 0017 An abstract base class that provides a common base for all calendar incidence 0018 classes. 0019 0020 @author Cornelius Schumacher \<schumacher@kde.org\> 0021 @author Reinhold Kainhofer \<reinhold@kainhofer.com\> 0022 */ 0023 0024 #include "incidencebase.h" 0025 #include "incidencebase_p.h" 0026 #include "calformat.h" 0027 #include "utils_p.h" 0028 #include "visitor.h" 0029 0030 #include "kcalendarcore_debug.h" 0031 #include <QTime> 0032 0033 #include <QStringList> 0034 0035 #define KCALCORE_MAGIC_NUMBER 0xCA1C012E 0036 #define KCALCORE_SERIALIZATION_VERSION 1 0037 0038 using namespace KCalendarCore; 0039 0040 //@cond PRIVATE 0041 void IncidenceBasePrivate::init(const IncidenceBasePrivate &other) 0042 { 0043 mLastModified = other.mLastModified; 0044 mDtStart = other.mDtStart; 0045 mOrganizer = other.mOrganizer; 0046 mUid = other.mUid; 0047 mDuration = other.mDuration; 0048 mAllDay = other.mAllDay; 0049 mHasDuration = other.mHasDuration; 0050 0051 mComments = other.mComments; 0052 mContacts = other.mContacts; 0053 0054 mAttendees = other.mAttendees; 0055 mAttendees.reserve(other.mAttendees.count()); 0056 mUrl = other.mUrl; 0057 } 0058 0059 //@endcond 0060 0061 #if KCALENDARCORE_BUILD_DEPRECATED_SINCE(5, 91) 0062 IncidenceBase::IncidenceBase() 0063 : d_ptr(new KCalendarCore::IncidenceBasePrivate) 0064 { 0065 mReadOnly = false; 0066 setUid(CalFormat::createUniqueId()); 0067 } 0068 0069 IncidenceBase::IncidenceBase(const IncidenceBase &i) 0070 : CustomProperties(i) 0071 , d_ptr(new KCalendarCore::IncidenceBasePrivate(*i.d_ptr)) 0072 { 0073 mReadOnly = i.mReadOnly; 0074 } 0075 #endif 0076 0077 IncidenceBase::IncidenceBase(IncidenceBasePrivate *p) 0078 : d_ptr(p) 0079 { 0080 mReadOnly = false; 0081 setUid(CalFormat::createUniqueId()); 0082 } 0083 0084 IncidenceBase::IncidenceBase(const IncidenceBase &i, IncidenceBasePrivate *p) 0085 : CustomProperties(i) 0086 , d_ptr(p) 0087 { 0088 mReadOnly = i.mReadOnly; 0089 } 0090 0091 IncidenceBase::~IncidenceBase() 0092 { 0093 delete d_ptr; 0094 } 0095 0096 IncidenceBase &IncidenceBase::operator=(const IncidenceBase &other) 0097 { 0098 Q_ASSERT(type() == other.type()); 0099 0100 startUpdates(); 0101 0102 // assign is virtual, will call the derived class's 0103 IncidenceBase &ret = assign(other); 0104 endUpdates(); 0105 return ret; 0106 } 0107 0108 IncidenceBase &IncidenceBase::assign(const IncidenceBase &other) 0109 { 0110 CustomProperties::operator=(other); 0111 d_ptr->init(*other.d_ptr); 0112 mReadOnly = other.mReadOnly; 0113 d_ptr->mDirtyFields.clear(); 0114 d_ptr->mDirtyFields.insert(FieldUnknown); 0115 return *this; 0116 } 0117 0118 bool IncidenceBase::operator==(const IncidenceBase &i2) const 0119 { 0120 if (i2.type() != type()) { 0121 return false; 0122 } else { 0123 // equals is virtual, so here we're calling the derived class method 0124 return equals(i2); 0125 } 0126 } 0127 0128 bool IncidenceBase::operator!=(const IncidenceBase &i2) const 0129 { 0130 return !operator==(i2); 0131 } 0132 0133 bool IncidenceBase::equals(const IncidenceBase &other) const 0134 { 0135 if (attendees().count() != other.attendees().count()) { 0136 // qCDebug(KCALCORE_LOG) << "Attendee count is different"; 0137 return false; 0138 } 0139 0140 // TODO Does the order of attendees in the list really matter? 0141 // Please delete this comment if you know it's ok, kthx 0142 const Attendee::List list = attendees(); 0143 const Attendee::List otherList = other.attendees(); 0144 0145 if (list.size() != otherList.size()) { 0146 return false; 0147 } 0148 0149 auto [it1, it2] = std::mismatch(list.cbegin(), list.cend(), otherList.cbegin(), otherList.cend()); 0150 0151 // Checking the iterator from one list only, since we've already checked 0152 // they are the same size 0153 if (it1 != list.cend()) { 0154 // qCDebug(KCALCORE_LOG) << "Attendees are different"; 0155 return false; 0156 } 0157 0158 if (!CustomProperties::operator==(other)) { 0159 // qCDebug(KCALCORE_LOG) << "Properties are different"; 0160 return false; 0161 } 0162 0163 // Don't compare lastModified, otherwise the operator is not 0164 // of much use. We are not comparing for identity, after all. 0165 // no need to compare mObserver 0166 0167 bool a = identical(dtStart(), other.dtStart()); 0168 bool b = organizer() == other.organizer(); 0169 bool c = uid() == other.uid(); 0170 bool d = allDay() == other.allDay(); 0171 bool e = duration() == other.duration(); 0172 bool f = hasDuration() == other.hasDuration(); 0173 bool g = url() == other.url(); 0174 0175 // qCDebug(KCALCORE_LOG) << a << b << c << d << e << f << g; 0176 return a && b && c && d && e && f && g; 0177 } 0178 0179 bool IncidenceBase::accept(Visitor &v, const IncidenceBase::Ptr &incidence) 0180 { 0181 Q_UNUSED(v); 0182 Q_UNUSED(incidence); 0183 return false; 0184 } 0185 0186 void IncidenceBase::setUid(const QString &uid) 0187 { 0188 if (d_ptr->mUid != uid) { 0189 update(); 0190 d_ptr->mUid = uid; 0191 d_ptr->mDirtyFields.insert(FieldUid); 0192 updated(); 0193 } 0194 } 0195 0196 QString IncidenceBase::uid() const 0197 { 0198 return d_ptr->mUid; 0199 } 0200 0201 void IncidenceBase::setLastModified(const QDateTime &lm) 0202 { 0203 // DON'T! updated() because we call this from 0204 // Calendar::updateEvent(). 0205 0206 d_ptr->mDirtyFields.insert(FieldLastModified); 0207 0208 // Convert to UTC and remove milliseconds part. 0209 QDateTime current = lm.toUTC(); 0210 QTime t = current.time(); 0211 t.setHMS(t.hour(), t.minute(), t.second(), 0); 0212 current.setTime(t); 0213 0214 d_ptr->mLastModified = current; 0215 } 0216 0217 QDateTime IncidenceBase::lastModified() const 0218 { 0219 return d_ptr->mLastModified; 0220 } 0221 0222 void IncidenceBase::setOrganizer(const Person &organizer) 0223 { 0224 update(); 0225 // we don't check for readonly here, because it is 0226 // possible that by setting the organizer we are changing 0227 // the event's readonly status... 0228 d_ptr->mOrganizer = organizer; 0229 0230 d_ptr->mDirtyFields.insert(FieldOrganizer); 0231 0232 updated(); 0233 } 0234 0235 void IncidenceBase::setOrganizer(const QString &o) 0236 { 0237 QString mail(o); 0238 if (mail.startsWith(QLatin1String("MAILTO:"), Qt::CaseInsensitive)) { 0239 mail.remove(0, 7); 0240 } 0241 0242 // split the string into full name plus email. 0243 const Person organizer = Person::fromFullName(mail); 0244 setOrganizer(organizer); 0245 } 0246 0247 Person IncidenceBase::organizer() const 0248 { 0249 return d_ptr->mOrganizer; 0250 } 0251 0252 void IncidenceBase::setReadOnly(bool readOnly) 0253 { 0254 mReadOnly = readOnly; 0255 } 0256 0257 bool IncidenceBase::isReadOnly() const 0258 { 0259 return mReadOnly; 0260 } 0261 0262 void IncidenceBase::setDtStart(const QDateTime &dtStart) 0263 { 0264 // if ( mReadOnly ) return; 0265 0266 if (!dtStart.isValid() && type() != IncidenceBase::TypeTodo) { 0267 qCWarning(KCALCORE_LOG) << "Invalid dtStart"; 0268 } 0269 0270 if (!identical(d_ptr->mDtStart, dtStart)) { 0271 update(); 0272 d_ptr->mDtStart = dtStart; 0273 d_ptr->mDirtyFields.insert(FieldDtStart); 0274 updated(); 0275 } 0276 } 0277 0278 QDateTime IncidenceBase::dtStart() const 0279 { 0280 return d_ptr->mDtStart; 0281 } 0282 0283 bool IncidenceBase::allDay() const 0284 { 0285 return d_ptr->mAllDay; 0286 } 0287 0288 void IncidenceBase::setAllDay(bool f) 0289 { 0290 if (mReadOnly || f == d_ptr->mAllDay) { 0291 return; 0292 } 0293 update(); 0294 d_ptr->mAllDay = f; 0295 if (d_ptr->mDtStart.isValid()) { 0296 d_ptr->mDirtyFields.insert(FieldDtStart); 0297 } 0298 updated(); 0299 } 0300 0301 void IncidenceBase::shiftTimes(const QTimeZone &oldZone, const QTimeZone &newZone) 0302 { 0303 update(); 0304 d_ptr->mDtStart = d_ptr->mDtStart.toTimeZone(oldZone); 0305 d_ptr->mDtStart.setTimeZone(newZone); 0306 d_ptr->mDirtyFields.insert(FieldDtStart); 0307 updated(); 0308 } 0309 0310 void IncidenceBase::addComment(const QString &comment) 0311 { 0312 update(); 0313 d_ptr->mComments += comment; 0314 d_ptr->mDirtyFields.insert(FieldComment); 0315 updated(); 0316 } 0317 0318 bool IncidenceBase::removeComment(const QString &comment) 0319 { 0320 auto it = std::find(d_ptr->mComments.begin(), d_ptr->mComments.end(), comment); 0321 bool found = it != d_ptr->mComments.end(); 0322 if (found) { 0323 update(); 0324 d_ptr->mComments.erase(it); 0325 d_ptr->mDirtyFields.insert(FieldComment); 0326 updated(); 0327 } 0328 return found; 0329 } 0330 0331 void IncidenceBase::clearComments() 0332 { 0333 update(); 0334 d_ptr->mDirtyFields.insert(FieldComment); 0335 d_ptr->mComments.clear(); 0336 updated(); 0337 } 0338 0339 QStringList IncidenceBase::comments() const 0340 { 0341 return d_ptr->mComments; 0342 } 0343 0344 void IncidenceBase::addContact(const QString &contact) 0345 { 0346 if (!contact.isEmpty()) { 0347 update(); 0348 d_ptr->mContacts += contact; 0349 d_ptr->mDirtyFields.insert(FieldContact); 0350 updated(); 0351 } 0352 } 0353 0354 bool IncidenceBase::removeContact(const QString &contact) 0355 { 0356 auto it = std::find(d_ptr->mContacts.begin(), d_ptr->mContacts.end(), contact); 0357 bool found = it != d_ptr->mContacts.end(); 0358 if (found) { 0359 update(); 0360 d_ptr->mContacts.erase(it); 0361 d_ptr->mDirtyFields.insert(FieldContact); 0362 updated(); 0363 } 0364 return found; 0365 } 0366 0367 void IncidenceBase::clearContacts() 0368 { 0369 update(); 0370 d_ptr->mDirtyFields.insert(FieldContact); 0371 d_ptr->mContacts.clear(); 0372 updated(); 0373 } 0374 0375 QStringList IncidenceBase::contacts() const 0376 { 0377 return d_ptr->mContacts; 0378 } 0379 0380 void IncidenceBase::addAttendee(const Attendee &a, bool doupdate) 0381 { 0382 if (a.isNull() || mReadOnly) { 0383 return; 0384 } 0385 Q_ASSERT(!a.uid().isEmpty()); 0386 0387 if (doupdate) { 0388 update(); 0389 } 0390 0391 d_ptr->mAttendees.append(a); 0392 if (doupdate) { 0393 d_ptr->mDirtyFields.insert(FieldAttendees); 0394 updated(); 0395 } 0396 } 0397 0398 Attendee::List IncidenceBase::attendees() const 0399 { 0400 return d_ptr->mAttendees; 0401 } 0402 0403 int IncidenceBase::attendeeCount() const 0404 { 0405 return d_ptr->mAttendees.count(); 0406 } 0407 0408 void IncidenceBase::setAttendees(const Attendee::List &attendees, bool doUpdate) 0409 { 0410 if (mReadOnly) { 0411 return; 0412 } 0413 0414 // don't simply assign, we need the logic in addAttendee here too 0415 clearAttendees(); 0416 0417 if (doUpdate) { 0418 update(); 0419 } 0420 0421 d_ptr->mAttendees.reserve(attendees.size()); 0422 for (const auto &a : attendees) { 0423 addAttendee(a, false); 0424 } 0425 0426 if (doUpdate) { 0427 d_ptr->mDirtyFields.insert(FieldAttendees); 0428 updated(); 0429 } 0430 } 0431 0432 void IncidenceBase::clearAttendees() 0433 { 0434 if (mReadOnly) { 0435 return; 0436 } 0437 update(); 0438 d_ptr->mDirtyFields.insert(FieldAttendees); 0439 d_ptr->mAttendees.clear(); 0440 updated(); 0441 } 0442 0443 Attendee IncidenceBase::attendeeByMail(const QString &email) const 0444 { 0445 auto it = std::find_if(d_ptr->mAttendees.cbegin(), d_ptr->mAttendees.cend(), [&email](const Attendee &att) { 0446 return att.email() == email; 0447 }); 0448 0449 return it != d_ptr->mAttendees.cend() ? *it : Attendee{}; 0450 } 0451 0452 Attendee IncidenceBase::attendeeByMails(const QStringList &emails, const QString &email) const 0453 { 0454 QStringList mails = emails; 0455 if (!email.isEmpty()) { 0456 mails.append(email); 0457 } 0458 0459 auto it = std::find_if(d_ptr->mAttendees.cbegin(), d_ptr->mAttendees.cend(), [&mails](const Attendee &a) { 0460 return mails.contains(a.email()); 0461 }); 0462 0463 return it != d_ptr->mAttendees.cend() ? *it : Attendee{}; 0464 } 0465 0466 Attendee IncidenceBase::attendeeByUid(const QString &uid) const 0467 { 0468 auto it = std::find_if(d_ptr->mAttendees.cbegin(), d_ptr->mAttendees.cend(), [&uid](const Attendee &a) { 0469 return a.uid() == uid; 0470 }); 0471 return it != d_ptr->mAttendees.cend() ? *it : Attendee{}; 0472 } 0473 0474 void IncidenceBase::setDuration(const Duration &duration) 0475 { 0476 update(); 0477 d_ptr->mDuration = duration; 0478 setHasDuration(true); 0479 d_ptr->mDirtyFields.insert(FieldDuration); 0480 updated(); 0481 } 0482 0483 Duration IncidenceBase::duration() const 0484 { 0485 return d_ptr->mDuration; 0486 } 0487 0488 void IncidenceBase::setHasDuration(bool hasDuration) 0489 { 0490 d_ptr->mHasDuration = hasDuration; 0491 } 0492 0493 bool IncidenceBase::hasDuration() const 0494 { 0495 return d_ptr->mHasDuration; 0496 } 0497 0498 void IncidenceBase::setUrl(const QUrl &url) 0499 { 0500 update(); 0501 d_ptr->mDirtyFields.insert(FieldUrl); 0502 d_ptr->mUrl = url; 0503 updated(); 0504 } 0505 0506 QUrl IncidenceBase::url() const 0507 { 0508 return d_ptr->mUrl; 0509 } 0510 0511 void IncidenceBase::registerObserver(IncidenceBase::IncidenceObserver *observer) 0512 { 0513 if (observer && !d_ptr->mObservers.contains(observer)) { 0514 d_ptr->mObservers.append(observer); 0515 } 0516 } 0517 0518 void IncidenceBase::unRegisterObserver(IncidenceBase::IncidenceObserver *observer) 0519 { 0520 d_ptr->mObservers.removeAll(observer); 0521 } 0522 0523 void IncidenceBase::update() 0524 { 0525 if (!d_ptr->mUpdateGroupLevel) { 0526 d_ptr->mUpdatedPending = true; 0527 const auto rid = recurrenceId(); 0528 for (IncidenceObserver *o : std::as_const(d_ptr->mObservers)) { 0529 o->incidenceUpdate(uid(), rid); 0530 } 0531 } 0532 } 0533 0534 void IncidenceBase::updated() 0535 { 0536 if (d_ptr->mUpdateGroupLevel) { 0537 d_ptr->mUpdatedPending = true; 0538 } else { 0539 const auto rid = recurrenceId(); 0540 for (IncidenceObserver *o : std::as_const(d_ptr->mObservers)) { 0541 o->incidenceUpdated(uid(), rid); 0542 } 0543 } 0544 } 0545 0546 void IncidenceBase::startUpdates() 0547 { 0548 update(); 0549 ++d_ptr->mUpdateGroupLevel; 0550 } 0551 0552 void IncidenceBase::endUpdates() 0553 { 0554 if (d_ptr->mUpdateGroupLevel > 0) { 0555 if (--d_ptr->mUpdateGroupLevel == 0 && d_ptr->mUpdatedPending) { 0556 d_ptr->mUpdatedPending = false; 0557 updated(); 0558 } 0559 } 0560 } 0561 0562 void IncidenceBase::customPropertyUpdate() 0563 { 0564 update(); 0565 } 0566 0567 void IncidenceBase::customPropertyUpdated() 0568 { 0569 updated(); 0570 } 0571 0572 QDateTime IncidenceBase::recurrenceId() const 0573 { 0574 return QDateTime(); 0575 } 0576 0577 void IncidenceBase::resetDirtyFields() 0578 { 0579 d_ptr->mDirtyFields.clear(); 0580 } 0581 0582 QSet<IncidenceBase::Field> IncidenceBase::dirtyFields() const 0583 { 0584 return d_ptr->mDirtyFields; 0585 } 0586 0587 void IncidenceBase::setFieldDirty(IncidenceBase::Field field) 0588 { 0589 d_ptr->mDirtyFields.insert(field); 0590 } 0591 0592 QUrl IncidenceBase::uri() const 0593 { 0594 return QUrl(QStringLiteral("urn:x-ical:") + uid()); 0595 } 0596 0597 void IncidenceBase::setDirtyFields(const QSet<IncidenceBase::Field> &dirtyFields) 0598 { 0599 d_ptr->mDirtyFields = dirtyFields; 0600 } 0601 0602 void IncidenceBase::serialize(QDataStream &out) const 0603 { 0604 Q_UNUSED(out); 0605 } 0606 0607 void IncidenceBase::deserialize(QDataStream &in) 0608 { 0609 Q_UNUSED(in); 0610 } 0611 0612 /** static */ 0613 quint32 IncidenceBase::magicSerializationIdentifier() 0614 { 0615 return KCALCORE_MAGIC_NUMBER; 0616 } 0617 0618 bool KCalendarCore::identical(QDateTime dt1, QDateTime dt2) 0619 { 0620 return dt1 == dt2 && dt1.timeSpec() == dt2.timeSpec() && dt1.timeZone() == dt2.timeZone(); 0621 } 0622 0623 QDataStream &KCalendarCore::operator<<(QDataStream &out, const KCalendarCore::IncidenceBase::Ptr &i) 0624 { 0625 if (!i) { 0626 return out; 0627 } 0628 0629 out << static_cast<quint32>(KCALCORE_MAGIC_NUMBER); // Magic number to identify KCalendarCore data 0630 out << static_cast<quint32>(KCALCORE_SERIALIZATION_VERSION); 0631 out << static_cast<qint32>(i->type()); 0632 0633 out << *(static_cast<CustomProperties *>(i.data())); 0634 serializeQDateTimeAsKDateTime(out, i->d_ptr->mLastModified); 0635 serializeQDateTimeAsKDateTime(out, i->d_ptr->mDtStart); 0636 out << i->organizer() << i->d_ptr->mUid << i->d_ptr->mDuration << i->d_ptr->mAllDay << i->d_ptr->mHasDuration << i->d_ptr->mComments << i->d_ptr->mContacts 0637 << (qint32)i->d_ptr->mAttendees.count() << i->d_ptr->mUrl; 0638 0639 for (const Attendee &attendee : std::as_const(i->d_ptr->mAttendees)) { 0640 out << attendee; 0641 } 0642 0643 // Serialize the sub-class data. 0644 i->serialize(out); 0645 0646 return out; 0647 } 0648 0649 QDataStream &KCalendarCore::operator>>(QDataStream &in, KCalendarCore::IncidenceBase::Ptr &i) 0650 { 0651 if (!i) { 0652 return in; 0653 } 0654 0655 qint32 attendeeCount; 0656 qint32 type; 0657 quint32 magic; 0658 quint32 version; 0659 0660 in >> magic; 0661 0662 if (magic != KCALCORE_MAGIC_NUMBER) { 0663 qCWarning(KCALCORE_LOG) << "Invalid magic on serialized data"; 0664 return in; 0665 } 0666 0667 in >> version; 0668 0669 if (version > KCALCORE_MAGIC_NUMBER) { 0670 qCWarning(KCALCORE_LOG) << "Invalid version on serialized data"; 0671 return in; 0672 } 0673 0674 in >> type; 0675 0676 in >> *(static_cast<CustomProperties *>(i.data())); 0677 deserializeKDateTimeAsQDateTime(in, i->d_ptr->mLastModified); 0678 deserializeKDateTimeAsQDateTime(in, i->d_ptr->mDtStart); 0679 in >> i->d_ptr->mOrganizer >> i->d_ptr->mUid >> i->d_ptr->mDuration >> i->d_ptr->mAllDay >> i->d_ptr->mHasDuration >> i->d_ptr->mComments >> i->d_ptr->mContacts >> attendeeCount 0680 >> i->d_ptr->mUrl; 0681 0682 i->d_ptr->mAttendees.clear(); 0683 i->d_ptr->mAttendees.reserve(attendeeCount); 0684 for (int it = 0; it < attendeeCount; it++) { 0685 Attendee attendee; 0686 in >> attendee; 0687 i->d_ptr->mAttendees.append(attendee); 0688 } 0689 0690 // Deserialize the sub-class data. 0691 i->deserialize(in); 0692 0693 return in; 0694 } 0695 0696 IncidenceBase::IncidenceObserver::~IncidenceObserver() 0697 { 0698 } 0699 0700 QVariantList IncidenceBase::attendeesVariant() const 0701 { 0702 QVariantList l; 0703 l.reserve(d_ptr->mAttendees.size()); 0704 std::transform(d_ptr->mAttendees.begin(), d_ptr->mAttendees.end(), std::back_inserter(l), [](const Attendee &a) { 0705 return QVariant::fromValue(a); 0706 }); 0707 return l; 0708 } 0709 0710 #include "moc_incidencebase.cpp"