KCalendarCore

incidencebase.cpp
Go to the documentation of this file.
1/*
2 This file is part of the kcalcore library.
3
4 SPDX-FileCopyrightText: 2001,2004 Cornelius Schumacher <schumacher@kde.org>
5 SPDX-FileCopyrightText: 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
6 SPDX-FileCopyrightText: 2009 Nokia Corporation and/or its subsidiary(-ies). All rights reserved.
7 SPDX-FileContributor: Alvaro Manera <alvaro.manera@nokia.com>
8
9 SPDX-License-Identifier: LGPL-2.0-or-later
10*/
11/**
12 @file
13 This file is part of the API for handling calendar data and
14 defines the IncidenceBase class.
15
16 @brief
17 An abstract base class that provides a common base for all calendar incidence
18 classes.
19
20 @author Cornelius Schumacher <schumacher@kde.org>
21 @author Reinhold Kainhofer <reinhold@kainhofer.com>
22*/
23
24#include "incidencebase.h"
25#include "incidencebase_p.h"
26#include "calformat.h"
27#include "utils_p.h"
28#include "visitor.h"
29
30#include "kcalendarcore_debug.h"
31#include <QTime>
32
33#include <QStringList>
34
35#define KCALCORE_MAGIC_NUMBER 0xCA1C012E
36#define KCALCORE_SERIALIZATION_VERSION 1
37
38using namespace KCalendarCore;
39
40//@cond PRIVATE
41void IncidenceBasePrivate::init(const IncidenceBasePrivate &other)
42{
43 mLastModified = other.mLastModified;
44 mDtStart = other.mDtStart;
45 mOrganizer = other.mOrganizer;
46 mUid = other.mUid;
47 mDuration = other.mDuration;
48 mAllDay = other.mAllDay;
49 mHasDuration = other.mHasDuration;
50
51 mComments = other.mComments;
52 mContacts = other.mContacts;
53
54 mAttendees = other.mAttendees;
55 mAttendees.reserve(other.mAttendees.count());
56 mUrl = other.mUrl;
57}
58
59//@endcond
60
61IncidenceBase::IncidenceBase(IncidenceBasePrivate *p)
62 : d_ptr(p)
63{
64 mReadOnly = false;
66}
67
68IncidenceBase::IncidenceBase(const IncidenceBase &i, IncidenceBasePrivate *p)
70 , d_ptr(p)
71{
73}
74
76{
77 delete d_ptr;
78}
79
81{
82 Q_ASSERT(type() == other.type());
83
85
86 // assign is virtual, will call the derived class's
87 IncidenceBase &ret = assign(other);
88 endUpdates();
89 return ret;
90}
91
93{
95 d_ptr->init(*other.d_ptr);
96 mReadOnly = other.mReadOnly;
97 d_ptr->mDirtyFields.clear();
98 d_ptr->mDirtyFields.insert(FieldUnknown);
99 return *this;
100}
101
103{
104 if (i2.type() != type()) {
105 return false;
106 } else {
107 // equals is virtual, so here we're calling the derived class method
108 return equals(i2);
109 }
110}
111
113{
114 return !operator==(i2);
115}
116
117bool IncidenceBase::equals(const IncidenceBase &other) const
118{
119 if (attendees().count() != other.attendees().count()) {
120 // qCDebug(KCALCORE_LOG) << "Attendee count is different";
121 return false;
122 }
123
124 // TODO Does the order of attendees in the list really matter?
125 // Please delete this comment if you know it's ok, kthx
126 const Attendee::List list = attendees();
127 const Attendee::List otherList = other.attendees();
128
129 if (list.size() != otherList.size()) {
130 return false;
131 }
132
133 auto [it1, it2] = std::mismatch(list.cbegin(), list.cend(), otherList.cbegin(), otherList.cend());
134
135 // Checking the iterator from one list only, since we've already checked
136 // they are the same size
137 if (it1 != list.cend()) {
138 // qCDebug(KCALCORE_LOG) << "Attendees are different";
139 return false;
140 }
141
142 if (!CustomProperties::operator==(other)) {
143 // qCDebug(KCALCORE_LOG) << "Properties are different";
144 return false;
145 }
146
147 // Don't compare lastModified, otherwise the operator is not
148 // of much use. We are not comparing for identity, after all.
149 // no need to compare mObserver
150
151 bool a = identical(dtStart(), other.dtStart());
152 bool b = organizer() == other.organizer();
153 bool c = uid() == other.uid();
154 bool d = allDay() == other.allDay();
155 bool e = duration() == other.duration();
156 bool f = hasDuration() == other.hasDuration();
157 bool g = url() == other.url();
158
159 // qCDebug(KCALCORE_LOG) << a << b << c << d << e << f << g;
160 return a && b && c && d && e && f && g;
161}
162
164{
165 Q_UNUSED(v);
166 Q_UNUSED(incidence);
167 return false;
168}
169
171{
172 if (d_ptr->mUid != uid) {
173 update();
174 d_ptr->mUid = uid;
175 d_ptr->mDirtyFields.insert(FieldUid);
176 updated();
177 }
178}
179
180QString IncidenceBase::uid() const
181{
182 return d_ptr->mUid;
183}
184
186{
187 // DON'T! updated() because we call this from
188 // Calendar::updateEvent().
189
190 d_ptr->mDirtyFields.insert(FieldLastModified);
191
192 // Convert to UTC and remove milliseconds part.
193 QDateTime current = lm.toUTC();
194 QTime t = current.time();
195 t.setHMS(t.hour(), t.minute(), t.second(), 0);
196 current.setTime(t);
197
198 d_ptr->mLastModified = current;
199}
200
201QDateTime IncidenceBase::lastModified() const
202{
203 return d_ptr->mLastModified;
204}
205
207{
208 update();
209 // we don't check for readonly here, because it is
210 // possible that by setting the organizer we are changing
211 // the event's readonly status...
212 d_ptr->mOrganizer = organizer;
213
214 d_ptr->mDirtyFields.insert(FieldOrganizer);
215
216 updated();
217}
218
220{
221 QString mail(o);
222 if (mail.startsWith(QLatin1String("MAILTO:"), Qt::CaseInsensitive)) {
223 mail.remove(0, 7);
224 }
225
226 // split the string into full name plus email.
227 const Person organizer = Person::fromFullName(mail);
228 setOrganizer(organizer);
229}
230
231Person IncidenceBase::organizer() const
232{
233 return d_ptr->mOrganizer;
234}
235
236void IncidenceBase::setReadOnly(bool readOnly)
237{
238 mReadOnly = readOnly;
239}
240
242{
243 return mReadOnly;
244}
245
247{
248 // if ( mReadOnly ) return;
249
250 if (!dtStart.isValid() && type() != IncidenceBase::TypeTodo) {
251 qCWarning(KCALCORE_LOG) << "Invalid dtStart";
252 }
253
254 if (!identical(d_ptr->mDtStart, dtStart)) {
255 update();
256 d_ptr->mDtStart = dtStart;
257 d_ptr->mDirtyFields.insert(FieldDtStart);
258 updated();
259 }
260}
261
262QDateTime IncidenceBase::dtStart() const
263{
264 return d_ptr->mDtStart;
265}
266
267bool IncidenceBase::allDay() const
268{
269 return d_ptr->mAllDay;
270}
271
273{
274 if (mReadOnly || f == d_ptr->mAllDay) {
275 return;
276 }
277 update();
278 d_ptr->mAllDay = f;
279 if (d_ptr->mDtStart.isValid()) {
280 d_ptr->mDirtyFields.insert(FieldDtStart);
281 }
282 updated();
283}
284
285void IncidenceBase::shiftTimes(const QTimeZone &oldZone, const QTimeZone &newZone)
286{
287 update();
288 d_ptr->mDtStart = d_ptr->mDtStart.toTimeZone(oldZone);
289 d_ptr->mDtStart.setTimeZone(newZone);
290 d_ptr->mDirtyFields.insert(FieldDtStart);
291 updated();
292}
293
295{
296 update();
297 d_ptr->mComments += comment;
298 d_ptr->mDirtyFields.insert(FieldComment);
299 updated();
300}
301
303{
304 auto it = std::find(d_ptr->mComments.begin(), d_ptr->mComments.end(), comment);
305 bool found = it != d_ptr->mComments.end();
306 if (found) {
307 update();
308 d_ptr->mComments.erase(it);
309 d_ptr->mDirtyFields.insert(FieldComment);
310 updated();
311 }
312 return found;
313}
314
316{
317 update();
318 d_ptr->mDirtyFields.insert(FieldComment);
319 d_ptr->mComments.clear();
320 updated();
321}
322
324{
325 return d_ptr->mComments;
326}
327
329{
330 if (!contact.isEmpty()) {
331 update();
332 d_ptr->mContacts += contact;
333 d_ptr->mDirtyFields.insert(FieldContact);
334 updated();
335 }
336}
337
339{
340 auto it = std::find(d_ptr->mContacts.begin(), d_ptr->mContacts.end(), contact);
341 bool found = it != d_ptr->mContacts.end();
342 if (found) {
343 update();
344 d_ptr->mContacts.erase(it);
345 d_ptr->mDirtyFields.insert(FieldContact);
346 updated();
347 }
348 return found;
349}
350
352{
353 update();
354 d_ptr->mDirtyFields.insert(FieldContact);
355 d_ptr->mContacts.clear();
356 updated();
357}
358
360{
361 return d_ptr->mContacts;
362}
363
364void IncidenceBase::addAttendee(const Attendee &a, bool doupdate)
365{
366 if (a.isNull() || mReadOnly) {
367 return;
368 }
369 Q_ASSERT(!a.uid().isEmpty());
370
371 if (doupdate) {
372 update();
373 }
374
375 d_ptr->mAttendees.append(a);
376 if (doupdate) {
377 d_ptr->mDirtyFields.insert(FieldAttendees);
378 updated();
379 }
380}
381
382Attendee::List IncidenceBase::attendees() const
383{
384 return d_ptr->mAttendees;
385}
386
388{
389 return d_ptr->mAttendees.count();
390}
391
392void IncidenceBase::setAttendees(const Attendee::List &attendees, bool doUpdate)
393{
394 if (mReadOnly) {
395 return;
396 }
397
398 // don't simply assign, we need the logic in addAttendee here too
400
401 if (doUpdate) {
402 update();
403 }
404
405 d_ptr->mAttendees.reserve(attendees.size());
406 for (const auto &a : attendees) {
407 addAttendee(a, false);
408 }
409
410 if (doUpdate) {
411 d_ptr->mDirtyFields.insert(FieldAttendees);
412 updated();
413 }
414}
415
417{
418 if (mReadOnly) {
419 return;
420 }
421 update();
422 d_ptr->mDirtyFields.insert(FieldAttendees);
423 d_ptr->mAttendees.clear();
424 updated();
425}
426
428{
429 auto it = std::find_if(d_ptr->mAttendees.cbegin(), d_ptr->mAttendees.cend(), [&email](const Attendee &att) {
430 return att.email() == email;
431 });
432
433 return it != d_ptr->mAttendees.cend() ? *it : Attendee{};
434}
435
437{
438 QStringList mails = emails;
439 if (!email.isEmpty()) {
440 mails.append(email);
441 }
442
443 auto it = std::find_if(d_ptr->mAttendees.cbegin(), d_ptr->mAttendees.cend(), [&mails](const Attendee &a) {
444 return mails.contains(a.email());
445 });
446
447 return it != d_ptr->mAttendees.cend() ? *it : Attendee{};
448}
449
451{
452 auto it = std::find_if(d_ptr->mAttendees.cbegin(), d_ptr->mAttendees.cend(), [&uid](const Attendee &a) {
453 return a.uid() == uid;
454 });
455 return it != d_ptr->mAttendees.cend() ? *it : Attendee{};
456}
457
459{
460 update();
461 d_ptr->mDuration = duration;
462 setHasDuration(true);
463 d_ptr->mDirtyFields.insert(FieldDuration);
464 updated();
465}
466
468{
469 return d_ptr->mDuration;
470}
471
472void IncidenceBase::setHasDuration(bool hasDuration)
473{
474 d_ptr->mHasDuration = hasDuration;
475}
476
478{
479 return d_ptr->mHasDuration;
480}
481
483{
484 update();
485 d_ptr->mDirtyFields.insert(FieldUrl);
486 d_ptr->mUrl = url;
487 updated();
488}
489
490QUrl IncidenceBase::url() const
491{
492 return d_ptr->mUrl;
493}
494
496{
497 if (observer && !d_ptr->mObservers.contains(observer)) {
498 d_ptr->mObservers.append(observer);
499 }
500}
501
503{
504 d_ptr->mObservers.removeAll(observer);
505}
506
508{
509 if (!d_ptr->mUpdateGroupLevel) {
510 d_ptr->mUpdatedPending = true;
511 const auto rid = recurrenceId();
512 for (IncidenceObserver *o : std::as_const(d_ptr->mObservers)) {
513 o->incidenceUpdate(uid(), rid);
514 }
515 }
516}
517
519{
520 if (d_ptr->mUpdateGroupLevel) {
521 d_ptr->mUpdatedPending = true;
522 } else {
523 const auto rid = recurrenceId();
524 for (IncidenceObserver *o : std::as_const(d_ptr->mObservers)) {
525 o->incidenceUpdated(uid(), rid);
526 }
527 }
528}
529
531{
532 update();
533 ++d_ptr->mUpdateGroupLevel;
534}
535
537{
538 if (d_ptr->mUpdateGroupLevel > 0) {
539 if (--d_ptr->mUpdateGroupLevel == 0 && d_ptr->mUpdatedPending) {
540 d_ptr->mUpdatedPending = false;
541 updated();
542 }
543 }
544}
545
550
555
557{
558 return QDateTime();
559}
560
562{
563 d_ptr->mDirtyFields.clear();
564}
565
567{
568 return d_ptr->mDirtyFields;
569}
570
572{
573 d_ptr->mDirtyFields.insert(field);
574}
575
577{
578 return QUrl(QStringLiteral("urn:x-ical:") + uid());
579}
580
582{
583 d_ptr->mDirtyFields = dirtyFields;
584}
585
587{
588 Q_UNUSED(out);
589}
590
592{
593 Q_UNUSED(in);
594}
595
596/** static */
598{
599 return KCALCORE_MAGIC_NUMBER;
600}
601
602static bool isUtc(const QDateTime &dt)
603{
604 return dt.timeSpec() == Qt::UTC || (dt.timeSpec() == Qt::TimeZone && dt.timeZone() == QTimeZone::utc())
605 || (dt.timeSpec() == Qt::OffsetFromUTC && dt.offsetFromUtc() == 0);
606}
607
608bool KCalendarCore::identical(const QDateTime &dt1, const QDateTime &dt2)
609{
610 if (dt1 != dt2) {
611 return false;
612 }
613
614 return (dt1.timeSpec() == dt2.timeSpec() && dt1.timeZone() == dt2.timeZone()) || (isUtc(dt1) && isUtc(dt2));
615}
616
618{
619 if (!i) {
620 return out;
621 }
622
623 out << static_cast<quint32>(KCALCORE_MAGIC_NUMBER); // Magic number to identify KCalendarCore data
624 out << static_cast<quint32>(KCALCORE_SERIALIZATION_VERSION);
625 out << static_cast<qint32>(i->type());
626
627 out << *(static_cast<CustomProperties *>(i.data()));
628 serializeQDateTimeAsKDateTime(out, i->d_ptr->mLastModified);
629 serializeQDateTimeAsKDateTime(out, i->d_ptr->mDtStart);
630 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
631 << (qint32)i->d_ptr->mAttendees.count() << i->d_ptr->mUrl;
632
633 for (const Attendee &attendee : std::as_const(i->d_ptr->mAttendees)) {
634 out << attendee;
635 }
636
637 // Serialize the sub-class data.
638 i->serialize(out);
639
640 return out;
641}
642
644{
645 if (!i) {
646 return in;
647 }
648
649 qint32 attendeeCount;
650 qint32 type;
651 quint32 magic;
652 quint32 version;
653
654 in >> magic;
655
656 if (magic != KCALCORE_MAGIC_NUMBER) {
657 qCWarning(KCALCORE_LOG) << "Invalid magic on serialized data";
658 return in;
659 }
660
661 in >> version;
662
663 if (version > KCALCORE_MAGIC_NUMBER) {
664 qCWarning(KCALCORE_LOG) << "Invalid version on serialized data";
665 return in;
666 }
667
668 in >> type;
669
670 in >> *(static_cast<CustomProperties *>(i.data()));
671 deserializeKDateTimeAsQDateTime(in, i->d_ptr->mLastModified);
672 deserializeKDateTimeAsQDateTime(in, i->d_ptr->mDtStart);
673 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
674 >> i->d_ptr->mUrl;
675
676 i->d_ptr->mAttendees.clear();
677 i->d_ptr->mAttendees.reserve(attendeeCount);
678 for (int it = 0; it < attendeeCount; it++) {
679 Attendee attendee;
680 in >> attendee;
681 i->d_ptr->mAttendees.append(attendee);
682 }
683
684 // Deserialize the sub-class data.
685 i->deserialize(in);
686
687 return in;
688}
689
693
694#include "moc_incidencebase.cpp"
This file is part of the API for handling calendar data and defines the CalFormat abstract base class...
Represents information related to an attendee of an Calendar Incidence, typically a meeting or task (...
Definition attendee.h:45
static QString createUniqueId()
Creates a unique id string.
Definition calformat.cpp:88
A class to manage custom calendar properties.
CustomProperties & operator=(const CustomProperties &other)
Assignment operator.
Represents a span of time measured in seconds or days.
Definition duration.h:44
virtual ~IncidenceObserver()
Destroys the IncidenceObserver.
An abstract class that provides a common base for all calendar incidence classes.
void addAttendee(const Attendee &attendee, bool doUpdate=true)
Add Attendee to this incidence.
virtual bool accept(Visitor &v, const IncidenceBase::Ptr &incidence)
Accept IncidenceVisitor.
void updated()
Call this to notify the observers after the IncidenceBase object has changed.
virtual void setDtStart(const QDateTime &dtStart)
Sets the incidence's starting date/time with a QDateTime.
QStringList comments() const
Returns all incidence comments as a list of strings.
void addContact(const QString &contact)
Adds a contact to thieincidence.
bool removeContact(const QString &contact)
Removes a contact from the incidence.
virtual void shiftTimes(const QTimeZone &oldZone, const QTimeZone &newZone)
Shift the times of the incidence so that they appear at the same clock time as before but in a new ti...
bool mReadOnly
Identifies a read-only incidence.
Duration duration() const
Returns the length of the incidence duration.
void startUpdates()
Call this when a group of updates is going to be made.
void addComment(const QString &comment)
Adds a comment to the incidence.
Attendee attendeeByUid(const QString &uid) const
Returns the incidence attendee with the specified attendee UID.
void setAttendees(const Attendee::List &attendees, bool doUpdate=true)
Set the attendees of this incidence.
void update()
Call this to notify the observers after the IncidenceBase object will be changed.
virtual IncidenceType type() const =0
Returns the incidence type.
QSet< IncidenceBase::Field > dirtyFields() const
Returns a QSet with all Fields that were changed since the incidence was created or resetDirtyFields(...
IncidenceBase & operator=(const IncidenceBase &other)
Assignment operator.
void clearComments()
Deletes all incidence comments.
Attendee attendeeByMail(const QString &email) const
Returns the attendee with the specified email address.
void setOrganizer(const Person &organizer)
Sets the organizer for the incidence.
virtual void setDuration(const Duration &duration)
Sets the incidence duration.
bool operator==(const IncidenceBase &ib) const
Compares this with IncidenceBase ib for equality.
void clearAttendees()
Removes all attendees from the incidence.
virtual void setReadOnly(bool readOnly)
Sets readonly status.
QUrl uri() const
Returns the uri for the incidence, of form urn:x-ical:<uid>
virtual void setLastModified(const QDateTime &lm)
Sets the time the incidence was last modified to lm.
Field
The different types of incidence fields.
@ FieldComment
Field representing the COMMENT component.
@ FieldUnknown
Something changed. Always set when you use the assignment operator.
@ FieldDtStart
Field representing the DTSTART component.
@ FieldContact
Field representing the CONTACT component.
@ FieldOrganizer
Field representing the ORGANIZER component.
@ FieldDuration
Field representing the DURATION component.
@ FieldUrl
Field representing the URL component.
@ FieldUid
Field representing the UID component.
@ FieldLastModified
Field representing the LAST-MODIFIED component.
@ FieldAttendees
Field representing the ATTENDEE component.
void setDirtyFields(const QSet< IncidenceBase::Field > &)
Sets which fields are dirty.
virtual IncidenceBase & assign(const IncidenceBase &other)
Provides polymorfic assignment.
Attendee attendeeByMails(const QStringList &emails, const QString &email=QString()) const
Returns the first incidence attendee with one of the specified email addresses.
void setHasDuration(bool hasDuration)
Sets if the incidence has a duration.
virtual void deserialize(QDataStream &in)
Sub-type specific deserialization.
void endUpdates()
Call this when a group of updates is complete, to notify observers that the instance has changed.
void unRegisterObserver(IncidenceObserver *observer)
Unregister observer.
void setFieldDirty(IncidenceBase::Field field)
Marks Field field as dirty.
void customPropertyUpdated() override
void clearContacts()
Deletes all incidence contacts.
int attendeeCount() const
Returns the number of incidence attendees.
void setUid(const QString &uid)
Sets the unique id for the incidence to uid.
void setUrl(const QUrl &url)
Sets the incidences url.
void resetDirtyFields()
Resets dirty fields.
virtual QDateTime recurrenceId() const
Returns the incidence recurrenceId.
virtual void setAllDay(bool allDay)
Sets whether the incidence is all-day, i.e.
bool isReadOnly() const
Returns true the object is read-only; false otherwise.
bool operator!=(const IncidenceBase &ib) const
Compares this with IncidenceBase ib for inequality.
QStringList contacts() const
Returns all incidence contacts as a list of strings.
static quint32 magicSerializationIdentifier()
Constant that identifies KCalendarCore data in a binary stream.
~IncidenceBase() override
Destroys the IncidenceBase.
bool hasDuration() const
Returns true if the incidence has a duration; false otherwise.
bool removeComment(const QString &comment)
Removes a comment from the incidence.
void registerObserver(IncidenceObserver *observer)
Register observer.
virtual bool equals(const IncidenceBase &incidenceBase) const
Provides polymorfic comparison for equality.
virtual void serialize(QDataStream &out) const
Sub-type specific serialization.
Represents a person, by name and email address.
Definition person.h:38
static Person fromFullName(const QString &fullName)
Constructs a person with name and email address taken from fullName.
Definition person.cpp:362
This class provides the interface for a visitor of calendar components.
Definition visitor.h:31
This file is part of the API for handling calendar data and defines the IncidenceBase class.
Namespace for all KCalendarCore types.
Definition alarm.h:37
KCALENDARCORE_EXPORT QDataStream & operator>>(QDataStream &in, const KCalendarCore::Alarm::Ptr &)
Alarm deserializer.
Definition alarm.cpp:833
KCALENDARCORE_EXPORT QDataStream & operator<<(QDataStream &out, const KCalendarCore::Alarm::Ptr &)
Alarm serializer.
Definition alarm.cpp:820
KCALENDARCORE_EXPORT bool identical(const QDateTime &dt1, const QDateTime &dt2)
Compare two QDateTimes for extended equality.
bool isValid() const const
int offsetFromUtc() const const
void setTime(QTime time)
QTime time() const const
Qt::TimeSpec timeSpec() const const
QTimeZone timeZone() const const
QDateTime toUTC() const const
void append(QList< T > &&value)
const_iterator cbegin() const const
const_iterator cend() const const
qsizetype size() const const
T * data() const const
bool isEmpty() const const
CaseInsensitive
int hour() const const
int minute() const const
int second() const const
bool setHMS(int h, int m, int s, int ms)
QTimeZone utc()
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:13:47 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.