diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4e037fedd..e43c64991 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,121 +1,124 @@ ########### next target ############### set(kcalcore_LIB_SRCS alarm.cpp attachment.cpp attendee.cpp calendar.cpp calfilter.cpp calformat.cpp calstorage.cpp compat.cpp customproperties.cpp duration.cpp event.cpp exceptions.cpp filestorage.cpp freebusy.cpp freebusycache.cpp freebusyperiod.cpp icalformat.cpp icalformat_p.cpp icaltimezones.cpp incidence.cpp incidencebase.cpp journal.cpp memorycalendar.cpp occurrenceiterator.cpp period.cpp person.cpp recurrence.cpp recurrencerule.cpp schedulemessage.cpp sorting.cpp todo.cpp utils.cpp vcalformat.cpp visitor.cpp ) ecm_qt_declare_logging_category(kcalcore_LIB_SRCS HEADER kcalcore_debug.h IDENTIFIER KCALCORE_LOG CATEGORY_NAME org.kde.pim.kcalcore) add_library(KF5CalendarCore ${kcalcore_LIB_SRCS}) generate_export_header(KF5CalendarCore BASE_NAME kcalcore) add_library(KF5::CalendarCore ALIAS KF5CalendarCore) +# backward compatibility with the old name +# TODO: remove for KF6 +target_compile_definitions(KF5CalendarCore INTERFACE "-DKCalCore=KCalendarCore") target_include_directories(KF5CalendarCore INTERFACE "$") target_include_directories(KF5CalendarCore PUBLIC "$") target_link_libraries(KF5CalendarCore PUBLIC Qt5::Core PRIVATE Qt5::Gui LibIcal ) set_target_properties(KF5CalendarCore PROPERTIES VERSION ${KCALCORE_VERSION_STRING} SOVERSION ${KCALCORE_SOVERSION} EXPORT_NAME CalendarCore ) install(TARGETS KF5CalendarCore EXPORT KF5CalendarCoreTargets ${KF5_INSTALL_TARGETS_DEFAULT_ARGS}) ########### Generate Headers ############### ecm_generate_headers(KCalCore_CamelCase_HEADERS HEADER_NAMES Alarm Attachment Attendee CalFilter CalFormat CalStorage Calendar CustomProperties Duration Event Exceptions # NOTE: Used to be called 'Exception' in KDE4 FileStorage FreeBusy FreeBusyCache FreeBusyPeriod ICalFormat Incidence IncidenceBase Journal MemoryCalendar OccurrenceIterator Period Person Recurrence RecurrenceRule ScheduleMessage Sorting Todo VCalFormat Visitor PREFIX KCalCore REQUIRED_HEADERS KCalCore_HEADERS ) ########### install files ############### install(FILES ${CMAKE_CURRENT_BINARY_DIR}/kcalcore_export.h ${KCalCore_HEADERS} DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/KCalCore/kcalcore COMPONENT Devel ) install(FILES ${KCalCore_CamelCase_HEADERS} DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/KCalCore/KCalCore COMPONENT Devel ) ecm_generate_pri_file(BASE_NAME KCalCore LIB_NAME KF5CalendarCore DEPS "Core" FILENAME_VAR PRI_FILENAME INCLUDE_INSTALL_DIR ${KDE_INSTALL_INCLUDEDIR_KF5}/KCalCore/KCalCore) install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) diff --git a/src/alarm.cpp b/src/alarm.cpp index ba0740d34..c3ae471c9 100644 --- a/src/alarm.cpp +++ b/src/alarm.cpp @@ -1,881 +1,881 @@ /* This file is part of the kcalcore library. Copyright (c) 1998 Preston Brown Copyright (c) 2001 Cornelius Schumacher Copyright (c) 2003 David Jarvie This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the Alarm class. @brief Represents an alarm notification. @author Cornelius Schumacher \ */ #include "alarm.h" #include "duration.h" #include "incidence.h" #include "utils.h" #include #include -using namespace KCalCore; +using namespace KCalendarCore; /** Private class that helps to provide binary compatibility between releases. @internal */ //@cond PRIVATE -class Q_DECL_HIDDEN KCalCore::Alarm::Private +class Q_DECL_HIDDEN KCalendarCore::Alarm::Private { public: Private() : mParent(nullptr), mType(Alarm::Invalid), mAlarmSnoozeTime(5), mAlarmRepeatCount(0), mEndOffset(false), mHasTime(false), mAlarmEnabled(false), mHasLocationRadius(false), mLocationRadius(0) {} Incidence *mParent = nullptr; // the incidence which this alarm belongs to Type mType; // type of alarm QString mDescription;// text to display/email body/procedure arguments QString mFile; // program to run/optional audio file to play QString mMailSubject;// subject of email QStringList mMailAttachFiles; // filenames to attach to email Person::List mMailAddresses; // who to mail for reminder QDateTime mAlarmTime;// time at which to trigger the alarm Duration mAlarmSnoozeTime; // how long after alarm to snooze before // triggering again int mAlarmRepeatCount;// number of times for alarm to repeat // after the initial time Duration mOffset; // time relative to incidence DTSTART // to trigger the alarm bool mEndOffset; // if true, mOffset relates to DTEND, not DTSTART bool mHasTime; // use mAlarmTime, not mOffset bool mAlarmEnabled; bool mHasLocationRadius; int mLocationRadius; // location radius for the alarm }; //@endcond -Alarm::Alarm(Incidence *parent) : d(new KCalCore::Alarm::Private) +Alarm::Alarm(Incidence *parent) : d(new KCalendarCore::Alarm::Private) { d->mParent = parent; } Alarm::Alarm(const Alarm &other) : - CustomProperties(other), d(new KCalCore::Alarm::Private(*other.d)) + CustomProperties(other), d(new KCalendarCore::Alarm::Private(*other.d)) { } Alarm::~Alarm() { delete d; } Alarm &Alarm::operator=(const Alarm &a) { if (&a != this) { d->mParent = a.d->mParent; d->mType = a.d->mType; d->mDescription = a.d->mDescription; d->mFile = a.d->mFile; d->mMailAttachFiles = a.d->mMailAttachFiles; d->mMailAddresses = a.d->mMailAddresses; d->mMailSubject = a.d->mMailSubject; d->mAlarmSnoozeTime = a.d->mAlarmSnoozeTime; d->mAlarmRepeatCount = a.d->mAlarmRepeatCount; d->mAlarmTime = a.d->mAlarmTime; d->mOffset = a.d->mOffset; d->mEndOffset = a.d->mEndOffset; d->mHasTime = a.d->mHasTime; d->mAlarmEnabled = a.d->mAlarmEnabled; } return *this; } static bool compareMailAddresses(const Person::List &list1, const Person::List &list2) { if (list1.count() == list2.count()) { for (int i = 0; i < list1.count(); ++i) { if (list1.at(i) != list2.at(i)) { return false; } } return true; } return false; } bool Alarm::operator==(const Alarm &rhs) const { if (d->mType != rhs.d->mType || d->mAlarmSnoozeTime != rhs.d->mAlarmSnoozeTime || d->mAlarmRepeatCount != rhs.d->mAlarmRepeatCount || d->mAlarmEnabled != rhs.d->mAlarmEnabled || d->mHasTime != rhs.d->mHasTime || d->mHasLocationRadius != rhs.d->mHasLocationRadius || d->mLocationRadius != rhs.d->mLocationRadius) { return false; } if (d->mHasTime) { if (d->mAlarmTime != rhs.d->mAlarmTime) { return false; } } else { if (d->mOffset != rhs.d->mOffset || d->mEndOffset != rhs.d->mEndOffset) { return false; } } switch (d->mType) { case Display: return d->mDescription == rhs.d->mDescription; case Email: return d->mDescription == rhs.d->mDescription && d->mMailAttachFiles == rhs.d->mMailAttachFiles && compareMailAddresses(d->mMailAddresses, rhs.d->mMailAddresses) && d->mMailSubject == rhs.d->mMailSubject; case Procedure: return d->mFile == rhs.d->mFile && d->mDescription == rhs.d->mDescription; case Audio: return d->mFile == rhs.d->mFile; case Invalid: break; } return false; } bool Alarm::operator!=(const Alarm &a) const { return !operator==(a); } void Alarm::setType(Alarm::Type type) { if (type == d->mType) { return; } if (d->mParent) { d->mParent->update(); } switch (type) { case Display: d->mDescription.clear(); break; case Procedure: d->mFile.clear(); d->mDescription.clear(); break; case Audio: d->mFile.clear(); break; case Email: d->mMailSubject.clear(); d->mDescription.clear(); d->mMailAddresses.clear(); d->mMailAttachFiles.clear(); break; case Invalid: break; default: if (d->mParent) { d->mParent->updated(); // not really } return; } d->mType = type; if (d->mParent) { d->mParent->updated(); } } Alarm::Type Alarm::type() const { return d->mType; } void Alarm::setAudioAlarm(const QString &audioFile) { if (d->mParent) { d->mParent->update(); } d->mType = Audio; d->mFile = audioFile; if (d->mParent) { d->mParent->updated(); } } void Alarm::setAudioFile(const QString &audioFile) { if (d->mType == Audio) { if (d->mParent) { d->mParent->update(); } d->mFile = audioFile; if (d->mParent) { d->mParent->updated(); } } } QString Alarm::audioFile() const { return (d->mType == Audio) ? d->mFile : QString(); } void Alarm::setProcedureAlarm(const QString &programFile, const QString &arguments) { if (d->mParent) { d->mParent->update(); } d->mType = Procedure; d->mFile = programFile; d->mDescription = arguments; if (d->mParent) { d->mParent->updated(); } } void Alarm::setProgramFile(const QString &programFile) { if (d->mType == Procedure) { if (d->mParent) { d->mParent->update(); } d->mFile = programFile; if (d->mParent) { d->mParent->updated(); } } } QString Alarm::programFile() const { return (d->mType == Procedure) ? d->mFile : QString(); } void Alarm::setProgramArguments(const QString &arguments) { if (d->mType == Procedure) { if (d->mParent) { d->mParent->update(); } d->mDescription = arguments; if (d->mParent) { d->mParent->updated(); } } } QString Alarm::programArguments() const { return (d->mType == Procedure) ? d->mDescription : QString(); } void Alarm::setEmailAlarm(const QString &subject, const QString &text, const Person::List &addressees, const QStringList &attachments) { if (d->mParent) { d->mParent->update(); } d->mType = Email; d->mMailSubject = subject; d->mDescription = text; d->mMailAddresses = addressees; d->mMailAttachFiles = attachments; if (d->mParent) { d->mParent->updated(); } } void Alarm::setMailAddress(const Person &mailAddress) { if (d->mType == Email) { if (d->mParent) { d->mParent->update(); } d->mMailAddresses.clear(); d->mMailAddresses.append(mailAddress); if (d->mParent) { d->mParent->updated(); } } } void Alarm::setMailAddresses(const Person::List &mailAddresses) { if (d->mType == Email) { if (d->mParent) { d->mParent->update(); } d->mMailAddresses += mailAddresses; if (d->mParent) { d->mParent->updated(); } } } void Alarm::addMailAddress(const Person &mailAddress) { if (d->mType == Email) { if (d->mParent) { d->mParent->update(); } d->mMailAddresses.append(mailAddress); if (d->mParent) { d->mParent->updated(); } } } Person::List Alarm::mailAddresses() const { return (d->mType == Email) ? d->mMailAddresses : Person::List(); } void Alarm::setMailSubject(const QString &mailAlarmSubject) { if (d->mType == Email) { if (d->mParent) { d->mParent->update(); } d->mMailSubject = mailAlarmSubject; if (d->mParent) { d->mParent->updated(); } } } QString Alarm::mailSubject() const { return (d->mType == Email) ? d->mMailSubject : QString(); } void Alarm::setMailAttachment(const QString &mailAttachFile) { if (d->mType == Email) { if (d->mParent) { d->mParent->update(); } d->mMailAttachFiles.clear(); d->mMailAttachFiles += mailAttachFile; if (d->mParent) { d->mParent->updated(); } } } void Alarm::setMailAttachments(const QStringList &mailAttachFiles) { if (d->mType == Email) { if (d->mParent) { d->mParent->update(); } d->mMailAttachFiles = mailAttachFiles; if (d->mParent) { d->mParent->updated(); } } } void Alarm::addMailAttachment(const QString &mailAttachFile) { if (d->mType == Email) { if (d->mParent) { d->mParent->update(); } d->mMailAttachFiles += mailAttachFile; if (d->mParent) { d->mParent->updated(); } } } QStringList Alarm::mailAttachments() const { return (d->mType == Email) ? d->mMailAttachFiles : QStringList(); } void Alarm::setMailText(const QString &text) { if (d->mType == Email) { if (d->mParent) { d->mParent->update(); } d->mDescription = text; if (d->mParent) { d->mParent->updated(); } } } QString Alarm::mailText() const { return (d->mType == Email) ? d->mDescription : QString(); } void Alarm::setDisplayAlarm(const QString &text) { if (d->mParent) { d->mParent->update(); } d->mType = Display; if (!text.isNull()) { d->mDescription = text; } if (d->mParent) { d->mParent->updated(); } } void Alarm::setText(const QString &text) { if (d->mType == Display) { if (d->mParent) { d->mParent->update(); } d->mDescription = text; if (d->mParent) { d->mParent->updated(); } } } QString Alarm::text() const { return (d->mType == Display) ? d->mDescription : QString(); } void Alarm::setTime(const QDateTime &alarmTime) { if (d->mParent) { d->mParent->update(); } d->mAlarmTime = alarmTime; d->mHasTime = true; if (d->mParent) { d->mParent->updated(); } } QDateTime Alarm::time() const { if (hasTime()) { return d->mAlarmTime; } else if (d->mParent) { if (d->mEndOffset) { QDateTime dt = d->mParent->dateTime(Incidence::RoleAlarmEndOffset); return d->mOffset.end(dt); } else { QDateTime dt = d->mParent->dateTime(Incidence::RoleAlarmStartOffset); return d->mOffset.end(dt); } } else { return QDateTime(); } } QDateTime Alarm::nextTime(const QDateTime &preTime, bool ignoreRepetitions) const { if (d->mParent && d->mParent->recurs()) { QDateTime dtEnd = d->mParent->dateTime(Incidence::RoleAlarmEndOffset); QDateTime dtStart = d->mParent->dtStart(); // Find the incidence's earliest alarm // Alarm time is defined by an offset from the event start or end time. QDateTime alarmStart = d->mOffset.end(d->mEndOffset ? dtEnd : dtStart); // Find the offset from the event start time, which is also used as the // offset from the recurrence time. Duration alarmOffset(dtStart, alarmStart); /* qCDebug(KCALCORE_LOG) << "dtStart " << dtStart; qCDebug(KCALCORE_LOG) << "dtEnd " << dtEnd; qCDebug(KCALCORE_LOG) << "alarmStart " << alarmStart; qCDebug(KCALCORE_LOG) << "alarmOffset " << alarmOffset.value(); qCDebug(KCALCORE_LOG) << "preTime " << preTime; */ if (alarmStart > preTime) { // No need to go further. return alarmStart; } if (d->mAlarmRepeatCount && !ignoreRepetitions) { // The alarm has repetitions, so check whether repetitions of previous // recurrences happen after given time. QDateTime prevRecurrence = d->mParent->recurrence()->getPreviousDateTime(preTime); if (prevRecurrence.isValid()) { QDateTime prevLastRepeat = alarmOffset.end(duration().end(prevRecurrence)); // qCDebug(KCALCORE_LOG) << "prevRecurrence" << prevRecurrence; // qCDebug(KCALCORE_LOG) << "prevLastRepeat" << prevLastRepeat; if (prevLastRepeat > preTime) { // Yes they did, return alarm offset to previous recurrence. QDateTime prevAlarm = alarmOffset.end(prevRecurrence); // qCDebug(KCALCORE_LOG) << "prevAlarm " << prevAlarm; return prevAlarm; } } } // Check the next recurrence now. QDateTime nextRecurrence = d->mParent->recurrence()->getNextDateTime(preTime); if (nextRecurrence.isValid()) { QDateTime nextAlarm = alarmOffset.end(nextRecurrence); /* qCDebug(KCALCORE_LOG) << "nextRecurrence" << nextRecurrence; qCDebug(KCALCORE_LOG) << "nextAlarm " << nextAlarm; */ if (nextAlarm > preTime) { // It's first alarm takes place after given time. return nextAlarm; } } } else { // Not recurring. QDateTime alarmTime = time(); if (alarmTime > preTime) { return alarmTime; } } return QDateTime(); } bool Alarm::hasTime() const { return d->mHasTime; } void Alarm::shiftTimes(const QTimeZone &oldZone, const QTimeZone &newZone) { if (d->mParent) { d->mParent->update(); } d->mAlarmTime = d->mAlarmTime.toTimeZone(oldZone); d->mAlarmTime.setTimeZone(newZone); if (d->mParent) { d->mParent->updated(); } } void Alarm::setSnoozeTime(const Duration &alarmSnoozeTime) { if (alarmSnoozeTime.value() > 0) { if (d->mParent) { d->mParent->update(); } d->mAlarmSnoozeTime = alarmSnoozeTime; if (d->mParent) { d->mParent->updated(); } } } Duration Alarm::snoozeTime() const { return d->mAlarmSnoozeTime; } void Alarm::setRepeatCount(int alarmRepeatCount) { if (d->mParent) { d->mParent->update(); } d->mAlarmRepeatCount = alarmRepeatCount; if (d->mParent) { d->mParent->updated(); } } int Alarm::repeatCount() const { return d->mAlarmRepeatCount; } Duration Alarm::duration() const { return Duration(d->mAlarmSnoozeTime.value() * d->mAlarmRepeatCount, d->mAlarmSnoozeTime.type()); } QDateTime Alarm::nextRepetition(const QDateTime &preTime) const { QDateTime at = nextTime(preTime); if (at > preTime) { return at; } if (!d->mAlarmRepeatCount) { // there isn't an occurrence after the specified time return QDateTime(); } qint64 repetition; int interval = d->mAlarmSnoozeTime.value(); bool daily = d->mAlarmSnoozeTime.isDaily(); if (daily) { qint64 daysTo = at.daysTo(preTime); if (preTime.time() <= at.time()) { --daysTo; } repetition = daysTo / interval + 1; } else { repetition = at.secsTo(preTime) / interval + 1; } if (repetition > d->mAlarmRepeatCount) { // all repetitions have finished before the specified time return QDateTime(); } return daily ? at.addDays(int(repetition * interval)) : at.addSecs(repetition * interval); } QDateTime Alarm::previousRepetition(const QDateTime &afterTime) const { QDateTime at = time(); if (at >= afterTime) { // alarm's first/only time is at/after the specified time return QDateTime(); } if (!d->mAlarmRepeatCount) { return at; } qint64 repetition; int interval = d->mAlarmSnoozeTime.value(); bool daily = d->mAlarmSnoozeTime.isDaily(); if (daily) { qint64 daysTo = at.daysTo(afterTime); if (afterTime.time() <= at.time()) { --daysTo; } repetition = daysTo / interval; } else { repetition = (at.secsTo(afterTime) - 1) / interval; } if (repetition > d->mAlarmRepeatCount) { repetition = d->mAlarmRepeatCount; } return daily ? at.addDays(int(repetition * interval)) : at.addSecs(repetition * interval); } QDateTime Alarm::endTime() const { if (!d->mAlarmRepeatCount) { return time(); } if (d->mAlarmSnoozeTime.isDaily()) { return time().addDays(d->mAlarmRepeatCount * d->mAlarmSnoozeTime.asDays()); } else { return time().addSecs(d->mAlarmRepeatCount * d->mAlarmSnoozeTime.asSeconds()); } } void Alarm::toggleAlarm() { if (d->mParent) { d->mParent->update(); } d->mAlarmEnabled = !d->mAlarmEnabled; if (d->mParent) { d->mParent->updated(); } } void Alarm::setEnabled(bool enable) { if (d->mParent) { d->mParent->update(); } d->mAlarmEnabled = enable; if (d->mParent) { d->mParent->updated(); } } bool Alarm::enabled() const { return d->mAlarmEnabled; } void Alarm::setStartOffset(const Duration &offset) { if (d->mParent) { d->mParent->update(); } d->mOffset = offset; d->mEndOffset = false; d->mHasTime = false; if (d->mParent) { d->mParent->updated(); } } Duration Alarm::startOffset() const { return (d->mHasTime || d->mEndOffset) ? Duration(0) : d->mOffset; } bool Alarm::hasStartOffset() const { return !d->mHasTime && !d->mEndOffset; } bool Alarm::hasEndOffset() const { return !d->mHasTime && d->mEndOffset; } void Alarm::setEndOffset(const Duration &offset) { if (d->mParent) { d->mParent->update(); } d->mOffset = offset; d->mEndOffset = true; d->mHasTime = false; if (d->mParent) { d->mParent->updated(); } } Duration Alarm::endOffset() const { return (d->mHasTime || !d->mEndOffset) ? Duration(0) : d->mOffset; } void Alarm::setParent(Incidence *parent) { d->mParent = parent; } QString Alarm::parentUid() const { return d->mParent ? d->mParent->uid() : QString(); } void Alarm::customPropertyUpdated() { if (d->mParent) { d->mParent->update(); d->mParent->updated(); } } void Alarm::setHasLocationRadius(bool hasLocationRadius) { if (d->mParent) { d->mParent->update(); } d->mHasLocationRadius = hasLocationRadius; if (hasLocationRadius) { setNonKDECustomProperty("X-LOCATION-RADIUS", QString::number(d->mLocationRadius)); } else { removeNonKDECustomProperty("X-LOCATION-RADIUS"); } if (d->mParent) { d->mParent->updated(); } } bool Alarm::hasLocationRadius() const { return d->mHasLocationRadius; } void Alarm::setLocationRadius(int locationRadius) { if (d->mParent) { d->mParent->update(); } d->mLocationRadius = locationRadius; if (d->mParent) { d->mParent->updated(); } } int Alarm::locationRadius() const { return d->mLocationRadius; } -QDataStream &KCalCore::operator<<(QDataStream &out, const KCalCore::Alarm::Ptr &a) +QDataStream &KCalendarCore::operator<<(QDataStream &out, const KCalendarCore::Alarm::Ptr &a) { if (a) { out << ((quint32)a->d->mType) << a->d->mAlarmSnoozeTime << a->d->mAlarmRepeatCount << a->d->mEndOffset << a->d->mHasTime << a->d->mAlarmEnabled << a->d->mHasLocationRadius << a->d->mLocationRadius << a->d->mOffset; serializeQDateTimeAsKDateTime(out, a->d->mAlarmTime); out << a->d->mFile << a->d->mMailSubject << a->d->mDescription << a->d->mMailAttachFiles << a->d->mMailAddresses; } return out; } -QDataStream &KCalCore::operator>>(QDataStream &in, const KCalCore::Alarm::Ptr &a) +QDataStream &KCalendarCore::operator>>(QDataStream &in, const KCalendarCore::Alarm::Ptr &a) { if (a) { quint32 type; in >> type; a->d->mType = static_cast(type); in >> a->d->mAlarmSnoozeTime >> a->d->mAlarmRepeatCount >> a->d->mEndOffset >> a->d->mHasTime >> a->d->mAlarmEnabled >> a->d->mHasLocationRadius >> a->d->mLocationRadius >> a->d->mOffset; deserializeKDateTimeAsQDateTime(in, a->d->mAlarmTime); in >> a->d->mFile >> a->d->mMailSubject >> a->d->mDescription >> a->d->mMailAttachFiles >> a->d->mMailAddresses; } return in; } void Alarm::virtual_hook(int id, void *data) { Q_UNUSED(id); Q_UNUSED(data); Q_ASSERT(false); } diff --git a/src/alarm.h b/src/alarm.h index 99826061b..de34f0513 100644 --- a/src/alarm.h +++ b/src/alarm.h @@ -1,699 +1,699 @@ /* This file is part of the kcalcore library. Copyright (c) 2001-2003 Cornelius Schumacher Copyright (c) 2003 David Jarvie Copyright (c) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the Alarm class. @author Cornelius Schumacher \ */ #ifndef KCALCORE_ALARM_H #define KCALCORE_ALARM_H #include "kcalcore_export.h" #include "customproperties.h" #include "duration.h" #include "person.h" #include #include #include #include #include #include #include class QTimeZone; -namespace KCalCore +namespace KCalendarCore { class Incidence; /** @brief Represents an alarm notification. Alarms are user notifications that occur at specified times. Notifications can be on-screen pop-up dialogs, email messages, the playing of audio files, or the running of another program. Alarms always belong to a parent Incidence. */ class KCALCORE_EXPORT Alarm : public CustomProperties { public: /** The different types of alarms. */ enum Type { Invalid, /**< Invalid, or no alarm */ Display, /**< Display a dialog box */ Procedure, /**< Call a script */ Email, /**< Send email */ Audio /**< Play an audio file */ }; /** A shared pointer to an Alarm object. */ typedef QSharedPointer Ptr; /** List of alarms. */ typedef QVector List; /** Constructs an alarm belonging to the @p parent Incidence. @param parent is the Incidence this alarm will belong to. */ // Can't find a way to use a shared pointer here. // Inside incidence.cpp, it does alarm->setParent( this ) explicit Alarm(Incidence *parent); /** Copy constructor. @param other is the alarm to copy. */ Alarm(const Alarm &other); /** Destroys the alarm. */ ~Alarm() override; /** Copy operator. */ Alarm &operator=(const Alarm &); /** Compares two alarms for equality. @param a is the comparison alarm. */ bool operator==(const Alarm &a) const; /** Compares two alarms for inequality. @param a is the comparison alarm. */ bool operator!=(const Alarm &a) const; /** Sets the @p parent Incidence of the alarm. @param parent is alarm parent Incidence to set. @see parentUid() */ // Is there a way to use QSharedPointer here? // although it's safe, Incidence's dtor calls setParent( 0 ) // se we don't dereference a deleted pointer here. // Also, I renamed "Incidence *parent()" to "QString parentUid()" // So we don't return raw pointers void setParent(Incidence *parent); /** Returns the parent's incidence UID of the alarm. @see setParent() */ // We don't have a share pointer to return, so return the UID. Q_REQUIRED_RESULT QString parentUid() const; /** Sets the #Type for this alarm to @p type. If the specified type is different from the current type of the alarm, then the alarm's type-specific properties are re-initialized. @param type is the alarm #Type to set. @see type() */ void setType(Type type); /** Returns the #Type of the alarm. @see setType() */ Q_REQUIRED_RESULT Type type() const; /** Sets the #Display type for this alarm. If @p text is specified non-empty, then it is used as the description text to display when the alarm is triggered. @param text is the description to display when the alarm is triggered. @see setText(), text() */ void setDisplayAlarm(const QString &text = QString()); /** Sets the description @p text to be displayed when the alarm is triggered. Ignored if the alarm is not a display alarm. @param text is the description to display when the alarm is triggered. @see setDisplayAlarm(), text() */ void setText(const QString &text); /** Returns the display text string for a #Display alarm type. Returns an empty string if the alarm is not a #Display type. @see setDisplayAlarm(), setText() */ Q_REQUIRED_RESULT QString text() const; /** Sets the #Audio type for this alarm and the name of the audio file to play when the alarm is triggered. @param audioFile is the name of the audio file to play when the alarm is triggered. @see setAudioFile(), audioFile() */ void setAudioAlarm(const QString &audioFile = QString()); /** Sets the name of the audio file to play when the audio alarm is triggered. Ignored if the alarm is not an #Audio type. @param audioFile is the name of the audio file to play when the alarm is triggered. @see setAudioAlarm(), audioFile() */ void setAudioFile(const QString &audioFile); /** Returns the audio file name for an #Audio alarm type. Returns an empty string if the alarm is not an #Audio type. @see setAudioAlarm(), setAudioFile() */ Q_REQUIRED_RESULT QString audioFile() const; /** Sets the #Procedure type for this alarm and the program (with arguments) to execute when the alarm is triggered. @param programFile is the name of the program file to execute when the alarm is triggered. @param arguments is a string of arguments to supply to @p programFile. @see setProgramFile(), programFile(), setProgramArguments(), programArguments() */ void setProcedureAlarm(const QString &programFile, const QString &arguments = QString()); /** Sets the program file to execute when the alarm is triggered. Ignored if the alarm is not a #Procedure type. @param programFile is the name of the program file to execute when the alarm is triggered. @see setProcedureAlarm(), programFile(), setProgramArguments(), programArguments() */ void setProgramFile(const QString &programFile); /** Returns the program file name for a #Procedure alarm type. Returns an empty string if the alarm is not a #Procedure type. @see setProcedureAlarm(), setProgramFile(), setProgramArguments(), programArguments() */ Q_REQUIRED_RESULT QString programFile() const; /** Sets the program arguments string when the alarm is triggered. Ignored if the alarm is not a #Procedure type. @param arguments is a string of arguments to supply to the program. @see setProcedureAlarm(), setProgramFile(), programFile(), programArguments() */ void setProgramArguments(const QString &arguments); /** Returns the program arguments string for a #Procedure alarm type. Returns an empty string if the alarm is not a #Procedure type. @see setProcedureAlarm(), setProgramFile(), programFile(), setProgramArguments() */ Q_REQUIRED_RESULT QString programArguments() const; /** Sets the #Email type for this alarm and the email @p subject, @p text, @p addresses, and @p attachments that make up an email message to be sent when the alarm is triggered. @param subject is the email subject. @param text is a string containing the body of the email message. @param addressees is Person list of email addresses. @param attachments is a a QStringList of optional file names of email attachments. @see setMailSubject(), setMailText(), setMailAddresses(), setMailAttachments() */ void setEmailAlarm(const QString &subject, const QString &text, const Person::List &addressees, const QStringList &attachments = QStringList()); /** Sets the email address of an #Email type alarm. Ignored if the alarm is not an #Email type. @param mailAlarmAddress is a Person to receive a mail message when an #Email type alarm is triggered. @see setMailSubject(), setMailText(), setMailAddresses(), setMailAttachment(), setMailAttachments(), mailAddresses() */ void setMailAddress(const Person &mailAlarmAddress); /** Sets a list of email addresses of an #Email type alarm. Ignored if the alarm is not an #Email type. @param mailAlarmAddresses is a Person list to receive a mail message when an #Email type alarm is triggered. @see setMailSubject(), setMailText(), setMailAddress(), setMailAttachments(), setMailAttachment(), mailAddresses() */ void setMailAddresses(const Person::List &mailAlarmAddresses); /** Adds an address to the list of email addresses to send mail to when the alarm is triggered. Ignored if the alarm is not an #Email type. @param mailAlarmAddress is a Person to add to the list of addresses to receive a mail message when an #Email type alarm is triggered. @see setMailAddress(), setMailAddresses(), mailAddresses() */ void addMailAddress(const Person &mailAlarmAddress); /** Returns the list of addresses for an #Email alarm type. Returns an empty list if the alarm is not an #Email type. @see addMailAddress(), setMailAddress(), setMailAddresses() */ Q_REQUIRED_RESULT Person::List mailAddresses() const; /** Sets the subject line of a mail message for an #Email alarm type. Ignored if the alarm is not an #Email type. @param mailAlarmSubject is a string to be used as a subject line of an email message to send when the #Email type alarm is triggered. @see setMailText(), setMailAddress(), setMailAddresses(), setMailAttachment(), setMailAttachments(), mailSubject() */ void setMailSubject(const QString &mailAlarmSubject); /** Returns the subject line string for an #Email alarm type. Returns an empty string if the alarm is not an #Email type. @see setMailSubject() */ Q_REQUIRED_RESULT QString mailSubject() const; /** Sets the filename to attach to a mail message for an #Email alarm type. Ignored if the alarm is not an #Email type. @param mailAttachFile is a string containing a filename to be attached to an email message to send when the #Email type alarm is triggered. @see setMailSubject(), setMailText(), setMailAddress(), setMailAddresses(), setMailAttachments(), mailAttachments() */ void setMailAttachment(const QString &mailAttachFile); /** Sets a list of filenames to attach to a mail message for an #Email alarm type. Ignored if the alarm is not an #Email type. @param mailAttachFiles is a QString list of filenames to attach to a mail message when an #Email type alarm is triggered. @see setMailSubject(), setMailText(), setMailAttachment(), setMailAddress(), setMailAddresses() */ void setMailAttachments(const QStringList &mailAttachFiles); /** Adds a filename to the list of files to attach to a mail message for an #Email alarm type. Ignored if the alarm is not an #Email type. @param mailAttachFile is a string containing a filename to be attached to an email message to send when the #Email type alarm is triggered. @see setMailAttachment(), setMailAttachments(), mailAttachments() */ void addMailAttachment(const QString &mailAttachFile); /** Returns the list of attachment filenames for an #Email alarm type. Returns an empty list if the alarm is not an #Email type. @see addMailAttachment(), setMailAttachment(), setMailAttachments() */ Q_REQUIRED_RESULT QStringList mailAttachments() const; /** Sets the body text for an #Email alarm type. Ignored if the alarm is not an #Email type. @param text is a string containing the body text of a mail message when an #Email type alarm is triggered. @see setMailSubject(), setMailAddress(), setMailAddresses(), setMailAttachment(), setMailAttachments() */ void setMailText(const QString &text); /** Returns the body text for an #Email alarm type. Returns an empty string if the alarm is not an #Email type. @see setMailText() */ Q_REQUIRED_RESULT QString mailText() const; /** Sets the trigger time of the alarm. @param alarmTime is the QDateTime alarm trigger. @see time() */ void setTime(const QDateTime &alarmTime); /** Returns the alarm trigger date/time. @see setTime() */ Q_REQUIRED_RESULT QDateTime time() const; /** Returns the next alarm trigger date/time after given date/time. Takes recurrent incidences into account. @param preTime date/time from where to start @param ignoreRepetitions don't take repetitions into account @see nextRepetition() */ Q_REQUIRED_RESULT QDateTime nextTime(const QDateTime &preTime, bool ignoreRepetitions = false) const; /** Returns the date/time when the last repetition of the alarm goes off. If the alarm does not repeat this is equivalent to calling time(). @see setTime() */ Q_REQUIRED_RESULT QDateTime endTime() const; /** Returns true if the alarm has a trigger date/time. */ Q_REQUIRED_RESULT bool hasTime() const; /** Sets the alarm offset relative to the start of the parent Incidence. @param offset is a Duration to be used as a time relative to the start of the parent Incidence to be used as the alarm trigger. @see setEndOffset(), startOffset(), endOffset() */ void setStartOffset(const Duration &offset); /** Returns offset of alarm in time relative to the start of the parent Incidence. If the alarm's time is not defined in terms of an offset relative to the start of the event, returns zero. @see setStartOffset(), hasStartOffset() */ Q_REQUIRED_RESULT Duration startOffset() const; /** Returns whether the alarm is defined in terms of an offset relative to the start of the parent Incidence. @see startOffset(), setStartOffset() */ bool hasStartOffset() const; /** Sets the alarm offset relative to the end of the parent Incidence. @param offset is a Duration to be used as a time relative to the end of the parent Incidence to be used as the alarm trigger. @see setStartOffset(), startOffset(), endOffset() */ void setEndOffset(const Duration &offset); /** Returns offset of alarm in time relative to the end of the event. If the alarm's time is not defined in terms of an offset relative to the end of the event, returns zero. @see setEndOffset(), hasEndOffset() */ Q_REQUIRED_RESULT Duration endOffset() const; /** Returns whether the alarm is defined in terms of an offset relative to the end of the event. @see endOffset(), setEndOffset() */ bool hasEndOffset() const; /** Shift the times of the alarm so that they appear at the same clock time as before but in a new time zone. The shift is done from a viewing time zone rather than from the actual alarm time zone. For example, shifting an alarm whose start time is 09:00 America/New York, using an old viewing time zone (@p oldZone) of Europe/London, to a new time zone (@p newZone) of Europe/Paris, will result in the time being shifted from 14:00 (which is the London time of the alarm start) to 14:00 Paris time. @param oldZone the time zone which provides the clock times @param newZone the new time zone */ void shiftTimes(const QTimeZone &oldZone, const QTimeZone &newZone); /** Sets the snooze time interval for the alarm. @param alarmSnoozeTime the time between snoozes. @see snoozeTime() */ void setSnoozeTime(const Duration &alarmSnoozeTime); /** Returns the snooze time interval. @see setSnoozeTime() */ Q_REQUIRED_RESULT Duration snoozeTime() const; /** Sets how many times an alarm is to repeat itself after its initial occurrence (w/snoozes). @param alarmRepeatCount is the number of times an alarm may repeat, excluding the initial occurrence. @see repeatCount() */ void setRepeatCount(int alarmRepeatCount); /** Returns how many times an alarm may repeats after its initial occurrence. @see setRepeatCount() */ Q_REQUIRED_RESULT int repeatCount() const; /** Returns the date/time of the alarm's initial occurrence or its next repetition after a given time. @param preTime the date/time after which to find the next repetition. @return the date/time of the next repetition, or an invalid date/time if the specified time is at or after the alarm's last repetition. @see previousRepetition() */ Q_REQUIRED_RESULT QDateTime nextRepetition(const QDateTime &preTime) const; /** Returns the date/time of the alarm's latest repetition or, if none, its initial occurrence before a given time. @param afterTime is the date/time before which to find the latest repetition. @return the date and time of the latest repetition, or an invalid date/time if the specified time is at or before the alarm's initial occurrence. @see nextRepetition() */ Q_REQUIRED_RESULT QDateTime previousRepetition(const QDateTime &afterTime) const; /** Returns the interval between the alarm's initial occurrence and its final repetition. */ Q_REQUIRED_RESULT Duration duration() const; /** Toggles the alarm status, i.e, an enable alarm becomes disabled and a disabled alarm becomes enabled. @see enabled(), setEnabled() */ void toggleAlarm(); /** Sets the enabled status of the alarm. @param enable if true, then enable the alarm; else disable the alarm. @see enabled(), toggleAlarm() */ void setEnabled(bool enable); /** Returns the alarm enabled status: true (enabled) or false (disabled). @see setEnabled(), toggleAlarm() */ Q_REQUIRED_RESULT bool enabled() const; /** Set if the location radius for the alarm has been defined. @param hasLocationRadius if true, then this alarm has a location radius. @see setLocationRadius() */ void setHasLocationRadius(bool hasLocationRadius); /** Returns true if alarm has location radius defined. @see setLocationRadius() */ Q_REQUIRED_RESULT bool hasLocationRadius() const; /** Set location radius for the alarm. This means that alarm will be triggered when user approaches the location. Given value will be stored into custom properties as X-LOCATION-RADIUS. @param locationRadius radius in meters @see locationRadius() */ void setLocationRadius(int locationRadius); /** Returns the location radius in meters. @see setLocationRadius() */ Q_REQUIRED_RESULT int locationRadius() const; protected: /** @copydoc CustomProperties::customPropertyUpdated() */ void customPropertyUpdated() override; /** @copydoc IncidenceBase::virtual_hook() */ virtual void virtual_hook(int id, void *data); private: //@cond PRIVATE class Private; Private *const d; //@endcond - friend KCALCORE_EXPORT QDataStream &operator<<(QDataStream &s, const KCalCore::Alarm::Ptr &); - friend KCALCORE_EXPORT QDataStream &operator>>(QDataStream &s, const KCalCore::Alarm::Ptr &); + friend KCALCORE_EXPORT QDataStream &operator<<(QDataStream &s, const KCalendarCore::Alarm::Ptr &); + friend KCALCORE_EXPORT QDataStream &operator>>(QDataStream &s, const KCalendarCore::Alarm::Ptr &); }; /** * Alarm serializer. * * @since 4.12 */ -KCALCORE_EXPORT QDataStream &operator<<(QDataStream &out, const KCalCore::Alarm::Ptr &); +KCALCORE_EXPORT QDataStream &operator<<(QDataStream &out, const KCalendarCore::Alarm::Ptr &); /** * Alarm deserializer. * * @since 4.12 */ -KCALCORE_EXPORT QDataStream &operator>>(QDataStream &in, const KCalCore::Alarm::Ptr &); +KCALCORE_EXPORT QDataStream &operator>>(QDataStream &in, const KCalendarCore::Alarm::Ptr &); } //@cond PRIVATE -Q_DECLARE_TYPEINFO(KCalCore::Alarm::Ptr, Q_MOVABLE_TYPE); -Q_DECLARE_METATYPE(KCalCore::Alarm::Ptr) +Q_DECLARE_TYPEINFO(KCalendarCore::Alarm::Ptr, Q_MOVABLE_TYPE); +Q_DECLARE_METATYPE(KCalendarCore::Alarm::Ptr) //@endcond #endif diff --git a/src/attachment.cpp b/src/attachment.cpp index acddb3fc9..0fad2f81c 100644 --- a/src/attachment.cpp +++ b/src/attachment.cpp @@ -1,246 +1,246 @@ /* This file is part of the kcalcore library. Copyright (c) 2002 Michael Brade This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the Attachment class. @brief Represents information related to an attachment for a Calendar Incidence. @author Michael Brade \ */ #include "attachment.h" #include -using namespace KCalCore; +using namespace KCalendarCore; /** Private class that helps to provide binary compatibility between releases. @internal */ //@cond PRIVATE -class Q_DECL_HIDDEN KCalCore::Attachment::Private : public QSharedData +class Q_DECL_HIDDEN KCalendarCore::Attachment::Private : public QSharedData { public: Private() = default; Private(const QString &mime, bool binary) : mMimeType(mime), mBinary(binary) {} Private(const Private &other) = default; ~Private() { } mutable uint mSize = 0; mutable QByteArray mDecodedDataCache; QString mMimeType; QString mUri; QByteArray mEncodedData; QString mLabel; bool mBinary = false; bool mLocal = false; bool mShowInline = false; }; //@endcond Attachment::Attachment() : d(new Attachment::Private) { } Attachment::Attachment(const Attachment &attachment) = default; Attachment::Attachment(const QString &uri, const QString &mime) : d(new Attachment::Private(mime, false)) { d->mUri = uri; } Attachment::Attachment(const QByteArray &base64, const QString &mime) : d(new Attachment::Private(mime, true)) { d->mEncodedData = base64; } Attachment::~Attachment() = default; bool Attachment::isEmpty() const { return d->mMimeType.isEmpty() && d->mUri.isEmpty() && d->mEncodedData.isEmpty(); } bool Attachment::isUri() const { return !d->mBinary; } QString Attachment::uri() const { if (!d->mBinary) { return d->mUri; } else { return QString(); } } void Attachment::setUri(const QString &uri) { d->mUri = uri; d->mBinary = false; } bool Attachment::isBinary() const { return d->mBinary; } QByteArray Attachment::data() const { if (d->mBinary) { return d->mEncodedData; } else { return QByteArray(); } } QByteArray Attachment::decodedData() const { if (d->mDecodedDataCache.isNull()) { d->mDecodedDataCache = QByteArray::fromBase64(d->mEncodedData); } return d->mDecodedDataCache; } void Attachment::setDecodedData(const QByteArray &data) { setData(data.toBase64()); d->mDecodedDataCache = data; d->mSize = d->mDecodedDataCache.size(); } void Attachment::setData(const QByteArray &base64) { d->mEncodedData = base64; d->mBinary = true; d->mDecodedDataCache = QByteArray(); d->mSize = 0; } uint Attachment::size() const { if (isUri()) { return 0; } if (!d->mSize) { d->mSize = decodedData().size(); } return d->mSize; } QString Attachment::mimeType() const { return d->mMimeType; } void Attachment::setMimeType(const QString &mime) { d->mMimeType = mime; } bool Attachment::showInline() const { return d->mShowInline; } void Attachment::setShowInline(bool showinline) { d->mShowInline = showinline; } QString Attachment::label() const { return d->mLabel; } void Attachment::setLabel(const QString &label) { d->mLabel = label; } bool Attachment::isLocal() const { return d->mLocal; } void Attachment::setLocal(bool local) { d->mLocal = local; } Attachment &Attachment::operator=(const Attachment &other) = default; bool Attachment::operator==(const Attachment &a2) const { return uri() == a2.uri() && d->mLabel == a2.label() && d->mLocal == a2.isLocal() && d->mBinary == a2.isBinary() && d->mShowInline == a2.showInline() && size() == a2.size() && decodedData() == a2.decodedData(); } bool Attachment::operator!=(const Attachment &a2) const { return !(*this == a2); } -QDataStream &KCalCore::operator<<(QDataStream &out, const KCalCore::Attachment &a) +QDataStream &KCalendarCore::operator<<(QDataStream &out, const KCalendarCore::Attachment &a) { out << a.d->mSize << a.d->mMimeType << a.d->mUri << a.d->mEncodedData << a.d->mLabel << a.d->mBinary << a.d->mLocal << a.d->mShowInline; return out; } -QDataStream &KCalCore::operator>>(QDataStream &in, KCalCore::Attachment &a) +QDataStream &KCalendarCore::operator>>(QDataStream &in, KCalendarCore::Attachment &a) { in >> a.d->mSize >> a.d->mMimeType >> a.d->mUri >> a.d->mEncodedData >> a.d->mLabel >> a.d->mBinary >> a.d->mLocal >> a.d->mShowInline; return in; } diff --git a/src/attachment.h b/src/attachment.h index 27c1899a5..aa2a57c8b 100644 --- a/src/attachment.h +++ b/src/attachment.h @@ -1,306 +1,306 @@ /* This file is part of the kcalcore library. Copyright (c) 2002 Michael Brade This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the Attachment class. @author Michael Brade \ */ #ifndef KCALCORE_ATTACHMENT_H #define KCALCORE_ATTACHMENT_H #include "kcalcore_export.h" #include #include #include #include -namespace KCalCore +namespace KCalendarCore { /** @brief Represents information related to an attachment for a Calendar Incidence. This is not an email message attachment. Calendar Incidence attachments consist of: - A Uniform Resource Identifier (URI) or a base64 encoded binary blob. - A Multipurpose Internet Mail Extensions (MIME) type. This class is used to associate files (local or remote) or other resources with a Calendar Incidence. */ class KCALCORE_EXPORT Attachment { Q_GADGET Q_PROPERTY(bool isEmpty READ isEmpty) Q_PROPERTY(QString uri READ uri WRITE setUri) Q_PROPERTY(bool isUri READ isUri) Q_PROPERTY(bool isBinary READ isBinary) Q_PROPERTY(int size READ size) Q_PROPERTY(QString mimeType READ mimeType WRITE setMimeType) Q_PROPERTY(bool showInline READ showInline WRITE setShowInline) Q_PROPERTY(QString label READ label WRITE setLabel) Q_PROPERTY(bool isLocal READ isLocal WRITE setLocal) public: /** List of attachments. */ typedef QVector List; /** Constructs an empty attachment. */ explicit Attachment(); /** Constructs an attachment consisting of a @p uri and a @p mime type. @param uri is the @acronym URI referred to by this attachment. @param mime is the (optional) @acronym MIME type of the @p uri */ explicit Attachment(const QString &uri, const QString &mime = QString()); /** Constructs an attachment consisting of a binary blob of data and a @p mime type. @param base64 is the binary data in base64 format for the attachment. @param mime is the (optional) @acronym MIME type of the attachment */ explicit Attachment(const QByteArray &base64, const QString &mime = QString()); /** Constructs an attachment by copying another attachment. @param attachment is the attachment to be copied. */ Attachment(const Attachment &attachment); /** Destroys the attachment. */ ~Attachment(); /** Returns whether this is an empty or default constructed object. */ bool isEmpty() const; /** Sets the @acronym URI for this attachment to @p uri. @param uri is the @acronym URI to use for the attachment. @see uri(), isUri() */ void setUri(const QString &uri); /** Returns the @acronym URI of the attachment. @see setUri(), isUri() */ Q_REQUIRED_RESULT QString uri() const; /** Returns true if the attachment has a @acronym URI; false otherwise. @see uri(), setUri(I), isBinary() */ Q_REQUIRED_RESULT bool isUri() const; /** Returns true if the attachment has a binary blob; false otherwise. @see isUri() */ Q_REQUIRED_RESULT bool isBinary() const; /** Sets the base64 encoded binary blob data of the attachment. @param base64 contains the base64 encoded binary data. @see data(), decodedData() */ void setData(const QByteArray &base64); /** Returns a pointer to a QByteArray containing the base64 encoded binary data of the attachment. @see setData(), setDecodedData() */ Q_REQUIRED_RESULT QByteArray data() const; /** Sets the decoded attachment data. @param data is the decoded base64 binary data. @see decodedData(), data() */ void setDecodedData(const QByteArray &data); /** Returns a QByteArray containing the decoded base64 binary data of the attachment. @see setDecodedData(), setData() */ Q_REQUIRED_RESULT QByteArray decodedData() const; /** Returns the size of the attachment, in bytes. If the attachment is binary (i.e, there is no @acronym URI associated with the attachment) then a value of 0 is returned. */ uint size() const; /** Sets the @acronym MIME-type of the attachment to @p mime. @param mime is the string to use for the attachment @acronym MIME-type. @see mimeType() */ void setMimeType(const QString &mime); /** Returns the @acronym MIME-type of the attachment. @see setMimeType() */ Q_REQUIRED_RESULT QString mimeType() const; /** Sets the attachment "show in-line" option, which is derived from the Calendar Incidence @b X-CONTENT-DISPOSITION parameter. @param showinline is the flag to set (true) or unset (false) for the attachment "show in-line" option. @see showInline() */ void setShowInline(bool showinline); /** Returns the attachment "show in-line" flag. @see setShowInline() */ Q_REQUIRED_RESULT bool showInline() const; /** Sets the attachment label to @p label, which is derived from the Calendar Incidence @b X-LABEL parameter. @param label is the string to use for the attachment label. @see label() */ void setLabel(const QString &label); /** Returns the attachment label string. */ Q_REQUIRED_RESULT QString label() const; /** Sets the attachment "local" option, which is derived from the Calendar Incidence @b X-KONTACT-TYPE parameter. @param local is the flag to set (true) or unset (false) for the attachment "local" option. @see local() */ void setLocal(bool local); /** Returns the attachment "local" flag. */ Q_REQUIRED_RESULT bool isLocal() const; /** Assignment operator. @param attachment is the attachment to assign. */ Attachment &operator=(const Attachment &attachment); /** Compare this with @p attachment for equality. @param attachment is the attachment to compare. @return true if the attachments are equal; false otherwise. */ bool operator==(const Attachment &attachment) const; /** Compare this with @p attachment for inequality. @param attachment is the attachment to compare. @return true if the attachments are /not/ equal; false otherwise. */ bool operator!=(const Attachment &attachment) const; private: //@cond PRIVATE class Private; QSharedDataPointer d; //@endcond - friend KCALCORE_EXPORT QDataStream &operator<<(QDataStream &s, const KCalCore::Attachment&); - friend KCALCORE_EXPORT QDataStream &operator>>(QDataStream &s, KCalCore::Attachment&); + friend KCALCORE_EXPORT QDataStream &operator<<(QDataStream &s, const KCalendarCore::Attachment&); + friend KCALCORE_EXPORT QDataStream &operator>>(QDataStream &s, KCalendarCore::Attachment&); }; /** * Attachment serializer. * * @since 4.12 */ -KCALCORE_EXPORT QDataStream &operator<<(QDataStream &out, const KCalCore::Attachment&); +KCALCORE_EXPORT QDataStream &operator<<(QDataStream &out, const KCalendarCore::Attachment&); /** * Attachment deserializer. * * @since 4.12 */ -KCALCORE_EXPORT QDataStream &operator>>(QDataStream &in, KCalCore::Attachment&); +KCALCORE_EXPORT QDataStream &operator>>(QDataStream &in, KCalendarCore::Attachment&); } //@cond PRIVATE -Q_DECLARE_TYPEINFO(KCalCore::Attachment, Q_MOVABLE_TYPE); -Q_DECLARE_METATYPE(KCalCore::Attachment) +Q_DECLARE_TYPEINFO(KCalendarCore::Attachment, Q_MOVABLE_TYPE); +Q_DECLARE_METATYPE(KCalendarCore::Attachment) //@endcond #endif diff --git a/src/attendee.cpp b/src/attendee.cpp index 4e4d24924..a64c9aec5 100644 --- a/src/attendee.cpp +++ b/src/attendee.cpp @@ -1,369 +1,369 @@ /* This file is part of the kcalcore library. Copyright (c) 2001 Cornelius Schumacher Copyright (C) 2010 Casey Link Copyright (C) 2009-2010 Klaralvdalens Datakonsult AB, a KDAB Group company This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the Attendee class. @brief Represents information related to an attendee of an Calendar Incidence. @author Cornelius Schumacher \ */ #include "attendee.h" #include "person.h" #include "person_p.h" #include -using namespace KCalCore; +using namespace KCalendarCore; /** Private class that helps to provide binary compatibility between releases. @internal */ //@cond PRIVATE -class Q_DECL_HIDDEN KCalCore::Attendee::Private : public QSharedData +class Q_DECL_HIDDEN KCalendarCore::Attendee::Private : public QSharedData { public: void setCuType(CuType cuType); void setCuType(const QString &cuType); CuType cuType() const; QString cuTypeStr() const; bool mRSVP = false; Role mRole; PartStat mStatus; mutable QString mUid; QString mDelegate; QString mDelegator; CustomProperties mCustomProperties; QString mName; QString mEmail; private: QString sCuType; CuType mCuType; }; //@endcond -void KCalCore::Attendee::Private::setCuType(Attendee::CuType cuType) +void KCalendarCore::Attendee::Private::setCuType(Attendee::CuType cuType) { mCuType = cuType; sCuType.clear(); } -void KCalCore::Attendee::Private::setCuType(const QString &cuType) +void KCalendarCore::Attendee::Private::setCuType(const QString &cuType) { const QString upper = cuType.toUpper(); if (upper == QLatin1String("INDIVIDUAL")) { setCuType(Attendee::Individual); } else if (upper == QLatin1String("GROUP")) { setCuType(Attendee::Group); } else if (upper == QLatin1String("RESOURCE")) { setCuType(Attendee::Resource); } else if (upper == QLatin1String("ROOM")) { setCuType(Attendee::Room); } else { setCuType(Attendee::Unknown); if (upper.startsWith(QLatin1String("X-")) || upper.startsWith(QLatin1String("IANA-"))) { sCuType = upper; } } } -Attendee::CuType KCalCore::Attendee::Private::cuType() const +Attendee::CuType KCalendarCore::Attendee::Private::cuType() const { return mCuType; } -QString KCalCore::Attendee::Private::cuTypeStr() const +QString KCalendarCore::Attendee::Private::cuTypeStr() const { switch (mCuType) { case Attendee::Individual: return QStringLiteral("INDIVIDUAL"); case Attendee::Group: return QStringLiteral("GROUP"); case Attendee::Resource: return QStringLiteral("RESOURCE"); case Attendee::Room: return QStringLiteral("ROOM"); case Attendee::Unknown: if (sCuType.isEmpty()) { return QStringLiteral("UNKNOWN"); } else { return sCuType; } } return QStringLiteral("UNKNOWN"); } Attendee::Attendee() : d(new Attendee::Private) { } Attendee::Attendee(const QString &name, const QString &email, bool rsvp, Attendee::PartStat status, Attendee::Role role, const QString &uid) : d(new Attendee::Private) { setName(name); setEmail(email); d->mRSVP = rsvp; d->mStatus = status; d->mRole = role; d->mUid = uid; d->setCuType(Attendee::Individual); } Attendee::Attendee(const Attendee &attendee) : d(attendee.d) { } Attendee::~Attendee() = default; bool Attendee::isNull() const { // isNull rather than isEmpty, as user code is actually creating empty but non-null attendees... return d->mName.isNull() && d->mEmail.isNull(); } -bool KCalCore::Attendee::operator==(const Attendee &attendee) const +bool KCalendarCore::Attendee::operator==(const Attendee &attendee) const { return d->mUid == attendee.d->mUid && d->mRSVP == attendee.d->mRSVP && d->mRole == attendee.d->mRole && d->mStatus == attendee.d->mStatus && d->mDelegate == attendee.d->mDelegate && d->mDelegator == attendee.d->mDelegator && d->cuTypeStr() == attendee.d->cuTypeStr() && d->mName == attendee.d->mName && d->mEmail == attendee.d->mEmail; } -bool KCalCore::Attendee::operator!=(const Attendee &attendee) const +bool KCalendarCore::Attendee::operator!=(const Attendee &attendee) const { return !operator==(attendee); } -Attendee &KCalCore::Attendee::operator=(const Attendee &attendee) +Attendee &KCalendarCore::Attendee::operator=(const Attendee &attendee) { // check for self assignment if (&attendee == this) { return *this; } d = attendee.d; return *this; } QString Attendee::name() const { return d->mName; } void Attendee::setName(const QString &name) { if (name.startsWith(QLatin1String("mailto:"), Qt::CaseInsensitive)) { d->mName = name.mid(7); } else { d->mName = name; } } QString Attendee::fullName() const { return fullNameHelper(d->mName, d->mEmail); } QString Attendee::email() const { return d->mEmail; } void Attendee::setEmail(const QString &email) { if (email.startsWith(QLatin1String("mailto:"), Qt::CaseInsensitive)) { d->mEmail = email.mid(7); } else { d->mEmail = email; } } void Attendee::setRSVP(bool r) { d->mRSVP = r; } bool Attendee::RSVP() const { return d->mRSVP; } void Attendee::setStatus(Attendee::PartStat status) { d->mStatus = status; } Attendee::PartStat Attendee::status() const { return d->mStatus; } void Attendee::setCuType(Attendee::CuType cuType) { d->setCuType(cuType); } void Attendee::setCuType(const QString &cuType) { d->setCuType(cuType); } Attendee::CuType Attendee::cuType() const { return d->cuType(); } QString Attendee::cuTypeStr() const { return d->cuTypeStr(); } void Attendee::setRole(Attendee::Role role) { d->mRole = role; } Attendee::Role Attendee::role() const { return d->mRole; } void Attendee::setUid(const QString &uid) { d->mUid = uid; } QString Attendee::uid() const { /* If Uid is empty, just use the pointer to Attendee (encoded to * string) as Uid. Only thing that matters is that the Uid is unique * insofar IncidenceBase is concerned, and this does that (albeit * not very nicely). If these are ever saved to disk, should use * (considerably more expensive) CalFormat::createUniqueId(). As Uid * is not part of Attendee in iCal std, it's fairly safe bet that * these will never hit disc though so faster generation speed is * more important than actually being forever unique.*/ if (d->mUid.isEmpty()) { d->mUid = QString::number((qlonglong)d.constData()); } return d->mUid; } void Attendee::setDelegate(const QString &delegate) { d->mDelegate = delegate; } QString Attendee::delegate() const { return d->mDelegate; } void Attendee::setDelegator(const QString &delegator) { d->mDelegator = delegator; } QString Attendee::delegator() const { return d->mDelegator; } void Attendee::setCustomProperty(const QByteArray &xname, const QString &xvalue) { d->mCustomProperties.setNonKDECustomProperty(xname, xvalue); } CustomProperties &Attendee::customProperties() { return d->mCustomProperties; } const CustomProperties &Attendee::customProperties() const { return d->mCustomProperties; } -QDataStream &KCalCore::operator<<(QDataStream &stream, const KCalCore::Attendee &attendee) +QDataStream &KCalendarCore::operator<<(QDataStream &stream, const KCalendarCore::Attendee &attendee) { - KCalCore::Person p(attendee.name(), attendee.email()); + KCalendarCore::Person p(attendee.name(), attendee.email()); stream << p; return stream << attendee.d->mRSVP << int(attendee.d->mRole) << int(attendee.d->mStatus) << attendee.d->mUid << attendee.d->mDelegate << attendee.d->mDelegator << attendee.d->cuTypeStr() << attendee.d->mCustomProperties; } -QDataStream &KCalCore::operator>>(QDataStream &stream, KCalCore::Attendee &attendee) +QDataStream &KCalendarCore::operator>>(QDataStream &stream, KCalendarCore::Attendee &attendee) { bool RSVP; Attendee::Role role; Attendee::PartStat status; QString uid; QString delegate; QString delegator; QString cuType; CustomProperties customProperties; uint role_int; uint status_int; - KCalCore::Person person; + KCalendarCore::Person person; stream >> person; stream >> RSVP >> role_int >> status_int >> uid >> delegate >> delegator >> cuType >> customProperties; role = Attendee::Role(role_int); status = Attendee::PartStat(status_int); attendee = Attendee(person.name(), person.email(), RSVP, status, role, uid); attendee.setDelegate(delegate); attendee.setDelegator(delegator); attendee.setCuType(cuType); attendee.d->mCustomProperties = customProperties; return stream; } diff --git a/src/attendee.h b/src/attendee.h index 9d9065f90..3a30b2ef7 100644 --- a/src/attendee.h +++ b/src/attendee.h @@ -1,391 +1,391 @@ /* This file is part of the kcalcore library. Copyright (c) 2001-2003 Cornelius Schumacher This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the Attendee class. @author Cornelius Schumacher \ */ #ifndef KCALCORE_ATTENDEE_H #define KCALCORE_ATTENDEE_H #include #include #include "kcalcore_export.h" #include "customproperties.h" -namespace KCalCore +namespace KCalendarCore { /** @brief Represents information related to an attendee of an Calendar Incidence, typically a meeting or task (to-do). Attendees are people with a name and (optional) email address who are invited to participate in some way in a meeting or task. This class also tracks that status of the invitation: accepted; tentatively accepted; declined; delegated to another person; in-progress; completed. Attendees may optionally be asked to @acronym RSVP ("Respond Please") to the invitation. Note that each attendee be can optionally associated with a @acronym UID (unique identifier) derived from a Calendar Incidence, Email Message, or any other thing you want. */ class KCALCORE_EXPORT Attendee { Q_GADGET Q_PROPERTY(bool isNull READ isNull) Q_PROPERTY(QString name READ name WRITE setName) Q_PROPERTY(QString fullName READ fullName) Q_PROPERTY(QString email READ email WRITE setEmail) Q_PROPERTY(Role role READ role WRITE setRole) Q_PROPERTY(QString uid READ uid WRITE setUid) Q_PROPERTY(PartStat status READ status WRITE setStatus) Q_PROPERTY(CuType cuType READ cuType WRITE setCuType) Q_PROPERTY(bool rsvp READ RSVP WRITE setRSVP) Q_PROPERTY(QString delegate READ delegate WRITE setDelegate) Q_PROPERTY(QString delegator READ delegator WRITE setDelegator) public: /** The different types of participant status. The meaning is specific to the incidence type in context. */ enum PartStat { NeedsAction, /**< Event, to-do or journal needs action (default) */ Accepted, /**< Event, to-do or journal accepted */ Declined, /**< Event, to-do or journal declined */ Tentative, /**< Event or to-do tentatively accepted */ Delegated, /**< Event or to-do delegated */ Completed, /**< To-do completed */ InProcess, /**< To-do in process of being completed */ None }; Q_ENUM(PartStat) /** The different types of participation roles. */ enum Role { ReqParticipant, /**< Participation is required (default) */ OptParticipant, /**< Participation is optional */ NonParticipant, /**< Non-Participant; copied for information purposes */ Chair /**< Chairperson */ }; Q_ENUM(Role) /** * The different types of a participant. * * @since 4.14 */ enum CuType { Individual, /**< An individual (default) */ Group, /**< A group of individuals */ Resource, /**< A physical resource */ Room, /**< A room resource */ Unknown /**< Otherwise not known */ /** * Parameters that have to set via the QString variant of @setCuType() and @cuType() * x-name ; Experimental cuType * iana-token ; Other IANA-registered */ }; Q_ENUM(CuType) /** List of attendees. */ typedef QVector List; /** Create a null Attendee. */ Attendee(); /** Constructs an attendee consisting of a person name (@p name) and email address (@p email); invitation status and #Role; an optional @acronym RSVP flag and @acronym UID. @param name is person name of the attendee. @param email is person email address of the attendee. @param rsvp if true, the attendee is requested to reply to invitations. @param status is the #PartStat status of the attendee. @param role is the #Role of the attendee. @param uid is the @acronym UID of the attendee. */ Attendee(const QString &name, const QString &email, bool rsvp = false, PartStat status = None, Role role = ReqParticipant, const QString &uid = QString()); /** Constructs an attendee by copying another attendee. @param attendee is the attendee to be copied. */ Attendee(const Attendee &attendee); /** Destroys the attendee. */ ~Attendee(); /** * Returns @c true if this is a default-constructed Attendee instance. */ bool isNull() const; /** Returns the name of the attendee. */ Q_REQUIRED_RESULT QString name() const; /** Sets the name of the attendee to @p name. */ void setName(const QString &name); /** Returns the full name and email address of this attendee @return A QString containing the person's full name in the form "FirstName LastName \". */ Q_REQUIRED_RESULT QString fullName() const; /** Returns the email address for this attendee. */ Q_REQUIRED_RESULT QString email() const; /** Sets the email address for this attendee to @p email. */ void setEmail(const QString &email); /** Sets the Role of the attendee to @p role. @param role is the Role to use for the attendee. @see role() */ void setRole(Role role); /** Returns the Role of the attendee. @see setRole() */ Q_REQUIRED_RESULT Role role() const; /** Sets the @acronym UID of the attendee to @p uid. @param uid is the @acronym UID to use for the attendee. @see uid() */ void setUid(const QString &uid); /** Returns the @acronym UID of the attendee. @see setUid() */ Q_REQUIRED_RESULT QString uid() const; /** Sets the #PartStat of the attendee to @p status. @param status is the #PartStat to use for the attendee. @see status() */ void setStatus(PartStat status); /** Returns the #PartStat of the attendee. @see setStatus() */ Q_REQUIRED_RESULT PartStat status() const; /** Sets the #CuType of the attendee to @p cuType. @param cuType is the #CuType to use for the attendee. @see cuType() @since 4.14 */ void setCuType(CuType cuType); /** Sets the #CuType of the attendee to @p cuType. @param cuType is the #CuType to use for the attendee. @see cuType() @since 4.14 */ void setCuType(const QString &cuType); /** Returns the #CuType of the attendee. @see setCuType() @since 4.14 */ Q_REQUIRED_RESULT CuType cuType() const; /** Returns the #CuType of the attendee. @see setCuType() @since 4.14 */ Q_REQUIRED_RESULT QString cuTypeStr() const; /** Sets the @acronym RSVP flag of the attendee to @p rsvp. @param rsvp if set (true), the attendee is requested to reply to invitations. @see RSVP() */ void setRSVP(bool rsvp); /** Returns the attendee @acronym RSVP flag. @see setRSVP() */ Q_REQUIRED_RESULT bool RSVP() const; /** Compares this with @p attendee for equality. @param attendee the attendee to compare. */ bool operator==(const Attendee &attendee) const; /** Compares this with @p attendee for inequality. @param attendee the attendee to compare. */ bool operator!=(const Attendee &attendee) const; /** Sets the delegate. @param delegate is a string containing a MAILTO URI of those delegated to attend the meeting. @see delegate(), setDelegator(). */ void setDelegate(const QString &delegate); /** Returns the delegate. @see setDelegate(). */ Q_REQUIRED_RESULT QString delegate() const; /** Sets the delegator. @param delegator is a string containing a MAILTO URI of those who have delegated their meeting attendance. @see delegator(), setDelegate(). */ void setDelegator(const QString &delegator); /** Returns the delegator. @see setDelegator(). */ Q_REQUIRED_RESULT QString delegator() const; /** Adds a custom property. If the property already exists it will be overwritten. @param xname is the name of the property. @param xvalue is its value. */ void setCustomProperty(const QByteArray &xname, const QString &xvalue); /** Returns a reference to the CustomProperties object */ Q_REQUIRED_RESULT CustomProperties &customProperties(); /** Returns a const reference to the CustomProperties object */ const CustomProperties &customProperties() const; /** Sets this attendee equal to @p attendee. @param attendee is the attendee to copy. */ Attendee &operator=(const Attendee &attendee); private: //@cond PRIVATE class Private; QSharedDataPointer d; //@endcond friend KCALCORE_EXPORT QDataStream &operator<<(QDataStream &s, - const KCalCore::Attendee &attendee); + const KCalendarCore::Attendee &attendee); friend KCALCORE_EXPORT QDataStream &operator>>(QDataStream &s, - KCalCore::Attendee &attendee); + KCalendarCore::Attendee &attendee); }; /** Serializes an Attendee object into a data stream. @param stream is a QDataStream. @param attendee is a pointer to a Attendee object to be serialized. */ KCALCORE_EXPORT QDataStream &operator<<(QDataStream &stream, - const KCalCore::Attendee &attendee); + const KCalendarCore::Attendee &attendee); /** Initializes an Attendee object from a data stream. @param stream is a QDataStream. @param attendee is a pointer to a Attendee object to be initialized. */ KCALCORE_EXPORT QDataStream &operator>>(QDataStream &stream, - KCalCore::Attendee &attendee); + KCalendarCore::Attendee &attendee); } //@cond PRIVATE -Q_DECLARE_TYPEINFO(KCalCore::Attendee, Q_MOVABLE_TYPE); -Q_DECLARE_METATYPE(KCalCore::Attendee) +Q_DECLARE_TYPEINFO(KCalendarCore::Attendee, Q_MOVABLE_TYPE); +Q_DECLARE_METATYPE(KCalendarCore::Attendee) //@endcond #endif diff --git a/src/calendar.cpp b/src/calendar.cpp index 75880e171..d7c9b6a8c 100644 --- a/src/calendar.cpp +++ b/src/calendar.cpp @@ -1,1369 +1,1369 @@ /* This file is part of the kcalcore library. Copyright (c) 1998 Preston Brown Copyright (c) 2000-2004 Cornelius Schumacher Copyright (C) 2003-2004 Reinhold Kainhofer Copyright (c) 2006 David Jarvie This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the Calendar class. @brief Represents the main calendar class. @author Preston Brown \ @author Cornelius Schumacher \ @author Reinhold Kainhofer \ @author David Jarvie \ */ #include "calendar.h" #include "calendar_p.h" #include "calfilter.h" #include "icaltimezones_p.h" #include "sorting.h" #include "visitor.h" #include "utils.h" #include "kcalcore_debug.h" #include extern "C" { #include } #include // for std::remove() -using namespace KCalCore; +using namespace KCalendarCore; /** Make a QHash::value that returns a QVector. */ template QVector values(const QMultiHash &c) { QVector v; v.reserve(c.size()); for (typename QMultiHash::const_iterator it = c.begin(), end = c.end(); it != end; ++it) { v.push_back(it.value()); } return v; } template QVector values(const QMultiHash &c, const K &x) { QVector v; typename QMultiHash::const_iterator it = c.find(x); while (it != c.end() && it.key() == x) { v.push_back(it.value()); ++it; } return v; } /** Template for a class that implements a visitor for adding an Incidence to a resource supporting addEvent(), addTodo() and addJournal() calls. */ template class AddVisitor : public Visitor { public: AddVisitor(T *r) : mResource(r) {} bool visit(const Event::Ptr &e) override { return mResource->addEvent(e); } bool visit(const Todo::Ptr &t) override { return mResource->addTodo(t); } bool visit(const Journal::Ptr &j) override { return mResource->addJournal(j); } bool visit(const FreeBusy::Ptr &) override { return false; } private: T *mResource; }; /** Template for a class that implements a visitor for deleting an Incidence from a resource supporting deleteEvent(), deleteTodo() and deleteJournal() calls. */ template class DeleteVisitor : public Visitor { public: DeleteVisitor(T *r) : mResource(r) {} bool visit(const Event::Ptr &e) override { mResource->deleteEvent(e); return true; } bool visit(const Todo::Ptr &t) override { mResource->deleteTodo(t); return true; } bool visit(const Journal::Ptr &j) override { mResource->deleteJournal(j); return true; } bool visit(const FreeBusy::Ptr &) override { return false; } private: T *mResource; }; //@endcond Calendar::Calendar(const QTimeZone &timeZone) - : d(new KCalCore::Calendar::Private) + : d(new KCalendarCore::Calendar::Private) { d->mTimeZone = timeZone; } Calendar::Calendar(const QByteArray &timeZoneId) - : d(new KCalCore::Calendar::Private) + : d(new KCalendarCore::Calendar::Private) { setTimeZoneId(timeZoneId); } Calendar::~Calendar() { delete d; } Person Calendar::owner() const { return d->mOwner; } void Calendar::setOwner(const Person &owner) { d->mOwner = owner; setModified(true); } void Calendar::setTimeZone(const QTimeZone &timeZone) { d->mTimeZone = timeZone; doSetTimeZone(d->mTimeZone); } QTimeZone Calendar::timeZone() const { return d->mTimeZone; } void Calendar::setTimeZoneId(const QByteArray &timeZoneId) { d->mTimeZone = d->timeZoneIdSpec(timeZoneId); doSetTimeZone(d->mTimeZone); //NOLINT false clang-analyzer-optin.cplusplus.VirtualCall } //@cond PRIVATE QTimeZone Calendar::Private::timeZoneIdSpec(const QByteArray &timeZoneId) { if (timeZoneId == QByteArrayLiteral("UTC")) { return QTimeZone::utc(); } auto tz = QTimeZone(timeZoneId); if (tz.isValid()) { return tz; } return QTimeZone::systemTimeZone(); } //@endcond QByteArray Calendar::timeZoneId() const { return d->mTimeZone.id(); } void Calendar::shiftTimes(const QTimeZone &oldZone, const QTimeZone &newZone) { setTimeZone(newZone); int i, end; Event::List ev = events(); for (i = 0, end = ev.count(); i < end; ++i) { ev[i]->shiftTimes(oldZone, newZone); } Todo::List to = todos(); for (i = 0, end = to.count(); i < end; ++i) { to[i]->shiftTimes(oldZone, newZone); } Journal::List jo = journals(); for (i = 0, end = jo.count(); i < end; ++i) { jo[i]->shiftTimes(oldZone, newZone); } } void Calendar::setFilter(CalFilter *filter) { if (filter) { d->mFilter = filter; } else { d->mFilter = d->mDefaultFilter; } emit filterChanged(); } CalFilter *Calendar::filter() const { return d->mFilter; } QStringList Calendar::categories() const { Incidence::List rawInc(rawIncidences()); QStringList cats, thisCats; // @TODO: For now just iterate over all incidences. In the future, // the list of categories should be built when reading the file. for (Incidence::List::ConstIterator i = rawInc.constBegin(); i != rawInc.constEnd(); ++i) { thisCats = (*i)->categories(); for (QStringList::ConstIterator si = thisCats.constBegin(); si != thisCats.constEnd(); ++si) { if (!cats.contains(*si)) { cats.append(*si); } } } return cats; } Incidence::List Calendar::incidences(const QDate &date) const { return mergeIncidenceList(events(date), todos(date), journals(date)); } Incidence::List Calendar::incidences() const { return mergeIncidenceList(events(), todos(), journals()); } Incidence::List Calendar::rawIncidences() const { return mergeIncidenceList(rawEvents(), rawTodos(), rawJournals()); } Incidence::List Calendar::instances(const Incidence::Ptr &incidence) const { if (incidence) { Event::List elist; Todo::List tlist; Journal::List jlist; if (incidence->type() == Incidence::TypeEvent) { elist = eventInstances(incidence); } else if (incidence->type() == Incidence::TypeTodo) { tlist = todoInstances(incidence); } else if (incidence->type() == Incidence::TypeJournal) { jlist = journalInstances(incidence); } return mergeIncidenceList(elist, tlist, jlist); } else { return Incidence::List(); } } Incidence::List Calendar::duplicates(const Incidence::Ptr &incidence) { if (incidence) { Incidence::List list; Incidence::List vals = values(d->mNotebookIncidences); Incidence::List::const_iterator it; for (it = vals.constBegin(); it != vals.constEnd(); ++it) { if (((incidence->dtStart() == (*it)->dtStart()) || (!incidence->dtStart().isValid() && !(*it)->dtStart().isValid())) && (incidence->summary() == (*it)->summary())) { list.append(*it); } } return list; } else { return Incidence::List(); } } bool Calendar::addNotebook(const QString ¬ebook, bool isVisible) { if (d->mNotebooks.contains(notebook)) { return false; } else { d->mNotebooks.insert(notebook, isVisible); return true; } } bool Calendar::updateNotebook(const QString ¬ebook, bool isVisible) { if (!d->mNotebooks.contains(notebook)) { return false; } else { d->mNotebooks.insert(notebook, isVisible); return true; } } bool Calendar::deleteNotebook(const QString ¬ebook) { if (!d->mNotebooks.contains(notebook)) { return false; } else { return d->mNotebooks.remove(notebook); } } bool Calendar::setDefaultNotebook(const QString ¬ebook) { if (!d->mNotebooks.contains(notebook)) { return false; } else { d->mDefaultNotebook = notebook; return true; } } QString Calendar::defaultNotebook() const { return d->mDefaultNotebook; } bool Calendar::hasValidNotebook(const QString ¬ebook) const { return d->mNotebooks.contains(notebook); } bool Calendar::isVisible(const Incidence::Ptr &incidence) const { if (d->mIncidenceVisibility.contains(incidence)) { return d->mIncidenceVisibility[incidence]; } const QString nuid = notebook(incidence); bool rv; if (d->mNotebooks.contains(nuid)) { rv = d->mNotebooks.value(nuid); } else { // NOTE returns true also for nonexisting notebooks for compatibility rv = true; } d->mIncidenceVisibility[incidence] = rv; return rv; } void Calendar::clearNotebookAssociations() { d->mNotebookIncidences.clear(); d->mUidToNotebook.clear(); d->mIncidenceVisibility.clear(); } bool Calendar::setNotebook(const Incidence::Ptr &inc, const QString ¬ebook) { if (!inc) { return false; } if (!notebook.isEmpty() && !incidence(inc->uid(), inc->recurrenceId())) { qCWarning(KCALCORE_LOG) << "cannot set notebook until incidence has been added"; return false; } if (d->mUidToNotebook.contains(inc->uid())) { QString old = d->mUidToNotebook.value(inc->uid()); if (!old.isEmpty() && notebook != old) { if (inc->hasRecurrenceId()) { qCWarning(KCALCORE_LOG) << "cannot set notebook for child incidences"; return false; } // Move all possible children also. Incidence::List list = instances(inc); Incidence::List::Iterator it; for (it = list.begin(); it != list.end(); ++it) { d->mNotebookIncidences.remove(old, *it); d->mNotebookIncidences.insert(notebook, *it); } notifyIncidenceChanged(inc); // for removing from old notebook // don not remove from mUidToNotebook to keep deleted incidences d->mNotebookIncidences.remove(old, inc); } } if (!notebook.isEmpty()) { d->mUidToNotebook.insert(inc->uid(), notebook); d->mNotebookIncidences.insert(notebook, inc); qCDebug(KCALCORE_LOG) << "setting notebook" << notebook << "for" << inc->uid(); notifyIncidenceChanged(inc); // for inserting into new notebook } return true; } QString Calendar::notebook(const Incidence::Ptr &incidence) const { if (incidence) { return d->mUidToNotebook.value(incidence->uid()); } else { return QString(); } } QString Calendar::notebook(const QString &uid) const { return d->mUidToNotebook.value(uid); } QStringList Calendar::notebooks() const { return d->mNotebookIncidences.uniqueKeys(); } Incidence::List Calendar::incidences(const QString ¬ebook) const { if (notebook.isEmpty()) { return values(d->mNotebookIncidences); } else { return values(d->mNotebookIncidences, notebook); } } /** static */ Event::List Calendar::sortEvents(const Event::List &eventList, EventSortField sortField, SortDirection sortDirection) { if (eventList.isEmpty()) { return Event::List(); } Event::List eventListSorted; // Notice we alphabetically presort Summaries first. // We do this so comparison "ties" stay in a nice order. eventListSorted = eventList; switch (sortField) { case EventSortUnsorted: break; case EventSortStartDate: if (sortDirection == SortDirectionAscending) { std::sort(eventListSorted.begin(), eventListSorted.end(), Events::startDateLessThan); } else { std::sort(eventListSorted.begin(), eventListSorted.end(), Events::startDateMoreThan); } break; case EventSortEndDate: if (sortDirection == SortDirectionAscending) { std::sort(eventListSorted.begin(), eventListSorted.end(), Events::endDateLessThan); } else { std::sort(eventListSorted.begin(), eventListSorted.end(), Events::endDateMoreThan); } break; case EventSortSummary: if (sortDirection == SortDirectionAscending) { std::sort(eventListSorted.begin(), eventListSorted.end(), Events::summaryLessThan); } else { std::sort(eventListSorted.begin(), eventListSorted.end(), Events::summaryMoreThan); } break; } return eventListSorted; } Event::List Calendar::events(const QDate &date, const QTimeZone &timeZone, EventSortField sortField, SortDirection sortDirection) const { Event::List el = rawEventsForDate(date, timeZone, sortField, sortDirection); d->mFilter->apply(&el); return el; } Event::List Calendar::events(const QDateTime &dt) const { Event::List el = rawEventsForDate(dt); d->mFilter->apply(&el); return el; } Event::List Calendar::events(const QDate &start, const QDate &end, const QTimeZone &timeZone, bool inclusive) const { Event::List el = rawEvents(start, end, timeZone, inclusive); d->mFilter->apply(&el); return el; } Event::List Calendar::events(EventSortField sortField, SortDirection sortDirection) const { Event::List el = rawEvents(sortField, sortDirection); d->mFilter->apply(&el); return el; } bool Calendar::addIncidence(const Incidence::Ptr &incidence) { if (!incidence) { return false; } AddVisitor v(this); return incidence->accept(v, incidence); } bool Calendar::deleteIncidence(const Incidence::Ptr &incidence) { if (!incidence) { return false; } if (beginChange(incidence)) { DeleteVisitor v(this); const bool result = incidence->accept(v, incidence); endChange(incidence); return result; } else { return false; } } Incidence::Ptr Calendar::createException(const Incidence::Ptr &incidence, const QDateTime &recurrenceId, bool thisAndFuture) { Q_ASSERT(recurrenceId.isValid()); if (!incidence || !incidence->recurs() || !recurrenceId.isValid()) { return Incidence::Ptr(); } Incidence::Ptr newInc(incidence->clone()); newInc->setCreated(QDateTime::currentDateTimeUtc()); newInc->setRevision(0); //Recurring exceptions are not support for now newInc->clearRecurrence(); newInc->setRecurrenceId(recurrenceId); newInc->setThisAndFuture(thisAndFuture); newInc->setDtStart(recurrenceId); // Calculate and set the new end of the incidence QDateTime end = incidence->dateTime(IncidenceBase::RoleEnd); if (end.isValid()) { if (incidence->allDay()) { qint64 offset = incidence->dtStart().daysTo(recurrenceId); end = end.addDays(offset); } else { qint64 offset = incidence->dtStart().secsTo(recurrenceId); end = end.addSecs(offset); } newInc->setDateTime(end, IncidenceBase::RoleEnd); } return newInc; } Incidence::Ptr Calendar::incidence(const QString &uid, const QDateTime &recurrenceId) const { Incidence::Ptr i = event(uid, recurrenceId); if (i) { return i; } i = todo(uid, recurrenceId); if (i) { return i; } i = journal(uid, recurrenceId); return i; } Incidence::Ptr Calendar::deleted(const QString &uid, const QDateTime &recurrenceId) const { Incidence::Ptr i = deletedEvent(uid, recurrenceId); if (i) { return i; } i = deletedTodo(uid, recurrenceId); if (i) { return i; } i = deletedJournal(uid, recurrenceId); return i; } Incidence::List Calendar::incidencesFromSchedulingID(const QString &sid) const { Incidence::List result; const Incidence::List incidences = rawIncidences(); Incidence::List::const_iterator it = incidences.begin(); for (; it != incidences.end(); ++it) { if ((*it)->schedulingID() == sid) { result.append(*it); } } return result; } Incidence::Ptr Calendar::incidenceFromSchedulingID(const QString &uid) const { const Incidence::List incidences = rawIncidences(); Incidence::List::const_iterator it = incidences.begin(); for (; it != incidences.end(); ++it) { if ((*it)->schedulingID() == uid) { // Touchdown, and the crowd goes wild return *it; } } // Not found return Incidence::Ptr(); } /** static */ Todo::List Calendar::sortTodos(const Todo::List &todoList, TodoSortField sortField, SortDirection sortDirection) { if (todoList.isEmpty()) { return Todo::List(); } Todo::List todoListSorted; // Notice we alphabetically presort Summaries first. // We do this so comparison "ties" stay in a nice order. // Note that To-dos may not have Start DateTimes nor due DateTimes. todoListSorted = todoList; switch (sortField) { case TodoSortUnsorted: break; case TodoSortStartDate: if (sortDirection == SortDirectionAscending) { std::sort(todoListSorted.begin(), todoListSorted.end(), Todos::startDateLessThan); } else { std::sort(todoListSorted.begin(), todoListSorted.end(), Todos::startDateMoreThan); } break; case TodoSortDueDate: if (sortDirection == SortDirectionAscending) { std::sort(todoListSorted.begin(), todoListSorted.end(), Todos::dueDateLessThan); } else { std::sort(todoListSorted.begin(), todoListSorted.end(), Todos::dueDateMoreThan); } break; case TodoSortPriority: if (sortDirection == SortDirectionAscending) { std::sort(todoListSorted.begin(), todoListSorted.end(), Todos::priorityLessThan); } else { std::sort(todoListSorted.begin(), todoListSorted.end(), Todos::priorityMoreThan); } break; case TodoSortPercentComplete: if (sortDirection == SortDirectionAscending) { std::sort(todoListSorted.begin(), todoListSorted.end(), Todos::percentLessThan); } else { std::sort(todoListSorted.begin(), todoListSorted.end(), Todos::percentMoreThan); } break; case TodoSortSummary: if (sortDirection == SortDirectionAscending) { std::sort(todoListSorted.begin(), todoListSorted.end(), Todos::summaryLessThan); } else { std::sort(todoListSorted.begin(), todoListSorted.end(), Todos::summaryMoreThan); } break; case TodoSortCreated: if (sortDirection == SortDirectionAscending) { std::sort(todoListSorted.begin(), todoListSorted.end(), Todos::createdLessThan); } else { std::sort(todoListSorted.begin(), todoListSorted.end(), Todos::createdMoreThan); } break; } return todoListSorted; } Todo::List Calendar::todos(TodoSortField sortField, SortDirection sortDirection) const { Todo::List tl = rawTodos(sortField, sortDirection); d->mFilter->apply(&tl); return tl; } Todo::List Calendar::todos(const QDate &date) const { Todo::List el = rawTodosForDate(date); d->mFilter->apply(&el); return el; } Todo::List Calendar::todos(const QDate &start, const QDate &end, const QTimeZone &timeZone, bool inclusive) const { Todo::List tl = rawTodos(start, end, timeZone, inclusive); d->mFilter->apply(&tl); return tl; } /** static */ Journal::List Calendar::sortJournals(const Journal::List &journalList, JournalSortField sortField, SortDirection sortDirection) { if (journalList.isEmpty()) { return Journal::List(); } Journal::List journalListSorted = journalList; switch (sortField) { case JournalSortUnsorted: break; case JournalSortDate: if (sortDirection == SortDirectionAscending) { std::sort(journalListSorted.begin(), journalListSorted.end(), Journals::dateLessThan); } else { std::sort(journalListSorted.begin(), journalListSorted.end(), Journals::dateMoreThan); } break; case JournalSortSummary: if (sortDirection == SortDirectionAscending) { std::sort(journalListSorted.begin(), journalListSorted.end(), Journals::summaryLessThan); } else { std::sort(journalListSorted.begin(), journalListSorted.end(), Journals::summaryMoreThan); } break; } return journalListSorted; } Journal::List Calendar::journals(JournalSortField sortField, SortDirection sortDirection) const { Journal::List jl = rawJournals(sortField, sortDirection); d->mFilter->apply(&jl); return jl; } Journal::List Calendar::journals(const QDate &date) const { Journal::List el = rawJournalsForDate(date); d->mFilter->apply(&el); return el; } // When this is called, the to-dos have already been added to the calendar. // This method is only about linking related to-dos. void Calendar::setupRelations(const Incidence::Ptr &forincidence) { if (!forincidence) { return; } const QString uid = forincidence->uid(); // First, go over the list of orphans and see if this is their parent Incidence::List l = values(d->mOrphans, uid); d->mOrphans.remove(uid); if (!l.isEmpty()) { Incidence::List &relations = d->mIncidenceRelations[uid]; relations.reserve(relations.count() + l.count()); for (int i = 0, end = l.count(); i < end; ++i) { relations.append(l[i]); d->mOrphanUids.remove(l[i]->uid()); } } // Now see about this incidences parent if (forincidence->relatedTo().isEmpty() && !forincidence->relatedTo().isEmpty()) { // Incidence has a uid it is related to but is not registered to it yet. // Try to find it Incidence::Ptr parent = incidence(forincidence->relatedTo()); if (parent) { // Found it // look for hierarchy loops if (isAncestorOf(forincidence, parent)) { forincidence->setRelatedTo(QString()); qCWarning(KCALCORE_LOG) << "hierarchy loop between " << forincidence->uid() << " and " << parent->uid(); } else { d->mIncidenceRelations[parent->uid()].append(forincidence); } } else { // Not found, put this in the mOrphans list // Note that the mOrphans dict might contain multiple entries with the // same key! which are multiple children that wait for the parent // incidence to be inserted. d->mOrphans.insert(forincidence->relatedTo(), forincidence); d->mOrphanUids.insert(forincidence->uid(), forincidence); } } } // If a to-do with sub-to-dos is deleted, move it's sub-to-dos to the orphan list void Calendar::removeRelations(const Incidence::Ptr &incidence) { if (!incidence) { qCDebug(KCALCORE_LOG) << "Warning: incidence is 0"; return; } const QString uid = incidence->uid(); for (const Incidence::Ptr &i : qAsConst(d->mIncidenceRelations[uid])) { if (!d->mOrphanUids.contains(i->uid())) { d->mOrphans.insert(uid, i); d->mOrphanUids.insert(i->uid(), i); i->setRelatedTo(uid); } } const QString parentUid = incidence->relatedTo(); // If this incidence is related to something else, tell that about it if (!parentUid.isEmpty()) { Incidence::List &relations = d->mIncidenceRelations[parentUid]; relations.erase( std::remove(relations.begin(), relations.end(), incidence), relations.end()); } // Remove this one from the orphans list if (d->mOrphanUids.remove(uid)) { // This incidence is located in the orphans list - it should be removed // Since the mOrphans dict might contain the same key (with different // child incidence pointers!) multiple times, take care that we remove // the correct one. So we need to remove all items with the given // parent UID, and readd those that are not for this item. Also, there // might be other entries with differnet UID that point to this // incidence (this might happen when the relatedTo of the item is // changed before its parent is inserted. This might happen with // groupware servers....). Remove them, too QStringList relatedToUids; // First, create a list of all keys in the mOrphans list which point // to the removed item relatedToUids << incidence->relatedTo(); for (QMultiHash::Iterator it = d->mOrphans.begin(); it != d->mOrphans.end(); ++it) { if (it.value()->uid() == uid) { relatedToUids << it.key(); } } // now go through all uids that have one entry that point to the incidence for (QStringList::const_iterator uidit = relatedToUids.constBegin(); uidit != relatedToUids.constEnd(); ++uidit) { Incidence::List tempList; // Remove all to get access to the remaining entries const Incidence::List l = values(d->mOrphans, *uidit); d->mOrphans.remove(*uidit); for (const Incidence::Ptr &i : l) { if (i != incidence) { tempList.append(i); } } // Readd those that point to a different orphan incidence for (Incidence::List::Iterator incit = tempList.begin(); incit != tempList.end(); ++incit) { d->mOrphans.insert(*uidit, *incit); } } } // Make sure the deleted incidence doesn't relate to a non-deleted incidence, // since that would cause trouble in MemoryCalendar::close(), as the deleted // incidences are destroyed after the non-deleted incidences. The destructor // of the deleted incidences would then try to access the already destroyed // non-deleted incidence, which would segfault. // // So in short: Make sure dead incidences don't point to alive incidences // via the relation. // // This crash is tested in MemoryCalendarTest::testRelationsCrash(). // incidence->setRelatedTo( Incidence::Ptr() ); } bool Calendar::isAncestorOf(const Incidence::Ptr &ancestor, const Incidence::Ptr &incidence) const { if (!incidence || incidence->relatedTo().isEmpty()) { return false; } else if (incidence->relatedTo() == ancestor->uid()) { return true; } else { return isAncestorOf(ancestor, this->incidence(incidence->relatedTo())); } } Incidence::List Calendar::relations(const QString &uid) const { return d->mIncidenceRelations[uid]; } Calendar::CalendarObserver::~CalendarObserver() { } void Calendar::CalendarObserver::calendarModified(bool modified, Calendar *calendar) { Q_UNUSED(modified); Q_UNUSED(calendar); } void Calendar::CalendarObserver::calendarIncidenceAdded(const Incidence::Ptr &incidence) { Q_UNUSED(incidence); } void Calendar::CalendarObserver::calendarIncidenceChanged(const Incidence::Ptr &incidence) { Q_UNUSED(incidence); } void Calendar::CalendarObserver::calendarIncidenceAboutToBeDeleted(const Incidence::Ptr &incidence) { Q_UNUSED(incidence); } void Calendar::CalendarObserver::calendarIncidenceDeleted(const Incidence::Ptr &incidence, const Calendar *calendar) { Q_UNUSED(incidence); Q_UNUSED(calendar); } void Calendar::CalendarObserver::calendarIncidenceAdditionCanceled(const Incidence::Ptr &incidence) { Q_UNUSED(incidence); } void Calendar::registerObserver(CalendarObserver *observer) { if (!observer) { return; } if (!d->mObservers.contains(observer)) { d->mObservers.append(observer); } else { d->mNewObserver = true; } } void Calendar::unregisterObserver(CalendarObserver *observer) { if (!observer) { return; } else { d->mObservers.removeAll(observer); } } bool Calendar::isSaving() const { return false; } void Calendar::setModified(bool modified) { if (modified != d->mModified || d->mNewObserver) { d->mNewObserver = false; for (CalendarObserver *observer : qAsConst(d->mObservers)) { observer->calendarModified(modified, this); } d->mModified = modified; } } bool Calendar::isModified() const { return d->mModified; } bool Calendar::save() { return true; } bool Calendar::reload() { return true; } void Calendar::incidenceUpdated(const QString &uid, const QDateTime &recurrenceId) { Incidence::Ptr inc = incidence(uid, recurrenceId); if (!inc) { return; } inc->setLastModified(QDateTime::currentDateTimeUtc()); // we should probably update the revision number here, // or internally in the Event itself when certain things change. // need to verify with ical documentation. notifyIncidenceChanged(inc); setModified(true); } void Calendar::doSetTimeZone(const QTimeZone &timeZone) { Q_UNUSED(timeZone); } void Calendar::notifyIncidenceAdded(const Incidence::Ptr &incidence) { if (!incidence) { return; } if (!d->mObserversEnabled) { return; } for (CalendarObserver *observer : qAsConst(d->mObservers)) { observer->calendarIncidenceAdded(incidence); } for (auto role : { IncidenceBase::RoleStartTimeZone, IncidenceBase::RoleEndTimeZone }) { const auto dt = incidence->dateTime(role); if (dt.isValid() && dt.timeZone() != QTimeZone::utc()) { if (!d->mTimeZones.contains(dt.timeZone())) { d->mTimeZones.push_back(dt.timeZone()); } } } } void Calendar::notifyIncidenceChanged(const Incidence::Ptr &incidence) { if (!incidence) { return; } if (!d->mObserversEnabled) { return; } for (CalendarObserver *observer : qAsConst(d->mObservers)) { observer->calendarIncidenceChanged(incidence); } } void Calendar::notifyIncidenceAboutToBeDeleted(const Incidence::Ptr &incidence) { if (!incidence) { return; } if (!d->mObserversEnabled) { return; } for (CalendarObserver *observer : qAsConst(d->mObservers)) { observer->calendarIncidenceAboutToBeDeleted(incidence); } } void Calendar::notifyIncidenceDeleted(const Incidence::Ptr &incidence) { if (!incidence) { return; } if (!d->mObserversEnabled) { return; } for (CalendarObserver *observer : qAsConst(d->mObservers)) { observer->calendarIncidenceDeleted(incidence, this); } } void Calendar::notifyIncidenceAdditionCanceled(const Incidence::Ptr &incidence) { if (!incidence) { return; } if (!d->mObserversEnabled) { return; } for (CalendarObserver *observer : qAsConst(d->mObservers)) { observer->calendarIncidenceAdditionCanceled(incidence); } } void Calendar::customPropertyUpdated() { setModified(true); } void Calendar::setProductId(const QString &id) { d->mProductId = id; } QString Calendar::productId() const { return d->mProductId; } /** static */ Incidence::List Calendar::mergeIncidenceList(const Event::List &events, const Todo::List &todos, const Journal::List &journals) { Incidence::List incidences; incidences.reserve(events.count() + todos.count() + journals.count()); int i, end; for (i = 0, end = events.count(); i < end; ++i) { incidences.append(events[i]); } for (i = 0, end = todos.count(); i < end; ++i) { incidences.append(todos[i]); } for (i = 0, end = journals.count(); i < end; ++i) { incidences.append(journals[i]); } return incidences; } bool Calendar::beginChange(const Incidence::Ptr &incidence) { Q_UNUSED(incidence); return true; } bool Calendar::endChange(const Incidence::Ptr &incidence) { Q_UNUSED(incidence); return true; } void Calendar::setObserversEnabled(bool enabled) { d->mObserversEnabled = enabled; } void Calendar::appendAlarms(Alarm::List &alarms, const Incidence::Ptr &incidence, const QDateTime &from, const QDateTime &to) const { QDateTime preTime = from.addSecs(-1); Alarm::List alarmlist = incidence->alarms(); for (int i = 0, iend = alarmlist.count(); i < iend; ++i) { if (alarmlist[i]->enabled()) { QDateTime dt = alarmlist[i]->nextRepetition(preTime); if (dt.isValid() && dt <= to) { qCDebug(KCALCORE_LOG) << incidence->summary() << "':" << dt.toString(); alarms.append(alarmlist[i]); } } } } void Calendar::appendRecurringAlarms(Alarm::List &alarms, const Incidence::Ptr &incidence, const QDateTime &from, const QDateTime &to) const { QDateTime dt; bool endOffsetValid = false; Duration endOffset(0); Duration period(from, to); Alarm::List alarmlist = incidence->alarms(); for (int i = 0, iend = alarmlist.count(); i < iend; ++i) { Alarm::Ptr a = alarmlist[i]; if (a->enabled()) { if (a->hasTime()) { // The alarm time is defined as an absolute date/time dt = a->nextRepetition(from.addSecs(-1)); if (!dt.isValid() || dt > to) { continue; } } else { // Alarm time is defined by an offset from the event start or end time. // Find the offset from the event start time, which is also used as the // offset from the recurrence time. Duration offset(0); if (a->hasStartOffset()) { offset = a->startOffset(); } else if (a->hasEndOffset()) { offset = a->endOffset(); if (!endOffsetValid) { endOffset = Duration(incidence->dtStart(), incidence->dateTime(Incidence::RoleAlarmEndOffset)); endOffsetValid = true; } } // Find the incidence's earliest alarm QDateTime alarmStart = offset.end(a->hasEndOffset() ? incidence->dateTime(Incidence::RoleAlarmEndOffset) : incidence->dtStart()); if (alarmStart > to) { continue; } QDateTime baseStart = incidence->dtStart(); if (from > alarmStart) { alarmStart = from; // don't look earlier than the earliest alarm baseStart = (-offset).end((-endOffset).end(alarmStart)); } // Adjust the 'alarmStart' date/time and find the next recurrence at or after it. // Treate the two offsets separately in case one is daily and the other not. dt = incidence->recurrence()->getNextDateTime(baseStart.addSecs(-1)); if (!dt.isValid() || (dt = endOffset.end(offset.end(dt))) > to) { // adjust 'dt' to get the alarm time // The next recurrence is too late. if (!a->repeatCount()) { continue; } // The alarm has repetitions, so check whether repetitions of previous // recurrences fall within the time period. bool found = false; Duration alarmDuration = a->duration(); for (QDateTime base = baseStart; (dt = incidence->recurrence()->getPreviousDateTime(base)).isValid(); base = dt) { if (a->duration().end(dt) < base) { break; // this recurrence's last repetition is too early, so give up } // The last repetition of this recurrence is at or after 'alarmStart' time. // Check if a repetition occurs between 'alarmStart' and 'to'. int snooze = a->snoozeTime().value(); // in seconds or days if (a->snoozeTime().isDaily()) { Duration toFromDuration(dt, base); int toFrom = toFromDuration.asDays(); if (a->snoozeTime().end(from) <= to || (toFromDuration.isDaily() && toFrom % snooze == 0) || (toFrom / snooze + 1) * snooze <= toFrom + period.asDays()) { found = true; #ifndef NDEBUG // for debug output dt = offset.end(dt).addDays(((toFrom - 1) / snooze + 1) * snooze); #endif break; } } else { int toFrom = dt.secsTo(base); if (period.asSeconds() >= snooze || toFrom % snooze == 0 || (toFrom / snooze + 1) * snooze <= toFrom + period.asSeconds()) { found = true; #ifndef NDEBUG // for debug output dt = offset.end(dt).addSecs(((toFrom - 1) / snooze + 1) * snooze); #endif break; } } } if (!found) { continue; } } } qCDebug(KCALCORE_LOG) << incidence->summary() << "':" << dt.toString(); alarms.append(a); } } } void Calendar::startBatchAdding() { d->batchAddingInProgress = true; } void Calendar::endBatchAdding() { d->batchAddingInProgress = false; } bool Calendar::batchAdding() const { return d->batchAddingInProgress; } void Calendar::setDeletionTracking(bool enable) { d->mDeletionTracking = enable; } bool Calendar::deletionTracking() const { return d->mDeletionTracking; } void Calendar::virtual_hook(int id, void *data) { Q_UNUSED(id); Q_UNUSED(data); Q_ASSERT(false); } diff --git a/src/calendar.h b/src/calendar.h index d34cbd724..ae537fc02 100644 --- a/src/calendar.h +++ b/src/calendar.h @@ -1,1412 +1,1412 @@ /* This file is part of the kcalcore library. Copyright (c) 1998 Preston Brown Copyright (c) 2001,2003,2004 Cornelius Schumacher Copyright (C) 2003-2004 Reinhold Kainhofer Copyright (c) 2006 David Jarvie This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the Calendar class. @author Preston Brown \ @author Cornelius Schumacher \ @author Reinhold Kainhofer \ @author David Jarvie \ */ /* TODO: KDE5: This API needs serious cleaning up: - Most (all) methods aren't async ( deleteIncidence(), addIncidence(), load(), save(), ... ) so it's not very easy to make a derived class that loads from akonadi. - It has too many methods. Why do we need fooEvent()/fooJournal()/fooTodo() when fooIncidence() should be enough. */ #ifndef KCALCORE_CALENDAR_H #define KCALCORE_CALENDAR_H #include "kcalcore_export.h" #include "event.h" #include "customproperties.h" #include "incidence.h" #include "journal.h" #include "todo.h" #include #include #include -/** Namespace for all KCalCore types. */ -namespace KCalCore +/** Namespace for all KCalendarCore types. */ +namespace KCalendarCore { class CalFilter; class Person; class ICalFormat; /** Calendar Incidence sort directions. */ enum SortDirection { SortDirectionAscending, /**< Sort in ascending order (first to last) */ SortDirectionDescending /**< Sort in descending order (last to first) */ }; /** Calendar Event sort keys. */ enum EventSortField { EventSortUnsorted, /**< Do not sort Events */ EventSortStartDate, /**< Sort Events chronologically, by start date */ EventSortEndDate, /**< Sort Events chronologically, by end date */ EventSortSummary /**< Sort Events alphabetically, by summary */ }; /** Calendar Todo sort keys. */ enum TodoSortField { TodoSortUnsorted, /**< Do not sort Todos */ TodoSortStartDate, /**< Sort Todos chronologically, by start date */ TodoSortDueDate, /**< Sort Todos chronologically, by due date */ TodoSortPriority, /**< Sort Todos by priority */ TodoSortPercentComplete, /**< Sort Todos by percentage completed */ TodoSortSummary, /**< Sort Todos alphabetically, by summary */ TodoSortCreated /**< Sort Todos chronologically, by creation date */ }; /** Calendar Journal sort keys. */ enum JournalSortField { JournalSortUnsorted, /**< Do not sort Journals */ JournalSortDate, /**< Sort Journals chronologically by date */ JournalSortSummary /**< Sort Journals alphabetically, by summary */ }; /** @brief Represents the main calendar class. A calendar contains information like incidences (events, to-dos, journals), alarms, time zones, and other useful information. This is an abstract base class defining the interface to a calendar. It is implemented by subclasses like MemoryCalendar, which use different methods to store and access the data. Ownership of Incidences: Incidence ownership is handled by the following policy: as soon as an incidence (or any other subclass of IncidenceBase) is added to the Calendar by an add...() method it is owned by the Calendar object. The Calendar takes care of deleting the incidence using the delete...() methods. All Incidences returned by the query functions are returned as pointers so that changes to the returned Incidences are immediately visible in the Calendar. Do Not attempt to 'delete' any Incidence object you get from Calendar -- use the delete...() methods. */ class KCALCORE_EXPORT Calendar : public QObject, public CustomProperties, public IncidenceBase::IncidenceObserver { Q_OBJECT Q_PROPERTY(QString productId READ productId WRITE setProductId) //clazy:exclude=qproperty-without-notify - Q_PROPERTY(KCalCore::Person owner READ owner WRITE setOwner) + Q_PROPERTY(KCalendarCore::Person owner READ owner WRITE setOwner) public: /** A shared pointer to a Calendar */ typedef QSharedPointer Ptr; /** Constructs a calendar with a specified time zone @p timeZone. The time zone is used as the default for creating or modifying incidences in the Calendar. The time zone does not alter existing incidences. @param timeZone time specification */ explicit Calendar(const QTimeZone &timeZone); /** Construct Calendar object using a time zone ID. The time zone ID is used as the default for creating or modifying incidences in the Calendar. The time zone does not alter existing incidences. @param timeZoneId is a string containing a time zone ID, which is assumed to be valid. If no time zone is found, the viewing time specification is set to local time zone. @e Example: "Europe/Berlin" */ explicit Calendar(const QByteArray &timeZoneId); /** Destroys the calendar. */ ~Calendar() override; /** Sets the calendar Product ID to @p id. @param id is a string containing the Product ID. @see productId() const */ void setProductId(const QString &id); /** Returns the calendar's Product ID. @see setProductId() */ Q_REQUIRED_RESULT QString productId() const; /** Sets the owner of the calendar to @p owner. @param owner is a Person object. Must be a non-null pointer. @see owner() */ void setOwner(const Person &owner); /** Returns the owner of the calendar. @return the owner Person object. @see setOwner() */ Q_REQUIRED_RESULT Person owner() const; /** Sets the default time specification zone used for creating or modifying incidences in the Calendar. @param timeZone The time zone */ void setTimeZone(const QTimeZone &timeZone); /** Get the time zone used for creating or modifying incidences in the Calendar. @return time specification */ Q_REQUIRED_RESULT QTimeZone timeZone() const; /** Sets the time zone ID used for creating or modifying incidences in the Calendar. This method has no effect on existing incidences. @param timeZoneId is a string containing a time zone ID, which is assumed to be valid. The time zone ID is used to set the time zone for viewing Incidence date/times. If no time zone is found, the viewing time specification is set to local time zone. @e Example: "Europe/Berlin" @see setTimeZone() */ void setTimeZoneId(const QByteArray &timeZoneId); /** Returns the time zone ID used for creating or modifying incidences in the calendar. @return the string containing the time zone ID, or empty string if the creation/modification time specification is not a time zone. */ Q_REQUIRED_RESULT QByteArray timeZoneId() const; /** Shifts the times of all incidences so that they appear at the same clock time as before but in a new time zone. The shift is done from a viewing time zone rather than from the actual incidence time zone. For example, shifting an incidence whose start time is 09:00 America/New York, using an old viewing time zone (@p oldSpec) of Europe/London, to a new time zone (@p newSpec) of Europe/Paris, will result in the time being shifted from 14:00 (which is the London time of the incidence start) to 14:00 Paris time. @param oldZone the time zone which provides the clock times @param newZone the new time zone */ void shiftTimes(const QTimeZone &oldZone, const QTimeZone &newZone); /** Sets if the calendar has been modified. @param modified is true if the calendar has been modified since open or last save. @see isModified() */ void setModified(bool modified); /** Determine the calendar's modification status. @return true if the calendar has been modified since open or last save. @see setModified() */ Q_REQUIRED_RESULT bool isModified() const; /** Clears out the current calendar, freeing all used memory etc. */ virtual void close() = 0; /** Syncs changes in memory to persistent storage. @return true if the save was successful; false otherwise. Base implementation returns true. */ virtual bool save(); /** Loads the calendar contents from storage. This requires that the calendar has been previously loaded (initialized). @return true if the reload was successful; otherwise false. Base implementation returns true. */ virtual bool reload(); /** Determine if the calendar is currently being saved. @return true if the calendar is currently being saved; false otherwise. */ virtual bool isSaving() const; /** Returns a list of all categories used by Incidences in this Calendar. @return a QStringList containing all the categories. */ Q_REQUIRED_RESULT QStringList categories() const; // Incidence Specific Methods // /** Call this to tell the calendar that you're adding a batch of incidences. So it doesn't, for example, ask the destination for each incidence. @see endBatchAdding() */ virtual void startBatchAdding(); /** Tells the Calendar that you stoped adding a batch of incidences. @see startBatchAdding() */ virtual void endBatchAdding(); /** @return true if batch adding is in progress */ Q_REQUIRED_RESULT bool batchAdding() const; /** Inserts an Incidence into the calendar. @param incidence is a pointer to the Incidence to insert. @return true if the Incidence was successfully inserted; false otherwise. @see deleteIncidence() */ virtual bool addIncidence(const Incidence::Ptr &incidence); /** Removes an Incidence from the calendar. @param incidence is a pointer to the Incidence to remove. @return true if the Incidence was successfully removed; false otherwise. @see addIncidence() */ virtual bool deleteIncidence(const Incidence::Ptr &incidence); /** Returns a filtered list of all Incidences for this Calendar. @return the list of all filtered Incidences. */ virtual Incidence::List incidences() const; /** Returns a filtered list of all Incidences which occur on the given date. @param date request filtered Incidence list for this QDate only. @return the list of filtered Incidences occurring on the specified date. */ virtual Incidence::List incidences(const QDate &date) const; /** Returns an unfiltered list of all Incidences for this Calendar. @return the list of all unfiltered Incidences. */ virtual Incidence::List rawIncidences() const; /** Returns an unfiltered list of all exceptions of this recurring incidence. @param incidence incidence to check @return the list of all unfiltered exceptions. */ virtual Incidence::List instances(const Incidence::Ptr &incidence) const; // Notebook Specific Methods // /** Clears notebook associations from hash-tables for incidences. Called when in-memory content of the calendar is cleared. */ virtual void clearNotebookAssociations(); /** Associate notebook for an incidence. @param incidence incidence @param notebook notebook uid @return true if the operation was successful; false otherwise. */ virtual bool setNotebook(const Incidence::Ptr &incidence, const QString ¬ebook); /** Get incidence's notebook. @param incidence incidence @return notebook uid */ virtual QString notebook(const Incidence::Ptr &incidence) const; /** Get incidence's notebook. @param uid is a unique identifier string @return notebook uid */ virtual QString notebook(const QString &uid) const; /** List all uids of notebooks currently in the memory. @return list of uids of notebooks */ virtual QStringList notebooks() const; /** Check if calendar knows about the given notebook. This means that it will be saved by one of the attached storages. @param notebook notebook uid @return true if calendar has valid notebook */ Q_REQUIRED_RESULT bool hasValidNotebook(const QString ¬ebook) const; /** Add notebook information into calendar. Is usually called by storages only. @param notebook notebook uid @param isVisible notebook visibility @return true if operation succeeded @see isVisible() */ Q_REQUIRED_RESULT bool addNotebook(const QString ¬ebook, bool isVisible); /** Update notebook information in calendar. Is usually called by storages only. @param notebook notebook uid @param isVisible notebook visibility @return true if operation succeeded @see isVisible() */ Q_REQUIRED_RESULT bool updateNotebook(const QString ¬ebook, bool isVisible); /** Delete notebook information from calendar. Is usually called by storages only. @param notebook notebook uid @return true if operation succeeded @see isVisible() */ Q_REQUIRED_RESULT bool deleteNotebook(const QString ¬ebook); /** set DefaultNotebook information to calendar. @param notebook notebook uid @return true if operation was successful; false otherwise. */ Q_REQUIRED_RESULT bool setDefaultNotebook(const QString ¬ebook); /** Get uid of default notebook. @return notebook uid */ Q_REQUIRED_RESULT QString defaultNotebook() const; /** Check if incidence is visible. @param incidence is a pointer to the Incidence to check for visibility. @return true if incidence is visible, false otherwise */ Q_REQUIRED_RESULT bool isVisible(const Incidence::Ptr &incidence) const; /** List all notebook incidences in the memory. @param notebook is the notebook uid. @return a list of incidences for the notebook. */ virtual Incidence::List incidences(const QString ¬ebook) const; /** List all possible duplicate incidences. @param incidence is the incidence to check. @return a list of duplicate incidences. */ virtual Incidence::List duplicates(const Incidence::Ptr &incidence); /** Returns the Incidence associated with the given unique identifier. @param uid is a unique identifier string. @param recurrenceId is possible recurrenceid of incidence, default is null @return a pointer to the Incidence. A null pointer is returned if no such Incidence exists. */ Incidence::Ptr incidence(const QString &uid, const QDateTime &recurrenceId = {}) const; /** Returns the deleted Incidence associated with the given unique identifier. @param uid is a unique identifier string. @param recurrenceId is possible recurrenceid of incidence, default is null @return a pointer to the Incidence. A null pointer is returned if no such Incidence exists. */ Incidence::Ptr deleted(const QString &uid, const QDateTime &recurrenceId = {}) const; /** Delete all incidences that are instances of recurring incidence @p incidence. @param incidence is a pointer to a deleted Incidence @return true if delete was successful; false otherwise */ virtual bool deleteIncidenceInstances(const Incidence::Ptr &incidence) = 0; /** Returns the Incidence associated with the given scheduling identifier. @param sid is a unique scheduling identifier string. @return a pointer to the Incidence. A null pointer is returned if no such Incidence exists. */ virtual Incidence::Ptr incidenceFromSchedulingID(const QString &sid) const; /** Searches all events and todos for an incidence with this scheduling identifier. Returns a list of matching results. @param sid is a unique scheduling identifier string. */ virtual Incidence::List incidencesFromSchedulingID(const QString &sid) const; /** Create a merged list of Events, Todos, and Journals. @param events is an Event list to merge. @param todos is a Todo list to merge. @param journals is a Journal list to merge. @return a list of merged Incidences. */ static Incidence::List mergeIncidenceList(const Event::List &events, const Todo::List &todos, const Journal::List &journals); /** Flag that a change to a Calendar Incidence is starting. @param incidence is a pointer to the Incidence that will be changing. */ virtual bool beginChange(const Incidence::Ptr &incidence); /** Flag that a change to a Calendar Incidence has completed. @param incidence is a pointer to the Incidence that was changed. */ virtual bool endChange(const Incidence::Ptr &incidence); /** Creates an exception for an occurrence from a recurring Incidence. The returned exception is not automatically inserted into the calendar. @param incidence is a pointer to a recurring Incidence. @param recurrenceId specifies the specific occurrence for which the exception applies. @param thisAndFuture specifies if the exception applies only this specific occcurrence or also to all future occurrences. @return a pointer to a new exception incidence with @param recurrenceId set. @since 4.11 */ static Incidence::Ptr createException(const Incidence::Ptr &incidence, const QDateTime &recurrenceId, bool thisAndFuture = false); // Event Specific Methods // /** Inserts an Event into the calendar. @param event is a pointer to the Event to insert. @return true if the Event was successfully inserted; false otherwise. @see deleteEvent() */ virtual bool addEvent(const Event::Ptr &event) = 0; /** Removes an Event from the calendar. @param event is a pointer to the Event to remove. @return true if the Event was successfully remove; false otherwise. @see addEvent() */ virtual bool deleteEvent(const Event::Ptr &event) = 0; /** Delete all events that are instances of recurring event @p event. @param event is a pointer to a deleted Event @return true if delete was successful; false otherwise */ virtual bool deleteEventInstances(const Event::Ptr &event) = 0; /** Sort a list of Events. @param eventList is a pointer to a list of Events. @param sortField specifies the EventSortField. @param sortDirection specifies the SortDirection. @return a list of Events sorted as specified. */ static Event::List sortEvents(const Event::List &eventList, EventSortField sortField, SortDirection sortDirection); /** Returns a sorted, filtered list of all Events for this Calendar. @param sortField specifies the EventSortField. @param sortDirection specifies the SortDirection. @return the list of all filtered Events sorted as specified. */ virtual Event::List events(EventSortField sortField = EventSortUnsorted, SortDirection sortDirection = SortDirectionAscending) const; /** Returns a filtered list of all Events which occur on the given timestamp. @param dt request filtered Event list for this QDateTime only. @return the list of filtered Events occurring on the specified timestamp. */ Q_REQUIRED_RESULT Event::List events(const QDateTime &dt) const; /** Returns a filtered list of all Events occurring within a date range. @param start is the starting date. @param end is the ending date. @param timeZone time zone to interpret @p start and @p end, or the calendar's default time zone if none is specified @param inclusive if true only Events which are completely included within the date range are returned. @return the list of filtered Events occurring within the specified date range. */ Q_REQUIRED_RESULT Event::List events(const QDate &start, const QDate &end, const QTimeZone &timeZone = {}, bool inclusive = false) const; /** Returns a sorted, filtered list of all Events which occur on the given date. The Events are sorted according to @a sortField and @a sortDirection. @param date request filtered Event list for this QDate only. @param timeZone time zone to interpret @p start and @p end, or the calendar's default time zone if none is specified @param sortField specifies the EventSortField. @param sortDirection specifies the SortDirection. @return the list of sorted, filtered Events occurring on @a date. */ Q_REQUIRED_RESULT Event::List events(const QDate &date, const QTimeZone &timeZone = {}, EventSortField sortField = EventSortUnsorted, SortDirection sortDirection = SortDirectionAscending) const; /** Returns a sorted, unfiltered list of all Events for this Calendar. @param sortField specifies the EventSortField. @param sortDirection specifies the SortDirection. @return the list of all unfiltered Events sorted as specified. */ virtual Event::List rawEvents( EventSortField sortField = EventSortUnsorted, SortDirection sortDirection = SortDirectionAscending) const = 0; /** Returns an unfiltered list of all Events which occur on the given timestamp. @param dt request unfiltered Event list for this QDateTime only. @return the list of unfiltered Events occurring on the specified timestamp. */ virtual Event::List rawEventsForDate(const QDateTime &dt) const = 0; /** Returns an unfiltered list of all Events occurring within a date range. @param start is the starting date @param end is the ending date @param timeZone time zone to interpret @p start and @p end, or the calendar's default time zone if none is specified @param inclusive if true only Events which are completely included within the date range are returned. @return the list of unfiltered Events occurring within the specified date range. */ virtual Event::List rawEvents(const QDate &start, const QDate &end, const QTimeZone &timeZone = {}, bool inclusive = false) const = 0; /** Returns a sorted, unfiltered list of all Events which occur on the given date. The Events are sorted according to @a sortField and @a sortDirection. @param date request unfiltered Event list for this QDate only @param timeZone time zone to interpret @p date, or the calendar's default time zone if none is specified @param sortField specifies the EventSortField @param sortDirection specifies the SortDirection @return the list of sorted, unfiltered Events occurring on @p date */ virtual Event::List rawEventsForDate( const QDate &date, const QTimeZone &timeZone = {}, EventSortField sortField = EventSortUnsorted, SortDirection sortDirection = SortDirectionAscending) const = 0; /** Returns the Event associated with the given unique identifier. @param uid is a unique identifier string. @param recurrenceId is possible recurrenceId of event, default is null @return a pointer to the Event. A null pointer is returned if no such Event exists. */ virtual Event::Ptr event(const QString &uid, const QDateTime &recurrenceId = {}) const = 0; /** Returns the deleted Event associated with the given unique identifier. @param uid is a unique identifier string. @param recurrenceId is possible recurrenceId of event, default is null @return a pointer to the deleted Event. A null pointer is returned if no such deleted Event exists, or if deletion tracking is disabled. @see deletionTracking() */ virtual Event::Ptr deletedEvent(const QString &uid, const QDateTime &recurrenceId = {}) const = 0; /** Returns a sorted, unfiltered list of all deleted Events for this Calendar. @param sortField specifies the EventSortField. @param sortDirection specifies the SortDirection. @return the list of all unfiltered deleted Events sorted as specified. An empty list is returned if deletion tracking is disabled. @see deletionTracking() */ virtual Event::List deletedEvents( EventSortField sortField = EventSortUnsorted, SortDirection sortDirection = SortDirectionAscending) const = 0; /** Returns a sorted, unfiltered list of all possible instances for this recurring Event. @param event event to check for. Caller guarantees it's of type Event. @param sortField specifies the EventSortField. @param sortDirection specifies the SortDirection. @return the list of all unfiltered event instances sorted as specified. */ virtual Event::List eventInstances( const Incidence::Ptr &event, EventSortField sortField = EventSortUnsorted, SortDirection sortDirection = SortDirectionAscending) const = 0; // Todo Specific Methods // /** Inserts a Todo into the calendar. @param todo is a pointer to the Todo to insert. @return true if the Todo was successfully inserted; false otherwise. @see deleteTodo() */ virtual bool addTodo(const Todo::Ptr &todo) = 0; /** Removes a Todo from the calendar. @param todo is a pointer to the Todo to remove. @return true if the Todo was successfully removed; false otherwise. @see addTodo() */ virtual bool deleteTodo(const Todo::Ptr &todo) = 0; /** Delete all to-dos that are instances of recurring to-do @p todo. @param todo is a pointer to a deleted Todo @return true if delete was successful; false otherwise */ virtual bool deleteTodoInstances(const Todo::Ptr &todo) = 0; /** Sort a list of Todos. @param todoList is a pointer to a list of Todos. @param sortField specifies the TodoSortField. @param sortDirection specifies the SortDirection. @return a list of Todos sorted as specified. */ static Todo::List sortTodos(const Todo::List &todoList, TodoSortField sortField, SortDirection sortDirection); /** Returns a sorted, filtered list of all Todos for this Calendar. @param sortField specifies the TodoSortField. @param sortDirection specifies the SortDirection. @return the list of all filtered Todos sorted as specified. */ virtual Todo::List todos(TodoSortField sortField = TodoSortUnsorted, SortDirection sortDirection = SortDirectionAscending) const; /** Returns a filtered list of all Todos which are due on the specified date. @param date request filtered Todos due on this QDate. @return the list of filtered Todos due on the specified date. */ virtual Todo::List todos(const QDate &date) const; /** Returns a filtered list of all Todos occurring within a date range. @param start is the starting date @param end is the ending date @param timeZone time zone to interpret @p start and @p end, or the calendar's default time zone if none is specified @param inclusive if true only Todos which are completely included within the date range are returned. @return the list of filtered Todos occurring within the specified date range. */ virtual Todo::List todos(const QDate &start, const QDate &end, const QTimeZone &timeZone = {}, bool inclusive = false) const; /** Returns a sorted, unfiltered list of all Todos for this Calendar. @param sortField specifies the TodoSortField. @param sortDirection specifies the SortDirection. @return the list of all unfiltered Todos sorted as specified. */ virtual Todo::List rawTodos( TodoSortField sortField = TodoSortUnsorted, SortDirection sortDirection = SortDirectionAscending) const = 0; /** Returns an unfiltered list of all Todos which due on the specified date. @param date request unfiltered Todos due on this QDate. @return the list of unfiltered Todos due on the specified date. */ virtual Todo::List rawTodosForDate(const QDate &date) const = 0; /** Returns an unfiltered list of all Todos occurring within a date range. @param start is the starting date @param end is the ending date @param timeZone time zone to interpret @p start and @p end, or the calendar's default time zone if none is specified @param inclusive if true only Todos which are completely included within the date range are returned. @return the list of unfiltered Todos occurring within the specified date range. */ virtual Todo::List rawTodos(const QDate &start, const QDate &end, const QTimeZone &timeZone = {}, bool inclusive = false) const = 0; /** Returns the Todo associated with the given unique identifier. @param uid is a unique identifier string. @param recurrenceId is possible recurrenceId of todo, default is null @return a pointer to the Todo. A null pointer is returned if no such Todo exists. */ virtual Todo::Ptr todo(const QString &uid, const QDateTime &recurrenceId = {}) const = 0; /** Returns the deleted Todo associated with the given unique identifier. @param uid is a unique identifier string. @param recurrenceId is possible recurrenceId of todo, default is null @return a pointer to the deleted Todo. A null pointer is returned if no such deleted Todo exists or if deletion tracking is disabled. @see deletionTracking() */ virtual Todo::Ptr deletedTodo(const QString &uid, const QDateTime &recurrenceId = {}) const = 0; /** Returns a sorted, unfiltered list of all deleted Todos for this Calendar. @param sortField specifies the TodoSortField. @param sortDirection specifies the SortDirection. @return the list of all unfiltered deleted Todos sorted as specified. An empty list is returned if deletion tracking is disabled. @see deletionTracking() */ virtual Todo::List deletedTodos( TodoSortField sortField = TodoSortUnsorted, SortDirection sortDirection = SortDirectionAscending) const = 0; /** Returns a sorted, unfiltered list of all possible instances for this recurring Todo. @param todo todo to check for. Caller guarantees it's of type Todo. @param sortField specifies the TodoSortField. @param sortDirection specifies the SortDirection. @return the list of all unfiltered todo instances sorted as specified. */ virtual Todo::List todoInstances( const Incidence::Ptr &todo, TodoSortField sortField = TodoSortUnsorted, SortDirection sortDirection = SortDirectionAscending) const = 0; // Journal Specific Methods // /** Inserts a Journal into the calendar. @param journal is a pointer to the Journal to insert. @return true if the Journal was successfully inserted; false otherwise. @see deleteJournal() */ virtual bool addJournal(const Journal::Ptr &journal) = 0; /** Removes a Journal from the calendar. @param journal is a pointer to the Journal to remove. @return true if the Journal was successfully removed; false otherwise. @see addJournal() */ virtual bool deleteJournal(const Journal::Ptr &journal) = 0; /** Delete all journals that are instances of recurring journal @p journal. @param journal is a pointer to a deleted Journal @return true if delete was successful; false otherwise */ virtual bool deleteJournalInstances(const Journal::Ptr &journal) = 0; /** Sort a list of Journals. @param journalList is a pointer to a list of Journals. @param sortField specifies the JournalSortField. @param sortDirection specifies the SortDirection. @return a list of Journals sorted as specified. */ static Journal::List sortJournals(const Journal::List &journalList, JournalSortField sortField, SortDirection sortDirection); /** Returns a sorted, filtered list of all Journals for this Calendar. @param sortField specifies the JournalSortField. @param sortDirection specifies the SortDirection. @return the list of all filtered Journals sorted as specified. */ virtual Journal::List journals( JournalSortField sortField = JournalSortUnsorted, SortDirection sortDirection = SortDirectionAscending) const; /** Returns a filtered list of all Journals for on the specified date. @param date request filtered Journals for this QDate only. @return the list of filtered Journals for the specified date. */ virtual Journal::List journals(const QDate &date) const; /** Returns a sorted, unfiltered list of all Journals for this Calendar. @param sortField specifies the JournalSortField. @param sortDirection specifies the SortDirection. @return the list of all unfiltered Journals sorted as specified. */ virtual Journal::List rawJournals( JournalSortField sortField = JournalSortUnsorted, SortDirection sortDirection = SortDirectionAscending) const = 0; /** Returns an unfiltered list of all Journals for on the specified date. @param date request unfiltered Journals for this QDate only. @return the list of unfiltered Journals for the specified date. */ virtual Journal::List rawJournalsForDate(const QDate &date) const = 0; /** Returns the Journal associated with the given unique identifier. @param uid is a unique identifier string. @param recurrenceId is possible recurrenceId of journal, default is null @return a pointer to the Journal. A null pointer is returned if no such Journal exists. */ virtual Journal::Ptr journal(const QString &uid, const QDateTime &recurrenceId = {}) const = 0; /** Returns the deleted Journal associated with the given unique identifier. @param uid is a unique identifier string. @param recurrenceId is possible recurrenceId of journal, default is null @return a pointer to the deleted Journal. A null pointer is returned if no such deleted Journal exists or if deletion tracking is disabled. @see deletionTracking() */ virtual Journal::Ptr deletedJournal(const QString &uid, const QDateTime &recurrenceId = {}) const = 0; /** Returns a sorted, unfiltered list of all deleted Journals for this Calendar. @param sortField specifies the JournalSortField. @param sortDirection specifies the SortDirection. @return the list of all unfiltered deleted Journals sorted as specified. An empty list is returned if deletion tracking is disabled. @see deletionTracking() */ virtual Journal::List deletedJournals( JournalSortField sortField = JournalSortUnsorted, SortDirection sortDirection = SortDirectionAscending) const = 0; /** Returns a sorted, unfiltered list of all instances for this recurring Journal. @param journal journal to check for. Caller guarantees it's of type Journal. @param sortField specifies the JournalSortField. @param sortDirection specifies the SortDirection. @return the list of all unfiltered journal instances sorted as specified. */ virtual Journal::List journalInstances( const Incidence::Ptr &journal, JournalSortField sortField = JournalSortUnsorted, SortDirection sortDirection = SortDirectionAscending) const = 0; // Relations Specific Methods // /** Setup Relations for an Incidence. @param incidence is a pointer to the Incidence to have a Relation setup. */ virtual void setupRelations(const Incidence::Ptr &incidence); /** Removes all Relations from an Incidence. @param incidence is a pointer to the Incidence to have a Relation removed. */ virtual void removeRelations(const Incidence::Ptr &incidence); /** Checks if @p ancestor is an ancestor of @p incidence @param ancestor is the incidence we are testing to be an ancestor. @param incidence is the incidence we are testing to be descended from @p ancestor. */ bool isAncestorOf(const Incidence::Ptr &ancestor, const Incidence::Ptr &incidence) const; /** Returns a list of incidences that have a relation of RELTYPE parent to incidence @p uid. @param uid The parent identifier whos children we want to obtain. */ Incidence::List relations(const QString &uid) const; // Filter Specific Methods // /** Sets the calendar filter. @param filter a pointer to a CalFilter object which will be used to filter Calendar Incidences. The Calendar takes ownership of @p filter. @see filter() */ void setFilter(CalFilter *filter); /** Returns the calendar filter. @return a pointer to the calendar CalFilter. A null pointer is returned if no such CalFilter exists. @see setFilter() */ CalFilter *filter() const; // Alarm Specific Methods // /** Returns a list of Alarms within a time range for this Calendar. @param from is the starting timestamp. @param to is the ending timestamp. @param excludeBlockedAlarms if true, alarms belonging to blocked collections aren't returned. @return the list of Alarms for the for the specified time range. */ virtual Alarm::List alarms(const QDateTime &from, const QDateTime &to, bool excludeBlockedAlarms = false) const = 0; // Observer Specific Methods // /** @class CalendarObserver The CalendarObserver class. */ class KCALCORE_EXPORT CalendarObserver //krazy:exclude=dpointer { public: /** Destructor. */ virtual ~CalendarObserver(); /** Notify the Observer that a Calendar has been modified. @param modified set if the calendar has been modified. @param calendar is a pointer to the Calendar object that is being observed. */ virtual void calendarModified(bool modified, Calendar *calendar); /** Notify the Observer that an Incidence has been inserted. @param incidence is a pointer to the Incidence that was inserted. */ virtual void calendarIncidenceAdded(const Incidence::Ptr &incidence); /** Notify the Observer that an Incidence has been modified. @param incidence is a pointer to the Incidence that was modified. */ virtual void calendarIncidenceChanged(const Incidence::Ptr &incidence); /** Notify the Observer that an Incidence will be removed. @param incidence is a pointer to the Incidence that will be removed. */ virtual void calendarIncidenceAboutToBeDeleted(const Incidence::Ptr &incidence); /** Notify the Observer that an Incidence has been removed. @param incidence is a pointer to the Incidence that was removed. @param calendar is a pointer to the calendar where the incidence was part of, because the incidence was deleted, there is now way to determine the calendar @since 4.83.0 */ virtual void calendarIncidenceDeleted(const Incidence::Ptr &incidence, const Calendar *calendar); /** Notify the Observer that an addition of Incidence has been canceled. @param incidence is a pointer to the Incidence that was removed. */ virtual void calendarIncidenceAdditionCanceled(const Incidence::Ptr &incidence); }; /** Registers an Observer for this Calendar. @param observer is a pointer to an Observer object that will be watching this Calendar. @see unregisterObserver() */ void registerObserver(CalendarObserver *observer); /** Unregisters an Observer for this Calendar. @param observer is a pointer to an Observer object that has been watching this Calendar. @see registerObserver() */ void unregisterObserver(CalendarObserver *observer); using QObject::event; // prevent warning about hidden virtual method protected: /** The Observer interface. So far not implemented. @param uid is the UID for the Incidence that has been updated. @param recurrenceId is possible recurrenceid of incidence. */ void incidenceUpdated(const QString &uid, const QDateTime &recurrenceId) override; /** Let Calendar subclasses set the time specification. @param timeZone is the time specification (time zone, etc.) for viewing Incidence dates.\n */ virtual void doSetTimeZone(const QTimeZone &timeZone); /** Let Calendar subclasses notify that they inserted an Incidence. @param incidence is a pointer to the Incidence object that was inserted. */ void notifyIncidenceAdded(const Incidence::Ptr &incidence); /** Let Calendar subclasses notify that they modified an Incidence. @param incidence is a pointer to the Incidence object that was modified. */ void notifyIncidenceChanged(const Incidence::Ptr &incidence); /** Let Calendar subclasses notify that they will remove an Incidence. @param incidence is a pointer to the Incidence object that will be removed. */ void notifyIncidenceAboutToBeDeleted(const Incidence::Ptr &incidence); /** Let Calendar subclasses notify that they removed an Incidence. @param incidence is a pointer to the Incidence object that has been removed. */ void notifyIncidenceDeleted(const Incidence::Ptr &incidence); /** Let Calendar subclasses notify that they canceled addition of an Incidence. @param incidence is a pointer to the Incidence object that addition as canceled. */ void notifyIncidenceAdditionCanceled(const Incidence::Ptr &incidence); /** @copydoc CustomProperties::customPropertyUpdated() */ void customPropertyUpdated() override; /** Let Calendar subclasses notify that they enabled an Observer. @param enabled if true tells the calendar that a subclass has enabled an Observer. */ void setObserversEnabled(bool enabled); /** Appends alarms of incidence in interval to list of alarms. @param alarms is a List of Alarms to be appended onto. @param incidence is a pointer to an Incidence containing the Alarm to be appended. @param from is the lower range of the next Alarm repitition. @param to is the upper range of the next Alarm repitition. */ void appendAlarms(Alarm::List &alarms, const Incidence::Ptr &incidence, const QDateTime &from, const QDateTime &to) const; /** Appends alarms of recurring events in interval to list of alarms. @param alarms is a List of Alarms to be appended onto. @param incidence is a pointer to an Incidence containing the Alarm to be appended. @param from is the lower range of the next Alarm repitition. @param to is the upper range of the next Alarm repitition. */ void appendRecurringAlarms(Alarm::List &alarms, const Incidence::Ptr &incidence, const QDateTime &from, const QDateTime &to) const; /** Enables or disabled deletion tracking. Default is true. @see deletedEvent() @see deletedTodo() @see deletedJournal() @since 4.11 */ void setDeletionTracking(bool enable); /** Returns if deletion tracking is enabled. Default is true. @since 4.11 */ bool deletionTracking() const; /** @copydoc IncidenceBase::virtual_hook() */ virtual void virtual_hook(int id, void *data); Q_SIGNALS: /** Emitted when setFilter() is called. @since 4.11 */ void filterChanged(); private: friend class ICalFormat; //@cond PRIVATE class Private; Private *const d; //@endcond Q_DISABLE_COPY(Calendar) }; } #endif diff --git a/src/calendar_p.h b/src/calendar_p.h index d30b41ef9..f3cb3c32e 100644 --- a/src/calendar_p.h +++ b/src/calendar_p.h @@ -1,95 +1,95 @@ /* This file is part of the kcalcore library. Copyright (c) 1998 Preston Brown Copyright (c) 2000-2004 Cornelius Schumacher Copyright (C) 2003-2004 Reinhold Kainhofer Copyright (c) 2006 David Jarvie This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KCALCORE_CALENDAR_P_H #define KCALCORE_CALENDAR_P_H #include "calendar.h" #include "calfilter.h" -namespace KCalCore { +namespace KCalendarCore { /** Private class that helps to provide binary compatibility between releases. @internal */ //@cond PRIVATE class Q_DECL_HIDDEN Calendar::Private { public: Private() : mModified(false), mNewObserver(false), mObserversEnabled(true), mDefaultFilter(new CalFilter), batchAddingInProgress(false), mDeletionTracking(true) { // Setup default filter, which does nothing mFilter = mDefaultFilter; mFilter->setEnabled(false); mOwner.setName(QStringLiteral("Unknown Name")); mOwner.setEmail(QStringLiteral("unknown@nowhere")); } ~Private() { if (mFilter != mDefaultFilter) { delete mFilter; } delete mDefaultFilter; } QTimeZone timeZoneIdSpec(const QByteArray &timeZoneId); QString mProductId; Person mOwner; QTimeZone mTimeZone; QVector mTimeZones; bool mModified = false; bool mNewObserver = false; bool mObserversEnabled = false; QList mObservers; CalFilter *mDefaultFilter = nullptr; CalFilter *mFilter = nullptr; // These lists are used to put together related To-dos QMultiHash mOrphans; QMultiHash mOrphanUids; // Lists for associating incidences to notebooks QMultiHash mNotebookIncidences; QHash mUidToNotebook; QHash mNotebooks; // name to visibility QHash mIncidenceVisibility; // incidence -> visibility QString mDefaultNotebook; // uid of default notebook QMap mIncidenceRelations; bool batchAddingInProgress = false; bool mDeletionTracking = false; }; } #endif diff --git a/src/calfilter.cpp b/src/calfilter.cpp index 946201c0d..b6ef8386a 100644 --- a/src/calfilter.cpp +++ b/src/calfilter.cpp @@ -1,268 +1,268 @@ /* This file is part of the kcalcore library. Copyright (c) 2001 Cornelius Schumacher Copyright (C) 2003-2004 Reinhold Kainhofer Copyright (C) 2004 Bram Schoenmakers This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the CalFilter class. @brief Provides a filter for calendars. @author Cornelius Schumacher \ @author Reinhold Kainhofer \ @author Bram Schoenmakers \ */ #include "calfilter.h" -using namespace KCalCore; +using namespace KCalendarCore; /** Private class that helps to provide binary compatibility between releases. @internal */ //@cond PRIVATE -class Q_DECL_HIDDEN KCalCore::CalFilter::Private +class Q_DECL_HIDDEN KCalendarCore::CalFilter::Private { public: Private() {} QString mName; // filter name QStringList mCategoryList; QStringList mEmailList; int mCriteria = 0; int mCompletedTimeSpan = 0; bool mEnabled = true; }; //@endcond -CalFilter::CalFilter() : d(new KCalCore::CalFilter::Private) +CalFilter::CalFilter() : d(new KCalendarCore::CalFilter::Private) { } CalFilter::CalFilter(const QString &name) - : d(new KCalCore::CalFilter::Private) + : d(new KCalendarCore::CalFilter::Private) { d->mName = name; } CalFilter::~CalFilter() { delete d; } -bool KCalCore::CalFilter::operator==(const CalFilter &filter) const +bool KCalendarCore::CalFilter::operator==(const CalFilter &filter) const { return d->mName == filter.d->mName && d->mCriteria == filter.d->mCriteria && d->mCategoryList == filter.d->mCategoryList && d->mEmailList == filter.d->mEmailList && d->mCompletedTimeSpan == filter.d->mCompletedTimeSpan; } void CalFilter::apply(Event::List *eventList) const { if (!d->mEnabled) { return; } Event::List::Iterator it = eventList->begin(); while (it != eventList->end()) { if (!filterIncidence(*it)) { it = eventList->erase(it); } else { ++it; } } } // TODO: avoid duplicating apply() code void CalFilter::apply(Todo::List *todoList) const { if (!d->mEnabled) { return; } Todo::List::Iterator it = todoList->begin(); while (it != todoList->end()) { if (!filterIncidence(*it)) { it = todoList->erase(it); } else { ++it; } } } void CalFilter::apply(Journal::List *journalList) const { if (!d->mEnabled) { return; } Journal::List::Iterator it = journalList->begin(); while (it != journalList->end()) { if (!filterIncidence(*it)) { it = journalList->erase(it); } else { ++it; } } } bool CalFilter::filterIncidence(const Incidence::Ptr &incidence) const { if (!d->mEnabled) { return true; } Todo::Ptr todo = incidence.dynamicCast(); if (todo) { if ((d->mCriteria & HideCompletedTodos) && todo->isCompleted()) { // Check if completion date is suffently long ago: if (todo->completed().addDays(d->mCompletedTimeSpan) < QDateTime::currentDateTimeUtc()) { return false; } } if ((d->mCriteria & HideInactiveTodos) && ((todo->hasStartDate() && QDateTime::currentDateTimeUtc() < todo->dtStart()) || todo->isCompleted())) { return false; } if (d->mCriteria & HideNoMatchingAttendeeTodos) { bool iAmOneOfTheAttendees = false; const Attendee::List &attendees = todo->attendees(); if (!todo->attendees().isEmpty()) { Attendee::List::ConstIterator it; for (it = attendees.begin(); it != attendees.end(); ++it) { if (d->mEmailList.contains((*it).email())) { iAmOneOfTheAttendees = true; break; } } } else { // no attendees, must be me only iAmOneOfTheAttendees = true; } if (!iAmOneOfTheAttendees) { return false; } } } if (d->mCriteria & HideRecurring) { if (incidence->recurs() || incidence->hasRecurrenceId()) { return false; } } if (d->mCriteria & ShowCategories) { for (QStringList::ConstIterator it = d->mCategoryList.constBegin(); it != d->mCategoryList.constEnd(); ++it) { QStringList incidenceCategories = incidence->categories(); for (QStringList::ConstIterator it2 = incidenceCategories.constBegin(); it2 != incidenceCategories.constEnd(); ++it2) { if ((*it) == (*it2)) { return true; } } } return false; } else { for (QStringList::ConstIterator it = d->mCategoryList.constBegin(); it != d->mCategoryList.constEnd(); ++it) { QStringList incidenceCategories = incidence->categories(); for (QStringList::ConstIterator it2 = incidenceCategories.constBegin(); it2 != incidenceCategories.constEnd(); ++it2) { if ((*it) == (*it2)) { return false; } } } return true; } } void CalFilter::setName(const QString &name) { d->mName = name; } QString CalFilter::name() const { return d->mName; } void CalFilter::setEnabled(bool enabled) { d->mEnabled = enabled; } bool CalFilter::isEnabled() const { return d->mEnabled; } void CalFilter::setCriteria(int criteria) { d->mCriteria = criteria; } int CalFilter::criteria() const { return d->mCriteria; } void CalFilter::setCategoryList(const QStringList &categoryList) { d->mCategoryList = categoryList; } QStringList CalFilter::categoryList() const { return d->mCategoryList; } void CalFilter::setEmailList(const QStringList &emailList) { d->mEmailList = emailList; } QStringList CalFilter::emailList() const { return d->mEmailList; } void CalFilter::setCompletedTimeSpan(int timespan) { d->mCompletedTimeSpan = timespan; } int CalFilter::completedTimeSpan() const { return d->mCompletedTimeSpan; } diff --git a/src/calfilter.h b/src/calfilter.h index 939ea59d5..554e0e02c 100644 --- a/src/calfilter.h +++ b/src/calfilter.h @@ -1,227 +1,227 @@ /* This file is part of the kcalcore library. Copyright (c) 2001,2003,2004 Cornelius Schumacher Copyright (C) 2003-2004 Reinhold Kainhofer This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the CalFilter class. @author Cornelius Schumacher \ @author Reinhold Kainhofer \ */ #ifndef KCALCORE_CALFILTER_H #define KCALCORE_CALFILTER_H #include "kcalcore_export.h" #include "event.h" #include "journal.h" #include "todo.h" -namespace KCalCore +namespace KCalendarCore { /** @brief Provides a filter for calendars. This class provides a means for filtering calendar incidences by a list of email addresses, a list of categories, or other #Criteria. The following #Criteria are available: - remove recurring Incidences - keep Incidences with a matching category (see setCategoryList()) - remove completed To-dos (see setCompletedTimeSpan()) - remove inactive To-dos - remove To-dos without a matching attendee (see setEmailList()) */ class KCALCORE_EXPORT CalFilter { public: /** Filtering Criteria. */ enum Criteria { HideRecurring = 1, /**< Remove incidences that recur */ HideCompletedTodos = 2,/**< Remove completed to-dos */ ShowCategories = 4, /**< Show incidences with at least one matching category */ HideInactiveTodos = 8, /**< Remove to-dos that haven't started yet */ HideNoMatchingAttendeeTodos = 16 /**< Remove to-dos without a matching attendee */ }; /** Constructs an empty filter -- a filter without a name or criteria. */ CalFilter(); /** Constructs a filter with @p name. @param name is the name of this filter. */ explicit CalFilter(const QString &name); /** Destroys this filter. */ ~CalFilter(); /** Sets the filter name. @param name is the name of this filter. @see name(). */ void setName(const QString &name); /** Returns the filter name. @see setName(). */ Q_REQUIRED_RESULT QString name() const; /** Sets the criteria which must be fulfilled for an Incidence to pass the filter. @param criteria is a combination of #Criteria. @see criteria(). */ void setCriteria(int criteria); /** Returns the inclusive filter criteria. @see setCriteria(). */ Q_REQUIRED_RESULT int criteria() const; /** Applies the filter to a list of Events. All events not matching the filter criteria are removed from the list. @param eventList is a list of Events to filter. */ void apply(Event::List *eventList) const; /** Applies the filter to a list of To-dos. All to-dos not matching the filter criterias are removed from the list. @param todoList is a list of To-dos to filter. */ void apply(Todo::List *todoList) const; /** Applies the filter to a list of Journals. All journals not matching the filter criterias are removed from the list. @param journalList is a list of Journals to filter. */ void apply(Journal::List *journalList) const; /** Applies the filter criteria to the specified Incidence. @param incidence is the Incidence to filter. @return true if the Incidence passes the criteria; false otherwise. */ Q_REQUIRED_RESULT bool filterIncidence(const Incidence::Ptr &incidence) const; /** Enables or disables the filter. @param enabled is true if the filter is to be enabled; false otherwise. @see isEnabled(). */ void setEnabled(bool enabled); /** Returns whether the filter is enabled or not. @see setEnabled(). */ Q_REQUIRED_RESULT bool isEnabled() const; /** Sets the list of categories to be considered when filtering incidences according to the #ShowCategories criteria. @param categoryList is a QStringList of categories. @see categoryList(). */ void setCategoryList(const QStringList &categoryList); /** Returns the category list for this filter. @see setCategoryList(). */ Q_REQUIRED_RESULT QStringList categoryList() const; /** Sets the list of email addresses to be considered when filtering incidences according ot the #HideNoMatchingAttendeeTodos criteria. @param emailList is a QStringList of email addresses. @see emailList(). */ void setEmailList(const QStringList &emailList); /** Returns the email list for this filter. @see setEmailList(). */ Q_REQUIRED_RESULT QStringList emailList() const; /** Sets the number of days for the #HideCompletedTodos criteria. If a to-do has been completed within the recent @p timespan days, then that to-do will be removed during filtering. If a time span is not specified in the filter, then all completed to-dos will be removed if the #HideCompletedTodos criteria is set. @param timespan is an integer representing a time span in days. @see completedTimeSpan(). */ void setCompletedTimeSpan(int timespan); /** Returns the completed time span for this filter. @see setCompletedTimeSpan() */ Q_REQUIRED_RESULT int completedTimeSpan() const; /** Compares this with @p filter for equality. @param filter the CalFilter to compare. */ bool operator==(const CalFilter &filter) const; private: //@cond PRIVATE Q_DISABLE_COPY(CalFilter) class Private; Private *const d; //@endcond }; } #endif diff --git a/src/calformat.cpp b/src/calformat.cpp index 20a1c5871..1794c0534 100644 --- a/src/calformat.cpp +++ b/src/calformat.cpp @@ -1,128 +1,128 @@ /* This file is part of the kcalcore library. Copyright (c) 2001 Cornelius Schumacher This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the CalFormat base class. @brief Base class providing an interface to various calendar formats. @author Cornelius Schumacher \ */ #include "calformat.h" #include "exceptions.h" #include -using namespace KCalCore; +using namespace KCalendarCore; /** Private class that helps to provide binary compatibility between releases. @internal */ //@cond PRIVATE -class Q_DECL_HIDDEN KCalCore::CalFormat::Private +class Q_DECL_HIDDEN KCalendarCore::CalFormat::Private { public: Private() {} ~Private() { delete mException; } static QString mApplication; // Name of application, for creating unique ID strings static QString mProductId; // PRODID string to write to calendar files QString mLoadedProductId; // PRODID string loaded from calendar file Exception *mException = nullptr; }; QString CalFormat::Private::mApplication = QStringLiteral("libkcal"); QString CalFormat::Private::mProductId = QStringLiteral("-//K Desktop Environment//NONSGML libkcal 4.3//EN"); //@endcond CalFormat::CalFormat() - : d(new KCalCore::CalFormat::Private) + : d(new KCalendarCore::CalFormat::Private) { } CalFormat::~CalFormat() { clearException(); delete d; } void CalFormat::clearException() { delete d->mException; d->mException = nullptr; } void CalFormat::setException(Exception *exception) { delete d->mException; d->mException = exception; } Exception *CalFormat::exception() const { return d->mException; } void CalFormat::setApplication(const QString &application, const QString &productID) { Private::mApplication = application; Private::mProductId = productID; } const QString &CalFormat::application() { return Private::mApplication; } const QString &CalFormat::productId() { return Private::mProductId; } QString CalFormat::loadedProductId() { return d->mLoadedProductId; } void CalFormat::setLoadedProductId(const QString &id) { d->mLoadedProductId = id; } QString CalFormat::createUniqueId() { return QUuid::createUuid().toString().mid(1, 36); } void CalFormat::virtual_hook(int id, void *data) { Q_UNUSED(id); Q_UNUSED(data); Q_ASSERT(false); } diff --git a/src/calformat.h b/src/calformat.h index 3db502667..e8e11e276 100644 --- a/src/calformat.h +++ b/src/calformat.h @@ -1,200 +1,200 @@ /* This file is part of the kcalcore library. Copyright (c) 2001-2003 Cornelius Schumacher This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the CalFormat abstract base class. @author Cornelius Schumacher \ */ #ifndef KCALCORE_CALFORMAT_H #define KCALCORE_CALFORMAT_H #include "kcalcore_export.h" #include "calendar.h" #include -namespace KCalCore +namespace KCalendarCore { class Exception; /** @brief An abstract base class that provides an interface to various calendar formats. This is the base class for calendar formats. It provides an interface for the generation/interpretation of a textual representation of a calendar. */ class KCALCORE_EXPORT CalFormat { public: /** Constructs a new Calendar Format object. */ CalFormat(); /** Destructor. */ virtual ~CalFormat(); /** Loads a calendar on disk into the calendar associated with this format. @param calendar is the Calendar to be loaded. @param fileName is the name of the disk file containing the Calendar data. @return true if successful; false otherwise. */ virtual bool load(const Calendar::Ptr &calendar, const QString &fileName) = 0; /** Writes the calendar to disk. @param calendar is the Calendar containing the data to be saved. @param fileName is the name of the file to write the calendar data. @return true if successful; false otherwise. */ virtual bool save(const Calendar::Ptr &calendar, const QString &fileName) = 0; /** Loads a calendar from a string @param calendar is the Calendar to be loaded. @param string is the QString containing the Calendar data. @param deleted use deleted incidences @param notebook notebook uid @return true if successful; false otherwise. @see fromRawString(), toString(). */ virtual bool fromString(const Calendar::Ptr &calendar, const QString &string, bool deleted = false, const QString ¬ebook = QString()) = 0; /** Parses a utf8 encoded string, returning the first iCal component encountered in that string. This is an overload used for efficient reading to avoid utf8 conversions, which are expensive when reading from disk. @param calendar is the Calendar to be loaded. @param string is the QByteArray containing the Calendar data. @param deleted use deleted incidences @param notebook notebook uid @return true if successful; false otherwise. @see fromString(), toString(). */ virtual bool fromRawString(const Calendar::Ptr &calendar, const QByteArray &string, bool deleted = false, const QString ¬ebook = QString()) = 0; /** Returns the calendar as a string. @param calendar is the Calendar containing the data to be saved. @param notebook uid use only incidences with given notebook @param deleted use deleted incidences @return a QString containing the Calendar data if successful; an empty string otherwise. @see fromString(), fromRawString(). */ virtual QString toString(const Calendar::Ptr &calendar, const QString ¬ebook = QString(), bool deleted = false) = 0; /** Clears the exception status. */ void clearException(); /** Returns an exception, if there is any, containing information about the last error that occurred. */ Exception *exception() const; /** Sets the application name for use in unique IDs and error messages, and product ID for incidence PRODID property @param application is a string containing the application name. @param productID is a string containing the product identifier. */ static void setApplication(const QString &application, const QString &productID); /** Returns the application name used in unique IDs and error messages. */ static const QString &application(); //krazy:exclude=constref /** Returns the our library's PRODID string to write into calendar files. */ static const QString &productId(); //krazy:exclude=constref /** Returns the PRODID string loaded from calendar file. @see setLoadedProductId() */ QString loadedProductId(); /** Creates a unique id string. */ static QString createUniqueId(); /** Sets an exception that is to be used by the functions of this class to report errors. @param error is a pointer to an Exception which contains the exception. */ void setException(Exception *error); protected: /** Sets the PRODID string loaded from calendar file. @param id is a pruduct Id string to set for the calendar file. @see loadedProductId() */ void setLoadedProductId(const QString &id); /** @copydoc IncidenceBase::virtual_hook() */ virtual void virtual_hook(int id, void *data); private: //@cond PRIVATE Q_DISABLE_COPY(CalFormat) class Private; Private *const d; //@endcond }; } #endif diff --git a/src/calstorage.cpp b/src/calstorage.cpp index 414e535ac..2764ab577 100644 --- a/src/calstorage.cpp +++ b/src/calstorage.cpp @@ -1,64 +1,64 @@ /* This file is part of the kcalcore library. Copyright (c) 2002,2003 Cornelius Schumacher This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the CalStorage abstract base class. @brief An abstract base class that provides a calendar storage interface. @author Cornelius Schumacher \ */ #include "calstorage.h" -using namespace KCalCore; +using namespace KCalendarCore; /** Private class that helps to provide binary compatibility between releases. @internal */ //@cond PRIVATE -class Q_DECL_HIDDEN KCalCore::CalStorage::Private +class Q_DECL_HIDDEN KCalendarCore::CalStorage::Private { public: Private(const Calendar::Ptr &cal) : mCalendar(cal) {} Calendar::Ptr mCalendar; }; //@endcond CalStorage::CalStorage(const Calendar::Ptr &calendar) - : d(new KCalCore::CalStorage::Private(calendar)) + : d(new KCalendarCore::CalStorage::Private(calendar)) { } CalStorage::~CalStorage() { delete d; } Calendar::Ptr CalStorage::calendar() const { return d->mCalendar; } diff --git a/src/calstorage.h b/src/calstorage.h index cfd210bfb..e2d38b9b3 100644 --- a/src/calstorage.h +++ b/src/calstorage.h @@ -1,103 +1,103 @@ /* This file is part of the kcalcore library. Copyright (c) 2002,2003 Cornelius Schumacher This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the CalStorage abstract base class. @author Cornelius Schumacher \ */ #ifndef KCALCORE_CALSTORAGE_H #define KCALCORE_CALSTORAGE_H #include "kcalcore_export.h" #include "calendar.h" #include -namespace KCalCore +namespace KCalendarCore { /** @brief An abstract base class that provides a calendar storage interface. This is the base class for calendar storage. It provides an interface for the loading and saving of calendars. */ class KCALCORE_EXPORT CalStorage : public QObject { Q_OBJECT public: /** Constructs a new storage object for a calendar. @param calendar is a pointer to a valid Calendar object. */ explicit CalStorage(const Calendar::Ptr &calendar); /** Destuctor. */ ~CalStorage() override; /** Returns the calendar for this storage object. @return A pointer to the calendar whose storage is being managed. */ Calendar::Ptr calendar() const; /** Opens the calendar for storage. @return true if the open was successful; false otherwise. */ virtual bool open() = 0; /** Loads the calendar into memory. @return true if the load was successful; false otherwise. */ virtual bool load() = 0; /** Saves the calendar. @return true if the save was successful; false otherwise. */ virtual bool save() = 0; /** Closes the calendar storage. @return true if the close was successful; false otherwise. */ virtual bool close() = 0; private: //@cond PRIVATE Q_DISABLE_COPY(CalStorage) class Private; Private *const d; //@endcond }; } #endif diff --git a/src/compat.cpp b/src/compat.cpp index 56b28e436..bf6614b1b 100644 --- a/src/compat.cpp +++ b/src/compat.cpp @@ -1,412 +1,412 @@ /* This file is part of the kcalcore library. Copyright (c) 2002 Cornelius Schumacher Copyright (C) 2003-2004 Reinhold Kainhofer Copyright (C) 2012 Christian Mollekopf This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines classes for managing compatibility between different calendar formats. @brief Classes that provide compatibility to older or "broken" calendar formats. @author Cornelius Schumacher \ @author Reinhold Kainhofer \ */ #include "compat_p.h" #include "incidence.h" #include "utils.h" #include "kcalcore_debug.h" #include #include #include -using namespace KCalCore; +using namespace KCalendarCore; Compat *CompatFactory::createCompat(const QString &productId, const QString &implementationVersion) { Compat *compat = nullptr; int korg = productId.indexOf(QLatin1String("KOrganizer")); int outl9 = productId.indexOf(QLatin1String("Outlook 9.0")); if (korg >= 0) { int versionStart = productId.indexOf(QLatin1Char(' '), korg); if (versionStart >= 0) { int versionStop = productId.indexOf(QRegExp(QStringLiteral("[ /]")), versionStart + 1); if (versionStop >= 0) { QString version = productId.mid(versionStart + 1, versionStop - versionStart - 1); int versionNum = version.section(QLatin1Char('.'), 0, 0).toInt() * 10000 + version.section(QLatin1Char('.'), 1, 1).toInt() * 100 + version.section(QLatin1Char('.'), 2, 2).toInt(); int releaseStop = productId.indexOf(QLatin1Char('/'), versionStop); QString release; if (releaseStop > versionStop) { release = productId.mid(versionStop + 1, releaseStop - versionStop - 1); } if (versionNum < 30100) { compat = new CompatPre31; } else if (versionNum < 30200) { compat = new CompatPre32; } else if (versionNum == 30200 && release == QLatin1String("pre")) { qCDebug(KCALCORE_LOG) << "Generating compat for KOrganizer 3.2 pre"; compat = new Compat32PrereleaseVersions; } else if (versionNum < 30400) { compat = new CompatPre34; } else if (versionNum < 30500) { compat = new CompatPre35; } } } } else if (outl9 >= 0) { qCDebug(KCALCORE_LOG) << "Generating compat for Outlook < 2000 (Outlook 9.0)"; compat = new CompatOutlook9; } if (!compat) { compat = new Compat; } // Older implementations lacked the implementation version, // so apply this fix if it is a file from kontact and the version is missing. if (implementationVersion.isEmpty() && (productId.contains(QStringLiteral("libkcal")) || productId.contains(QStringLiteral("KOrganizer")) || productId.contains(QStringLiteral("KAlarm")))) { compat = new CompatPre410(compat); } return compat; } Compat::Compat() : d(nullptr) { } Compat::~Compat() { } void Compat::fixEmptySummary(const Incidence::Ptr &incidence) { // some stupid vCal exporters ignore the standard and use Description // instead of Summary for the default field. Correct for this: Copy the // first line of the description to the summary (if summary is just one // line, move it) if (incidence->summary().isEmpty() && !(incidence->description().isEmpty())) { QString oldDescription = incidence->description().trimmed(); QString newSummary(oldDescription); newSummary.remove(QRegExp(QStringLiteral("\n.*"))); incidence->setSummary(newSummary); if (oldDescription == newSummary) { incidence->setDescription(QLatin1String("")); } } } void Compat::fixAlarms(const Incidence::Ptr &incidence) { Q_UNUSED(incidence); } void Compat::fixFloatingEnd(QDate &date) { Q_UNUSED(date); } void Compat::fixRecurrence(const Incidence::Ptr &incidence) { Q_UNUSED(incidence); // Prevent use of compatibility mode during subsequent changes by the application // incidence->recurrence()->setCompatVersion(); } int Compat::fixPriority(int priority) { return priority; } bool Compat::useTimeZoneShift() const { return true; } void Compat::setCreatedToDtStamp(const Incidence::Ptr &incidence, const QDateTime &dtstamp) { Q_UNUSED(incidence); Q_UNUSED(dtstamp); } class Q_DECL_HIDDEN CompatDecorator::Private { public: Compat *compat; }; CompatDecorator::CompatDecorator(Compat *compat) : d(new CompatDecorator::Private) { d->compat = compat; } CompatDecorator::~CompatDecorator() { delete d->compat; delete d; } void CompatDecorator::fixEmptySummary(const Incidence::Ptr &incidence) { d->compat->fixEmptySummary(incidence); } void CompatDecorator::fixAlarms(const Incidence::Ptr &incidence) { d->compat->fixAlarms(incidence); } void CompatDecorator::fixFloatingEnd(QDate &date) { d->compat->fixFloatingEnd(date); } void CompatDecorator::fixRecurrence(const Incidence::Ptr &incidence) { d->compat->fixRecurrence(incidence); } int CompatDecorator::fixPriority(int priority) { return d->compat->fixPriority(priority); } bool CompatDecorator::useTimeZoneShift() const { return d->compat->useTimeZoneShift(); } void CompatDecorator::setCreatedToDtStamp(const Incidence::Ptr &incidence, const QDateTime &dtstamp) { d->compat->setCreatedToDtStamp(incidence, dtstamp); } CompatPre35::CompatPre35() : d(nullptr) { } CompatPre35::~CompatPre35() { } void CompatPre35::fixRecurrence(const Incidence::Ptr &incidence) { Recurrence *recurrence = incidence->recurrence(); if (recurrence) { QDateTime start(incidence->dtStart()); // kde < 3.5 only had one rrule, so no need to loop over all RRULEs. RecurrenceRule *r = recurrence->defaultRRule(); if (r && !r->dateMatchesRules(start)) { recurrence->addExDateTime(start); } } // Call base class method now that everything else is done Compat::fixRecurrence(incidence); } CompatPre34::CompatPre34() : d(nullptr) { } CompatPre34::~CompatPre34() { } int CompatPre34::fixPriority(int priority) { if (0 < priority && priority < 6) { // adjust 1->1, 2->3, 3->5, 4->7, 5->9 return 2 * priority - 1; } else { return priority; } } CompatPre32::CompatPre32() : d(nullptr) { } CompatPre32::~CompatPre32() { } void CompatPre32::fixRecurrence(const Incidence::Ptr &incidence) { Recurrence *recurrence = incidence->recurrence(); if (recurrence->recurs() && recurrence->duration() > 0) { recurrence->setDuration(recurrence->duration() + incidence->recurrence()->exDates().count()); } // Call base class method now that everything else is done CompatPre35::fixRecurrence(incidence); } CompatPre31::CompatPre31() : d(nullptr) { } CompatPre31::~CompatPre31() { } void CompatPre31::fixFloatingEnd(QDate &endDate) { endDate = endDate.addDays(1); } void CompatPre31::fixRecurrence(const Incidence::Ptr &incidence) { CompatPre32::fixRecurrence(incidence); Recurrence *recur = incidence->recurrence(); RecurrenceRule *r = nullptr; if (recur) { r = recur->defaultRRule(); } if (recur && r) { int duration = r->duration(); if (duration > 0) { // Backwards compatibility for KDE < 3.1. // rDuration was set to the number of time periods to recur, // with week start always on a Monday. // Convert this to the number of occurrences. r->setDuration(-1); QDate end(r->startDt().date()); bool doNothing = false; // # of periods: int tmp = (duration - 1) * r->frequency(); switch (r->recurrenceType()) { case RecurrenceRule::rWeekly: { end = end.addDays(tmp * 7 + 7 - end.dayOfWeek()); break; } case RecurrenceRule::rMonthly: { int month = end.month() - 1 + tmp; end.setDate(end.year() + month / 12, month % 12 + 1, 31); break; } case RecurrenceRule::rYearly: { end.setDate(end.year() + tmp, 12, 31); break; } default: doNothing = true; break; } if (!doNothing) { duration = r->durationTo( QDateTime(end, QTime(0, 0, 0), incidence->dtStart().timeZone())); r->setDuration(duration); } } /* addYearlyNum */ // Dates were stored as day numbers, with a fiddle to take account of // leap years. Convert the day number to a month. QList days = r->byYearDays(); if (!days.isEmpty()) { QList months = r->byMonths(); for (int i = 0; i < months.size(); ++i) { int newmonth = QDate(r->startDt().date().year(), 1, 1).addDays(months.at(i) - 1).month(); if (!months.contains(newmonth)) { months.append(newmonth); } } r->setByMonths(months); days.clear(); r->setByYearDays(days); } } } CompatOutlook9::CompatOutlook9() : d(nullptr) { } CompatOutlook9::~CompatOutlook9() { } void CompatOutlook9::fixAlarms(const Incidence::Ptr &incidence) { if (!incidence) { return; } Alarm::List alarms = incidence->alarms(); Alarm::List::Iterator end(alarms.end()); for (Alarm::List::Iterator it = alarms.begin(); it != end; ++it) { Alarm::Ptr al = *it; if (al && al->hasStartOffset()) { Duration offsetDuration = al->startOffset(); int offs = offsetDuration.asSeconds(); if (offs > 0) { offsetDuration = Duration(-offs); } al->setStartOffset(offsetDuration); } } } Compat32PrereleaseVersions::Compat32PrereleaseVersions() : d(nullptr) { } Compat32PrereleaseVersions::~Compat32PrereleaseVersions() { } bool Compat32PrereleaseVersions::useTimeZoneShift() const { return false; } CompatPre410::CompatPre410(Compat *decoratedCompat) : CompatDecorator(decoratedCompat) , d(nullptr) { } CompatPre410::~CompatPre410() { } void CompatPre410::setCreatedToDtStamp(const Incidence::Ptr &incidence, const QDateTime &dtstamp) { if (dtstamp.isValid()) { incidence->setCreated(dtstamp); } } diff --git a/src/compat_p.h b/src/compat_p.h index c439f7e49..f0379389b 100644 --- a/src/compat_p.h +++ b/src/compat_p.h @@ -1,392 +1,392 @@ /* This file is part of the kcalcore library. Copyright (c) 2002 Cornelius Schumacher Copyright (C) 2003-2004 Reinhold Kainhofer Copyright (C) 2012 Christian Mollekopf This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines classes for managing compatibility between different calendar formats. @author Cornelius Schumacher \ @author Reinhold Kainhofer \ */ #ifndef KCALCORE_COMPAT_P_H #define KCALCORE_COMPAT_P_H #include "incidence.h" #include // for Q_DISABLE_COPY() class QDate; class QString; -namespace KCalCore +namespace KCalendarCore { class Compat; /** @brief Factory for creating the right Compat object. @internal */ class CompatFactory { public: /** Creates the appropriate Compat class as determined by the Product ID. @param productId is a string containing a valid Product ID from a supported calendar format. @return A pointer to a Compat object which is owned by the caller. */ static Compat *createCompat(const QString &productId, const QString &implementationVersion); }; /** @brief This class provides compatibility to older or broken calendar files. @internal */ class Compat { public: /** Constructor. */ Compat(); /** Destructor. */ virtual ~Compat(); /** Fixes the recurrence rule for an incidence. @param incidence is a pointer to an Incidence object that may need its recurrence rule fixed. */ virtual void fixRecurrence(const Incidence::Ptr &incidence); /** Fixes an empty summary for an incidence. @param incidence is a pointer to an Incidence object that may need its summary fixed. */ virtual void fixEmptySummary(const Incidence::Ptr &incidence); /** Fixes the alarms list an incidence. @param incidence is a pointer to an Incidence object that may need its alarms fixed. */ virtual void fixAlarms(const Incidence::Ptr &incidence); /** Fixes the end date for floating events. @param date is the end date to fix. */ virtual void fixFloatingEnd(QDate &date); /** Fixes the priority. @param priority is the priority value to fix. @return an integer representing a valid priority value. */ virtual int fixPriority(int priority); /** Returns true if a timezone shift should be used; false otherwise. */ virtual bool useTimeZoneShift() const; /** Sets the created and dtstamp. */ virtual void setCreatedToDtStamp(const Incidence::Ptr &incidence, const QDateTime &dtstamp); private: //@cond PRIVATE Q_DISABLE_COPY(Compat) class Private; Private *const d; //@endcond }; /** @brief Decorator so multiple compatibility classes can be stacked. */ class CompatDecorator : public Compat { public: explicit CompatDecorator(Compat *decoratedCompat); ~CompatDecorator() override; /** @copydoc Compat::fixRecurrence() */ void fixRecurrence(const Incidence::Ptr &incidence) override; /** @copydoc Compat::fixEmptySummary() */ void fixEmptySummary(const Incidence::Ptr &incidence) override; /** @copydoc Compat::fixAlarms() */ void fixAlarms(const Incidence::Ptr &incidence) override; /** @copydoc Compat::fixFloatingEnd() */ void fixFloatingEnd(QDate &date) override; /** @copydoc Compat::fixPriority() */ int fixPriority(int priority) override; /** @copydoc Compat::useTimeZoneShift() */ bool useTimeZoneShift() const override; /** @copydoc Compat::setCreatedToDtStamp() */ void setCreatedToDtStamp(const Incidence::Ptr &incidence, const QDateTime &dtstamp) override; private: //@cond PRIVATE Q_DISABLE_COPY(CompatDecorator) class Private; Private *const d; //@endcond }; /** @brief Compatibility class for KOrganizer pre-3.5 calendar files. Before kde 3.5, the start date was not automatically a recurring date. So, if the start date doesn't match the recurrence rule, we need to add an ex-date for the date start. If a duration was given, the DTSTART was only counted if it matched, so by accident this was already the correct behavior, so we don't need to adjust the duration. */ class CompatPre35 : public Compat { public: CompatPre35(); ~CompatPre35() override; /** @copydoc Compat::fixRecurrence() */ void fixRecurrence(const Incidence::Ptr &incidence) override; private: //@cond PRIVATE class Private; Private *const d; //@endcond }; /** @brief Compatibility class for KOrganizer pre-3.4 calendar files. */ class CompatPre34 : public CompatPre35 { public: CompatPre34(); ~CompatPre34() override; /** @copydoc Compat::fixPriority() */ int fixPriority(int priority) override; private: //@cond PRIVATE class Private; Private *const d; //@endcond }; /** @brief Compatibility class for KOrganizer pre-3.2 calendar files. The recurrence has a specified number of repetitions. Pre-3.2, this was extended by the number of exception dates. This is also rfc 2445-compliant. The duration of an RRULE also counts events that are later excluded via EXDATE or EXRULE. */ class CompatPre32 : public CompatPre34 { public: CompatPre32(); ~CompatPre32() override; /** @copydoc Compat::fixRecurrence() */ void fixRecurrence(const Incidence::Ptr &incidence) override; private: //@cond PRIVATE class Private; Private *const d; //@endcond }; /** @brief Compatibility class for KOrganizer pre-3.1 calendar files. Before kde 3.1, floating events (events without a date) had 0:00 of their last day as the end date. E.g. 28.5.2005 0:00 until 28.5.2005 0:00 for an event that lasted the whole day on May 28, 2005. According to RFC 2445, the end date for such an event needs to be 29.5.2005 0:00. Update: We misunderstood rfc 2445 in this regard. For all-day events, the DTEND is the last day of the event. See a mail from the Author or rfc 2445: http://www.imc.org/ietf-calendar/archive1/msg03648.html However, as all other applications also got this wrong, we'll just leave it as it is and use the wrong interpretation (was also discussed on ietf-calsify) */ class CompatPre31 : public CompatPre32 { public: CompatPre31(); ~CompatPre31(); /** @copydoc Compat::fixFloatingEnd() */ void fixFloatingEnd(QDate &date) override; /** @copydoc Compat::fixRecurrence() */ void fixRecurrence(const Incidence::Ptr &incidence) override; private: //@cond PRIVATE class Private; Private *const d; //@endcond }; /** @brief Compatibility class for KOrganizer prerelease 3.2 calendar files. */ class Compat32PrereleaseVersions : public Compat { public: Compat32PrereleaseVersions(); ~Compat32PrereleaseVersions() override; /** @copydoc Compat::useTimeZoneShift() */ bool useTimeZoneShift() const override; private: //@cond PRIVATE class Private; Private *const d; //@endcond }; /** @brief Compatibility class for Outlook 9 calendar files. In Outlook 9, alarms have the wrong sign. I.e. RFC 2445 says that negative values for the trigger are before the event's start. Outlook/exchange, however used positive values. */ class CompatOutlook9 : public Compat { public: CompatOutlook9(); ~CompatOutlook9() override; /** @copydoc Compat::fixAlarms() */ void fixAlarms(const Incidence::Ptr &incidence) override; private: //@cond PRIVATE class Private; Private *const d; //@endcond }; /** @brief Compatibility class for Kontact < 4.10 calendar files. */ class CompatPre410 : public CompatDecorator { public: explicit CompatPre410(Compat *decoratedCompat); ~CompatPre410() override; /** @copydoc Compat::setCreatedToDtStamp() */ void setCreatedToDtStamp(const Incidence::Ptr &incidence, const QDateTime &dtstamp) override; private: //@cond PRIVATE class Private; Private *const d; //@endcond }; } #endif diff --git a/src/customproperties.cpp b/src/customproperties.cpp index 8a3ea3f2a..9237488c9 100644 --- a/src/customproperties.cpp +++ b/src/customproperties.cpp @@ -1,269 +1,269 @@ /* This file is part of the kcalcore library. Copyright (c) 2002,2006,2010 David Jarvie This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the CustomProperties class. @brief A class to manage custom calendar properties. @author David Jarvie \ */ #include "customproperties.h" #include #include "kcalcore_debug.h" -using namespace KCalCore; +using namespace KCalendarCore; //@cond PRIVATE static bool checkName(const QByteArray &name); class Q_DECL_HIDDEN CustomProperties::Private { public: bool operator==(const Private &other) const; QMap mProperties; // custom calendar properties QMap mPropertyParameters; // Volatile properties are not written back to the serialized format and are not compared in operator== // They are only used for runtime purposes and are not part of the payload. QMap mVolatileProperties; bool isVolatileProperty(const QString &name) const { return name.startsWith(QLatin1String("X-KDE-VOLATILE")); } }; bool CustomProperties::Private::operator==(const CustomProperties::Private &other) const { if (mProperties.count() != other.mProperties.count()) { // qCDebug(KCALCORE_LOG) << "Property count is different:" << mProperties << other.mProperties; return false; } for (QMap::ConstIterator it = mProperties.begin(); it != mProperties.end(); ++it) { QMap::ConstIterator itOther = other.mProperties.find(it.key()); if (itOther == other.mProperties.end() || itOther.value() != it.value()) { return false; } } for (QMap::ConstIterator it = mPropertyParameters.begin(); it != mPropertyParameters.end(); ++it) { QMap::ConstIterator itOther = other.mPropertyParameters.find(it.key()); if (itOther == other.mPropertyParameters.end() || itOther.value() != it.value()) { return false; } } return true; } //@endcond CustomProperties::CustomProperties() : d(new Private) { } CustomProperties::CustomProperties(const CustomProperties &cp) : d(new Private(*cp.d)) { } CustomProperties &CustomProperties::operator=(const CustomProperties &other) { // check for self assignment if (&other == this) { return *this; } *d = *other.d; return *this; } CustomProperties::~CustomProperties() { delete d; } bool CustomProperties::operator==(const CustomProperties &other) const { return *d == *other.d; } void CustomProperties::setCustomProperty(const QByteArray &app, const QByteArray &key, const QString &value) { if (value.isNull() || key.isEmpty() || app.isEmpty()) { return; } QByteArray property = "X-KDE-" + app + '-' + key; if (!checkName(property)) { return; } customPropertyUpdate(); if (d->isVolatileProperty(QLatin1String(property))) { d->mVolatileProperties[property] = value; } else { d->mProperties[property] = value; } customPropertyUpdated(); } void CustomProperties::removeCustomProperty(const QByteArray &app, const QByteArray &key) { removeNonKDECustomProperty(QByteArray("X-KDE-" + app + '-' + key)); } QString CustomProperties::customProperty(const QByteArray &app, const QByteArray &key) const { return nonKDECustomProperty(QByteArray("X-KDE-" + app + '-' + key)); } QByteArray CustomProperties::customPropertyName(const QByteArray &app, const QByteArray &key) { QByteArray property("X-KDE-" + app + '-' + key); if (!checkName(property)) { return QByteArray(); } return property; } void CustomProperties::setNonKDECustomProperty(const QByteArray &name, const QString &value, const QString ¶meters) { if (value.isNull() || !checkName(name)) { return; } customPropertyUpdate(); d->mProperties[name] = value; d->mPropertyParameters[name] = parameters; customPropertyUpdated(); } void CustomProperties::removeNonKDECustomProperty(const QByteArray &name) { if (d->mProperties.contains(name)) { customPropertyUpdate(); d->mProperties.remove(name); d->mPropertyParameters.remove(name); customPropertyUpdated(); } else if (d->mVolatileProperties.contains(name)) { customPropertyUpdate(); d->mVolatileProperties.remove(name); customPropertyUpdated(); } } QString CustomProperties::nonKDECustomProperty(const QByteArray &name) const { return d->isVolatileProperty(QLatin1String(name)) ? d->mVolatileProperties.value(name) : d->mProperties.value(name); } QString CustomProperties::nonKDECustomPropertyParameters(const QByteArray &name) const { return d->mPropertyParameters.value(name); } void CustomProperties::setCustomProperties(const QMap &properties) { bool changed = false; for (QMap::ConstIterator it = properties.begin(); it != properties.end(); ++it) { // Validate the property name and convert any null string to empty string if (checkName(it.key())) { if (d->isVolatileProperty(QLatin1String(it.key()))) { d->mVolatileProperties[it.key()] = it.value().isNull() ? QLatin1String("") : it.value(); } else { d->mProperties[it.key()] = it.value().isNull() ? QLatin1String("") : it.value(); } if (!changed) { customPropertyUpdate(); } changed = true; } } if (changed) { customPropertyUpdated(); } } QMap CustomProperties::customProperties() const { QMap result; result.unite(d->mProperties); result.unite(d->mVolatileProperties); return result; } void CustomProperties::customPropertyUpdate() { } void CustomProperties::customPropertyUpdated() { } //@cond PRIVATE bool checkName(const QByteArray &name) { // Check that the property name starts with 'X-' and contains // only the permitted characters const char *n = name.constData(); int len = name.length(); if (len < 2 || n[0] != 'X' || n[1] != '-') { return false; } for (int i = 2; i < len; ++i) { char ch = n[i]; if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '-') { continue; } return false; // invalid character found } return true; } //@endcond -QDataStream &KCalCore::operator<<(QDataStream &stream, - const KCalCore::CustomProperties &properties) +QDataStream &KCalendarCore::operator<<(QDataStream &stream, + const KCalendarCore::CustomProperties &properties) { return stream << properties.d->mProperties << properties.d->mPropertyParameters; } -QDataStream &KCalCore::operator>>(QDataStream &stream, - KCalCore::CustomProperties &properties) +QDataStream &KCalendarCore::operator>>(QDataStream &stream, + KCalendarCore::CustomProperties &properties) { properties.d->mVolatileProperties.clear(); return stream >> properties.d->mProperties >> properties.d->mPropertyParameters; } diff --git a/src/customproperties.h b/src/customproperties.h index 5fe876091..9a0c90fb5 100644 --- a/src/customproperties.h +++ b/src/customproperties.h @@ -1,221 +1,221 @@ /* This file is part of the kcalcore library. Copyright (c) 2002,2006,2010 David Jarvie This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the CustomProperties class. @author David Jarvie \ */ #ifndef KCALCORE_CUSTOMPROPERTIES_H #define KCALCORE_CUSTOMPROPERTIES_H #include "kcalcore_export.h" #include #include -namespace KCalCore +namespace KCalendarCore { /** @brief A class to manage custom calendar properties. This class represents custom calendar properties. It is used as a base class for classes which represent calendar components. A custom property name written by the kcalcore library has the form X-KDE-APP-KEY where APP represents the application name, and KEY distinguishes individual properties for the application. In keeping with RFC2445, property names must be composed only of the characters A-Z, a-z, 0-9 and '-'. */ class KCALCORE_EXPORT CustomProperties { friend KCALCORE_EXPORT QDataStream &operator<<(QDataStream &s, - const KCalCore::CustomProperties &properties); + const KCalendarCore::CustomProperties &properties); friend KCALCORE_EXPORT QDataStream &operator>>(QDataStream &s, - KCalCore::CustomProperties &properties); + KCalendarCore::CustomProperties &properties); public: /** Constructs an empty custom properties instance. */ CustomProperties(); /** Copy constructor. @param other is the one to copy. */ CustomProperties(const CustomProperties &other); /** Destructor. */ virtual ~CustomProperties(); /** Compare this with @p properties for equality. @param properties is the one to compare. @warning The comparison is not polymorphic. */ bool operator==(const CustomProperties &properties) const; /** Create or modify a custom calendar property. @param app Application name as it appears in the custom property name. @param key Property identifier specific to the application. @param value The property's value. A call with a value of QString() will be ignored. @see removeCustomProperty(). */ void setCustomProperty(const QByteArray &app, const QByteArray &key, const QString &value); /** Delete a custom calendar property. @param app Application name as it appears in the custom property name. @param key Property identifier specific to the application. @see setCustomProperty(). */ void removeCustomProperty(const QByteArray &app, const QByteArray &key); /** Return the value of a custom calendar property. @param app Application name as it appears in the custom property name. @param key Property identifier specific to the application. @return Property value, or QString() if (and only if) the property does not exist. */ Q_REQUIRED_RESULT QString customProperty(const QByteArray &app, const QByteArray &key) const; /** Validate and return the full name of a custom calendar property. @param app Application name as it appears in the custom property name. @param key Property identifier specific to the application. @return Full property name, or empty string if it would contain invalid characters */ Q_REQUIRED_RESULT static QByteArray customPropertyName(const QByteArray &app, const QByteArray &key); /** Create or modify a non-KDE or non-standard custom calendar property. @param name Full property name @param value The property's value. A call with a value of QString() will be ignored. @param parameters The formatted list of parameters for the property. They should be formatted as RFC specifies, that is, KEY=VALUE;KEY2=VALUE2. We're mostly concerned about passing them through as-is albeit they can be of course parsed if need be. @see removeNonKDECustomProperty(). */ void setNonKDECustomProperty(const QByteArray &name, const QString &value, const QString ¶meters = QString()); /** Delete a non-KDE or non-standard custom calendar property. @param name Full property name @see setNonKDECustomProperty(). */ void removeNonKDECustomProperty(const QByteArray &name); /** Return the value of a non-KDE or non-standard custom calendar property. @param name Full property name @return Property value, or QString() if (and only if) the property does not exist. */ Q_REQUIRED_RESULT QString nonKDECustomProperty(const QByteArray &name) const; /** Return the parameters of a non-KDE or non-standard custom calendar property. @param name Full property name @return The parameters for the given property. Empty string is returned if none are set. */ Q_REQUIRED_RESULT QString nonKDECustomPropertyParameters(const QByteArray &name) const; /** Initialise the alarm's custom calendar properties to the specified key/value pairs. @param properties is a QMap of property key/value pairs. @see customProperties(). */ void setCustomProperties(const QMap &properties); /** Returns all custom calendar property key/value pairs. @see setCustomProperties(). */ Q_REQUIRED_RESULT QMap customProperties() const; /** Assignment operator. @warning The assignment is not polymorphic. @param other is the CustomProperty to assign. */ CustomProperties &operator=(const CustomProperties &other); protected: /** Called before a custom property will be changed. The default implementation does nothing: override in derived classes to perform change processing. */ virtual void customPropertyUpdate(); /** Called when a custom property has been changed. The default implementation does nothing: override in derived classes to perform change processing. */ virtual void customPropertyUpdated(); private: //@cond PRIVATE class Private; Private *const d; //@endcond }; /** Serializes the @p properties object into the @p stream. */ KCALCORE_EXPORT QDataStream &operator<<(QDataStream &stream, - const KCalCore::CustomProperties &properties); + const KCalendarCore::CustomProperties &properties); /** Initializes the @p properties object from the @p stream. */ KCALCORE_EXPORT QDataStream &operator>>(QDataStream &stream, - KCalCore::CustomProperties &properties); + KCalendarCore::CustomProperties &properties); } #endif diff --git a/src/duration.cpp b/src/duration.cpp index 7d33da11a..4da6aff8b 100644 --- a/src/duration.cpp +++ b/src/duration.cpp @@ -1,231 +1,231 @@ /* This file is part of the kcalcore library. Copyright (c) 2001 Cornelius Schumacher Copyright (c) 2007 David Jarvie This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the Duration class. @brief Represents a span of time measured in seconds. @author Cornelius Schumacher \ @author David Jarvie \ */ #include "duration.h" #include #include -using namespace KCalCore; +using namespace KCalendarCore; /** Private class that helps to provide binary compatibility between releases. @internal */ //@cond PRIVATE -class Q_DECL_HIDDEN KCalCore::Duration::Private +class Q_DECL_HIDDEN KCalendarCore::Duration::Private { public: int seconds() const { return mDaily ? mDuration * 86400 : mDuration; } int mDuration; // number of seconds or days in the duration bool mDaily = false; // specified in terms of days rather than seconds }; //@endcond Duration::Duration() - : d(new KCalCore::Duration::Private()) + : d(new KCalendarCore::Duration::Private()) { } Duration::Duration(const QDateTime &start, const QDateTime &end) - : d(new KCalCore::Duration::Private()) + : d(new KCalendarCore::Duration::Private()) { if (start.time() == end.time() && start.timeZone() == end.timeZone()) { d->mDuration = start.daysTo(end); d->mDaily = true; } else { d->mDuration = start.secsTo(end); d->mDaily = false; } } Duration::Duration(const QDateTime &start, const QDateTime &end, Type type) - : d(new KCalCore::Duration::Private()) + : d(new KCalendarCore::Duration::Private()) { if (type == Days) { QDateTime endSt(end.toTimeZone(start.timeZone())); d->mDuration = start.daysTo(endSt); if (d->mDuration) { // Round down to whole number of days if necessary if (start < endSt) { if (endSt.time() < start.time()) { --d->mDuration; } } else { if (endSt.time() > start.time()) { ++d->mDuration; } } } d->mDaily = true; } else { d->mDuration = start.secsTo(end); d->mDaily = false; } } Duration::Duration(int duration, Type type) - : d(new KCalCore::Duration::Private()) + : d(new KCalendarCore::Duration::Private()) { d->mDuration = duration; d->mDaily = (type == Days); } Duration::Duration(const Duration &duration) - : d(new KCalCore::Duration::Private(*duration.d)) + : d(new KCalendarCore::Duration::Private(*duration.d)) { } Duration::~Duration() { delete d; } Duration &Duration::operator=(const Duration &duration) { // check for self assignment if (&duration == this) { return *this; } *d = *duration.d; return *this; } Duration::operator bool() const { return d->mDuration; } bool Duration::operator<(const Duration &other) const { if (d->mDaily == other.d->mDaily) { // guard against integer overflow for two daily durations return d->mDuration < other.d->mDuration; } return d->seconds() < other.d->seconds(); } bool Duration::operator==(const Duration &other) const { // Note: daily and non-daily durations are always unequal, since a day's // duration may differ from 24 hours if it happens to span a daylight saving // time change. return d->mDuration == other.d->mDuration && d->mDaily == other.d->mDaily; } Duration &Duration::operator+=(const Duration &other) { if (d->mDaily == other.d->mDaily) { d->mDuration += other.d->mDuration; } else if (d->mDaily) { d->mDuration = d->mDuration * 86400 + other.d->mDuration; d->mDaily = false; } else { d->mDuration += other.d->mDuration + 86400; } return *this; } Duration Duration::operator-() const { return Duration(-d->mDuration, (d->mDaily ? Days : Seconds)); } Duration &Duration::operator-=(const Duration &duration) { return operator+=(-duration); } Duration &Duration::operator*=(int value) { d->mDuration *= value; return *this; } Duration &Duration::operator/=(int value) { d->mDuration /= value; return *this; } QDateTime Duration::end(const QDateTime &start) const { return d->mDaily ? start.addDays(d->mDuration) : start.addSecs(d->mDuration); } Duration::Type Duration::type() const { return d->mDaily ? Days : Seconds; } bool Duration::isDaily() const { return d->mDaily; } int Duration::asSeconds() const { return d->seconds(); } int Duration::asDays() const { return d->mDaily ? d->mDuration : d->mDuration / 86400; } int Duration::value() const { return d->mDuration; } bool Duration::isNull() const { return d->mDuration == 0; } -QDataStream &KCalCore::operator<<(QDataStream &out, const KCalCore::Duration &duration) +QDataStream &KCalendarCore::operator<<(QDataStream &out, const KCalendarCore::Duration &duration) { out << duration.d->mDuration << duration.d->mDaily; return out; } -QDataStream &KCalCore::operator>>(QDataStream &in, KCalCore::Duration &duration) +QDataStream &KCalendarCore::operator>>(QDataStream &in, KCalendarCore::Duration &duration) { in >> duration.d->mDuration >> duration.d->mDaily; return in; } diff --git a/src/duration.h b/src/duration.h index 1ba31fc75..1c4707bae 100644 --- a/src/duration.h +++ b/src/duration.h @@ -1,353 +1,353 @@ /* This file is part of the kcalcore library. Copyright (c) 2001-2003 Cornelius Schumacher Copyright (c) 2007 David Jarvie This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the Duration class. @author Cornelius Schumacher \ @author David Jarvie \ */ #ifndef KCALCORE_DURATION_H #define KCALCORE_DURATION_H #include "kcalcore_export.h" #include #include #include class QDateTime; -namespace KCalCore +namespace KCalendarCore { /** @brief Represents a span of time measured in seconds or days. A duration is a span of time measured in seconds or days. Construction can be done by specifying a stop and end time, or simply by specifying the number of seconds or days. Much of the time, it does not matter whether a duration is specified in seconds or in days. But it does make a difference when a duration is used to define a time period encompassing a daylight saving time change. */ class KCALCORE_EXPORT Duration { public: /** The unit of time used to define the duration. */ enum Type { Seconds, /**< duration is a number of seconds */ Days /**< duration is a number of days */ }; /** Constructs a duration of 0 seconds. */ Duration(); /** Constructs a duration from @p start to @p end. If the time of day in @p start and @p end is equal, and their time specifications (i.e. time zone etc.) are the same, the duration will be set in terms of days. Otherwise, the duration will be set in terms of seconds. @param start is the time the duration begins. @param end is the time the duration ends. */ Duration(const QDateTime &start, const QDateTime &end); /** Constructs a duration from @p start to @p end. If @p type is Days, and the time of day in @p start's time zone differs between @p start and @p end, the duration will be rounded down to the nearest whole number of days. @param start is the time the duration begins. @param end is the time the duration ends. @param type the unit of time to use (seconds or days) */ Duration(const QDateTime &start, const QDateTime &end, Type type); /** Constructs a duration with a number of seconds or days. @param duration the number of seconds or days in the duration @param type the unit of time to use (seconds or days) */ // Keep the following implicit since instances are often used in integer evaluations. Duration(int duration, Type type = Seconds); //krazy:exclude=explicit /** Constructs a duration by copying another duration object. @param duration is the duration to copy. */ Duration(const Duration &duration); /** Destroys a duration. */ ~Duration(); /** Sets this duration equal to @p duration. @param duration is the duration to copy. */ Duration &operator=(const Duration &duration); /** Returns true if this duration is non-zero. */ operator bool() const; /** Returns true if this duration is zero. */ bool operator!() const { return !operator bool(); } /** Returns true if this duration is smaller than the @p other. @param other is the other duration to compare. */ bool operator<(const Duration &other) const; /** Returns true if this duration is smaller than or equal to the @p other. @param other is the other duration to compare. */ bool operator<=(const Duration &other) const { return !other.operator < (*this); } /** Returns true if this duration is greater than the @p other. @param other is the other duration to compare. */ bool operator>(const Duration &other) const { return other.operator < (*this); } /** Returns true if this duration is greater than or equal to the @p other. @param other is the other duration to compare. */ bool operator>=(const Duration &other) const { return !operator<(other); } /** Returns true if this duration is equal to the @p other. Daily and non-daily durations are always considered unequal, since a day's duration may differ from 24 hours if it happens to span a daylight saving time change. @param other the other duration to compare */ bool operator==(const Duration &other) const; /** Returns true if this duration is not equal to the @p other. Daily and non-daily durations are always considered unequal, since a day's duration may differ from 24 hours if it happens to span a daylight saving time change. @param other is the other duration to compare. */ bool operator!=(const Duration &other) const { return !operator==(other); } /** Adds another duration to this one. If one is in terms of days and the other in terms of seconds, the result is in terms of seconds. @param other the other duration to add */ Duration &operator+=(const Duration &other); /** Adds two durations. If one is in terms of days and the other in terms of seconds, the result is in terms of seconds. @param other the other duration to add @return combined duration */ Duration operator+(const Duration &other) const { return Duration(*this) += other; } /** Returns the negative of this duration. */ Duration operator-() const; /** Subtracts another duration from this one. If one is in terms of days and the other in terms of seconds, the result is in terms of seconds. @param other the other duration to subtract */ Duration &operator-=(const Duration &other); /** Returns the difference between another duration and this. If one is in terms of days and the other in terms of seconds, the result is in terms of seconds. @param other the other duration to subtract @return difference in durations */ Duration operator-(const Duration &other) const { return Duration(*this) += other; } /** Multiplies this duration by a value. @param value value to multiply by */ Duration &operator*=(int value); /** Multiplies a duration by a value. @param value value to multiply by @return resultant duration */ Duration operator*(int value) const { return Duration(*this) *= value; } /** Divides this duration by a value. @param value value to divide by */ Duration &operator/=(int value); /** Divides a duration by a value. @param value value to divide by @return resultant duration */ Duration operator/(int value) const { return Duration(*this) /= value; } /** Computes a duration end time by adding the number of seconds or days in the duration to the specified @p start time. @param start is the start time. @return end time. */ Q_REQUIRED_RESULT QDateTime end(const QDateTime &start) const; /** Returns the time units (seconds or days) used to specify the duration. */ Q_REQUIRED_RESULT Type type() const; /** Returns whether the duration is specified in terms of days rather than seconds. */ Q_REQUIRED_RESULT bool isDaily() const; /** Returns the length of the duration in seconds. */ Q_REQUIRED_RESULT int asSeconds() const; /** Returns the length of the duration in days. If the duration is not an exact number of days, it is rounded down to return the number of whole days. */ Q_REQUIRED_RESULT int asDays() const; /** Returns the length of the duration in seconds or days. @return if isDaily(), duration in days, else duration in seconds */ Q_REQUIRED_RESULT int value() const; /** Returns true if the duration is 0 seconds. */ Q_REQUIRED_RESULT bool isNull() const; private: //@cond PRIVATE class Private; Private *const d; //@endcond - friend KCALCORE_EXPORT QDataStream &operator<<(QDataStream &s, const KCalCore::Duration &); - friend KCALCORE_EXPORT QDataStream &operator>>(QDataStream &s, KCalCore::Duration &); + friend KCALCORE_EXPORT QDataStream &operator<<(QDataStream &s, const KCalendarCore::Duration &); + friend KCALCORE_EXPORT QDataStream &operator>>(QDataStream &s, KCalendarCore::Duration &); }; /** * Duration serializer. * * @since 4.12 */ -KCALCORE_EXPORT QDataStream &operator<<(QDataStream &out, const KCalCore::Duration &); +KCALCORE_EXPORT QDataStream &operator<<(QDataStream &out, const KCalendarCore::Duration &); /** * Duration deserializer. * * @since 4.12 */ -KCALCORE_EXPORT QDataStream &operator>>(QDataStream &in, KCalCore::Duration &); +KCALCORE_EXPORT QDataStream &operator>>(QDataStream &in, KCalendarCore::Duration &); inline uint qHash(const Duration &duration, uint seed = 0) { return qHash(qMakePair(duration.isDaily(), duration.asSeconds()), seed); } } -Q_DECLARE_METATYPE(KCalCore::Duration) +Q_DECLARE_METATYPE(KCalendarCore::Duration) #endif diff --git a/src/event.cpp b/src/event.cpp index 5d1a8c62d..24724e99f 100644 --- a/src/event.cpp +++ b/src/event.cpp @@ -1,355 +1,355 @@ /* This file is part of the kcalcore library. Copyright (c) 2001 Cornelius Schumacher This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the Event class. @brief This class provides an Event in the sense of RFC2445. @author Cornelius Schumacher \ */ #include "event.h" #include "visitor.h" #include "utils.h" #include "kcalcore_debug.h" #include -using namespace KCalCore; +using namespace KCalendarCore; /** Private class that helps to provide binary compatibility between releases. @internal */ //@cond PRIVATE -class Q_DECL_HIDDEN KCalCore::Event::Private +class Q_DECL_HIDDEN KCalendarCore::Event::Private { public: Private() : mTransparency(Opaque), mMultiDayValid(false), mMultiDay(false) {} - Private(const KCalCore::Event::Private &other) + Private(const KCalendarCore::Event::Private &other) : mDtEnd(other.mDtEnd), mTransparency(other.mTransparency), mMultiDayValid(false), mMultiDay(false) {} QDateTime mDtEnd; Transparency mTransparency; bool mMultiDayValid = false; bool mMultiDay = false; }; //@endcond Event::Event() - : d(new KCalCore::Event::Private) + : d(new KCalendarCore::Event::Private) { } Event::Event(const Event &other) - : Incidence(other), d(new KCalCore::Event::Private(*other.d)) + : Incidence(other), d(new KCalendarCore::Event::Private(*other.d)) { } Event::Event(const Incidence &other) : Incidence(other) - , d(new KCalCore::Event::Private) + , d(new KCalendarCore::Event::Private) { } Event::~Event() { delete d; } Event *Event::clone() const { return new Event(*this); } IncidenceBase &Event::assign(const IncidenceBase &other) { if (&other != this) { Incidence::assign(other); const Event *e = static_cast(&other); *d = *(e->d); } return *this; } bool Event::equals(const IncidenceBase &event) const { if (!Incidence::equals(event)) { return false; } else { // If they weren't the same type IncidenceBase::equals would had returned false already const Event *e = static_cast(&event); return ((dtEnd() == e->dtEnd()) || (!dtEnd().isValid() && !e->dtEnd().isValid())) && transparency() == e->transparency(); } } Incidence::IncidenceType Event::type() const { return TypeEvent; } QByteArray Event::typeStr() const { return QByteArrayLiteral("Event"); } void Event::setDtStart(const QDateTime &dt) { d->mMultiDayValid = false; Incidence::setDtStart(dt); } void Event::setDtEnd(const QDateTime &dtEnd) { if (mReadOnly) { return; } if (d->mDtEnd != dtEnd || hasDuration() == dtEnd.isValid()) { update(); d->mDtEnd = dtEnd; d->mMultiDayValid = false; setHasDuration(!dtEnd.isValid()); setFieldDirty(FieldDtEnd); updated(); } } QDateTime Event::dtEnd() const { if (d->mDtEnd.isValid()) { return d->mDtEnd; } if (hasDuration()) { if (allDay()) { // For all day events, dtEnd is always inclusive QDateTime end = duration().end(dtStart().addDays(-1)); return end >= dtStart() ? end : dtStart(); } else { return duration().end(dtStart()); } } // It is valid for a VEVENT to be without a DTEND. See RFC2445, Sect4.6.1. // Be careful to use Event::dateEnd() as appropriate due to this possibility. return dtStart(); } QDate Event::dateEnd() const { QDateTime end = dtEnd().toTimeZone(dtStart().timeZone()); if (allDay()) { return end.date(); } else { return end.addSecs(-1).date(); } } bool Event::hasEndDate() const { return d->mDtEnd.isValid(); } bool Event::isMultiDay(const QTimeZone &zone) const { // First off, if spec's not valid, we can check for cache if (!zone.isValid() && d->mMultiDayValid) { return d->mMultiDay; } // Not in cache -> do it the hard way QDateTime start, end; if (!zone.isValid()) { start = dtStart(); end = dtEnd(); } else { start = dtStart().toTimeZone(zone); end = dtEnd().toTimeZone(zone); } bool multi = (start < end && start.date() != end.date()); // End date is non inclusive // If we have an incidence that duration is one day and ends with a start of a new day // than it is not a multiday event if (multi && end.time() == QTime(0, 0, 0)) { multi = start.daysTo(end) > 1; } // Update the cache // Also update Cache if spec is invalid d->mMultiDayValid = true; d->mMultiDay = multi; return multi; } void Event::shiftTimes(const QTimeZone &oldZone, const QTimeZone &newZone) { Incidence::shiftTimes(oldZone, newZone); if (d->mDtEnd.isValid()) { d->mDtEnd = d->mDtEnd.toTimeZone(oldZone); d->mDtEnd.setTimeZone(newZone); } } void Event::setTransparency(Event::Transparency transparency) { if (mReadOnly) { return; } update(); d->mTransparency = transparency; setFieldDirty(FieldTransparency); updated(); } Event::Transparency Event::transparency() const { return d->mTransparency; } void Event::setDuration(const Duration &duration) { setDtEnd(QDateTime()); Incidence::setDuration(duration); } void Event::setAllDay(bool allday) { if (allday != allDay() && !mReadOnly) { setFieldDirty(FieldDtEnd); Incidence::setAllDay(allday); } } bool Event::accept(Visitor &v, const IncidenceBase::Ptr &incidence) { return v.visit(incidence.staticCast()); } QDateTime Event::dateTime(DateTimeRole role) const { switch (role) { case RoleRecurrenceStart: case RoleAlarmStartOffset: case RoleStartTimeZone: case RoleSort: return dtStart(); case RoleCalendarHashing: return !recurs() && !isMultiDay() ? dtStart() : QDateTime(); case RoleAlarmEndOffset: case RoleEndTimeZone: case RoleEndRecurrenceBase: case RoleEnd: case RoleDisplayEnd: return dtEnd(); case RoleDisplayStart: return dtStart(); case RoleAlarm: if (alarms().isEmpty()) { return QDateTime(); } else { Alarm::Ptr alarm = alarms().at(0); return alarm->hasStartOffset() ? dtStart() : dtEnd(); } default: return QDateTime(); } } void Event::setDateTime(const QDateTime &dateTime, DateTimeRole role) { switch (role) { case RoleDnD: { const qint64 duration = dtStart().secsTo(dtEnd()); setDtStart(dateTime); setDtEnd(dateTime.addSecs(duration <= 0 ? 3600 : duration)); break; } case RoleEnd: setDtEnd(dateTime); break; default: qCDebug(KCALCORE_LOG) << "Unhandled role" << role; } } void Event::virtual_hook(VirtualHook id, void *data) { Q_UNUSED(id); Q_UNUSED(data); } -QLatin1String KCalCore::Event::mimeType() const +QLatin1String KCalendarCore::Event::mimeType() const { return Event::eventMimeType(); } QLatin1String Event::eventMimeType() { return QLatin1String("application/x-vnd.akonadi.calendar.event"); } QLatin1String Event::iconName(const QDateTime &) const { return QLatin1String("view-calendar-day"); } void Event::serialize(QDataStream &out) const { Incidence::serialize(out); serializeQDateTimeAsKDateTime(out, d->mDtEnd); out << hasEndDate() << static_cast(d->mTransparency) << d->mMultiDayValid << d->mMultiDay; } void Event::deserialize(QDataStream &in) { Incidence::deserialize(in); bool hasEndDateDummy = true; deserializeKDateTimeAsQDateTime(in, d->mDtEnd); in >> hasEndDateDummy; quint32 transp; in >> transp; d->mTransparency = static_cast(transp); in >> d->mMultiDayValid >> d->mMultiDay; } bool Event::supportsGroupwareCommunication() const { return true; } diff --git a/src/event.h b/src/event.h index 8ef6fd9b2..5c8f6f8ac 100644 --- a/src/event.h +++ b/src/event.h @@ -1,281 +1,281 @@ /* This file is part of the kcalcore library. Copyright (c) 2001-2003 Cornelius Schumacher This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the Event class. @author Cornelius Schumacher \ */ #ifndef KCALCORE_EVENT_H #define KCALCORE_EVENT_H #include "kcalcore_export.h" #include "incidence.h" #include -namespace KCalCore +namespace KCalendarCore { /** @brief This class provides an Event in the sense of RFC2445. */ class KCALCORE_EXPORT Event : public Incidence { Q_GADGET Q_PROPERTY(QDateTime dtEnd READ dtEnd WRITE setDtEnd) - Q_PROPERTY(KCalCore::Event::Transparency transparency READ transparency WRITE setTransparency) + Q_PROPERTY(KCalendarCore::Event::Transparency transparency READ transparency WRITE setTransparency) public: /** The different Event transparency types. */ enum Transparency { Opaque, /**< Event appears in free/busy time */ Transparent /**< Event does @b not appear in free/busy time */ }; Q_ENUM(Transparency) /** A shared pointer to an Event object. */ typedef QSharedPointer Ptr; /** List of events. */ typedef QVector List; ///@cond PRIVATE // needed for Akonadi polymorphic payload support typedef Incidence SuperClass; ///@endcond /** Constructs an event. */ Event(); /** Copy constructor. @param other is the event to copy. */ Event(const Event &other); /** Costructs an event out of an incidence This constructs allows to make it easy to create an event from a todo. @param other is the incidence to copy. @since 4.14 */ Event(const Incidence &other); //krazy:exclude=explicit (copy ctor) /** Destroys the event. */ ~Event() override; /** @copydoc IncidenceBase::type() */ Q_REQUIRED_RESULT IncidenceType type() const override; /** @copydoc IncidenceBase::typeStr() */ Q_REQUIRED_RESULT QByteArray typeStr() const override; /** Returns an exact copy of this Event. The caller owns the returned object. */ Event *clone() const override; /** Sets the incidence starting date/time. @param dt is the starting date/time. @see IncidenceBase::dtStart(). */ void setDtStart(const QDateTime &dt) override; /** Sets the event end date and time. Important note for all day events: the end date is inclusive, the event will still occur during dtEnd(). When serializing to iCalendar DTEND will be dtEnd()+1, because the RFC states that DTEND is exclusive. @param dtEnd is a QDateTime specifying when the event ends. @see dtEnd(), dateEnd(). */ void setDtEnd(const QDateTime &dtEnd); /** Returns the event end date and time. Important note for all day events: the returned end date is inclusive, the event will still occur during dtEnd(). When serializing to iCalendar DTEND will be dtEnd()+1, because the RFC states that DTEND is exclusive. @see setDtEnd(). */ virtual QDateTime dtEnd() const; /** Returns the date when the event ends. This might be different from dtEnd().date, since the end date/time is non-inclusive. So timed events ending at 0:00 have their end date on the day before. */ Q_REQUIRED_RESULT QDate dateEnd() const; /** Returns whether the event has an end date/time. */ Q_REQUIRED_RESULT bool hasEndDate() const; /** Returns true if the event spans multiple days, otherwise return false. For recurring events, it returns true if the first occurrence spans multiple days, otherwise returns false. Other occurrences might have a different span due to day light savings changes. @param zone If set, looks if the event is multiday for the given zone. If not set, looks if event this multiday for its zone. */ Q_REQUIRED_RESULT bool isMultiDay(const QTimeZone &zone = {}) const; /** @copydoc IncidenceBase::shiftTimes() */ void shiftTimes(const QTimeZone &oldZone, const QTimeZone &newZone) override; /** Sets the event's time transparency level. @param transparency is the event Transparency level. */ void setTransparency(Transparency transparency); /** Returns the event's time transparency level. */ Q_REQUIRED_RESULT Transparency transparency() const; /** Sets the duration of this event. @param duration is the event Duration. */ void setDuration(const Duration &duration) override; /** @copydoc IncidenceBase::setAllDay(). */ void setAllDay(bool allDay) override; /** @copydoc IncidenceBase::dateTime() */ Q_REQUIRED_RESULT QDateTime dateTime(DateTimeRole role) const override; /** @copydoc IncidenceBase::setDateTime() */ void setDateTime(const QDateTime &dateTime, DateTimeRole role) override; /** @copydoc IncidenceBase::mimeType() */ Q_REQUIRED_RESULT QLatin1String mimeType() const override; /** @copydoc Incidence::iconName() */ Q_REQUIRED_RESULT QLatin1String iconName(const QDateTime &recurrenceId = {}) const override; /** @copydoc Incidence::supportsGroupwareCommunication() */ Q_REQUIRED_RESULT bool supportsGroupwareCommunication() const override; /** - Returns the Akonadi specific sub MIME type of a KCalCore::Event. + Returns the Akonadi specific sub MIME type of a KCalendarCore::Event. */ Q_REQUIRED_RESULT static QLatin1String eventMimeType(); protected: /** Compares two events for equality. @param event is the event to compare. */ bool equals(const IncidenceBase &event) const override; /** @copydoc IncidenceBase::assign() */ IncidenceBase &assign(const IncidenceBase &other) override; /** @copydoc IncidenceBase::virtual_hook() */ void virtual_hook(VirtualHook id, void *data) override; private: /** @copydoc IncidenceBase::accept() */ bool accept(Visitor &v, const IncidenceBase::Ptr &incidence) override; /** Disabled, otherwise could be dangerous if you subclass Event. Use IncidenceBase::operator= which is safe because it calls virtual function assign(). @param other is another Event object to assign to this one. */ Event &operator=(const Event &other); // For polymorfic serialization void serialize(QDataStream &out) const override; void deserialize(QDataStream &in) override; //@cond PRIVATE class Private; Private *const d; //@endcond }; -} // namespace KCalCore +} // namespace KCalendarCore //@cond PRIVATE -Q_DECLARE_TYPEINFO(KCalCore::Event::Ptr, Q_MOVABLE_TYPE); -Q_DECLARE_METATYPE(KCalCore::Event::Ptr) -Q_DECLARE_METATYPE(KCalCore::Event *) +Q_DECLARE_TYPEINFO(KCalendarCore::Event::Ptr, Q_MOVABLE_TYPE); +Q_DECLARE_METATYPE(KCalendarCore::Event::Ptr) +Q_DECLARE_METATYPE(KCalendarCore::Event *) //@endcond #endif diff --git a/src/exceptions.cpp b/src/exceptions.cpp index b9c4bde0e..c570fd2c1 100644 --- a/src/exceptions.cpp +++ b/src/exceptions.cpp @@ -1,76 +1,76 @@ /* This file is part of the kcalcore library. Copyright (c) 2001 Cornelius Schumacher This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the Exception class. We don't use actual C++ exceptions right now. These classes are currently returned by an error function; but we can build upon them, if/when we start to use C++ exceptions. @brief Exception base class. @author Cornelius Schumacher \ */ #include "exceptions.h" #include "calformat.h" -using namespace KCalCore; +using namespace KCalendarCore; -namespace KCalCore { +namespace KCalendarCore { class ExceptionPrivate { public: /** The current exception code. */ Exception::ErrorCode mCode; /** Arguments to pass to i18n(). */ QStringList mArguments; }; } Exception::Exception(const ErrorCode code, const QStringList &arguments) : d(new ExceptionPrivate) { d->mCode = code; d->mArguments = arguments; } Exception::~Exception() { } Exception::ErrorCode Exception::code() const { return d->mCode; } QStringList Exception::arguments() const { return d->mArguments; } diff --git a/src/exceptions.h b/src/exceptions.h index a7420ce34..e37b841e2 100644 --- a/src/exceptions.h +++ b/src/exceptions.h @@ -1,119 +1,119 @@ /* This file is part of the kcalcore library. Copyright (c) 2001-2003 Cornelius Schumacher This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the Exception class. We don't use actual C++ exceptions right now. These classes are currently returned by an error function; but we can build upon them, if/when we start to use C++ exceptions. @brief Exception base class. @author Cornelius Schumacher \ */ #ifndef KCALCORE_EXCEPTIONS_H #define KCALCORE_EXCEPTIONS_H #include "kcalcore_export.h" #include #include #include -namespace KCalCore +namespace KCalendarCore { class ExceptionPrivate; /** Exception base class, currently used as a fancy kind of error code and not as an C++ exception. */ class KCALCORE_EXPORT Exception { public: /** The different types of error codes */ enum ErrorCode { LoadError, /**< Load error */ SaveError, /**< Save error */ ParseErrorIcal, /**< Parse error in libical */ ParseErrorKcal, /**< Parse error in libkcal */ NoCalendar, /**< No calendar component found */ CalVersion1, /**< vCalendar v1.0 detected */ CalVersion2, /**< iCalendar v2.0 detected */ CalVersionUnknown, /**< Unknown calendar format detected */ Restriction, /**< Restriction violation */ UserCancel, /**< User canceled the operation */ NoWritableFound, /**< No writable resource is available */ SaveErrorOpenFile, SaveErrorSaveFile, LibICalError, VersionPropertyMissing, ExpectedCalVersion2, ExpectedCalVersion2Unknown, ParseErrorNotIncidence, ParseErrorEmptyMessage, ParseErrorUnableToParse, ParseErrorMethodProperty }; /** Construct an exception. @param code is the error code. @param arguments is a list of arguments that can be passed to an i18n engine to help build a descriptive message for the user, a common argument is for example the filename where the error occurred. */ explicit Exception(const ErrorCode code, const QStringList &arguments = QStringList()); /** Destructor. */ virtual ~Exception(); /** Returns the error code. @return The ErrorCode for this exception. */ Q_REQUIRED_RESULT virtual ErrorCode code() const; /** Returns the arguments. @return A QStringList with the argument list for this exception. */ Q_REQUIRED_RESULT virtual QStringList arguments() const; private: std::unique_ptr d; }; } // namespace #endif diff --git a/src/filestorage.cpp b/src/filestorage.cpp index 63236e729..b4c9f8751 100644 --- a/src/filestorage.cpp +++ b/src/filestorage.cpp @@ -1,183 +1,183 @@ /* This file is part of the kcalcore library. Copyright (c) 2002 Cornelius Schumacher This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the FileStorage class. @brief This class provides a calendar storage as a local file. @author Cornelius Schumacher \ */ #include "filestorage.h" #include "exceptions.h" #include "icalformat.h" #include "memorycalendar.h" #include "vcalformat.h" #include "kcalcore_debug.h" -using namespace KCalCore; +using namespace KCalendarCore; /* Private class that helps to provide binary compatibility between releases. */ //@cond PRIVATE -class Q_DECL_HIDDEN KCalCore::FileStorage::Private +class Q_DECL_HIDDEN KCalendarCore::FileStorage::Private { public: Private(const QString &fileName, CalFormat *format) : mFileName(fileName), mSaveFormat(format) {} ~Private() { delete mSaveFormat; } QString mFileName; CalFormat *mSaveFormat = nullptr; }; //@endcond FileStorage::FileStorage(const Calendar::Ptr &cal, const QString &fileName, CalFormat *format) : CalStorage(cal), d(new Private(fileName, format)) { } FileStorage::~FileStorage() { delete d; } void FileStorage::setFileName(const QString &fileName) { d->mFileName = fileName; } QString FileStorage::fileName() const { return d->mFileName; } void FileStorage::setSaveFormat(CalFormat *format) { delete d->mSaveFormat; d->mSaveFormat = format; } CalFormat *FileStorage::saveFormat() const { return d->mSaveFormat; } bool FileStorage::open() { return true; } bool FileStorage::load() { if (d->mFileName.isEmpty()) { qCWarning(KCALCORE_LOG) << "Empty filename while trying to load"; return false; } // Always try to load with iCalendar. It will detect, if it is actually a // vCalendar file. bool success; QString productId; // First try the supplied format. Otherwise fall through to iCalendar, then // to vCalendar success = saveFormat() && saveFormat()->load(calendar(), d->mFileName); if (success) { productId = saveFormat()->loadedProductId(); } else { ICalFormat iCal; success = iCal.load(calendar(), d->mFileName); if (success) { productId = iCal.loadedProductId(); } else { if (iCal.exception()) { if (iCal.exception()->code() == Exception::CalVersion1) { // Expected non vCalendar file, but detected vCalendar qCDebug(KCALCORE_LOG) << "Fallback to VCalFormat"; VCalFormat vCal; success = vCal.load(calendar(), d->mFileName); productId = vCal.loadedProductId(); if (!success) { if (vCal.exception()) { qCWarning(KCALCORE_LOG) << "Exception while importing:" << vCal.exception()->code(); } return false; } } else { return false; } } else { qCWarning(KCALCORE_LOG) << "There should be an exception set."; return false; } } } calendar()->setProductId(productId); calendar()->setModified(false); return true; } bool FileStorage::save() { if (d->mFileName.isEmpty()) { return false; } CalFormat *format = d->mSaveFormat ? d->mSaveFormat : new ICalFormat; bool success = format->save(calendar(), d->mFileName); if (success) { calendar()->setModified(false); } else { if (!format->exception()) { qCDebug(KCALCORE_LOG) << "Error. There should be an expection set."; } else { qCDebug(KCALCORE_LOG) << int(format->exception()->code()); } } if (!d->mSaveFormat) { delete format; } return success; } bool FileStorage::close() { return true; } diff --git a/src/filestorage.h b/src/filestorage.h index be436bae4..3ee7af258 100644 --- a/src/filestorage.h +++ b/src/filestorage.h @@ -1,136 +1,136 @@ /* This file is part of the kcalcore library. Copyright (c) 2002,2003 Cornelius Schumacher This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the FileStorage class. @author Cornelius Schumacher \ */ #ifndef KCALCORE_FILESTORAGE_H #define KCALCORE_FILESTORAGE_H #include "kcalcore_export.h" #include "calstorage.h" -namespace KCalCore +namespace KCalendarCore { class CalFormat; class Calendar; /** @brief This class provides a calendar storage as a local file. */ class KCALCORE_EXPORT FileStorage : public CalStorage { Q_OBJECT public: /** A shared pointer to a FileStorage. */ typedef QSharedPointer Ptr; /** Constructs a new FileStorage object for Calendar @p calendar with format @p format, and storage to file @p fileName. @param calendar is a pointer to a valid Calendar object. @param fileName is the name of the disk file containing the calendar data. @param format is a pointer to a valid CalFormat object that specifies the calendar format to be used. FileStorage takes ownership; i.e., the memory for @p format is deleted by this destructor. If no format is specified, then iCalendar format is assumed. */ explicit FileStorage(const Calendar::Ptr &calendar, const QString &fileName = QString(), - KCalCore::CalFormat *format = nullptr); + KCalendarCore::CalFormat *format = nullptr); /** Destructor. */ ~FileStorage() override; /** Sets the name of the file that contains the calendar data. @param fileName is the name of the disk file containing the calendar data. @see fileName(). */ void setFileName(const QString &fileName); /** Returns the calendar file name. @return A QString with the name of the calendar file for this storge. @see setFileName(). */ Q_REQUIRED_RESULT QString fileName() const; /** Sets the CalFormat object to use for this storage. @param format is a pointer to a valid CalFormat object that specifies the calendar format to be used. FileStorage takes ownership. @see saveFormat(). */ - void setSaveFormat(KCalCore::CalFormat *format); + void setSaveFormat(KCalendarCore::CalFormat *format); /** Returns the CalFormat object used by this storage. @return A pointer to the CalFormat object used by this storage. @see setSaveFormat(). */ CalFormat *saveFormat() const; /** @copydoc CalStorage::open() */ Q_REQUIRED_RESULT bool open() override; /** @copydoc CalStorage::load() */ Q_REQUIRED_RESULT bool load() override; /** @copydoc CalStorage::save() */ Q_REQUIRED_RESULT bool save() override; /** @copydoc CalStorage::close() */ Q_REQUIRED_RESULT bool close() override; private: //@cond PRIVATE Q_DISABLE_COPY(FileStorage) class Private; Private *const d; //@endcond }; } #endif diff --git a/src/freebusy.cpp b/src/freebusy.cpp index 6ffd7b6ab..7ec6d7861 100644 --- a/src/freebusy.cpp +++ b/src/freebusy.cpp @@ -1,433 +1,433 @@ /* This file is part of the kcalcore library. Copyright (c) 2001 Cornelius Schumacher Copyright (C) 2004 Reinhold Kainhofer This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the FreeBusy class. @brief Provides information about the free/busy time of a calendar user. @author Cornelius Schumacher \ @author Reinhold Kainhofer \ */ #include "freebusy.h" #include "visitor.h" #include "utils.h" #include "icalformat.h" #include "kcalcore_debug.h" #include -using namespace KCalCore; +using namespace KCalendarCore; //@cond PRIVATE -class Q_DECL_HIDDEN KCalCore::FreeBusy::Private +class Q_DECL_HIDDEN KCalendarCore::FreeBusy::Private { private: FreeBusy *q; public: Private(FreeBusy *qq) : q(qq) {} - Private(const KCalCore::FreeBusy::Private &other, FreeBusy *qq) : q(qq) + Private(const KCalendarCore::FreeBusy::Private &other, FreeBusy *qq) : q(qq) { init(other); } Private(const FreeBusyPeriod::List &busyPeriods, FreeBusy *qq) : q(qq), mBusyPeriods(busyPeriods) {} - void init(const KCalCore::FreeBusy::Private &other); + void init(const KCalendarCore::FreeBusy::Private &other); void init(const Event::List &events, const QDateTime &start, const QDateTime &end); QDateTime mDtEnd; // end datetime FreeBusyPeriod::List mBusyPeriods; // list of periods // This is used for creating a freebusy object for the current user bool addLocalPeriod(FreeBusy *fb, const QDateTime &start, const QDateTime &end); }; -void KCalCore::FreeBusy::Private::init(const KCalCore::FreeBusy::Private &other) +void KCalendarCore::FreeBusy::Private::init(const KCalendarCore::FreeBusy::Private &other) { mDtEnd = other.mDtEnd; mBusyPeriods = other.mBusyPeriods; } //@endcond FreeBusy::FreeBusy() - : d(new KCalCore::FreeBusy::Private(this)) + : d(new KCalendarCore::FreeBusy::Private(this)) { } FreeBusy::FreeBusy(const FreeBusy &other) : IncidenceBase(other), - d(new KCalCore::FreeBusy::Private(*other.d, this)) + d(new KCalendarCore::FreeBusy::Private(*other.d, this)) { } FreeBusy::FreeBusy(const QDateTime &start, const QDateTime &end) - : d(new KCalCore::FreeBusy::Private(this)) + : d(new KCalendarCore::FreeBusy::Private(this)) { setDtStart(start); //NOLINT false clang-analyzer-optin.cplusplus.VirtualCall setDtEnd(end); //NOLINT false clang-analyzer-optin.cplusplus.VirtualCall } FreeBusy::FreeBusy(const Event::List &events, const QDateTime &start, const QDateTime &end) - : d(new KCalCore::FreeBusy::Private(this)) + : d(new KCalendarCore::FreeBusy::Private(this)) { setDtStart(start); //NOLINT false clang-analyzer-optin.cplusplus.VirtualCall setDtEnd(end); //NOLINT false clang-analyzer-optin.cplusplus.VirtualCall d->init(events, start, end); } //@cond PRIVATE void FreeBusy::Private::init(const Event::List &eventList, const QDateTime &start, const QDateTime &end) { qint64 extraDays; int i; int x; qint64 duration = start.daysTo(end); QDate day; QDateTime tmpStart; QDateTime tmpEnd; // Loops through every event in the calendar Event::List::ConstIterator it; for (it = eventList.constBegin(); it != eventList.constEnd(); ++it) { Event::Ptr event = *it; // If this event is transparent it shouldn't be in the freebusy list. if (event->transparency() == Event::Transparent) { continue; } // The code below can not handle all-day events. Fixing this resulted // in a lot of duplicated code. Instead, make a copy of the event and // set the period to the full day(s). This trick works for recurring, // multiday, and single day all-day events. Event::Ptr allDayEvent; if (event->allDay()) { // addDay event. Do the hack qCDebug(KCALCORE_LOG) << "All-day event"; allDayEvent = Event::Ptr(new Event(*event)); // Set the start and end times to be on midnight QDateTime st = allDayEvent->dtStart(); st.setTime(QTime(0, 0)); QDateTime nd = allDayEvent->dtEnd(); nd.setTime(QTime(23, 59, 59, 999)); allDayEvent->setAllDay(false); allDayEvent->setDtStart(st); allDayEvent->setDtEnd(nd); qCDebug(KCALCORE_LOG) << "Use:" << st.toString() << "to" << nd.toString(); // Finally, use this event for the setting below event = allDayEvent; } // This whole for loop is for recurring events, it loops through // each of the days of the freebusy request for (i = 0; i <= duration; ++i) { day = start.addDays(i).date(); tmpStart.setDate(day); tmpEnd.setDate(day); if (event->recurs()) { if (event->isMultiDay()) { // FIXME: This doesn't work for sub-daily recurrences or recurrences with // a different time than the original event. extraDays = event->dtStart().daysTo(event->dtEnd()); for (x = 0; x <= extraDays; ++x) { if (event->recursOn(day.addDays(-x), start.timeZone())) { tmpStart.setDate(day.addDays(-x)); tmpStart.setTime(event->dtStart().time()); tmpEnd = event->duration().end(tmpStart); addLocalPeriod(q, tmpStart, tmpEnd); break; } } } else { if (event->recursOn(day, start.timeZone())) { tmpStart.setTime(event->dtStart().time()); tmpEnd.setTime(event->dtEnd().time()); addLocalPeriod(q, tmpStart, tmpEnd); } } } } // Non-recurring events addLocalPeriod(q, event->dtStart(), event->dtEnd()); } q->sortList(); } //@endcond FreeBusy::FreeBusy(const Period::List &busyPeriods) - : d(new KCalCore::FreeBusy::Private(this)) + : d(new KCalendarCore::FreeBusy::Private(this)) { addPeriods(busyPeriods); } FreeBusy::FreeBusy(const FreeBusyPeriod::List &busyPeriods) - : d(new KCalCore::FreeBusy::Private(busyPeriods, this)) + : d(new KCalendarCore::FreeBusy::Private(busyPeriods, this)) { } FreeBusy::~FreeBusy() { delete d; } IncidenceBase::IncidenceType FreeBusy::type() const { return TypeFreeBusy; } QByteArray FreeBusy::typeStr() const { return QByteArrayLiteral("FreeBusy"); } void FreeBusy::setDtStart(const QDateTime &start) { IncidenceBase::setDtStart(start.toUTC()); updated(); } void FreeBusy::setDtEnd(const QDateTime &end) { d->mDtEnd = end; } QDateTime FreeBusy::dtEnd() const { return d->mDtEnd; } Period::List FreeBusy::busyPeriods() const { Period::List res; res.reserve(d->mBusyPeriods.count()); for (const FreeBusyPeriod &p : qAsConst(d->mBusyPeriods)) { res << p; } return res; } FreeBusyPeriod::List FreeBusy::fullBusyPeriods() const { return d->mBusyPeriods; } void FreeBusy::sortList() { std::sort(d->mBusyPeriods.begin(), d->mBusyPeriods.end()); } void FreeBusy::addPeriods(const Period::List &list) { d->mBusyPeriods.reserve(d->mBusyPeriods.count() + list.count()); for (const Period &p : qAsConst(list)) { d->mBusyPeriods << FreeBusyPeriod(p); } sortList(); } void FreeBusy::addPeriods(const FreeBusyPeriod::List &list) { d->mBusyPeriods += list; sortList(); } void FreeBusy::addPeriod(const QDateTime &start, const QDateTime &end) { d->mBusyPeriods.append(FreeBusyPeriod(start, end)); sortList(); } void FreeBusy::addPeriod(const QDateTime &start, const Duration &duration) { d->mBusyPeriods.append(FreeBusyPeriod(start, duration)); sortList(); } void FreeBusy::merge(const FreeBusy::Ptr &freeBusy) { if (freeBusy->dtStart() < dtStart()) { setDtStart(freeBusy->dtStart()); } if (freeBusy->dtEnd() > dtEnd()) { setDtEnd(freeBusy->dtEnd()); } Period::List periods = freeBusy->busyPeriods(); Period::List::ConstIterator it; d->mBusyPeriods.reserve(d->mBusyPeriods.count() + periods.count()); for (it = periods.constBegin(); it != periods.constEnd(); ++it) { d->mBusyPeriods.append(FreeBusyPeriod((*it).start(), (*it).end())); } sortList(); } void FreeBusy::shiftTimes(const QTimeZone &oldZone, const QTimeZone &newZone) { if (oldZone.isValid() && newZone.isValid() && oldZone != newZone) { IncidenceBase::shiftTimes(oldZone, newZone); d->mDtEnd = d->mDtEnd.toTimeZone(oldZone); d->mDtEnd.setTimeZone(newZone); for (FreeBusyPeriod p : qAsConst(d->mBusyPeriods)) { p.shiftTimes(oldZone, newZone); } } } IncidenceBase &FreeBusy::assign(const IncidenceBase &other) { if (&other != this) { IncidenceBase::assign(other); const FreeBusy *f = static_cast(&other); d->init(*(f->d)); } return *this; } bool FreeBusy::equals(const IncidenceBase &freeBusy) const { if (!IncidenceBase::equals(freeBusy)) { return false; } else { // If they weren't the same type IncidenceBase::equals would had returned false already const FreeBusy *fb = static_cast(&freeBusy); return dtEnd() == fb->dtEnd() && d->mBusyPeriods == fb->d->mBusyPeriods; } } bool FreeBusy::accept(Visitor &v, const IncidenceBase::Ptr &incidence) { return v.visit(incidence.staticCast()); } QDateTime FreeBusy::dateTime(DateTimeRole role) const { Q_UNUSED(role); // No roles affecting freeBusy yet return QDateTime(); } void FreeBusy::setDateTime(const QDateTime &dateTime, DateTimeRole role) { Q_UNUSED(dateTime); Q_UNUSED(role); } void FreeBusy::virtual_hook(VirtualHook id, void *data) { Q_UNUSED(id); Q_UNUSED(data); Q_ASSERT(false); } //@cond PRIVATE bool FreeBusy::Private::addLocalPeriod(FreeBusy *fb, const QDateTime &eventStart, const QDateTime &eventEnd) { QDateTime tmpStart; QDateTime tmpEnd; //Check to see if the start *or* end of the event is //between the start and end of the freebusy dates. QDateTime start = fb->dtStart(); if (!(((start.secsTo(eventStart) >= 0) && (eventStart.secsTo(mDtEnd) >= 0)) || ((start.secsTo(eventEnd) >= 0) && (eventEnd.secsTo(mDtEnd) >= 0)))) { return false; } if (eventStart.secsTo(start) >= 0) { tmpStart = start; } else { tmpStart = eventStart; } if (eventEnd.secsTo(mDtEnd) <= 0) { tmpEnd = mDtEnd; } else { tmpEnd = eventEnd; } FreeBusyPeriod p(tmpStart, tmpEnd); mBusyPeriods.append(p); return true; } //@endcond QLatin1String FreeBusy::mimeType() const { return FreeBusy::freeBusyMimeType(); } -QLatin1String KCalCore::FreeBusy::freeBusyMimeType() +QLatin1String KCalendarCore::FreeBusy::freeBusyMimeType() { return QLatin1String("application/x-vnd.akonadi.calendar.freebusy"); } -QDataStream &KCalCore::operator<<(QDataStream &stream, const KCalCore::FreeBusy::Ptr &freebusy) +QDataStream &KCalendarCore::operator<<(QDataStream &stream, const KCalendarCore::FreeBusy::Ptr &freebusy) { - KCalCore::ICalFormat format; + KCalendarCore::ICalFormat format; QString data = format.createScheduleMessage(freebusy, iTIPPublish); return stream << data; } -QDataStream &KCalCore::operator>>(QDataStream &stream, KCalCore::FreeBusy::Ptr &freebusy) +QDataStream &KCalendarCore::operator>>(QDataStream &stream, KCalendarCore::FreeBusy::Ptr &freebusy) { QString freeBusyVCal; stream >> freeBusyVCal; - KCalCore::ICalFormat format; + KCalendarCore::ICalFormat format; freebusy = format.parseFreeBusy(freeBusyVCal); if (!freebusy) { qCDebug(KCALCORE_LOG) << "Error parsing free/busy"; qCDebug(KCALCORE_LOG) << freeBusyVCal; } return stream; } diff --git a/src/freebusy.h b/src/freebusy.h index 9f0140f54..f2b9f4af9 100644 --- a/src/freebusy.h +++ b/src/freebusy.h @@ -1,296 +1,296 @@ /* This file is part of the kcalcore library. Copyright (c) 2001-2003 Cornelius Schumacher Copyright (C) 2004 Reinhold Kainhofer This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the FreeBusy class. @author Cornelius Schumacher \ @author Reinhold Kainhofer \ */ #ifndef KCALCORE_FREEBUSY_H #define KCALCORE_FREEBUSY_H #include "kcalcore_export.h" #include "event.h" #include "freebusyperiod.h" #include "incidencebase.h" #include "period.h" #include -namespace KCalCore +namespace KCalendarCore { class FreeBusy; /** @brief Provides information about the free/busy time of a calendar. A free/busy is a collection of Periods (@see Period). */ class KCALCORE_EXPORT FreeBusy : public IncidenceBase { friend KCALCORE_EXPORT QDataStream &operator<<(QDataStream &s, - const KCalCore::FreeBusy::Ptr &freebusy); + const KCalendarCore::FreeBusy::Ptr &freebusy); friend KCALCORE_EXPORT QDataStream &operator>>(QDataStream &s, - KCalCore::FreeBusy::Ptr &freebusy); + KCalendarCore::FreeBusy::Ptr &freebusy); public: /** A shared pointer to a FreeBusy object. */ typedef QSharedPointer Ptr; /** List of FreeBusy objects. */ typedef QVector List; /** Constructs an free/busy without any periods. */ FreeBusy(); /** Copy constructor. @param other is the free/busy to copy. */ FreeBusy(const FreeBusy &other); /** Constructs a free/busy from a list of periods. @param busyPeriods is a list of periods. */ explicit FreeBusy(const Period::List &busyPeriods); /** Constructs a free/busy from a list of periods. @param busyPeriods is a list of periods. */ explicit FreeBusy(const FreeBusyPeriod::List &busyPeriods); /** Constructs a free/busy from a single period. @param start is the start date/time of the period. @param end is the end date/time of the period. */ FreeBusy(const QDateTime &start, const QDateTime &end); /** Constructs a freebusy for a specified list of events given a single period. @param events list of events. @param start is the start date/time of the period. @param end is the end date/time of the period. */ FreeBusy(const Event::List &events, const QDateTime &start, const QDateTime &end); /** Destroys a free/busy. */ ~FreeBusy() override; /** @copydoc IncidenceBase::type() */ Q_REQUIRED_RESULT IncidenceType type() const override; /** @copydoc IncidenceBase::typeStr() */ Q_REQUIRED_RESULT QByteArray typeStr() const override; /** Sets the start date/time for the free/busy. Note that this date/time may be later or earlier than all periods within the free/busy. @param start is a QDateTime specifying an start datetime. @see IncidenceBase::dtStart(), setDtEnd(). */ void setDtStart(const QDateTime &start) override; /** Sets the end datetime for the free/busy. Note that this datetime may be later or earlier than all periods within the free/busy. @param end is a QDateTime specifying an end datetime. @see dtEnd(), setDtStart(). */ void setDtEnd(const QDateTime &end); /** Returns the end datetime for the free/busy. FIXME: calling addPeriod() does not change mDtEnd. Is that incorrect? @see setDtEnd(). */ Q_REQUIRED_RESULT virtual QDateTime dtEnd() const; /** @copydoc IncidenceBase::shiftTimes() */ void shiftTimes(const QTimeZone &oldZone, const QTimeZone &newZone) override; /** Returns the list of all periods within the free/busy. */ Q_REQUIRED_RESULT Period::List busyPeriods() const; /** Returns the list of all periods within the free/busy. */ Q_REQUIRED_RESULT FreeBusyPeriod::List fullBusyPeriods() const; /** Adds a period to the freebusy list and sorts the list. @param start is the start datetime of the period. @param end is the end datetime of the period. */ void addPeriod(const QDateTime &start, const QDateTime &end); /** Adds a period to the freebusy list and sorts the list. @param start is the start datetime of the period. @param duration is the Duration of the period. */ void addPeriod(const QDateTime &start, const Duration &duration); /** Adds a list of periods to the freebusy object and then sorts that list. Use this if you are adding many items, instead of the addPeriod method, to avoid sorting repeatedly. @param list is a list of Period objects. */ void addPeriods(const Period::List &list); /** Adds a list of periods to the freebusy object and then sorts that list. Use this if you are adding many items, instead of the addPeriod method, to avoid sorting repeatedly. @param list is a list of FreeBusyPeriod objects. */ void addPeriods(const FreeBusyPeriod::List &list); /** Sorts the list of free/busy periods into ascending order. */ void sortList(); /** Merges another free/busy into this free/busy. @param freebusy is a pointer to a valid FreeBusy object. */ void merge(const FreeBusy::Ptr &freebusy); /** @copydoc IncidenceBase::dateTime() */ Q_REQUIRED_RESULT QDateTime dateTime(DateTimeRole role) const override; /** @copydoc IncidenceBase::setDateTime() */ void setDateTime(const QDateTime &dateTime, DateTimeRole role) override; /** @copydoc IncidenceBase::mimeType() */ Q_REQUIRED_RESULT QLatin1String mimeType() const override; /** - Returns the Akonadi specific sub MIME type of a KCalCore::FreeBusy. + Returns the Akonadi specific sub MIME type of a KCalendarCore::FreeBusy. */ Q_REQUIRED_RESULT static QLatin1String freeBusyMimeType(); protected: /** Compare this with @p freebusy for equality. @param freebusy is the FreeBusy to compare. */ bool equals(const IncidenceBase &freebusy) const override; /** @copydoc IncidenceBase::assign() */ IncidenceBase &assign(const IncidenceBase &other) override; /** @copydoc IncidenceBase::virtual_hook() */ void virtual_hook(VirtualHook id, void *data) override; private: /** @copydoc IncidenceBase::accept() */ bool accept(Visitor &v, const IncidenceBase::Ptr &incidence) override; /** Disabled, otherwise could be dangerous if you subclass FreeBusy. Use IncidenceBase::operator= which is safe because it calls virtual function assign(). @param other is another FreeBusy object to assign to this one. */ FreeBusy &operator=(const FreeBusy &other); //@cond PRIVATE class Private; Private *const d; //@endcond }; /** Serializes the @p freebusy object into the @p stream. */ KCALCORE_EXPORT QDataStream &operator<<(QDataStream &stream, - const KCalCore::FreeBusy::Ptr &freebusy); + const KCalendarCore::FreeBusy::Ptr &freebusy); /** Initializes the @p freebusy object from the @p stream. */ KCALCORE_EXPORT QDataStream &operator>>(QDataStream &stream, - KCalCore::FreeBusy::Ptr &freebusy); + KCalendarCore::FreeBusy::Ptr &freebusy); } //@cond PRIVATE -Q_DECLARE_TYPEINFO(KCalCore::FreeBusy::Ptr, Q_MOVABLE_TYPE); -Q_DECLARE_METATYPE(KCalCore::FreeBusy::Ptr) +Q_DECLARE_TYPEINFO(KCalendarCore::FreeBusy::Ptr, Q_MOVABLE_TYPE); +Q_DECLARE_METATYPE(KCalendarCore::FreeBusy::Ptr) //@endcond #endif diff --git a/src/freebusycache.cpp b/src/freebusycache.cpp index d1668bbd7..690ae6d6c 100644 --- a/src/freebusycache.cpp +++ b/src/freebusycache.cpp @@ -1,42 +1,42 @@ /* This file is part of the kcalcore library. Copyright (c) 2004 Cornelius Schumacher This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the FreeBusyCache abstract base class. @author Cornelius Schumacher \ */ #include "freebusycache.h" -using namespace KCalCore; +using namespace KCalendarCore; FreeBusyCache::~FreeBusyCache() { } void FreeBusyCache::virtual_hook(int id, void *data) { Q_UNUSED(id); Q_UNUSED(data); Q_ASSERT(false); } diff --git a/src/freebusycache.h b/src/freebusycache.h index b5c77f9d0..b25499664 100644 --- a/src/freebusycache.h +++ b/src/freebusycache.h @@ -1,84 +1,84 @@ /* This file is part of the kcalcore library. Copyright (c) 2004 Cornelius Schumacher This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the FreeBusyCache abstract base class. @author Cornelius Schumacher \ */ #ifndef KCALCORE_FREEBUSYCACHE_H #define KCALCORE_FREEBUSYCACHE_H #include "kcalcore_export.h" #include "freebusy.h" class QString; -namespace KCalCore +namespace KCalendarCore { class Person; /** @brief An abstract base class to allow different implementations of storing free busy information, e.g. local storage or storage on a Kolab server. */ class KCALCORE_EXPORT FreeBusyCache { public: /** Destructor. */ virtual ~FreeBusyCache(); /** Save freebusy information belonging to an email. @param freebusy is a pointer to a valid FreeBusy instance. @param person is a valid Person instance. @return true if the save was successful; false otherwise. */ virtual bool saveFreeBusy(const FreeBusy::Ptr &freebusy, const Person &person) = 0; /** Load freebusy information belonging to an email. @param email is a QString containing a email string in the "FirstName LastName " format. @return A pointer to the FreeBusy object loaded for the specified email; returns 0 if there was some problem attempting to load the FreeBusy information. */ virtual FreeBusy::Ptr loadFreeBusy(const QString &email) = 0; protected: /** @copydoc IncidenceBase::virtual_hook() */ virtual void virtual_hook(int id, void *data); }; } #endif diff --git a/src/freebusyperiod.cpp b/src/freebusyperiod.cpp index 7819cc6f7..473abc1e1 100644 --- a/src/freebusyperiod.cpp +++ b/src/freebusyperiod.cpp @@ -1,144 +1,144 @@ /* This file is part of the kcalcore library. Copyright (c) 2001 Cornelius Schumacher Copyright (c) 2007 David Jarvie This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the FreeBusyPeriod class. @brief Represents a period of time. @author Cornelius Schumacher \ */ #include "freebusyperiod.h" -using namespace KCalCore; +using namespace KCalendarCore; //@cond PRIVATE -class Q_DECL_HIDDEN KCalCore::FreeBusyPeriod::Private +class Q_DECL_HIDDEN KCalendarCore::FreeBusyPeriod::Private { public: Private(): mType(Unknown) {} QString mSummary; QString mLocation; FreeBusyType mType; }; //@endcond -FreeBusyPeriod::FreeBusyPeriod() : Period(), d(new KCalCore::FreeBusyPeriod::Private()) +FreeBusyPeriod::FreeBusyPeriod() : Period(), d(new KCalendarCore::FreeBusyPeriod::Private()) { } FreeBusyPeriod::FreeBusyPeriod(const QDateTime &start, const QDateTime &end) - : Period(start, end), d(new KCalCore::FreeBusyPeriod::Private()) + : Period(start, end), d(new KCalendarCore::FreeBusyPeriod::Private()) { } FreeBusyPeriod::FreeBusyPeriod(const QDateTime &start, const Duration &duration) - : Period(start, duration), d(new KCalCore::FreeBusyPeriod::Private()) + : Period(start, duration), d(new KCalendarCore::FreeBusyPeriod::Private()) { } FreeBusyPeriod::FreeBusyPeriod(const FreeBusyPeriod &period) - : Period(period), d(new KCalCore::FreeBusyPeriod::Private(*period.d)) + : Period(period), d(new KCalendarCore::FreeBusyPeriod::Private(*period.d)) { } FreeBusyPeriod::FreeBusyPeriod(const Period &period) - : Period(period), d(new KCalCore::FreeBusyPeriod::Private()) + : Period(period), d(new KCalendarCore::FreeBusyPeriod::Private()) { } FreeBusyPeriod::~FreeBusyPeriod() { delete d; } FreeBusyPeriod &FreeBusyPeriod::operator=(const FreeBusyPeriod &other) { // check for self assignment if (&other == this) { return *this; } Period::operator=(other); *d = *other.d; return *this; } QString FreeBusyPeriod::summary() const { return d->mSummary; } void FreeBusyPeriod::setSummary(const QString &summary) { d->mSummary = summary; } QString FreeBusyPeriod::location() const { return d->mLocation; } void FreeBusyPeriod::setLocation(const QString &location) { d->mLocation = location; } FreeBusyPeriod::FreeBusyType FreeBusyPeriod::type() const { return d->mType; } void FreeBusyPeriod::setType(FreeBusyPeriod::FreeBusyType type) { d->mType = type; } -QDataStream &KCalCore::operator<<(QDataStream &stream, const KCalCore::FreeBusyPeriod &period) +QDataStream &KCalendarCore::operator<<(QDataStream &stream, const KCalendarCore::FreeBusyPeriod &period) { - KCalCore::Period periodParent = static_cast(period); + KCalendarCore::Period periodParent = static_cast(period); stream << periodParent; stream << period.summary() << period.location() << static_cast(period.type()); return stream; } -QDataStream &KCalCore::operator>>(QDataStream &stream, FreeBusyPeriod &period) +QDataStream &KCalendarCore::operator>>(QDataStream &stream, FreeBusyPeriod &period) { - KCalCore::Period periodParent; + KCalendarCore::Period periodParent; QString summary, location; int type; stream >> periodParent >> summary >> location >> type; period = periodParent; period.setLocation(location); period.setSummary(summary); period.setType(static_cast(type)); return stream; } diff --git a/src/freebusyperiod.h b/src/freebusyperiod.h index 72333c785..7daf99cc2 100644 --- a/src/freebusyperiod.h +++ b/src/freebusyperiod.h @@ -1,177 +1,177 @@ /* This file is part of the kcalcore library. Copyright (c) 2001-2003 Cornelius Schumacher This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the Period class. @brief Represents a period of time. @author Cornelius Schumacher \ */ #ifndef KCALCORE_FREEBUSYPERIOD_H #define KCALCORE_FREEBUSYPERIOD_H #include "kcalcore_export.h" #include "period.h" #include -namespace KCalCore +namespace KCalendarCore { /** The period can be defined by either a start time and an end time or by a start time and a duration. */ class KCALCORE_EXPORT FreeBusyPeriod : public Period { public: enum FreeBusyType { Free, Busy, BusyUnavailable, BusyTentative, Unknown }; /** List of periods. */ typedef QVector List; /** Constructs a period without a duration. */ FreeBusyPeriod(); /** Constructs a period from @p start to @p end. @param start the time the period begins. @param end the time the period ends. */ FreeBusyPeriod(const QDateTime &start, const QDateTime &end); /** Constructs a period from @p start and lasting @p duration. @param start the time when the period starts. @param duration how long the period lasts. */ FreeBusyPeriod(const QDateTime &start, const Duration &duration); /** Constructs a period by copying another period object @param period the period to copy */ FreeBusyPeriod(const FreeBusyPeriod &period); /** Constructs a period by copying another period object @param period the period to copy */ FreeBusyPeriod(const Period &period); //krazy:exclude=explicit /** Destroys a period. */ ~FreeBusyPeriod(); /** Sets this period equal to the @p other one. @param other is the other period to compare. */ FreeBusyPeriod &operator=(const FreeBusyPeriod &other); /** Sets the period summary. @param summary is the period summary string. @see summary(). */ void setSummary(const QString &summary); /** Returns the period summary. @see setSummary() */ Q_REQUIRED_RESULT QString summary() const; /** Sets the period location. @param location is the period location string. @see location(). */ void setLocation(const QString &location); /** Returns the period location. @see setLocation() */ Q_REQUIRED_RESULT QString location() const; /** Sets the free/busy type. @param type is the type of free/busy period @see type(). @since 5.0 */ void setType(FreeBusyType type); /** Returns free/busy type @see setType(). @since 5.0 */ Q_REQUIRED_RESULT FreeBusyType type() const; private: //@cond PRIVATE class Private; Private *const d; //@endcond friend KCALCORE_EXPORT QDataStream &operator<<(QDataStream &stream, - const KCalCore::FreeBusyPeriod &period); + const KCalendarCore::FreeBusyPeriod &period); friend KCALCORE_EXPORT QDataStream &operator>>(QDataStream &stream, - KCalCore::FreeBusyPeriod &period); + KCalendarCore::FreeBusyPeriod &period); }; /** Write @p period to the datastream @p stream, in binary format. */ KCALCORE_EXPORT QDataStream &operator<<(QDataStream &stream, - const KCalCore::FreeBusyPeriod &period); + const KCalendarCore::FreeBusyPeriod &period); /** Read a Period object into @p period from @p stream, in binary format. */ -KCALCORE_EXPORT QDataStream &operator>>(QDataStream &stream, KCalCore::FreeBusyPeriod &period); +KCALCORE_EXPORT QDataStream &operator>>(QDataStream &stream, KCalendarCore::FreeBusyPeriod &period); } //@cond PRIVATE -Q_DECLARE_METATYPE(KCalCore::FreeBusyPeriod) +Q_DECLARE_METATYPE(KCalendarCore::FreeBusyPeriod) //@endcond #endif diff --git a/src/icalformat.cpp b/src/icalformat.cpp index 4501db50e..83e34c059 100644 --- a/src/icalformat.cpp +++ b/src/icalformat.cpp @@ -1,672 +1,672 @@ /* This file is part of the kcalcore library. Copyright (c) 2001 Cornelius Schumacher This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the ICalFormat class. @brief iCalendar format implementation: a layer of abstraction for libical. @author Cornelius Schumacher \ */ #include "icalformat.h" #include "icalformat_p.h" #include "icaltimezones_p.h" #include "freebusy.h" #include "memorycalendar.h" #include "kcalcore_debug.h" #include "calendar_p.h" #include #include #include extern "C" { #include #include #include #include #include } -using namespace KCalCore; +using namespace KCalendarCore; //@cond PRIVATE -class Q_DECL_HIDDEN KCalCore::ICalFormat::Private +class Q_DECL_HIDDEN KCalendarCore::ICalFormat::Private { public: Private(ICalFormat *parent) : mImpl(new ICalFormatImpl(parent)), mTimeZone(QTimeZone::utc()) {} ~Private() { delete mImpl; } ICalFormatImpl *mImpl = nullptr; QTimeZone mTimeZone; }; //@endcond ICalFormat::ICalFormat() : d(new Private(this)) { } ICalFormat::~ICalFormat() { icalmemory_free_ring(); delete d; } bool ICalFormat::load(const Calendar::Ptr &calendar, const QString &fileName) { qCDebug(KCALCORE_LOG) << fileName; clearException(); QFile file(fileName); if (!file.open(QIODevice::ReadOnly)) { qCritical() << "load error"; setException(new Exception(Exception::LoadError)); return false; } const QByteArray text = file.readAll().trimmed(); file.close(); if (text.isEmpty()) { // empty files are valid return true; } else { return fromRawString(calendar, text, false, fileName); } } bool ICalFormat::save(const Calendar::Ptr &calendar, const QString &fileName) { qCDebug(KCALCORE_LOG) << fileName; clearException(); QString text = toString(calendar); if (text.isEmpty()) { return false; } // Write backup file const QString backupFile = fileName + QLatin1Char('~'); QFile::remove(backupFile); QFile::copy(fileName, backupFile); QSaveFile file(fileName); if (!file.open(QIODevice::WriteOnly)) { qCritical() << "file open error: " << file.errorString() << ";filename=" << fileName; setException(new Exception(Exception::SaveErrorOpenFile, QStringList(fileName))); return false; } // Convert to UTF8 and save QByteArray textUtf8 = text.toUtf8(); file.write(textUtf8.data(), textUtf8.size()); if (!file.commit()) { qCDebug(KCALCORE_LOG) << "file finalize error:" << file.errorString(); setException(new Exception(Exception::SaveErrorSaveFile, QStringList(fileName))); return false; } return true; } bool ICalFormat::fromString(const Calendar::Ptr &cal, const QString &string, bool deleted, const QString ¬ebook) { return fromRawString(cal, string.toUtf8(), deleted, notebook); } Incidence::Ptr ICalFormat::readIncidence(const QByteArray &string) { // Let's defend const correctness until the very gates of hell^Wlibical icalcomponent *calendar = icalcomponent_new_from_string(const_cast(string.constData())); if (!calendar) { qCritical() << "parse error from icalcomponent_new_from_string. string=" << QString::fromLatin1(string); setException(new Exception(Exception::ParseErrorIcal)); return Incidence::Ptr(); } ICalTimeZoneCache tzCache; ICalTimeZoneParser parser(&tzCache); parser.parse(calendar); Incidence::Ptr incidence; if (icalcomponent_isa(calendar) == ICAL_VCALENDAR_COMPONENT) { incidence = d->mImpl->readOneIncidence(calendar, &tzCache); } else if (icalcomponent_isa(calendar) == ICAL_XROOT_COMPONENT) { icalcomponent *comp = icalcomponent_get_first_component(calendar, ICAL_VCALENDAR_COMPONENT); if (comp) { incidence = d->mImpl->readOneIncidence(comp, &tzCache); } } if (!incidence) { qCDebug(KCALCORE_LOG) << "No VCALENDAR component found"; setException(new Exception(Exception::NoCalendar)); } icalcomponent_free(calendar); icalmemory_free_ring(); return incidence; } bool ICalFormat::fromRawString(const Calendar::Ptr &cal, const QByteArray &string, bool deleted, const QString ¬ebook) { Q_UNUSED(notebook); // Get first VCALENDAR component. // TODO: Handle more than one VCALENDAR or non-VCALENDAR top components icalcomponent *calendar; // Let's defend const correctness until the very gates of hell^Wlibical calendar = icalcomponent_new_from_string(const_cast(string.constData())); if (!calendar) { qCritical() << "parse error from icalcomponent_new_from_string. string=" << QString::fromLatin1(string); setException(new Exception(Exception::ParseErrorIcal)); return false; } bool success = true; if (icalcomponent_isa(calendar) == ICAL_XROOT_COMPONENT) { icalcomponent *comp; for (comp = icalcomponent_get_first_component(calendar, ICAL_VCALENDAR_COMPONENT); comp; comp = icalcomponent_get_next_component(calendar, ICAL_VCALENDAR_COMPONENT)) { // put all objects into their proper places if (!d->mImpl->populate(cal, comp, deleted)) { qCritical() << "Could not populate calendar"; if (!exception()) { setException(new Exception(Exception::ParseErrorKcal)); } success = false; } else { setLoadedProductId(d->mImpl->loadedProductId()); } } } else if (icalcomponent_isa(calendar) != ICAL_VCALENDAR_COMPONENT) { qCDebug(KCALCORE_LOG) << "No VCALENDAR component found"; setException(new Exception(Exception::NoCalendar)); success = false; } else { // put all objects into their proper places if (!d->mImpl->populate(cal, calendar, deleted)) { qCDebug(KCALCORE_LOG) << "Could not populate calendar"; if (!exception()) { setException(new Exception(Exception::ParseErrorKcal)); } success = false; } else { setLoadedProductId(d->mImpl->loadedProductId()); } } icalcomponent_free(calendar); icalmemory_free_ring(); return success; } Incidence::Ptr ICalFormat::fromString(const QString &string) { MemoryCalendar::Ptr cal(new MemoryCalendar(d->mTimeZone)); fromString(cal, string); const Incidence::List list = cal->incidences(); return !list.isEmpty() ? list.first() : Incidence::Ptr(); } QString ICalFormat::toString(const Calendar::Ptr &cal, const QString ¬ebook, bool deleted) { icalcomponent *calendar = d->mImpl->createCalendarComponent(cal); icalcomponent *component; QVector tzUsedList; TimeZoneEarliestDate earliestTz; // todos Todo::List todoList = deleted ? cal->deletedTodos() : cal->rawTodos(); for (auto it = todoList.cbegin(), end = todoList.cend(); it != end; ++it) { if (!deleted || !cal->todo((*it)->uid(), (*it)->recurrenceId())) { // use existing ones, or really deleted ones if (notebook.isEmpty() || (!cal->notebook(*it).isEmpty() && notebook.endsWith(cal->notebook(*it)))) { component = d->mImpl->writeTodo(*it, &tzUsedList); icalcomponent_add_component(calendar, component); ICalTimeZoneParser::updateTzEarliestDate((*it), &earliestTz); } } } // events Event::List events = deleted ? cal->deletedEvents() : cal->rawEvents(); for (auto it = events.cbegin(), end = events.cend(); it != end; ++it) { if (!deleted || !cal->event((*it)->uid(), (*it)->recurrenceId())) { // use existing ones, or really deleted ones if (notebook.isEmpty() || (!cal->notebook(*it).isEmpty() && notebook.endsWith(cal->notebook(*it)))) { component = d->mImpl->writeEvent(*it, &tzUsedList); icalcomponent_add_component(calendar, component); ICalTimeZoneParser::updateTzEarliestDate((*it), &earliestTz); } } } // journals Journal::List journals = deleted ? cal->deletedJournals() : cal->rawJournals(); for (auto it = journals.cbegin(), end = journals.cend(); it != end; ++it) { if (!deleted || !cal->journal((*it)->uid(), (*it)->recurrenceId())) { // use existing ones, or really deleted ones if (notebook.isEmpty() || (!cal->notebook(*it).isEmpty() && notebook.endsWith(cal->notebook(*it)))) { component = d->mImpl->writeJournal(*it, &tzUsedList); icalcomponent_add_component(calendar, component); ICalTimeZoneParser::updateTzEarliestDate((*it), &earliestTz); } } } // time zones if (todoList.isEmpty() && events.isEmpty() && journals.isEmpty()) { // no incidences means no used timezones, use all timezones // this will export a calendar having only timezone definitions tzUsedList = cal->d->mTimeZones; } for (const auto &qtz : qAsConst(tzUsedList)) { if (qtz != QTimeZone::utc()) { icaltimezone *tz = ICalTimeZoneParser::icaltimezoneFromQTimeZone(qtz, earliestTz[qtz]); if (!tz) { qCritical() << "bad time zone"; } else { component = icalcomponent_new_clone(icaltimezone_get_component(tz)); icalcomponent_add_component(calendar, component); icaltimezone_free(tz, 1); } } } char *const componentString = icalcomponent_as_ical_string_r(calendar); const QString &text = QString::fromUtf8(componentString); free(componentString); icalcomponent_free(calendar); icalmemory_free_ring(); if (text.isEmpty()) { setException(new Exception(Exception::LibICalError)); } return text; } QString ICalFormat::toICalString(const Incidence::Ptr &incidence) { MemoryCalendar::Ptr cal(new MemoryCalendar(d->mTimeZone)); cal->addIncidence(Incidence::Ptr(incidence->clone())); return toString(cal.staticCast()); } QString ICalFormat::toString(const Incidence::Ptr &incidence) { return QString::fromUtf8(toRawString(incidence)); } QByteArray ICalFormat::toRawString(const Incidence::Ptr &incidence) { TimeZoneList tzUsedList; icalcomponent *component = d->mImpl->writeIncidence(incidence, iTIPRequest, &tzUsedList); QByteArray text = icalcomponent_as_ical_string(component); TimeZoneEarliestDate earliestTzDt; ICalTimeZoneParser::updateTzEarliestDate(incidence, &earliestTzDt); // time zones for (const auto &qtz : qAsConst(tzUsedList)) { if (qtz != QTimeZone::utc()) { icaltimezone *tz = ICalTimeZoneParser::icaltimezoneFromQTimeZone(qtz, earliestTzDt[qtz]); if (!tz) { qCritical() << "bad time zone"; } else { icalcomponent *tzcomponent = icaltimezone_get_component(tz); icalcomponent_add_component(component, component); text.append(icalcomponent_as_ical_string(tzcomponent)); icaltimezone_free(tz, 1); } } } icalcomponent_free(component); return text; } QString ICalFormat::toString(RecurrenceRule *recurrence) { icalproperty *property = icalproperty_new_rrule(d->mImpl->writeRecurrenceRule(recurrence)); QString text = QString::fromUtf8(icalproperty_as_ical_string(property)); icalproperty_free(property); return text; } bool ICalFormat::fromString(RecurrenceRule *recurrence, const QString &rrule) { if (!recurrence) { return false; } bool success = true; icalerror_clear_errno(); struct icalrecurrencetype recur = icalrecurrencetype_from_string(rrule.toLatin1().constData()); if (icalerrno != ICAL_NO_ERROR) { qCDebug(KCALCORE_LOG) << "Recurrence parsing error:" << icalerror_strerror(icalerrno); success = false; } if (success) { d->mImpl->readRecurrence(recur, recurrence); } return success; } QString ICalFormat::createScheduleMessage(const IncidenceBase::Ptr &incidence, iTIPMethod method) { icalcomponent *message = nullptr; if (incidence->type() == Incidence::TypeEvent || incidence->type() == Incidence::TypeTodo) { Incidence::Ptr i = incidence.staticCast(); // Recurring events need timezone information to allow proper calculations // across timezones with different DST. const bool useUtcTimes = !i->recurs(); const bool hasSchedulingId = (i->schedulingID() != i->uid()); const bool incidenceNeedChanges = (useUtcTimes || hasSchedulingId); if (incidenceNeedChanges) { // The incidence need changes, so clone it before we continue i = Incidence::Ptr(i->clone()); // Handle conversion to UTC times if (useUtcTimes) { i->shiftTimes(QTimeZone::utc(), QTimeZone::utc()); } // Handle scheduling ID being present if (hasSchedulingId) { // We have a separation of scheduling ID and UID i->setSchedulingID(QString(), i->schedulingID()); } // Build the message with the cloned incidence message = d->mImpl->createScheduleComponent(i, method); } } if (message == nullptr) { message = d->mImpl->createScheduleComponent(incidence, method); } QString messageText = QString::fromUtf8(icalcomponent_as_ical_string(message)); icalcomponent_free(message); return messageText; } FreeBusy::Ptr ICalFormat::parseFreeBusy(const QString &str) { clearException(); icalcomponent *message = icalparser_parse_string(str.toUtf8().constData()); if (!message) { return FreeBusy::Ptr(); } FreeBusy::Ptr freeBusy; icalcomponent *c = nullptr; for (c = icalcomponent_get_first_component(message, ICAL_VFREEBUSY_COMPONENT); c != nullptr; c = icalcomponent_get_next_component(message, ICAL_VFREEBUSY_COMPONENT)) { FreeBusy::Ptr fb = d->mImpl->readFreeBusy(c); if (freeBusy) { freeBusy->merge(fb); } else { freeBusy = fb; } } if (!freeBusy) { qCDebug(KCALCORE_LOG) << "object is not a freebusy."; } icalcomponent_free(message); return freeBusy; } ScheduleMessage::Ptr ICalFormat::parseScheduleMessage(const Calendar::Ptr &cal, const QString &messageText) { setTimeZone(cal->timeZone()); clearException(); if (messageText.isEmpty()) { setException( new Exception(Exception::ParseErrorEmptyMessage)); return ScheduleMessage::Ptr(); } icalcomponent *message = icalparser_parse_string(messageText.toUtf8().constData()); if (!message) { setException( new Exception(Exception::ParseErrorUnableToParse)); return ScheduleMessage::Ptr(); } icalproperty *m = icalcomponent_get_first_property(message, ICAL_METHOD_PROPERTY); if (!m) { setException( new Exception(Exception::ParseErrorMethodProperty)); return ScheduleMessage::Ptr(); } // Populate the message's time zone collection with all VTIMEZONE components ICalTimeZoneCache tzlist; ICalTimeZoneParser parser(&tzlist); parser.parse(message); IncidenceBase::Ptr incidence; icalcomponent *c = icalcomponent_get_first_component(message, ICAL_VEVENT_COMPONENT); if (c) { incidence = d->mImpl->readEvent(c, &tzlist).staticCast(); } if (!incidence) { c = icalcomponent_get_first_component(message, ICAL_VTODO_COMPONENT); if (c) { incidence = d->mImpl->readTodo(c, &tzlist).staticCast(); } } if (!incidence) { c = icalcomponent_get_first_component(message, ICAL_VJOURNAL_COMPONENT); if (c) { incidence = d->mImpl->readJournal(c, &tzlist).staticCast(); } } if (!incidence) { c = icalcomponent_get_first_component(message, ICAL_VFREEBUSY_COMPONENT); if (c) { incidence = d->mImpl->readFreeBusy(c).staticCast(); } } if (!incidence) { qCDebug(KCALCORE_LOG) << "object is not a freebusy, event, todo or journal"; setException(new Exception(Exception::ParseErrorNotIncidence)); return ScheduleMessage::Ptr(); } icalproperty_method icalmethod = icalproperty_get_method(m); iTIPMethod method; switch (icalmethod) { case ICAL_METHOD_PUBLISH: method = iTIPPublish; break; case ICAL_METHOD_REQUEST: method = iTIPRequest; break; case ICAL_METHOD_REFRESH: method = iTIPRefresh; break; case ICAL_METHOD_CANCEL: method = iTIPCancel; break; case ICAL_METHOD_ADD: method = iTIPAdd; break; case ICAL_METHOD_REPLY: method = iTIPReply; break; case ICAL_METHOD_COUNTER: method = iTIPCounter; break; case ICAL_METHOD_DECLINECOUNTER: method = iTIPDeclineCounter; break; default: method = iTIPNoMethod; qCDebug(KCALCORE_LOG) << "Unknown method"; break; } if (!icalrestriction_check(message)) { qCWarning(KCALCORE_LOG) << endl << "kcalcore library reported a problem while parsing:"; qCWarning(KCALCORE_LOG) << ScheduleMessage::methodName(method) << ":" << d->mImpl->extractErrorProperty(c); } Incidence::Ptr existingIncidence = cal->incidence(incidence->uid()); icalcomponent *calendarComponent = nullptr; if (existingIncidence) { calendarComponent = d->mImpl->createCalendarComponent(cal); // TODO: check, if cast is required, or if it can be done by virtual funcs. // TODO: Use a visitor for this! if (existingIncidence->type() == Incidence::TypeTodo) { Todo::Ptr todo = existingIncidence.staticCast(); icalcomponent_add_component(calendarComponent, d->mImpl->writeTodo(todo)); } if (existingIncidence->type() == Incidence::TypeEvent) { Event::Ptr event = existingIncidence.staticCast(); icalcomponent_add_component(calendarComponent, d->mImpl->writeEvent(event)); } } else { icalcomponent_free(message); return ScheduleMessage::Ptr(new ScheduleMessage(incidence, method, ScheduleMessage::Unknown)); } icalproperty_xlicclass result = icalclassify(message, calendarComponent, static_cast("")); ScheduleMessage::Status status; switch (result) { case ICAL_XLICCLASS_PUBLISHNEW: status = ScheduleMessage::PublishNew; break; case ICAL_XLICCLASS_PUBLISHUPDATE: status = ScheduleMessage::PublishUpdate; break; case ICAL_XLICCLASS_OBSOLETE: status = ScheduleMessage::Obsolete; break; case ICAL_XLICCLASS_REQUESTNEW: status = ScheduleMessage::RequestNew; break; case ICAL_XLICCLASS_REQUESTUPDATE: status = ScheduleMessage::RequestUpdate; break; case ICAL_XLICCLASS_UNKNOWN: default: status = ScheduleMessage::Unknown; break; } icalcomponent_free(message); icalcomponent_free(calendarComponent); return ScheduleMessage::Ptr(new ScheduleMessage(incidence, method, status)); } void ICalFormat::setTimeZone(const QTimeZone &timeZone) { d->mTimeZone = timeZone; } QTimeZone ICalFormat::timeZone() const { return d->mTimeZone; } QByteArray ICalFormat::timeZoneId() const { return d->mTimeZone.id(); } void ICalFormat::virtual_hook(int id, void *data) { Q_UNUSED(id); Q_UNUSED(data); Q_ASSERT(false); } diff --git a/src/icalformat.h b/src/icalformat.h index f929007b0..c1c020d1a 100644 --- a/src/icalformat.h +++ b/src/icalformat.h @@ -1,244 +1,244 @@ /* This file is part of the kcalcore library. Copyright (c) 2001-2003 Cornelius Schumacher This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the ICalFormat class. @author Cornelius Schumacher \ */ #ifndef KCALCORE_ICALFORMAT_H #define KCALCORE_ICALFORMAT_H #include "incidence.h" #include "freebusy.h" #include "kcalcore_export.h" #include "calformat.h" #include "schedulemessage.h" -namespace KCalCore +namespace KCalendarCore { class FreeBusy; class Incidence; class IncidenceBase; class RecurrenceRule; /** @brief iCalendar format implementation. This class implements the iCalendar format. It provides methods for loading/saving/converting iCalendar format data into the internal representation as Calendar and Incidences. @warning When importing/loading to a Calendar, there is only duplicate check if those Incidences are loaded into the Calendar. If they are not loaded it will create duplicates. */ class KCALCORE_EXPORT ICalFormat : public CalFormat { public: /** Constructor a new iCalendar Format object. */ ICalFormat(); /** Destructor. */ ~ICalFormat() override; /** @copydoc CalFormat::load() */ bool load(const Calendar::Ptr &calendar, const QString &fileName) override; /** @copydoc CalFormat::save() */ bool save(const Calendar::Ptr &calendar, const QString &fileName) override; /** @copydoc CalFormat::fromString() @note The notebook is ignored and the default one is used */ bool fromString(const Calendar::Ptr &calendar, const QString &string, bool deleted = false, const QString ¬ebook = QString()) override; /** Parses a string, returning the first iCal component as an Incidence. @param string is a QString containing the data to be parsed. @return non-zero pointer if the parsing was successful; 0 otherwise. @see fromString(const Calendar::Ptr &, const QString &), fromRawString() */ Incidence::Ptr fromString(const QString &string); /** Parses a bytearray, returning the first iCal component as an Incidence, ignoring timezone information. This function is significantly faster than fromString by avoiding the overhead of parsing timezone information. Timezones are instead solely interpreted by using system-timezones. @param string is a utf8 QByteArray containing the data to be parsed. @return non-zero pointer if the parsing was successful; 0 otherwise. @see fromString(const QString &), fromRawString() */ Incidence::Ptr readIncidence(const QByteArray &string); /** Parses a string and fills a RecurrenceRule object with the information. @param rule is a pointer to a RecurrenceRule object. @param string is a QString containing the data to be parsed. @return true if successful; false otherwise. */ Q_REQUIRED_RESULT bool fromString(RecurrenceRule *rule, const QString &string); /** @copydoc CalFormat::fromRawString() */ Q_REQUIRED_RESULT bool fromRawString(const Calendar::Ptr &calendar, const QByteArray &string, bool deleted = false, const QString ¬ebook = QString()) override; /** @copydoc CalFormat::toString() */ Q_REQUIRED_RESULT QString toString(const Calendar::Ptr &calendar, const QString ¬ebook = QString(), bool deleted = false) override; /** Converts an Incidence to a QString. @param incidence is a pointer to an Incidence object to be converted into a QString. @return the QString will be Null if the conversion was unsuccessful. */ Q_REQUIRED_RESULT QString toString(const Incidence::Ptr &incidence); /** Converts an Incidence to a QByteArray. @param incidence is a pointer to an Incidence object to be converted into a QByteArray. @return the QString will be Null if the conversion was unsuccessful. @since 4.7 */ Q_REQUIRED_RESULT QByteArray toRawString(const Incidence::Ptr &incidence); /** Converts a RecurrenceRule to a QString. @param rule is a pointer to a RecurrenceRule object to be converted into a QString. @return the QString will be Null if the conversion was unsuccessful. */ Q_REQUIRED_RESULT QString toString(RecurrenceRule *rule); /** Converts an Incidence to iCalendar formatted text. @param incidence is a pointer to an Incidence object to be converted into iCal formatted text. @return the QString will be Null if the conversion was unsuccessful. */ Q_REQUIRED_RESULT QString toICalString(const Incidence::Ptr &incidence); /** Creates a scheduling message string for an Incidence. @param incidence is a pointer to an IncidenceBase object to be scheduled. @param method is a Scheduler::Method @return a QString containing the message if successful; 0 otherwise. */ Q_REQUIRED_RESULT QString createScheduleMessage(const IncidenceBase::Ptr &incidence, iTIPMethod method); /** Parses a Calendar scheduling message string into ScheduleMessage object. @param calendar is a pointer to a Calendar object associated with the scheduling message. @param string is a QString containing the data to be parsed. @return a pointer to a ScheduleMessage object if successful; 0 otherwise. The calling routine may later free the return memory. */ ScheduleMessage::Ptr parseScheduleMessage(const Calendar::Ptr &calendar, const QString &string); /** Converts a QString into a FreeBusy object. @param string is a QString containing the data to be parsed. @return a pointer to a FreeBusy object if successful; 0 otherwise. @note Do not attempt to free the FreeBusy memory from the calling routine. */ FreeBusy::Ptr parseFreeBusy(const QString &string); /** Sets the iCalendar time zone. @param timeZone is the time zone to set. @see timeZone(). */ void setTimeZone(const QTimeZone &timeZone); /** Returns the iCalendar time zone. @see setTimeZone(). */ Q_REQUIRED_RESULT QTimeZone timeZone() const; /** Returns the timezone id string used by the iCalendar; an empty string if the iCalendar does not have a timezone. */ Q_REQUIRED_RESULT QByteArray timeZoneId() const; protected: /** @copydoc IncidenceBase::virtual_hook() */ void virtual_hook(int id, void *data) override; private: //@cond PRIVATE Q_DISABLE_COPY(ICalFormat) class Private; Private *const d; //@endcond }; } #endif diff --git a/src/icalformat_p.cpp b/src/icalformat_p.cpp index d65363a5f..dc5919cb9 100644 --- a/src/icalformat_p.cpp +++ b/src/icalformat_p.cpp @@ -1,3092 +1,3092 @@ /* This file is part of the kcalcore library. Copyright (c) 2001 Cornelius Schumacher Copyright (C) 2003-2004 Reinhold Kainhofer Copyright (c) 2006 David Jarvie Copyright (C) 2012 Christian Mollekopf This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the internal ICalFormat classes. @brief This class provides the libical dependent functions for ICalFormat. @author Cornelius Schumacher \ @author Reinhold Kainhofer \ @author David Jarvie \ */ #include "icalformat_p.h" #include "compat_p.h" #include "event.h" #include "freebusy.h" #include "icalformat.h" #include "icaltimezones_p.h" #include "incidencebase.h" #include "journal.h" #include "memorycalendar.h" #include "todo.h" #include "visitor.h" #include "utils.h" #include "kcalcore_debug.h" #include -using namespace KCalCore; +using namespace KCalendarCore; static const char APP_NAME_FOR_XPROPERTIES[] = "KCALCORE"; static const char ENABLED_ALARM_XPROPERTY[] = "ENABLED"; static const char IMPLEMENTATION_VERSION_XPROPERTY[] = "X-KDE-ICAL-IMPLEMENTATION-VERSION"; /* Static helpers */ /* static void _dumpIcaltime( const icaltimetype& t) { qCDebug(KCALCORE_LOG) << "--- Y:" << t.year << "M:" << t.month << "D:" << t.day; qCDebug(KCALCORE_LOG) << "--- H:" << t.hour << "M:" << t.minute << "S:" << t.second; qCDebug(KCALCORE_LOG) << "--- isUtc:" << icaltime_is_utc( t ); qCDebug(KCALCORE_LOG) << "--- zoneId:" << icaltimezone_get_tzid( const_cast( t.zone ) ); } */ //@cond PRIVATE template void removeAllICal(QVector< QSharedPointer > &c, const QSharedPointer &x) { if (c.count() < 1) { return; } int cnt = c.count(x); if (cnt != 1) { qCritical() << "There number of relatedTos for this incidence is " << cnt << " (there must be 1 relatedTo only)"; Q_ASSERT_X(false, "removeAllICal", "Count is not 1."); return; } c.remove(c.indexOf(x)); } #if !defined(USE_ICAL_3) static QString quoteForParam(const QString &text) { QString tmp = text; tmp.remove(QLatin1Char('"')); if (tmp.contains(QLatin1Char(';')) || tmp.contains(QLatin1Char(':')) || tmp.contains(QLatin1Char(','))) { return tmp; // libical quotes in this case already, see icalparameter_as_ical_string() } return QStringLiteral("\"") + tmp + QStringLiteral("\""); } #endif const int gSecondsPerMinute = 60; const int gSecondsPerHour = gSecondsPerMinute * 60; const int gSecondsPerDay = gSecondsPerHour * 24; const int gSecondsPerWeek = gSecondsPerDay * 7; class ToComponentVisitor : public Visitor { public: ToComponentVisitor(ICalFormatImpl *impl, iTIPMethod m, TimeZoneList *tzUsedList = nullptr) : mImpl(impl), mComponent(nullptr), mMethod(m), mTzUsedList(tzUsedList) { } ~ToComponentVisitor(); bool visit(const Event::Ptr &e) override { mComponent = mImpl->writeEvent(e, mTzUsedList); return true; } bool visit(const Todo::Ptr &t) override { mComponent = mImpl->writeTodo(t, mTzUsedList); return true; } bool visit(const Journal::Ptr &j) override { mComponent = mImpl->writeJournal(j, mTzUsedList); return true; } bool visit(const FreeBusy::Ptr &fb) override { mComponent = mImpl->writeFreeBusy(fb, mMethod); return true; } icalcomponent *component() { return mComponent; } private: ICalFormatImpl *mImpl = nullptr; icalcomponent *mComponent = nullptr; iTIPMethod mMethod; TimeZoneList *mTzUsedList = nullptr; }; ToComponentVisitor::~ToComponentVisitor() { } class Q_DECL_HIDDEN ICalFormatImpl::Private { public: Private(ICalFormatImpl *impl, ICalFormat *parent) : mImpl(impl), mParent(parent), mCompat(new Compat) {} ~Private() { delete mCompat; } void writeIncidenceBase(icalcomponent *parent, const IncidenceBase::Ptr &); void readIncidenceBase(icalcomponent *parent, const IncidenceBase::Ptr &); void writeCustomProperties(icalcomponent *parent, CustomProperties *); void readCustomProperties(icalcomponent *parent, CustomProperties *); ICalFormatImpl *mImpl = nullptr; ICalFormat *mParent = nullptr; QString mLoadedProductId; // PRODID string loaded from calendar file Event::List mEventsRelate; // events with relations Todo::List mTodosRelate; // todos with relations Compat *mCompat = nullptr; }; //@endcond inline icaltimetype ICalFormatImpl::writeICalUtcDateTime(const QDateTime &dt, bool dayOnly) { return writeICalDateTime(dt.toUTC(), dayOnly); } ICalFormatImpl::ICalFormatImpl(ICalFormat *parent) : d(new Private(this, parent)) { } ICalFormatImpl::~ICalFormatImpl() { delete d; } QString ICalFormatImpl::loadedProductId() const { return d->mLoadedProductId; } icalcomponent *ICalFormatImpl::writeIncidence(const IncidenceBase::Ptr &incidence, iTIPMethod method, TimeZoneList *tzUsedList) { ToComponentVisitor v(this, method, tzUsedList); if (incidence->accept(v, incidence)) { return v.component(); } else { return nullptr; } } icalcomponent *ICalFormatImpl::writeTodo(const Todo::Ptr &todo, TimeZoneList *tzUsedList) { icalcomponent *vtodo = icalcomponent_new(ICAL_VTODO_COMPONENT); writeIncidence(vtodo, todo.staticCast(), tzUsedList); // due date icalproperty *prop; if (todo->hasDueDate()) { icaltimetype due; if (todo->allDay()) { due = writeICalDate(todo->dtDue(true).date()); prop = icalproperty_new_due(due); } else { prop = writeICalDateTimeProperty(ICAL_DUE_PROPERTY, todo->dtDue(true), tzUsedList); } icalcomponent_add_property(vtodo, prop); } // start time if (todo->hasStartDate()) { icaltimetype start; if (todo->allDay()) { start = writeICalDate(todo->dtStart(true).date()); prop = icalproperty_new_dtstart(start); } else { prop = writeICalDateTimeProperty(ICAL_DTSTART_PROPERTY, todo->dtStart(true), tzUsedList); } icalcomponent_add_property(vtodo, prop); } // completion date (UTC) if (todo->isCompleted()) { if (!todo->hasCompletedDate()) { // If the todo was created by KOrganizer<2.2 it does not have // a correct completion date. Set one now. todo->setCompleted(QDateTime::currentDateTimeUtc()); } icaltimetype completed = writeICalUtcDateTime(todo->completed()); icalcomponent_add_property( vtodo, icalproperty_new_completed(completed)); } icalcomponent_add_property( vtodo, icalproperty_new_percentcomplete(todo->percentComplete())); if (todo->isCompleted()) { if (icalcomponent_count_properties(vtodo, ICAL_STATUS_PROPERTY)) { icalproperty *p = icalcomponent_get_first_property(vtodo, ICAL_STATUS_PROPERTY); icalcomponent_remove_property(vtodo, p); icalproperty_free(p); } icalcomponent_add_property(vtodo, icalproperty_new_status(ICAL_STATUS_COMPLETED)); } if (todo->recurs() && todo->dtDue().isValid()) { // dtDue( first = true ) returns the dtRecurrence() prop = writeICalDateTimeProperty(ICAL_X_PROPERTY, todo->dtDue(), tzUsedList); icalproperty_set_x_name(prop, "X-KDE-LIBKCAL-DTRECURRENCE"); icalcomponent_add_property(vtodo, prop); } return vtodo; } icalcomponent *ICalFormatImpl::writeEvent(const Event::Ptr &event, TimeZoneList *tzUsedList) { icalcomponent *vevent = icalcomponent_new(ICAL_VEVENT_COMPONENT); writeIncidence(vevent, event.staticCast(), tzUsedList); // start time icalproperty *prop = nullptr; icaltimetype start; QDateTime dt = event->dtStart(); if (dt.isValid()) { if (event->allDay()) { start = writeICalDate(event->dtStart().date()); prop = icalproperty_new_dtstart(start); } else { prop = writeICalDateTimeProperty(ICAL_DTSTART_PROPERTY, event->dtStart(), tzUsedList); } icalcomponent_add_property(vevent, prop); } if (event->hasEndDate()) { // End time. // RFC2445 says that if DTEND is present, it has to be greater than DTSTART. icaltimetype end; QDateTime dt = event->dtEnd(); if (event->allDay()) { // +1 day because end date is non-inclusive. end = writeICalDate(dt.date().addDays(1)); icalcomponent_add_property(vevent, icalproperty_new_dtend(end)); } else { if (dt != event->dtStart()) { icalcomponent_add_property( vevent, writeICalDateTimeProperty(ICAL_DTEND_PROPERTY, dt, tzUsedList)); } } } // TODO: resources #if 0 // resources QStringList tmpStrList = anEvent->resources(); QString tmpStr = tmpStrList.join(";"); if (!tmpStr.isEmpty()) { addPropValue(vevent, VCResourcesProp, tmpStr.toUtf8()); } #endif // Transparency switch (event->transparency()) { case Event::Transparent: icalcomponent_add_property( vevent, icalproperty_new_transp(ICAL_TRANSP_TRANSPARENT)); break; case Event::Opaque: icalcomponent_add_property( vevent, icalproperty_new_transp(ICAL_TRANSP_OPAQUE)); break; } return vevent; } icalcomponent *ICalFormatImpl::writeFreeBusy(const FreeBusy::Ptr &freebusy, iTIPMethod method) { icalcomponent *vfreebusy = icalcomponent_new(ICAL_VFREEBUSY_COMPONENT); d->writeIncidenceBase(vfreebusy, freebusy.staticCast()); icalcomponent_add_property( vfreebusy, icalproperty_new_dtstart(writeICalUtcDateTime(freebusy->dtStart()))); icalcomponent_add_property( vfreebusy, icalproperty_new_dtend(writeICalUtcDateTime(freebusy->dtEnd()))); Q_UNUSED(method); icalcomponent_add_property( vfreebusy, icalproperty_new_uid(freebusy->uid().toUtf8().constData())); //Loops through all the periods in the freebusy object FreeBusyPeriod::List list = freebusy->fullBusyPeriods(); icalperiodtype period = icalperiodtype_null_period(); for (int i = 0, count = list.count(); i < count; ++i) { const FreeBusyPeriod fbPeriod = list[i]; period.start = writeICalUtcDateTime(fbPeriod.start()); if (fbPeriod.hasDuration()) { period.duration = writeICalDuration(fbPeriod.duration()); } else { period.end = writeICalUtcDateTime(fbPeriod.end()); } icalproperty *property = icalproperty_new_freebusy(period); icalparameter_fbtype fbType; switch (fbPeriod.type()) { case FreeBusyPeriod::Free: fbType = ICAL_FBTYPE_FREE; break; case FreeBusyPeriod::Busy: fbType = ICAL_FBTYPE_BUSY; break; case FreeBusyPeriod::BusyTentative: fbType = ICAL_FBTYPE_BUSYTENTATIVE; break; case FreeBusyPeriod::BusyUnavailable: fbType = ICAL_FBTYPE_BUSYUNAVAILABLE; break; case FreeBusyPeriod::Unknown: fbType = ICAL_FBTYPE_X; break; default: fbType = ICAL_FBTYPE_NONE; break; } icalproperty_set_parameter(property, icalparameter_new_fbtype(fbType)); if (!fbPeriod.summary().isEmpty()) { icalparameter *param = icalparameter_new_x("X-SUMMARY"); icalparameter_set_xvalue(param, fbPeriod.summary().toUtf8().toBase64().constData()); icalproperty_set_parameter(property, param); } if (!fbPeriod.location().isEmpty()) { icalparameter *param = icalparameter_new_x("X-LOCATION"); icalparameter_set_xvalue(param, fbPeriod.location().toUtf8().toBase64().constData()); icalproperty_set_parameter(property, param); } icalcomponent_add_property(vfreebusy, property); } return vfreebusy; } icalcomponent *ICalFormatImpl::writeJournal(const Journal::Ptr &journal, TimeZoneList *tzUsedList) { icalcomponent *vjournal = icalcomponent_new(ICAL_VJOURNAL_COMPONENT); writeIncidence(vjournal, journal.staticCast(), tzUsedList); // start time icalproperty *prop = nullptr; QDateTime dt = journal->dtStart(); if (dt.isValid()) { icaltimetype start; if (journal->allDay()) { start = writeICalDate(dt.date()); prop = icalproperty_new_dtstart(start); } else { prop = writeICalDateTimeProperty(ICAL_DTSTART_PROPERTY, dt, tzUsedList); } icalcomponent_add_property(vjournal, prop); } return vjournal; } void ICalFormatImpl::writeIncidence(icalcomponent *parent, const Incidence::Ptr &incidence, TimeZoneList *tzUsedList) { if (incidence->schedulingID() != incidence->uid()) { // We need to store the UID in here. The rawSchedulingID will // go into the iCal UID component incidence->setCustomProperty("LIBKCAL", "ID", incidence->uid()); } else { incidence->removeCustomProperty("LIBKCAL", "ID"); } d->writeIncidenceBase(parent, incidence.staticCast()); // creation date in storage icalcomponent_add_property( parent, writeICalDateTimeProperty( ICAL_CREATED_PROPERTY, incidence->created())); // unique id // If the scheduling ID is different from the real UID, the real // one is stored on X-REALID above if (!incidence->schedulingID().isEmpty()) { icalcomponent_add_property( parent, icalproperty_new_uid(incidence->schedulingID().toUtf8().constData())); } // revision if (incidence->revision() > 0) { // 0 is default, so don't write that out icalcomponent_add_property( parent, icalproperty_new_sequence(incidence->revision())); } // last modification date if (incidence->lastModified().isValid()) { icalcomponent_add_property( parent, writeICalDateTimeProperty( ICAL_LASTMODIFIED_PROPERTY, incidence->lastModified())); } // description if (!incidence->description().isEmpty()) { icalcomponent_add_property( parent, writeDescription( incidence->description(), incidence->descriptionIsRich())); } // summary if (!incidence->summary().isEmpty()) { icalcomponent_add_property( parent, writeSummary( incidence->summary(), incidence->summaryIsRich())); } // location if (!incidence->location().isEmpty()) { icalcomponent_add_property( parent, writeLocation( incidence->location(), incidence->locationIsRich())); } // status icalproperty_status status = ICAL_STATUS_NONE; switch (incidence->status()) { case Incidence::StatusTentative: status = ICAL_STATUS_TENTATIVE; break; case Incidence::StatusConfirmed: status = ICAL_STATUS_CONFIRMED; break; case Incidence::StatusCompleted: status = ICAL_STATUS_COMPLETED; break; case Incidence::StatusNeedsAction: status = ICAL_STATUS_NEEDSACTION; break; case Incidence::StatusCanceled: status = ICAL_STATUS_CANCELLED; break; case Incidence::StatusInProcess: status = ICAL_STATUS_INPROCESS; break; case Incidence::StatusDraft: status = ICAL_STATUS_DRAFT; break; case Incidence::StatusFinal: status = ICAL_STATUS_FINAL; break; case Incidence::StatusX: { icalproperty *p = icalproperty_new_status(ICAL_STATUS_X); icalvalue_set_x(icalproperty_get_value(p), incidence->customStatus().toUtf8().constData()); icalcomponent_add_property(parent, p); break; } case Incidence::StatusNone: default: break; } if (status != ICAL_STATUS_NONE) { icalcomponent_add_property(parent, icalproperty_new_status(status)); } // secrecy icalproperty_class secClass; switch (incidence->secrecy()) { case Incidence::SecrecyPublic: secClass = ICAL_CLASS_PUBLIC; break; case Incidence::SecrecyConfidential: secClass = ICAL_CLASS_CONFIDENTIAL; break; case Incidence::SecrecyPrivate: default: secClass = ICAL_CLASS_PRIVATE; break; } if (secClass != ICAL_CLASS_PUBLIC) { icalcomponent_add_property(parent, icalproperty_new_class(secClass)); } // geo if (incidence->hasGeo()) { icalgeotype geo; geo.lat = incidence->geoLatitude(); geo.lon = incidence->geoLongitude(); icalcomponent_add_property(parent, icalproperty_new_geo(geo)); } // priority if (incidence->priority() > 0) { // 0 is undefined priority icalcomponent_add_property( parent, icalproperty_new_priority(incidence->priority())); } // categories QString categories = incidence->categories().join(QLatin1Char(',')); if (!categories.isEmpty()) { icalcomponent_add_property( parent, icalproperty_new_categories(categories.toUtf8().constData())); } // related event if (!incidence->relatedTo().isEmpty()) { icalcomponent_add_property( parent, icalproperty_new_relatedto(incidence->relatedTo().toUtf8().constData())); } // recurrenceid if (incidence->hasRecurrenceId()) { icalproperty *p = writeICalDateTimeProperty( ICAL_RECURRENCEID_PROPERTY, incidence->recurrenceId(), tzUsedList); if (incidence->thisAndFuture()) { icalproperty_add_parameter( p, icalparameter_new_range(ICAL_RANGE_THISANDFUTURE)); } icalcomponent_add_property(parent, p); } RecurrenceRule::List rrules(incidence->recurrence()->rRules()); RecurrenceRule::List::ConstIterator rit; for (rit = rrules.constBegin(); rit != rrules.constEnd(); ++rit) { icalcomponent_add_property( parent, icalproperty_new_rrule(writeRecurrenceRule((*rit)))); } RecurrenceRule::List exrules(incidence->recurrence()->exRules()); RecurrenceRule::List::ConstIterator exit; for (exit = exrules.constBegin(); exit != exrules.constEnd(); ++exit) { icalcomponent_add_property( parent, icalproperty_new_exrule(writeRecurrenceRule((*exit)))); } DateList dateList = incidence->recurrence()->exDates(); DateList::ConstIterator exIt; for (exIt = dateList.constBegin(); exIt != dateList.constEnd(); ++exIt) { icalcomponent_add_property( parent, icalproperty_new_exdate(writeICalDate(*exIt))); } auto dateTimeList = incidence->recurrence()->exDateTimes(); for (auto extIt = dateTimeList.constBegin(); extIt != dateTimeList.constEnd(); ++extIt) { icalcomponent_add_property( parent, writeICalDateTimeProperty(ICAL_EXDATE_PROPERTY, *extIt, tzUsedList)); } dateList = incidence->recurrence()->rDates(); DateList::ConstIterator rdIt; for (rdIt = dateList.constBegin(); rdIt != dateList.constEnd(); ++rdIt) { icalcomponent_add_property( parent, icalproperty_new_rdate(writeICalDatePeriod(*rdIt))); } dateTimeList = incidence->recurrence()->rDateTimes(); for (auto rdtIt = dateTimeList.constBegin(); rdtIt != dateTimeList.constEnd(); ++rdtIt) { icalcomponent_add_property( parent, writeICalDateTimeProperty(ICAL_RDATE_PROPERTY, *rdtIt, tzUsedList)); } // attachments Attachment::List attachments = incidence->attachments(); Attachment::List::ConstIterator atIt; for (atIt = attachments.constBegin(); atIt != attachments.constEnd(); ++atIt) { icalcomponent_add_property(parent, writeAttachment(*atIt)); } // alarms auto alarms = incidence->alarms(); for (auto it = alarms.cbegin(), end = alarms.cend(); it != end; ++it) { icalcomponent_add_component(parent, writeAlarm(*it)); } // duration if (incidence->hasDuration()) { icaldurationtype duration; duration = writeICalDuration(incidence->duration()); icalcomponent_add_property(parent, icalproperty_new_duration(duration)); } } //@cond PRIVATE void ICalFormatImpl::Private::writeIncidenceBase(icalcomponent *parent, const IncidenceBase::Ptr &incidenceBase) { // organizer stuff if (!incidenceBase->organizer().isEmpty()) { icalproperty *p = mImpl->writeOrganizer(incidenceBase->organizer()); if (p) { icalcomponent_add_property(parent, p); } } icalcomponent_add_property( parent, icalproperty_new_dtstamp(writeICalUtcDateTime(incidenceBase->lastModified()))); // attendees if (incidenceBase->attendeeCount() > 0) { auto attendees = incidenceBase->attendees(); for (auto it = attendees.constBegin(); it != attendees.constEnd(); ++it) { icalproperty *p = mImpl->writeAttendee(*it); if (p) { icalcomponent_add_property(parent, p); } } } //contacts QStringList contacts = incidenceBase->contacts(); for (QStringList::const_iterator it = contacts.constBegin(); it != contacts.constEnd(); ++it) { icalcomponent_add_property(parent, icalproperty_new_contact((*it).toUtf8().constData())); } // comments QStringList comments = incidenceBase->comments(); for (QStringList::const_iterator it = comments.constBegin(); it != comments.constEnd(); ++it) { icalcomponent_add_property(parent, icalproperty_new_comment((*it).toUtf8().constData())); } // url const QUrl url = incidenceBase->url(); if (url.isValid()) { icalcomponent_add_property(parent, icalproperty_new_url(url.toString().toUtf8().constData())); } // custom properties writeCustomProperties(parent, incidenceBase.data()); } void ICalFormatImpl::Private::writeCustomProperties(icalcomponent *parent, CustomProperties *properties) { const QMap custom = properties->customProperties(); for (QMap::ConstIterator c = custom.begin(); c != custom.end(); ++c) { if (c.key().startsWith("X-KDE-VOLATILE")) { //krazy:exclude=strings // We don't write these properties to disk to disk continue; } icalproperty *p = icalproperty_new_x(c.value().toUtf8().constData()); QString parameters = properties->nonKDECustomPropertyParameters(c.key()); // Minimalist parameter handler: extract icalparameter's out of // the given input text (not really parsing as such) if (!parameters.isEmpty()) { const QStringList sl = parameters.split(QLatin1Char(';')); for (const QString ¶meter : sl) { icalparameter *param = icalparameter_new_from_string(parameter.toUtf8().constData()); if (param) { icalproperty_add_parameter(p, param); } } } icalproperty_set_x_name(p, c.key().constData()); icalcomponent_add_property(parent, p); } } //@endcond icalproperty *ICalFormatImpl::writeOrganizer(const Person &organizer) { if (organizer.email().isEmpty()) { return nullptr; } icalproperty *p = icalproperty_new_organizer(QByteArray(QByteArray("MAILTO:") + organizer.email().toUtf8()).constData()); if (!organizer.name().isEmpty()) { icalproperty_add_parameter(p, #if defined(USE_ICAL_3) icalparameter_new_cn(organizer.name().toUtf8().constData())); #else icalparameter_new_cn(quoteForParam(organizer.name()).toUtf8().constData())); #endif } // TODO: Write dir, sent-by and language return p; } icalproperty *ICalFormatImpl::writeDescription(const QString &description, bool isRich) { icalproperty *p = icalproperty_new_description(description.toUtf8().constData()); if (isRich) { icalproperty_add_parameter(p, icalparameter_new_from_string("X-KDE-TEXTFORMAT=HTML")); } return p; } icalproperty *ICalFormatImpl::writeSummary(const QString &summary, bool isRich) { icalproperty *p = icalproperty_new_summary(summary.toUtf8().constData()); if (isRich) { icalproperty_add_parameter(p, icalparameter_new_from_string("X-KDE-TEXTFORMAT=HTML")); } return p; } icalproperty *ICalFormatImpl::writeLocation(const QString &location, bool isRich) { icalproperty *p = icalproperty_new_location(location.toUtf8().constData()); if (isRich) { icalproperty_add_parameter(p, icalparameter_new_from_string("X-KDE-TEXTFORMAT=HTML")); } return p; } icalproperty *ICalFormatImpl::writeAttendee(const Attendee &attendee) { if (attendee.email().isEmpty()) { return nullptr; } icalproperty *p = icalproperty_new_attendee(QByteArray(QByteArray("mailto:") + attendee.email().toUtf8()).constData()); if (!attendee.name().isEmpty()) { icalproperty_add_parameter(p, #if defined(USE_ICAL_3) icalparameter_new_cn(attendee.name().toUtf8().constData())); #else icalparameter_new_cn(quoteForParam(attendee.name()).toUtf8().constData())); #endif } icalproperty_add_parameter( p, icalparameter_new_rsvp(attendee.RSVP() ? ICAL_RSVP_TRUE : ICAL_RSVP_FALSE)); icalparameter_partstat status = ICAL_PARTSTAT_NEEDSACTION; switch (attendee.status()) { default: case Attendee::NeedsAction: status = ICAL_PARTSTAT_NEEDSACTION; break; case Attendee::Accepted: status = ICAL_PARTSTAT_ACCEPTED; break; case Attendee::Declined: status = ICAL_PARTSTAT_DECLINED; break; case Attendee::Tentative: status = ICAL_PARTSTAT_TENTATIVE; break; case Attendee::Delegated: status = ICAL_PARTSTAT_DELEGATED; break; case Attendee::Completed: status = ICAL_PARTSTAT_COMPLETED; break; case Attendee::InProcess: status = ICAL_PARTSTAT_INPROCESS; break; } icalproperty_add_parameter(p, icalparameter_new_partstat(status)); icalparameter_role role = ICAL_ROLE_REQPARTICIPANT; switch (attendee.role()) { case Attendee::Chair: role = ICAL_ROLE_CHAIR; break; default: case Attendee::ReqParticipant: role = ICAL_ROLE_REQPARTICIPANT; break; case Attendee::OptParticipant: role = ICAL_ROLE_OPTPARTICIPANT; break; case Attendee::NonParticipant: role = ICAL_ROLE_NONPARTICIPANT; break; } icalproperty_add_parameter(p, icalparameter_new_role(role)); icalparameter_cutype cutype = ICAL_CUTYPE_INDIVIDUAL; switch (attendee.cuType()) { case Attendee::Unknown: cutype = ICAL_CUTYPE_UNKNOWN; break; default: case Attendee::Individual: cutype = ICAL_CUTYPE_INDIVIDUAL; break; case Attendee::Group: cutype = ICAL_CUTYPE_GROUP; break; case Attendee::Resource: cutype = ICAL_CUTYPE_RESOURCE; break; case Attendee::Room: cutype = ICAL_CUTYPE_ROOM; break; } icalproperty_add_parameter(p, icalparameter_new_cutype(cutype)); if (!attendee.uid().isEmpty()) { icalparameter *icalparameter_uid = icalparameter_new_x(attendee.uid().toUtf8().constData()); icalparameter_set_xname(icalparameter_uid, "X-UID"); icalproperty_add_parameter(p, icalparameter_uid); } if (!attendee.delegate().isEmpty()) { icalparameter *icalparameter_delegate = icalparameter_new_delegatedto(attendee.delegate().toUtf8().constData()); icalproperty_add_parameter(p, icalparameter_delegate); } if (!attendee.delegator().isEmpty()) { icalparameter *icalparameter_delegator = icalparameter_new_delegatedfrom(attendee.delegator().toUtf8().constData()); icalproperty_add_parameter(p, icalparameter_delegator); } return p; } icalproperty *ICalFormatImpl::writeAttachment(const Attachment &att) { icalattach *attach; if (att.isUri()) { attach = icalattach_new_from_url(att.uri().toUtf8().data()); } else { attach = icalattach_new_from_data((const char *)att.data().constData(), nullptr, nullptr); } icalproperty *p = icalproperty_new_attach(attach); icalattach_unref(attach); if (!att.mimeType().isEmpty()) { icalproperty_add_parameter( p, icalparameter_new_fmttype(att.mimeType().toUtf8().data())); } if (att.isBinary()) { icalproperty_add_parameter(p, icalparameter_new_value(ICAL_VALUE_BINARY)); icalproperty_add_parameter(p, icalparameter_new_encoding(ICAL_ENCODING_BASE64)); } if (att.showInline()) { icalparameter *icalparameter_inline = icalparameter_new_x("inline"); icalparameter_set_xname(icalparameter_inline, "X-CONTENT-DISPOSITION"); icalproperty_add_parameter(p, icalparameter_inline); } if (!att.label().isEmpty()) { icalparameter *icalparameter_label = icalparameter_new_x(att.label().toUtf8().constData()); icalparameter_set_xname(icalparameter_label, "X-LABEL"); icalproperty_add_parameter(p, icalparameter_label); } if (att.isLocal()) { icalparameter *icalparameter_local = icalparameter_new_x("local"); icalparameter_set_xname(icalparameter_local, "X-KONTACT-TYPE"); icalproperty_add_parameter(p, icalparameter_local); } return p; } icalrecurrencetype ICalFormatImpl::writeRecurrenceRule(RecurrenceRule *recur) { icalrecurrencetype r; icalrecurrencetype_clear(&r); switch (recur->recurrenceType()) { case RecurrenceRule::rSecondly: r.freq = ICAL_SECONDLY_RECURRENCE; break; case RecurrenceRule::rMinutely: r.freq = ICAL_MINUTELY_RECURRENCE; break; case RecurrenceRule::rHourly: r.freq = ICAL_HOURLY_RECURRENCE; break; case RecurrenceRule::rDaily: r.freq = ICAL_DAILY_RECURRENCE; break; case RecurrenceRule::rWeekly: r.freq = ICAL_WEEKLY_RECURRENCE; break; case RecurrenceRule::rMonthly: r.freq = ICAL_MONTHLY_RECURRENCE; break; case RecurrenceRule::rYearly: r.freq = ICAL_YEARLY_RECURRENCE; break; default: r.freq = ICAL_NO_RECURRENCE; qCDebug(KCALCORE_LOG) << "no recurrence"; break; } int index = 0; QList bys; QList::ConstIterator it; // Now write out the BY* parts: bys = recur->bySeconds(); index = 0; for (it = bys.constBegin(); it != bys.constEnd(); ++it) { r.by_second[index++] = *it; r.by_second[index++] = static_cast(*it); } bys = recur->byMinutes(); index = 0; for (it = bys.constBegin(); it != bys.constEnd(); ++it) { r.by_minute[index++] = *it; r.by_minute[index++] = static_cast(*it); } bys = recur->byHours(); index = 0; for (it = bys.constBegin(); it != bys.constEnd(); ++it) { r.by_hour[index++] = *it; r.by_hour[index++] = static_cast(*it); } bys = recur->byMonthDays(); index = 0; for (it = bys.constBegin(); it != bys.constEnd(); ++it) { short dShort = static_cast((*it) * 8); r.by_month_day[index++] = static_cast(icalrecurrencetype_day_position(dShort)); } bys = recur->byYearDays(); index = 0; for (it = bys.constBegin(); it != bys.constEnd(); ++it) { r.by_year_day[index++] = static_cast(*it); } bys = recur->byWeekNumbers(); index = 0; for (it = bys.constBegin(); it != bys.constEnd(); ++it) { r.by_week_no[index++] = static_cast(*it); } bys = recur->byMonths(); index = 0; for (it = bys.constBegin(); it != bys.constEnd(); ++it) { r.by_month[index++] = static_cast(*it); } bys = recur->bySetPos(); index = 0; for (it = bys.constBegin(); it != bys.constEnd(); ++it) { r.by_set_pos[index++] = static_cast(*it); } const QList &byd = recur->byDays(); int day; index = 0; for (QList::ConstIterator dit = byd.constBegin(); dit != byd.constEnd(); ++dit) { day = (*dit).day() % 7 + 1; // convert from Monday=1 to Sunday=1 if ((*dit).pos() < 0) { day += (-(*dit).pos()) * 8; day = -day; } else { day += (*dit).pos() * 8; } r.by_day[index++] = static_cast(day); } r.week_start = static_cast(recur->weekStart() % 7 + 1); if (recur->frequency() > 1) { // Dont' write out INTERVAL=1, because that's the default anyway r.interval = static_cast(recur->frequency()); } if (recur->duration() > 0) { r.count = recur->duration(); } else if (recur->duration() == -1) { r.count = 0; } else { if (recur->allDay()) { r.until = writeICalDate(recur->endDt().date()); } else { r.until = writeICalUtcDateTime(recur->endDt()); } } return r; } icalcomponent *ICalFormatImpl::writeAlarm(const Alarm::Ptr &alarm) { if (alarm->enabled()) { alarm->setCustomProperty(APP_NAME_FOR_XPROPERTIES, ENABLED_ALARM_XPROPERTY, QStringLiteral("TRUE")); } else { alarm->setCustomProperty(APP_NAME_FOR_XPROPERTIES, ENABLED_ALARM_XPROPERTY, QStringLiteral("FALSE")); } icalcomponent *a = icalcomponent_new(ICAL_VALARM_COMPONENT); icalproperty_action action; icalattach *attach = nullptr; switch (alarm->type()) { case Alarm::Procedure: action = ICAL_ACTION_PROCEDURE; attach = icalattach_new_from_url( QFile::encodeName(alarm->programFile()).data()); icalcomponent_add_property(a, icalproperty_new_attach(attach)); if (!alarm->programArguments().isEmpty()) { icalcomponent_add_property( a, icalproperty_new_description(alarm->programArguments().toUtf8().constData())); } break; case Alarm::Audio: action = ICAL_ACTION_AUDIO; if (!alarm->audioFile().isEmpty()) { attach = icalattach_new_from_url( QFile::encodeName(alarm->audioFile()).data()); icalcomponent_add_property(a, icalproperty_new_attach(attach)); } break; case Alarm::Email: { action = ICAL_ACTION_EMAIL; const Person::List addresses = alarm->mailAddresses(); for (Person::List::ConstIterator ad = addresses.constBegin(); ad != addresses.constEnd(); ++ad) { if (!(*ad).email().isEmpty()) { icalproperty *p = icalproperty_new_attendee(QByteArray(QByteArray("MAILTO:") + (*ad).email().toUtf8()).constData()); if (!(*ad).name().isEmpty()) { icalproperty_add_parameter(p, #if defined(USE_ICAL_3) icalparameter_new_cn((*ad).name().toUtf8().constData())); #else icalparameter_new_cn(quoteForParam((*ad).name()).toUtf8().constData())); #endif } icalcomponent_add_property(a, p); } } icalcomponent_add_property( a, icalproperty_new_summary(alarm->mailSubject().toUtf8().constData())); icalcomponent_add_property( a, icalproperty_new_description(alarm->mailText().toUtf8().constData())); const QStringList attachments = alarm->mailAttachments(); if (!attachments.isEmpty()) { for (QStringList::const_iterator at = attachments.constBegin(), end = attachments.constEnd(); at != end; ++at) { attach = icalattach_new_from_url(QFile::encodeName(*at).data()); icalcomponent_add_property(a, icalproperty_new_attach(attach)); } } break; } case Alarm::Display: action = ICAL_ACTION_DISPLAY; icalcomponent_add_property( a, icalproperty_new_description(alarm->text().toUtf8().constData())); break; case Alarm::Invalid: default: qCDebug(KCALCORE_LOG) << "Unknown type of alarm"; action = ICAL_ACTION_NONE; break; } icalcomponent_add_property(a, icalproperty_new_action(action)); // Trigger time icaltriggertype trigger; if (alarm->hasTime()) { trigger.time = writeICalUtcDateTime(alarm->time(), false); trigger.duration = icaldurationtype_null_duration(); } else { trigger.time = icaltime_null_time(); Duration offset; if (alarm->hasStartOffset()) { offset = alarm->startOffset(); } else { offset = alarm->endOffset(); } trigger.duration = writeICalDuration(offset); } icalproperty *p = icalproperty_new_trigger(trigger); if (alarm->hasEndOffset()) { icalproperty_add_parameter(p, icalparameter_new_related(ICAL_RELATED_END)); } icalcomponent_add_property(a, p); // Repeat count and duration if (alarm->repeatCount()) { icalcomponent_add_property( a, icalproperty_new_repeat(alarm->repeatCount())); icalcomponent_add_property( a, icalproperty_new_duration(writeICalDuration(alarm->snoozeTime()))); } // Custom properties const QMap custom = alarm->customProperties(); for (QMap::ConstIterator c = custom.begin(); c != custom.end(); ++c) { icalproperty *p = icalproperty_new_x(c.value().toUtf8().constData()); icalproperty_set_x_name(p, c.key().constData()); icalcomponent_add_property(a, p); } icalattach_unref(attach); return a; } Todo::Ptr ICalFormatImpl::readTodo(icalcomponent *vtodo, const ICalTimeZoneCache *tzlist) { Todo::Ptr todo(new Todo); readIncidence(vtodo, todo, tzlist); icalproperty *p = icalcomponent_get_first_property(vtodo, ICAL_ANY_PROPERTY); while (p) { icalproperty_kind kind = icalproperty_isa(p); switch (kind) { case ICAL_DUE_PROPERTY: { // due date/time bool allDay = false; QDateTime kdt = readICalDateTimeProperty(p, tzlist, false, &allDay); todo->setDtDue(kdt, true); todo->setAllDay(allDay); break; } case ICAL_COMPLETED_PROPERTY: // completion date/time todo->setCompleted(readICalDateTimeProperty(p, tzlist)); break; case ICAL_PERCENTCOMPLETE_PROPERTY: // Percent completed todo->setPercentComplete(icalproperty_get_percentcomplete(p)); break; case ICAL_RELATEDTO_PROPERTY: // related todo (parent) todo->setRelatedTo(QString::fromUtf8(icalproperty_get_relatedto(p))); d->mTodosRelate.append(todo); break; case ICAL_DTSTART_PROPERTY: // Flag that todo has start date. Value is read in by readIncidence(). if (!todo->comments().filter(QStringLiteral("NoStartDate")).isEmpty()) { todo->setDtStart(QDateTime()); } break; case ICAL_X_PROPERTY: { const char *name = icalproperty_get_x_name(p); if (QLatin1String(name) == QLatin1String("X-KDE-LIBKCAL-DTRECURRENCE")) { const QDateTime dateTime = readICalDateTimeProperty(p, tzlist); if (dateTime.isValid()) { todo->setDtRecurrence(dateTime); } else { qCDebug(KCALCORE_LOG) << "Invalid dateTime"; } } } break; default: // TODO: do something about unknown properties? break; } p = icalcomponent_get_next_property(vtodo, ICAL_ANY_PROPERTY); } if (d->mCompat) { d->mCompat->fixEmptySummary(todo); } todo->resetDirtyFields(); return todo; } Event::Ptr ICalFormatImpl::readEvent(icalcomponent *vevent, const ICalTimeZoneCache *tzlist) { Event::Ptr event(new Event); readIncidence(vevent, event, tzlist); icalproperty *p = icalcomponent_get_first_property(vevent, ICAL_ANY_PROPERTY); bool dtEndProcessed = false; while (p) { icalproperty_kind kind = icalproperty_isa(p); switch (kind) { case ICAL_DTEND_PROPERTY: { // end date and time bool allDay = false; QDateTime kdt = readICalDateTimeProperty(p, tzlist, false, &allDay); if (allDay) { // End date is non-inclusive QDate endDate = kdt.date().addDays(-1); if (d->mCompat) { d->mCompat->fixFloatingEnd(endDate); } if (endDate < event->dtStart().date()) { endDate = event->dtStart().date(); } event->setDtEnd(QDateTime(endDate, {}, event->dtStart().timeZone())); event->setAllDay(true); } else { event->setDtEnd(kdt); event->setAllDay(false); } dtEndProcessed = true; break; } case ICAL_RELATEDTO_PROPERTY: // related event (parent) event->setRelatedTo(QString::fromUtf8(icalproperty_get_relatedto(p))); d->mEventsRelate.append(event); break; case ICAL_TRANSP_PROPERTY: { // Transparency icalproperty_transp transparency = icalproperty_get_transp(p); if (transparency == ICAL_TRANSP_TRANSPARENT) { event->setTransparency(Event::Transparent); } else { event->setTransparency(Event::Opaque); } break; } default: // TODO: do something about unknown properties? break; } p = icalcomponent_get_next_property(vevent, ICAL_ANY_PROPERTY); } // according to rfc2445 the dtend shouldn't be written when it equals // start date. so assign one equal to start date. if (!dtEndProcessed && !event->hasDuration()) { event->setDtEnd(event->dtStart()); } QString msade = event->nonKDECustomProperty("X-MICROSOFT-CDO-ALLDAYEVENT"); if (!msade.isEmpty()) { bool allDay = (msade == QLatin1String("TRUE")); event->setAllDay(allDay); } if (d->mCompat) { d->mCompat->fixEmptySummary(event); } event->resetDirtyFields(); return event; } FreeBusy::Ptr ICalFormatImpl::readFreeBusy(icalcomponent *vfreebusy) { FreeBusy::Ptr freebusy(new FreeBusy); d->readIncidenceBase(vfreebusy, freebusy); icalproperty *p = icalcomponent_get_first_property(vfreebusy, ICAL_ANY_PROPERTY); FreeBusyPeriod::List periods; while (p) { icalproperty_kind kind = icalproperty_isa(p); switch (kind) { case ICAL_DTSTART_PROPERTY: // start date and time (UTC) freebusy->setDtStart(readICalUtcDateTimeProperty(p, nullptr)); break; case ICAL_DTEND_PROPERTY: // end Date and Time (UTC) freebusy->setDtEnd(readICalUtcDateTimeProperty(p, nullptr)); break; case ICAL_FREEBUSY_PROPERTY: { //Any FreeBusy Times (UTC) icalperiodtype icalperiod = icalproperty_get_freebusy(p); QDateTime period_start = readICalUtcDateTime(p, icalperiod.start); FreeBusyPeriod period; if (!icaltime_is_null_time(icalperiod.end)) { QDateTime period_end = readICalUtcDateTime(p, icalperiod.end); period = FreeBusyPeriod(period_start, period_end); } else { Duration duration(readICalDuration(icalperiod.duration)); period = FreeBusyPeriod(period_start, duration); } icalparameter *param = icalproperty_get_first_parameter(p, ICAL_FBTYPE_PARAMETER); if (param) { icalparameter_fbtype fbType = icalparameter_get_fbtype(param); switch (fbType) { case ICAL_FBTYPE_FREE: period.setType(FreeBusyPeriod::Free); break; case ICAL_FBTYPE_BUSY: period.setType(FreeBusyPeriod::Busy); break; case ICAL_FBTYPE_BUSYTENTATIVE: period.setType(FreeBusyPeriod::BusyTentative); break; case ICAL_FBTYPE_BUSYUNAVAILABLE: period.setType(FreeBusyPeriod::BusyUnavailable); break; case ICAL_FBTYPE_X: period.setType(FreeBusyPeriod::Unknown); break; case ICAL_FBTYPE_NONE: period.setType(FreeBusyPeriod::Free); break; } } param = icalproperty_get_first_parameter(p, ICAL_X_PARAMETER); while (param) { if (strncmp(icalparameter_get_xname(param), "X-SUMMARY", 9) == 0) { period.setSummary(QString::fromUtf8( QByteArray::fromBase64(icalparameter_get_xvalue(param)))); } if (strncmp(icalparameter_get_xname(param), "X-LOCATION", 10) == 0) { period.setLocation(QString::fromUtf8( QByteArray::fromBase64(icalparameter_get_xvalue(param)))); } param = icalproperty_get_next_parameter(p, ICAL_X_PARAMETER); } periods.append(period); break; } default: // TODO: do something about unknown properties? break; } p = icalcomponent_get_next_property(vfreebusy, ICAL_ANY_PROPERTY); } freebusy->addPeriods(periods); freebusy->resetDirtyFields(); return freebusy; } Journal::Ptr ICalFormatImpl::readJournal(icalcomponent *vjournal, const ICalTimeZoneCache *tzList) { Journal::Ptr journal(new Journal); readIncidence(vjournal, journal, tzList); journal->resetDirtyFields(); return journal; } Attendee ICalFormatImpl::readAttendee(icalproperty *attendee) { // the following is a hack to support broken calendars (like WebCalendar 1.0.x) // that include non-RFC-compliant attendees. Otherwise libical 0.42 asserts. if (!icalproperty_get_value(attendee)) { return {}; } icalparameter *p = nullptr; QString email = QString::fromUtf8(icalproperty_get_attendee(attendee)); if (email.startsWith(QLatin1String("mailto:"), Qt::CaseInsensitive)) { email = email.mid(7); } // libical may return everything after ATTENDEE tag if the rest is // not meaningful. Verify the address to filter out these cases. if (!Person::isValidEmail(email)) { return {}; } QString name; QString uid; p = icalproperty_get_first_parameter(attendee, ICAL_CN_PARAMETER); if (p) { name = QString::fromUtf8(icalparameter_get_cn(p)); } else { } bool rsvp = false; p = icalproperty_get_first_parameter(attendee, ICAL_RSVP_PARAMETER); if (p) { icalparameter_rsvp rsvpParameter = icalparameter_get_rsvp(p); if (rsvpParameter == ICAL_RSVP_TRUE) { rsvp = true; } } Attendee::PartStat status = Attendee::NeedsAction; p = icalproperty_get_first_parameter(attendee, ICAL_PARTSTAT_PARAMETER); if (p) { icalparameter_partstat partStatParameter = icalparameter_get_partstat(p); switch (partStatParameter) { default: case ICAL_PARTSTAT_NEEDSACTION: status = Attendee::NeedsAction; break; case ICAL_PARTSTAT_ACCEPTED: status = Attendee::Accepted; break; case ICAL_PARTSTAT_DECLINED: status = Attendee::Declined; break; case ICAL_PARTSTAT_TENTATIVE: status = Attendee::Tentative; break; case ICAL_PARTSTAT_DELEGATED: status = Attendee::Delegated; break; case ICAL_PARTSTAT_COMPLETED: status = Attendee::Completed; break; case ICAL_PARTSTAT_INPROCESS: status = Attendee::InProcess; break; } } Attendee::Role role = Attendee::ReqParticipant; p = icalproperty_get_first_parameter(attendee, ICAL_ROLE_PARAMETER); if (p) { icalparameter_role roleParameter = icalparameter_get_role(p); switch (roleParameter) { case ICAL_ROLE_CHAIR: role = Attendee::Chair; break; default: case ICAL_ROLE_REQPARTICIPANT: role = Attendee::ReqParticipant; break; case ICAL_ROLE_OPTPARTICIPANT: role = Attendee::OptParticipant; break; case ICAL_ROLE_NONPARTICIPANT: role = Attendee::NonParticipant; break; } } Attendee::CuType cuType = Attendee::Individual; p = icalproperty_get_first_parameter(attendee, ICAL_CUTYPE_PARAMETER); if (p) { icalparameter_cutype cutypeParameter = icalparameter_get_cutype(p); switch (cutypeParameter) { case ICAL_CUTYPE_X: case ICAL_CUTYPE_UNKNOWN: cuType = Attendee::Unknown; break; default: case ICAL_CUTYPE_NONE: case ICAL_CUTYPE_INDIVIDUAL: cuType = Attendee::Individual; break; case ICAL_CUTYPE_GROUP: cuType = Attendee::Group; break; case ICAL_CUTYPE_RESOURCE: cuType = Attendee::Resource; break; case ICAL_CUTYPE_ROOM: cuType = Attendee::Room; break; } } p = icalproperty_get_first_parameter(attendee, ICAL_X_PARAMETER); QMap custom; while (p) { QString xname = QString::fromLatin1(icalparameter_get_xname(p)).toUpper(); QString xvalue = QString::fromUtf8(icalparameter_get_xvalue(p)); if (xname == QLatin1String("X-UID")) { uid = xvalue; } else { custom[xname.toUtf8()] = xvalue; } p = icalproperty_get_next_parameter(attendee, ICAL_X_PARAMETER); } Attendee a(name, email, rsvp, status, role, uid); a.setCuType(cuType); a.customProperties().setCustomProperties(custom); p = icalproperty_get_first_parameter(attendee, ICAL_DELEGATEDTO_PARAMETER); if (p) { a.setDelegate(QLatin1String(icalparameter_get_delegatedto(p))); } p = icalproperty_get_first_parameter(attendee, ICAL_DELEGATEDFROM_PARAMETER); if (p) { a.setDelegator(QLatin1String(icalparameter_get_delegatedfrom(p))); } return a; } Person ICalFormatImpl::readOrganizer(icalproperty *organizer) { QString email = QString::fromUtf8(icalproperty_get_organizer(organizer)); if (email.startsWith(QLatin1String("mailto:"), Qt::CaseInsensitive)) { email = email.mid(7); } QString cn; icalparameter *p = icalproperty_get_first_parameter(organizer, ICAL_CN_PARAMETER); if (p) { cn = QString::fromUtf8(icalparameter_get_cn(p)); } Person org(cn, email); // TODO: Treat sent-by, dir and language here, too return org; } Attachment ICalFormatImpl::readAttachment(icalproperty *attach) { Attachment attachment; QByteArray p; icalvalue *value = icalproperty_get_value(attach); switch (icalvalue_isa(value)) { case ICAL_ATTACH_VALUE: { icalattach *a = icalproperty_get_attach(attach); if (!icalattach_get_is_url(a)) { p = QByteArray(reinterpret_cast(icalattach_get_data(a))); if (!p.isEmpty()) { attachment = Attachment(p); } } else { p = icalattach_get_url(a); if (!p.isEmpty()) { attachment = Attachment(QString::fromUtf8(p)); } } break; } case ICAL_BINARY_VALUE: { icalattach *a = icalproperty_get_attach(attach); p = QByteArray(reinterpret_cast(icalattach_get_data(a))); if (!p.isEmpty()) { attachment = Attachment(p); } break; } case ICAL_URI_VALUE: p = icalvalue_get_uri(value); attachment = Attachment(QString::fromUtf8(p)); break; default: break; } if (!attachment.isEmpty()) { icalparameter *p = icalproperty_get_first_parameter(attach, ICAL_FMTTYPE_PARAMETER); if (p) { attachment.setMimeType(QLatin1String(icalparameter_get_fmttype(p))); } p = icalproperty_get_first_parameter(attach, ICAL_X_PARAMETER); while (p) { QString xname = QString::fromLatin1(icalparameter_get_xname(p)).toUpper(); QString xvalue = QString::fromUtf8(icalparameter_get_xvalue(p)); if (xname == QLatin1String("X-CONTENT-DISPOSITION")) { attachment.setShowInline(xvalue.toLower() == QLatin1String("inline")); } else if (xname == QLatin1String("X-LABEL")) { attachment.setLabel(xvalue); } else if (xname == QLatin1String("X-KONTACT-TYPE")) { attachment.setLocal(xvalue.toLower() == QLatin1String("local")); } p = icalproperty_get_next_parameter(attach, ICAL_X_PARAMETER); } p = icalproperty_get_first_parameter(attach, ICAL_X_PARAMETER); while (p) { if (strncmp(icalparameter_get_xname(p), "X-LABEL", 7) == 0) { attachment.setLabel(QString::fromUtf8(icalparameter_get_xvalue(p))); } p = icalproperty_get_next_parameter(attach, ICAL_X_PARAMETER); } } return attachment; } void ICalFormatImpl::readIncidence(icalcomponent *parent, const Incidence::Ptr &incidence, const ICalTimeZoneCache *tzlist) { d->readIncidenceBase(parent, incidence); icalproperty *p = icalcomponent_get_first_property(parent, ICAL_ANY_PROPERTY); const char *text; int intvalue, inttext; icaldurationtype icalduration; QDateTime kdt; QDateTime dtstamp; QStringList categories; while (p) { icalproperty_kind kind = icalproperty_isa(p); switch (kind) { case ICAL_CREATED_PROPERTY: incidence->setCreated(readICalDateTimeProperty(p, tzlist)); break; case ICAL_DTSTAMP_PROPERTY: dtstamp = readICalDateTimeProperty(p, tzlist); break; case ICAL_SEQUENCE_PROPERTY: // sequence intvalue = icalproperty_get_sequence(p); incidence->setRevision(intvalue); break; case ICAL_LASTMODIFIED_PROPERTY: // last modification UTC date/time incidence->setLastModified(readICalDateTimeProperty(p, tzlist)); break; case ICAL_DTSTART_PROPERTY: { // start date and time bool allDay = false; kdt = readICalDateTimeProperty(p, tzlist, false, &allDay); incidence->setDtStart(kdt); incidence->setAllDay(allDay); break; } case ICAL_DURATION_PROPERTY: // start date and time icalduration = icalproperty_get_duration(p); incidence->setDuration(readICalDuration(icalduration)); break; case ICAL_DESCRIPTION_PROPERTY: { // description QString textStr = QString::fromUtf8(icalproperty_get_description(p)); if (!textStr.isEmpty()) { QString valStr = QString::fromUtf8( icalproperty_get_parameter_as_string(p, "X-KDE-TEXTFORMAT")); if (!valStr.compare(QLatin1String("HTML"), Qt::CaseInsensitive)) { incidence->setDescription(textStr, true); } else { incidence->setDescription(textStr, false); } } } break; case ICAL_SUMMARY_PROPERTY: { // summary QString textStr = QString::fromUtf8(icalproperty_get_summary(p)); if (!textStr.isEmpty()) { QString valStr = QString::fromUtf8( icalproperty_get_parameter_as_string(p, "X-KDE-TEXTFORMAT")); if (!valStr.compare(QLatin1String("HTML"), Qt::CaseInsensitive)) { incidence->setSummary(textStr, true); } else { incidence->setSummary(textStr, false); } } } break; case ICAL_LOCATION_PROPERTY: { // location if (!icalproperty_get_value(p)) { //Fix for #191472. This is a pre-crash guard in case libical was //compiled in superstrict mode (--enable-icalerrors-are-fatal) //TODO: pre-crash guard other property getters too. break; } QString textStr = QString::fromUtf8(icalproperty_get_location(p)); if (!textStr.isEmpty()) { QString valStr = QString::fromUtf8( icalproperty_get_parameter_as_string(p, "X-KDE-TEXTFORMAT")); if (!valStr.compare(QLatin1String("HTML"), Qt::CaseInsensitive)) { incidence->setLocation(textStr, true); } else { incidence->setLocation(textStr, false); } } } break; case ICAL_STATUS_PROPERTY: { // status Incidence::Status stat; switch (icalproperty_get_status(p)) { case ICAL_STATUS_TENTATIVE: stat = Incidence::StatusTentative; break; case ICAL_STATUS_CONFIRMED: stat = Incidence::StatusConfirmed; break; case ICAL_STATUS_COMPLETED: stat = Incidence::StatusCompleted; break; case ICAL_STATUS_NEEDSACTION: stat = Incidence::StatusNeedsAction; break; case ICAL_STATUS_CANCELLED: stat = Incidence::StatusCanceled; break; case ICAL_STATUS_INPROCESS: stat = Incidence::StatusInProcess; break; case ICAL_STATUS_DRAFT: stat = Incidence::StatusDraft; break; case ICAL_STATUS_FINAL: stat = Incidence::StatusFinal; break; case ICAL_STATUS_X: incidence->setCustomStatus( QString::fromUtf8(icalvalue_get_x(icalproperty_get_value(p)))); stat = Incidence::StatusX; break; case ICAL_STATUS_NONE: default: stat = Incidence::StatusNone; break; } if (stat != Incidence::StatusX) { incidence->setStatus(stat); } break; } case ICAL_GEO_PROPERTY: { // geo icalgeotype geo = icalproperty_get_geo(p); incidence->setGeoLatitude(geo.lat); incidence->setGeoLongitude(geo.lon); incidence->setHasGeo(true); break; } case ICAL_PRIORITY_PROPERTY: // priority intvalue = icalproperty_get_priority(p); if (d->mCompat) { intvalue = d->mCompat->fixPriority(intvalue); } incidence->setPriority(intvalue); break; case ICAL_CATEGORIES_PROPERTY: { // categories // We have always supported multiple CATEGORIES properties per component // even though the RFC seems to indicate only 1 is permitted. // We can't change that -- in order to retain backwards compatibility. text = icalproperty_get_categories(p); const QString val = QString::fromUtf8(text); const QStringList lstVal = val.split(QLatin1Char(','), QString::SkipEmptyParts); for (const QString &cat : lstVal) { // ensure no duplicates if (!categories.contains(cat)) { categories.append(cat); } } break; } case ICAL_RECURRENCEID_PROPERTY: // recurrenceId kdt = readICalDateTimeProperty(p, tzlist); if (kdt.isValid()) { incidence->setRecurrenceId(kdt); const icalparameter *param = icalproperty_get_first_parameter(p, ICAL_RANGE_PARAMETER); if (param && icalparameter_get_range(param) == ICAL_RANGE_THISANDFUTURE) { incidence->setThisAndFuture(true); } else { // A workaround for a bug in libical (https://github.com/libical/libical/issues/185) // If a recurrenceId has both tzid and range, both parameters end up in the tzid. // This results in invalid tzid's like: "Europe/Berlin;RANGE=THISANDFUTURE" const icalparameter *param = icalproperty_get_first_parameter(p, ICAL_TZID_PARAMETER); QString tzid = QString::fromLatin1(icalparameter_get_tzid(param)); const QStringList parts = tzid.toLower().split(QLatin1Char(';')); for (const QString &part : parts) { if (part == QLatin1String("range=thisandfuture")) { incidence->setThisAndFuture(true); break; } } } } break; case ICAL_RRULE_PROPERTY: readRecurrenceRule(p, incidence); break; case ICAL_RDATE_PROPERTY: { bool allDay = false; kdt = readICalDateTimeProperty(p, tzlist, false, &allDay); if (kdt.isValid()) { if (allDay) { incidence->recurrence()->addRDate(kdt.date()); } else { incidence->recurrence()->addRDateTime(kdt); } } else { // TODO: RDates as period are not yet implemented! } break; } case ICAL_EXRULE_PROPERTY: readExceptionRule(p, incidence); break; case ICAL_EXDATE_PROPERTY: { bool allDay = false; kdt = readICalDateTimeProperty(p, tzlist, false, &allDay); if (allDay) { incidence->recurrence()->addExDate(kdt.date()); } else { incidence->recurrence()->addExDateTime(kdt); } break; } case ICAL_CLASS_PROPERTY: inttext = icalproperty_get_class(p); if (inttext == ICAL_CLASS_PUBLIC) { incidence->setSecrecy(Incidence::SecrecyPublic); } else if (inttext == ICAL_CLASS_CONFIDENTIAL) { incidence->setSecrecy(Incidence::SecrecyConfidential); } else { incidence->setSecrecy(Incidence::SecrecyPrivate); } break; case ICAL_ATTACH_PROPERTY: // attachments incidence->addAttachment(readAttachment(p)); break; default: // TODO: do something about unknown properties? break; } p = icalcomponent_get_next_property(parent, ICAL_ANY_PROPERTY); } // Set the scheduling ID const QString uid = incidence->customProperty("LIBKCAL", "ID"); if (!uid.isNull()) { // The UID stored in incidencebase is actually the scheduling ID // It has to be stored in the iCal UID component for compatibility // with other iCal applications incidence->setSchedulingID(incidence->uid(), uid); } // Now that recurrence and exception stuff is completely set up, // do any backwards compatibility adjustments. if (incidence->recurs() && d->mCompat) { d->mCompat->fixRecurrence(incidence); } // add categories incidence->setCategories(categories); // iterate through all alarms for (icalcomponent *alarm = icalcomponent_get_first_component(parent, ICAL_VALARM_COMPONENT); alarm; alarm = icalcomponent_get_next_component(parent, ICAL_VALARM_COMPONENT)) { readAlarm(alarm, incidence); } if (d->mCompat) { // Fix incorrect alarm settings by other applications (like outloook 9) d->mCompat->fixAlarms(incidence); d->mCompat->setCreatedToDtStamp(incidence, dtstamp); } } //@cond PRIVATE void ICalFormatImpl::Private::readIncidenceBase(icalcomponent *parent, const IncidenceBase::Ptr &incidenceBase) { icalproperty *p = icalcomponent_get_first_property(parent, ICAL_ANY_PROPERTY); bool uidProcessed = false; while (p) { icalproperty_kind kind = icalproperty_isa(p); switch (kind) { case ICAL_UID_PROPERTY: // unique id uidProcessed = true; incidenceBase->setUid(QString::fromUtf8(icalproperty_get_uid(p))); break; case ICAL_ORGANIZER_PROPERTY: // organizer incidenceBase->setOrganizer(mImpl->readOrganizer(p)); break; case ICAL_ATTENDEE_PROPERTY: // attendee incidenceBase->addAttendee(mImpl->readAttendee(p)); break; case ICAL_COMMENT_PROPERTY: incidenceBase->addComment( QString::fromUtf8(icalproperty_get_comment(p))); break; case ICAL_CONTACT_PROPERTY: incidenceBase->addContact( QString::fromUtf8(icalproperty_get_contact(p))); break; case ICAL_URL_PROPERTY: incidenceBase->setUrl( QUrl(QString::fromUtf8(icalproperty_get_url(p)))); break; default: break; } p = icalcomponent_get_next_property(parent, ICAL_ANY_PROPERTY); } if (!uidProcessed) { qCWarning(KCALCORE_LOG) << "The incidence didn't have any UID! Report a bug " << "to the application that generated this file." << endl; // Our in-memory incidence has a random uid generated in Event's ctor. // Make it empty so it matches what's in the file: incidenceBase->setUid(QString()); // Otherwise, next time we read the file, this function will return // an event with another random uid and we will have two events in the calendar. } // custom properties readCustomProperties(parent, incidenceBase.data()); } void ICalFormatImpl::Private::readCustomProperties(icalcomponent *parent, CustomProperties *properties) { QByteArray property; QString value, parameters; icalproperty *p = icalcomponent_get_first_property(parent, ICAL_X_PROPERTY); icalparameter *param = nullptr; while (p) { QString nvalue = QString::fromUtf8(icalproperty_get_x(p)); if (nvalue.isEmpty()) { icalvalue *value = icalproperty_get_value(p); if (icalvalue_isa(value) == ICAL_TEXT_VALUE) { // Calling icalvalue_get_text( value ) on a datetime value crashes. nvalue = QString::fromUtf8(icalvalue_get_text(value)); } else { p = icalcomponent_get_next_property(parent, ICAL_X_PROPERTY); continue; } } const char *name = icalproperty_get_x_name(p); QByteArray nproperty(name); if (property != nproperty) { // New property if (!property.isEmpty()) { properties->setNonKDECustomProperty(property, value, parameters); } property = name; value = nvalue; QStringList parametervalues; for (param = icalproperty_get_first_parameter(p, ICAL_ANY_PARAMETER); param; param = icalproperty_get_next_parameter(p, ICAL_ANY_PARAMETER)) { // 'c' is owned by ical library => all we need to do is just use it const char *c = icalparameter_as_ical_string(param); parametervalues.push_back(QLatin1String(c)); } parameters = parametervalues.join(QLatin1Char(';')); } else { value = value.append(QLatin1Char(',')).append(nvalue); } p = icalcomponent_get_next_property(parent, ICAL_X_PROPERTY); } if (!property.isEmpty()) { properties->setNonKDECustomProperty(property, value, parameters); } } //@endcond void ICalFormatImpl::readRecurrenceRule(icalproperty *rrule, const Incidence::Ptr &incidence) { Recurrence *recur = incidence->recurrence(); struct icalrecurrencetype r = icalproperty_get_rrule(rrule); // dumpIcalRecurrence(r); RecurrenceRule *recurrule = new RecurrenceRule(/*incidence*/); recurrule->setStartDt(incidence->dtStart()); readRecurrence(r, recurrule); recur->addRRule(recurrule); } void ICalFormatImpl::readExceptionRule(icalproperty *rrule, const Incidence::Ptr &incidence) { struct icalrecurrencetype r = icalproperty_get_exrule(rrule); // dumpIcalRecurrence(r); RecurrenceRule *recurrule = new RecurrenceRule(/*incidence*/); recurrule->setStartDt(incidence->dtStart()); readRecurrence(r, recurrule); Recurrence *recur = incidence->recurrence(); recur->addExRule(recurrule); } void ICalFormatImpl::readRecurrence(const struct icalrecurrencetype &r, RecurrenceRule *recur) { // Generate the RRULE string recur->setRRule( QLatin1String(icalrecurrencetype_as_string(const_cast(&r)))); // Period switch (r.freq) { case ICAL_SECONDLY_RECURRENCE: recur->setRecurrenceType(RecurrenceRule::rSecondly); break; case ICAL_MINUTELY_RECURRENCE: recur->setRecurrenceType(RecurrenceRule::rMinutely); break; case ICAL_HOURLY_RECURRENCE: recur->setRecurrenceType(RecurrenceRule::rHourly); break; case ICAL_DAILY_RECURRENCE: recur->setRecurrenceType(RecurrenceRule::rDaily); break; case ICAL_WEEKLY_RECURRENCE: recur->setRecurrenceType(RecurrenceRule::rWeekly); break; case ICAL_MONTHLY_RECURRENCE: recur->setRecurrenceType(RecurrenceRule::rMonthly); break; case ICAL_YEARLY_RECURRENCE: recur->setRecurrenceType(RecurrenceRule::rYearly); break; case ICAL_NO_RECURRENCE: default: recur->setRecurrenceType(RecurrenceRule::rNone); } // Frequency recur->setFrequency(r.interval); // Duration & End Date if (!icaltime_is_null_time(r.until)) { icaltimetype t = r.until; recur->setEndDt(readICalUtcDateTime(nullptr, t)); } else { if (r.count == 0) { recur->setDuration(-1); } else { recur->setDuration(r.count); } } // Week start setting short wkst = static_cast((r.week_start + 5) % 7 + 1); recur->setWeekStart(wkst); // And now all BY* QList lst; int i; int index = 0; //@cond PRIVATE #define readSetByList( rrulecomp, setfunc ) \ index = 0; \ lst.clear(); \ while ( ( i = r.rrulecomp[index++] ) != ICAL_RECURRENCE_ARRAY_MAX ) { \ lst.append( i ); \ } \ if ( !lst.isEmpty() ) { \ recur->setfunc( lst ); \ } //@endcond // BYSECOND, MINUTE and HOUR, MONTHDAY, YEARDAY, WEEKNUMBER, MONTH // and SETPOS are standard int lists, so we can treat them with the // same macro readSetByList(by_second, setBySeconds); readSetByList(by_minute, setByMinutes); readSetByList(by_hour, setByHours); readSetByList(by_month_day, setByMonthDays); readSetByList(by_year_day, setByYearDays); readSetByList(by_week_no, setByWeekNumbers); readSetByList(by_month, setByMonths); readSetByList(by_set_pos, setBySetPos); #undef readSetByList // BYDAY is a special case, since it's not an int list QList wdlst; short day; index = 0; while ((day = r.by_day[index++]) != ICAL_RECURRENCE_ARRAY_MAX) { RecurrenceRule::WDayPos pos; pos.setDay(static_cast((icalrecurrencetype_day_day_of_week(day) + 5) % 7 + 1)); pos.setPos(icalrecurrencetype_day_position(day)); wdlst.append(pos); } if (!wdlst.isEmpty()) { recur->setByDays(wdlst); } // TODO: Store all X- fields of the RRULE inside the recurrence (so they are // preserved } void ICalFormatImpl::readAlarm(icalcomponent *alarm, const Incidence::Ptr &incidence) { Alarm::Ptr ialarm = incidence->newAlarm(); ialarm->setRepeatCount(0); ialarm->setEnabled(true); // Determine the alarm's action type icalproperty *p = icalcomponent_get_first_property(alarm, ICAL_ACTION_PROPERTY); Alarm::Type type = Alarm::Display; icalproperty_action action = ICAL_ACTION_DISPLAY; if (!p) { qCDebug(KCALCORE_LOG) << "Unknown type of alarm, using default"; // TODO: do something about unknown alarm type? } else { action = icalproperty_get_action(p); switch (action) { case ICAL_ACTION_DISPLAY: type = Alarm::Display; break; case ICAL_ACTION_AUDIO: type = Alarm::Audio; break; case ICAL_ACTION_PROCEDURE: type = Alarm::Procedure; break; case ICAL_ACTION_EMAIL: type = Alarm::Email; break; default: break; // TODO: do something about invalid alarm type? } } ialarm->setType(type); p = icalcomponent_get_first_property(alarm, ICAL_ANY_PROPERTY); while (p) { icalproperty_kind kind = icalproperty_isa(p); switch (kind) { case ICAL_TRIGGER_PROPERTY: { icaltriggertype trigger = icalproperty_get_trigger(p); if (!icaltime_is_null_time(trigger.time)) { //set the trigger to a specific time (which is not in rfc2445, btw) ialarm->setTime(readICalUtcDateTime(p, trigger.time)); } else { //set the trigger to an offset from the incidence start or end time. if (!icaldurationtype_is_bad_duration(trigger.duration)) { Duration duration(readICalDuration(trigger.duration)); icalparameter *param = icalproperty_get_first_parameter(p, ICAL_RELATED_PARAMETER); if (param && icalparameter_get_related(param) == ICAL_RELATED_END) { ialarm->setEndOffset(duration); } else { ialarm->setStartOffset(duration); } } else { // a bad duration was encountered, just set a 0 duration from start ialarm->setStartOffset(Duration(0)); } } break; } case ICAL_DURATION_PROPERTY: { icaldurationtype duration = icalproperty_get_duration(p); ialarm->setSnoozeTime(readICalDuration(duration)); break; } case ICAL_REPEAT_PROPERTY: ialarm->setRepeatCount(icalproperty_get_repeat(p)); break; case ICAL_DESCRIPTION_PROPERTY: { // Only in DISPLAY and EMAIL and PROCEDURE alarms QString description = QString::fromUtf8(icalproperty_get_description(p)); switch (action) { case ICAL_ACTION_DISPLAY: ialarm->setText(description); break; case ICAL_ACTION_PROCEDURE: ialarm->setProgramArguments(description); break; case ICAL_ACTION_EMAIL: ialarm->setMailText(description); break; default: break; } break; } case ICAL_SUMMARY_PROPERTY: // Only in EMAIL alarm ialarm->setMailSubject(QString::fromUtf8(icalproperty_get_summary(p))); break; case ICAL_ATTENDEE_PROPERTY: { // Only in EMAIL alarm QString email = QString::fromUtf8(icalproperty_get_attendee(p)); if (email.startsWith(QLatin1String("mailto:"), Qt::CaseInsensitive)) { email = email.mid(7); } QString name; icalparameter *param = icalproperty_get_first_parameter(p, ICAL_CN_PARAMETER); if (param) { name = QString::fromUtf8(icalparameter_get_cn(param)); } ialarm->addMailAddress(Person(name, email)); break; } case ICAL_ATTACH_PROPERTY: { // Only in AUDIO and EMAIL and PROCEDURE alarms Attachment attach = readAttachment(p); if (!attach.isEmpty() && attach.isUri()) { switch (action) { case ICAL_ACTION_AUDIO: ialarm->setAudioFile(attach.uri()); break; case ICAL_ACTION_PROCEDURE: ialarm->setProgramFile(attach.uri()); break; case ICAL_ACTION_EMAIL: ialarm->addMailAttachment(attach.uri()); break; default: break; } } else { qCDebug(KCALCORE_LOG) << "Alarm attachments currently only support URIs," << "but no binary data"; } break; } default: break; } p = icalcomponent_get_next_property(alarm, ICAL_ANY_PROPERTY); } // custom properties d->readCustomProperties(alarm, ialarm.data()); QString locationRadius = ialarm->nonKDECustomProperty("X-LOCATION-RADIUS"); if (!locationRadius.isEmpty()) { ialarm->setLocationRadius(locationRadius.toInt()); ialarm->setHasLocationRadius(true); } if (ialarm->customProperty(APP_NAME_FOR_XPROPERTIES, ENABLED_ALARM_XPROPERTY) == QLatin1String("FALSE")) { ialarm->setEnabled(false); } // TODO: check for consistency of alarm properties } icaldatetimeperiodtype ICalFormatImpl::writeICalDatePeriod(const QDate &date) { icaldatetimeperiodtype t; t.time = writeICalDate(date); t.period = icalperiodtype_null_period(); return t; } icaltimetype ICalFormatImpl::writeICalDate(const QDate &date) { icaltimetype t = icaltime_null_time(); t.year = date.year(); t.month = date.month(); t.day = date.day(); t.hour = 0; t.minute = 0; t.second = 0; t.is_date = 1; #if !defined(USE_ICAL_3) t.is_utc = 0; #endif t.zone = nullptr; return t; } icaltimetype ICalFormatImpl::writeICalDateTime(const QDateTime &datetime, bool dateOnly) { icaltimetype t = icaltime_null_time(); t.year = datetime.date().year(); t.month = datetime.date().month(); t.day = datetime.date().day(); t.is_date = dateOnly; if (!t.is_date) { t.hour = datetime.time().hour(); t.minute = datetime.time().minute(); t.second = datetime.time().second(); } t.zone = nullptr; // zone is NOT set #if defined(USE_ICAL_3) if (datetime.timeSpec() == Qt::UTC || (datetime.timeSpec() == Qt::OffsetFromUTC && datetime.offsetFromUtc() == 0)) { t = icaltime_convert_to_zone(t, icaltimezone_get_utc_timezone()); } #else t.is_utc = datetime.timeSpec() == Qt::UTC || (datetime.timeSpec() == Qt::OffsetFromUTC && datetime.offsetFromUtc() == 0); #endif return t; } icalproperty *ICalFormatImpl::writeICalDateTimeProperty(const icalproperty_kind type, const QDateTime &dt, TimeZoneList *tzUsedList) { icaltimetype t; switch (type) { case ICAL_DTSTAMP_PROPERTY: case ICAL_CREATED_PROPERTY: case ICAL_LASTMODIFIED_PROPERTY: t = writeICalDateTime(dt.toUTC()); break; default: t = writeICalDateTime(dt); break; } icalproperty *p; switch (type) { case ICAL_DTSTAMP_PROPERTY: p = icalproperty_new_dtstamp(t); break; case ICAL_CREATED_PROPERTY: p = icalproperty_new_created(t); break; case ICAL_LASTMODIFIED_PROPERTY: p = icalproperty_new_lastmodified(t); break; case ICAL_DTSTART_PROPERTY: // start date and time p = icalproperty_new_dtstart(t); break; case ICAL_DTEND_PROPERTY: // end date and time p = icalproperty_new_dtend(t); break; case ICAL_DUE_PROPERTY: p = icalproperty_new_due(t); break; case ICAL_RECURRENCEID_PROPERTY: p = icalproperty_new_recurrenceid(t); break; case ICAL_EXDATE_PROPERTY: p = icalproperty_new_exdate(t); break; case ICAL_X_PROPERTY: { p = icalproperty_new_x(""); icaltimetype timeType = writeICalDateTime(dt); icalvalue *text = icalvalue_new_datetime(timeType); icalproperty_set_value(p, text); } break; default: { icaldatetimeperiodtype tp; tp.time = t; tp.period = icalperiodtype_null_period(); switch (type) { case ICAL_RDATE_PROPERTY: p = icalproperty_new_rdate(tp); break; default: return nullptr; } } } QTimeZone qtz; #if defined(USE_ICAL_3) if (!icaltime_is_utc(t)) { #else if (!t.is_utc) { #endif qtz = dt.timeZone(); } if (qtz.isValid()) { if (tzUsedList) { if (!tzUsedList->contains(qtz)) { tzUsedList->push_back(qtz); } } icalproperty_add_parameter( p, icalparameter_new_tzid(qtz.id().constData())); } return p; } QDateTime ICalFormatImpl::readICalDateTime(icalproperty *p, const icaltimetype &t, const ICalTimeZoneCache *tzCache, bool utc) { // qCDebug(KCALCORE_LOG); // _dumpIcaltime( t ); QTimeZone timeZone; #if defined(USE_ICAL_3) if (icaltime_is_utc(t) || t.zone == icaltimezone_get_utc_timezone()) { #else if (t.is_utc || t.zone == icaltimezone_get_utc_timezone()) { #endif timeZone = QTimeZone::utc(); // the time zone is UTC utc = false; // no need to convert to UTC } else { icalparameter *param = p ? icalproperty_get_first_parameter(p, ICAL_TZID_PARAMETER) : nullptr; QByteArray tzid = param ? QByteArray(icalparameter_get_tzid(param)) : QByteArray(); // A workaround for a bug in libical (https://github.com/libical/libical/issues/185) // If a recurrenceId has both tzid and range, both parameters end up in the tzid. // This results in invalid tzid's like: "Europe/Berlin;RANGE=THISANDFUTURE" QStringList parts = QString::fromLatin1(tzid).split(QLatin1Char(';')); if (parts.count() > 1) { tzid = parts.first().toLatin1(); } if (tzCache) { // First try to get the timezone from cache timeZone = tzCache->tzForTime(QDateTime({ t.year, t.month, t.day }, {}), tzid); } if (!timeZone.isValid()) { // Fallback to trying to match against Qt timezone timeZone = QTimeZone(tzid); } if (!timeZone.isValid()) { // Finally, give up and assume local timezone timeZone = QTimeZone::systemTimeZone(); } } QDateTime result; if (t.is_date) { result = QDateTime(QDate(t.year, t.month, t.day), {}, timeZone); } else { result = QDateTime(QDate(t.year, t.month, t.day), QTime(t.hour, t.minute, t.second), timeZone); } return utc ? result.toUTC() : result; } QDate ICalFormatImpl::readICalDate(const icaltimetype &t) { return QDate(t.year, t.month, t.day); } QDateTime ICalFormatImpl::readICalDateTimeProperty(icalproperty *p, const ICalTimeZoneCache *tzList, bool utc, bool *allDay) { icaldatetimeperiodtype tp; icalproperty_kind kind = icalproperty_isa(p); switch (kind) { case ICAL_CREATED_PROPERTY: // UTC date/time tp.time = icalproperty_get_created(p); utc = true; break; case ICAL_DTSTAMP_PROPERTY: // UTC date/time tp.time = icalproperty_get_dtstamp(p); utc = true; break; case ICAL_LASTMODIFIED_PROPERTY: // last modification UTC date/time tp.time = icalproperty_get_lastmodified(p); utc = true; break; case ICAL_DTSTART_PROPERTY: // start date and time (UTC for freebusy) tp.time = icalproperty_get_dtstart(p); break; case ICAL_DTEND_PROPERTY: // end date and time (UTC for freebusy) tp.time = icalproperty_get_dtend(p); break; case ICAL_DUE_PROPERTY: // due date/time tp.time = icalproperty_get_due(p); break; case ICAL_COMPLETED_PROPERTY: // UTC completion date/time tp.time = icalproperty_get_completed(p); utc = true; break; case ICAL_RECURRENCEID_PROPERTY: tp.time = icalproperty_get_recurrenceid(p); break; case ICAL_EXDATE_PROPERTY: tp.time = icalproperty_get_exdate(p); break; case ICAL_X_PROPERTY: { const char *name = icalproperty_get_x_name(p); if (QLatin1String(name) == QLatin1String("X-KDE-LIBKCAL-DTRECURRENCE")) { const char *value = icalvalue_as_ical_string(icalproperty_get_value(p)); icalvalue *v = icalvalue_new_from_string(ICAL_DATETIME_VALUE, value); tp.time = icalvalue_get_datetime(v); icalvalue_free(v); break; } } //end of ICAL_X_PROPERTY Q_FALLTHROUGH(); default: switch (kind) { case ICAL_RDATE_PROPERTY: tp = icalproperty_get_rdate(p); break; default: return QDateTime(); } if (!icaltime_is_valid_time(tp.time)) { return QDateTime(); // a time period was found (not implemented yet) } break; } if (allDay) { *allDay = tp.time.is_date; } if (tp.time.is_date) { return QDateTime(readICalDate(tp.time), QTime()); } else { return readICalDateTime(p, tp.time, tzList, utc); } } icaldurationtype ICalFormatImpl::writeICalDuration(const Duration &duration) { // should be able to use icaldurationtype_from_int(), except we know // that some older tools do not properly support weeks. So we never // set a week duration, only days icaldurationtype d; int value = duration.value(); d.is_neg = (value < 0) ? 1 : 0; if (value < 0) { value = -value; } // RFC2445 states that an ical duration value must be // EITHER weeks OR days/time, not both. if (duration.isDaily()) { if (!(value % 7)) { d.weeks = value / 7; d.days = 0; } else { d.weeks = 0; d.days = value; } d.hours = d.minutes = d.seconds = 0; } else { if (!(value % gSecondsPerWeek)) { d.weeks = value / gSecondsPerWeek; d.days = d.hours = d.minutes = d.seconds = 0; } else { d.weeks = 0; d.days = value / gSecondsPerDay; value %= gSecondsPerDay; d.hours = value / gSecondsPerHour; value %= gSecondsPerHour; d.minutes = value / gSecondsPerMinute; value %= gSecondsPerMinute; d.seconds = value; } } return d; } Duration ICalFormatImpl::readICalDuration(const icaldurationtype &d) { int days = d.weeks * 7; days += d.days; int seconds = d.hours * gSecondsPerHour; seconds += d.minutes * gSecondsPerMinute; seconds += d.seconds; if (seconds || !days) { // Create second-type duration for 0 delay durations. seconds += days * gSecondsPerDay; if (d.is_neg) { seconds = -seconds; } return Duration(seconds, Duration::Seconds); } else { if (d.is_neg) { days = -days; } return Duration(days, Duration::Days); } } icalcomponent *ICalFormatImpl::createCalendarComponent(const Calendar::Ptr &cal) { icalcomponent *calendar; // Root component calendar = icalcomponent_new(ICAL_VCALENDAR_COMPONENT); // Product Identifier icalproperty *p = icalproperty_new_prodid(CalFormat::productId().toUtf8().constData()); icalcomponent_add_property(calendar, p); // iCalendar version (2.0) p = icalproperty_new_version(const_cast(_ICAL_VERSION)); icalcomponent_add_property(calendar, p); // Implementation Version p = icalproperty_new_x(_ICAL_IMPLEMENTATION_VERSION); icalproperty_set_x_name(p, IMPLEMENTATION_VERSION_XPROPERTY); icalcomponent_add_property(calendar, p); // Add time zone // NOTE: Commented out since relevant timezones are added by the caller. // Previously we got some timezones listed twice in the ical file. /* if ( cal && cal->timeZones() ) { const ICalTimeZones::ZoneMap zmaps = cal->timeZones()->zones(); for ( ICalTimeZones::ZoneMap::ConstIterator it=zmaps.constBegin(); it != zmaps.constEnd(); ++it ) { icaltimezone *icaltz = (*it).icalTimezone(); if ( !icaltz ) { qCritical() << "bad time zone"; } else { icalcomponent *tz = icalcomponent_new_clone( icaltimezone_get_component( icaltz ) ); icalcomponent_add_component( calendar, tz ); icaltimezone_free( icaltz, 1 ); } } } */ // Custom properties if (cal != nullptr) { d->writeCustomProperties(calendar, cal.data()); } return calendar; } Incidence::Ptr ICalFormatImpl::readOneIncidence(icalcomponent *calendar, const ICalTimeZoneCache *tzlist) { if (!calendar) { qCWarning(KCALCORE_LOG) << "Populate called with empty calendar"; return Incidence::Ptr(); } icalcomponent *c = icalcomponent_get_first_component(calendar, ICAL_VEVENT_COMPONENT); if (c) { return readEvent(c, tzlist); } c = icalcomponent_get_first_component(calendar, ICAL_VTODO_COMPONENT); if (c) { return readTodo(c, tzlist); } c = icalcomponent_get_first_component(calendar, ICAL_VJOURNAL_COMPONENT); if (c) { return readJournal(c, tzlist); } qCWarning(KCALCORE_LOG) << "Found no incidence"; return Incidence::Ptr(); } // take a raw vcalendar (i.e. from a file on disk, clipboard, etc. etc. // and break it down from its tree-like format into the dictionary format // that is used internally in the ICalFormatImpl. bool ICalFormatImpl::populate(const Calendar::Ptr &cal, icalcomponent *calendar, bool deleted, const QString ¬ebook) { Q_UNUSED(notebook); // qCDebug(KCALCORE_LOG)<<"Populate called"; // this function will populate the caldict dictionary and other event // lists. It turns vevents into Events and then inserts them. if (!calendar) { qCWarning(KCALCORE_LOG) << "Populate called with empty calendar"; return false; } // TODO: check for METHOD icalproperty *p = icalcomponent_get_first_property(calendar, ICAL_X_PROPERTY); QString implementationVersion; while (p) { const char *name = icalproperty_get_x_name(p); QByteArray nproperty(name); if (nproperty == QByteArray(IMPLEMENTATION_VERSION_XPROPERTY)) { QString nvalue = QString::fromUtf8(icalproperty_get_x(p)); if (nvalue.isEmpty()) { icalvalue *value = icalproperty_get_value(p); if (icalvalue_isa(value) == ICAL_TEXT_VALUE) { nvalue = QString::fromUtf8(icalvalue_get_text(value)); } } implementationVersion = nvalue; icalcomponent_remove_property(calendar, p); icalproperty_free(p); } p = icalcomponent_get_next_property(calendar, ICAL_X_PROPERTY); } p = icalcomponent_get_first_property(calendar, ICAL_PRODID_PROPERTY); if (!p) { qCDebug(KCALCORE_LOG) << "No PRODID property found"; d->mLoadedProductId.clear(); } else { d->mLoadedProductId = QString::fromUtf8(icalproperty_get_prodid(p)); delete d->mCompat; d->mCompat = CompatFactory::createCompat(d->mLoadedProductId, implementationVersion); } p = icalcomponent_get_first_property(calendar, ICAL_VERSION_PROPERTY); if (!p) { qCDebug(KCALCORE_LOG) << "No VERSION property found"; d->mParent->setException(new Exception(Exception::CalVersionUnknown)); return false; } else { const char *version = icalproperty_get_version(p); if (!version) { qCDebug(KCALCORE_LOG) << "No VERSION property found"; d->mParent->setException(new Exception(Exception::VersionPropertyMissing)); return false; } if (strcmp(version, "1.0") == 0) { qCDebug(KCALCORE_LOG) << "Expected iCalendar, got vCalendar"; d->mParent->setException(new Exception(Exception::CalVersion1)); return false; } else if (strcmp(version, "2.0") != 0) { qCDebug(KCALCORE_LOG) << "Expected iCalendar, got unknown format"; d->mParent->setException(new Exception( Exception::CalVersionUnknown)); return false; } } // Populate the calendar's time zone collection with all VTIMEZONE components ICalTimeZoneCache timeZoneCache; ICalTimeZoneParser parser(&timeZoneCache); parser.parse(calendar); // custom properties d->readCustomProperties(calendar, cal.data()); // Store all events with a relatedTo property in a list for post-processing d->mEventsRelate.clear(); d->mTodosRelate.clear(); // TODO: make sure that only actually added events go to this lists. icalcomponent *c = icalcomponent_get_first_component(calendar, ICAL_VTODO_COMPONENT); while (c) { Todo::Ptr todo = readTodo(c, &timeZoneCache); if (todo) { // qCDebug(KCALCORE_LOG) << "todo is not zero and deleted is " << deleted; Todo::Ptr old = cal->todo(todo->uid(), todo->recurrenceId()); if (old) { if (old->uid().isEmpty()) { qCWarning(KCALCORE_LOG) << "Skipping invalid VTODO"; c = icalcomponent_get_next_component(calendar, ICAL_VTODO_COMPONENT); continue; } // qCDebug(KCALCORE_LOG) << "Found an old todo with uid " << old->uid(); if (deleted) { // qCDebug(KCALCORE_LOG) << "Todo " << todo->uid() << " already deleted"; cal->deleteTodo(old); // move old to deleted removeAllICal(d->mTodosRelate, old); } else if (todo->revision() > old->revision()) { // qCDebug(KCALCORE_LOG) << "Replacing old todo " << old.data() << " with this one " << todo.data(); cal->deleteTodo(old); // move old to deleted removeAllICal(d->mTodosRelate, old); cal->addTodo(todo); // and replace it with this one } } else if (deleted) { // qCDebug(KCALCORE_LOG) << "Todo " << todo->uid() << " already deleted"; old = cal->deletedTodo(todo->uid(), todo->recurrenceId()); if (!old) { cal->addTodo(todo); // add this one cal->deleteTodo(todo); // and move it to deleted } } else { // qCDebug(KCALCORE_LOG) << "Adding todo " << todo.data() << todo->uid(); cal->addTodo(todo); // just add this one } } c = icalcomponent_get_next_component(calendar, ICAL_VTODO_COMPONENT); } // Iterate through all events c = icalcomponent_get_first_component(calendar, ICAL_VEVENT_COMPONENT); while (c) { Event::Ptr event = readEvent(c, &timeZoneCache); if (event) { // qCDebug(KCALCORE_LOG) << "event is not zero and deleted is " << deleted; Event::Ptr old = cal->event(event->uid(), event->recurrenceId()); if (old) { if (old->uid().isEmpty()) { qCWarning(KCALCORE_LOG) << "Skipping invalid VEVENT"; c = icalcomponent_get_next_component(calendar, ICAL_VEVENT_COMPONENT); continue; } // qCDebug(KCALCORE_LOG) << "Found an old event with uid " << old->uid(); if (deleted) { // qCDebug(KCALCORE_LOG) << "Event " << event->uid() << " already deleted"; cal->deleteEvent(old); // move old to deleted removeAllICal(d->mEventsRelate, old); } else if (event->revision() > old->revision()) { // qCDebug(KCALCORE_LOG) << "Replacing old event " << old.data() // << " with this one " << event.data(); cal->deleteEvent(old); // move old to deleted removeAllICal(d->mEventsRelate, old); cal->addEvent(event); // and replace it with this one } } else if (deleted) { // qCDebug(KCALCORE_LOG) << "Event " << event->uid() << " already deleted"; old = cal->deletedEvent(event->uid(), event->recurrenceId()); if (!old) { cal->addEvent(event); // add this one cal->deleteEvent(event); // and move it to deleted } } else { // qCDebug(KCALCORE_LOG) << "Adding event " << event.data() << event->uid(); cal->addEvent(event); // just add this one } } c = icalcomponent_get_next_component(calendar, ICAL_VEVENT_COMPONENT); } // Iterate through all journals c = icalcomponent_get_first_component(calendar, ICAL_VJOURNAL_COMPONENT); while (c) { Journal::Ptr journal = readJournal(c, &timeZoneCache); if (journal) { Journal::Ptr old = cal->journal(journal->uid(), journal->recurrenceId()); if (old) { if (deleted) { cal->deleteJournal(old); // move old to deleted } else if (journal->revision() > old->revision()) { cal->deleteJournal(old); // move old to deleted cal->addJournal(journal); // and replace it with this one } } else if (deleted) { old = cal->deletedJournal(journal->uid(), journal->recurrenceId()); if (!old) { cal->addJournal(journal); // add this one cal->deleteJournal(journal); // and move it to deleted } } else { cal->addJournal(journal); // just add this one } } c = icalcomponent_get_next_component(calendar, ICAL_VJOURNAL_COMPONENT); } // TODO: Remove any previous time zones no longer referenced in the calendar return true; } QString ICalFormatImpl::extractErrorProperty(icalcomponent *c) { QString errorMessage; icalproperty *error = icalcomponent_get_first_property(c, ICAL_XLICERROR_PROPERTY); while (error) { errorMessage += QLatin1String(icalproperty_get_xlicerror(error)); errorMessage += QLatin1Char('\n'); error = icalcomponent_get_next_property(c, ICAL_XLICERROR_PROPERTY); } return errorMessage; } /* void ICalFormatImpl::dumpIcalRecurrence( const icalrecurrencetype &r ) { int i; qCDebug(KCALCORE_LOG) << " Freq:" << int( r.freq ); qCDebug(KCALCORE_LOG) << " Until:" << icaltime_as_ical_string( r.until ); qCDebug(KCALCORE_LOG) << " Count:" << r.count; if ( r.by_day[0] != ICAL_RECURRENCE_ARRAY_MAX ) { int index = 0; QString out = " By Day: "; while ( ( i = r.by_day[index++] ) != ICAL_RECURRENCE_ARRAY_MAX ) { out.append( QString::number( i ) + ' ' ); } qCDebug(KCALCORE_LOG) << out; } if ( r.by_month_day[0] != ICAL_RECURRENCE_ARRAY_MAX ) { int index = 0; QString out = " By Month Day: "; while ( ( i = r.by_month_day[index++] ) != ICAL_RECURRENCE_ARRAY_MAX ) { out.append( QString::number( i ) + ' ' ); } qCDebug(KCALCORE_LOG) << out; } if ( r.by_year_day[0] != ICAL_RECURRENCE_ARRAY_MAX ) { int index = 0; QString out = " By Year Day: "; while ( ( i = r.by_year_day[index++] ) != ICAL_RECURRENCE_ARRAY_MAX ) { out.append( QString::number( i ) + ' ' ); } qCDebug(KCALCORE_LOG) << out; } if ( r.by_month[0] != ICAL_RECURRENCE_ARRAY_MAX ) { int index = 0; QString out = " By Month: "; while ( ( i = r.by_month[index++] ) != ICAL_RECURRENCE_ARRAY_MAX ) { out.append( QString::number( i ) + ' ' ); } qCDebug(KCALCORE_LOG) << out; } if ( r.by_set_pos[0] != ICAL_RECURRENCE_ARRAY_MAX ) { int index = 0; QString out = " By Set Pos: "; while ( ( i = r.by_set_pos[index++] ) != ICAL_RECURRENCE_ARRAY_MAX ) { qCDebug(KCALCORE_LOG) << "=========" << i; out.append( QString::number( i ) + ' ' ); } qCDebug(KCALCORE_LOG) << out; } } */ icalcomponent *ICalFormatImpl::createScheduleComponent(const IncidenceBase::Ptr &incidence, iTIPMethod method) { icalcomponent *message = createCalendarComponent(); // Create VTIMEZONE components for this incidence TimeZoneList zones; if (incidence) { const QDateTime kd1 = incidence->dateTime(IncidenceBase::RoleStartTimeZone); const QDateTime kd2 = incidence->dateTime(IncidenceBase::RoleEndTimeZone); if (kd1.isValid() && kd1.timeZone() != QTimeZone::utc()) { zones << kd1.timeZone(); } if (kd2.isValid() && kd2.timeZone() != QTimeZone::utc() && kd1.timeZone() != kd2.timeZone()) { zones << kd2.timeZone(); } TimeZoneEarliestDate earliestTz; ICalTimeZoneParser::updateTzEarliestDate(incidence, &earliestTz); for (const auto &qtz : qAsConst(zones)) { icaltimezone *icaltz = ICalTimeZoneParser::icaltimezoneFromQTimeZone(qtz, earliestTz[qtz]); if (!icaltz) { qCritical() << "bad time zone"; } else { icalcomponent *tz = icalcomponent_new_clone(icaltimezone_get_component(icaltz)); icalcomponent_add_component(message, tz); icaltimezone_free(icaltz, 1); } } } else { qCDebug(KCALCORE_LOG) << "No incidence"; return message; } icalproperty_method icalmethod = ICAL_METHOD_NONE; switch (method) { case iTIPPublish: icalmethod = ICAL_METHOD_PUBLISH; break; case iTIPRequest: icalmethod = ICAL_METHOD_REQUEST; break; case iTIPRefresh: icalmethod = ICAL_METHOD_REFRESH; break; case iTIPCancel: icalmethod = ICAL_METHOD_CANCEL; break; case iTIPAdd: icalmethod = ICAL_METHOD_ADD; break; case iTIPReply: icalmethod = ICAL_METHOD_REPLY; break; case iTIPCounter: icalmethod = ICAL_METHOD_COUNTER; break; case iTIPDeclineCounter: icalmethod = ICAL_METHOD_DECLINECOUNTER; break; default: qCDebug(KCALCORE_LOG) << "Unknown method"; return message; } icalcomponent_add_property(message, icalproperty_new_method(icalmethod)); icalcomponent *inc = writeIncidence(incidence, method); - if (method != KCalCore::iTIPNoMethod) { + if (method != KCalendarCore::iTIPNoMethod) { //Not very nice, but since dtstamp changes semantics if used in scheduling, we have to adapt icalcomponent_set_dtstamp( inc, writeICalUtcDateTime(QDateTime::currentDateTimeUtc())); } /* * RFC 2446 states in section 3.4.3 ( REPLY to a VTODO ), that * a REQUEST-STATUS property has to be present. For the other two, event and * free busy, it can be there, but is optional. Until we do more * fine grained handling, assume all is well. Note that this is the * status of the _request_, not the attendee. Just to avoid confusion. * - till */ if (icalmethod == ICAL_METHOD_REPLY) { struct icalreqstattype rst; rst.code = ICAL_2_0_SUCCESS_STATUS; rst.desc = nullptr; rst.debug = nullptr; icalcomponent_add_property(inc, icalproperty_new_requeststatus(rst)); } icalcomponent_add_component(message, inc); return message; } diff --git a/src/icalformat_p.h b/src/icalformat_p.h index a50ffc0c3..1e5a2f0cd 100644 --- a/src/icalformat_p.h +++ b/src/icalformat_p.h @@ -1,257 +1,257 @@ /* This file is part of the kcalcore library. Copyright (c) 2001-2003 Cornelius Schumacher Copyright (C) 2003-2004 Reinhold Kainhofer Copyright (c) 2006 David Jarvie Copyright (C) 2012 Christian Mollekopf This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the internal ICalFormatImpl class. @author Cornelius Schumacher \ @author Reinhold Kainhofer \ @author David Jarvie \ */ #ifndef KCALCORE_ICALFORMAT_P_H #define KCALCORE_ICALFORMAT_P_H #include "exceptions.h" #include "freebusy.h" #include "todo.h" #include "journal.h" #include "event.h" #include "person.h" #include "calendar.h" #include "schedulemessage.h" #include class QDate; -namespace KCalCore +namespace KCalendarCore { class Alarm; class Attachment; class Attendee; class Duration; class Event; class FreeBusy; class ICalFormat; class ICalTimeZoneCache; class Incidence; class Journal; class Recurrence; class RecurrenceRule; class Todo; using TimeZoneList = QVector; /** Tell the Libical library that we are using ICal Version 2.0. @internal */ #define _ICAL_VERSION "2.0" /** Version of this library implementation @internal */ #define _ICAL_IMPLEMENTATION_VERSION "1.0" /** @brief This class provides the libical dependent functions for ICalFormat. This class implements the iCalendar format. It provides methods for loading/saving/converting iCalendar format data into the internal representation as Calendar and Incidences. @internal */ class ICalFormatImpl { public: /** Construct a new iCal format for calendar object. @param parent is a pointer to a valid ICalFormat object. */ explicit ICalFormatImpl(ICalFormat *parent); /** Destructor. */ virtual ~ICalFormatImpl(); /** Updates a calendar with data from a raw iCalendar. Incidences already existing in @p calendar are not affected except that if a new incidence with the same UID is found, the existing incidence is replaced. */ bool populate(const Calendar::Ptr &calendar, icalcomponent *fs, bool deleted = false, const QString ¬ebook = QString()); Incidence::Ptr readOneIncidence(icalcomponent *calendar, const ICalTimeZoneCache *tzlist); icalcomponent *writeIncidence(const IncidenceBase::Ptr &incidence, iTIPMethod method = iTIPRequest, TimeZoneList *tzUsedList = nullptr); icalcomponent *writeTodo(const Todo::Ptr &todo, TimeZoneList *tzUsedList = nullptr); icalcomponent *writeEvent(const Event::Ptr &event, TimeZoneList *tzUsedList = nullptr); icalcomponent *writeJournal(const Journal::Ptr &journal, TimeZoneList *tzUsedList = nullptr); icalcomponent *writeFreeBusy(const FreeBusy::Ptr &freebusy, iTIPMethod method = iTIPPublish); void writeIncidence(icalcomponent *parent, const Incidence::Ptr &incidence, TimeZoneList *tzUsedList = nullptr); icalproperty *writeDescription(const QString &description, bool isRich = false); icalproperty *writeSummary(const QString &summary, bool isRich = false); icalproperty *writeLocation(const QString &location, bool isRich = false); icalproperty *writeAttendee(const Attendee &attendee); icalproperty *writeOrganizer(const Person &organizer); icalproperty *writeAttachment(const Attachment &attach); icalproperty *writeRecurrenceRule(Recurrence *); icalrecurrencetype writeRecurrenceRule(RecurrenceRule *recur); icalcomponent *writeAlarm(const Alarm::Ptr &alarm); QString extractErrorProperty(icalcomponent *); Todo::Ptr readTodo(icalcomponent *vtodo, const ICalTimeZoneCache *tzList); Event::Ptr readEvent(icalcomponent *vevent, const ICalTimeZoneCache *tzList); FreeBusy::Ptr readFreeBusy(icalcomponent *vfreebusy); Journal::Ptr readJournal(icalcomponent *vjournal, const ICalTimeZoneCache *tzList); Attendee readAttendee(icalproperty *attendee); Person readOrganizer(icalproperty *organizer); Attachment readAttachment(icalproperty *attach); void readIncidence(icalcomponent *parent, const Incidence::Ptr &incidence, const ICalTimeZoneCache *tzList); void readRecurrenceRule(icalproperty *rrule, const Incidence::Ptr &event); void readExceptionRule(icalproperty *rrule, const Incidence::Ptr &incidence); void readRecurrence(const struct icalrecurrencetype &r, RecurrenceRule *recur); void readAlarm(icalcomponent *alarm, const Incidence::Ptr &incidence); /** Returns the PRODID string loaded from calendar file. */ QString loadedProductId() const; static icaltimetype writeICalDate(const QDate &); static QDate readICalDate(const icaltimetype &); static icaltimetype writeICalDateTime(const QDateTime &, bool dayOnly = false); static icaltimetype writeICalUtcDateTime(const QDateTime &, bool dayOnly = false); /** Creates an ical property from a date/time value. If a time zone is specified for the value, a TZID parameter is inserted into the ical property, @p tzlist and @p tzUsedList are updated to include the time zone. Note that while @p tzlist owns any time zone instances in its collection, @p tzUsedList does not. @param kind kind of property @param dt date/time value @param tzlist time zones collection @param tzUsedList time zones collection, only updated if @p tzlist is also specified @return property, or null if error. It is the caller's responsibility to free the returned property. */ static icalproperty *writeICalDateTimeProperty(const icalproperty_kind kind, const QDateTime &dt, TimeZoneList *tzUsedList = nullptr); /** Converts a date/time from ICal format. @param p property from which @p t has been obtained @param t ICal format date/time @param utc UTC date/time is expected @return date/time, converted to UTC if @p utc is @c true */ static QDateTime readICalDateTime(icalproperty *p, const icaltimetype &t, const ICalTimeZoneCache *tzList, bool utc = false); /** Converts a UTC date/time from ICal format. If @p t is not a UTC date/time, it is treated as invalid. @param p ical parameter to read from @param t ICal format date/time @return date/time, or invalid if @p t is not UTC */ static QDateTime readICalUtcDateTime(icalproperty *p, icaltimetype &t, const ICalTimeZoneCache *tzList = nullptr) { return readICalDateTime(p, t, tzList, true); } /** Reads a date or date/time value from a property. @param p ical parameter to read from @param utc true to read a UTC value, false to allow time zone to be specified. @return date or date/time, or invalid if property doesn't contain a time value. */ static QDateTime readICalDateTimeProperty(icalproperty *p, const ICalTimeZoneCache *tzList, bool utc = false, bool *allDay = nullptr); /** Reads a UTC date/time value from a property. @param p is a pointer to a valid icalproperty structure. */ static QDateTime readICalUtcDateTimeProperty(icalproperty *p, const ICalTimeZoneCache *tzList, bool *allDay = nullptr) { return readICalDateTimeProperty(p, tzList, true, allDay); } static icaldurationtype writeICalDuration(const Duration &duration); static Duration readICalDuration(const icaldurationtype &d); static icaldatetimeperiodtype writeICalDatePeriod(const QDate &date); icalcomponent *createCalendarComponent(const Calendar::Ptr &calendar = Calendar::Ptr()); icalcomponent *createScheduleComponent(const IncidenceBase::Ptr &incidence, iTIPMethod method); protected: // void dumpIcalRecurrence( const icalrecurrencetype &r ); private: //@cond PRIVATE class Private; Private *const d; //@endcond }; } #endif diff --git a/src/icaltimezones.cpp b/src/icaltimezones.cpp index 603fbe848..9ac543226 100644 --- a/src/icaltimezones.cpp +++ b/src/icaltimezones.cpp @@ -1,735 +1,735 @@ /* This file is part of the kcalcore library. Copyright (c) 2005-2007 David Jarvie This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "icaltimezones_p.h" #include "icalformat.h" #include "icalformat_p.h" #include "recurrence.h" #include "recurrencerule.h" #include "recurrencehelper_p.h" #include "utils.h" #include "kcalcore_debug.h" #include #include extern "C" { #include #include } -using namespace KCalCore; +using namespace KCalendarCore; // Minimum repetition counts for VTIMEZONE RRULEs static const int minRuleCount = 5; // for any RRULE static const int minPhaseCount = 8; // for separate STANDARD/DAYLIGHT component // Convert an ical time to QDateTime, preserving the UTC indicator static QDateTime toQDateTime(const icaltimetype &t) { return QDateTime(QDate(t.year, t.month, t.day), QTime(t.hour, t.minute, t.second), #if defined(USE_ICAL_3) (icaltime_is_utc(t) ? Qt::UTC : Qt::LocalTime)); #else (t.is_utc ? Qt::UTC : Qt::LocalTime)); #endif } // Maximum date for time zone data. // It's not sensible to try to predict them very far in advance, because // they can easily change. Plus, it limits the processing required. static QDateTime MAX_DATE() { static QDateTime dt; if (!dt.isValid()) { dt = QDateTime(QDate::currentDate().addYears(20), QTime(0, 0, 0)); } return dt; } static icaltimetype writeLocalICalDateTime(const QDateTime &utc, int offset) { const QDateTime local = utc.addSecs(offset); icaltimetype t = icaltime_null_time(); t.year = local.date().year(); t.month = local.date().month(); t.day = local.date().day(); t.hour = local.time().hour(); t.minute = local.time().minute(); t.second = local.time().second(); t.is_date = 0; t.zone = nullptr; #if !defined(USE_ICAL_3) t.is_utc = 0; #endif return t; } -namespace KCalCore +namespace KCalendarCore { void ICalTimeZonePhase::dump() { qDebug() << " ~~~ ICalTimeZonePhase ~~~"; qDebug() << " Abbreviations:" << abbrevs; qDebug() << " UTC offset:" << utcOffset; qDebug() << " Transitions:" << transitions; qDebug() << " ~~~~~~~~~~~~~~~~~~~~~~~~~"; } void ICalTimeZone::dump() { qDebug() << "~~~ ICalTimeZone ~~~"; qDebug() << "ID:" << id; qDebug() << "QZONE:" << qZone.id(); qDebug() << "STD:"; standard.dump(); qDebug() << "DST:"; daylight.dump(); qDebug() << "~~~~~~~~~~~~~~~~~~~~"; } ICalTimeZoneCache::ICalTimeZoneCache() { } void ICalTimeZoneCache::insert(const QByteArray &id, const ICalTimeZone &tz) { mCache.insert(id, tz); } namespace { template typename T::const_iterator greatestSmallerThan(const T &c, const typename T::value_type &v) { auto it = std::lower_bound(c.cbegin(), c.cend(), v); if (it != c.cbegin()) { return --it; } return c.cend(); } } QTimeZone ICalTimeZoneCache::tzForTime(const QDateTime &dt, const QByteArray &tzid) const { if (QTimeZone::isTimeZoneIdAvailable(tzid)) { return QTimeZone(tzid); } const ICalTimeZone tz = mCache.value(tzid); if (!tz.qZone.isValid()) { return QTimeZone::systemTimeZone(); } // If the matched timezone is one of the UTC offset timezones, we need to make // sure it's in the correct DTS. // The lookup in ICalTimeZoneParser will only find TZ in standard time, but // if the datetim in question fits in the DTS zone, we need to use another UTC // offset timezone if (tz.qZone.id().startsWith("UTC")) { //krazy:exclude=strings // Find the nearest standard and DST transitions that occur BEFORE the "dt" const auto stdPrev = greatestSmallerThan(tz.standard.transitions, dt); const auto dstPrev = greatestSmallerThan(tz.daylight.transitions, dt); if (stdPrev != tz.standard.transitions.cend() && dstPrev != tz.daylight.transitions.cend()) { if (*dstPrev > *stdPrev) { // Previous DTS is closer to "dt" than previous standard, which // means we are in DTS right now const auto tzids = QTimeZone::availableTimeZoneIds(tz.daylight.utcOffset); auto dtsTzId = std::find_if(tzids.cbegin(), tzids.cend(), [](const QByteArray &id) { return id.startsWith("UTC"); //krazy:exclude=strings }); if (dtsTzId != tzids.cend()) { return QTimeZone(*dtsTzId); } } } } return tz.qZone; } ICalTimeZoneParser::ICalTimeZoneParser(ICalTimeZoneCache *cache) : mCache(cache) { } void ICalTimeZoneParser::updateTzEarliestDate(const IncidenceBase::Ptr &incidence, TimeZoneEarliestDate *earliest) { for (auto role : { IncidenceBase::RoleStartTimeZone, IncidenceBase::RoleEndTimeZone }) { const auto dt = incidence->dateTime(role); if (dt.isValid()) { if (dt.timeZone() == QTimeZone::utc()) { continue; } const auto prev = earliest->value(incidence->dtStart().timeZone()); if (!prev.isValid() || incidence->dtStart() < prev) { earliest->insert(incidence->dtStart().timeZone(), prev); } } } } icalcomponent *ICalTimeZoneParser::icalcomponentFromQTimeZone(const QTimeZone &tz, const QDateTime &earliest) { // VTIMEZONE RRULE types enum { DAY_OF_MONTH = 0x01, WEEKDAY_OF_MONTH = 0x02, LAST_WEEKDAY_OF_MONTH = 0x04 }; // Write the time zone data into an iCal component icalcomponent *tzcomp = icalcomponent_new(ICAL_VTIMEZONE_COMPONENT); icalcomponent_add_property(tzcomp, icalproperty_new_tzid(tz.id().constData())); // icalcomponent_add_property(tzcomp, icalproperty_new_location( tz.name().toUtf8() )); // Compile an ordered list of transitions so that we can know the phases // which occur before and after each transition. QTimeZone::OffsetDataList transits = tz.transitions(QDateTime(), MAX_DATE()); if (transits.isEmpty()) { // If there is no way to compile a complete list of transitions // transitions() can return an empty list // In that case try get one transition to write a valid VTIMEZONE entry. if (transits.isEmpty()) { qCDebug(KCALCORE_LOG) << "No transition information available VTIMEZONE will be invalid."; } } if (earliest.isValid()) { // Remove all transitions earlier than those we are interested in for (int i = 0, end = transits.count(); i < end; ++i) { if (transits.at(i).atUtc >= earliest) { if (i > 0) { transits.erase(transits.begin(), transits.begin() + i); } break; } } } int trcount = transits.count(); QVector transitionsDone(trcount, false); // Go through the list of transitions and create an iCal component for each // distinct combination of phase after and UTC offset before the transition. icaldatetimeperiodtype dtperiod; dtperiod.period = icalperiodtype_null_period(); for (;;) { int i = 0; for (; i < trcount && transitionsDone[i]; ++i) { ; } if (i >= trcount) { break; } // Found a phase combination which hasn't yet been processed const int preOffset = (i > 0) ? transits.at(i - 1).offsetFromUtc : 0; const auto &transit = transits.at(i); if (transit.offsetFromUtc == preOffset) { transitionsDone[i] = true; while (++i < trcount) { if (transitionsDone[i] || transits.at(i).offsetFromUtc != transit.offsetFromUtc || transits.at(i).daylightTimeOffset != transit.daylightTimeOffset || transits.at(i - 1).offsetFromUtc != preOffset) { continue; } transitionsDone[i] = true; } continue; } const bool isDst = transit.daylightTimeOffset > 0; icalcomponent *phaseComp = icalcomponent_new(isDst ? ICAL_XDAYLIGHT_COMPONENT : ICAL_XSTANDARD_COMPONENT); if (!transit.abbreviation.isEmpty()) { icalcomponent_add_property(phaseComp, icalproperty_new_tzname( static_cast(transit.abbreviation.toUtf8().constData()))); } icalcomponent_add_property(phaseComp, icalproperty_new_tzoffsetfrom(preOffset)); icalcomponent_add_property(phaseComp, icalproperty_new_tzoffsetto(transit.offsetFromUtc)); // Create a component to hold initial RRULE if any, plus all RDATEs icalcomponent *phaseComp1 = icalcomponent_new_clone(phaseComp); icalcomponent_add_property(phaseComp1, icalproperty_new_dtstart( writeLocalICalDateTime(transits.at(i).atUtc, preOffset))); bool useNewRRULE = false; // Compile the list of UTC transition dates/times, and check // if the list can be reduced to an RRULE instead of multiple RDATEs. QTime time; QDate date; int year = 0, month = 0, daysInMonth = 0, dayOfMonth = 0; // avoid compiler warnings int dayOfWeek = 0; // Monday = 1 int nthFromStart = 0; // nth (weekday) of month int nthFromEnd = 0; // nth last (weekday) of month int newRule; int rule = 0; QList rdates;// dates which (probably) need to be written as RDATEs QList times; QDateTime qdt = transits.at(i).atUtc; // set 'qdt' for start of loop times += qdt; transitionsDone[i] = true; do { if (!rule) { // Initialise data for detecting a new rule rule = DAY_OF_MONTH | WEEKDAY_OF_MONTH | LAST_WEEKDAY_OF_MONTH; time = qdt.time(); date = qdt.date(); year = date.year(); month = date.month(); daysInMonth = date.daysInMonth(); dayOfWeek = date.dayOfWeek(); // Monday = 1 dayOfMonth = date.day(); nthFromStart = (dayOfMonth - 1) / 7 + 1; // nth (weekday) of month nthFromEnd = (daysInMonth - dayOfMonth) / 7 + 1; // nth last (weekday) of month } if (++i >= trcount) { newRule = 0; times += QDateTime(); // append a dummy value since last value in list is ignored } else { if (transitionsDone[i] || transits.at(i).offsetFromUtc != transit.offsetFromUtc || transits.at(i).daylightTimeOffset != transit.daylightTimeOffset || transits.at(i - 1).offsetFromUtc != preOffset) { continue; } transitionsDone[i] = true; qdt = transits.at(i).atUtc; if (!qdt.isValid()) { continue; } newRule = rule; times += qdt; date = qdt.date(); if (qdt.time() != time || date.month() != month || date.year() != ++year) { newRule = 0; } else { const int day = date.day(); if ((newRule & DAY_OF_MONTH) && day != dayOfMonth) { newRule &= ~DAY_OF_MONTH; } if (newRule & (WEEKDAY_OF_MONTH | LAST_WEEKDAY_OF_MONTH)) { if (date.dayOfWeek() != dayOfWeek) { newRule &= ~(WEEKDAY_OF_MONTH | LAST_WEEKDAY_OF_MONTH); } else { if ((newRule & WEEKDAY_OF_MONTH) && (day - 1) / 7 + 1 != nthFromStart) { newRule &= ~WEEKDAY_OF_MONTH; } if ((newRule & LAST_WEEKDAY_OF_MONTH) && (daysInMonth - day) / 7 + 1 != nthFromEnd) { newRule &= ~LAST_WEEKDAY_OF_MONTH; } } } } } if (!newRule) { // The previous rule (if any) no longer applies. // Write all the times up to but not including the current one. // First check whether any of the last RDATE values fit this rule. int yr = times[0].date().year(); while (!rdates.isEmpty()) { qdt = rdates.last(); date = qdt.date(); if (qdt.time() != time || date.month() != month || date.year() != --yr) { break; } const int day = date.day(); if (rule & DAY_OF_MONTH) { if (day != dayOfMonth) { break; } } else { if (date.dayOfWeek() != dayOfWeek || ((rule & WEEKDAY_OF_MONTH) && (day - 1) / 7 + 1 != nthFromStart) || ((rule & LAST_WEEKDAY_OF_MONTH) && (daysInMonth - day) / 7 + 1 != nthFromEnd)) { break; } } times.prepend(qdt); rdates.pop_back(); } if (times.count() > (useNewRRULE ? minPhaseCount : minRuleCount)) { // There are enough dates to combine into an RRULE icalrecurrencetype r; icalrecurrencetype_clear(&r); r.freq = ICAL_YEARLY_RECURRENCE; r.count = (year >= 2030) ? 0 : times.count() - 1; r.by_month[0] = month; if (rule & DAY_OF_MONTH) { r.by_month_day[0] = dayOfMonth; } else if (rule & WEEKDAY_OF_MONTH) { r.by_day[0] = (dayOfWeek % 7 + 1) + (nthFromStart * 8); // Sunday = 1 } else if (rule & LAST_WEEKDAY_OF_MONTH) { r.by_day[0] = -(dayOfWeek % 7 + 1) - (nthFromEnd * 8); // Sunday = 1 } r.until = writeLocalICalDateTime(times.takeAt(times.size() - 1), preOffset); icalproperty *prop = icalproperty_new_rrule(r); if (useNewRRULE) { // This RRULE doesn't start from the phase start date, so set it into // a new STANDARD/DAYLIGHT component in the VTIMEZONE. icalcomponent *c = icalcomponent_new_clone(phaseComp); icalcomponent_add_property( c, icalproperty_new_dtstart(writeLocalICalDateTime(times[0], preOffset))); icalcomponent_add_property(c, prop); icalcomponent_add_component(tzcomp, c); } else { icalcomponent_add_property(phaseComp1, prop); } } else { // Save dates for writing as RDATEs for (int t = 0, tend = times.count() - 1; t < tend; ++t) { rdates += times[t]; } } useNewRRULE = true; // All date/time values but the last have been added to the VTIMEZONE. // Remove them from the list. qdt = times.last(); // set 'qdt' for start of loop times.clear(); times += qdt; } rule = newRule; } while (i < trcount); // Write remaining dates as RDATEs for (int rd = 0, rdend = rdates.count(); rd < rdend; ++rd) { dtperiod.time = writeLocalICalDateTime(rdates[rd], preOffset); icalcomponent_add_property(phaseComp1, icalproperty_new_rdate(dtperiod)); } icalcomponent_add_component(tzcomp, phaseComp1); icalcomponent_free(phaseComp); } return tzcomp; } icaltimezone *ICalTimeZoneParser::icaltimezoneFromQTimeZone(const QTimeZone &tz, const QDateTime &earliest) { auto itz = icaltimezone_new(); icaltimezone_set_component(itz, icalcomponentFromQTimeZone(tz, earliest)); return itz; } void ICalTimeZoneParser::parse(icalcomponent *calendar) { for (auto *c = icalcomponent_get_first_component(calendar, ICAL_VTIMEZONE_COMPONENT); c; c = icalcomponent_get_next_component(calendar, ICAL_VTIMEZONE_COMPONENT)) { auto icalZone = parseTimeZone(c); //icalZone.dump(); if (!icalZone.id.isEmpty()) { if (!icalZone.qZone.isValid()) { icalZone.qZone = resolveICalTimeZone(icalZone); } if (!icalZone.qZone.isValid()) { qCWarning(KCALCORE_LOG) << "Failed to map" << icalZone.id << "to a known IANA timezone"; continue; } mCache->insert(icalZone.id, icalZone); } } } QTimeZone ICalTimeZoneParser::resolveICalTimeZone(const ICalTimeZone &icalZone) { const auto phase = icalZone.standard; const auto now = QDateTime::currentDateTimeUtc(); const auto candidates = QTimeZone::availableTimeZoneIds(phase.utcOffset); QMap matchedCandidates; for (const auto &tzid : candidates) { const QTimeZone candidate(tzid); // This would be a fallback, candidate has transitions, but the phase does not if (candidate.hasTransitions() == phase.transitions.isEmpty()) { matchedCandidates.insert(0, candidate); continue; } // Without transitions, we can't do any more precise matching, so just // accept this candidate and be done with it if (!candidate.hasTransitions() && phase.transitions.isEmpty()) { return candidate; } // Calculate how many transitions this candidate shares with the phase. // The candidate with the most matching transitions will win. auto begin = std::lower_bound(phase.transitions.cbegin(), phase.transitions.cend(), now.addYears(-20)); // If no transition older than 20 years is found, we will start from beginning if (begin == phase.transitions.cend()) { begin = phase.transitions.cbegin(); } auto end = std::upper_bound(begin, phase.transitions.cend(), now); int matchedTransitions = 0; for (auto it = begin; it != end; ++it) { const auto &transition = *it; const QTimeZone::OffsetDataList candidateTransitions = candidate.transitions(transition, transition); if (candidateTransitions.isEmpty()) { continue; } ++matchedTransitions; // 1 point for a matching transition const auto candidateTransition = candidateTransitions[0]; // FIXME: THIS IS HOW IT SHOULD BE: //const auto abvs = transition.abbreviations(); const auto abvs = phase.abbrevs; for (const auto &abv : abvs) { if (candidateTransition.abbreviation == QString::fromUtf8(abv)) { matchedTransitions += 1024; // lots of points for a transition with a matching abbreviation break; } } } matchedCandidates.insert(matchedTransitions, candidate); } if (!matchedCandidates.isEmpty()) { return matchedCandidates.value(matchedCandidates.lastKey()); } return {}; } ICalTimeZone ICalTimeZoneParser::parseTimeZone(icalcomponent *vtimezone) { ICalTimeZone icalTz; if (auto tzidProp = icalcomponent_get_first_property(vtimezone, ICAL_TZID_PROPERTY)) { icalTz.id = icalproperty_get_value_as_string(tzidProp); // If the VTIMEZONE is a known IANA time zone don't bother parsing the rest // of the VTIMEZONE, get QTimeZone directly from Qt if (QTimeZone::isTimeZoneIdAvailable(icalTz.id)) { icalTz.qZone = QTimeZone(icalTz.id); return icalTz; } else { // Not IANA, but maybe we can match it from Windows ID? const auto ianaTzid = QTimeZone::windowsIdToDefaultIanaId(icalTz.id); if (!ianaTzid.isEmpty()) { icalTz.qZone = QTimeZone(ianaTzid); return icalTz; } } } for (icalcomponent *c = icalcomponent_get_first_component(vtimezone, ICAL_ANY_COMPONENT); c; c = icalcomponent_get_next_component(vtimezone, ICAL_ANY_COMPONENT)) { icalcomponent_kind kind = icalcomponent_isa(c); switch (kind) { case ICAL_XSTANDARD_COMPONENT: //qCDebug(KCALCORE_LOG) << "---standard phase: found"; parsePhase(c, false, icalTz.standard); break; case ICAL_XDAYLIGHT_COMPONENT: //qCDebug(KCALCORE_LOG) << "---daylight phase: found"; parsePhase(c, true, icalTz.daylight); break; default: qCDebug(KCALCORE_LOG) << "Unknown component:" << int(kind); break; } } return icalTz; } bool ICalTimeZoneParser::parsePhase(icalcomponent *c, bool daylight, ICalTimeZonePhase &phase) { // Read the observance data for this standard/daylight savings phase int utcOffset = 0; int prevOffset = 0; bool recurs = false; bool found_dtstart = false; bool found_tzoffsetfrom = false; bool found_tzoffsetto = false; icaltimetype dtstart = icaltime_null_time(); QSet abbrevs; // Now do the ical reading. icalproperty *p = icalcomponent_get_first_property(c, ICAL_ANY_PROPERTY); while (p) { icalproperty_kind kind = icalproperty_isa(p); switch (kind) { case ICAL_TZNAME_PROPERTY: { // abbreviated name for this time offset // TZNAME can appear multiple times in order to provide language // translations of the time zone offset name. // TODO: Does this cope with multiple language specifications? QByteArray name = icalproperty_get_tzname(p); // Outlook (2000) places "Standard Time" and "Daylight Time" in the TZNAME // strings, which is totally useless. So ignore those. if ((!daylight && name == "Standard Time") || (daylight && name == "Daylight Time")) { break; } abbrevs.insert(name); break; } case ICAL_DTSTART_PROPERTY: // local time at which phase starts dtstart = icalproperty_get_dtstart(p); found_dtstart = true; break; case ICAL_TZOFFSETFROM_PROPERTY: // UTC offset immediately before start of phase prevOffset = icalproperty_get_tzoffsetfrom(p); found_tzoffsetfrom = true; break; case ICAL_TZOFFSETTO_PROPERTY: utcOffset = icalproperty_get_tzoffsetto(p); found_tzoffsetto = true; break; case ICAL_RDATE_PROPERTY: case ICAL_RRULE_PROPERTY: recurs = true; break; default: break; } p = icalcomponent_get_next_property(c, ICAL_ANY_PROPERTY); } // Validate the phase data if (!found_dtstart || !found_tzoffsetfrom || !found_tzoffsetto) { qCDebug(KCALCORE_LOG) << "DTSTART/TZOFFSETFROM/TZOFFSETTO missing"; return false; } // Convert DTSTART to QDateTime, and from local time to UTC const QDateTime localStart = toQDateTime(dtstart); // local time dtstart.second -= prevOffset; #if defined(USE_ICAL_3) dtstart = icaltime_convert_to_zone(dtstart, icaltimezone_get_utc_timezone()); #else dtstart.is_utc = 1; #endif const QDateTime utcStart = toQDateTime(icaltime_normalize(dtstart)); // UTC phase.abbrevs.unite(abbrevs); phase.utcOffset = utcOffset; phase.transitions += utcStart; if (recurs) { /* RDATE or RRULE is specified. There should only be one or the other, but * it doesn't really matter - the code can cope with both. * Note that we had to get DTSTART, TZOFFSETFROM, TZOFFSETTO before reading * recurrences. */ const QDateTime maxTime(MAX_DATE()); Recurrence recur; icalproperty *p = icalcomponent_get_first_property(c, ICAL_ANY_PROPERTY); while (p) { icalproperty_kind kind = icalproperty_isa(p); switch (kind) { case ICAL_RDATE_PROPERTY: { icaltimetype t = icalproperty_get_rdate(p).time; if (icaltime_is_date(t)) { // RDATE with a DATE value inherits the (local) time from DTSTART t.hour = dtstart.hour; t.minute = dtstart.minute; t.second = dtstart.second; #if !defined(USE_ICAL_3) t.is_utc = 0; // dtstart is in local time #endif t.is_date = 0; } // RFC2445 states that RDATE must be in local time, // but we support UTC as well to be safe. #if defined(USE_ICAL_3) if (!icaltime_is_utc(t)) { #else if (!t.is_utc) { #endif t.second -= prevOffset; // convert to UTC #if defined(USE_ICAL_3) t = icaltime_convert_to_zone(t, icaltimezone_get_utc_timezone()); #else t.is_utc = 1; #endif t = icaltime_normalize(t); } phase.transitions += toQDateTime(t); break; } case ICAL_RRULE_PROPERTY: { RecurrenceRule r; ICalFormat icf; ICalFormatImpl impl(&icf); impl.readRecurrence(icalproperty_get_rrule(p), &r); r.setStartDt(localStart); // The end date time specified in an RRULE should be in UTC. // Convert to local time to avoid timesInInterval() getting things wrong. if (r.duration() == 0) { QDateTime end(r.endDt()); if (end.timeSpec() == Qt::UTC) { end.setTimeSpec(Qt::LocalTime); r.setEndDt(end.addSecs(prevOffset)); } } const auto dts = r.timesInInterval(localStart, maxTime); for (int i = 0, end = dts.count(); i < end; ++i) { QDateTime utc = dts[i]; utc.setTimeSpec(Qt::UTC); phase.transitions += utc.addSecs(-prevOffset); } break; } default: break; } p = icalcomponent_get_next_property(c, ICAL_ANY_PROPERTY); } sortAndRemoveDuplicates(phase.transitions); } return true; } QByteArray ICalTimeZoneParser::vcaltimezoneFromQTimeZone(const QTimeZone &qtz, const QDateTime &earliest) { auto icalTz = icalcomponentFromQTimeZone(qtz, earliest); const QByteArray result(icalcomponent_as_ical_string(icalTz)); icalmemory_free_ring(); icalcomponent_free(icalTz); return result; } -} // namespace KCalCore +} // namespace KCalendarCore diff --git a/src/icaltimezones_p.h b/src/icaltimezones_p.h index b184ba8bd..cf00205a4 100644 --- a/src/icaltimezones_p.h +++ b/src/icaltimezones_p.h @@ -1,113 +1,113 @@ /* This file is part of the kcalcore library. Copyright (c) 2005-2007 David Jarvie This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KCALCORE_ICALTIMEZONES_P_H #define KCALCORE_ICALTIMEZONES_P_H #include "kcalcore_export.h" #include "incidencebase.h" #include #include #include #ifndef ICALCOMPONENT_H typedef struct icalcomponent_impl icalcomponent; #endif #ifndef ICALTIMEZONE_DEFINED #define ICALTIMEZONE_DEFINED typedef struct _icaltimezone icaltimezone; #endif -namespace KCalCore +namespace KCalendarCore { class ICalTimeZonePhase; class ICalTimeZonePhase { public: void dump(); QSet abbrevs; // abbreviations of the phase int utcOffset = 0; // UTC offset of the phase QVector transitions; // times on which transition into this phase occurs }; class ICalTimeZone { public: void dump(); QByteArray id; // original TZID QTimeZone qZone; // QTimeZone mapped from TZID ICalTimeZonePhase standard; // standard time ICalTimeZonePhase daylight; // DST time }; class KCALCORE_EXPORT ICalTimeZoneCache { public: explicit ICalTimeZoneCache(); void insert(const QByteArray &id, const ICalTimeZone &tz); QTimeZone tzForTime(const QDateTime &dt, const QByteArray &tzid) const; private: QHash mCache; }; using TimeZoneEarliestDate = QHash; class KCALCORE_EXPORT ICalTimeZoneParser { public: explicit ICalTimeZoneParser(ICalTimeZoneCache *cache); void parse(icalcomponent *calendar); static void updateTzEarliestDate(const IncidenceBase::Ptr &incidence, TimeZoneEarliestDate *earliestDate); static icaltimezone *icaltimezoneFromQTimeZone(const QTimeZone &qtz, const QDateTime &earliest); static QByteArray vcaltimezoneFromQTimeZone(const QTimeZone &qtz, const QDateTime &earliest); private: static icalcomponent *icalcomponentFromQTimeZone(const QTimeZone &qtz, const QDateTime &earliest); ICalTimeZone parseTimeZone(icalcomponent *zone); bool parsePhase(icalcomponent *c, bool daylight, ICalTimeZonePhase &phase); QTimeZone resolveICalTimeZone(const ICalTimeZone &icalZone); ICalTimeZoneCache *mCache; }; -} // namespace KCalCore +} // namespace KCalendarCore inline uint qHash(const QTimeZone &tz) { return qHash(tz.id()); } #endif diff --git a/src/incidence.cpp b/src/incidence.cpp index 3a59acf26..a685d25b5 100644 --- a/src/incidence.cpp +++ b/src/incidence.cpp @@ -1,1167 +1,1167 @@ /* This file is part of the kcalcore library. Copyright (c) 2001 Cornelius Schumacher Copyright (C) 2003-2004 Reinhold Kainhofer This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the Incidence class. @brief Provides the class common to non-FreeBusy (Events, To-dos, Journals) calendar components known as incidences. @author Cornelius Schumacher \ @author Reinhold Kainhofer \ */ #include "incidence.h" #include "calformat.h" #include "utils.h" #include // for .toHtmlEscaped() and Qt::mightBeRichText() #include #include #include -using namespace KCalCore; +using namespace KCalendarCore; /** Private class that helps to provide binary compatibility between releases. @internal */ //@cond PRIVATE -class Q_DECL_HIDDEN KCalCore::Incidence::Private +class Q_DECL_HIDDEN KCalendarCore::Incidence::Private { public: Private() : mGeoLatitude(INVALID_LATLON) , mGeoLongitude(INVALID_LATLON) , mRecurrence(nullptr) , mRevision(0) , mPriority(0) , mStatus(StatusNone) , mSecrecy(SecrecyPublic) , mDescriptionIsRich(false) , mSummaryIsRich(false) , mLocationIsRich(false) , mHasGeo(false) , mThisAndFuture(false) , mLocalOnly(false) { } Private(const Private &p) : mCreated(p.mCreated) , mDescription(p.mDescription) , mSummary(p.mSummary) , mLocation(p.mLocation) , mCategories(p.mCategories) , mResources(p.mResources) , mStatusString(p.mStatusString) , mSchedulingID(p.mSchedulingID) , mRelatedToUid(p.mRelatedToUid) , mRecurrenceId(p.mRecurrenceId) , mGeoLatitude(p.mGeoLatitude) , mGeoLongitude(p.mGeoLongitude) , mRecurrence(nullptr) , mRevision(p.mRevision) , mPriority(p.mPriority) , mStatus(p.mStatus) , mSecrecy(p.mSecrecy) , mDescriptionIsRich(p.mDescriptionIsRich) , mSummaryIsRich(p.mSummaryIsRich) , mLocationIsRich(p.mLocationIsRich) , mHasGeo(p.mHasGeo) , mThisAndFuture(p.mThisAndFuture) , mLocalOnly(false) { } void clear() { mAlarms.clear(); mAttachments.clear(); delete mRecurrence; mRecurrence = nullptr; } void init(Incidence *dest, const Incidence &src) { mRevision = src.d->mRevision; mCreated = src.d->mCreated; mDescription = src.d->mDescription; mSummary = src.d->mSummary; mCategories = src.d->mCategories; mRelatedToUid = src.d->mRelatedToUid; mResources = src.d->mResources; mStatusString = src.d->mStatusString; mStatus = src.d->mStatus; mSecrecy = src.d->mSecrecy; mPriority = src.d->mPriority; mLocation = src.d->mLocation; mGeoLatitude = src.d->mGeoLatitude; mGeoLongitude = src.d->mGeoLongitude; mHasGeo = src.d->mHasGeo; mRecurrenceId = src.d->mRecurrenceId; mThisAndFuture = src.d->mThisAndFuture; mLocalOnly = src.d->mLocalOnly; // Alarms and Attachments are stored in ListBase<...>, which is a QValueList<...*>. // We need to really duplicate the objects stored therein, otherwise deleting // i will also delete all attachments from this object (setAutoDelete...) mAlarms.reserve(src.d->mAlarms.count()); for (const Alarm::Ptr &alarm : qAsConst(src.d->mAlarms)) { Alarm::Ptr b(new Alarm(*alarm.data())); b->setParent(dest); mAlarms.append(b); } mAttachments = src.d->mAttachments; if (src.d->mRecurrence) { mRecurrence = new Recurrence(*(src.d->mRecurrence)); mRecurrence->addObserver(dest); } else { mRecurrence = nullptr; } } QDateTime mCreated; // creation datetime QString mDescription; // description string QString mSummary; // summary string QString mLocation; // location string QStringList mCategories; // category list Attachment::List mAttachments; // attachments list Alarm::List mAlarms; // alarms list QStringList mResources; // resources list (not calendar resources) QString mStatusString; // status string, for custom status QString mSchedulingID; // ID for scheduling mails QMap mRelatedToUid; // incidence uid this is related to, for each relType QDateTime mRecurrenceId; // recurrenceId float mGeoLatitude; // Specifies latitude in decimal degrees float mGeoLongitude; // Specifies longitude in decimal degrees mutable Recurrence *mRecurrence; // recurrence int mRevision; // revision number int mPriority; // priority: 1 = highest, 2 = less, etc. Status mStatus; // status Secrecy mSecrecy; // secrecy bool mDescriptionIsRich = false; // description string is richtext. bool mSummaryIsRich = false; // summary string is richtext. bool mLocationIsRich = false; // location string is richtext. bool mHasGeo = false; // if incidence has geo data bool mThisAndFuture = false; bool mLocalOnly = false; // allow changes that won't go to the server }; //@endcond Incidence::Incidence() : IncidenceBase() - , d(new KCalCore::Incidence::Private) + , d(new KCalendarCore::Incidence::Private) { recreate(); resetDirtyFields(); } Incidence::Incidence(const Incidence &i) : IncidenceBase(i) , Recurrence::RecurrenceObserver() - , d(new KCalCore::Incidence::Private(*i.d)) + , d(new KCalendarCore::Incidence::Private(*i.d)) { d->init(this, i); resetDirtyFields(); } Incidence::~Incidence() { // Alarm has a raw incidence pointer, so we must set it to 0 // so Alarm doesn't use it after Incidence is destroyed for (const Alarm::Ptr &alarm : qAsConst(d->mAlarms)) { alarm->setParent(nullptr); } delete d->mRecurrence; delete d; } //@cond PRIVATE // A string comparison that considers that null and empty are the same static bool stringCompare(const QString &s1, const QString &s2) { return (s1.isEmpty() && s2.isEmpty()) || (s1 == s2); } //@endcond IncidenceBase &Incidence::assign(const IncidenceBase &other) { if (&other != this) { d->clear(); //TODO: should relations be cleared out, as in destructor??? IncidenceBase::assign(other); const Incidence *i = static_cast(&other); d->init(this, *i); } return *this; } bool Incidence::equals(const IncidenceBase &incidence) const { if (!IncidenceBase::equals(incidence)) { return false; } // If they weren't the same type IncidenceBase::equals would had returned false already const Incidence *i2 = static_cast(&incidence); const auto alarmList = alarms(); const auto otherAlarmsList = i2->alarms(); if (alarmList.count() != otherAlarmsList.count()) { return false; } Alarm::List::ConstIterator a1 = alarmList.constBegin(); Alarm::List::ConstIterator a1end = alarmList.constEnd(); Alarm::List::ConstIterator a2 = otherAlarmsList.constBegin(); Alarm::List::ConstIterator a2end = otherAlarmsList.constEnd(); for (; a1 != a1end && a2 != a2end; ++a1, ++a2) { if (**a1 == **a2) { continue; } else { return false; } } const auto attachmentList = attachments(); const auto otherAttachmentList = i2->attachments(); if (attachmentList.count() != otherAttachmentList.count()) { return false; } Attachment::List::ConstIterator att1 = attachmentList.constBegin(); const Attachment::List::ConstIterator att1end = attachmentList.constEnd(); Attachment::List::ConstIterator att2 = otherAttachmentList.constBegin(); const Attachment::List::ConstIterator att2end = otherAttachmentList.constEnd(); for (; att1 != att1end && att2 != att2end; ++att1, ++att2) { if (*att1 == *att2) { continue; } else { return false; } } bool recurrenceEqual = (d->mRecurrence == nullptr && i2->d->mRecurrence == nullptr); if (!recurrenceEqual) { recurrence(); // create if doesn't exist i2->recurrence(); // create if doesn't exist recurrenceEqual = d->mRecurrence != nullptr && i2->d->mRecurrence != nullptr && *d->mRecurrence == *i2->d->mRecurrence; } if (d->mHasGeo == i2->d->mHasGeo) { if (d->mHasGeo && (!qFuzzyCompare(d->mGeoLatitude, i2->d->mGeoLatitude) || !qFuzzyCompare(d->mGeoLongitude, i2->d->mGeoLongitude))) { return false; } } else { return false; } return recurrenceEqual && created() == i2->created() && stringCompare(description(), i2->description()) && stringCompare(summary(), i2->summary()) && categories() == i2->categories() && stringCompare(relatedTo(), i2->relatedTo()) && resources() == i2->resources() && d->mStatus == i2->d->mStatus && (d->mStatus == StatusNone || stringCompare(d->mStatusString, i2->d->mStatusString)) && secrecy() == i2->secrecy() && priority() == i2->priority() && stringCompare(location(), i2->location()) && stringCompare(schedulingID(), i2->schedulingID()) && recurrenceId() == i2->recurrenceId() && thisAndFuture() == i2->thisAndFuture(); } QString Incidence::instanceIdentifier() const { if (hasRecurrenceId()) { return uid() + recurrenceId().toString(Qt::ISODate); } return uid(); } void Incidence::recreate() { const QDateTime nowUTC = QDateTime::currentDateTimeUtc(); setCreated(nowUTC); setSchedulingID(QString(), CalFormat::createUniqueId()); setRevision(0); setLastModified(nowUTC); //NOLINT false clang-analyzer-optin.cplusplus.VirtualCall } void Incidence::setLastModified(const QDateTime &lm) { if (!d->mLocalOnly) { IncidenceBase::setLastModified(lm); } } void Incidence::setReadOnly(bool readOnly) { IncidenceBase::setReadOnly(readOnly); if (d->mRecurrence) { d->mRecurrence->setRecurReadOnly(readOnly); } } void Incidence::setLocalOnly(bool localOnly) { if (mReadOnly) { return; } d->mLocalOnly = localOnly; } bool Incidence::localOnly() const { return d->mLocalOnly; } void Incidence::setAllDay(bool allDay) { if (mReadOnly) { return; } if (d->mRecurrence) { d->mRecurrence->setAllDay(allDay); } IncidenceBase::setAllDay(allDay); } void Incidence::setCreated(const QDateTime &created) { if (mReadOnly || d->mLocalOnly) { return; } d->mCreated = created.toUTC(); const auto ct = d->mCreated.time(); // Remove milliseconds d->mCreated.setTime(QTime(ct.hour(), ct.minute(), ct.second())); setFieldDirty(FieldCreated); // FIXME: Shouldn't we call updated for the creation date, too? // updated(); } QDateTime Incidence::created() const { return d->mCreated; } void Incidence::setRevision(int rev) { if (mReadOnly || d->mLocalOnly) { return; } update(); d->mRevision = rev; setFieldDirty(FieldRevision); updated(); } int Incidence::revision() const { return d->mRevision; } void Incidence::setDtStart(const QDateTime &dt) { IncidenceBase::setDtStart(dt); if (d->mRecurrence && dirtyFields().contains(FieldDtStart)) { d->mRecurrence->setStartDateTime(dt, allDay()); } } void Incidence::shiftTimes(const QTimeZone &oldZone, const QTimeZone &newZone) { IncidenceBase::shiftTimes(oldZone, newZone); if (d->mRecurrence) { d->mRecurrence->shiftTimes(oldZone, newZone); } for (int i = 0, end = d->mAlarms.count(); i < end; ++i) { d->mAlarms[i]->shiftTimes(oldZone, newZone); } } void Incidence::setDescription(const QString &description, bool isRich) { if (mReadOnly) { return; } update(); d->mDescription = description; d->mDescriptionIsRich = isRich; setFieldDirty(FieldDescription); updated(); } void Incidence::setDescription(const QString &description) { setDescription(description, Qt::mightBeRichText(description)); } QString Incidence::description() const { return d->mDescription; } QString Incidence::richDescription() const { if (descriptionIsRich()) { return d->mDescription; } else { return d->mDescription.toHtmlEscaped().replace(QLatin1Char('\n'), QStringLiteral("
")); } } bool Incidence::descriptionIsRich() const { return d->mDescriptionIsRich; } void Incidence::setSummary(const QString &summary, bool isRich) { if (mReadOnly) { return; } if (d->mSummary != summary || d->mSummaryIsRich != isRich) { update(); d->mSummary = summary; d->mSummaryIsRich = isRich; setFieldDirty(FieldSummary); updated(); } } void Incidence::setSummary(const QString &summary) { setSummary(summary, Qt::mightBeRichText(summary)); } QString Incidence::summary() const { return d->mSummary; } QString Incidence::richSummary() const { if (summaryIsRich()) { return d->mSummary; } else { return d->mSummary.toHtmlEscaped().replace(QLatin1Char('\n'), QStringLiteral("
")); } } bool Incidence::summaryIsRich() const { return d->mSummaryIsRich; } void Incidence::setCategories(const QStringList &categories) { if (mReadOnly) { return; } update(); d->mCategories = categories; updated(); } void Incidence::setCategories(const QString &catStr) { if (mReadOnly) { return; } update(); setFieldDirty(FieldCategories); d->mCategories.clear(); if (catStr.isEmpty()) { updated(); return; } d->mCategories = catStr.split(QLatin1Char(',')); QStringList::Iterator it; for (it = d->mCategories.begin(); it != d->mCategories.end(); ++it) { *it = (*it).trimmed(); } updated(); } QStringList Incidence::categories() const { return d->mCategories; } QString Incidence::categoriesStr() const { return d->mCategories.join(QLatin1Char(',')); } void Incidence::setRelatedTo(const QString &relatedToUid, RelType relType) { // TODO: RFC says that an incidence can have more than one related-to field // even for the same relType. if (d->mRelatedToUid[relType] != relatedToUid) { update(); d->mRelatedToUid[relType] = relatedToUid; setFieldDirty(FieldRelatedTo); updated(); } } QString Incidence::relatedTo(RelType relType) const { return d->mRelatedToUid.value(relType); } // %%%%%%%%%%%% Recurrence-related methods %%%%%%%%%%%%%%%%%%%% Recurrence *Incidence::recurrence() const { if (!d->mRecurrence) { d->mRecurrence = new Recurrence(); d->mRecurrence->setStartDateTime(dateTime(RoleRecurrenceStart), allDay()); d->mRecurrence->setAllDay(allDay()); d->mRecurrence->setRecurReadOnly(mReadOnly); - d->mRecurrence->addObserver(const_cast(this)); + d->mRecurrence->addObserver(const_cast(this)); } return d->mRecurrence; } void Incidence::clearRecurrence() { delete d->mRecurrence; d->mRecurrence = nullptr; } ushort Incidence::recurrenceType() const { if (d->mRecurrence) { return d->mRecurrence->recurrenceType(); } else { return Recurrence::rNone; } } bool Incidence::recurs() const { if (d->mRecurrence) { return d->mRecurrence->recurs(); } else { return false; } } bool Incidence::recursOn(const QDate &date, const QTimeZone &timeZone) const { return d->mRecurrence && d->mRecurrence->recursOn(date, timeZone); } bool Incidence::recursAt(const QDateTime &qdt) const { return d->mRecurrence && d->mRecurrence->recursAt(qdt); } QList Incidence::startDateTimesForDate(const QDate &date, const QTimeZone &timeZone) const { QDateTime start = dtStart(); QDateTime end = dateTime(RoleEndRecurrenceBase); QList result; // TODO_Recurrence: Also work if only due date is given... if (!start.isValid() && !end.isValid()) { return result; } // if the incidence doesn't recur, QDateTime kdate(date, {}, timeZone); if (!recurs()) { if (!(start > kdate || end < kdate)) { result << start; } return result; } qint64 days = start.daysTo(end); // Account for possible recurrences going over midnight, while the original event doesn't QDate tmpday(date.addDays(-days - 1)); QDateTime tmp; while (tmpday <= date) { if (recurrence()->recursOn(tmpday, timeZone)) { const QList times = recurrence()->recurTimesOn(tmpday, timeZone); for (const QTime &time : times) { tmp = QDateTime(tmpday, time, start.timeZone()); if (endDateForStart(tmp) >= kdate) { result << tmp; } } } tmpday = tmpday.addDays(1); } return result; } QList Incidence::startDateTimesForDateTime(const QDateTime &datetime) const { QDateTime start = dtStart(); QDateTime end = dateTime(RoleEndRecurrenceBase); QList result; // TODO_Recurrence: Also work if only due date is given... if (!start.isValid() && !end.isValid()) { return result; } // if the incidence doesn't recur, if (!recurs()) { if (!(start > datetime || end < datetime)) { result << start; } return result; } qint64 days = start.daysTo(end); // Account for possible recurrences going over midnight, while the original event doesn't QDate tmpday(datetime.date().addDays(-days - 1)); QDateTime tmp; while (tmpday <= datetime.date()) { if (recurrence()->recursOn(tmpday, datetime.timeZone())) { // Get the times during the day (in start date's time zone) when recurrences happen const QList times = recurrence()->recurTimesOn(tmpday, start.timeZone()); for (const QTime &time : times) { tmp = QDateTime(tmpday, time, start.timeZone()); if (!(tmp > datetime || endDateForStart(tmp) < datetime)) { result << tmp; } } } tmpday = tmpday.addDays(1); } return result; } QDateTime Incidence::endDateForStart(const QDateTime &startDt) const { QDateTime start = dtStart(); QDateTime end = dateTime(RoleEndRecurrenceBase); if (!end.isValid()) { return start; } if (!start.isValid()) { return end; } return startDt.addSecs(start.secsTo(end)); } void Incidence::addAttachment(const Attachment &attachment) { if (mReadOnly || attachment.isEmpty()) { return; } update(); d->mAttachments.append(attachment); setFieldDirty(FieldAttachment); updated(); } void Incidence::deleteAttachments(const QString &mime) { Attachment::List result; Attachment::List::Iterator it = d->mAttachments.begin(); while (it != d->mAttachments.end()) { if ((*it).mimeType() != mime) { result += *it; } ++it; } d->mAttachments = result; setFieldDirty(FieldAttachment); } Attachment::List Incidence::attachments() const { return d->mAttachments; } Attachment::List Incidence::attachments(const QString &mime) const { Attachment::List attachments; for (const Attachment &attachment : qAsConst(d->mAttachments)) { if (attachment.mimeType() == mime) { attachments.append(attachment); } } return attachments; } void Incidence::clearAttachments() { setFieldDirty(FieldAttachment); d->mAttachments.clear(); } void Incidence::setResources(const QStringList &resources) { if (mReadOnly) { return; } update(); d->mResources = resources; setFieldDirty(FieldResources); updated(); } QStringList Incidence::resources() const { return d->mResources; } void Incidence::setPriority(int priority) { if (mReadOnly) { return; } update(); d->mPriority = priority; setFieldDirty(FieldPriority); updated(); } int Incidence::priority() const { return d->mPriority; } void Incidence::setStatus(Incidence::Status status) { if (mReadOnly || status == StatusX) { return; } update(); d->mStatus = status; d->mStatusString.clear(); setFieldDirty(FieldStatus); updated(); } void Incidence::setCustomStatus(const QString &status) { if (mReadOnly) { return; } update(); d->mStatus = status.isEmpty() ? StatusNone : StatusX; d->mStatusString = status; setFieldDirty(FieldStatus); updated(); } Incidence::Status Incidence::status() const { return d->mStatus; } QString Incidence::customStatus() const { if (d->mStatus == StatusX) { return d->mStatusString; } else { return QString(); } } void Incidence::setSecrecy(Incidence::Secrecy secrecy) { if (mReadOnly) { return; } update(); d->mSecrecy = secrecy; setFieldDirty(FieldSecrecy); updated(); } Incidence::Secrecy Incidence::secrecy() const { return d->mSecrecy; } Alarm::List Incidence::alarms() const { return d->mAlarms; } Alarm::Ptr Incidence::newAlarm() { Alarm::Ptr alarm(new Alarm(this)); d->mAlarms.append(alarm); return alarm; } void Incidence::addAlarm(const Alarm::Ptr &alarm) { update(); d->mAlarms.append(alarm); setFieldDirty(FieldAlarms); updated(); } void Incidence::removeAlarm(const Alarm::Ptr &alarm) { const int index = d->mAlarms.indexOf(alarm); if (index > -1) { update(); d->mAlarms.remove(index); setFieldDirty(FieldAlarms); updated(); } } void Incidence::clearAlarms() { update(); d->mAlarms.clear(); setFieldDirty(FieldAlarms); updated(); } bool Incidence::hasEnabledAlarms() const { for (const Alarm::Ptr &alarm : qAsConst(d->mAlarms)) { if (alarm->enabled()) { return true; } } return false; } void Incidence::setLocation(const QString &location, bool isRich) { if (mReadOnly) { return; } if (d->mLocation != location || d->mLocationIsRich != isRich) { update(); d->mLocation = location; d->mLocationIsRich = isRich; setFieldDirty(FieldLocation); updated(); } } void Incidence::setLocation(const QString &location) { setLocation(location, Qt::mightBeRichText(location)); } QString Incidence::location() const { return d->mLocation; } QString Incidence::richLocation() const { if (locationIsRich()) { return d->mLocation; } else { return d->mLocation.toHtmlEscaped().replace(QLatin1Char('\n'), QStringLiteral("
")); } } bool Incidence::locationIsRich() const { return d->mLocationIsRich; } void Incidence::setSchedulingID(const QString &sid, const QString &uid) { if (!uid.isEmpty()) { setUid(uid); } if (sid != d->mSchedulingID) { d->mSchedulingID = sid; setFieldDirty(FieldSchedulingId); } } QString Incidence::schedulingID() const { if (d->mSchedulingID.isNull()) { // Nothing set, so use the normal uid return uid(); } return d->mSchedulingID; } bool Incidence::hasGeo() const { return d->mHasGeo; } void Incidence::setHasGeo(bool hasGeo) { if (mReadOnly) { return; } if (hasGeo == d->mHasGeo) { return; } update(); d->mHasGeo = hasGeo; setFieldDirty(FieldGeoLatitude); setFieldDirty(FieldGeoLongitude); updated(); } float Incidence::geoLatitude() const { return d->mGeoLatitude; } void Incidence::setGeoLatitude(float geolatitude) { if (mReadOnly) { return; } update(); d->mGeoLatitude = geolatitude; setFieldDirty(FieldGeoLatitude); updated(); } float Incidence::geoLongitude() const { return d->mGeoLongitude; } void Incidence::setGeoLongitude(float geolongitude) { if (!mReadOnly) { update(); d->mGeoLongitude = geolongitude; setFieldDirty(FieldGeoLongitude); updated(); } } bool Incidence::hasRecurrenceId() const { return (allDay() && d->mRecurrenceId.date().isValid()) || d->mRecurrenceId.isValid(); } QDateTime Incidence::recurrenceId() const { return d->mRecurrenceId; } void Incidence::setThisAndFuture(bool thisAndFuture) { d->mThisAndFuture = thisAndFuture; } bool Incidence::thisAndFuture() const { return d->mThisAndFuture; } void Incidence::setRecurrenceId(const QDateTime &recurrenceId) { if (!mReadOnly) { update(); d->mRecurrenceId = recurrenceId; setFieldDirty(FieldRecurrenceId); updated(); } } /** Observer interface for the recurrence class. If the recurrence is changed, this method will be called for the incidence the recurrence object belongs to. */ void Incidence::recurrenceUpdated(Recurrence *recurrence) { if (recurrence == d->mRecurrence) { update(); setFieldDirty(FieldRecurrence); updated(); } } //@cond PRIVATE #define ALT_DESC_FIELD "X-ALT-DESC" #define ALT_DESC_PARAMETERS QStringLiteral("FMTTYPE=text/html") //@endcond bool Incidence::hasAltDescription() const { const QString value = nonKDECustomProperty(ALT_DESC_FIELD); const QString parameter = nonKDECustomPropertyParameters(ALT_DESC_FIELD); return parameter == ALT_DESC_PARAMETERS && !value.isEmpty(); } void Incidence::setAltDescription(const QString &altdescription) { if (altdescription.isEmpty()) { removeNonKDECustomProperty(ALT_DESC_FIELD); } else { setNonKDECustomProperty(ALT_DESC_FIELD, altdescription, ALT_DESC_PARAMETERS); } } QString Incidence::altDescription() const { if (!hasAltDescription()) { return QString(); } else { return nonKDECustomProperty(ALT_DESC_FIELD); } } /** static */ QStringList Incidence::mimeTypes() { return QStringList() << QStringLiteral("text/calendar") - << KCalCore::Event::eventMimeType() - << KCalCore::Todo::todoMimeType() - << KCalCore::Journal::journalMimeType(); + << KCalendarCore::Event::eventMimeType() + << KCalendarCore::Todo::todoMimeType() + << KCalendarCore::Journal::journalMimeType(); } void Incidence::serialize(QDataStream &out) const { serializeQDateTimeAsKDateTime(out, d->mCreated); out << d->mRevision << d->mDescription << d->mDescriptionIsRich << d->mSummary << d->mSummaryIsRich << d->mLocation << d->mLocationIsRich << d->mCategories << d->mResources << d->mStatusString << d->mPriority << d->mSchedulingID << d->mGeoLatitude << d->mGeoLongitude << d->mHasGeo; serializeQDateTimeAsKDateTime(out, d->mRecurrenceId); out << d->mThisAndFuture << d->mLocalOnly << d->mStatus << d->mSecrecy << (d->mRecurrence ? true : false) << d->mAttachments.count() << d->mAlarms.count() << d->mRelatedToUid; if (d->mRecurrence) { out << d->mRecurrence; } for (const Attachment &attachment : qAsConst(d->mAttachments)) { out << attachment; } for (const Alarm::Ptr &alarm : qAsConst(d->mAlarms)) { out << alarm; } } void Incidence::deserialize(QDataStream &in) { quint32 status, secrecy; bool hasRecurrence; int attachmentCount, alarmCount; QMap relatedToUid; deserializeKDateTimeAsQDateTime(in, d->mCreated); in >> d->mRevision >> d->mDescription >> d->mDescriptionIsRich >> d->mSummary >> d->mSummaryIsRich >> d->mLocation >> d->mLocationIsRich >> d->mCategories >> d->mResources >> d->mStatusString >> d->mPriority >> d->mSchedulingID >> d->mGeoLatitude >> d->mGeoLongitude >> d->mHasGeo; deserializeKDateTimeAsQDateTime(in, d->mRecurrenceId); in >> d->mThisAndFuture >> d->mLocalOnly >> status >> secrecy >> hasRecurrence >> attachmentCount >> alarmCount >> relatedToUid; if (hasRecurrence) { d->mRecurrence = new Recurrence(); - d->mRecurrence->addObserver(const_cast(this)); + d->mRecurrence->addObserver(const_cast(this)); in >> d->mRecurrence; } d->mAttachments.clear(); d->mAlarms.clear(); d->mAttachments.reserve(attachmentCount); for (int i = 0; i < attachmentCount; ++i) { Attachment attachment; in >> attachment; d->mAttachments.append(attachment); } d->mAlarms.reserve(alarmCount); for (int i = 0; i < alarmCount; ++i) { Alarm::Ptr alarm = Alarm::Ptr(new Alarm(this)); in >> alarm; d->mAlarms.append(alarm); } d->mStatus = static_cast(status); d->mSecrecy = static_cast(secrecy); d->mRelatedToUid.clear(); auto it = relatedToUid.cbegin(), end = relatedToUid.cend(); for (; it != end; ++it) { d->mRelatedToUid.insert(static_cast(it.key()), it.value()); } } QVariantList Incidence::attachmentsVariant() const { QVariantList l; l.reserve(d->mAttachments.size()); std::transform(d->mAttachments.begin(), d->mAttachments.end(), std::back_inserter(l), [](const Attachment &att) { return QVariant::fromValue(att); }); return l; } diff --git a/src/incidence.h b/src/incidence.h index be1090a86..5f828f427 100644 --- a/src/incidence.h +++ b/src/incidence.h @@ -1,876 +1,876 @@ /* This file is part of the kcalcore library. Copyright (c) 2001-2003 Cornelius Schumacher Copyright (C) 2003-2004 Reinhold Kainhofer This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the Incidence class. @author Cornelius Schumacher \ @author Reinhold Kainhofer \ */ #ifndef KCALCORE_INCIDENCE_H #define KCALCORE_INCIDENCE_H #include "kcalcore_export.h" #include "alarm.h" #include "attachment.h" #include "incidencebase.h" #include "recurrence.h" #include //@cond PRIVATE // Value used to signal invalid/unset latitude or longitude. #define INVALID_LATLON 255.0 //krazy:exclude=defines (part of the API) //@endcond -namespace KCalCore +namespace KCalendarCore { /** @brief Provides the abstract base class common to non-FreeBusy (Events, To-dos, Journals) calendar components known as incidences. Several properties are not allowed for VFREEBUSY objects (see rfc:2445), so they are not in IncidenceBase. The hierarchy is: IncidenceBase + FreeBusy + Incidence + Event + Todo + Journal So IncidenceBase contains all properties that are common to all classes, and Incidence contains all additional properties that are common to Events, Todos and Journals, but are not allowed for FreeBusy entries. */ class KCALCORE_EXPORT Incidence : public IncidenceBase, public Recurrence::RecurrenceObserver { Q_GADGET Q_PROPERTY(QString description READ description WRITE setDescription) Q_PROPERTY(QString summary READ summary WRITE setSummary) Q_PROPERTY(QString location READ location WRITE setLocation) Q_PROPERTY(QStringList categories READ categories WRITE setCategories) Q_PROPERTY(int priority READ priority WRITE setPriority) Q_PROPERTY(QDateTime created READ created WRITE setCreated) - Q_PROPERTY(KCalCore::Incidence::Secrecy secrecy READ secrecy WRITE setSecrecy) - Q_PROPERTY(KCalCore::Incidence::Status status READ status WRITE setStatus) + Q_PROPERTY(KCalendarCore::Incidence::Secrecy secrecy READ secrecy WRITE setSecrecy) + Q_PROPERTY(KCalendarCore::Incidence::Status status READ status WRITE setStatus) Q_PROPERTY(QVariantList attachments READ attachmentsVariant) public: /** The different types of overall incidence status or confirmation. The meaning is specific to the incidence type in context. */ enum Status { StatusNone, /**< No status */ StatusTentative, /**< event is tentative */ StatusConfirmed, /**< event is definite */ StatusCompleted, /**< to-do completed */ StatusNeedsAction, /**< to-do needs action */ StatusCanceled, /**< event or to-do canceled; journal removed */ StatusInProcess, /**< to-do in process */ StatusDraft, /**< journal is draft */ StatusFinal, /**< journal is final */ StatusX /**< a non-standard status string */ }; Q_ENUM(Status) /** The different types of incidence access classifications. */ enum Secrecy { SecrecyPublic, /**< Not secret (default) */ SecrecyPrivate, /**< Secret to the owner */ SecrecyConfidential /**< Secret to the owner and some others */ }; Q_ENUM(Secrecy) /** The different types of RELTYPE values specified by the RFC. Only RelTypeParent is supported for now. */ enum RelType { RelTypeParent, /**< The related incidence is a parent. */ RelTypeChild, /**< The related incidence is a child. */ RelTypeSibling /**< The related incidence is a peer. */ }; /** A shared pointer to an Incidence. */ typedef QSharedPointer Ptr; /** List of incidences. */ typedef QVector List; /** Constructs an empty incidence.* */ Incidence(); /** Destroys an incidence. */ ~Incidence() override; /** Returns an exact copy of this incidence. The returned object is owned by the caller. Dirty fields are cleared. */ virtual Incidence *clone() const = 0; /** Returns a unique identifier for a specific instance of an incidence. - Due to the recurrence-id, the uid is not unique for a KCalCore::Incidence. + Due to the recurrence-id, the uid is not unique for a KCalendarCore::Incidence. @since 4.11 */ Q_REQUIRED_RESULT QString instanceIdentifier() const; /** Set readonly state of incidence. @param readonly If true, the incidence is set to readonly, if false the incidence is set to readwrite. */ void setReadOnly(bool readonly) override; /** @copydoc IncidenceBase::setLastModified(). */ void setLastModified(const QDateTime &lm) override; /** Set localOnly state of incidence. A local only incidence can be updated but it will not increase the revision number neither the modified date. @param localonly If true, the incidence is set to localonly, if false the incidence is set to normal stat. */ void setLocalOnly(bool localonly); /** Get the localOnly status. @return true if Local only, false otherwise. @see setLocalOnly() */ Q_REQUIRED_RESULT bool localOnly() const; /** @copydoc IncidenceBase::setAllDay(). */ void setAllDay(bool allDay) override; /** Recreate incidence. The incidence is made a new unique incidence, but already stored information is preserved. Sets unique id, creation date, last modification date and revision number. */ void recreate(); /** Sets the incidence creation date/time. It is stored as a UTC date/time. @param dt is the creation date/time. @see created(). */ void setCreated(const QDateTime &dt); /** Returns the incidence creation date/time. @see setCreated(). */ Q_REQUIRED_RESULT QDateTime created() const; /** Sets the number of revisions this incidence has seen. @param rev is the incidence revision number. @see revision(). */ void setRevision(int rev); /** Returns the number of revisions this incidence has seen. @see setRevision(). */ Q_REQUIRED_RESULT int revision() const; /** Sets the incidence starting date/time. @param dt is the starting date/time. @see IncidenceBase::dtStart(). */ void setDtStart(const QDateTime &dt) override; /** @copydoc IncidenceBase::shiftTimes() */ void shiftTimes(const QTimeZone &oldZone, const QTimeZone &newZone) override; /** Sets the incidence description. @param description is the incidence description string. @param isRich if true indicates the description string contains richtext. @see description(). */ void setDescription(const QString &description, bool isRich); /** Sets the incidence description and tries to guess if the description is rich text. @param description is the incidence description string. @see description(). */ void setDescription(const QString &description); /** Returns the incidence description. @see setDescription(). @see richDescription(). */ Q_REQUIRED_RESULT QString description() const; /** Returns the incidence description in rich text format. @see setDescription(). @see description(). */ Q_REQUIRED_RESULT QString richDescription() const; /** Returns true if incidence description contains RichText; false otherwise. @see setDescription(), description(). */ Q_REQUIRED_RESULT bool descriptionIsRich() const; /** Sets the incidence summary. @param summary is the incidence summary string. @param isRich if true indicates the summary string contains richtext. @see summary(). */ void setSummary(const QString &summary, bool isRich); /** Sets the incidence summary and tries to guess if the summary is richtext. @param summary is the incidence summary string. @see summary(). */ void setSummary(const QString &summary); /** Returns the incidence summary. @see setSummary(). @see richSummary(). */ Q_REQUIRED_RESULT QString summary() const; /** Returns the incidence summary in rich text format. @see setSummary(). @see summary(). */ Q_REQUIRED_RESULT QString richSummary() const; /** Returns true if incidence summary contains RichText; false otherwise. @see setSummary(), summary(). */ Q_REQUIRED_RESULT bool summaryIsRich() const; /** Sets the incidence location. Do _not_ use with journals. @param location is the incidence location string. @param isRich if true indicates the location string contains richtext. @see location(). */ void setLocation(const QString &location, bool isRich); /** Sets the incidence location and tries to guess if the location is richtext. Do _not_ use with journals. @param location is the incidence location string. @see location(). */ void setLocation(const QString &location); /** Returns the incidence location. Do _not_ use with journals. @see setLocation(). @see richLocation(). */ Q_REQUIRED_RESULT QString location() const; /** Returns the incidence location in rich text format. @see setLocation(). @see location(). */ Q_REQUIRED_RESULT QString richLocation() const; /** Returns true if incidence location contains RichText; false otherwise. @see setLocation(), location(). */ Q_REQUIRED_RESULT bool locationIsRich() const; /** Sets the incidence category list. @param categories is a list of category strings. @see setCategories( const QString &), categories(). */ void setCategories(const QStringList &categories); /** Sets the incidence category list based on a comma delimited string. @param catStr is a QString containing a list of categories which are delimited by a comma character. @see setCategories( const QStringList &), categories(). */ void setCategories(const QString &catStr); /** Returns the incidence categories as a list of strings. @see setCategories( const QStringList &), setCategories( const QString &). */ Q_REQUIRED_RESULT QStringList categories() const; /** Returns the incidence categories as a comma separated string. @see categories(). */ Q_REQUIRED_RESULT QString categoriesStr() const; /** Relates another incidence to this one, by UID. This function should only be used when constructing a calendar before the related incidence exists. @param uid is a QString containing a UID for another incidence. @param relType specifies the relation type. - @warning KCalCore only supports one related-to field per reltype for now. + @warning KCalendarCore only supports one related-to field per reltype for now. @see relatedTo(). */ void setRelatedTo(const QString &uid, RelType relType = RelTypeParent); /** Returns a UID string for the incidence that is related to this one. This function should only be used when constructing a calendar before the related incidence exists. - @warning KCalCore only supports one related-to field per reltype for now. + @warning KCalendarCore only supports one related-to field per reltype for now. @param relType specifies the relation type. @see setRelatedTo(). */ Q_REQUIRED_RESULT QString relatedTo(RelType relType = RelTypeParent) const; // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // %%%%% Convenience wrappers for property handling // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% /** Returns true if the alternative (=text/html) description is available. @see setAltDescription(), altDescription() */ Q_REQUIRED_RESULT bool hasAltDescription() const; /** Sets the incidence's alternative (=text/html) description. If the text is empty, the property is removed. @param altdescription is the incidence altdescription string. @see altAltdescription(). */ void setAltDescription(const QString &altdescription); /** Returns the incidence alternative (=text/html) description. @see setAltDescription(). */ Q_REQUIRED_RESULT QString altDescription() const; // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // %%%%% Recurrence-related methods // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% /** Returns the recurrence rule associated with this incidence. If there is none, returns an appropriate (non-0) object. */ Recurrence *recurrence() const; /** Removes all recurrence and exception rules and dates. */ void clearRecurrence(); /** @copydoc Recurrence::recurs() */ Q_REQUIRED_RESULT bool recurs() const; /** @copydoc Recurrence::recurrenceType() */ Q_REQUIRED_RESULT ushort recurrenceType() const; /** @copydoc Recurrence::recursOn() */ virtual bool recursOn(const QDate &date, const QTimeZone &timeZone) const; /** @copydoc Recurrence::recursAt() */ Q_REQUIRED_RESULT bool recursAt(const QDateTime &dt) const; /** Calculates the start date/time for all recurrences that happen at some time on the given date (might start before that date, but end on or after the given date). @param date the date when the incidence should occur @param timeSpec time specification for @p date. @return the start date/time of all occurrences that overlap with the given date; an empty list if the incidence does not overlap with the date at all. */ virtual QList startDateTimesForDate(const QDate &date, const QTimeZone &timeZone) const; /** Calculates the start date/time for all recurrences that happen at the given time. @param datetime the date/time when the incidence should occur. @return the start date/time of all occurrences that overlap with the given date/time; an empty list if the incidence does not happen at the given time at all. */ Q_REQUIRED_RESULT virtual QList startDateTimesForDateTime(const QDateTime &datetime) const; /** Returns the end date/time of the incidence occurrence if it starts at specified date/time. @param startDt is the specified starting date/time. @return the corresponding end date/time for the occurrence; or the start date/time if the end date/time is invalid; or the end date/time if the start date/time is invalid. */ Q_REQUIRED_RESULT virtual QDateTime endDateForStart(const QDateTime &startDt) const; // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // %%%%% Attachment-related methods // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% /** Adds an attachment to the incidence. @param attachment a valid Attachment object. */ void addAttachment(const Attachment &attachment); /** Removes all attachments of the specified MIME type from the incidence. The memory used by all the removed attachments is freed. @param mime is a QString containing the MIME type. @see deleteAttachment(). */ void deleteAttachments(const QString &mime); /** Returns a list of all incidence attachments. @see attachments( const QString &). */ Q_REQUIRED_RESULT Attachment::List attachments() const; /** Returns a list of all incidence attachments with the specified MIME type. @param mime is a QString containing the MIME type. @see attachments(). */ Q_REQUIRED_RESULT Attachment::List attachments(const QString &mime) const; /** Removes all attachments and frees the memory used by them. @see deleteAttachments( const QString &). */ void clearAttachments(); // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // %%%%% Secrecy and Status methods // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% /** Sets the incidence #Secrecy. @param secrecy is the incidence #Secrecy to set. @see secrecy(), secrecyStr(). */ void setSecrecy(Secrecy secrecy); /** Returns the incidence #Secrecy. @see setSecrecy(), secrecyStr(). */ Q_REQUIRED_RESULT Secrecy secrecy() const; /** Sets the incidence status to a standard #Status value. Note that StatusX cannot be specified. @param status is the incidence #Status to set. @see status(), setCustomStatus(). */ void setStatus(Status status); /** Sets the incidence #Status to a non-standard status value. @param status is a non-standard status string. If empty, the incidence #Status will be set to StatusNone. @see setStatus(), status() customStatus(). */ void setCustomStatus(const QString &status); /** Returns the non-standard status value. @see setCustomStatus(). */ Q_REQUIRED_RESULT QString customStatus() const; /** Returns the incidence #Status. @see setStatus(), setCustomStatus(), statusStr(). */ Q_REQUIRED_RESULT Status status() const; // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // %%%%% Other methods // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% /** Sets a list of incidence resources. (Note: resources in this context means items used by the incidence such as money, fuel, hours, etc). @param resources is a list of resource strings. @see resources(). */ void setResources(const QStringList &resources); /** Returns the incidence resources as a list of strings. @see setResources(). */ Q_REQUIRED_RESULT QStringList resources() const; /** Sets the incidences priority. The priority must be an integer value between 0 and 9, where 0 is undefined, 1 is the highest, and 9 is the lowest priority (decreasing order). @param priority is the incidence priority to set. @see priority(). */ void setPriority(int priority); /** Returns the incidence priority. @see setPriority(). */ Q_REQUIRED_RESULT int priority() const; /** Returns true if the incidence has geo data, otherwise return false. @see setHasGeo(), setGeoLatitude(float), setGeoLongitude(float). */ Q_REQUIRED_RESULT bool hasGeo() const; /** Sets if the incidence has geo data. @param hasGeo true if incidence has geo data, otherwise false @see hasGeo(), geoLatitude(), geoLongitude(). */ void setHasGeo(bool hasGeo); /** Set the incidences geoLatitude. @param geolatitude is the incidence geolatitude to set @see geoLatitude(). */ void setGeoLatitude(float geolatitude); /** Returns the incidence geoLatidude. @return incidences geolatitude value @see setGeoLatitude(). */ Q_REQUIRED_RESULT float geoLatitude() const; /** Set the incidencesgeoLongitude. @param geolongitude is the incidence geolongitude to set @see geoLongitude(). */ void setGeoLongitude(float geolongitude); /** Returns the incidence geoLongitude. @return incidences geolongitude value @see setGeoLongitude(). */ Q_REQUIRED_RESULT float geoLongitude() const; /** Returns true if the incidence has recurrenceId, otherwise return false. @see setRecurrenceId(QDateTime) */ Q_REQUIRED_RESULT bool hasRecurrenceId() const; /** Set the incidences recurrenceId. This field indicates that this is an exception to a recurring incidence. The uid of this incidence MUST be the same as the one of the recurring main incidence. @param recurrenceId is the incidence recurrenceId to set @see recurrenceId(). */ void setRecurrenceId(const QDateTime &recurrenceId); /** Returns the incidence recurrenceId. @return incidences recurrenceId value @see setRecurrenceId(). */ Q_REQUIRED_RESULT QDateTime recurrenceId() const override; /** Set to true if the exception also applies to all future occurrences. This option is only relevant if the incidence has a recurrenceId set. @param thisAndFuture value @see thisAndFuture(), setRecurrenceId() @since 4.11 */ void setThisAndFuture(bool thisAndFuture); /** Returns true if the exception also applies to all future occurrences. @return incidences thisAndFuture value @see setThisAndFuture() @since 4.11 */ Q_REQUIRED_RESULT bool thisAndFuture() const; // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // %%%%% Alarm-related methods // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% /** Returns a list of all incidence alarms. */ Q_REQUIRED_RESULT Alarm::List alarms() const; /** Create a new incidence alarm. */ Alarm::Ptr newAlarm(); /** Adds an alarm to the incidence. @param alarm is a pointer to a valid Alarm object. @see removeAlarm(). */ void addAlarm(const Alarm::Ptr &alarm); /** Removes the specified alarm from the incidence. @param alarm is a pointer to a valid Alarm object. @see addAlarm(). */ void removeAlarm(const Alarm::Ptr &alarm); /** Removes all alarms. @see removeAlarm(). */ void clearAlarms(); /** Returns true if any of the incidence alarms are enabled; false otherwise. */ Q_REQUIRED_RESULT bool hasEnabledAlarms() const; // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // %%%%% Other methods // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% /** Set the incidence scheduling ID. Do _not_ use with journals. This is used for accepted invitations as the place to store the UID of the invitation. It is later used again if updates to the invitation comes in. If we did not set a new UID on incidences from invitations, we can end up with more than one resource having events with the same UID, if you have access to other peoples resources. While constructing an incidence, when setting the scheduling ID, you will always want to set the incidence UID too. Instead of calling setUID() separately, you can pass the UID through @p uid so both members are changed in one atomic operation ( don't forget that setUID() emits incidenceUpdated() and whoever catches that signal will have an half-initialized incidence, therefore, always set the schedulingID and UID at the same time, and never with two separate calls). @param sid is a QString containing the scheduling ID. @param uid is a QString containing the incidence UID to set, if not specified, the current UID isn't changed, and this parameter is ignored. @see schedulingID(). */ void setSchedulingID(const QString &sid, const QString &uid = QString()); /** Returns the incidence scheduling ID. Do _not_ use with journals. If a scheduling ID is not set, then return the incidence UID. @see setSchedulingID(). */ Q_REQUIRED_RESULT QString schedulingID() const; /** Observer interface for the recurrence class. If the recurrence is changed, this method will be called for the incidence the recurrence object belongs to. @param recurrence is a pointer to a valid Recurrence object. */ void recurrenceUpdated(Recurrence *recurrence) override; /** Returns the name of the icon that best represents this incidence. @param recurrenceId Some recurring incidences might use a different icon, for example, completed to-do occurrences. Use this parameter to identify the specific occurrence in a recurring serie. */ virtual QLatin1String iconName(const QDateTime &recurrenceId = {}) const = 0; /** * Returns true if the incidence type supports groupware communication. * @since 4.10 */ virtual bool supportsGroupwareCommunication() const = 0; /** Returns the list of possible mime types in an Incidence object: "text/calendar" "application/x-vnd.akonadi.calendar.event" "application/x-vnd.akonadi.calendar.todo" "application/x-vnd.akonadi.calendar.journal" @since 4.12 */ Q_REQUIRED_RESULT static QStringList mimeTypes(); protected: /** Copy constructor. @param other is the incidence to copy. */ Incidence(const Incidence &other); /** Compares this with Incidence @p incidence for equality. @param incidence is the Incidence to compare against. @return true if the incidences are equal; false otherwise. */ bool equals(const IncidenceBase &incidence) const override; /** @copydoc IncidenceBase::assign() */ IncidenceBase &assign(const IncidenceBase &other) override; void serialize(QDataStream &out) const override; void deserialize(QDataStream &in) override; private: /** Disabled, not polymorphic. Use IncidenceBase::operator= which is safe because it calls virtual function assign. @param other is another Incidence object to assign to this one. */ Incidence &operator=(const Incidence &other); Q_DECL_HIDDEN QVariantList attachmentsVariant() const; //@cond PRIVATE class Private; Private *const d; //@endcond }; } //@cond PRIVATE -inline uint qHash(const QSharedPointer &key) +inline uint qHash(const QSharedPointer &key) { return qHash(key.data()); } //@endcond //@cond PRIVATE -Q_DECLARE_TYPEINFO(KCalCore::Incidence::Ptr, Q_MOVABLE_TYPE); -Q_DECLARE_METATYPE(KCalCore::Incidence *) +Q_DECLARE_TYPEINFO(KCalendarCore::Incidence::Ptr, Q_MOVABLE_TYPE); +Q_DECLARE_METATYPE(KCalendarCore::Incidence *) //@endcond #endif diff --git a/src/incidencebase.cpp b/src/incidencebase.cpp index 14f7cacb5..a7e7e3c13 100644 --- a/src/incidencebase.cpp +++ b/src/incidencebase.cpp @@ -1,752 +1,752 @@ /* This file is part of the kcalcore library. Copyright (c) 2001,2004 Cornelius Schumacher Copyright (C) 2003-2004 Reinhold Kainhofer Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). All rights reserved. Contact: Alvaro Manera This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the IncidenceBase class. @brief An abstract base class that provides a common base for all calendar incidence classes. @author Cornelius Schumacher \ @author Reinhold Kainhofer \ */ #include "incidencebase.h" #include "calformat.h" #include "visitor.h" #include "utils.h" #include #include "kcalcore_debug.h" #include #include #define KCALCORE_MAGIC_NUMBER 0xCA1C012E #define KCALCORE_SERIALIZATION_VERSION 1 -using namespace KCalCore; +using namespace KCalendarCore; /** Private class that helps to provide binary compatibility between releases. @internal */ //@cond PRIVATE -class Q_DECL_HIDDEN KCalCore::IncidenceBase::Private +class Q_DECL_HIDDEN KCalendarCore::IncidenceBase::Private { public: Private() : mUpdateGroupLevel(0) , mUpdatedPending(false) , mAllDay(false) , mHasDuration(false) { } Private(const Private &other) : mUpdateGroupLevel(0) , mUpdatedPending(false) , mAllDay(true) , mHasDuration(false) { init(other); } ~Private() { } void init(const Private &other); QDateTime mLastModified; // incidence last modified date QDateTime mDtStart; // incidence start time Person mOrganizer; // incidence person (owner) QString mUid; // incidence unique id Duration mDuration; // incidence duration int mUpdateGroupLevel; // if non-zero, suppresses update() calls bool mUpdatedPending = false; // true if an update has occurred since startUpdates() bool mAllDay = false; // true if the incidence is all-day bool mHasDuration = false; // true if the incidence has a duration Attendee::List mAttendees; // list of incidence attendees QStringList mComments; // list of incidence comments QStringList mContacts; // list of incidence contacts QList mObservers; // list of incidence observers QSet mDirtyFields; // Fields that changed since last time the incidence was created // or since resetDirtyFlags() was called QUrl mUrl; // incidence url property }; void IncidenceBase::Private::init(const Private &other) { mLastModified = other.mLastModified; mDtStart = other.mDtStart; mOrganizer = other.mOrganizer; mUid = other.mUid; mDuration = other.mDuration; mAllDay = other.mAllDay; mHasDuration = other.mHasDuration; mComments = other.mComments; mContacts = other.mContacts; mAttendees = other.mAttendees; mAttendees.reserve(other.mAttendees.count()); mUrl = other.mUrl; } //@endcond IncidenceBase::IncidenceBase() - : d(new KCalCore::IncidenceBase::Private) + : d(new KCalendarCore::IncidenceBase::Private) { mReadOnly = false; setUid(CalFormat::createUniqueId()); } IncidenceBase::IncidenceBase(const IncidenceBase &i) : CustomProperties(i) - , d(new KCalCore::IncidenceBase::Private(*i.d)) + , d(new KCalendarCore::IncidenceBase::Private(*i.d)) { mReadOnly = i.mReadOnly; } IncidenceBase::~IncidenceBase() { delete d; } IncidenceBase &IncidenceBase::operator=(const IncidenceBase &other) { Q_ASSERT(type() == other.type()); startUpdates(); // assign is virtual, will call the derived class's IncidenceBase &ret = assign(other); endUpdates(); return ret; } IncidenceBase &IncidenceBase::assign(const IncidenceBase &other) { CustomProperties::operator=(other); d->init(*other.d); mReadOnly = other.mReadOnly; d->mDirtyFields.clear(); d->mDirtyFields.insert(FieldUnknown); return *this; } bool IncidenceBase::operator==(const IncidenceBase &i2) const { if (i2.type() != type()) { return false; } else { // equals is virtual, so here we're calling the derived class method return equals(i2); } } bool IncidenceBase::operator!=(const IncidenceBase &i2) const { return !operator==(i2); } bool IncidenceBase::equals(const IncidenceBase &i2) const { if (attendees().count() != i2.attendees().count()) { // qCDebug(KCALCORE_LOG) << "Attendee count is different"; return false; } Attendee::List al1 = attendees(); Attendee::List al2 = i2.attendees(); Attendee::List::ConstIterator a1 = al1.constBegin(); Attendee::List::ConstIterator a2 = al2.constBegin(); //TODO Does the order of attendees in the list really matter? //Please delete this comment if you know it's ok, kthx for (; a1 != al1.constEnd() && a2 != al2.constEnd(); ++a1, ++a2) { if (!(*a1 == *a2)) { // qCDebug(KCALCORE_LOG) << "Attendees are different"; return false; } } if (!CustomProperties::operator==(i2)) { // qCDebug(KCALCORE_LOG) << "Properties are different"; return false; } // Don't compare lastModified, otherwise the operator is not // of much use. We are not comparing for identity, after all. // no need to compare mObserver bool a = ((dtStart() == i2.dtStart()) || (!dtStart().isValid() && !i2.dtStart().isValid())); bool b = organizer() == i2.organizer(); bool c = uid() == i2.uid(); bool d = allDay() == i2.allDay(); bool e = duration() == i2.duration(); bool f = hasDuration() == i2.hasDuration(); bool g = url() == i2.url(); //qCDebug(KCALCORE_LOG) << a << b << c << d << e << f << g; return a && b && c && d && e && f && g; } bool IncidenceBase::accept(Visitor &v, const IncidenceBase::Ptr &incidence) { Q_UNUSED(v); Q_UNUSED(incidence); return false; } void IncidenceBase::setUid(const QString &uid) { if (d->mUid != uid) { update(); d->mUid = uid; d->mDirtyFields.insert(FieldUid); updated(); } } QString IncidenceBase::uid() const { return d->mUid; } void IncidenceBase::setLastModified(const QDateTime &lm) { // DON'T! updated() because we call this from // Calendar::updateEvent(). d->mDirtyFields.insert(FieldLastModified); // Convert to UTC and remove milliseconds part. QDateTime current = lm.toUTC(); QTime t = current.time(); t.setHMS(t.hour(), t.minute(), t.second(), 0); current.setTime(t); d->mLastModified = current; } QDateTime IncidenceBase::lastModified() const { return d->mLastModified; } void IncidenceBase::setOrganizer(const Person &organizer) { update(); // we don't check for readonly here, because it is // possible that by setting the organizer we are changing // the event's readonly status... d->mOrganizer = organizer; d->mDirtyFields.insert(FieldOrganizer); updated(); } void IncidenceBase::setOrganizer(const QString &o) { QString mail(o); if (mail.startsWith(QLatin1String("MAILTO:"), Qt::CaseInsensitive)) { mail = mail.remove(0, 7); } // split the string into full name plus email. const Person organizer = Person::fromFullName(mail); setOrganizer(organizer); } Person IncidenceBase::organizer() const { return d->mOrganizer; } void IncidenceBase::setReadOnly(bool readOnly) { mReadOnly = readOnly; } bool IncidenceBase::isReadOnly() const { return mReadOnly; } void IncidenceBase::setDtStart(const QDateTime &dtStart) { // if ( mReadOnly ) return; if (!dtStart.isValid() && type() != IncidenceBase::TypeTodo) { qCWarning(KCALCORE_LOG) << "Invalid dtStart"; } if (d->mDtStart != dtStart) { update(); d->mDtStart = dtStart; d->mDirtyFields.insert(FieldDtStart); updated(); } } QDateTime IncidenceBase::dtStart() const { return d->mDtStart; } bool IncidenceBase::allDay() const { return d->mAllDay; } void IncidenceBase::setAllDay(bool f) { if (mReadOnly || f == d->mAllDay) { return; } update(); d->mAllDay = f; if (d->mDtStart.isValid()) { d->mDirtyFields.insert(FieldDtStart); } updated(); } void IncidenceBase::shiftTimes(const QTimeZone &oldZone, const QTimeZone &newZone) { update(); d->mDtStart = d->mDtStart.toTimeZone(oldZone); d->mDtStart.setTimeZone(newZone); d->mDirtyFields.insert(FieldDtStart); d->mDirtyFields.insert(FieldDtEnd); updated(); } void IncidenceBase::addComment(const QString &comment) { d->mComments += comment; } bool IncidenceBase::removeComment(const QString &comment) { bool found = false; QStringList::Iterator i; for (i = d->mComments.begin(); !found && i != d->mComments.end(); ++i) { if ((*i) == comment) { found = true; d->mComments.erase(i); } } if (found) { d->mDirtyFields.insert(FieldComment); } return found; } void IncidenceBase::clearComments() { d->mDirtyFields.insert(FieldComment); d->mComments.clear(); } QStringList IncidenceBase::comments() const { return d->mComments; } void IncidenceBase::addContact(const QString &contact) { if (!contact.isEmpty()) { d->mContacts += contact; d->mDirtyFields.insert(FieldContact); } } bool IncidenceBase::removeContact(const QString &contact) { bool found = false; QStringList::Iterator i; for (i = d->mContacts.begin(); !found && i != d->mContacts.end(); ++i) { if ((*i) == contact) { found = true; d->mContacts.erase(i); } } if (found) { d->mDirtyFields.insert(FieldContact); } return found; } void IncidenceBase::clearContacts() { d->mDirtyFields.insert(FieldContact); d->mContacts.clear(); } QStringList IncidenceBase::contacts() const { return d->mContacts; } void IncidenceBase::addAttendee(const Attendee &a, bool doupdate) { if (a.isNull() || mReadOnly) { return; } Q_ASSERT(!a.uid().isEmpty()); if (doupdate) { update(); } d->mAttendees.append(a); if (doupdate) { d->mDirtyFields.insert(FieldAttendees); updated(); } } Attendee::List IncidenceBase::attendees() const { return d->mAttendees; } int IncidenceBase::attendeeCount() const { return d->mAttendees.count(); } void IncidenceBase::setAttendees(const Attendee::List &attendees, bool doUpdate) { if (mReadOnly) { return; } if (doUpdate) { update(); } // don't simply assign, we need the logic in addAttendee here too clearAttendees(); d->mAttendees.reserve(attendees.size()); for (const auto &a : attendees) { addAttendee(a, false); } if (doUpdate) { d->mDirtyFields.insert(FieldAttendees); updated(); } } void IncidenceBase::clearAttendees() { if (mReadOnly) { return; } d->mDirtyFields.insert(FieldAttendees); d->mAttendees.clear(); } Attendee IncidenceBase::attendeeByMail(const QString &email) const { Attendee::List::ConstIterator it; for (it = d->mAttendees.constBegin(); it != d->mAttendees.constEnd(); ++it) { if ((*it).email() == email) { return *it; } } return {}; } Attendee IncidenceBase::attendeeByMails(const QStringList &emails, const QString &email) const { QStringList mails = emails; if (!email.isEmpty()) { mails.append(email); } Attendee::List::ConstIterator itA; for (itA = d->mAttendees.constBegin(); itA != d->mAttendees.constEnd(); ++itA) { for (QStringList::const_iterator it = mails.constBegin(); it != mails.constEnd(); ++it) { if ((*itA).email() == (*it)) { return *itA; } } } return {}; } Attendee IncidenceBase::attendeeByUid(const QString &uid) const { Attendee::List::ConstIterator it; for (it = d->mAttendees.constBegin(); it != d->mAttendees.constEnd(); ++it) { if ((*it).uid() == uid) { return *it; } } return {}; } void IncidenceBase::setDuration(const Duration &duration) { update(); d->mDuration = duration; setHasDuration(true); d->mDirtyFields.insert(FieldDuration); updated(); } Duration IncidenceBase::duration() const { return d->mDuration; } void IncidenceBase::setHasDuration(bool hasDuration) { d->mHasDuration = hasDuration; } bool IncidenceBase::hasDuration() const { return d->mHasDuration; } void IncidenceBase::setUrl(const QUrl &url) { d->mDirtyFields.insert(FieldUrl); d->mUrl = url; } QUrl IncidenceBase::url() const { return d->mUrl; } void IncidenceBase::registerObserver(IncidenceBase::IncidenceObserver *observer) { if (observer && !d->mObservers.contains(observer)) { d->mObservers.append(observer); } } void IncidenceBase::unRegisterObserver(IncidenceBase::IncidenceObserver *observer) { d->mObservers.removeAll(observer); } void IncidenceBase::update() { if (!d->mUpdateGroupLevel) { d->mUpdatedPending = true; const auto rid = recurrenceId(); for (IncidenceObserver *o : qAsConst(d->mObservers)) { o->incidenceUpdate(uid(), rid); } } } void IncidenceBase::updated() { if (d->mUpdateGroupLevel) { d->mUpdatedPending = true; } else { const auto rid = recurrenceId(); for (IncidenceObserver *o : qAsConst(d->mObservers)) { o->incidenceUpdated(uid(), rid); } } } void IncidenceBase::startUpdates() { update(); ++d->mUpdateGroupLevel; } void IncidenceBase::endUpdates() { if (d->mUpdateGroupLevel > 0) { if (--d->mUpdateGroupLevel == 0 && d->mUpdatedPending) { d->mUpdatedPending = false; updated(); } } } void IncidenceBase::customPropertyUpdate() { update(); } void IncidenceBase::customPropertyUpdated() { updated(); } QDateTime IncidenceBase::recurrenceId() const { return QDateTime(); } void IncidenceBase::resetDirtyFields() { d->mDirtyFields.clear(); } QSet IncidenceBase::dirtyFields() const { return d->mDirtyFields; } void IncidenceBase::setFieldDirty(IncidenceBase::Field field) { d->mDirtyFields.insert(field); } QUrl IncidenceBase::uri() const { return QUrl(QStringLiteral("urn:x-ical:") + uid()); } void IncidenceBase::setDirtyFields(const QSet &dirtyFields) { d->mDirtyFields = dirtyFields; } void IncidenceBase::serialize(QDataStream &out) const { Q_UNUSED(out); } void IncidenceBase::deserialize(QDataStream &in) { Q_UNUSED(in); } /** static */ quint32 IncidenceBase::magicSerializationIdentifier() { return KCALCORE_MAGIC_NUMBER; } -QDataStream &KCalCore::operator<<(QDataStream &out, const KCalCore::IncidenceBase::Ptr &i) +QDataStream &KCalendarCore::operator<<(QDataStream &out, const KCalendarCore::IncidenceBase::Ptr &i) { if (!i) { return out; } - out << static_cast(KCALCORE_MAGIC_NUMBER); // Magic number to identify KCalCore data + out << static_cast(KCALCORE_MAGIC_NUMBER); // Magic number to identify KCalendarCore data out << static_cast(KCALCORE_SERIALIZATION_VERSION); out << static_cast(i->type()); out << *(static_cast(i.data())); serializeQDateTimeAsKDateTime(out, i->d->mLastModified); serializeQDateTimeAsKDateTime(out, i->d->mDtStart); out << i->organizer() << i->d->mUid << i->d->mDuration << i->d->mAllDay << i->d->mHasDuration << i->d->mComments << i->d->mContacts << i->d->mAttendees.count() << i->d->mUrl; for (const Attendee &attendee : qAsConst(i->d->mAttendees)) { out << attendee; } // Serialize the sub-class data. i->serialize(out); return out; } -QDataStream &KCalCore::operator>>(QDataStream &in, KCalCore::IncidenceBase::Ptr &i) +QDataStream &KCalendarCore::operator>>(QDataStream &in, KCalendarCore::IncidenceBase::Ptr &i) { if (!i) { return in; } qint32 attendeeCount, type; quint32 magic, version; in >> magic; if (magic != KCALCORE_MAGIC_NUMBER) { qCWarning(KCALCORE_LOG) << "Invalid magic on serialized data"; return in; } in >> version; if (version > KCALCORE_MAGIC_NUMBER) { qCWarning(KCALCORE_LOG) << "Invalid version on serialized data"; return in; } in >> type; in >> *(static_cast(i.data())); deserializeKDateTimeAsQDateTime(in, i->d->mLastModified); deserializeKDateTimeAsQDateTime(in, i->d->mDtStart); in >> i->d->mOrganizer >> i->d->mUid >> i->d->mDuration >> i->d->mAllDay >> i->d->mHasDuration >> i->d->mComments >> i->d->mContacts >> attendeeCount >> i->d->mUrl; i->d->mAttendees.clear(); i->d->mAttendees.reserve(attendeeCount); for (int it = 0; it < attendeeCount; it++) { Attendee attendee; in >> attendee; i->d->mAttendees.append(attendee); } // Deserialize the sub-class data. i->deserialize(in); return in; } IncidenceBase::IncidenceObserver::~IncidenceObserver() { } QVariantList IncidenceBase::attendeesVariant() const { QVariantList l; l.reserve(d->mAttendees.size()); std::transform(d->mAttendees.begin(), d->mAttendees.end(), std::back_inserter(l), [](const Attendee &a) { return QVariant::fromValue(a); }); return l; } diff --git a/src/incidencebase.h b/src/incidencebase.h index a1d3a95ca..94a93eac5 100644 --- a/src/incidencebase.h +++ b/src/incidencebase.h @@ -1,786 +1,786 @@ /* This file is part of the kcalcore library. Copyright (c) 2001-2003 Cornelius Schumacher Copyright (c) 2003-2004 Reinhold Kainhofer Copyright (c) 2005 Rafal Rzepecki Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). All rights reserved. Contact: Alvaro Manera This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the IncidenceBase class. @author Cornelius Schumacher \ @author Reinhold Kainhofer \ @author Rafal Rzepecki \ @glossary @anchor incidence @b incidence: General term for a calendar component. Examples are events, to-dos, and journals. @glossary @anchor event @b event: An @ref incidence that has a start and end time, typically representing some occurrence of social or personal importance. May be recurring. Examples are appointments, meetings, or holidays. @glossary @anchor to-do @b to-do: An @ref incidence that has an optional start time and an optional due time typically representing some undertaking to be performed. May be recurring. Examples are "fix the bug" or "pay the bills". @glossary @anchor todo @b todo: See @ref to-do. @glossary @anchor journal @b journal: An @ref incidence with a start date that represents a diary or daily record of one's activities. May @b not be recurring. */ #ifndef KCALCORE_INCIDENCEBASE_H #define KCALCORE_INCIDENCEBASE_H #include "attendee.h" #include "customproperties.h" #include "duration.h" #include "person.h" #include #include #include #include #include class QUrl; class QDate; class QTimeZone; -namespace KCalCore { +namespace KCalendarCore { /** List of dates */ typedef QList DateList; /** List of times */ typedef QList DateTimeList; class Event; class Todo; class Journal; class FreeBusy; class Visitor; /** @brief An abstract class that provides a common base for all calendar incidence classes. define: organizer (person) define: uid (same as the attendee uid?) Several properties are not allowed for VFREEBUSY objects (see rfc:2445), so they are not in IncidenceBase. The hierarchy is: IncidenceBase + FreeBusy + Incidence + Event + Todo + Journal So IncidenceBase contains all properties that are common to all classes, and Incidence contains all additional properties that are common to Events, Todos and Journals, but are not allowed for FreeBusy entries. */ class KCALCORE_EXPORT IncidenceBase : public CustomProperties { Q_GADGET Q_PROPERTY(QString uid READ uid WRITE setUid) Q_PROPERTY(QDateTime lastModified READ lastModified WRITE setLastModified) Q_PROPERTY(QDateTime dtStart READ dtStart WRITE setDtStart) Q_PROPERTY(bool allDay READ allDay WRITE setAllDay) - Q_PROPERTY(KCalCore::Person organizer READ organizer WRITE setOrganizer) + Q_PROPERTY(KCalendarCore::Person organizer READ organizer WRITE setOrganizer) Q_PROPERTY(QVariantList attendees READ attendeesVariant) public: /** A shared pointer to an IncidenceBase. */ typedef QSharedPointer Ptr; /** The different types of incidences, per RFC2445. @see type(), typeStr() */ enum IncidenceType { TypeEvent = 0, /**< Type is an event */ TypeTodo, /**< Type is a to-do */ TypeJournal, /**< Type is a journal */ TypeFreeBusy, /**< Type is a free/busy */ TypeUnknown /**< Type unknown */ }; /** The different types of incidence date/times roles. @see dateTime() */ enum DateTimeRole { RoleAlarmStartOffset = 0,/**< Role for an incidence alarm's starting offset date/time */ RoleAlarmEndOffset, /**< Role for an incidence alarm's ending offset date/time */ RoleSort, /**< Role for an incidence's date/time used when sorting */ RoleCalendarHashing, /**< Role for looking up an incidence in a Calendar */ RoleStartTimeZone, /**< Role for determining an incidence's starting timezone */ RoleEndTimeZone, /**< Role for determining an incidence's ending timezone */ RoleEndRecurrenceBase, RoleEnd, /**< Role for determining an incidence's dtEnd, will return an invalid QDateTime if the incidence does not support dtEnd */ RoleDisplayEnd, /**< Role used for display purposes, represents the end boundary if an incidence supports dtEnd */ RoleAlarm, /**< Role for determining the date/time of the first alarm. Returns invalid time if the incidence doesn't have any alarm */ RoleRecurrenceStart, /**< Role for determining the start of the recurrence. Currently that's DTSTART for an event and DTDUE for a to-do. (NOTE: If the incidence is a to-do, recurrence should be calculated having DTSTART for a reference, not DT-DUE. - This is one place KCalCore isn't compliant with RFC2445) */ + This is one place KCalendarCore isn't compliant with RFC2445) */ RoleDisplayStart, /**< Role for display purposes, represents the start boundary of an incidence. To-dos return dtDue here, for historical reasons */ RoleDnD /**< Role for determining new start and end dates after a DnD */ }; /** The different types of incidence fields. */ enum Field { FieldDtStart, ///> Field representing the DTSTART component. FieldDtEnd, ///> Field representing the DTEND component. FieldLastModified, ///> Field representing the LAST-MODIFIED component. FieldDescription, ///> Field representing the DESCRIPTION component. FieldSummary, ///> Field representing the SUMMARY component. FieldLocation, ///> Field representing the LOCATION component. FieldCompleted, ///> Field representing the COMPLETED component. FieldPercentComplete, ///> Field representing the PERCENT-COMPLETE component. FieldDtDue, ///> Field representing the DUE component. FieldCategories, ///> Field representing the CATEGORIES component. FieldRelatedTo, ///> Field representing the RELATED-TO component. FieldRecurrence, ///> Field representing the EXDATE, EXRULE, RDATE, and RRULE components. FieldAttachment, ///> Field representing the ATTACH component. FieldSecrecy, ///> Field representing the CLASS component. FieldStatus, ///> Field representing the STATUS component. FieldTransparency, ///> Field representing the TRANSPARENCY component. FieldResources, ///> Field representing the RESOURCES component. FieldPriority, ///> Field representing the PRIORITY component. FieldGeoLatitude, ///> Field representing the latitude part of the GEO component. FieldGeoLongitude, ///> Field representing the longitude part of the GEO component. FieldRecurrenceId, ///> Field representing the RECURRENCE-ID component. FieldAlarms, ///> Field representing the VALARM component. FieldSchedulingId, ///> Field representing the X-KDE-LIBKCAL-ID component. FieldAttendees, ///> Field representing the ATTENDEE component. FieldOrganizer, ///> Field representing the ORGANIZER component. FieldCreated, ///> Field representing the CREATED component. FieldRevision, ///> Field representing the SEQUENCE component. FieldDuration, ///> Field representing the DURATION component. FieldContact, ///> Field representing the CONTACT component. FieldComment, ///> Field representing the COMMENT component. FieldUid, ///> Field representing the UID component. FieldUnknown, ///> Something changed. Always set when you use the assignment operator. FieldUrl ///> Field representing the URL component. }; /** The IncidenceObserver class. */ class KCALCORE_EXPORT IncidenceObserver { public: /** Destroys the IncidenceObserver. */ virtual ~IncidenceObserver(); /** The IncidenceObserver interface. This function is called before any changes are made. @param uid is the string containing the incidence @ref uid. @param recurrenceId is possible recurrenceid of incidence. */ virtual void incidenceUpdate(const QString &uid, const QDateTime &recurrenceId) = 0; /** The IncidenceObserver interface. This function is called after changes are completed. @param uid is the string containing the incidence @ref uid. @param recurrenceId is possible recurrenceid of incidence. */ virtual void incidenceUpdated(const QString &uid, const QDateTime &recurrenceId) = 0; }; /** Constructs an empty IncidenceBase. */ IncidenceBase(); /** Destroys the IncidenceBase. */ ~IncidenceBase() override; /** Assignment operator. All data belonging to derived classes are also copied. @see assign(). The caller guarantees that both types match. @code if ( i1.type() == i2.type() ) { i1 = i2; } else { qCDebug(KCALCORE_LOG) << "Invalid assignment!"; } @endcode Dirty field FieldUnknown will be set. @param other is the IncidenceBase to assign. */ IncidenceBase &operator=(const IncidenceBase &other); /** Compares this with IncidenceBase @p ib for equality. All data belonging to derived classes are also compared. @see equals(). @param ib is the IncidenceBase to compare against. @return true if the incidences are equal; false otherwise. */ bool operator==(const IncidenceBase &ib) const; /** Compares this with IncidenceBase @p ib for inequality. @param ib is the IncidenceBase to compare against. @return true if the incidences are /not/ equal; false otherwise. */ bool operator!=(const IncidenceBase &ib) const; /** Accept IncidenceVisitor. A class taking part in the visitor mechanism has to provide this implementation:
        bool accept(Visitor &v) { return v.visit(this); }
      
@param v is a reference to a Visitor object. @param incidence is a valid IncidenceBase object for visting. */ virtual bool accept(Visitor &v, const IncidenceBase::Ptr &incidence); /** Returns the incidence type. */ virtual IncidenceType type() const = 0; /** Prints the type of incidence as a string. */ virtual QByteArray typeStr() const = 0; /** Sets the unique id for the incidence to @p uid. @param uid is the string containing the incidence @ref uid. @see uid() */ void setUid(const QString &uid); /** Returns the unique id (@ref uid) for the incidence. @see setUid() */ Q_REQUIRED_RESULT QString uid() const; /** Returns the uri for the incidence, of form urn:x-ical:\ */ Q_REQUIRED_RESULT QUrl uri() const; /** Sets the time the incidence was last modified to @p lm. It is stored as a UTC date/time. @param lm is the QDateTime when the incidence was last modified. @see lastModified() */ virtual void setLastModified(const QDateTime &lm); /** Returns the time the incidence was last modified. @see setLastModified() */ Q_REQUIRED_RESULT QDateTime lastModified() const; /** Sets the organizer for the incidence. @param organizer is a non-null Person to use as the incidence @ref organizer. @see organizer(), setOrganizer(const QString &) */ void setOrganizer(const Person &organizer); /** Sets the incidence organizer to any string @p organizer. @param organizer is a string to use as the incidence @ref organizer. @see organizer(), setOrganizer(const Person &) */ void setOrganizer(const QString &organizer); /** Returns the Person associated with this incidence. If no Person was set through setOrganizer(), a default Person() is returned. @see setOrganizer(const QString &), setOrganizer(const Person &) */ Person organizer() const; /** Sets readonly status. @param readOnly if set, the incidence is read-only; else the incidence can be modified. @see isReadOnly(). */ virtual void setReadOnly(bool readOnly); /** Returns true the object is read-only; false otherwise. @see setReadOnly() */ Q_REQUIRED_RESULT bool isReadOnly() const; /** Sets the incidence's starting date/time with a QDateTime. The incidence's all-day status is set according to whether @p dtStart is a date/time (not all-day) or date-only (all-day). @param dtStart is the incidence start date/time. @see dtStart(). */ virtual void setDtStart(const QDateTime &dtStart); /** Returns an incidence's starting date/time as a QDateTime. @see setDtStart(). */ virtual QDateTime dtStart() const; /** Sets the incidence duration. @param duration the incidence duration @see duration() */ virtual void setDuration(const Duration &duration); /** Returns the length of the incidence duration. @see setDuration() */ Q_REQUIRED_RESULT Duration duration() const; /** Sets if the incidence has a duration. @param hasDuration true if the incidence has a duration; false otherwise. @see hasDuration() */ void setHasDuration(bool hasDuration); /** Returns true if the incidence has a duration; false otherwise. @see setHasDuration() */ Q_REQUIRED_RESULT bool hasDuration() const; /** Returns true or false depending on whether the incidence is all-day. i.e. has a date but no time attached to it. @see setAllDay() */ Q_REQUIRED_RESULT bool allDay() const; /** Sets whether the incidence is all-day, i.e. has a date but no time attached to it. @param allDay sets whether the incidence is all-day. @see allDay() */ virtual void setAllDay(bool allDay); /** Shift the times of the incidence so that they appear at the same clock time as before but in a new time zone. The shift is done from a viewing time zone rather than from the actual incidence time zone. For example, shifting an incidence whose start time is 09:00 America/New York, using an old viewing time zone (@p oldSpec) of Europe/London, to a new time zone (@p newSpec) of Europe/Paris, will result in the time being shifted from 14:00 (which is the London time of the incidence start) to 14:00 Paris time. @param oldZone the time zone which provides the clock times @param newZone the new time zone */ virtual void shiftTimes(const QTimeZone &oldZone, const QTimeZone &newZone); /** Adds a comment to the incidence. Does not add a linefeed character; simply appends the text as specified. @param comment is the QString containing the comment to add. @see removeComment(). */ void addComment(const QString &comment); /** Removes a comment from the incidence. Removes the first comment whose string is an exact match for the specified string in @p comment. @param comment is the QString containing the comment to remove. @return true if match found, false otherwise. @see addComment(). */ Q_REQUIRED_RESULT bool removeComment(const QString &comment); /** Deletes all incidence comments. */ void clearComments(); /** Returns all incidence comments as a list of strings. */ Q_REQUIRED_RESULT QStringList comments() const; /** Adds a contact to thieincidence. Does not add a linefeed character; simply appends the text as specified. @param contact is the QString containing the contact to add. @see removeContact(). */ void addContact(const QString &contact); /** Removes a contact from the incidence. Removes the first contact whose string is an exact match for the specified string in @p contact. @param contact is the QString containing the contact to remove. @return true if match found, false otherwise. @see addContact(). */ Q_REQUIRED_RESULT bool removeContact(const QString &contact); /** Deletes all incidence contacts. */ void clearContacts(); /** Returns all incidence contacts as a list of strings. */ Q_REQUIRED_RESULT QStringList contacts() const; /** Add Attendee to this incidence. @param attendee the attendee to add @param doUpdate If true the Observers are notified, if false they are not. */ void addAttendee(const Attendee &attendee, bool doUpdate = true); /** Removes all attendees from the incidence. */ void clearAttendees(); /** Set the attendees of this incidence. This replaces all previously set attendees, unlike addAttendee. @param attendees A list of attendees. @param doUpdate If true the Observers are notified, if false they are not. */ void setAttendees(const Attendee::List &attendees, bool doUpdate = true); /** Returns a list of incidence attendees. All pointers in the list are valid. */ Q_REQUIRED_RESULT Attendee::List attendees() const; /** Returns the number of incidence attendees. */ Q_REQUIRED_RESULT int attendeeCount() const; /** Returns the attendee with the specified email address. @param email is a QString containing an email address of the form "FirstName LastName ". @see attendeeByMails(), attendeesByUid(). */ Attendee attendeeByMail(const QString &email) const; /** Returns the first incidence attendee with one of the specified email addresses. @param emails is a list of QStrings containing email addresses of the form "FirstName LastName ". @param email is a QString containing a single email address to search in addition to the list specified in @p emails. @see attendeeByMail(), attendeesByUid(). */ Attendee attendeeByMails(const QStringList &emails, const QString &email = QString()) const; /** Returns the incidence attendee with the specified attendee @acronym UID. @param uid is a QString containing an attendee @acronym UID. @see attendeeByMail(), attendeeByMails(). */ Attendee attendeeByUid(const QString &uid) const; /** Sets the incidences url. This property can be used to point to a more dynamic rendition of the incidence. I.e. a website related to the incidence. @param url of the incience. @see url() @since 4.12 */ void setUrl(const QUrl &url); /** Returns the url. @return incidences url value @see setUrl() @since 4.12 */ Q_REQUIRED_RESULT QUrl url() const; /** Register observer. The observer is notified when the observed object changes. @param observer is a pointer to an IncidenceObserver object that will be watching this incidence. @see unRegisterObserver() */ void registerObserver(IncidenceObserver *observer); /** Unregister observer. It isn't notified anymore about changes. @param observer is a pointer to an IncidenceObserver object that will be watching this incidence. @see registerObserver(). */ void unRegisterObserver(IncidenceObserver *observer); /** Call this to notify the observers after the IncidenceBase object will be changed. */ void update(); /** Call this to notify the observers after the IncidenceBase object has changed. */ void updated(); /** Call this when a group of updates is going to be made. This suppresses change notifications until endUpdates() is called, at which point updated() will automatically be called. */ void startUpdates(); /** Call this when a group of updates is complete, to notify observers that the instance has changed. This should be called in conjunction with startUpdates(). */ void endUpdates(); /** Returns a date/time corresponding to the specified DateTimeRole. @param role is a DateTimeRole. */ virtual QDateTime dateTime(DateTimeRole role) const = 0; /** Sets the date/time corresponding to the specified DateTimeRole. @param dateTime is QDateTime value to set. @param role is a DateTimeRole. */ virtual void setDateTime(const QDateTime &dateTime, DateTimeRole role) = 0; /** - Returns the Akonadi specific sub MIME type of a KCalCore::IncidenceBase item, - e.g. getting "application/x-vnd.akonadi.calendar.event" for a KCalCore::Event. + Returns the Akonadi specific sub MIME type of a KCalendarCore::IncidenceBase item, + e.g. getting "application/x-vnd.akonadi.calendar.event" for a KCalendarCore::Event. */ virtual QLatin1String mimeType() const = 0; /** Returns the incidence recurrenceId. @return incidences recurrenceId value @see setRecurrenceId(). */ virtual QDateTime recurrenceId() const; /** Returns a QSet with all Fields that were changed since the incidence was created or resetDirtyFields() was called. @see resetDirtyFields() */ QSet dirtyFields() const; /** Sets which fields are dirty. @see dirtyFields() @since 4.8 */ void setDirtyFields(const QSet &); /** Resets dirty fields. @see dirtyFields() */ void resetDirtyFields(); /** - * Constant that identifies KCalCore data in a binary stream. + * Constant that identifies KCalendarCore data in a binary stream. * * @since 4.12 */ Q_REQUIRED_RESULT static quint32 magicSerializationIdentifier(); protected: /** Marks Field @p field as dirty. @param field is the Field type to mark as dirty. @see dirtyFields() */ void setFieldDirty(IncidenceBase::Field field); /** @copydoc CustomProperties::customPropertyUpdate() */ void customPropertyUpdate() override; /** @copydoc CustomProperties::customPropertyUpdated() */ void customPropertyUpdated() override; /** Constructs an IncidenceBase as a copy of another IncidenceBase object. @param ib is the IncidenceBase to copy. */ IncidenceBase(const IncidenceBase &ib); /** Provides polymorfic comparison for equality. Only called by IncidenceBase::operator==() which guarantees that @p incidenceBase is of the right type. @param incidenceBase is the IncidenceBase to compare against. @return true if the incidences are equal; false otherwise. */ virtual bool equals(const IncidenceBase &incidenceBase) const; /** Provides polymorfic assignment. @param other is the IncidenceBase to assign. */ virtual IncidenceBase &assign(const IncidenceBase &other); /** * Sub-type specific serialization. */ virtual void serialize(QDataStream &out) const; /** * Sub-type specific deserialization. */ virtual void deserialize(QDataStream &in); enum VirtualHook {}; /** Standard trick to add virtuals later. @param id is any integer unique to this class which we will use to identify the method to be called. @param data is a pointer to some glob of data, typically a struct. */ virtual void virtual_hook(VirtualHook id, void *data) = 0; /** Identifies a read-only incidence. */ bool mReadOnly; private: //@cond PRIVATE class Private; Private *const d; Q_DECL_HIDDEN QVariantList attendeesVariant() const; //@endcond - friend KCALCORE_EXPORT QDataStream &operator<<(QDataStream &stream, const KCalCore::IncidenceBase::Ptr &); + friend KCALCORE_EXPORT QDataStream &operator<<(QDataStream &stream, const KCalendarCore::IncidenceBase::Ptr &); - friend KCALCORE_EXPORT QDataStream &operator>>(QDataStream &stream, KCalCore::IncidenceBase::Ptr &); + friend KCALCORE_EXPORT QDataStream &operator>>(QDataStream &stream, KCalendarCore::IncidenceBase::Ptr &); }; /** * Incidence serializer. * * @since 4.12 */ -KCALCORE_EXPORT QDataStream &operator<<(QDataStream &out, const KCalCore::IncidenceBase::Ptr &); +KCALCORE_EXPORT QDataStream &operator<<(QDataStream &out, const KCalendarCore::IncidenceBase::Ptr &); /** * Incidence deserializer. * * @since 4.12 */ -KCALCORE_EXPORT QDataStream &operator>>(QDataStream &in, KCalCore::IncidenceBase::Ptr &); +KCALCORE_EXPORT QDataStream &operator>>(QDataStream &in, KCalendarCore::IncidenceBase::Ptr &); } -Q_DECLARE_METATYPE(KCalCore::IncidenceBase *) -Q_DECLARE_METATYPE(KCalCore::IncidenceBase::Ptr) +Q_DECLARE_METATYPE(KCalendarCore::IncidenceBase *) +Q_DECLARE_METATYPE(KCalendarCore::IncidenceBase::Ptr) #endif diff --git a/src/journal.cpp b/src/journal.cpp index 62e79627a..03994674c 100644 --- a/src/journal.cpp +++ b/src/journal.cpp @@ -1,139 +1,139 @@ /* This file is part of the kcalcore library. Copyright (c) 2001 Cornelius Schumacher This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the Journal class. @brief Provides a Journal in the sense of RFC2445. @author Cornelius Schumacher \ */ #include "journal.h" #include "visitor.h" #include "kcalcore_debug.h" -using namespace KCalCore; +using namespace KCalendarCore; Journal::Journal() : d(nullptr) { } Journal::~Journal() { } Incidence::IncidenceType Journal::type() const { return TypeJournal; } QByteArray Journal::typeStr() const { return QByteArrayLiteral("Journal"); } Journal *Journal::clone() const { return new Journal(*this); } IncidenceBase &Journal::assign(const IncidenceBase &other) { Incidence::assign(other); return *this; } bool Journal::equals(const IncidenceBase &journal) const { return Incidence::equals(journal); } bool Journal::accept(Visitor &v, const IncidenceBase::Ptr &incidence) { return v.visit(incidence.staticCast()); } QDateTime Journal::dateTime(DateTimeRole role) const { switch (role) { case RoleEnd: case RoleEndTimeZone: return QDateTime(); case RoleDisplayStart: case RoleDisplayEnd: return dtStart(); default: return dtStart(); } } void Journal::setDateTime(const QDateTime &dateTime, DateTimeRole role) { switch (role) { case RoleDnD: { setDtStart(dateTime); break; } default: qCDebug(KCALCORE_LOG) << "Unhandled role" << role; } } void Journal::virtual_hook(VirtualHook id, void *data) { Q_UNUSED(id); Q_UNUSED(data); } QLatin1String Journal::mimeType() const { return Journal::journalMimeType(); } /* static */ QLatin1String Journal::journalMimeType() { return QLatin1String("application/x-vnd.akonadi.calendar.journal"); } QLatin1String Journal::iconName(const QDateTime &) const { return QLatin1String("view-pim-journal"); } void Journal::serialize(QDataStream &out) const { Incidence::serialize(out); } void Journal::deserialize(QDataStream &in) { Incidence::deserialize(in); } bool Journal::supportsGroupwareCommunication() const { return false; } diff --git a/src/journal.h b/src/journal.h index 9da3bfd4d..3d0e65411 100644 --- a/src/journal.h +++ b/src/journal.h @@ -1,177 +1,177 @@ /* This file is part of the kcalcore library. Copyright (c) 2001-2003 Cornelius Schumacher Copyright (C) 2003-2004 Reinhold Kainhofer This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the Journal class. @author Cornelius Schumacher \ @author Reinhold Kainhofer \ */ #ifndef KCALCORE_JOURNAL_H #define KCALCORE_JOURNAL_H #include "kcalcore_export.h" #include "incidence.h" -namespace KCalCore +namespace KCalendarCore { /** @brief Provides a Journal in the sense of RFC2445. */ class KCALCORE_EXPORT Journal : public Incidence { public: /** A shared pointer to a Journal object. */ typedef QSharedPointer Ptr; /** List of journals. */ typedef QVector List; ///@cond PRIVATE // needed for Akonadi polymorphic payload support typedef Incidence SuperClass; ///@endcond /** Constructs an empty journal. */ Journal(); /** Destroys a journal. */ ~Journal() override; /** @copydoc IncidenceBase::type() */ Q_REQUIRED_RESULT IncidenceType type() const override; /** @copydoc IncidenceBase::typeStr() */ Q_REQUIRED_RESULT QByteArray typeStr() const override; /** Returns an exact copy of this journal. The returned object is owned by the caller. */ Journal *clone() const override; /** @copydoc IncidenceBase::dateTime(DateTimeRole)const */ Q_REQUIRED_RESULT QDateTime dateTime(DateTimeRole role) const override; /** @copydoc IncidenceBase::setDateTime(const QDateTime &, DateTimeRole ) */ void setDateTime(const QDateTime &dateTime, DateTimeRole role) override; /** @copydoc IncidenceBase::mimeType() */ Q_REQUIRED_RESULT QLatin1String mimeType() const override; /** @copydoc Incidence::iconName() */ Q_REQUIRED_RESULT QLatin1String iconName(const QDateTime &recurrenceId = {}) const override; /** @copydoc Incidence::supportsGroupwareCommunication() */ Q_REQUIRED_RESULT bool supportsGroupwareCommunication() const override; /** - Returns the Akonadi specific sub MIME type of a KCalCore::Journal. + Returns the Akonadi specific sub MIME type of a KCalendarCore::Journal. */ Q_REQUIRED_RESULT static QLatin1String journalMimeType(); protected: /** Compare this with @p journal for equality. @param journal is the journal to compare. */ bool equals(const IncidenceBase &journal) const override; /** @copydoc IncidenceBase::assign() */ IncidenceBase &assign(const IncidenceBase &other) override; /** @copydoc IncidenceBase::virtual_hook() */ void virtual_hook(VirtualHook id, void *data) override; private: /** @copydoc IncidenceBase::accept(Visitor &, const IncidenceBase::Ptr &) */ bool accept(Visitor &v, const IncidenceBase::Ptr &incidence) override; /** Disabled, otherwise could be dangerous if you subclass Journal. Use IncidenceBase::operator= which is safe because it calls virtual function assign(). @param other is another Journal object to assign to this one. */ Journal &operator=(const Journal &other); // For polymorfic serialization void serialize(QDataStream &out) const override; void deserialize(QDataStream &in) override; //@cond PRIVATE class Private; Private *const d; //@endcond }; -} // namespace KCalCore +} // namespace KCalendarCore //@cond PRIVATE -Q_DECLARE_TYPEINFO(KCalCore::Journal::Ptr, Q_MOVABLE_TYPE); -Q_DECLARE_METATYPE(KCalCore::Journal::Ptr) -Q_DECLARE_METATYPE(KCalCore::Journal *) +Q_DECLARE_TYPEINFO(KCalendarCore::Journal::Ptr, Q_MOVABLE_TYPE); +Q_DECLARE_METATYPE(KCalendarCore::Journal::Ptr) +Q_DECLARE_METATYPE(KCalendarCore::Journal *) //@endcond #endif diff --git a/src/memorycalendar.cpp b/src/memorycalendar.cpp index f68e9a99c..7d994a3c0 100644 --- a/src/memorycalendar.cpp +++ b/src/memorycalendar.cpp @@ -1,872 +1,872 @@ /* This file is part of the kcalcore library. Copyright (c) 1998 Preston Brown Copyright (c) 2001,2003,2004 Cornelius Schumacher Copyright (C) 2003-2004 Reinhold Kainhofer This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the MemoryCalendar class. @brief This class provides a calendar stored as a local file. @author Preston Brown \ @author Cornelius Schumacher \ */ #include "memorycalendar.h" #include "kcalcore_debug.h" #include "utils.h" #include "calformat.h" #include template static QVector values(const QMultiHash &c) { QVector v; v.reserve(c.size()); for (typename QMultiHash::const_iterator it = c.begin(), end = c.end(); it != end; ++it) { v.push_back(it.value()); } return v; } template static QVector values(const QMultiHash &c, const K &x) { QVector v; typename QMultiHash::const_iterator it = c.find(x); while (it != c.end() && it.key() == x) { v.push_back(it.value()); ++it; } return v; } -using namespace KCalCore; +using namespace KCalendarCore; /** Private class that helps to provide binary compatibility between releases. @internal */ //@cond PRIVATE -class Q_DECL_HIDDEN KCalCore::MemoryCalendar::Private +class Q_DECL_HIDDEN KCalendarCore::MemoryCalendar::Private { public: Private(MemoryCalendar *qq) : q(qq), mFormat(nullptr) { } ~Private() { } MemoryCalendar *q; CalFormat *mFormat; // calendar format QString mIncidenceBeingUpdated; // Instance identifier of Incidence currently being updated /** * List of all incidences. * First indexed by incidence->type(), then by incidence->uid(); */ QMap > mIncidences; /** * Has all incidences, indexed by identifier. */ - QHash mIncidencesByIdentifier; + QHash mIncidencesByIdentifier; /** * List of all deleted incidences. * First indexed by incidence->type(), then by incidence->uid(); */ QMap > mDeletedIncidences; /** * Contains incidences ( to-dos; non-recurring, non-multiday events; journals; ) * indexed by start/due date. * * The QMap key is the incidence->type(). * The QMultiHash key is the dtStart/dtDue().toString() * * Note: We had 3 variables, mJournalsForDate, mTodosForDate and mEventsForDate * but i merged them into one (indexed by type) because it simplifies code using * it. No need to if else based on type. */ QMap > mIncidencesForDate; void insertIncidence(const Incidence::Ptr &incidence); Incidence::Ptr incidence(const QString &uid, IncidenceBase::IncidenceType type, const QDateTime &recurrenceId = {}) const; Incidence::Ptr deletedIncidence(const QString &uid, const QDateTime &recurrenceId, IncidenceBase::IncidenceType type) const; void deleteAllIncidences(IncidenceBase::IncidenceType type); }; //@endcond MemoryCalendar::MemoryCalendar(const QTimeZone &timeZone) : Calendar(timeZone), - d(new KCalCore::MemoryCalendar::Private(this)) + d(new KCalendarCore::MemoryCalendar::Private(this)) { } MemoryCalendar::MemoryCalendar(const QByteArray &timeZoneId) : Calendar(timeZoneId), - d(new KCalCore::MemoryCalendar::Private(this)) + d(new KCalendarCore::MemoryCalendar::Private(this)) { } MemoryCalendar::~MemoryCalendar() { close(); //NOLINT false clang-analyzer-optin.cplusplus.VirtualCall delete d; } void MemoryCalendar::close() { setObserversEnabled(false); // Don't call the virtual function deleteEvents() etc, the base class might have // other ways of deleting the data. d->deleteAllIncidences(Incidence::TypeEvent); d->deleteAllIncidences(Incidence::TypeTodo); d->deleteAllIncidences(Incidence::TypeJournal); d->mIncidencesByIdentifier.clear(); d->mDeletedIncidences.clear(); setModified(false); setObserversEnabled(true); } bool MemoryCalendar::deleteIncidence(const Incidence::Ptr &incidence) { // Handle orphaned children // relations is an Incidence's property, not a Todo's, so // we remove relations in deleteIncidence, not in deleteTodo. removeRelations(incidence); const Incidence::IncidenceType type = incidence->type(); const QString uid = incidence->uid(); if (d->mIncidences[type].contains(uid, incidence)) { // Notify while the incidence is still available, // this is necessary so korganizer still has time to query for exceptions notifyIncidenceAboutToBeDeleted(incidence); d->mIncidences[type].remove(uid, incidence); d->mIncidencesByIdentifier.remove(incidence->instanceIdentifier()); setModified(true); if (deletionTracking()) { d->mDeletedIncidences[type].insert(uid, incidence); } const QDateTime dt = incidence->dateTime(Incidence::RoleCalendarHashing); if (dt.isValid()) { d->mIncidencesForDate[type].remove(dt.date().toString(), incidence); } // Delete child-incidences. if (!incidence->hasRecurrenceId()) { deleteIncidenceInstances(incidence); } notifyIncidenceDeleted(incidence); return true; } else { qCWarning(KCALCORE_LOG) << incidence->typeStr() << " not found. uid=" << uid; return false; } } bool MemoryCalendar::deleteIncidenceInstances(const Incidence::Ptr &incidence) { const Incidence::IncidenceType type = incidence->type(); Incidence::List values = ::values(d->mIncidences[type], incidence->uid()); for (auto it = values.constBegin(); it != values.constEnd(); ++it) { Incidence::Ptr i = *it; if (i->hasRecurrenceId()) { qCDebug(KCALCORE_LOG) << "deleting child" << ", type=" << int(type) << ", uid=" << i->uid() // << ", start=" << i->dtStart() << " from calendar"; deleteIncidence(i); } } return true; } //@cond PRIVATE void MemoryCalendar::Private::deleteAllIncidences(Incidence::IncidenceType incidenceType) { QHashIteratori(mIncidences[incidenceType]); while (i.hasNext()) { i.next(); q->notifyIncidenceAboutToBeDeleted(i.value()); i.value()->unRegisterObserver(q); } mIncidences[incidenceType].clear(); mIncidencesForDate[incidenceType].clear(); } Incidence::Ptr MemoryCalendar::Private::incidence(const QString &uid, Incidence::IncidenceType type, const QDateTime &recurrenceId) const { Incidence::List values = ::values(mIncidences[type], uid); for (auto it = values.constBegin(); it != values.constEnd(); ++it) { Incidence::Ptr i = *it; if (recurrenceId.isNull()) { if (!i->hasRecurrenceId()) { return i; } } else { if (i->hasRecurrenceId() && i->recurrenceId() == recurrenceId) { return i; } } } return Incidence::Ptr(); } Incidence::Ptr MemoryCalendar::Private::deletedIncidence(const QString &uid, const QDateTime &recurrenceId, IncidenceBase::IncidenceType type) const { if (!q->deletionTracking()) { return Incidence::Ptr(); } Incidence::List values = ::values(mDeletedIncidences[type], uid); for (auto it = values.constBegin(); it != values.constEnd(); ++it) { Incidence::Ptr i = *it; if (recurrenceId.isNull()) { if (!i->hasRecurrenceId()) { return i; } } else { if (i->hasRecurrenceId() && i->recurrenceId() == recurrenceId) { return i; } } } return Incidence::Ptr(); } void MemoryCalendar::Private::insertIncidence(const Incidence::Ptr &incidence) { const QString uid = incidence->uid(); const Incidence::IncidenceType type = incidence->type(); if (!mIncidences[type].contains(uid, incidence)) { mIncidences[type].insert(uid, incidence); mIncidencesByIdentifier.insert(incidence->instanceIdentifier(), incidence); const QDateTime dt = incidence->dateTime(Incidence::RoleCalendarHashing); if (dt.isValid()) { mIncidencesForDate[type].insert(dt.date().toString(), incidence); } } else { #ifndef NDEBUG // if we already have an to-do with this UID, it must be the same incidence, // otherwise something's really broken Q_ASSERT(mIncidences[type].value(uid) == incidence); #endif } } //@endcond bool MemoryCalendar::addIncidence(const Incidence::Ptr &incidence) { d->insertIncidence(incidence); notifyIncidenceAdded(incidence); incidence->registerObserver(this); setupRelations(incidence); setModified(true); return true; } bool MemoryCalendar::addEvent(const Event::Ptr &event) { return addIncidence(event); } bool MemoryCalendar::deleteEvent(const Event::Ptr &event) { return deleteIncidence(event); } bool MemoryCalendar::deleteEventInstances(const Event::Ptr &event) { return deleteIncidenceInstances(event); } Event::Ptr MemoryCalendar::event(const QString &uid, const QDateTime &recurrenceId) const { return d->incidence(uid, Incidence::TypeEvent, recurrenceId).staticCast(); } Event::Ptr MemoryCalendar::deletedEvent(const QString &uid, const QDateTime &recurrenceId) const { return d->deletedIncidence(uid, recurrenceId, Incidence::TypeEvent).staticCast(); } bool MemoryCalendar::addTodo(const Todo::Ptr &todo) { return addIncidence(todo); } bool MemoryCalendar::deleteTodo(const Todo::Ptr &todo) { return deleteIncidence(todo); } bool MemoryCalendar::deleteTodoInstances(const Todo::Ptr &todo) { return deleteIncidenceInstances(todo); } Todo::Ptr MemoryCalendar::todo(const QString &uid, const QDateTime &recurrenceId) const { return d->incidence(uid, Incidence::TypeTodo, recurrenceId).staticCast(); } Todo::Ptr MemoryCalendar::deletedTodo(const QString &uid, const QDateTime &recurrenceId) const { return d->deletedIncidence(uid, recurrenceId, Incidence::TypeTodo).staticCast(); } Todo::List MemoryCalendar::rawTodos(TodoSortField sortField, SortDirection sortDirection) const { Todo::List todoList; todoList.reserve(d->mIncidences[Incidence::TypeTodo].count()); QHashIteratori(d->mIncidences[Incidence::TypeTodo]); while (i.hasNext()) { i.next(); todoList.append(i.value().staticCast()); } return Calendar::sortTodos(todoList, sortField, sortDirection); } Todo::List MemoryCalendar::deletedTodos(TodoSortField sortField, SortDirection sortDirection) const { if (!deletionTracking()) { return Todo::List(); } Todo::List todoList; todoList.reserve(d->mDeletedIncidences[Incidence::TypeTodo].count()); QHashIteratori(d->mDeletedIncidences[Incidence::TypeTodo]); while (i.hasNext()) { i.next(); todoList.append(i.value().staticCast()); } return Calendar::sortTodos(todoList, sortField, sortDirection); } Todo::List MemoryCalendar::todoInstances(const Incidence::Ptr &todo, TodoSortField sortField, SortDirection sortDirection) const { Todo::List list; Incidence::List values = ::values(d->mIncidences[Incidence::TypeTodo], todo->uid()); for (auto it = values.constBegin(); it != values.constEnd(); ++it) { Todo::Ptr t = (*it).staticCast(); if (t->hasRecurrenceId()) { list.append(t); } } return Calendar::sortTodos(list, sortField, sortDirection); } Todo::List MemoryCalendar::rawTodosForDate(const QDate &date) const { Todo::List todoList; Todo::Ptr t; const QString dateStr = date.toString(); QMultiHash::const_iterator it = d->mIncidencesForDate[Incidence::TypeTodo].constFind(dateStr); while (it != d->mIncidencesForDate[Incidence::TypeTodo].constEnd() && it.key() == dateStr) { t = it.value().staticCast(); todoList.append(t); ++it; } // Iterate over all todos. Look for recurring todoss that occur on this date QHashIteratori(d->mIncidences[Incidence::TypeTodo]); while (i.hasNext()) { i.next(); t = i.value().staticCast(); if (t->recurs()) { if (t->recursOn(date, timeZone())) { todoList.append(t); } } } return todoList; } Todo::List MemoryCalendar::rawTodos(const QDate &start, const QDate &end, const QTimeZone &timeZone, bool inclusive) const { Q_UNUSED(inclusive); // use only exact dtDue/dtStart, not dtStart and dtEnd Todo::List todoList; const auto ts = timeZone.isValid() ? timeZone : this->timeZone(); QDateTime st(start, QTime(0, 0, 0), ts); QDateTime nd(end, QTime(23, 59, 59, 999), ts); // Get todos QHashIteratori(d->mIncidences[Incidence::TypeTodo]); Todo::Ptr todo; while (i.hasNext()) { i.next(); todo = i.value().staticCast(); if (!isVisible(todo)) { continue; } QDateTime rStart = todo->hasDueDate() ? todo->dtDue() : todo->hasStartDate() ? todo->dtStart() : QDateTime(); if (!rStart.isValid()) { continue; } if (!todo->recurs()) { // non-recurring todos if (nd.isValid() && nd < rStart) { continue; } if (st.isValid() && rStart < st) { continue; } } else { // recurring events switch (todo->recurrence()->duration()) { case -1: // infinite break; case 0: // end date given default: // count given QDateTime rEnd(todo->recurrence()->endDate(), QTime(23, 59, 59, 999), ts); if (!rEnd.isValid()) { continue; } if (st.isValid() && rEnd < st) { continue; } break; } // switch(duration) } //if(recurs) todoList.append(todo); } return todoList; } Alarm::List MemoryCalendar::alarmsTo(const QDateTime &to) const { return alarms(QDateTime(QDate(1900, 1, 1), QTime(0, 0, 0)), to); } Alarm::List MemoryCalendar::alarms(const QDateTime &from, const QDateTime &to, bool excludeBlockedAlarms) const { Q_UNUSED(excludeBlockedAlarms); Alarm::List alarmList; QHashIteratorie(d->mIncidences[Incidence::TypeEvent]); Event::Ptr e; while (ie.hasNext()) { ie.next(); e = ie.value().staticCast(); if (e->recurs()) { appendRecurringAlarms(alarmList, e, from, to); } else { appendAlarms(alarmList, e, from, to); } } QHashIteratorit(d->mIncidences[Incidence::TypeTodo]); Todo::Ptr t; while (it.hasNext()) { it.next(); t = it.value().staticCast(); if (!t->isCompleted()) { appendAlarms(alarmList, t, from, to); if (t->recurs()) { appendRecurringAlarms(alarmList, t, from, to); } else { appendAlarms(alarmList, t, from, to); } } } return alarmList; } void MemoryCalendar::incidenceUpdate(const QString &uid, const QDateTime &recurrenceId) { Incidence::Ptr inc = incidence(uid, recurrenceId); if (inc) { if (!d->mIncidenceBeingUpdated.isEmpty()) { qCWarning(KCALCORE_LOG) << "Incidence::update() called twice without an updated() call in between."; } // Save it so we can detect changes to uid or recurringId. d->mIncidenceBeingUpdated = inc->instanceIdentifier(); const QDateTime dt = inc->dateTime(Incidence::RoleCalendarHashing); if (dt.isValid()) { const Incidence::IncidenceType type = inc->type(); d->mIncidencesForDate[type].remove(dt.date().toString(), inc); } } } void MemoryCalendar::incidenceUpdated(const QString &uid, const QDateTime &recurrenceId) { Incidence::Ptr inc = incidence(uid, recurrenceId); if (inc) { if (d->mIncidenceBeingUpdated.isEmpty()) { qCWarning(KCALCORE_LOG) << "Incidence::updated() called twice without an update() call in between."; } else if (inc->instanceIdentifier() != d->mIncidenceBeingUpdated) { // Instance identifier changed, update our hash table d->mIncidencesByIdentifier.remove(d->mIncidenceBeingUpdated); d->mIncidencesByIdentifier.insert(inc->instanceIdentifier(), inc); } d->mIncidenceBeingUpdated = QString(); inc->setLastModified(QDateTime::currentDateTimeUtc()); // we should probably update the revision number here, // or internally in the Event itself when certain things change. // need to verify with ical documentation. const QDateTime dt = inc->dateTime(Incidence::RoleCalendarHashing); if (dt.isValid()) { const Incidence::IncidenceType type = inc->type(); d->mIncidencesForDate[type].insert(dt.date().toString(), inc); } notifyIncidenceChanged(inc); setModified(true); } } Event::List MemoryCalendar::rawEventsForDate(const QDate &date, const QTimeZone &timeZone, EventSortField sortField, SortDirection sortDirection) const { Event::List eventList; if (!date.isValid()) { // There can't be events on invalid dates return eventList; } Event::Ptr ev; // Find the hash for the specified date const QString dateStr = date.toString(); QMultiHash::const_iterator it = d->mIncidencesForDate[Incidence::TypeEvent].constFind(dateStr); // Iterate over all non-recurring, single-day events that start on this date const auto ts = timeZone.isValid() ? timeZone : this->timeZone(); while (it != d->mIncidencesForDate[Incidence::TypeEvent].constEnd() && it.key() == dateStr) { ev = it.value().staticCast(); QDateTime end(ev->dtEnd().toTimeZone(ev->dtStart().timeZone())); if (ev->allDay()) { end.setTime(QTime()); } else { end = end.addSecs(-1); } if (end.date() >= date) { eventList.append(ev); } ++it; } // Iterate over all events. Look for recurring events that occur on this date QHashIteratori(d->mIncidences[Incidence::TypeEvent]); while (i.hasNext()) { i.next(); ev = i.value().staticCast(); if (ev->recurs()) { if (ev->isMultiDay()) { int extraDays = ev->dtStart().date().daysTo(ev->dtEnd().date()); for (int i = 0; i <= extraDays; ++i) { if (ev->recursOn(date.addDays(-i), ts)) { eventList.append(ev); break; } } } else { if (ev->recursOn(date, ts)) { eventList.append(ev); } } } else { if (ev->isMultiDay()) { if (ev->dtStart().date() <= date && ev->dtEnd().date() >= date) { eventList.append(ev); } } } } return Calendar::sortEvents(eventList, sortField, sortDirection); } Event::List MemoryCalendar::rawEvents(const QDate &start, const QDate &end, const QTimeZone &timeZone, bool inclusive) const { Event::List eventList; const auto ts = timeZone.isValid() ? timeZone : this->timeZone(); QDateTime st(start, QTime(0, 0, 0), ts); QDateTime nd(end, QTime(23, 59, 59, 999), ts); // Get non-recurring events QHashIteratori(d->mIncidences[Incidence::TypeEvent]); Event::Ptr event; while (i.hasNext()) { i.next(); event = i.value().staticCast(); QDateTime rStart = event->dtStart(); if (nd < rStart) { continue; } if (inclusive && rStart < st) { continue; } if (!event->recurs()) { // non-recurring events QDateTime rEnd = event->dtEnd(); if (rEnd < st) { continue; } if (inclusive && nd < rEnd) { continue; } } else { // recurring events switch (event->recurrence()->duration()) { case -1: // infinite if (inclusive) { continue; } break; case 0: // end date given default: // count given QDateTime rEnd(event->recurrence()->endDate(), QTime(23, 59, 59, 999), ts); if (!rEnd.isValid()) { continue; } if (rEnd < st) { continue; } if (inclusive && nd < rEnd) { continue; } break; } // switch(duration) } //if(recurs) eventList.append(event); } return eventList; } Event::List MemoryCalendar::rawEventsForDate(const QDateTime &kdt) const { return rawEventsForDate(kdt.date(), kdt.timeZone()); } Event::List MemoryCalendar::rawEvents(EventSortField sortField, SortDirection sortDirection) const { Event::List eventList; eventList.reserve(d->mIncidences[Incidence::TypeEvent].count()); QHashIterator i(d->mIncidences[Incidence::TypeEvent]); while (i.hasNext()) { i.next(); eventList.append(i.value().staticCast()); } return Calendar::sortEvents(eventList, sortField, sortDirection); } Event::List MemoryCalendar::deletedEvents(EventSortField sortField, SortDirection sortDirection) const { if (!deletionTracking()) { return Event::List(); } Event::List eventList; eventList.reserve(d->mDeletedIncidences[Incidence::TypeEvent].count()); QHashIteratori(d->mDeletedIncidences[Incidence::TypeEvent]); while (i.hasNext()) { i.next(); eventList.append(i.value().staticCast()); } return Calendar::sortEvents(eventList, sortField, sortDirection); } Event::List MemoryCalendar::eventInstances(const Incidence::Ptr &event, EventSortField sortField, SortDirection sortDirection) const { Event::List list; Incidence::List values = ::values(d->mIncidences[Incidence::TypeEvent], event->uid()); for (auto it = values.constBegin(); it != values.constEnd(); ++it) { Event::Ptr ev = (*it).staticCast(); if (ev->hasRecurrenceId()) { list.append(ev); } } return Calendar::sortEvents(list, sortField, sortDirection); } bool MemoryCalendar::addJournal(const Journal::Ptr &journal) { return addIncidence(journal); } bool MemoryCalendar::deleteJournal(const Journal::Ptr &journal) { return deleteIncidence(journal); } bool MemoryCalendar::deleteJournalInstances(const Journal::Ptr &journal) { return deleteIncidenceInstances(journal); } Journal::Ptr MemoryCalendar::journal(const QString &uid, const QDateTime &recurrenceId) const { return d->incidence(uid, Incidence::TypeJournal, recurrenceId).staticCast(); } Journal::Ptr MemoryCalendar::deletedJournal(const QString &uid, const QDateTime &recurrenceId) const { return d->deletedIncidence(uid, recurrenceId, Incidence::TypeJournal).staticCast(); } Journal::List MemoryCalendar::rawJournals(JournalSortField sortField, SortDirection sortDirection) const { Journal::List journalList; QHashIteratori(d->mIncidences[Incidence::TypeJournal]); while (i.hasNext()) { i.next(); journalList.append(i.value().staticCast()); } return Calendar::sortJournals(journalList, sortField, sortDirection); } Journal::List MemoryCalendar::deletedJournals(JournalSortField sortField, SortDirection sortDirection) const { if (!deletionTracking()) { return Journal::List(); } Journal::List journalList; journalList.reserve(d->mDeletedIncidences[Incidence::TypeJournal].count()); QHashIteratori(d->mDeletedIncidences[Incidence::TypeJournal]); while (i.hasNext()) { i.next(); journalList.append(i.value().staticCast()); } return Calendar::sortJournals(journalList, sortField, sortDirection); } Journal::List MemoryCalendar::journalInstances(const Incidence::Ptr &journal, JournalSortField sortField, SortDirection sortDirection) const { Journal::List list; Incidence::List values = ::values(d->mIncidences[Incidence::TypeJournal], journal->uid()); for (auto it = values.constBegin(); it != values.constEnd(); ++it) { Journal::Ptr j = (*it).staticCast(); if (j->hasRecurrenceId()) { list.append(j); } } return Calendar::sortJournals(list, sortField, sortDirection); } Journal::List MemoryCalendar::rawJournalsForDate(const QDate &date) const { Journal::List journalList; Journal::Ptr j; QString dateStr = date.toString(); QMultiHash::const_iterator it = d->mIncidencesForDate[Incidence::TypeJournal].constFind(dateStr); while (it != d->mIncidencesForDate[Incidence::TypeJournal].constEnd() && it.key() == dateStr) { j = it.value().staticCast(); journalList.append(j); ++it; } return journalList; } Incidence::Ptr MemoryCalendar::instance(const QString &identifier) const { return d->mIncidencesByIdentifier.value(identifier); } void MemoryCalendar::virtual_hook(int id, void *data) { Q_UNUSED(id); Q_UNUSED(data); Q_ASSERT(false); } diff --git a/src/memorycalendar.h b/src/memorycalendar.h index 1d3ba7304..adfbd3cc2 100644 --- a/src/memorycalendar.h +++ b/src/memorycalendar.h @@ -1,333 +1,333 @@ /* This file is part of the kcalcore library. Copyright (c) 1998 Preston Brown Copyright (c) 2001,2003 Cornelius Schumacher This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the MemoryCalendar class. Very simple implementation of a Calendar that is only in memory @author Preston Brown \ @author Cornelius Schumacher \ */ #ifndef KCALCORE_MEMORYCALENDAR_H #define KCALCORE_MEMORYCALENDAR_H #include "kcalcore_export.h" #include "calendar.h" -namespace KCalCore +namespace KCalendarCore { /** @brief This class provides a calendar stored in memory. */ class KCALCORE_EXPORT MemoryCalendar : public Calendar { Q_OBJECT public: /** A shared pointer to a MemoryCalendar */ typedef QSharedPointer Ptr; /** @copydoc Calendar::Calendar(const QTimeZone &) */ explicit MemoryCalendar(const QTimeZone &timeZone); /** @copydoc Calendar::Calendar(const QString &) */ explicit MemoryCalendar(const QByteArray &timeZoneId); /** @copydoc Calendar::~Calendar() */ ~MemoryCalendar() override; /** Clears out the current calendar, freeing all used memory etc. etc. */ void close() override; /** @copydoc Calendar::deleteIncidence() */ bool deleteIncidence(const Incidence::Ptr &incidence) override; /** @copydoc Calendar::deleteIncidenceInstances */ bool deleteIncidenceInstances(const Incidence::Ptr &incidence) override; /** @copydoc Calendar::addIncidence() */ bool addIncidence(const Incidence::Ptr &incidence) override; // Event Specific Methods // /** @copydoc Calendar::addEvent() */ bool addEvent(const Event::Ptr &event) override; /** @copydoc Calendar::deleteEvent() */ bool deleteEvent(const Event::Ptr &event) override; /** @copydoc Calendar::deleteEventInstances() */ bool deleteEventInstances(const Event::Ptr &event) override; /** @copydoc Calendar::rawEvents(EventSortField, SortDirection)const */ Q_REQUIRED_RESULT Event::List rawEvents( EventSortField sortField = EventSortUnsorted, SortDirection sortDirection = SortDirectionAscending) const override; /** @copydoc Calendar::rawEvents(const QDate &, const QDate &, const QTimeZone &, bool)const */ Q_REQUIRED_RESULT Event::List rawEvents(const QDate &start, const QDate &end, const QTimeZone &timeZone = {}, bool inclusive = false) const override; /** Returns an unfiltered list of all Events which occur on the given date. @param date request unfiltered Event list for this QDate only. @param timeZone time zone to interpret @p date, or the calendar's default time zone if none is specified @param sortField specifies the EventSortField. @param sortDirection specifies the SortDirection. @return the list of unfiltered Events occurring on the specified QDate. */ Q_REQUIRED_RESULT Event::List rawEventsForDate( const QDate &date, const QTimeZone &timeZone = {}, EventSortField sortField = EventSortUnsorted, SortDirection sortDirection = SortDirectionAscending) const override; /** @copydoc Calendar::rawEventsForDate(const QDateTime &)const */ Q_REQUIRED_RESULT Event::List rawEventsForDate(const QDateTime &dt) const override; /** * Returns an incidence by identifier. * @see Incidence::instanceIdentifier() * @since 4.11 */ Incidence::Ptr instance(const QString &identifier) const; /** @copydoc Calendar::event() */ Q_REQUIRED_RESULT Event::Ptr event(const QString &uid, const QDateTime &recurrenceId = {}) const override; /** @copydoc Calendar::deletedEvent() */ Q_REQUIRED_RESULT Event::Ptr deletedEvent(const QString &uid, const QDateTime &recurrenceId = {}) const override; /** @copydoc Calendar::deletedEvents(EventSortField, SortDirection)const */ Q_REQUIRED_RESULT Event::List deletedEvents( EventSortField sortField = EventSortUnsorted, SortDirection sortDirection = SortDirectionAscending) const override; /** @copydoc Calendar::eventInstances(const Incidence::Ptr &, EventSortField, SortDirection)const */ Q_REQUIRED_RESULT Event::List eventInstances( const Incidence::Ptr &event, EventSortField sortField = EventSortUnsorted, SortDirection sortDirection = SortDirectionAscending) const override; // To-do Specific Methods // /** @copydoc Calendar::addTodo() */ bool addTodo(const Todo::Ptr &todo) override; /** @copydoc Calendar::deleteTodo() */ bool deleteTodo(const Todo::Ptr &todo) override; /** @copydoc Calendar::deleteTodoInstances() */ bool deleteTodoInstances(const Todo::Ptr &todo) override; /** @copydoc Calendar::rawTodos(TodoSortField, SortDirection)const */ Q_REQUIRED_RESULT Todo::List rawTodos( TodoSortField sortField = TodoSortUnsorted, SortDirection sortDirection = SortDirectionAscending) const override; /** @copydoc Calendar::rawTodos(const QDate &, const QDate &, const QTimeZone &, bool)const */ Q_REQUIRED_RESULT Todo::List rawTodos( const QDate &start, const QDate &end, const QTimeZone &timeZone = {}, bool inclusive = false) const override; /** @copydoc Calendar::rawTodosForDate() */ Q_REQUIRED_RESULT Todo::List rawTodosForDate(const QDate &date) const override; /** @copydoc Calendar::todo() */ Q_REQUIRED_RESULT Todo::Ptr todo(const QString &uid, const QDateTime &recurrenceId = {}) const override; /** @copydoc Calendar::deletedTodo() */ Q_REQUIRED_RESULT Todo::Ptr deletedTodo(const QString &uid, const QDateTime &recurrenceId = {}) const override; /** @copydoc Calendar::deletedTodos(TodoSortField, SortDirection)const */ Q_REQUIRED_RESULT Todo::List deletedTodos( TodoSortField sortField = TodoSortUnsorted, SortDirection sortDirection = SortDirectionAscending) const override; /** @copydoc Calendar::todoInstances(const Incidence::Ptr &, TodoSortField, SortDirection)const */ Q_REQUIRED_RESULT Todo::List todoInstances(const Incidence::Ptr &todo, TodoSortField sortField = TodoSortUnsorted, SortDirection sortDirection = SortDirectionAscending) const override; // Journal Specific Methods // /** @copydoc Calendar::addJournal() */ bool addJournal(const Journal::Ptr &journal) override; /** @copydoc Calendar::deleteJournal() */ bool deleteJournal(const Journal::Ptr &journal) override; /** @copydoc Calendar::deleteJournalInstances() */ bool deleteJournalInstances(const Journal::Ptr &journal) override; /** @copydoc Calendar::rawJournals() */ Q_REQUIRED_RESULT Journal::List rawJournals( JournalSortField sortField = JournalSortUnsorted, SortDirection sortDirection = SortDirectionAscending) const override; /** @copydoc Calendar::rawJournalsForDate() */ Q_REQUIRED_RESULT Journal::List rawJournalsForDate(const QDate &date) const override; /** @copydoc Calendar::journal() */ Journal::Ptr journal(const QString &uid, const QDateTime &recurrenceId = {}) const override; /** @copydoc Calendar::deletedJournal() */ Journal::Ptr deletedJournal(const QString &uid, const QDateTime &recurrenceId = {}) const override; /** @copydoc Calendar::deletedJournals(JournalSortField, SortDirection)const */ Q_REQUIRED_RESULT Journal::List deletedJournals( JournalSortField sortField = JournalSortUnsorted, SortDirection sortDirection = SortDirectionAscending) const override; /** @copydoc Calendar::journalInstances(const Incidence::Ptr &, JournalSortField, SortDirection)const */ Q_REQUIRED_RESULT Journal::List journalInstances(const Incidence::Ptr &journal, JournalSortField sortField = JournalSortUnsorted, SortDirection sortDirection = SortDirectionAscending) const override; // Alarm Specific Methods // /** @copydoc Calendar::alarms() */ Q_REQUIRED_RESULT Alarm::List alarms(const QDateTime &from, const QDateTime &to, bool excludeBlockedAlarms = false) const override; /** Return a list of Alarms that occur before the specified timestamp. @param to is the ending timestamp. @return the list of Alarms occurring before the specified QDateTime. */ Q_REQUIRED_RESULT Alarm::List alarmsTo(const QDateTime &to) const; /** @copydoc Calendar::incidenceUpdate(const QString &,const QDateTime &) */ void incidenceUpdate(const QString &uid, const QDateTime &recurrenceId) override; /** @copydoc Calendar::incidenceUpdated(const QString &,const QDateTime &) */ void incidenceUpdated(const QString &uid, const QDateTime &recurrenceId) override; using QObject::event; // prevent warning about hidden virtual method protected: /** @copydoc IncidenceBase::virtual_hook() */ void virtual_hook(int id, void *data) override; private: //@cond PRIVATE class Private; Private *const d; //@endcond Q_DISABLE_COPY(MemoryCalendar) }; } #endif diff --git a/src/occurrenceiterator.cpp b/src/occurrenceiterator.cpp index f42523a7b..01e0e0446 100644 --- a/src/occurrenceiterator.cpp +++ b/src/occurrenceiterator.cpp @@ -1,255 +1,255 @@ /* This file is part of the kcalcore library. Copyright (C) 2013 Christian Mollekopf This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the OccurrenceIterator class. @brief This class provides an iterator to iterate over all occurrences of incidences. @author Christian Mollekopf \ */ #include "occurrenceiterator.h" #include "calendar.h" #include "calfilter.h" #include "utils.h" #include -using namespace KCalCore; +using namespace KCalendarCore; /** Private class that helps to provide binary compatibility between releases. @internal */ //@cond PRIVATE -class Q_DECL_HIDDEN KCalCore::OccurrenceIterator::Private +class Q_DECL_HIDDEN KCalendarCore::OccurrenceIterator::Private { public: Private(OccurrenceIterator *qq) : q(qq), occurrenceIt(occurrenceList) { } OccurrenceIterator *q; QDateTime start; QDateTime end; struct Occurrence { Occurrence() { } Occurrence(const Incidence::Ptr &i, const QDateTime &recurrenceId, const QDateTime &startDate) : incidence(i), recurrenceId(recurrenceId), startDate(startDate) { } Incidence::Ptr incidence; QDateTime recurrenceId; QDateTime startDate; }; QList occurrenceList; QListIterator occurrenceIt; Occurrence current; /* - * KCalCore::CalFilter can't handle individual occurrences. + * KCalendarCore::CalFilter can't handle individual occurrences. * When filtering completed to-dos, the CalFilter doesn't hide * them if it's a recurring to-do. */ bool occurrenceIsHidden(const Calendar &calendar, const Incidence::Ptr &inc, const QDateTime &occurrenceDate) { if ((inc->type() == Incidence::TypeTodo) && calendar.filter() && - (calendar.filter()->criteria() & KCalCore::CalFilter::HideCompletedTodos)) { + (calendar.filter()->criteria() & KCalendarCore::CalFilter::HideCompletedTodos)) { if (inc->recurs()) { const Todo::Ptr todo = inc.staticCast(); if (todo && (occurrenceDate < todo->dtDue())) { return true; } } else if (inc->hasRecurrenceId()) { const Todo::Ptr mainTodo = calendar.todo(inc->uid()); if (mainTodo && mainTodo->isCompleted()) { return true; } } } return false; } void setupIterator(const Calendar &calendar, const Incidence::List &incidences) { for (const Incidence::Ptr &inc : qAsConst(incidences)) { if (inc->hasRecurrenceId()) { continue; } if (inc->recurs()) { QHash recurrenceIds; QDateTime incidenceRecStart = inc->dateTime(Incidence::RoleRecurrenceStart); //const bool isAllDay = inc->allDay(); const auto lstInstances = calendar.instances(inc); for (const Incidence::Ptr &exception : lstInstances) { if (incidenceRecStart.isValid()) { recurrenceIds.insert( exception->recurrenceId().toTimeZone(incidenceRecStart.timeZone()), exception); } } const auto occurrences = inc->recurrence()->timesInInterval(start, end); Incidence::Ptr incidence(inc), lastInc(inc); qint64 offset(0), lastOffset(0); QDateTime occurrenceStartDate; for (const auto &recurrenceId : qAsConst(occurrences)) { occurrenceStartDate = recurrenceId; bool resetIncidence = false; if (recurrenceIds.contains(recurrenceId)) { // TODO: exclude exceptions where the start/end is not within // (so the occurrence of the recurrence is omitted, but no exception is added) if (recurrenceIds.value(recurrenceId)->status() == Incidence::StatusCanceled) { continue; } incidence = recurrenceIds.value(recurrenceId); occurrenceStartDate = incidence->dtStart(); resetIncidence = !incidence->thisAndFuture(); offset = incidence->recurrenceId().secsTo(incidence->dtStart()); if (incidence->thisAndFuture()) { lastInc = incidence; lastOffset = offset; } } else if (inc != incidence) { //thisAndFuture exception is active occurrenceStartDate = occurrenceStartDate.addSecs(offset); } if (!occurrenceIsHidden(calendar, incidence, occurrenceStartDate)) { occurrenceList << Private::Occurrence(incidence, recurrenceId, occurrenceStartDate); } if (resetIncidence) { incidence = lastInc; offset = lastOffset; } } } else { occurrenceList << Private::Occurrence(inc, {}, inc->dtStart()); } } occurrenceIt = QListIterator(occurrenceList); } }; //@endcond /** * Right now there is little point in the iterator, but: * With an iterator it should be possible to solve this more memory efficiently * and with immediate results at the beginning of the selected timeframe. * Either all events are iterated simoulatneously, resulting in occurrences * of all events in parallel in the correct time-order, or incidence after * incidence, which would be even more efficient. * * By making this class a friend of calendar, we could also use the internally * available data structures. */ OccurrenceIterator::OccurrenceIterator(const Calendar &calendar, const QDateTime &start, const QDateTime &end) - : d(new KCalCore::OccurrenceIterator::Private(this)) + : d(new KCalendarCore::OccurrenceIterator::Private(this)) { d->start = start; d->end = end; Event::List events = calendar.rawEvents(start.date(), end.date(), start.timeZone()); if (calendar.filter()) { calendar.filter()->apply(&events); } Todo::List todos = calendar.rawTodos(start.date(), end.date(), start.timeZone()); if (calendar.filter()) { calendar.filter()->apply(&todos); } Journal::List journals; const Journal::List allJournals = calendar.rawJournals(); - for (const KCalCore::Journal::Ptr &journal : allJournals) { + for (const KCalendarCore::Journal::Ptr &journal : allJournals) { const QDate journalStart = journal->dtStart().toTimeZone(start.timeZone()).date(); if (journal->dtStart().isValid() && journalStart >= start.date() && journalStart <= end.date()) { journals << journal; } } if (calendar.filter()) { calendar.filter()->apply(&journals); } const Incidence::List incidences = - KCalCore::Calendar::mergeIncidenceList(events, todos, journals); + KCalendarCore::Calendar::mergeIncidenceList(events, todos, journals); d->setupIterator(calendar, incidences); } OccurrenceIterator::OccurrenceIterator(const Calendar &calendar, const Incidence::Ptr &incidence, const QDateTime &start, const QDateTime &end) - : d(new KCalCore::OccurrenceIterator::Private(this)) + : d(new KCalendarCore::OccurrenceIterator::Private(this)) { Q_ASSERT(incidence); d->start = start; d->end = end; d->setupIterator(calendar, Incidence::List() << incidence); } OccurrenceIterator::~OccurrenceIterator() { } bool OccurrenceIterator::hasNext() const { return d->occurrenceIt.hasNext(); } void OccurrenceIterator::next() { d->current = d->occurrenceIt.next(); } Incidence::Ptr OccurrenceIterator::incidence() const { return d->current.incidence; } QDateTime OccurrenceIterator::occurrenceStartDate() const { return d->current.startDate; } QDateTime OccurrenceIterator::recurrenceId() const { return d->current.recurrenceId; } diff --git a/src/occurrenceiterator.h b/src/occurrenceiterator.h index d88f46032..52813e954 100644 --- a/src/occurrenceiterator.h +++ b/src/occurrenceiterator.h @@ -1,105 +1,105 @@ /* This file is part of the kcalcore library. Copyright (c) 2013 Christian Mollekopf This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the OccurrenceIterator class. @author Christian Mollekopf \ */ #ifndef KCALCORE_OCCURRENCEITERATOR_H #define KCALCORE_OCCURRENCEITERATOR_H #include "kcalcore_export.h" #include "incidence.h" -namespace KCalCore +namespace KCalendarCore { class Calendar; /** * Iterate over calendar items in a calendar. * * The iterator takes recurrences and exceptions to recurrences into account * * The iterator does not iterate the occurrences of all incidences chronologically. * @since 4.11 */ class KCALCORE_EXPORT OccurrenceIterator { public: /** * Creates iterator that iterates over all occurrences of all incidences * between @param start and @param end (inclusive) */ explicit OccurrenceIterator(const Calendar &calendar, const QDateTime &start = QDateTime(), const QDateTime &end = QDateTime()); /** * Creates iterator that iterates over all occurrences * of @param incidence between @param start and @param end (inclusive) */ OccurrenceIterator(const Calendar &calendar, - const KCalCore::Incidence::Ptr &incidence, + const KCalendarCore::Incidence::Ptr &incidence, const QDateTime &start = QDateTime(), const QDateTime &end = QDateTime()); ~OccurrenceIterator(); bool hasNext() const; /** * Advance iterator to the next occurrence. */ void next(); /** * Returns either main incidence or exception, depending on occurrence. */ Incidence::Ptr incidence() const; /** * Returns the start date of the occurrence * * This is either the occurrence date, or the start date of an exception * which overrides that occurrence. */ QDateTime occurrenceStartDate() const; /** * Returns the recurrence Id. * * This is the date where the occurrence starts without exceptions, * this id is used to identify one exact occurrence. */ QDateTime recurrenceId() const; private: Q_DISABLE_COPY(OccurrenceIterator) //@cond PRIVATE class Private; QScopedPointer d; //@endcond }; } //namespace #endif diff --git a/src/period.cpp b/src/period.cpp index 98a94ef73..b85635416 100644 --- a/src/period.cpp +++ b/src/period.cpp @@ -1,174 +1,174 @@ /* This file is part of the kcalcore library. Copyright (c) 2001 Cornelius Schumacher Copyright (c) 2007 David Jarvie This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the Period class. @brief Represents a period of time. @author Cornelius Schumacher \ */ #include "period.h" #include "utils.h" #include #include -using namespace KCalCore; +using namespace KCalendarCore; //@cond PRIVATE -class Q_DECL_HIDDEN KCalCore::Period::Private +class Q_DECL_HIDDEN KCalendarCore::Period::Private { public: Private() : mHasDuration(false), mDailyDuration(false) {} Private(const QDateTime &start, const QDateTime &end, bool hasDuration) : mStart(start), mEnd(end), mHasDuration(hasDuration), mDailyDuration(false) {} QDateTime mStart; // period starting date/time QDateTime mEnd; // period ending date/time bool mHasDuration = false; // does period have a duration? bool mDailyDuration = false; // duration is defined as number of days, not seconds }; //@endcond -Period::Period() : d(new KCalCore::Period::Private()) +Period::Period() : d(new KCalendarCore::Period::Private()) { } Period::Period(const QDateTime &start, const QDateTime &end) - : d(new KCalCore::Period::Private(start, end, false)) + : d(new KCalendarCore::Period::Private(start, end, false)) { } Period::Period(const QDateTime &start, const Duration &duration) - : d(new KCalCore::Period::Private(start, duration.end(start), true)) + : d(new KCalendarCore::Period::Private(start, duration.end(start), true)) { d->mDailyDuration = duration.isDaily(); } Period::Period(const Period &period) - : d(new KCalCore::Period::Private(*period.d)) + : d(new KCalendarCore::Period::Private(*period.d)) { } Period::~Period() { delete d; } bool Period::operator<(const Period &other) const { return d->mStart < other.d->mStart; } bool Period::operator==(const Period &other) const { return ((d->mStart == other.d->mStart) || (!d->mStart.isValid() && !other.d->mStart.isValid())) && ((d->mEnd == other.d->mEnd) || (!d->mEnd.isValid() && !other.d->mEnd.isValid())) && d->mHasDuration == other.d->mHasDuration; } Period &Period::operator=(const Period &other) { // check for self assignment if (&other == this) { return *this; } *d = *other.d; return *this; } QDateTime Period::start() const { return d->mStart; } QDateTime Period::end() const { return d->mEnd; } Duration Period::duration() const { if (d->mHasDuration) { return Duration(d->mStart, d->mEnd, d->mDailyDuration ? Duration::Days : Duration::Seconds); } else { return Duration(d->mStart, d->mEnd); } } Duration Period::duration(Duration::Type type) const { return Duration(d->mStart, d->mEnd, type); } bool Period::hasDuration() const { return d->mHasDuration; } void Period::shiftTimes(const QTimeZone &oldZone, const QTimeZone &newZone) { if (oldZone.isValid() && newZone.isValid() && oldZone != newZone) { d->mStart = d->mStart.toTimeZone(oldZone); d->mStart.setTimeZone(newZone); d->mEnd = d->mEnd.toTimeZone(oldZone); d->mEnd.setTimeZone(newZone); } } -QDataStream &KCalCore::operator<<(QDataStream &stream, const KCalCore::Period &period) +QDataStream &KCalendarCore::operator<<(QDataStream &stream, const KCalendarCore::Period &period) { serializeQDateTimeAsKDateTime(stream, period.d->mStart); serializeQDateTimeAsKDateTime(stream, period.d->mEnd); return stream << period.d->mDailyDuration << period.d->mHasDuration; } -QDataStream &KCalCore::operator>>(QDataStream &stream, KCalCore::Period &period) +QDataStream &KCalendarCore::operator>>(QDataStream &stream, KCalendarCore::Period &period) { deserializeKDateTimeAsQDateTime(stream, period.d->mStart); deserializeKDateTimeAsQDateTime(stream, period.d->mEnd); stream >> period.d->mDailyDuration >> period.d->mHasDuration; return stream; } -uint KCalCore::qHash(const KCalCore::Period &key) +uint KCalendarCore::qHash(const KCalendarCore::Period &key) { if (key.hasDuration()) { return qHash(key.duration()); } else { return qHash(key.start().toString() + key.end().toString()); } } diff --git a/src/period.h b/src/period.h index 80e1d01a9..0eb897517 100644 --- a/src/period.h +++ b/src/period.h @@ -1,230 +1,230 @@ /* This file is part of the kcalcore library. Copyright (c) 2001-2003 Cornelius Schumacher This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the Period class. @brief Represents a period of time. @author Cornelius Schumacher \ */ #ifndef KCALCORE_PERIOD_H #define KCALCORE_PERIOD_H #include "kcalcore_export.h" #include "duration.h" #include #include #include #include class QTimeZone; -namespace KCalCore +namespace KCalendarCore { /** The period can be defined by either a start time and an end time or by a start time and a duration. */ class KCALCORE_EXPORT Period { public: /** List of periods. */ typedef QVector List; /** Constructs a period without a duration. */ Period(); /** Constructs a period from @p start to @p end. @param start the time the period begins. @param end the time the period ends. */ Period(const QDateTime &start, const QDateTime &end); /** Constructs a period from @p start and lasting @p duration. @param start the time when the period starts. @param duration how long the period lasts. */ Period(const QDateTime &start, const Duration &duration); /** Constructs a period by copying another period object @param period the period to copy */ Period(const Period &period); /** Destroys a period. */ ~Period(); /** Returns true if the start of this period is earlier than the start of the @p other one. @param other is the other period to compare. */ bool operator<(const Period &other) const; /** Returns true if the start of this period is later than the start of the @p other one. @param other the other period to compare */ bool operator>(const Period &other) const { return other.operator < (*this); } /** Returns true if this period is equal to the @p other one. Even if their start and end times are the same, two periods are considered not equal if one is defined in terms of a duration and the other in terms of a start and end time. @param other the other period to compare */ bool operator==(const Period &other) const; /** Returns true if this period is not equal to the @p other one. @param other the other period to compare @see operator==() */ bool operator!=(const Period &other) const { return !operator==(other); } /** Sets this period equal to the @p other one. @param other is the other period to compare. */ Period &operator=(const Period &other); /** Returns when this period starts. */ Q_REQUIRED_RESULT QDateTime start() const; /** Returns when this period ends. */ Q_REQUIRED_RESULT QDateTime end() const; /** Returns the duration of the period. If the period is defined in terms of a start and end time, the duration is computed from these. In this case, if the time of day in @p start and @p end is equal, and their time specifications (i.e. time zone etc.) are the same, the duration will be set in terms of days. Otherwise, the duration will be set in terms of seconds. If the period is defined in terms of a duration, that duration is returned unchanged. */ Q_REQUIRED_RESULT Duration duration() const; /** Returns the duration of the period. If the period is defined in terms of a start and end time, the duration is first computed from these. If @p type is Days, and the duration is not an exact number of days, the duration will be rounded down to the nearest whole number of days. @param type the unit of time to use (seconds or days) */ Q_REQUIRED_RESULT Duration duration(Duration::Type type) const; /** Returns true if this period has a set duration, false if it just has a start and an end. */ Q_REQUIRED_RESULT bool hasDuration() const; /** Shift the times of the period so that they appear at the same clock time as before but in a new time zone. The shift is done from a viewing time zone rather than from the actual period time zone. For example, shifting a period whose start time is 09:00 America/New York, using an old viewing time zone (@p oldSpec) of Europe/London, to a new time zone (@p newSpec) of Europe/Paris, will result in the time being shifted from 14:00 (which is the London time of the period start) to 14:00 Paris time. @param oldZone the time zone which provides the clock times @param newZone the new time zone */ void shiftTimes(const QTimeZone &oldZone, const QTimeZone &newZone); private: //@cond PRIVATE class Private; Private *const d; //@endcond friend KCALCORE_EXPORT QDataStream &operator<<(QDataStream &stream, - const KCalCore::Period &period); + const KCalendarCore::Period &period); friend KCALCORE_EXPORT QDataStream &operator>>(QDataStream &stream, - KCalCore::Period &period); + KCalendarCore::Period &period); }; /** Write @p period to the datastream @p stream, in binary format. */ -KCALCORE_EXPORT QDataStream &operator<<(QDataStream &stream, const KCalCore::Period &period); +KCALCORE_EXPORT QDataStream &operator<<(QDataStream &stream, const KCalendarCore::Period &period); /** Read a Period object into @p period from @p stream, in binary format. */ -KCALCORE_EXPORT QDataStream &operator>>(QDataStream &stream, KCalCore::Period &period); +KCALCORE_EXPORT QDataStream &operator>>(QDataStream &stream, KCalendarCore::Period &period); /** Return a hash value for a Period argument. @param key is a Period. */ -KCALCORE_EXPORT uint qHash(const KCalCore::Period &key); +KCALCORE_EXPORT uint qHash(const KCalendarCore::Period &key); } //@cond PRIVATE -Q_DECLARE_METATYPE(KCalCore::Period) -Q_DECLARE_TYPEINFO(KCalCore::Period, Q_MOVABLE_TYPE); +Q_DECLARE_METATYPE(KCalendarCore::Period) +Q_DECLARE_TYPEINFO(KCalendarCore::Period, Q_MOVABLE_TYPE); //@endcond #endif diff --git a/src/person.cpp b/src/person.cpp index 6e8fc2038..2e850bcad 100644 --- a/src/person.cpp +++ b/src/person.cpp @@ -1,380 +1,380 @@ /* This file is part of the kcalcore library. Copyright (c) 2001 Cornelius Schumacher Copyright (C) 2003-2004 Reinhold Kainhofer Copyright (C) 2010 Casey Link Copyright (C) 2009-2010 Klaralvdalens Datakonsult AB, a KDAB Group company This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the Person class. @brief Represents a person, by name and email address. @author Cornelius Schumacher \ @author Reinhold Kainhofer \ */ #include "person.h" #include "person_p.h" #include #include -using namespace KCalCore; +using namespace KCalendarCore; /** Private class that helps to provide binary compatibility between releases. @internal */ //@cond PRIVATE -class Q_DECL_HIDDEN KCalCore::Person::Private : public QSharedData +class Q_DECL_HIDDEN KCalendarCore::Person::Private : public QSharedData { public: QString mName; // person name QString mEmail; // person email address }; //@endcond -Person::Person() : d(new KCalCore::Person::Private) +Person::Person() : d(new KCalendarCore::Person::Private) { } Person::Person(const QString &name, const QString &email) - : d(new KCalCore::Person::Private) + : d(new KCalendarCore::Person::Private) { d->mName = name; d->mEmail = email; } Person::Person(const Person &person) : d(person.d) { } Person::~Person() = default; -bool KCalCore::Person::operator==(const Person &person) const +bool KCalendarCore::Person::operator==(const Person &person) const { return d->mName == person.d->mName && d->mEmail == person.d->mEmail; } -bool KCalCore::Person::operator!=(const Person &person) const +bool KCalendarCore::Person::operator!=(const Person &person) const { return !(*this == person); } -Person &KCalCore::Person::operator=(const Person &person) +Person &KCalendarCore::Person::operator=(const Person &person) { // check for self assignment if (&person == this) { return *this; } d = person.d; return *this; } -QString KCalCore::fullNameHelper(const QString &name, const QString &email) +QString KCalendarCore::fullNameHelper(const QString &name, const QString &email) { if (name.isEmpty()) { return email; } if (email.isEmpty()) { return name; } // Taken from KContacts::Addressee::fullEmail QString fullName = name; QRegExp needQuotes(QStringLiteral("[^ 0-9A-Za-z\\x0080-\\xFFFF]")); bool weNeedToQuote = name.indexOf(needQuotes) != -1; if (weNeedToQuote) { if (fullName[0] != QLatin1Char('"')) { fullName.prepend(QLatin1Char('"')); } if (fullName[ fullName.length() - 1 ] != QLatin1Char('"')) { fullName.append(QLatin1Char('"')); } } return fullName + QStringLiteral(" <") + email + QLatin1Char('>'); } QString Person::fullName() const { return fullNameHelper(d->mName, d->mEmail); } QString Person::name() const { return d->mName; } QString Person::email() const { return d->mEmail; } bool Person::isEmpty() const { return d->mEmail.isEmpty() && d->mName.isEmpty(); } void Person::setName(const QString &name) { d->mName = name; } void Person::setEmail(const QString &email) { if (email.startsWith(QLatin1String("mailto:"), Qt::CaseInsensitive)) { d->mEmail = email.mid(7); } else { d->mEmail = email; } } bool Person::isValidEmail(const QString &email) { const int pos = email.lastIndexOf(QLatin1Char('@')); return (pos > 0) && (email.lastIndexOf(QLatin1Char('.')) > pos) && ((email.length() - pos) > 4); } -uint KCalCore::qHash(const KCalCore::Person &key) +uint KCalendarCore::qHash(const KCalendarCore::Person &key) { return qHash(key.fullName()); } -QDataStream &KCalCore::operator<<(QDataStream &stream, const KCalCore::Person &person) +QDataStream &KCalendarCore::operator<<(QDataStream &stream, const KCalendarCore::Person &person) { return stream << person.d->mName << person.d->mEmail << (int)(0); } -QDataStream &KCalCore::operator>>(QDataStream &stream, Person &person) +QDataStream &KCalendarCore::operator>>(QDataStream &stream, Person &person) { int count; stream >> person.d->mName >> person.d->mEmail >> count; return stream; } // The following function was lifted directly from KPIMUtils // in order to eliminate the dependency on that library. // Any changes made here should be ported there, and vice versa. static bool extractEmailAddressAndName(const QString &aStr, QString &mail, QString &name) { name.clear(); mail.clear(); const int len = aStr.length(); const char cQuotes = '"'; bool bInComment = false; bool bInQuotesOutsideOfEmail = false; int i = 0, iAd = 0, iMailStart = 0, iMailEnd = 0; QChar c; unsigned int commentstack = 0; // Find the '@' of the email address // skipping all '@' inside "(...)" comments: while (i < len) { c = aStr[i]; if (QLatin1Char('(') == c) { commentstack++; } if (QLatin1Char(')') == c) { commentstack--; } bInComment = commentstack != 0; if (QLatin1Char('"') == c && !bInComment) { bInQuotesOutsideOfEmail = !bInQuotesOutsideOfEmail; } if (!bInComment && !bInQuotesOutsideOfEmail) { if (QLatin1Char('@') == c) { iAd = i; break; // found it } } ++i; } if (!iAd) { // We suppose the user is typing the string manually and just // has not finished typing the mail address part. // So we take everything that's left of the '<' as name and the rest as mail for (i = 0; len > i; ++i) { c = aStr[i]; if (QLatin1Char('<') != c) { name.append(c); } else { break; } } mail = aStr.mid(i + 1); if (mail.endsWith(QLatin1Char('>'))) { mail.truncate(mail.length() - 1); } } else { // Loop backwards until we find the start of the string // or a ',' that is outside of a comment // and outside of quoted text before the leading '<'. bInComment = false; bInQuotesOutsideOfEmail = false; for (i = iAd - 1; 0 <= i; --i) { c = aStr[i]; if (bInComment) { if (QLatin1Char('(') == c) { if (!name.isEmpty()) { name.prepend(QLatin1Char(' ')); } bInComment = false; } else { name.prepend(c); // all comment stuff is part of the name } } else if (bInQuotesOutsideOfEmail) { if (QLatin1Char(cQuotes) == c) { bInQuotesOutsideOfEmail = false; } else if (c != QLatin1Char('\\')) { name.prepend(c); } } else { // found the start of this addressee ? if (QLatin1Char(',') == c) { break; } // stuff is before the leading '<' ? if (iMailStart) { if (QLatin1Char(cQuotes) == c) { bInQuotesOutsideOfEmail = true; // end of quoted text found } else { name.prepend(c); } } else { switch (c.toLatin1()) { case '<': iMailStart = i; break; case ')': if (!name.isEmpty()) { name.prepend(QLatin1Char(' ')); } bInComment = true; break; default: if (QLatin1Char(' ') != c) { mail.prepend(c); } } } } } name = name.simplified(); mail = mail.simplified(); if (mail.isEmpty()) { return false; } mail.append(QLatin1Char('@')); // Loop forward until we find the end of the string // or a ',' that is outside of a comment // and outside of quoted text behind the trailing '>'. bInComment = false; bInQuotesOutsideOfEmail = false; int parenthesesNesting = 0; for (i = iAd + 1; len > i; ++i) { c = aStr[i]; if (bInComment) { if (QLatin1Char(')') == c) { if (--parenthesesNesting == 0) { bInComment = false; if (!name.isEmpty()) { name.append(QLatin1Char(' ')); } } else { // nested ")", add it name.append(QLatin1Char(')')); // name can't be empty here } } else { if (QLatin1Char('(') == c) { // nested "(" ++parenthesesNesting; } name.append(c); // all comment stuff is part of the name } } else if (bInQuotesOutsideOfEmail) { if (QLatin1Char(cQuotes) == c) { bInQuotesOutsideOfEmail = false; } else if (c != QLatin1Char('\\')) { name.append(c); } } else { // found the end of this addressee ? if (QLatin1Char(',') == c) { break; } // stuff is behind the trailing '>' ? if (iMailEnd) { if (QLatin1Char(cQuotes) == c) { bInQuotesOutsideOfEmail = true; // start of quoted text found } else { name.append(c); } } else { switch (c.toLatin1()) { case '>': iMailEnd = i; break; case '(': if (!name.isEmpty()) { name.append(QLatin1Char(' ')); } if (++parenthesesNesting > 0) { bInComment = true; } break; default: if (QLatin1Char(' ') != c) { mail.append(c); } } } } } } name = name.simplified(); mail = mail.simplified(); return !(name.isEmpty() || mail.isEmpty()); } Person Person::fromFullName(const QString &fullName) { QString email, name; extractEmailAddressAndName(fullName, email, name); return Person(name, email); } diff --git a/src/person.h b/src/person.h index cf8333c17..0555f32bd 100644 --- a/src/person.h +++ b/src/person.h @@ -1,209 +1,209 @@ /* This file is part of the kcalcore library. Copyright (c) 2001-2003 Cornelius Schumacher Copyright (C) 2003-2004 Reinhold Kainhofer This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the Person class. @author Cornelius Schumacher \ @author Reinhold Kainhofer \ */ #ifndef KCALCORE_PERSON_H #define KCALCORE_PERSON_H #include "kcalcore_export.h" #include #include #include #include -namespace KCalCore +namespace KCalendarCore { /** @brief Represents a person, by name and email address. This class represents a person, with a name and an email address. It supports the "FirstName LastName\ " format. */ class KCALCORE_EXPORT Person { Q_GADGET Q_PROPERTY(bool isEmpty READ isEmpty) Q_PROPERTY(QString fullName READ fullName) Q_PROPERTY(QString name READ name WRITE setName) Q_PROPERTY(QString email READ email WRITE setEmail) public: /** List of persons. */ typedef QVector List; /** Constructs a blank person. */ Person(); /** Constructs a person with name and email address taken from @p fullName. @param fullName is the name and email of the person in the form "FirstName LastName \". @return A Person object. */ static Person fromFullName(const QString &fullName); /** Constructs a person with the name @p name and email address @p email. @param name is the name of this person. @param email is the email address of this person. */ Person(const QString &name, const QString &email); /** Constructs a person as a copy of another person object. @param person is the person to copy. */ Person(const Person &person); /** Destroys a person. */ virtual ~Person(); /** Returns true if the person name and email address are empty. */ Q_REQUIRED_RESULT bool isEmpty() const; /** Returns the full name of this person. @return A QString containing the person's full name in the form "FirstName LastName \". */ Q_REQUIRED_RESULT QString fullName() const; /** Sets the name of the person to @p name. @param name is the name of this person. @see name() */ void setName(const QString &name); /** Returns the person name string. @see setName() */ Q_REQUIRED_RESULT QString name() const; /** Sets the email address for this person to @p email. @param email is the email address for this person. @see email() */ void setEmail(const QString &email); /** Returns the email address for this person. @return A QString containing the person's email address. @see setEmail() */ Q_REQUIRED_RESULT QString email() const; /** Returns true if person's email address is valid. Simple email validity check, test that there: * is at least one @ * is at least one character in the local part * is at least one dot in the domain part * is at least four characters in the domain (assuming that no-one has an address at the tld, that the tld is at least 2 chars) @param email is the email address to validate */ Q_REQUIRED_RESULT static bool isValidEmail(const QString &email); /** Compares this with @p person for equality. @param person is the person to compare. */ bool operator==(const Person &person) const; /** Compares this with @p person for non-equality. @param person is the person to compare. */ bool operator!=(const Person &person) const; /** Sets this person equal to @p person. @param person is the person to copy. */ Person &operator=(const Person &person); private: //@cond PRIVATE class Private; QSharedDataPointer d; //@endcond - friend KCALCORE_EXPORT QDataStream &operator<<(QDataStream &s, const KCalCore::Person &person); - friend KCALCORE_EXPORT QDataStream &operator>>(QDataStream &s, KCalCore::Person &person); + friend KCALCORE_EXPORT QDataStream &operator<<(QDataStream &s, const KCalendarCore::Person &person); + friend KCALCORE_EXPORT QDataStream &operator>>(QDataStream &s, KCalendarCore::Person &person); }; /** Serializes the @p person object into the @p stream. */ -KCALCORE_EXPORT QDataStream &operator<<(QDataStream &stream, const KCalCore::Person &person); +KCALCORE_EXPORT QDataStream &operator<<(QDataStream &stream, const KCalendarCore::Person &person); /** Initializes the @p person object from the @p stream. */ -KCALCORE_EXPORT QDataStream &operator>>(QDataStream &stream, KCalCore::Person &person); +KCALCORE_EXPORT QDataStream &operator>>(QDataStream &stream, KCalendarCore::Person &person); /** Return a hash value for a Person argument. @param key is a Person. */ -KCALCORE_EXPORT uint qHash(const KCalCore::Person &key); +KCALCORE_EXPORT uint qHash(const KCalendarCore::Person &key); } //@cond PRIVATE -Q_DECLARE_TYPEINFO(KCalCore::Person, Q_MOVABLE_TYPE); -Q_DECLARE_METATYPE(KCalCore::Person) +Q_DECLARE_TYPEINFO(KCalendarCore::Person, Q_MOVABLE_TYPE); +Q_DECLARE_METATYPE(KCalendarCore::Person) //@endcond #endif diff --git a/src/person_p.h b/src/person_p.h index 2ed2f013c..9648fac15 100644 --- a/src/person_p.h +++ b/src/person_p.h @@ -1,32 +1,32 @@ /* This file is part of the kcalcore library. Copyright (c) 2019 Volker Krause This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KCALCORE_PERSON_P_H #define KCALCORE_PERSON_P_H class QString; -namespace KCalCore +namespace KCalendarCore { QString fullNameHelper(const QString &name, const QString &email); } #endif diff --git a/src/recurrence.cpp b/src/recurrence.cpp index 36507ff69..04ffdcc18 100644 --- a/src/recurrence.cpp +++ b/src/recurrence.cpp @@ -1,1555 +1,1555 @@ /* This file is part of kcalcore library. Copyright (c) 1998 Preston Brown Copyright (c) 2001 Cornelius Schumacher Copyright (c) 2002,2006 David Jarvie Copyright (C) 2005 Reinhold Kainhofer This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "recurrence.h" #include "utils.h" #include "recurrencehelper_p.h" #include "kcalcore_debug.h" #include #include #include #include -using namespace KCalCore; +using namespace KCalendarCore; //@cond PRIVATE -class Q_DECL_HIDDEN KCalCore::Recurrence::Private +class Q_DECL_HIDDEN KCalendarCore::Recurrence::Private { public: Private() : mCachedType(rMax), mAllDay(false), mRecurReadOnly(false) { } Private(const Private &p) : mRDateTimes(p.mRDateTimes), mRDates(p.mRDates), mExDateTimes(p.mExDateTimes), mExDates(p.mExDates), mStartDateTime(p.mStartDateTime), mCachedType(p.mCachedType), mAllDay(p.mAllDay), mRecurReadOnly(p.mRecurReadOnly) { } bool operator==(const Private &p) const; RecurrenceRule::List mExRules; RecurrenceRule::List mRRules; QList mRDateTimes; DateList mRDates; QList mExDateTimes; DateList mExDates; QDateTime mStartDateTime; // date/time of first recurrence QList mObservers; // Cache the type of the recurrence with the old system (e.g. MonthlyPos) mutable ushort mCachedType; bool mAllDay = false; // the recurrence has no time, just a date bool mRecurReadOnly = false; }; bool Recurrence::Private::operator==(const Recurrence::Private &p) const { // qCDebug(KCALCORE_LOG) << mStartDateTime << p.mStartDateTime; if ((mStartDateTime != p.mStartDateTime && (mStartDateTime.isValid() || p.mStartDateTime.isValid())) || mAllDay != p.mAllDay || mRecurReadOnly != p.mRecurReadOnly || mExDates != p.mExDates || mExDateTimes != p.mExDateTimes || mRDates != p.mRDates || mRDateTimes != p.mRDateTimes) { return false; } // Compare the rrules, exrules! Assume they have the same order... This only // matters if we have more than one rule (which shouldn't be the default anyway) int i; int end = mRRules.count(); if (end != p.mRRules.count()) { return false; } for (i = 0; i < end; ++i) { if (*mRRules[i] != *p.mRRules[i]) { return false; } } end = mExRules.count(); if (end != p.mExRules.count()) { return false; } for (i = 0; i < end; ++i) { if (*mExRules[i] != *p.mExRules[i]) { return false; } } return true; } //@endcond Recurrence::Recurrence() - : d(new KCalCore::Recurrence::Private()) + : d(new KCalendarCore::Recurrence::Private()) { } Recurrence::Recurrence(const Recurrence &r) : RecurrenceRule::RuleObserver(), - d(new KCalCore::Recurrence::Private(*r.d)) + d(new KCalendarCore::Recurrence::Private(*r.d)) { int i, end; d->mRRules.reserve(r.d->mRRules.count()); for (i = 0, end = r.d->mRRules.count(); i < end; ++i) { RecurrenceRule *rule = new RecurrenceRule(*r.d->mRRules[i]); d->mRRules.append(rule); rule->addObserver(this); } d->mExRules.reserve(r.d->mExRules.count()); for (i = 0, end = r.d->mExRules.count(); i < end; ++i) { RecurrenceRule *rule = new RecurrenceRule(*r.d->mExRules[i]); d->mExRules.append(rule); rule->addObserver(this); } } Recurrence::~Recurrence() { qDeleteAll(d->mExRules); qDeleteAll(d->mRRules); delete d; } bool Recurrence::operator==(const Recurrence &recurrence) const { return *d == *recurrence.d; } Recurrence &Recurrence::operator=(const Recurrence &recurrence) { // check for self assignment if (&recurrence == this) { return *this; } *d = *recurrence.d; return *this; } void Recurrence::addObserver(RecurrenceObserver *observer) { if (!d->mObservers.contains(observer)) { d->mObservers.append(observer); } } void Recurrence::removeObserver(RecurrenceObserver *observer) { d->mObservers.removeAll(observer); } QDateTime Recurrence::startDateTime() const { return d->mStartDateTime; } bool Recurrence::allDay() const { return d->mAllDay; } void Recurrence::setAllDay(bool allDay) { if (d->mRecurReadOnly || allDay == d->mAllDay) { return; } d->mAllDay = allDay; for (int i = 0, end = d->mRRules.count(); i < end; ++i) { d->mRRules[i]->setAllDay(allDay); } for (int i = 0, end = d->mExRules.count(); i < end; ++i) { d->mExRules[i]->setAllDay(allDay); } updated(); } RecurrenceRule *Recurrence::defaultRRule(bool create) const { if (d->mRRules.isEmpty()) { if (!create || d->mRecurReadOnly) { return nullptr; } RecurrenceRule *rrule = new RecurrenceRule(); rrule->setStartDt(startDateTime()); - const_cast(this)->addRRule(rrule); + const_cast(this)->addRRule(rrule); return rrule; } else { return d->mRRules[0]; } } RecurrenceRule *Recurrence::defaultRRuleConst() const { return d->mRRules.isEmpty() ? nullptr : d->mRRules[0]; } void Recurrence::updated() { // recurrenceType() re-calculates the type if it's rMax d->mCachedType = rMax; for (int i = 0, end = d->mObservers.count(); i < end; ++i) { if (d->mObservers[i]) { d->mObservers[i]->recurrenceUpdated(this); } } } bool Recurrence::recurs() const { return !d->mRRules.isEmpty() || !d->mRDates.isEmpty() || !d->mRDateTimes.isEmpty(); } ushort Recurrence::recurrenceType() const { if (d->mCachedType == rMax) { d->mCachedType = recurrenceType(defaultRRuleConst()); } return d->mCachedType; } ushort Recurrence::recurrenceType(const RecurrenceRule *rrule) { if (!rrule) { return rNone; } RecurrenceRule::PeriodType type = rrule->recurrenceType(); // BYSETPOS, BYWEEKNUMBER and BYSECOND were not supported in old versions if (!rrule->bySetPos().isEmpty() || !rrule->bySeconds().isEmpty() || !rrule->byWeekNumbers().isEmpty()) { return rOther; } // It wasn't possible to set BYMINUTES, BYHOUR etc. by the old code. So if // it's set, it's none of the old types if (!rrule->byMinutes().isEmpty() || !rrule->byHours().isEmpty()) { return rOther; } // Possible combinations were: // BYDAY: with WEEKLY, MONTHLY, YEARLY // BYMONTHDAY: with MONTHLY, YEARLY // BYMONTH: with YEARLY // BYYEARDAY: with YEARLY if ((!rrule->byYearDays().isEmpty() && type != RecurrenceRule::rYearly) || (!rrule->byMonths().isEmpty() && type != RecurrenceRule::rYearly)) { return rOther; } if (!rrule->byDays().isEmpty()) { if (type != RecurrenceRule::rYearly && type != RecurrenceRule::rMonthly && type != RecurrenceRule::rWeekly) { return rOther; } } switch (type) { case RecurrenceRule::rNone: return rNone; case RecurrenceRule::rMinutely: return rMinutely; case RecurrenceRule::rHourly: return rHourly; case RecurrenceRule::rDaily: return rDaily; case RecurrenceRule::rWeekly: return rWeekly; case RecurrenceRule::rMonthly: { if (rrule->byDays().isEmpty()) { return rMonthlyDay; } else if (rrule->byMonthDays().isEmpty()) { return rMonthlyPos; } else { return rOther; // both position and date specified } } case RecurrenceRule::rYearly: { // Possible combinations: // rYearlyMonth: [BYMONTH &] BYMONTHDAY // rYearlyDay: BYYEARDAY // rYearlyPos: [BYMONTH &] BYDAY if (!rrule->byDays().isEmpty()) { // can only by rYearlyPos if (rrule->byMonthDays().isEmpty() && rrule->byYearDays().isEmpty()) { return rYearlyPos; } else { return rOther; } } else if (!rrule->byYearDays().isEmpty()) { // Can only be rYearlyDay if (rrule->byMonths().isEmpty() && rrule->byMonthDays().isEmpty()) { return rYearlyDay; } else { return rOther; } } else { return rYearlyMonth; } } default: return rOther; } } bool Recurrence::recursOn(const QDate &qd, const QTimeZone &timeZone) const { // Don't waste time if date is before the start of the recurrence if (QDateTime(qd, QTime(23, 59, 59), timeZone) < d->mStartDateTime) { return false; } // First handle dates. Exrules override if (std::binary_search(d->mExDates.constBegin(), d->mExDates.constEnd(), qd)) { return false; } int i, end; // For all-day events a matching exrule excludes the whole day // since exclusions take precedence over inclusions, we know it can't occur on that day. if (allDay()) { for (i = 0, end = d->mExRules.count(); i < end; ++i) { if (d->mExRules[i]->recursOn(qd, timeZone)) { return false; } } } if (std::binary_search(d->mRDates.constBegin(), d->mRDates.constEnd(), qd)) { return true; } // Check if it might recur today at all. bool recurs = (startDate() == qd); for (i = 0, end = d->mRDateTimes.count(); i < end && !recurs; ++i) { recurs = (d->mRDateTimes[i].toTimeZone(timeZone).date() == qd); } for (i = 0, end = d->mRRules.count(); i < end && !recurs; ++i) { recurs = d->mRRules[i]->recursOn(qd, timeZone); } // If the event wouldn't recur at all, simply return false, don't check ex* if (!recurs) { return false; } // Check if there are any times for this day excluded, either by exdate or exrule: bool exon = false; for (i = 0, end = d->mExDateTimes.count(); i < end && !exon; ++i) { exon = (d->mExDateTimes[i].toTimeZone(timeZone).date() == qd); } if (!allDay()) { // we have already checked all-day times above for (i = 0, end = d->mExRules.count(); i < end && !exon; ++i) { exon = d->mExRules[i]->recursOn(qd, timeZone); } } if (!exon) { // Simple case, nothing on that day excluded, return the value from before return recurs; } else { // Harder part: I don't think there is any way other than to calculate the // whole list of items for that day. //TODO: consider whether it would be more efficient to call // Rule::recurTimesOn() instead of Rule::recursOn() from the start TimeList timesForDay(recurTimesOn(qd, timeZone)); return !timesForDay.isEmpty(); } } bool Recurrence::recursAt(const QDateTime &dt) const { // Convert to recurrence's time zone for date comparisons, and for more efficient time comparisons const auto dtrecur = dt.toTimeZone(d->mStartDateTime.timeZone()); // if it's excluded anyway, don't bother to check if it recurs at all. if (std::binary_search(d->mExDateTimes.constBegin(), d->mExDateTimes.constEnd(), dtrecur) || std::binary_search(d->mExDates.constBegin(), d->mExDates.constEnd(), dtrecur.date())) { return false; } int i, end; for (i = 0, end = d->mExRules.count(); i < end; ++i) { if (d->mExRules[i]->recursAt(dtrecur)) { return false; } } // Check explicit recurrences, then rrules. if (startDateTime() == dtrecur || std::binary_search(d->mRDateTimes.constBegin(), d->mRDateTimes.constEnd(), dtrecur)) { return true; } for (i = 0, end = d->mRRules.count(); i < end; ++i) { if (d->mRRules[i]->recursAt(dtrecur)) { return true; } } return false; } /** Calculates the cumulative end of the whole recurrence (rdates and rrules). If any rrule is infinite, or the recurrence doesn't have any rrules or rdates, an invalid date is returned. */ QDateTime Recurrence::endDateTime() const { QList dts; dts << startDateTime(); if (!d->mRDates.isEmpty()) { dts << QDateTime(d->mRDates.last(), QTime(0, 0, 0), d->mStartDateTime.timeZone()); } if (!d->mRDateTimes.isEmpty()) { dts << d->mRDateTimes.last(); } for (int i = 0, end = d->mRRules.count(); i < end; ++i) { auto rl = d->mRRules[i]->endDt(); // if any of the rules is infinite, the whole recurrence is if (!rl.isValid()) { return QDateTime(); } dts << rl; } sortAndRemoveDuplicates(dts); return dts.isEmpty() ? QDateTime() : dts.last(); } /** Calculates the cumulative end of the whole recurrence (rdates and rrules). If any rrule is infinite, or the recurrence doesn't have any rrules or rdates, an invalid date is returned. */ QDate Recurrence::endDate() const { QDateTime end(endDateTime()); return end.isValid() ? end.date() : QDate(); } void Recurrence::setEndDate(const QDate &date) { QDateTime dt(date, d->mStartDateTime.time(), d->mStartDateTime.timeZone()); if (allDay()) { dt.setTime(QTime(23, 59, 59)); } setEndDateTime(dt); } void Recurrence::setEndDateTime(const QDateTime &dateTime) { if (d->mRecurReadOnly) { return; } RecurrenceRule *rrule = defaultRRule(true); if (!rrule) { return; } // If the recurrence rule has a duration, and we're trying to set an invalid end date, // we have to skip setting it to avoid setting the field dirty. // The end date is already invalid since the duration is set and end date/duration // are mutually exclusive. // We can't use inequality check below, because endDt() also returns a valid date // for a duration (it is calculated from the duration). if (rrule->duration() > 0 && !dateTime.isValid()) { return; } if (dateTime != rrule->endDt()) { rrule->setEndDt(dateTime); updated(); } } int Recurrence::duration() const { RecurrenceRule *rrule = defaultRRuleConst(); return rrule ? rrule->duration() : 0; } int Recurrence::durationTo(const QDateTime &datetime) const { // Emulate old behavior: This is just an interface to the first rule! RecurrenceRule *rrule = defaultRRuleConst(); return rrule ? rrule->durationTo(datetime) : 0; } int Recurrence::durationTo(const QDate &date) const { return durationTo(QDateTime(date, QTime(23, 59, 59), d->mStartDateTime.timeZone())); } void Recurrence::setDuration(int duration) { if (d->mRecurReadOnly) { return; } RecurrenceRule *rrule = defaultRRule(true); if (!rrule) { return; } if (duration != rrule->duration()) { rrule->setDuration(duration); updated(); } } void Recurrence::shiftTimes(const QTimeZone &oldTz, const QTimeZone &newTz) { if (d->mRecurReadOnly) { return; } d->mStartDateTime = d->mStartDateTime.toTimeZone(oldTz); d->mStartDateTime.setTimeZone(newTz); int i, end; for (i = 0, end = d->mRDateTimes.count(); i < end; ++i) { d->mRDateTimes[i] = d->mRDateTimes[i].toTimeZone(oldTz); d->mRDateTimes[i].setTimeZone(newTz); } for (i = 0, end = d->mExDateTimes.count(); i < end; ++i) { d->mExDateTimes[i] = d->mExDateTimes[i].toTimeZone(oldTz); d->mExDateTimes[i].setTimeZone(newTz); } for (i = 0, end = d->mRRules.count(); i < end; ++i) { d->mRRules[i]->shiftTimes(oldTz, newTz); } for (i = 0, end = d->mExRules.count(); i < end; ++i) { d->mExRules[i]->shiftTimes(oldTz, newTz); } } void Recurrence::unsetRecurs() { if (d->mRecurReadOnly) { return; } qDeleteAll(d->mRRules); d->mRRules.clear(); updated(); } void Recurrence::clear() { if (d->mRecurReadOnly) { return; } qDeleteAll(d->mRRules); d->mRRules.clear(); qDeleteAll(d->mExRules); d->mExRules.clear(); d->mRDates.clear(); d->mRDateTimes.clear(); d->mExDates.clear(); d->mExDateTimes.clear(); d->mCachedType = rMax; updated(); } void Recurrence::setRecurReadOnly(bool readOnly) { d->mRecurReadOnly = readOnly; } bool Recurrence::recurReadOnly() const { return d->mRecurReadOnly; } QDate Recurrence::startDate() const { return d->mStartDateTime.date(); } void Recurrence::setStartDateTime(const QDateTime &start, bool isAllDay) { if (d->mRecurReadOnly) { return; } d->mStartDateTime = start; setAllDay(isAllDay); // set all RRULEs and EXRULEs int i, end; for (i = 0, end = d->mRRules.count(); i < end; ++i) { d->mRRules[i]->setStartDt(start); } for (i = 0, end = d->mExRules.count(); i < end; ++i) { d->mExRules[i]->setStartDt(start); } updated(); } int Recurrence::frequency() const { RecurrenceRule *rrule = defaultRRuleConst(); return rrule ? rrule->frequency() : 0; } // Emulate the old behaviour. Make this methods just an interface to the // first rrule void Recurrence::setFrequency(int freq) { if (d->mRecurReadOnly || freq <= 0) { return; } RecurrenceRule *rrule = defaultRRule(true); if (rrule) { rrule->setFrequency(freq); } updated(); } // WEEKLY int Recurrence::weekStart() const { RecurrenceRule *rrule = defaultRRuleConst(); return rrule ? rrule->weekStart() : 1; } // Emulate the old behavior QBitArray Recurrence::days() const { QBitArray days(7); days.fill(0); RecurrenceRule *rrule = defaultRRuleConst(); if (rrule) { const QList &bydays = rrule->byDays(); for (int i = 0; i < bydays.size(); ++i) { if (bydays.at(i).pos() == 0) { days.setBit(bydays.at(i).day() - 1); } } } return days; } // MONTHLY // Emulate the old behavior QList Recurrence::monthDays() const { RecurrenceRule *rrule = defaultRRuleConst(); if (rrule) { return rrule->byMonthDays(); } else { return QList(); } } // Emulate the old behavior QList Recurrence::monthPositions() const { RecurrenceRule *rrule = defaultRRuleConst(); return rrule ? rrule->byDays() : QList(); } // YEARLY QList Recurrence::yearDays() const { RecurrenceRule *rrule = defaultRRuleConst(); return rrule ? rrule->byYearDays() : QList(); } QList Recurrence::yearDates() const { return monthDays(); } QList Recurrence::yearMonths() const { RecurrenceRule *rrule = defaultRRuleConst(); return rrule ? rrule->byMonths() : QList(); } QList Recurrence::yearPositions() const { return monthPositions(); } RecurrenceRule *Recurrence::setNewRecurrenceType(RecurrenceRule::PeriodType type, int freq) { if (d->mRecurReadOnly || freq <= 0) { return nullptr; } // Ignore the call if nothing has change if (defaultRRuleConst() && defaultRRuleConst()->recurrenceType() == type && frequency() == freq) { return nullptr; } qDeleteAll(d->mRRules); d->mRRules.clear(); updated(); RecurrenceRule *rrule = defaultRRule(true); if (!rrule) { return nullptr; } rrule->setRecurrenceType(type); rrule->setFrequency(freq); rrule->setDuration(-1); return rrule; } void Recurrence::setMinutely(int _rFreq) { if (setNewRecurrenceType(RecurrenceRule::rMinutely, _rFreq)) { updated(); } } void Recurrence::setHourly(int _rFreq) { if (setNewRecurrenceType(RecurrenceRule::rHourly, _rFreq)) { updated(); } } void Recurrence::setDaily(int _rFreq) { if (setNewRecurrenceType(RecurrenceRule::rDaily, _rFreq)) { updated(); } } void Recurrence::setWeekly(int freq, int weekStart) { RecurrenceRule *rrule = setNewRecurrenceType(RecurrenceRule::rWeekly, freq); if (!rrule) { return; } rrule->setWeekStart(weekStart); updated(); } void Recurrence::setWeekly(int freq, const QBitArray &days, int weekStart) { setWeekly(freq, weekStart); addMonthlyPos(0, days); } void Recurrence::addWeeklyDays(const QBitArray &days) { addMonthlyPos(0, days); } void Recurrence::setMonthly(int freq) { if (setNewRecurrenceType(RecurrenceRule::rMonthly, freq)) { updated(); } } void Recurrence::addMonthlyPos(short pos, const QBitArray &days) { // Allow 53 for yearly! if (d->mRecurReadOnly || pos > 53 || pos < -53) { return; } RecurrenceRule *rrule = defaultRRule(false); if (!rrule) { return; } bool changed = false; QList positions = rrule->byDays(); for (int i = 0; i < 7; ++i) { if (days.testBit(i)) { RecurrenceRule::WDayPos p(pos, i + 1); if (!positions.contains(p)) { changed = true; positions.append(p); } } } if (changed) { rrule->setByDays(positions); updated(); } } void Recurrence::addMonthlyPos(short pos, ushort day) { // Allow 53 for yearly! if (d->mRecurReadOnly || pos > 53 || pos < -53) { return; } RecurrenceRule *rrule = defaultRRule(false); if (!rrule) { return; } QList positions = rrule->byDays(); RecurrenceRule::WDayPos p(pos, day); if (!positions.contains(p)) { positions.append(p); setMonthlyPos(positions); } } void Recurrence::setMonthlyPos(const QList &monthlyDays) { if (d->mRecurReadOnly) { return; } RecurrenceRule *rrule = defaultRRule(true); if (!rrule) { return; } //TODO: sort lists // the position inside the list has no meaning, so sort the list before testing if it changed if (monthlyDays != rrule->byDays()) { rrule->setByDays(monthlyDays); updated(); } } void Recurrence::addMonthlyDate(short day) { if (d->mRecurReadOnly || day > 31 || day < -31) { return; } RecurrenceRule *rrule = defaultRRule(true); if (!rrule) { return; } QList monthDays = rrule->byMonthDays(); if (!monthDays.contains(day)) { monthDays.append(day); setMonthlyDate(monthDays); } } void Recurrence::setMonthlyDate(const QList< int > &monthlyDays) { if (d->mRecurReadOnly) { return; } RecurrenceRule *rrule = defaultRRule(true); if (!rrule) { return; } QList mD(monthlyDays); QList rbD(rrule->byMonthDays()); sortAndRemoveDuplicates(mD); sortAndRemoveDuplicates(rbD); if (mD != rbD) { rrule->setByMonthDays(monthlyDays); updated(); } } void Recurrence::setYearly(int freq) { if (setNewRecurrenceType(RecurrenceRule::rYearly, freq)) { updated(); } } // Daynumber within year void Recurrence::addYearlyDay(int day) { RecurrenceRule *rrule = defaultRRule(false); // It must already exist! if (!rrule) { return; } QList days = rrule->byYearDays(); if (!days.contains(day)) { days << day; setYearlyDay(days); } } void Recurrence::setYearlyDay(const QList &days) { RecurrenceRule *rrule = defaultRRule(false); // It must already exist! if (!rrule) { return; } QList d(days); QList bYD(rrule->byYearDays()); sortAndRemoveDuplicates(d); sortAndRemoveDuplicates(bYD); if (d != bYD) { rrule->setByYearDays(days); updated(); } } // day part of date within year void Recurrence::addYearlyDate(int day) { addMonthlyDate(day); } void Recurrence::setYearlyDate(const QList &dates) { setMonthlyDate(dates); } // day part of date within year, given as position (n-th weekday) void Recurrence::addYearlyPos(short pos, const QBitArray &days) { addMonthlyPos(pos, days); } void Recurrence::setYearlyPos(const QList &days) { setMonthlyPos(days); } // month part of date within year void Recurrence::addYearlyMonth(short month) { if (d->mRecurReadOnly || month < 1 || month > 12) { return; } RecurrenceRule *rrule = defaultRRule(false); if (!rrule) { return; } QList months = rrule->byMonths(); if (!months.contains(month)) { months << month; setYearlyMonth(months); } } void Recurrence::setYearlyMonth(const QList &months) { if (d->mRecurReadOnly) { return; } RecurrenceRule *rrule = defaultRRule(false); if (!rrule) { return; } QList m(months); QList bM(rrule->byMonths()); sortAndRemoveDuplicates(m); sortAndRemoveDuplicates(bM); if (m != bM) { rrule->setByMonths(months); updated(); } } TimeList Recurrence::recurTimesOn(const QDate &date, const QTimeZone &timeZone) const { // qCDebug(KCALCORE_LOG) << "recurTimesOn(" << date << ")"; int i, end; TimeList times; // The whole day is excepted if (std::binary_search(d->mExDates.constBegin(), d->mExDates.constEnd(), date)) { return times; } // EXRULE takes precedence over RDATE entries, so for all-day events, // a matching excule also excludes the whole day automatically if (allDay()) { for (i = 0, end = d->mExRules.count(); i < end; ++i) { if (d->mExRules[i]->recursOn(date, timeZone)) { return times; } } } QDateTime dt = startDateTime().toTimeZone(timeZone); if (dt.date() == date) { times << dt.time(); } bool foundDate = false; for (i = 0, end = d->mRDateTimes.count(); i < end; ++i) { dt = d->mRDateTimes[i].toTimeZone(timeZone); if (dt.date() == date) { times << dt.time(); foundDate = true; } else if (foundDate) { break; // <= Assume that the rdatetime list is sorted } } for (i = 0, end = d->mRRules.count(); i < end; ++i) { times += d->mRRules[i]->recurTimesOn(date, timeZone); } sortAndRemoveDuplicates(times); foundDate = false; TimeList extimes; for (i = 0, end = d->mExDateTimes.count(); i < end; ++i) { dt = d->mExDateTimes[i].toTimeZone(timeZone); if (dt.date() == date) { extimes << dt.time(); foundDate = true; } else if (foundDate) { break; } } if (!allDay()) { // we have already checked all-day times above for (i = 0, end = d->mExRules.count(); i < end; ++i) { extimes += d->mExRules[i]->recurTimesOn(date, timeZone); } } sortAndRemoveDuplicates(extimes); inplaceSetDifference(times, extimes); return times; } QList Recurrence::timesInInterval(const QDateTime &start, const QDateTime &end) const { int i, count; QList times; for (i = 0, count = d->mRRules.count(); i < count; ++i) { times += d->mRRules[i]->timesInInterval(start, end); } // add rdatetimes that fit in the interval for (i = 0, count = d->mRDateTimes.count(); i < count; ++i) { if (d->mRDateTimes[i] >= start && d->mRDateTimes[i] <= end) { times += d->mRDateTimes[i]; } } // add rdates that fit in the interval QDateTime kdt = d->mStartDateTime; for (i = 0, count = d->mRDates.count(); i < count; ++i) { kdt.setDate(d->mRDates[i]); if (kdt >= start && kdt <= end) { times += kdt; } } // Recurrence::timesInInterval(...) doesn't explicitly add mStartDateTime to the list // of times to be returned. It calls mRRules[i]->timesInInterval(...) which include // mStartDateTime. // So, If we have rdates/rdatetimes but don't have any rrule we must explicitly // add mStartDateTime to the list, otherwise we won't see the first occurrence. if ((!d->mRDates.isEmpty() || !d->mRDateTimes.isEmpty()) && d->mRRules.isEmpty() && start <= d->mStartDateTime && end >= d->mStartDateTime) { times += d->mStartDateTime; } sortAndRemoveDuplicates(times); // Remove excluded times int idt = 0; int enddt = times.count(); for (i = 0, count = d->mExDates.count(); i < count && idt < enddt; ++i) { while (idt < enddt && times[idt].date() < d->mExDates[i]) { ++idt; } while (idt < enddt && times[idt].date() == d->mExDates[i]) { times.removeAt(idt); --enddt; } } QList extimes; for (i = 0, count = d->mExRules.count(); i < count; ++i) { extimes += d->mExRules[i]->timesInInterval(start, end); } extimes += d->mExDateTimes; sortAndRemoveDuplicates(extimes); inplaceSetDifference(times, extimes); return times; } QDateTime Recurrence::getNextDateTime(const QDateTime &preDateTime) const { QDateTime nextDT = preDateTime; // prevent infinite loops, e.g. when an exrule extinguishes an rrule (e.g. // the exrule is identical to the rrule). If an occurrence is found, break // out of the loop by returning that QDateTime // TODO_Recurrence: Is a loop counter of 1000 really okay? I mean for secondly // recurrence, an exdate might exclude more than 1000 intervals! int loop = 0; while (loop < 1000) { // Outline of the algo: // 1) Find the next date/time after preDateTime when the event could recur // 1.0) Add the start date if it's after preDateTime // 1.1) Use the next occurrence from the explicit RDATE lists // 1.2) Add the next recurrence for each of the RRULEs // 2) Take the earliest recurrence of these = QDateTime nextDT // 3) If that date/time is not excluded, either explicitly by an EXDATE or // by an EXRULE, return nextDT as the next date/time of the recurrence // 4) If it's excluded, start all at 1), but starting at nextDT (instead // of preDateTime). Loop at most 1000 times. ++loop; // First, get the next recurrence from the RDate lists QList dates; if (nextDT < startDateTime()) { dates << startDateTime(); } // Assume that the rdatetime list is sorted const auto it = std::upper_bound(d->mRDateTimes.constBegin(), d->mRDateTimes.constEnd(), nextDT); if (it != d->mRDateTimes.constEnd()) { dates << *it; } QDateTime kdt(startDateTime()); for (const auto &date : qAsConst(d->mRDates)) { kdt.setDate(date); if (kdt > nextDT) { dates << kdt; break; } } // Add the next occurrences from all RRULEs. for (const auto &rule : qAsConst(d->mRRules)) { QDateTime dt = rule->getNextDate(nextDT); if (dt.isValid()) { dates << dt; } } // Take the first of these (all others can't be used later on) sortAndRemoveDuplicates(dates); if (dates.isEmpty()) { return QDateTime(); } nextDT = dates.first(); // Check if that date/time is excluded explicitly or by an exrule: if (!std::binary_search(d->mExDates.constBegin(), d->mExDates.constEnd(), nextDT.date()) && !std::binary_search(d->mExDateTimes.constBegin(), d->mExDateTimes.constEnd(), nextDT)) { bool allowed = true; for (const auto &rule : qAsConst(d->mExRules)) { allowed = allowed && !rule->recursAt(nextDT); } if (allowed) { return nextDT; } } } // Couldn't find a valid occurrences in 1000 loops, something is wrong! return QDateTime(); } QDateTime Recurrence::getPreviousDateTime(const QDateTime &afterDateTime) const { QDateTime prevDT = afterDateTime; // prevent infinite loops, e.g. when an exrule extinguishes an rrule (e.g. // the exrule is identical to the rrule). If an occurrence is found, break // out of the loop by returning that QDateTime int loop = 0; while (loop < 1000) { // Outline of the algo: // 1) Find the next date/time after preDateTime when the event could recur // 1.1) Use the next occurrence from the explicit RDATE lists // 1.2) Add the next recurrence for each of the RRULEs // 2) Take the earliest recurrence of these = QDateTime nextDT // 3) If that date/time is not excluded, either explicitly by an EXDATE or // by an EXRULE, return nextDT as the next date/time of the recurrence // 4) If it's excluded, start all at 1), but starting at nextDT (instead // of preDateTime). Loop at most 1000 times. ++loop; // First, get the next recurrence from the RDate lists QList dates; if (prevDT > startDateTime()) { dates << startDateTime(); } const auto it = strictLowerBound(d->mRDateTimes.constBegin(), d->mRDateTimes.constEnd(), prevDT); if (it != d->mRDateTimes.constEnd()) { dates << *it; } QDateTime kdt(startDateTime()); for (const auto &date : qAsConst(d->mRDates)) { kdt.setDate(date); if (kdt < prevDT) { dates << kdt; break; } } // Add the previous occurrences from all RRULEs. for (const auto &rule : qAsConst(d->mRRules)) { QDateTime dt = rule->getPreviousDate(prevDT); if (dt.isValid()) { dates << dt; } } // Take the last of these (all others can't be used later on) sortAndRemoveDuplicates(dates); if (dates.isEmpty()) { return QDateTime(); } prevDT = dates.last(); // Check if that date/time is excluded explicitly or by an exrule: if (!std::binary_search(d->mExDates.constBegin(), d->mExDates.constEnd(), prevDT.date()) && !std::binary_search(d->mExDateTimes.constBegin(), d->mExDateTimes.constEnd(), prevDT)) { bool allowed = true; for (const auto &rule : qAsConst(d->mExRules)) { allowed = allowed && !rule->recursAt(prevDT); } if (allowed) { return prevDT; } } } // Couldn't find a valid occurrences in 1000 loops, something is wrong! return QDateTime(); } /***************************** PROTECTED FUNCTIONS ***************************/ RecurrenceRule::List Recurrence::rRules() const { return d->mRRules; } void Recurrence::addRRule(RecurrenceRule *rrule) { if (d->mRecurReadOnly || !rrule) { return; } rrule->setAllDay(d->mAllDay); d->mRRules.append(rrule); rrule->addObserver(this); updated(); } void Recurrence::removeRRule(RecurrenceRule *rrule) { if (d->mRecurReadOnly) { return; } d->mRRules.removeAll(rrule); rrule->removeObserver(this); updated(); } void Recurrence::deleteRRule(RecurrenceRule *rrule) { if (d->mRecurReadOnly) { return; } d->mRRules.removeAll(rrule); delete rrule; updated(); } RecurrenceRule::List Recurrence::exRules() const { return d->mExRules; } void Recurrence::addExRule(RecurrenceRule *exrule) { if (d->mRecurReadOnly || !exrule) { return; } exrule->setAllDay(d->mAllDay); d->mExRules.append(exrule); exrule->addObserver(this); updated(); } void Recurrence::removeExRule(RecurrenceRule *exrule) { if (d->mRecurReadOnly) { return; } d->mExRules.removeAll(exrule); exrule->removeObserver(this); updated(); } void Recurrence::deleteExRule(RecurrenceRule *exrule) { if (d->mRecurReadOnly) { return; } d->mExRules.removeAll(exrule); delete exrule; updated(); } QList Recurrence::rDateTimes() const { return d->mRDateTimes; } void Recurrence::setRDateTimes(const QList &rdates) { if (d->mRecurReadOnly) { return; } d->mRDateTimes = rdates; sortAndRemoveDuplicates(d->mRDateTimes); updated(); } void Recurrence::addRDateTime(const QDateTime &rdate) { if (d->mRecurReadOnly) { return; } setInsert(d->mRDateTimes, rdate); updated(); } DateList Recurrence::rDates() const { return d->mRDates; } void Recurrence::setRDates(const DateList &rdates) { if (d->mRecurReadOnly) { return; } d->mRDates = rdates; sortAndRemoveDuplicates(d->mRDates); updated(); } void Recurrence::addRDate(const QDate &rdate) { if (d->mRecurReadOnly) { return; } setInsert(d->mRDates, rdate); updated(); } QList Recurrence::exDateTimes() const { return d->mExDateTimes; } void Recurrence::setExDateTimes(const QList &exdates) { if (d->mRecurReadOnly) { return; } d->mExDateTimes = exdates; sortAndRemoveDuplicates(d->mExDateTimes); } void Recurrence::addExDateTime(const QDateTime &exdate) { if (d->mRecurReadOnly) { return; } setInsert(d->mExDateTimes, exdate); updated(); } DateList Recurrence::exDates() const { return d->mExDates; } void Recurrence::setExDates(const DateList &exdates) { if (d->mRecurReadOnly) { return; } DateList l = exdates; sortAndRemoveDuplicates(l); if (d->mExDates != l) { d->mExDates = l; updated(); } } void Recurrence::addExDate(const QDate &exdate) { if (d->mRecurReadOnly) { return; } setInsert(d->mExDates, exdate); updated(); } void Recurrence::recurrenceChanged(RecurrenceRule *) { updated(); } // %%%%%%%%%%%%%%%%%% end:Recurrencerule %%%%%%%%%%%%%%%%%% void Recurrence::dump() const { int i; int count = d->mRRules.count(); qCDebug(KCALCORE_LOG) << " -)" << count << "RRULEs:"; for (i = 0; i < count; ++i) { qCDebug(KCALCORE_LOG) << " -) RecurrenceRule: "; d->mRRules[i]->dump(); } count = d->mExRules.count(); qCDebug(KCALCORE_LOG) << " -)" << count << "EXRULEs:"; for (i = 0; i < count; ++i) { qCDebug(KCALCORE_LOG) << " -) ExceptionRule :"; d->mExRules[i]->dump(); } count = d->mRDates.count(); qCDebug(KCALCORE_LOG) << " -)" << count << "Recurrence Dates:"; for (i = 0; i < count; ++i) { qCDebug(KCALCORE_LOG) << " " << d->mRDates[i]; } count = d->mRDateTimes.count(); qCDebug(KCALCORE_LOG) << " -)" << count << "Recurrence Date/Times:"; for (i = 0; i < count; ++i) { qCDebug(KCALCORE_LOG) << " " << d->mRDateTimes[i]; } count = d->mExDates.count(); qCDebug(KCALCORE_LOG) << " -)" << count << "Exceptions Dates:"; for (i = 0; i < count; ++i) { qCDebug(KCALCORE_LOG) << " " << d->mExDates[i]; } count = d->mExDateTimes.count(); qCDebug(KCALCORE_LOG) << " -)" << count << "Exception Date/Times:"; for (i = 0; i < count; ++i) { qCDebug(KCALCORE_LOG) << " " << d->mExDateTimes[i]; } } Recurrence::RecurrenceObserver::~RecurrenceObserver() { } -KCALCORE_EXPORT QDataStream &KCalCore::operator<<(QDataStream &out, KCalCore::Recurrence *r) +KCALCORE_EXPORT QDataStream &KCalendarCore::operator<<(QDataStream &out, KCalendarCore::Recurrence *r) { if (!r) { return out; } serializeQDateTimeList(out, r->d->mRDateTimes); serializeQDateTimeList(out, r->d->mExDateTimes); out << r->d->mRDates; serializeQDateTimeAsKDateTime(out, r->d->mStartDateTime); out << r->d->mCachedType << r->d->mAllDay << r->d->mRecurReadOnly << r->d->mExDates << r->d->mExRules.count() << r->d->mRRules.count(); for (RecurrenceRule *rule : qAsConst(r->d->mExRules)) { out << rule; } for (RecurrenceRule *rule : qAsConst(r->d->mRRules)) { out << rule; } return out; } -KCALCORE_EXPORT QDataStream &KCalCore::operator>>(QDataStream &in, KCalCore::Recurrence *r) +KCALCORE_EXPORT QDataStream &KCalendarCore::operator>>(QDataStream &in, KCalendarCore::Recurrence *r) { if (!r) { return in; } int rruleCount, exruleCount; deserializeQDateTimeList(in, r->d->mRDateTimes); deserializeQDateTimeList(in, r->d->mExDateTimes); in >> r->d->mRDates; deserializeKDateTimeAsQDateTime(in, r->d->mStartDateTime); in >> r->d->mCachedType >> r->d->mAllDay >> r->d->mRecurReadOnly >> r->d->mExDates >> exruleCount >> rruleCount; r->d->mExRules.clear(); r->d->mRRules.clear(); for (int i = 0; i < exruleCount; ++i) { RecurrenceRule *rule = new RecurrenceRule(); rule->addObserver(r); in >> rule; r->d->mExRules.append(rule); } for (int i = 0; i < rruleCount; ++i) { RecurrenceRule *rule = new RecurrenceRule(); rule->addObserver(r); in >> rule; r->d->mRRules.append(rule); } return in; } diff --git a/src/recurrence.h b/src/recurrence.h index aba4debf5..1173dd0c1 100644 --- a/src/recurrence.h +++ b/src/recurrence.h @@ -1,678 +1,678 @@ /* This file is part of the kcalcore library. Copyright (c) 1998 Preston Brown Copyright (c) 2001,2003 Cornelius Schumacher Copyright (c) 2002,2006 David Jarvie Copyright (C) 2005 Reinhold Kainhofer This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KCALCORE_RECURRENCE_H #define KCALCORE_RECURRENCE_H #include "kcalcore_export.h" #include "recurrencerule.h" class QBitArray; class QTimeZone; -namespace KCalCore +namespace KCalendarCore { class RecurrenceRule; /** This class represents a recurrence rule for a calendar incidence. It manages all recurrence rules, recurrence date/times, exception rules and exception date times that can appear inside calendar items. Each recurrence rule and exception rule is represented as an object of type RecurrenceRule. For the simple case where at most one recurrence rule is present, this class provides shortcut methods to set the type: setMinutely() setHourly() setDaily() setWeekly() setMonthly() setYearly() to set/get general information about the recurrence: setEndDate() setEndDateTime() duration() durationTo() setDuration() frequency() setFrequency() and to set/get specific information about the recurrence within the interval: days() monthDays() monthPositions() yearDays() yearDates() yearMonths() yearPositions() addMonthlyPos() addMonthlyDate() addYearlyDay() addYearlyDate() addYearlyPos() addYearlyMonth() These are all available so that you don't have to work on the RecurrenceRule objects themselves. In other words, in that simple situation the interface stays almost the same compared to the old Recurrence class, which allowed only one recurrence rule. As soon as your recurrence consists of multiple recurrence rules or exception rules, you cannot use the methods mentioned above any more (since each rule will have a different type and different settings). If you still call any of them, the set*ly methods will remove all rules and add one rule with the specified type. The add* and the other set* methods will change only the first recurrence rule, but leave the others untouched. */ class KCALCORE_EXPORT Recurrence : public RecurrenceRule::RuleObserver { public: class RecurrenceObserver { public: virtual ~RecurrenceObserver(); /** This method will be called on each change of the recurrence object */ virtual void recurrenceUpdated(Recurrence *r) = 0; }; /** enumeration for describing how an event recurs, if at all. */ enum { rNone = 0, rMinutely = 0x001, rHourly = 0x0002, rDaily = 0x0003, rWeekly = 0x0004, rMonthlyPos = 0x0005, rMonthlyDay = 0x0006, rYearlyMonth = 0x0007, rYearlyDay = 0x0008, rYearlyPos = 0x0009, rOther = 0x000A, rMax = 0x00FF }; /** Constructs an empty recurrence. */ Recurrence(); /** Copy constructor. @param r instance to copy from */ Recurrence(const Recurrence &r); /** Destructor. */ ~Recurrence() override; /** Comparison operator for equality. @param r instance to compare with @return true if recurrences are the same, false otherwise */ bool operator==(const Recurrence &r) const; /** Comparison operator for inequality. @param r instance to compare with @return true if recurrences are the different, false if the same */ bool operator!=(const Recurrence &r) const { return !operator==(r); } /** Assignment operator. @param r the recurrence which will be assigned to this. */ Recurrence &operator=(const Recurrence &r); /** Return the start date/time of the recurrence (Time for all-day recurrences will be 0:00). @return the current start/time of the recurrence. */ Q_REQUIRED_RESULT QDateTime startDateTime() const; /** Return the start date/time of the recurrence */ Q_REQUIRED_RESULT QDate startDate() const; /** Set start of recurrence. If @p start is date-only, the recurrence is set to all-day. Otherwise, the start is set to a date and time, and the recurrence is set to non-all-day. @param start the new start date or date/time of the recurrence. */ void setStartDateTime(const QDateTime &start, bool isAllDay); /** Set whether the recurrence has no time, just a date. * All-day means -- according to rfc2445 -- that the event has no time * associated. * N.B. This property is derived by default from whether setStartDateTime() is * called with a date-only or date/time parameter. * @return whether the recurrence has a time (false) or it is just a date (true). */ Q_REQUIRED_RESULT bool allDay() const; /** Sets whether the dtstart is a all-day (i.e. has no time attached) @param allDay If the recurrence is for all-day item (true) or has a time associated (false). */ void setAllDay(bool allDay); /** Set if recurrence is read-only or can be changed. */ void setRecurReadOnly(bool readOnly); /** Returns true if the recurrence is read-only, or false if it can be changed. */ Q_REQUIRED_RESULT bool recurReadOnly() const; /** Returns whether the event recurs at all. */ Q_REQUIRED_RESULT bool recurs() const; /** Returns the event's recurrence status. See the enumeration at the top * of this file for possible values. */ Q_REQUIRED_RESULT ushort recurrenceType() const; /** Returns the recurrence status for a recurrence rule. * See the enumeration at the top of this file for possible values. * * @param rrule the recurrence rule to get the type for */ static ushort recurrenceType(const RecurrenceRule *rrule); /** Returns true if the date specified is one on which the event will recur. @param date date to check. @param timeZone time zone for the @p date. */ bool recursOn(const QDate &date, const QTimeZone &timeZone) const; /** Returns true if the date/time specified is one at which the event will recur. Times are rounded down to the nearest minute to determine the result. @param dt is the date/time to check. */ bool recursAt(const QDateTime &dt) const; /** Removes all recurrence rules. Recurrence dates and exceptions are not removed. */ void unsetRecurs(); /** Removes all recurrence and exception rules and dates. */ void clear(); /** Returns a list of the times on the specified date at which the * recurrence will occur. The returned times should be interpreted in the * context of @p timeSpec. * @param date the date for which to find the recurrence times * @param timeZone timezone for @p date */ Q_REQUIRED_RESULT TimeList recurTimesOn(const QDate &date, const QTimeZone &timeZone) const; /** Returns a list of all the times at which the recurrence will occur * between two specified times. * * There is a (large) maximum limit to the number of times returned. If due to * this limit the list is incomplete, this is indicated by the last entry being * set to an invalid QDateTime value. If you need further values, call the * method again with a start time set to just after the last valid time returned. * * @param start inclusive start of interval * @param end inclusive end of interval * @return list of date/time values */ Q_REQUIRED_RESULT QList timesInInterval(const QDateTime &start, const QDateTime &end) const; /** Returns the date and time of the next recurrence, after the specified date/time. * If the recurrence has no time, the next date after the specified date is returned. * @param preDateTime the date/time after which to find the recurrence. * @return date/time of next recurrence (strictly later than the given * QDateTime), or invalid date if none. */ Q_REQUIRED_RESULT QDateTime getNextDateTime(const QDateTime &preDateTime) const; /** Returns the date and time of the last previous recurrence, before the specified date/time. * If a time later than 00:00:00 is specified and the recurrence has no time, 00:00:00 on * the specified date is returned if that date recurs. * * @param afterDateTime the date/time before which to find the recurrence. * @return date/time of previous recurrence (strictly earlier than the given * QDateTime), or invalid date if none. */ Q_REQUIRED_RESULT QDateTime getPreviousDateTime(const QDateTime &afterDateTime) const; /** Returns frequency of recurrence, in terms of the recurrence time period type. */ Q_REQUIRED_RESULT int frequency() const; /** Sets the frequency of recurrence, in terms of the recurrence time period type. */ void setFrequency(int freq); /** * Returns -1 if the event recurs infinitely, 0 if the end date is set, * otherwise the total number of recurrences, including the initial occurrence. */ Q_REQUIRED_RESULT int duration() const; /** Sets the total number of times the event is to occur, including both the * first and last. */ void setDuration(int duration); /** Returns the number of recurrences up to and including the date/time specified. * @warning This function can be very time consuming - use it sparingly! */ Q_REQUIRED_RESULT int durationTo(const QDateTime &dt) const; /** Returns the number of recurrences up to and including the date specified. * @warning This function can be very time consuming - use it sparingly! */ Q_REQUIRED_RESULT int durationTo(const QDate &date) const; /** Returns the date/time of the last recurrence. * An invalid date is returned if the recurrence has no end. */ Q_REQUIRED_RESULT QDateTime endDateTime() const; /** Returns the date of the last recurrence. * An invalid date is returned if the recurrence has no end. */ Q_REQUIRED_RESULT QDate endDate() const; /** Sets the date of the last recurrence. The end time is set to the recurrence start time. * @param endDate the ending date after which to stop recurring. If the * recurrence is not all-day, the end time will be 23:59.*/ void setEndDate(const QDate &endDate); /** Sets the date and time of the last recurrence. * @param endDateTime the ending date/time after which to stop recurring. */ void setEndDateTime(const QDateTime &endDateTime); /** Shift the times of the recurrence so that they appear at the same clock time as before but in a new time zone. The shift is done from a viewing time zone rather than from the actual recurrence time zone. For example, shifting a recurrence whose start time is 09:00 America/New York, using an old viewing time zone (@p oldSpec) of Europe/London, to a new time zone (@p newSpec) of Europe/Paris, will result in the time being shifted from 14:00 (which is the London time of the recurrence start) to 14:00 Paris time. @param oldZone the time specification which provides the clock times @param newZone the new time specification */ void shiftTimes(const QTimeZone &oldZone, const QTimeZone &newZone); /** Sets an event to recur minutely. By default infinite recurrence is used. To set an end date use the method setEndDate and to set the number of occurrences use setDuration. This method clears all recurrence rules and adds one rule with a minutely recurrence. All other recurrence components (recurrence date/times, exception date/times and exception rules) are not modified. * @param freq the frequency to recur, e.g. 2 is every other minute */ void setMinutely(int freq); /** Sets an event to recur hourly. By default infinite recurrence is used. The minute of the recurrence is taken from the start date (if you need to change it, you will have to modify the defaultRRule's byMinute list manually. To set an end date use the method setEndDate and to set the number of occurrences use setDuration. This method clears all recurrence rules and adds one rule with a hourly recurrence. All other recurrence components (recurrence date/times, exception date/times and exception rules) are not modified. * @param freq the frequency to recur, e.g. 2 is every other hour */ void setHourly(int freq); /** Sets an event to recur daily. By default infinite recurrence is used. The minute and second of the recurrence is taken from the start date (if you need to change them, you will have to modify the defaultRRule's byMinute list manually. To set an end date use the method setEndDate and to set the number of occurrences use setDuration. This method clears all recurrence rules and adds one rule with a daily recurrence. All other recurrence components (recurrence date/times, exception date/times and exception rules) are not modified. * @param freq the frequency to recur, e.g. 2 is every other day */ void setDaily(int freq); /** Sets an event to recur weekly. By default infinite recurrence is used. To set an end date use the method setEndDate and to set the number of occurrences use setDuration. This method clears all recurrence rules and adds one rule with a weekly recurrence. All other recurrence components (recurrence date/times, exception date/times and exception rules) are not modified. * @param freq the frequency to recur, e.g. every other week etc. * @param weekStart the first day of the week (Monday=1 .. Sunday=7, default is Monday). */ void setWeekly(int freq, int weekStart = 1); /** Sets an event to recur weekly. By default infinite recurrence is used. To set an end date use the method setEndDate and to set the number of occurrences use setDuration. This method clears all recurrence rules and adds one rule with a weekly recurrence. All other recurrence components (recurrence date/times, exception date/times and exception rules) are not modified. * @param freq the frequency to recur, e.g. every other week etc. * @param days a 7 bit array indicating which days on which to recur (bit 0 = Monday). * @param weekStart the first day of the week (Monday=1 .. Sunday=7, default is Monday). */ void setWeekly(int freq, const QBitArray &days, int weekStart = 1); /** Adds days to the weekly day recurrence list. * @param days a 7 bit array indicating which days on which to recur (bit 0 = Monday). */ void addWeeklyDays(const QBitArray &days); /** Returns the first day of the week. Uses only the * first RRULE if present (i.e. a second RRULE as well as all EXRULES are * ignored! * @return Weekday of the first day of the week (Monday=1 .. Sunday=7) */ int weekStart() const; /** Returns week day mask (bit 0 = Monday). */ Q_REQUIRED_RESULT QBitArray days() const; // Emulate the old behavior /** Sets an event to recur monthly. By default infinite recurrence is used. The date of the monthly recurrence will be taken from the start date unless you explicitly add one or more recurrence dates with addMonthlyDate or a recurrence position in the month (e.g. first monday) using addMonthlyPos. To set an end date use the method setEndDate and to set the number of occurrences use setDuration. This method clears all recurrence rules and adds one rule with a monthly recurrence. All other recurrence components (recurrence date/times, exception date/times and exception rules) are not modified. * @param freq the frequency to recur, e.g. 3 for every third month. */ void setMonthly(int freq); /** Adds a position (e.g. first monday) to the monthly recurrence rule. * @param pos the position in the month for the recurrence, with valid * values being 1-5 (5 weeks max in a month). * @param days the days for the position to recur on (bit 0 = Monday). * Example: pos = 2, and bits 0 and 2 are set in days: * the rule is to repeat every 2nd Monday and Wednesday in the month. */ void addMonthlyPos(short pos, const QBitArray &days); void addMonthlyPos(short pos, ushort day); void setMonthlyPos(const QList &monthlyDays); /** Adds a date (e.g. the 15th of each month) to the monthly day * recurrence list. * @param day the date in the month to recur. */ void addMonthlyDate(short day); void setMonthlyDate(const QList &monthlyDays); /** Returns list of day positions in months. */ Q_REQUIRED_RESULT QList monthPositions() const; /** Returns list of day numbers of a month. */ // Emulate old behavior Q_REQUIRED_RESULT QList monthDays() const; /** Sets an event to recur yearly. By default, this will recur every year * on the same date (e.g. every year on April 15 if the start date was * April 15). * The day of the year can be specified with addYearlyDay(). * The day of the month can be specified with addYearlyByDate * If both a month and a day ar specified with addYearlyMonth and * addYearlyDay, the day is understood as day number within the month. * * A position (e.g. 3rd Sunday of year/month, or last Friday of year/month) * can be specified with addYearlyPos. Again, if a month is specified, * this position is understood as within that month, otherwise within * the year. * * By default infinite recurrence is used. To set an end date use the * method setEndDate and to set the number of occurrences use setDuration. This method clears all recurrence rules and adds one rule with a yearly recurrence. All other recurrence components (recurrence date/times, exception date/times and exception rules) are not modified. * @param freq the frequency to recur, e.g. 3 for every third year. */ void setYearly(int freq); /** Adds day number of year within a yearly recurrence. * By default infinite recurrence is used. To set an end date use the * method setEndDate and to set the number of occurrences use setDuration. * @param day the day of the year for the event. E.g. if day is 60, this * means Feb 29 in leap years and March 1 in non-leap years. */ void addYearlyDay(int day); void setYearlyDay(const QList &days); /** Adds date within a yearly recurrence. The month(s) for the recurrence * can be specified with addYearlyMonth(), otherwise the month of the * start date is used. * * By default infinite recurrence is used. To set an end date use the * method setEndDate and to set the number of occurrences use setDuration. * @param date the day of the month for the event */ void addYearlyDate(int date); void setYearlyDate(const QList &dates); /** Adds month in yearly recurrence. You can specify specific day numbers * within the months (by calling addYearlyDate()) or specific day positions * within the month (by calling addYearlyPos). * @param _rNum the month in which the event shall recur. */ void addYearlyMonth(short _rNum); void setYearlyMonth(const QList< int > &months); /** Adds position within month/year within a yearly recurrence. If months * are specified (via addYearlyMonth()), the parameters are understood as * position within these months, otherwise within the year. * * By default infinite recurrence is used. * To set an end date use the method setEndDate and to set the number * of occurrences use setDuration. * @param pos the position in the month/year for the recurrence, with valid * values being 1 to 53 and -1 to -53 (53 weeks max in a year). * @param days the days for the position to recur on (bit 0 = Monday). * Example: pos = 2, and bits 0 and 2 are set in days * If months are specified (via addYearlyMonth), e.g. March, the rule is * to repeat every year on the 2nd Monday and Wednesday of March. * If no months are specified, the fule is to repeat every year on the * 2nd Monday and Wednesday of the year. */ void addYearlyPos(short pos, const QBitArray &days); void setYearlyPos(const QList &days); /** Returns the day numbers within a yearly recurrence. * @return the days of the year for the event. E.g. if the list contains * 60, this means the recurrence happens on day 60 of the year, i.e. * on Feb 29 in leap years and March 1 in non-leap years. */ Q_REQUIRED_RESULT QList yearDays() const; /** Returns the dates within a yearly recurrence. * @return the days of the month for the event. E.g. if the list contains * 13, this means the recurrence happens on the 13th of the month. * The months for the recurrence can be obtained through * yearlyMonths(). If this list is empty, the month of the start * date is used. */ Q_REQUIRED_RESULT QList yearDates() const; /** Returns the months within a yearly recurrence. * @return the months for the event. E.g. if the list contains * 11, this means the recurrence happens in November. * The days for the recurrence can be obtained either through * yearDates() if they are given as dates within the month or * through yearlyPositions() if they are given as positions within the * month. If none is specified, the date of the start date is used. */ Q_REQUIRED_RESULT QList yearMonths() const; /** Returns the positions within a yearly recurrence. * @return the positions for the event, either within a month (if months * are set through addYearlyMonth()) or within the year. * E.g. if the list contains {Pos=3, Day=5}, this means the third * friday. If a month is set this position is understoodas third * Friday in the given months, otherwise as third Friday of the * year. */ /** Returns list of day positions in months, for a recursYearlyPos recurrence rule. */ Q_REQUIRED_RESULT QList yearPositions() const; /** Upper date limit for recurrences */ static const QDate MAX_DATE; /** Debug output. */ void dump() const; // RRULE Q_REQUIRED_RESULT RecurrenceRule::List rRules() const; /** Add a recurrence rule to the recurrence. @param rrule the recurrence rule to add */ void addRRule(RecurrenceRule *rrule); /** Remove a recurrence rule from the recurrence. @p rrule is not deleted; it is the responsibility of the caller to ensure that it is deleted. @param rrule the recurrence rule to remove */ void removeRRule(RecurrenceRule *rrule); /** Remove a recurrence rule from the recurrence and delete it. @param rrule the recurrence rule to remove */ void deleteRRule(RecurrenceRule *rrule); // EXRULE Q_REQUIRED_RESULT RecurrenceRule::List exRules() const; /** Add an exception rule to the recurrence. @param exrule the exception rule to add */ void addExRule(RecurrenceRule *exrule); /** Remove an exception rule from the recurrence. @p exrule is not deleted; it is the responsibility of the caller to ensure that it is deleted. @param exrule the exception rule to remove */ void removeExRule(RecurrenceRule *exrule); /** Remove an exception rule from the recurrence and delete it. @param exrule the exception rule to remove */ void deleteExRule(RecurrenceRule *exrule); // RDATE Q_REQUIRED_RESULT QList rDateTimes() const; Q_REQUIRED_RESULT DateList rDates() const; void setRDateTimes(const QList &rdates); void setRDates(const DateList &rdates); void addRDateTime(const QDateTime &rdate); void addRDate(const QDate &rdate); // ExDATE Q_REQUIRED_RESULT QList exDateTimes() const; Q_REQUIRED_RESULT DateList exDates() const; void setExDateTimes(const QList &exdates); void setExDates(const DateList &exdates); void addExDateTime(const QDateTime &exdate); void addExDate(const QDate &exdate); RecurrenceRule *defaultRRule(bool create = false) const; RecurrenceRule *defaultRRuleConst() const; void updated(); /** Installs an observer. Whenever some setting of this recurrence object is changed, the recurrenceUpdated( Recurrence* ) method of each observer will be called to inform it of changes. @param observer the Recurrence::Observer-derived object, which will be installed as an observer of this object. */ void addObserver(RecurrenceObserver *observer); /** Removes an observer that was added with addObserver. If the given object was not an observer, it does nothing. @param observer the Recurrence::Observer-derived object to be removed from the list of observers of this object. */ void removeObserver(RecurrenceObserver *observer); void recurrenceChanged(RecurrenceRule *) override; protected: RecurrenceRule *setNewRecurrenceType(RecurrenceRule::PeriodType type, int freq); private: //@cond PRIVATE class Private; Private *const d; //@endcond - friend KCALCORE_EXPORT QDataStream &operator<<(QDataStream &out, KCalCore::Recurrence *); - friend KCALCORE_EXPORT QDataStream &operator>>(QDataStream &in, KCalCore::Recurrence *); + friend KCALCORE_EXPORT QDataStream &operator<<(QDataStream &out, KCalendarCore::Recurrence *); + friend KCALCORE_EXPORT QDataStream &operator>>(QDataStream &in, KCalendarCore::Recurrence *); }; /** * Recurrence serializer and deserializer. * @since 4.12 */ -KCALCORE_EXPORT QDataStream &operator<<(QDataStream &out, KCalCore::Recurrence *); -KCALCORE_EXPORT QDataStream &operator>>(QDataStream &in, KCalCore::Recurrence *); +KCALCORE_EXPORT QDataStream &operator<<(QDataStream &out, KCalendarCore::Recurrence *); +KCALCORE_EXPORT QDataStream &operator>>(QDataStream &in, KCalendarCore::Recurrence *); } #endif diff --git a/src/recurrencehelper_p.h b/src/recurrencehelper_p.h index d381d5534..74bd2a551 100644 --- a/src/recurrencehelper_p.h +++ b/src/recurrencehelper_p.h @@ -1,69 +1,69 @@ /* This file is part of the kcalcore library. Copyright (c) 2019 Volker Krause This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KCALCORE_RECURRENCEHELPER_P_H #define KCALCORE_RECURRENCEHELPER_P_H #include -namespace KCalCore { +namespace KCalendarCore { template inline void sortAndRemoveDuplicates(T &container) { std::sort(container.begin(), container.end()); container.erase(std::unique(container.begin(), container.end()), container.end()); } template inline void inplaceSetDifference(T &set1, const T &set2) { auto beginIt = set1.begin(); for (const auto &elem : set2) { const auto it = std::lower_bound(beginIt, set1.end(), elem); if (it != set1.end() && *it == elem) { beginIt = set1.erase(it); } } } template inline void setInsert(Container &c, const Value &v) { const auto it = std::lower_bound(c.begin(), c.end(), v); if (it == c.end() || *it != v) { c.insert(it, v); } } template inline It strictLowerBound(It begin, It end, const Value &v) { const auto it = std::lower_bound(begin, end, v); if (it == end || (*it) >= v) { return it == begin ? end : (it - 1); } return it; } } #endif diff --git a/src/recurrencerule.cpp b/src/recurrencerule.cpp index 8d0f61c06..a3300f1df 100644 --- a/src/recurrencerule.cpp +++ b/src/recurrencerule.cpp @@ -1,2331 +1,2331 @@ /* This file is part of the kcalcore library. Copyright (c) 2005 Reinhold Kainhofer Copyright (c) 2006-2008 David Jarvie This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "recurrencerule.h" #include "utils.h" #include "kcalcore_debug.h" #include "recurrencehelper_p.h" #include #include #include #include #include -using namespace KCalCore; +using namespace KCalendarCore; // Maximum number of intervals to process const int LOOP_LIMIT = 10000; #ifndef NDEBUG static QString dumpTime(const QDateTime &dt, bool allDay); // for debugging #endif /*========================================================================= = = = IMPORTANT CODING NOTE: = = = = Recurrence handling code is time critical, especially for sub-daily = = recurrences. For example, if getNextDate() is called repeatedly to = = check all consecutive occurrences over a few years, on a slow machine = = this could take many seconds to complete in the worst case. Simple = = sub-daily recurrences are optimised by use of mTimedRepetition. = = = ==========================================================================*/ /************************************************************************** * DateHelper * **************************************************************************/ //@cond PRIVATE class DateHelper { public: #ifndef NDEBUG static QString dayName(short day); #endif static QDate getNthWeek(int year, int weeknumber, short weekstart = 1); static int weekNumbersInYear(int year, short weekstart = 1); static int getWeekNumber(const QDate &date, short weekstart, int *year = nullptr); static int getWeekNumberNeg(const QDate &date, short weekstart, int *year = nullptr); // Convert to QDate, allowing for day < 0. // month and day must be non-zero. static QDate getDate(int year, int month, int day) { if (day >= 0) { return QDate(year, month, day); } else { if (++month > 12) { month = 1; ++year; } return QDate(year, month, 1).addDays(day); } } }; #ifndef NDEBUG // TODO: Move to a general library / class, as we need the same in the iCal // generator and in the xcal format QString DateHelper::dayName(short day) { switch (day) { case 1: return QStringLiteral("MO"); case 2: return QStringLiteral("TU"); case 3: return QStringLiteral("WE"); case 4: return QStringLiteral("TH"); case 5: return QStringLiteral("FR"); case 6: return QStringLiteral("SA"); case 7: return QStringLiteral("SU"); default: return QStringLiteral("??"); } } #endif QDate DateHelper::getNthWeek(int year, int weeknumber, short weekstart) { if (weeknumber == 0) { return QDate(); } // Adjust this to the first day of week #1 of the year and add 7*weekno days. QDate dt(year, 1, 4); // Week #1 is the week that contains Jan 4 int adjust = -(7 + dt.dayOfWeek() - weekstart) % 7; if (weeknumber > 0) { dt = dt.addDays(7 * (weeknumber - 1) + adjust); } else if (weeknumber < 0) { dt = dt.addYears(1); dt = dt.addDays(7 * weeknumber + adjust); } return dt; } int DateHelper::getWeekNumber(const QDate &date, short weekstart, int *year) { int y = date.year(); QDate dt(y, 1, 4); // <= definitely in week #1 dt = dt.addDays(-(7 + dt.dayOfWeek() - weekstart) % 7); // begin of week #1 qint64 daysto = dt.daysTo(date); if (daysto < 0) { // in first week of year --y; dt = QDate(y, 1, 4); dt = dt.addDays(-(7 + dt.dayOfWeek() - weekstart) % 7); // begin of week #1 daysto = dt.daysTo(date); } else if (daysto > 355) { // near the end of the year - check if it's next year QDate dtn(y + 1, 1, 4); // <= definitely first week of next year dtn = dtn.addDays(-(7 + dtn.dayOfWeek() - weekstart) % 7); qint64 dayston = dtn.daysTo(date); if (dayston >= 0) { // in first week of next year; ++y; daysto = dayston; } } if (year) { *year = y; } return daysto / 7 + 1; } int DateHelper::weekNumbersInYear(int year, short weekstart) { QDate dt(year, 1, weekstart); QDate dt1(year + 1, 1, weekstart); return dt.daysTo(dt1) / 7; } // Week number from the end of the year int DateHelper::getWeekNumberNeg(const QDate &date, short weekstart, int *year) { int weekpos = getWeekNumber(date, weekstart, year); return weekNumbersInYear(*year, weekstart) - weekpos - 1; } //@endcond /************************************************************************** * WDayPos * **************************************************************************/ bool RecurrenceRule::WDayPos::operator==(const RecurrenceRule::WDayPos &pos2) const { return mDay == pos2.mDay && mPos == pos2.mPos; } bool RecurrenceRule::WDayPos::operator!=(const RecurrenceRule::WDayPos &pos2) const { return !operator==(pos2); } /************************************************************************** * Constraint * **************************************************************************/ //@cond PRIVATE class Constraint { public: typedef QVector List; Constraint() {} explicit Constraint(const QTimeZone &, int wkst = 1); Constraint(const QDateTime &dt, RecurrenceRule::PeriodType type, int wkst); void clear(); void setYear(int n) { year = n; useCachedDt = false; } void setMonth(int n) { month = n; useCachedDt = false; } void setDay(int n) { day = n; useCachedDt = false; } void setHour(int n) { hour = n; useCachedDt = false; } void setMinute(int n) { minute = n; useCachedDt = false; } void setSecond(int n) { second = n; useCachedDt = false; } void setWeekday(int n) { weekday = n; useCachedDt = false; } void setWeekdaynr(int n) { weekdaynr = n; useCachedDt = false; } void setWeeknumber(int n) { weeknumber = n; useCachedDt = false; } void setYearday(int n) { yearday = n; useCachedDt = false; } void setWeekstart(int n) { weekstart = n; useCachedDt = false; } int year; // 0 means unspecified int month; // 0 means unspecified int day; // 0 means unspecified int hour; // -1 means unspecified int minute; // -1 means unspecified int second; // -1 means unspecified int weekday; // 0 means unspecified int weekdaynr; // index of weekday in month/year (0=unspecified) int weeknumber; // 0 means unspecified int yearday; // 0 means unspecified int weekstart; // first day of week (1=monday, 7=sunday, 0=unspec.) QTimeZone timeZone; // time zone etc. to use bool readDateTime(const QDateTime &dt, RecurrenceRule::PeriodType type); bool matches(const QDate &dt, RecurrenceRule::PeriodType type) const; bool matches(const QDateTime &dt, RecurrenceRule::PeriodType type) const; bool merge(const Constraint &interval); bool isConsistent(RecurrenceRule::PeriodType period) const; bool increase(RecurrenceRule::PeriodType type, int freq); QDateTime intervalDateTime(RecurrenceRule::PeriodType type) const; QList dateTimes(RecurrenceRule::PeriodType type) const; void appendDateTime(const QDate &date, const QTime &time, QList &list) const; void dump() const; private: mutable bool useCachedDt; mutable QDateTime cachedDt; }; Constraint::Constraint(const QTimeZone &timeZone, int wkst) : weekstart(wkst), timeZone(timeZone) { clear(); } Constraint::Constraint(const QDateTime &dt, RecurrenceRule::PeriodType type, int wkst) : weekstart(wkst), timeZone(dt.timeZone()) { clear(); readDateTime(dt, type); } void Constraint::clear() { year = 0; month = 0; day = 0; hour = -1; minute = -1; second = -1; weekday = 0; weekdaynr = 0; weeknumber = 0; yearday = 0; useCachedDt = false; } bool Constraint::matches(const QDate &dt, RecurrenceRule::PeriodType type) const { // If the event recurs in week 53 or 1, the day might not belong to the same // year as the week it is in. E.g. Jan 1, 2005 is in week 53 of year 2004. // So we can't simply check the year in that case! if (weeknumber == 0) { if (year > 0 && year != dt.year()) { return false; } } else { int y = 0; if (weeknumber > 0 && weeknumber != DateHelper::getWeekNumber(dt, weekstart, &y)) { return false; } if (weeknumber < 0 && weeknumber != DateHelper::getWeekNumberNeg(dt, weekstart, &y)) { return false; } if (year > 0 && year != y) { return false; } } if (month > 0 && month != dt.month()) { return false; } if (day > 0 && day != dt.day()) { return false; } if (day < 0 && dt.day() != (dt.daysInMonth() + day + 1)) { return false; } if (weekday > 0) { if (weekday != dt.dayOfWeek()) { return false; } if (weekdaynr != 0) { // If it's a yearly recurrence and a month is given, the position is // still in the month, not in the year. if ((type == RecurrenceRule::rMonthly) || (type == RecurrenceRule::rYearly && month > 0)) { // Monthly if (weekdaynr > 0 && weekdaynr != (dt.day() - 1) / 7 + 1) { return false; } if (weekdaynr < 0 && weekdaynr != -((dt.daysInMonth() - dt.day()) / 7 + 1)) { return false; } } else { // Yearly if (weekdaynr > 0 && weekdaynr != (dt.dayOfYear() - 1) / 7 + 1) { return false; } if (weekdaynr < 0 && weekdaynr != -((dt.daysInYear() - dt.dayOfYear()) / 7 + 1)) { return false; } } } } if (yearday > 0 && yearday != dt.dayOfYear()) { return false; } if (yearday < 0 && yearday != dt.daysInYear() - dt.dayOfYear() + 1) { return false; } return true; } /* Check for a match with the specified date/time. * The date/time's time specification must correspond with that of the start date/time. */ bool Constraint::matches(const QDateTime &dt, RecurrenceRule::PeriodType type) const { if ((hour >= 0 && hour != dt.time().hour()) || (minute >= 0 && minute != dt.time().minute()) || (second >= 0 && second != dt.time().second()) || !matches(dt.date(), type)) { return false; } return true; } bool Constraint::isConsistent(RecurrenceRule::PeriodType /*period*/) const { // TODO: Check for consistency, e.g. byyearday=3 and bymonth=10 return true; } // Return a date/time set to the constraint values, but with those parts less // significant than the given period type set to 1 (for dates) or 0 (for times). QDateTime Constraint::intervalDateTime(RecurrenceRule::PeriodType type) const { if (useCachedDt) { return cachedDt; } QDate d; QTime t(0, 0, 0); bool subdaily = true; switch (type) { case RecurrenceRule::rSecondly: t.setHMS(hour, minute, second); break; case RecurrenceRule::rMinutely: t.setHMS(hour, minute, 0); break; case RecurrenceRule::rHourly: t.setHMS(hour, 0, 0); break; case RecurrenceRule::rDaily: break; case RecurrenceRule::rWeekly: d = DateHelper::getNthWeek(year, weeknumber, weekstart); subdaily = false; break; case RecurrenceRule::rMonthly: d.setDate(year, month, 1); subdaily = false; break; case RecurrenceRule::rYearly: d.setDate(year, 1, 1); subdaily = false; break; default: break; } if (subdaily) { d = DateHelper::getDate(year, (month > 0) ? month : 1, day ? day : 1); } cachedDt = QDateTime(d, t, timeZone); useCachedDt = true; return cachedDt; } bool Constraint::merge(const Constraint &interval) { #define mergeConstraint( name, cmparison ) \ if ( interval.name cmparison ) { \ if ( !( name cmparison ) ) { \ name = interval.name; \ } else if ( name != interval.name ) { \ return false;\ } \ } useCachedDt = false; mergeConstraint(year, > 0); mergeConstraint(month, > 0); mergeConstraint(day, != 0); mergeConstraint(hour, >= 0); mergeConstraint(minute, >= 0); mergeConstraint(second, >= 0); mergeConstraint(weekday, != 0); mergeConstraint(weekdaynr, != 0); mergeConstraint(weeknumber, != 0); mergeConstraint(yearday, != 0); #undef mergeConstraint return true; } // Y M D | H Mn S | WD #WD | WN | YD // required: // x | x x x | | | // 0) Trivial: Exact date given, maybe other restrictions // x x x | x x x | | | // 1) Easy case: no weekly restrictions -> at most a loop through possible dates // x + + | x x x | - - | - | - // 2) Year day is given -> date known // x | x x x | | | + // 3) week number is given -> loop through all days of that week. Further // restrictions will be applied in the end, when we check all dates for // consistency with the constraints // x | x x x | | + | (-) // 4) week day is specified -> // x | x x x | x ? | (-)| (-) // 5) All possiblecases have already been treated, so this must be an error! QList Constraint::dateTimes(RecurrenceRule::PeriodType type) const { QList result; if (!isConsistent(type)) { return result; } // TODO_Recurrence: Handle all-day QTime tm(hour, minute, second); bool done = false; if (day && month > 0) { appendDateTime(DateHelper::getDate(year, month, day), tm, result); done = true; } if (!done && weekday == 0 && weeknumber == 0 && yearday == 0) { // Easy case: date is given, not restrictions by week or yearday uint mstart = (month > 0) ? month : 1; uint mend = (month <= 0) ? 12 : month; for (uint m = mstart; m <= mend; ++m) { uint dstart, dend; if (day > 0) { dstart = dend = day; } else if (day < 0) { QDate date(year, month, 1); dstart = dend = date.daysInMonth() + day + 1; } else { QDate date(year, month, 1); dstart = 1; dend = date.daysInMonth(); } uint d = dstart; for (QDate dt(year, m, dstart);; dt = dt.addDays(1)) { appendDateTime(dt, tm, result); if (++d > dend) { break; } } } done = true; } // Else: At least one of the week / yearday restrictions was given... // If we have a yearday (and of course a year), we know the exact date if (!done && yearday != 0) { // yearday < 0 means from end of year, so we'll need Jan 1 of the next year QDate d(year + ((yearday > 0) ? 0 : 1), 1, 1); d = d.addDays(yearday - ((yearday > 0) ? 1 : 0)); appendDateTime(d, tm, result); done = true; } // Else: If we have a weeknumber, we have at most 7 possible dates, loop through them if (!done && weeknumber != 0) { QDate wst(DateHelper::getNthWeek(year, weeknumber, weekstart)); if (weekday != 0) { wst = wst.addDays((7 + weekday - weekstart) % 7); appendDateTime(wst, tm, result); } else { for (int i = 0; i < 7; ++i) { appendDateTime(wst, tm, result); wst = wst.addDays(1); } } done = true; } // weekday is given if (!done && weekday != 0) { QDate dt(year, 1, 1); // If type == yearly and month is given, pos is still in month not year! // TODO_Recurrence: Correct handling of n-th BYDAY... int maxloop = 53; bool inMonth = (type == RecurrenceRule::rMonthly) || (type == RecurrenceRule::rYearly && month > 0); if (inMonth && month > 0) { dt = QDate(year, month, 1); maxloop = 5; } if (weekdaynr < 0) { // From end of period (month, year) => relative to begin of next period if (inMonth) { dt = dt.addMonths(1); } else { dt = dt.addYears(1); } } int adj = (7 + weekday - dt.dayOfWeek()) % 7; dt = dt.addDays(adj); // correct first weekday of the period if (weekdaynr > 0) { dt = dt.addDays((weekdaynr - 1) * 7); appendDateTime(dt, tm, result); } else if (weekdaynr < 0) { dt = dt.addDays(weekdaynr * 7); appendDateTime(dt, tm, result); } else { // loop through all possible weeks, non-matching will be filtered later for (int i = 0; i < maxloop; ++i) { appendDateTime(dt, tm, result); dt = dt.addDays(7); } } } // weekday != 0 // Only use those times that really match all other constraints, too QList valid; for (int i = 0, iend = result.count(); i < iend; ++i) { if (matches(result[i], type)) { valid.append(result[i]); } } // Don't sort it here, would be unnecessary work. The results from all // constraints will be merged to one big list of the interval. Sort that one! return valid; } void Constraint::appendDateTime(const QDate &date, const QTime &time, QList &list) const { QDateTime dt(date, time, timeZone); if (dt.isValid()) { list.append(dt); } } bool Constraint::increase(RecurrenceRule::PeriodType type, int freq) { // convert the first day of the interval to QDateTime intervalDateTime(type); // Now add the intervals switch (type) { case RecurrenceRule::rSecondly: cachedDt = cachedDt.addSecs(freq); break; case RecurrenceRule::rMinutely: cachedDt = cachedDt.addSecs(60 * freq); break; case RecurrenceRule::rHourly: cachedDt = cachedDt.addSecs(3600 * freq); break; case RecurrenceRule::rDaily: cachedDt = cachedDt.addDays(freq); break; case RecurrenceRule::rWeekly: cachedDt = cachedDt.addDays(7 * freq); break; case RecurrenceRule::rMonthly: cachedDt = cachedDt.addMonths(freq); break; case RecurrenceRule::rYearly: cachedDt = cachedDt.addYears(freq); break; default: break; } // Convert back from QDateTime to the Constraint class readDateTime(cachedDt, type); useCachedDt = true; // readDateTime() resets this return true; } // Set the constraint's value appropriate to 'type', to the value contained in a date/time. bool Constraint::readDateTime(const QDateTime &dt, RecurrenceRule::PeriodType type) { switch (type) { // Really fall through! Only weekly needs to be treated differently! case RecurrenceRule::rSecondly: second = dt.time().second(); Q_FALLTHROUGH(); case RecurrenceRule::rMinutely: minute = dt.time().minute(); Q_FALLTHROUGH(); case RecurrenceRule::rHourly: hour = dt.time().hour(); Q_FALLTHROUGH(); case RecurrenceRule::rDaily: day = dt.date().day(); Q_FALLTHROUGH(); case RecurrenceRule::rMonthly: month = dt.date().month(); Q_FALLTHROUGH(); case RecurrenceRule::rYearly: year = dt.date().year(); break; case RecurrenceRule::rWeekly: // Determine start day of the current week, calculate the week number from that weeknumber = DateHelper::getWeekNumber(dt.date(), weekstart, &year); break; default: break; } useCachedDt = false; return true; } //@endcond /************************************************************************** * RecurrenceRule::Private * **************************************************************************/ //@cond PRIVATE -class Q_DECL_HIDDEN KCalCore::RecurrenceRule::Private +class Q_DECL_HIDDEN KCalendarCore::RecurrenceRule::Private { public: Private(RecurrenceRule *parent) : mParent(parent), mPeriod(rNone), mFrequency(0), mDuration(-1), mWeekStart(1), mIsReadOnly(false), mAllDay(false) { setDirty(); } Private(RecurrenceRule *parent, const Private &p); Private &operator=(const Private &other); bool operator==(const Private &other) const; void clear(); void setDirty(); void buildConstraints(); bool buildCache() const; Constraint getNextValidDateInterval(const QDateTime &preDate, PeriodType type) const; Constraint getPreviousValidDateInterval(const QDateTime &afterDate, PeriodType type) const; QList datesForInterval(const Constraint &interval, PeriodType type) const; RecurrenceRule *mParent; QString mRRule; // RRULE string PeriodType mPeriod; QDateTime mDateStart; // start of recurrence (but mDateStart is not an occurrence // unless it matches the rule) uint mFrequency; /** how often it recurs: < 0 means no end date, 0 means an explicit end date, positive values give the number of occurrences */ int mDuration; QDateTime mDateEnd; QList mBySeconds; // values: second 0-59 QList mByMinutes; // values: minute 0-59 QList mByHours; // values: hour 0-23 QList mByDays; // n-th weekday of the month or year QList mByMonthDays; // values: day -31 to -1 and 1-31 QList mByYearDays; // values: day -366 to -1 and 1-366 QList mByWeekNumbers; // values: week -53 to -1 and 1-53 QList mByMonths; // values: month 1-12 QList mBySetPos; // values: position -366 to -1 and 1-366 short mWeekStart; // first day of the week (1=Monday, 7=Sunday) Constraint::List mConstraints; QList mObservers; // Cache for duration mutable QList mCachedDates; mutable QDateTime mCachedDateEnd; mutable QDateTime mCachedLastDate; // when mCachedDateEnd invalid, last date checked mutable bool mCached; bool mIsReadOnly; bool mAllDay; bool mNoByRules; // no BySeconds, ByMinutes, ... rules exist uint mTimedRepetition; // repeats at a regular number of seconds interval, or 0 }; RecurrenceRule::Private::Private(RecurrenceRule *parent, const Private &p) : mParent(parent), mRRule(p.mRRule), mPeriod(p.mPeriod), mDateStart(p.mDateStart), mFrequency(p.mFrequency), mDuration(p.mDuration), mDateEnd(p.mDateEnd), mBySeconds(p.mBySeconds), mByMinutes(p.mByMinutes), mByHours(p.mByHours), mByDays(p.mByDays), mByMonthDays(p.mByMonthDays), mByYearDays(p.mByYearDays), mByWeekNumbers(p.mByWeekNumbers), mByMonths(p.mByMonths), mBySetPos(p.mBySetPos), mWeekStart(p.mWeekStart), mIsReadOnly(p.mIsReadOnly), mAllDay(p.mAllDay), mNoByRules(p.mNoByRules) { setDirty(); } RecurrenceRule::Private &RecurrenceRule::Private::operator=(const Private &p) { // check for self assignment if (&p == this) { return *this; } mRRule = p.mRRule; mPeriod = p.mPeriod; mDateStart = p.mDateStart; mFrequency = p.mFrequency; mDuration = p.mDuration; mDateEnd = p.mDateEnd; mBySeconds = p.mBySeconds; mByMinutes = p.mByMinutes; mByHours = p.mByHours; mByDays = p.mByDays; mByMonthDays = p.mByMonthDays; mByYearDays = p.mByYearDays; mByWeekNumbers = p.mByWeekNumbers; mByMonths = p.mByMonths; mBySetPos = p.mBySetPos; mWeekStart = p.mWeekStart; mIsReadOnly = p.mIsReadOnly; mAllDay = p.mAllDay; mNoByRules = p.mNoByRules; setDirty(); return *this; } bool RecurrenceRule::Private::operator==(const Private &r) const { return mPeriod == r.mPeriod && ((mDateStart == r.mDateStart) || (!mDateStart.isValid() && !r.mDateStart.isValid())) && mDuration == r.mDuration && ((mDateEnd == r.mDateEnd) || (!mDateEnd.isValid() && !r.mDateEnd.isValid())) && mFrequency == r.mFrequency && mIsReadOnly == r.mIsReadOnly && mAllDay == r.mAllDay && mBySeconds == r.mBySeconds && mByMinutes == r.mByMinutes && mByHours == r.mByHours && mByDays == r.mByDays && mByMonthDays == r.mByMonthDays && mByYearDays == r.mByYearDays && mByWeekNumbers == r.mByWeekNumbers && mByMonths == r.mByMonths && mBySetPos == r.mBySetPos && mWeekStart == r.mWeekStart && mNoByRules == r.mNoByRules; } void RecurrenceRule::Private::clear() { if (mIsReadOnly) { return; } mPeriod = rNone; mBySeconds.clear(); mByMinutes.clear(); mByHours.clear(); mByDays.clear(); mByMonthDays.clear(); mByYearDays.clear(); mByWeekNumbers.clear(); mByMonths.clear(); mBySetPos.clear(); mWeekStart = 1; mNoByRules = false; setDirty(); } void RecurrenceRule::Private::setDirty() { buildConstraints(); mCached = false; mCachedDates.clear(); for (int i = 0, iend = mObservers.count(); i < iend; ++i) { if (mObservers[i]) { mObservers[i]->recurrenceChanged(mParent); } } } //@endcond /************************************************************************** * RecurrenceRule * **************************************************************************/ RecurrenceRule::RecurrenceRule() : d(new Private(this)) { } RecurrenceRule::RecurrenceRule(const RecurrenceRule &r) : d(new Private(this, *r.d)) { } RecurrenceRule::~RecurrenceRule() { delete d; } bool RecurrenceRule::operator==(const RecurrenceRule &r) const { return *d == *r.d; } RecurrenceRule &RecurrenceRule::operator=(const RecurrenceRule &r) { // check for self assignment if (&r == this) { return *this; } *d = *r.d; return *this; } void RecurrenceRule::addObserver(RuleObserver *observer) { if (!d->mObservers.contains(observer)) { d->mObservers.append(observer); } } void RecurrenceRule::removeObserver(RuleObserver *observer) { d->mObservers.removeAll(observer); } void RecurrenceRule::setRecurrenceType(PeriodType period) { if (isReadOnly()) { return; } d->mPeriod = period; d->setDirty(); } QDateTime RecurrenceRule::endDt(bool *result) const { if (result) { *result = false; } if (d->mPeriod == rNone) { return QDateTime(); } if (d->mDuration < 0) { return QDateTime(); } if (d->mDuration == 0) { if (result) { *result = true; } return d->mDateEnd; } // N occurrences. Check if we have a full cache. If so, return the cached end date. if (!d->mCached) { // If not enough occurrences can be found (i.e. inconsistent constraints) if (!d->buildCache()) { return QDateTime(); } } if (result) { *result = true; } return d->mCachedDateEnd; } void RecurrenceRule::setEndDt(const QDateTime &dateTime) { if (isReadOnly()) { return; } d->mDateEnd = dateTime; if (d->mDateEnd.isValid()) { d->mDuration = 0; // set to 0 because there is an end date/time } d->setDirty(); } void RecurrenceRule::setDuration(int duration) { if (isReadOnly()) { return; } d->mDuration = duration; d->setDirty(); } void RecurrenceRule::setAllDay(bool allDay) { if (isReadOnly()) { return; } d->mAllDay = allDay; d->setDirty(); } void RecurrenceRule::clear() { d->clear(); } void RecurrenceRule::setDirty() { d->setDirty(); } void RecurrenceRule::setStartDt(const QDateTime &start) { if (isReadOnly()) { return; } d->mDateStart = start; d->setDirty(); } void RecurrenceRule::setFrequency(int freq) { if (isReadOnly() || freq <= 0) { return; } d->mFrequency = freq; d->setDirty(); } void RecurrenceRule::setBySeconds(const QList &bySeconds) { if (isReadOnly()) { return; } d->mBySeconds = bySeconds; d->setDirty(); } void RecurrenceRule::setByMinutes(const QList &byMinutes) { if (isReadOnly()) { return; } d->mByMinutes = byMinutes; d->setDirty(); } void RecurrenceRule::setByHours(const QList &byHours) { if (isReadOnly()) { return; } d->mByHours = byHours; d->setDirty(); } void RecurrenceRule::setByDays(const QList &byDays) { if (isReadOnly()) { return; } d->mByDays = byDays; d->setDirty(); } void RecurrenceRule::setByMonthDays(const QList &byMonthDays) { if (isReadOnly()) { return; } d->mByMonthDays = byMonthDays; d->setDirty(); } void RecurrenceRule::setByYearDays(const QList &byYearDays) { if (isReadOnly()) { return; } d->mByYearDays = byYearDays; d->setDirty(); } void RecurrenceRule::setByWeekNumbers(const QList &byWeekNumbers) { if (isReadOnly()) { return; } d->mByWeekNumbers = byWeekNumbers; d->setDirty(); } void RecurrenceRule::setByMonths(const QList &byMonths) { if (isReadOnly()) { return; } d->mByMonths = byMonths; d->setDirty(); } void RecurrenceRule::setBySetPos(const QList &bySetPos) { if (isReadOnly()) { return; } d->mBySetPos = bySetPos; d->setDirty(); } void RecurrenceRule::setWeekStart(short weekStart) { if (isReadOnly()) { return; } d->mWeekStart = weekStart; d->setDirty(); } void RecurrenceRule::shiftTimes(const QTimeZone &oldTz, const QTimeZone &newTz) { d->mDateStart = d->mDateStart.toTimeZone(oldTz); d->mDateStart.setTimeZone(newTz); if (d->mDuration == 0) { d->mDateEnd = d->mDateEnd.toTimeZone(oldTz); d->mDateEnd.setTimeZone(newTz); } d->setDirty(); } // Taken from recurrence.cpp // int RecurrenceRule::maxIterations() const // { // /* Find the maximum number of iterations which may be needed to reach the // * next actual occurrence of a monthly or yearly recurrence. // * More than one iteration may be needed if, for example, it's the 29th February, // * the 31st day of the month or the 5th Monday, and the month being checked is // * February or a 30-day month. // * The following recurrences may never occur: // * - For rMonthlyDay: if the frequency is a whole number of years. // * - For rMonthlyPos: if the frequency is an even whole number of years. // * - For rYearlyDay, rYearlyMonth: if the frequeny is a multiple of 4 years. // * - For rYearlyPos: if the frequency is an even number of years. // * The maximum number of iterations needed, assuming that it does actually occur, // * was found empirically. // */ // switch (recurs) { // case rMonthlyDay: // return (rFreq % 12) ? 6 : 8; // // case rMonthlyPos: // if (rFreq % 12 == 0) { // // Some of these frequencies may never occur // return (rFreq % 84 == 0) ? 364 // frequency = multiple of 7 years // : (rFreq % 48 == 0) ? 7 // frequency = multiple of 4 years // : (rFreq % 24 == 0) ? 14 : 28; // frequency = multiple of 2 or 1 year // } // // All other frequencies will occur sometime // if (rFreq > 120) // return 364; // frequencies of > 10 years will hit the date limit first // switch (rFreq) { // case 23: return 50; // case 46: return 38; // case 56: return 138; // case 66: return 36; // case 89: return 54; // case 112: return 253; // default: return 25; // most frequencies will need < 25 iterations // } // // case rYearlyMonth: // case rYearlyDay: // return 8; // only 29th Feb or day 366 will need more than one iteration // // case rYearlyPos: // if (rFreq % 7 == 0) // return 364; // frequencies of a multiple of 7 years will hit the date limit first // if (rFreq % 2 == 0) { // // Some of these frequencies may never occur // return (rFreq % 4 == 0) ? 7 : 14; // frequency = even number of years // } // return 28; // } // return 1; // } //@cond PRIVATE void RecurrenceRule::Private::buildConstraints() { mTimedRepetition = 0; mNoByRules = mBySetPos.isEmpty(); mConstraints.clear(); Constraint con(mDateStart.timeZone()); if (mWeekStart > 0) { con.setWeekstart(mWeekStart); } mConstraints.append(con); int c, cend; int i, iend; Constraint::List tmp; #define intConstraint( list, setElement ) \ if ( !list.isEmpty() ) { \ mNoByRules = false; \ iend = list.count(); \ if ( iend == 1 ) { \ for ( c = 0, cend = mConstraints.count(); c < cend; ++c ) { \ mConstraints[c].setElement( list[0] ); \ } \ } else { \ tmp.reserve(mConstraints.count() * iend); \ for ( c = 0, cend = mConstraints.count(); c < cend; ++c ) { \ for ( i = 0; i < iend; ++i ) { \ con = mConstraints[c]; \ con.setElement( list[i] ); \ tmp.append( con ); \ } \ } \ mConstraints = tmp; \ tmp.clear(); \ } \ } intConstraint(mBySeconds, setSecond); intConstraint(mByMinutes, setMinute); intConstraint(mByHours, setHour); intConstraint(mByMonthDays, setDay); intConstraint(mByMonths, setMonth); intConstraint(mByYearDays, setYearday); intConstraint(mByWeekNumbers, setWeeknumber); #undef intConstraint if (!mByDays.isEmpty()) { mNoByRules = false; tmp.reserve(mConstraints.count() * mByDays.count()); for (c = 0, cend = mConstraints.count(); c < cend; ++c) { for (i = 0, iend = mByDays.count(); i < iend; ++i) { con = mConstraints[c]; con.setWeekday(mByDays[i].day()); con.setWeekdaynr(mByDays[i].pos()); tmp.append(con); } } mConstraints = tmp; tmp.clear(); } #define fixConstraint( setElement, value ) \ { \ for ( c = 0, cend = mConstraints.count(); c < cend; ++c ) { \ mConstraints[c].setElement( value ); \ } \ } // Now determine missing values from DTSTART. This can speed up things, // because we have more restrictions and save some loops. // TODO: Does RFC 2445 intend to restrict the weekday in all cases of weekly? if (mPeriod == rWeekly && mByDays.isEmpty()) { fixConstraint(setWeekday, mDateStart.date().dayOfWeek()); } // Really fall through in the cases, because all smaller time intervals are // constrained from dtstart switch (mPeriod) { case rYearly: if (mByDays.isEmpty() && mByWeekNumbers.isEmpty() && mByYearDays.isEmpty() && mByMonths.isEmpty()) { fixConstraint(setMonth, mDateStart.date().month()); } Q_FALLTHROUGH(); case rMonthly: if (mByDays.isEmpty() && mByWeekNumbers.isEmpty() && mByYearDays.isEmpty() && mByMonthDays.isEmpty()) { fixConstraint(setDay, mDateStart.date().day()); } Q_FALLTHROUGH(); case rWeekly: case rDaily: if (mByHours.isEmpty()) { fixConstraint(setHour, mDateStart.time().hour()); } Q_FALLTHROUGH(); case rHourly: if (mByMinutes.isEmpty()) { fixConstraint(setMinute, mDateStart.time().minute()); } Q_FALLTHROUGH(); case rMinutely: if (mBySeconds.isEmpty()) { fixConstraint(setSecond, mDateStart.time().second()); } Q_FALLTHROUGH(); case rSecondly: default: break; } #undef fixConstraint if (mNoByRules) { switch (mPeriod) { case rHourly: mTimedRepetition = mFrequency * 3600; break; case rMinutely: mTimedRepetition = mFrequency * 60; break; case rSecondly: mTimedRepetition = mFrequency; break; default: break; } } else { for (c = 0, cend = mConstraints.count(); c < cend;) { if (mConstraints[c].isConsistent(mPeriod)) { ++c; } else { mConstraints.removeAt(c); --cend; } } } } // Build and cache a list of all occurrences. // Only call buildCache() if mDuration > 0. bool RecurrenceRule::Private::buildCache() const { Q_ASSERT(mDuration > 0); // Build the list of all occurrences of this event (we need that to determine // the end date!) Constraint interval(getNextValidDateInterval(mDateStart, mPeriod)); auto dts = datesForInterval(interval, mPeriod); // Only use dates after the event has started (start date is only included // if it matches) const auto it = strictLowerBound(dts.begin(), dts.end(), mDateStart); if (it != dts.end()) { dts.erase(dts.begin(), it + 1); } // some validity checks to avoid infinite loops (i.e. if we have // done this loop already 10000 times, bail out ) for (int loopnr = 0; loopnr < LOOP_LIMIT && dts.count() < mDuration; ++loopnr) { interval.increase(mPeriod, mFrequency); // The returned date list is already sorted! dts += datesForInterval(interval, mPeriod); } if (dts.count() > mDuration) { // we have picked up more occurrences than necessary, remove them dts.erase(dts.begin() + mDuration, dts.end()); } mCached = true; mCachedDates = dts; // it = dts.begin(); // while ( it != dts.end() ) { // qCDebug(KCALCORE_LOG) << " -=>" << dumpTime(*it); // ++it; // } if (int(dts.count()) == mDuration) { mCachedDateEnd = dts.last(); return true; } else { // The cached date list is incomplete mCachedDateEnd = QDateTime(); mCachedLastDate = interval.intervalDateTime(mPeriod); return false; } } //@endcond bool RecurrenceRule::dateMatchesRules(const QDateTime &kdt) const { QDateTime dt = kdt.toTimeZone(d->mDateStart.timeZone()); for (int i = 0, iend = d->mConstraints.count(); i < iend; ++i) { if (d->mConstraints[i].matches(dt, recurrenceType())) { return true; } } return false; } bool RecurrenceRule::recursOn(const QDate &qd, const QTimeZone &timeZone) const { int i, iend; if (!qd.isValid() || !d->mDateStart.isValid()) { // There can't be recurrences on invalid dates return false; } if (allDay()) { // It's a date-only rule, so it has no time specification. // Therefore ignore 'timeSpec'. if (qd < d->mDateStart.date()) { return false; } // Start date is only included if it really matches QDate endDate; if (d->mDuration >= 0) { endDate = endDt().date(); if (qd > endDate) { return false; } } // The date must be in an appropriate interval (getNextValidDateInterval), // Plus it must match at least one of the constraints bool match = false; for (i = 0, iend = d->mConstraints.count(); i < iend && !match; ++i) { match = d->mConstraints[i].matches(qd, recurrenceType()); } if (!match) { return false; } QDateTime start(qd, QTime(0, 0, 0), timeZone); //d->mDateStart.timeZone()); Constraint interval(d->getNextValidDateInterval(start, recurrenceType())); // Constraint::matches is quite efficient, so first check if it can occur at // all before we calculate all actual dates. if (!interval.matches(qd, recurrenceType())) { return false; } // We really need to obtain the list of dates in this interval, since // otherwise BYSETPOS will not work (i.e. the date will match the interval, // but BYSETPOS selects only one of these matching dates! QDateTime end = start.addDays(1); do { auto dts = d->datesForInterval(interval, recurrenceType()); for (i = 0, iend = dts.count(); i < iend; ++i) { if (dts[i].date() >= qd) { return dts[i].date() == qd; } } interval.increase(recurrenceType(), frequency()); } while (interval.intervalDateTime(recurrenceType()) < end); return false; } // It's a date-time rule, so we need to take the time specification into account. QDateTime start(qd, QTime(0, 0, 0), timeZone); QDateTime end = start.addDays(1).toTimeZone(d->mDateStart.timeZone()); start = start.toTimeZone(d->mDateStart.timeZone()); if (end < d->mDateStart) { return false; } if (start < d->mDateStart) { start = d->mDateStart; } // Start date is only included if it really matches if (d->mDuration >= 0) { QDateTime endRecur = endDt(); if (endRecur.isValid()) { if (start > endRecur) { return false; } if (end > endRecur) { end = endRecur; // limit end-of-day time to end of recurrence rule } } } if (d->mTimedRepetition) { // It's a simple sub-daily recurrence with no constraints int n = static_cast((d->mDateStart.secsTo(start) - 1) % d->mTimedRepetition); return start.addSecs(d->mTimedRepetition - n) < end; } // Find the start and end dates in the time spec for the rule QDate startDay = start.date(); QDate endDay = end.addSecs(-1).date(); int dayCount = startDay.daysTo(endDay) + 1; // The date must be in an appropriate interval (getNextValidDateInterval), // Plus it must match at least one of the constraints bool match = false; for (i = 0, iend = d->mConstraints.count(); i < iend && !match; ++i) { match = d->mConstraints[i].matches(startDay, recurrenceType()); for (int day = 1; day < dayCount && !match; ++day) { match = d->mConstraints[i].matches(startDay.addDays(day), recurrenceType()); } } if (!match) { return false; } Constraint interval(d->getNextValidDateInterval(start, recurrenceType())); // Constraint::matches is quite efficient, so first check if it can occur at // all before we calculate all actual dates. Constraint intervalm = interval; do { match = intervalm.matches(startDay, recurrenceType()); for (int day = 1; day < dayCount && !match; ++day) { match = intervalm.matches(startDay.addDays(day), recurrenceType()); } if (match) { break; } intervalm.increase(recurrenceType(), frequency()); } while (intervalm.intervalDateTime(recurrenceType()).isValid() && intervalm.intervalDateTime(recurrenceType()) < end); if (!match) { return false; } // We really need to obtain the list of dates in this interval, since // otherwise BYSETPOS will not work (i.e. the date will match the interval, // but BYSETPOS selects only one of these matching dates! do { auto dts = d->datesForInterval(interval, recurrenceType()); const auto it = std::lower_bound(dts.constBegin(), dts.constEnd(), start); if (it != dts.constEnd()) { return *it <= end; } interval.increase(recurrenceType(), frequency()); } while (interval.intervalDateTime(recurrenceType()).isValid() && interval.intervalDateTime(recurrenceType()) < end); return false; } bool RecurrenceRule::recursAt(const QDateTime &kdt) const { // Convert to the time spec used by this recurrence rule QDateTime dt(kdt.toTimeZone(d->mDateStart.timeZone())); if (allDay()) { return recursOn(dt.date(), dt.timeZone()); } if (dt < d->mDateStart) { return false; } // Start date is only included if it really matches if (d->mDuration >= 0 && dt > endDt()) { return false; } if (d->mTimedRepetition) { // It's a simple sub-daily recurrence with no constraints return !(d->mDateStart.secsTo(dt) % d->mTimedRepetition); } // The date must be in an appropriate interval (getNextValidDateInterval), // Plus it must match at least one of the constraints if (!dateMatchesRules(dt)) { return false; } // if it recurs every interval, speed things up... // if ( d->mFrequency == 1 && d->mBySetPos.isEmpty() && d->mByDays.isEmpty() ) return true; Constraint interval(d->getNextValidDateInterval(dt, recurrenceType())); // TODO_Recurrence: Does this work with BySetPos??? if (interval.matches(dt, recurrenceType())) { return true; } return false; } TimeList RecurrenceRule::recurTimesOn(const QDate &date, const QTimeZone &timeZone) const { TimeList lst; if (allDay()) { return lst; } QDateTime start(date, QTime(0, 0, 0), timeZone); QDateTime end = start.addDays(1).addSecs(-1); auto dts = timesInInterval(start, end); // returns between start and end inclusive for (int i = 0, iend = dts.count(); i < iend; ++i) { lst += dts[i].toTimeZone(timeZone).time(); } return lst; } /** Returns the number of recurrences up to and including the date/time specified. */ int RecurrenceRule::durationTo(const QDateTime &dt) const { // Convert to the time spec used by this recurrence rule QDateTime toDate(dt.toTimeZone(d->mDateStart.timeZone())); // Easy cases: // either before start, or after all recurrences and we know their number if (toDate < d->mDateStart) { return 0; } // Start date is only included if it really matches if (d->mDuration > 0 && toDate >= endDt()) { return d->mDuration; } if (d->mTimedRepetition) { // It's a simple sub-daily recurrence with no constraints return static_cast(d->mDateStart.secsTo(toDate) / d->mTimedRepetition); } return timesInInterval(d->mDateStart, toDate).count(); } int RecurrenceRule::durationTo(const QDate &date) const { return durationTo(QDateTime(date, QTime(23, 59, 59), d->mDateStart.timeZone())); } QDateTime RecurrenceRule::getPreviousDate(const QDateTime &afterDate) const { // Convert to the time spec used by this recurrence rule QDateTime toDate(afterDate.toTimeZone(d->mDateStart.timeZone())); // Invalid starting point, or beyond end of recurrence if (!toDate.isValid() || toDate < d->mDateStart) { return QDateTime(); } if (d->mTimedRepetition) { // It's a simple sub-daily recurrence with no constraints QDateTime prev = toDate; if (d->mDuration >= 0 && endDt().isValid() && toDate > endDt()) { prev = endDt().addSecs(1).toTimeZone(d->mDateStart.timeZone()); } int n = static_cast((d->mDateStart.secsTo(prev) - 1) % d->mTimedRepetition); if (n < 0) { return QDateTime(); // before recurrence start } prev = prev.addSecs(-n - 1); return prev >= d->mDateStart ? prev : QDateTime(); } // If we have a cache (duration given), use that if (d->mDuration > 0) { if (!d->mCached) { d->buildCache(); } const auto it = strictLowerBound(d->mCachedDates.constBegin(), d->mCachedDates.constEnd(), toDate); if (it != d->mCachedDates.constEnd()) { return *it; } return QDateTime(); } QDateTime prev = toDate; if (d->mDuration >= 0 && endDt().isValid() && toDate > endDt()) { prev = endDt().addSecs(1).toTimeZone(d->mDateStart.timeZone()); } Constraint interval(d->getPreviousValidDateInterval(prev, recurrenceType())); const auto dts = d->datesForInterval(interval, recurrenceType()); const auto it = strictLowerBound(dts.begin(), dts.end(), prev); if (it != dts.end()) { return ((*it) >= d->mDateStart) ? (*it) : QDateTime(); } // Previous interval. As soon as we find an occurrence, we're done. while (interval.intervalDateTime(recurrenceType()) > d->mDateStart) { interval.increase(recurrenceType(), -int(frequency())); // The returned date list is sorted auto dts = d->datesForInterval(interval, recurrenceType()); // The list is sorted, so take the last one. if (!dts.isEmpty()) { prev = dts.last(); if (prev.isValid() && prev >= d->mDateStart) { return prev; } else { return QDateTime(); } } } return QDateTime(); } QDateTime RecurrenceRule::getNextDate(const QDateTime &preDate) const { // Convert to the time spec used by this recurrence rule QDateTime fromDate(preDate.toTimeZone(d->mDateStart.timeZone())); // Beyond end of recurrence if (d->mDuration >= 0 && endDt().isValid() && fromDate >= endDt()) { return QDateTime(); } // Start date is only included if it really matches if (fromDate < d->mDateStart) { fromDate = d->mDateStart.addSecs(-1); } if (d->mTimedRepetition) { // It's a simple sub-daily recurrence with no constraints int n = static_cast((d->mDateStart.secsTo(fromDate) + 1) % d->mTimedRepetition); QDateTime next = fromDate.addSecs(d->mTimedRepetition - n + 1); return d->mDuration < 0 || !endDt().isValid() || next <= endDt() ? next : QDateTime(); } if (d->mDuration > 0) { if (!d->mCached) { d->buildCache(); } const auto it = std::upper_bound(d->mCachedDates.constBegin(), d->mCachedDates.constEnd(), fromDate); if (it != d->mCachedDates.constEnd()) { return *it; } } QDateTime end = endDt(); Constraint interval(d->getNextValidDateInterval(fromDate, recurrenceType())); const auto dts = d->datesForInterval(interval, recurrenceType()); const auto it = std::upper_bound(dts.begin(), dts.end(), fromDate); if (it != dts.end()) { return (d->mDuration < 0 || *it <= end) ? *it : QDateTime(); } interval.increase(recurrenceType(), frequency()); if (d->mDuration >= 0 && interval.intervalDateTime(recurrenceType()) > end) { return QDateTime(); } // Increase the interval. The first occurrence that we find is the result (if // if's before the end date). // TODO: some validity checks to avoid infinite loops for contradictory constraints int loop = 0; do { auto dts = d->datesForInterval(interval, recurrenceType()); if (!dts.isEmpty()) { QDateTime ret(dts[0]); if (d->mDuration >= 0 && ret > end) { return QDateTime(); } else { return ret; } } interval.increase(recurrenceType(), frequency()); } while (++loop < LOOP_LIMIT && (d->mDuration < 0 || interval.intervalDateTime(recurrenceType()) < end)); return QDateTime(); } QList RecurrenceRule::timesInInterval(const QDateTime &dtStart, const QDateTime &dtEnd) const { const QDateTime start = dtStart.toTimeZone(d->mDateStart.timeZone()); const QDateTime end = dtEnd.toTimeZone(d->mDateStart.timeZone()); QList result; if (end < d->mDateStart) { return result; // before start of recurrence } QDateTime enddt = end; if (d->mDuration >= 0) { const QDateTime endRecur = endDt(); if (endRecur.isValid()) { if (start > endRecur) { return result; // beyond end of recurrence } if (end >= endRecur) { enddt = endRecur; // limit end time to end of recurrence rule } } } if (d->mTimedRepetition) { // It's a simple sub-daily recurrence with no constraints //Seconds to add to interval start, to get first occurrence which is within interval qint64 offsetFromNextOccurrence; if (d->mDateStart < start) { offsetFromNextOccurrence = d->mTimedRepetition - (d->mDateStart.secsTo(start) % d->mTimedRepetition); } else { offsetFromNextOccurrence = -(d->mDateStart.secsTo(start) % d->mTimedRepetition); } QDateTime dt = start.addSecs(offsetFromNextOccurrence); if (dt <= enddt) { int numberOfOccurrencesWithinInterval = static_cast(dt.secsTo(enddt) / d->mTimedRepetition) + 1; // limit n by a sane value else we can "explode". numberOfOccurrencesWithinInterval = qMin(numberOfOccurrencesWithinInterval, LOOP_LIMIT); for (int i = 0; i < numberOfOccurrencesWithinInterval; dt = dt.addSecs(d->mTimedRepetition), ++i) { result += dt; } } return result; } QDateTime st = start; bool done = false; if (d->mDuration > 0) { if (!d->mCached) { d->buildCache(); } if (d->mCachedDateEnd.isValid() && start > d->mCachedDateEnd) { return result; // beyond end of recurrence } const auto it = std::lower_bound(d->mCachedDates.constBegin(), d->mCachedDates.constEnd(), start); if (it != d->mCachedDates.constEnd()) { const auto itEnd = std::upper_bound(it, d->mCachedDates.constEnd(), enddt); if (itEnd != d->mCachedDates.constEnd()) { done = true; } std::copy(it, itEnd, std::back_inserter(result)); } if (d->mCachedDateEnd.isValid()) { done = true; } else if (!result.isEmpty()) { result += QDateTime(); // indicate that the returned list is incomplete done = true; } if (done) { return result; } // We don't have any result yet, but we reached the end of the incomplete cache st = d->mCachedLastDate.addSecs(1); } Constraint interval(d->getNextValidDateInterval(st, recurrenceType())); int loop = 0; do { auto dts = d->datesForInterval(interval, recurrenceType()); auto it = dts.begin(); auto itEnd = dts.end(); if (loop == 0) { it = std::lower_bound(dts.begin(), dts.end(), st); } itEnd = std::upper_bound(it, dts.end(), enddt); if (itEnd != dts.end()) { loop = LOOP_LIMIT; } std::copy(it, itEnd, std::back_inserter(result)); // Increase the interval. interval.increase(recurrenceType(), frequency()); } while (++loop < LOOP_LIMIT && interval.intervalDateTime(recurrenceType()) < end); return result; } //@cond PRIVATE // Find the date/time of the occurrence at or before a date/time, // for a given period type. // Return a constraint whose value appropriate to 'type', is set to // the value contained in the date/time. Constraint RecurrenceRule::Private::getPreviousValidDateInterval(const QDateTime &dt, PeriodType type) const { long periods = 0; QDateTime start = mDateStart; QDateTime nextValid(start); int modifier = 1; QDateTime toDate(dt.toTimeZone(start.timeZone())); // for super-daily recurrences, don't care about the time part // Find the #intervals since the dtstart and round to the next multiple of // the frequency switch (type) { // Really fall through for sub-daily, since the calculations only differ // by the factor 60 and 60*60! Same for weekly and daily (factor 7) case rHourly: modifier *= 60; Q_FALLTHROUGH(); case rMinutely: modifier *= 60; Q_FALLTHROUGH(); case rSecondly: periods = static_cast(start.secsTo(toDate) / modifier); // round it down to the next lower multiple of frequency: if (mFrequency > 0) { periods = (periods / mFrequency) * mFrequency; } nextValid = start.addSecs(modifier * periods); break; case rWeekly: toDate = toDate.addDays(-(7 + toDate.date().dayOfWeek() - mWeekStart) % 7); start = start.addDays(-(7 + start.date().dayOfWeek() - mWeekStart) % 7); modifier *= 7; Q_FALLTHROUGH(); case rDaily: periods = start.daysTo(toDate) / modifier; // round it down to the next lower multiple of frequency: if (mFrequency > 0) { periods = (periods / mFrequency) * mFrequency; } nextValid = start.addDays(modifier * periods); break; case rMonthly: { periods = 12 * (toDate.date().year() - start.date().year()) + (toDate.date().month() - start.date().month()); // round it down to the next lower multiple of frequency: if (mFrequency > 0) { periods = (periods / mFrequency) * mFrequency; } // set the day to the first day of the month, so we don't have problems // with non-existent days like Feb 30 or April 31 start.setDate(QDate(start.date().year(), start.date().month(), 1)); nextValid.setDate(start.date().addMonths(periods)); break; } case rYearly: periods = (toDate.date().year() - start.date().year()); // round it down to the next lower multiple of frequency: if (mFrequency > 0) { periods = (periods / mFrequency) * mFrequency; } nextValid.setDate(start.date().addYears(periods)); break; default: break; } return Constraint(nextValid, type, mWeekStart); } // Find the date/time of the next occurrence at or after a date/time, // for a given period type. // Return a constraint whose value appropriate to 'type', is set to the // value contained in the date/time. Constraint RecurrenceRule::Private::getNextValidDateInterval(const QDateTime &dt, PeriodType type) const { // TODO: Simplify this! long periods = 0; QDateTime start = mDateStart; QDateTime nextValid(start); int modifier = 1; QDateTime toDate(dt.toTimeZone(start.timeZone())); // for super-daily recurrences, don't care about the time part // Find the #intervals since the dtstart and round to the next multiple of // the frequency switch (type) { // Really fall through for sub-daily, since the calculations only differ // by the factor 60 and 60*60! Same for weekly and daily (factor 7) case rHourly: modifier *= 60; Q_FALLTHROUGH(); case rMinutely: modifier *= 60; Q_FALLTHROUGH(); case rSecondly: periods = static_cast(start.secsTo(toDate) / modifier); periods = qMax(0L, periods); if (periods > 0 && mFrequency > 0) { periods += (mFrequency - 1 - ((periods - 1) % mFrequency)); } nextValid = start.addSecs(modifier * periods); break; case rWeekly: // correct both start date and current date to start of week toDate = toDate.addDays(-(7 + toDate.date().dayOfWeek() - mWeekStart) % 7); start = start.addDays(-(7 + start.date().dayOfWeek() - mWeekStart) % 7); modifier *= 7; Q_FALLTHROUGH(); case rDaily: periods = start.daysTo(toDate) / modifier; periods = qMax(0L, periods); if (periods > 0 && mFrequency > 0) { periods += (mFrequency - 1 - ((periods - 1) % mFrequency)); } nextValid = start.addDays(modifier * periods); break; case rMonthly: { periods = 12 * (toDate.date().year() - start.date().year()) + (toDate.date().month() - start.date().month()); periods = qMax(0L, periods); if (periods > 0 && mFrequency > 0) { periods += (mFrequency - 1 - ((periods - 1) % mFrequency)); } // set the day to the first day of the month, so we don't have problems // with non-existent days like Feb 30 or April 31 start.setDate(QDate(start.date().year(), start.date().month(), 1)); nextValid.setDate(start.date().addMonths(periods)); break; } case rYearly: periods = (toDate.date().year() - start.date().year()); periods = qMax(0L, periods); if (periods > 0 && mFrequency > 0) { periods += (mFrequency - 1 - ((periods - 1) % mFrequency)); } nextValid.setDate(start.date().addYears(periods)); break; default: break; } return Constraint(nextValid, type, mWeekStart); } QList RecurrenceRule::Private::datesForInterval(const Constraint &interval, PeriodType type) const { /* -) Loop through constraints, -) merge interval with each constraint -) if merged constraint is not consistent => ignore that constraint -) if complete => add that one date to the date list -) Loop through all missing fields => For each add the resulting */ QList lst; for (int i = 0, iend = mConstraints.count(); i < iend; ++i) { Constraint merged(interval); if (merged.merge(mConstraints[i])) { // If the information is incomplete, we can't use this constraint if (merged.year > 0 && merged.hour >= 0 && merged.minute >= 0 && merged.second >= 0) { // We have a valid constraint, so get all datetimes that match it andd // append it to all date/times of this interval QList lstnew = merged.dateTimes(type); lst += lstnew; } } } // Sort it so we can apply the BySetPos. Also some logic relies on this being sorted sortAndRemoveDuplicates(lst); /*if ( lst.isEmpty() ) { qCDebug(KCALCORE_LOG) << " No Dates in Interval"; } else { qCDebug(KCALCORE_LOG) << " Dates:"; for ( int i = 0, iend = lst.count(); i < iend; ++i ) { qCDebug(KCALCORE_LOG)<< " -)" << dumpTime(lst[i]); } qCDebug(KCALCORE_LOG) << " ---------------------"; }*/ if (!mBySetPos.isEmpty()) { auto tmplst = lst; lst.clear(); for (int i = 0, iend = mBySetPos.count(); i < iend; ++i) { int pos = mBySetPos[i]; if (pos > 0) { --pos; } if (pos < 0) { pos += tmplst.count(); } if (pos >= 0 && pos < tmplst.count()) { lst.append(tmplst[pos]); } } sortAndRemoveDuplicates(lst); } return lst; } //@endcond void RecurrenceRule::dump() const { #ifndef NDEBUG if (!d->mRRule.isEmpty()) { qCDebug(KCALCORE_LOG) << " RRULE=" << d->mRRule; } qCDebug(KCALCORE_LOG) << " Read-Only:" << isReadOnly(); qCDebug(KCALCORE_LOG) << " Period type:" << int(recurrenceType()) << ", frequency:" << frequency(); qCDebug(KCALCORE_LOG) << " #occurrences:" << duration(); qCDebug(KCALCORE_LOG) << " start date:" << dumpTime(startDt(), allDay()) << ", end date:" << dumpTime(endDt(), allDay()); #define dumpByIntList(list,label) \ if ( !list.isEmpty() ) {\ QStringList lst;\ for ( int i = 0, iend = list.count(); i < iend; ++i ) {\ lst.append( QString::number( list[i] ) );\ }\ qCDebug(KCALCORE_LOG) << " " << label << lst.join( QStringLiteral(", ") );\ } dumpByIntList(d->mBySeconds, QStringLiteral("BySeconds: ")); dumpByIntList(d->mByMinutes, QStringLiteral("ByMinutes: ")); dumpByIntList(d->mByHours, QStringLiteral("ByHours: ")); if (!d->mByDays.isEmpty()) { QStringList lst; for (int i = 0, iend = d->mByDays.count(); i < iend; ++i) { lst.append((d->mByDays[i].pos() ? QString::number(d->mByDays[i].pos()) : QLatin1String("")) + DateHelper::dayName(d->mByDays[i].day())); } qCDebug(KCALCORE_LOG) << " ByDays: " << lst.join(QStringLiteral(", ")); } dumpByIntList(d->mByMonthDays, QStringLiteral("ByMonthDays:")); dumpByIntList(d->mByYearDays, QStringLiteral("ByYearDays: ")); dumpByIntList(d->mByWeekNumbers, QStringLiteral("ByWeekNr: ")); dumpByIntList(d->mByMonths, QStringLiteral("ByMonths: ")); dumpByIntList(d->mBySetPos, QStringLiteral("BySetPos: ")); #undef dumpByIntList qCDebug(KCALCORE_LOG) << " Week start:" << DateHelper::dayName(d->mWeekStart); qCDebug(KCALCORE_LOG) << " Constraints:"; // dump constraints for (int i = 0, iend = d->mConstraints.count(); i < iend; ++i) { d->mConstraints[i].dump(); } #endif } //@cond PRIVATE void Constraint::dump() const { qCDebug(KCALCORE_LOG) << " ~> Y=" << year << ", M=" << month << ", D=" << day << ", H=" << hour << ", m=" << minute << ", S=" << second << ", wd=" << weekday << ",#wd=" << weekdaynr << ", #w=" << weeknumber << ", yd=" << yearday; } //@endcond QString dumpTime(const QDateTime &dt, bool isAllDay) { #ifndef NDEBUG if (!dt.isValid()) { return QString(); } QString result; if (isAllDay) { result = dt.toString(QStringLiteral("ddd yyyy-MM-dd t")); } else { result = dt.toString(QStringLiteral("ddd yyyy-MM-dd hh:mm:ss t")); } return result; #else Q_UNUSED(dt); Q_UNUSED(isAllDay); return QString(); #endif } QDateTime RecurrenceRule::startDt() const { return d->mDateStart; } RecurrenceRule::PeriodType RecurrenceRule::recurrenceType() const { return d->mPeriod; } uint RecurrenceRule::frequency() const { return d->mFrequency; } int RecurrenceRule::duration() const { return d->mDuration; } QString RecurrenceRule::rrule() const { return d->mRRule; } void RecurrenceRule::setRRule(const QString &rrule) { d->mRRule = rrule; } bool RecurrenceRule::isReadOnly() const { return d->mIsReadOnly; } void RecurrenceRule::setReadOnly(bool readOnly) { d->mIsReadOnly = readOnly; } bool RecurrenceRule::recurs() const { return d->mPeriod != rNone; } bool RecurrenceRule::allDay() const { return d->mAllDay; } const QList &RecurrenceRule::bySeconds() const { return d->mBySeconds; } const QList &RecurrenceRule::byMinutes() const { return d->mByMinutes; } const QList &RecurrenceRule::byHours() const { return d->mByHours; } const QList &RecurrenceRule::byDays() const { return d->mByDays; } const QList &RecurrenceRule::byMonthDays() const { return d->mByMonthDays; } const QList &RecurrenceRule::byYearDays() const { return d->mByYearDays; } const QList &RecurrenceRule::byWeekNumbers() const { return d->mByWeekNumbers; } const QList &RecurrenceRule::byMonths() const { return d->mByMonths; } const QList &RecurrenceRule::bySetPos() const { return d->mBySetPos; } short RecurrenceRule::weekStart() const { return d->mWeekStart; } RecurrenceRule::RuleObserver::~RuleObserver() { } RecurrenceRule::WDayPos::WDayPos(int ps, short dy) : mDay(dy), mPos(ps) { } void RecurrenceRule::WDayPos::setDay(short dy) { mDay = dy; } short RecurrenceRule::WDayPos::day() const { return mDay; } void RecurrenceRule::WDayPos::setPos(int ps) { mPos = ps; } int RecurrenceRule::WDayPos::pos() const { return mPos; } QDataStream &operator<<(QDataStream &out, const Constraint &c) { out << c.year << c.month << c.day << c.hour << c.minute << c.second << c.weekday << c.weekdaynr << c.weeknumber << c.yearday << c.weekstart; serializeQTimeZoneAsSpec(out, c.timeZone); out << false; // for backwards compatibility return out; } QDataStream &operator>>(QDataStream &in, Constraint &c) { bool secondOccurrence; // no longer used in >> c.year >> c.month >> c.day >> c.hour >> c.minute >> c.second >> c.weekday >> c.weekdaynr >> c.weeknumber >> c.yearday >> c.weekstart; deserializeSpecAsQTimeZone(in, c.timeZone); in >> secondOccurrence; return in; } -KCALCORE_EXPORT QDataStream &KCalCore::operator<<(QDataStream &out, const KCalCore::RecurrenceRule::WDayPos &w) +KCALCORE_EXPORT QDataStream &KCalendarCore::operator<<(QDataStream &out, const KCalendarCore::RecurrenceRule::WDayPos &w) { out << w.mDay << w.mPos; return out; } -KCALCORE_EXPORT QDataStream &KCalCore::operator>>(QDataStream &in, KCalCore::RecurrenceRule::WDayPos &w) +KCALCORE_EXPORT QDataStream &KCalendarCore::operator>>(QDataStream &in, KCalendarCore::RecurrenceRule::WDayPos &w) { in >> w.mDay >> w.mPos; return in; } -KCALCORE_EXPORT QDataStream &KCalCore::operator<<(QDataStream &out, const KCalCore::RecurrenceRule *r) +KCALCORE_EXPORT QDataStream &KCalendarCore::operator<<(QDataStream &out, const KCalendarCore::RecurrenceRule *r) { if (!r) { return out; } RecurrenceRule::Private *d = r->d; out << d->mRRule << static_cast(d->mPeriod); serializeQDateTimeAsKDateTime(out, d->mDateStart); out << d->mFrequency << d->mDuration; serializeQDateTimeAsKDateTime(out, d->mDateEnd); out << d->mBySeconds << d->mByMinutes << d->mByHours << d->mByDays << d->mByMonthDays << d->mByYearDays << d->mByWeekNumbers << d->mByMonths << d->mBySetPos << d->mWeekStart << d->mConstraints << d->mAllDay << d->mNoByRules << d->mTimedRepetition << d->mIsReadOnly; return out; } -KCALCORE_EXPORT QDataStream &KCalCore::operator>>(QDataStream &in, const KCalCore::RecurrenceRule *r) +KCALCORE_EXPORT QDataStream &KCalendarCore::operator>>(QDataStream &in, const KCalendarCore::RecurrenceRule *r) { if (!r) { return in; } RecurrenceRule::Private *d = r->d; quint32 period; in >> d->mRRule >> period; deserializeKDateTimeAsQDateTime(in, d->mDateStart); in >> d->mFrequency >> d->mDuration; deserializeKDateTimeAsQDateTime(in, d->mDateEnd); in >> d->mBySeconds >> d->mByMinutes >> d->mByHours >> d->mByDays >> d->mByMonthDays >> d->mByYearDays >> d->mByWeekNumbers >> d->mByMonths >> d->mBySetPos >> d->mWeekStart >> d->mConstraints >> d->mAllDay >> d->mNoByRules >> d->mTimedRepetition >> d->mIsReadOnly; d->mPeriod = static_cast(period); return in; } diff --git a/src/recurrencerule.h b/src/recurrencerule.h index dc462e69c..7404a6333 100644 --- a/src/recurrencerule.h +++ b/src/recurrencerule.h @@ -1,349 +1,349 @@ /* This file is part of the kcalcore library. Copyright (c) 1998 Preston Brown Copyright (c) 2001,2003 Cornelius Schumacher Copyright (c) 2002,2006,2007 David Jarvie Copyright (c) 2005, Reinhold Kainhofer This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KCALCORE_RECURRENCERULE_H #define KCALCORE_RECURRENCERULE_H #include "kcalcore_export.h" #include #include class QTimeZone; -namespace KCalCore { +namespace KCalendarCore { // These two are duplicates wrt. incidencebase.h typedef QList DateList; /* List of times */ typedef QList TimeList; /** This class represents a recurrence rule for a calendar incidence. */ class KCALCORE_EXPORT RecurrenceRule { public: class RuleObserver { public: virtual ~RuleObserver(); /** This method is called on each change of the recurrence object */ virtual void recurrenceChanged(RecurrenceRule *) = 0; }; typedef QList List; /** enum for describing the frequency how an event recurs, if at all. */ enum PeriodType { rNone = 0, rSecondly, rMinutely, rHourly, rDaily, rWeekly, rMonthly, rYearly }; /** structure for describing the n-th weekday of the month/year. */ class KCALCORE_EXPORT WDayPos //krazy:exclude=dpointer { public: explicit WDayPos(int ps = 0, short dy = 0); void setDay(short dy); short day() const; void setPos(int ps); int pos() const; bool operator==(const RecurrenceRule::WDayPos &pos2) const; bool operator!=(const RecurrenceRule::WDayPos &pos2) const; protected: short mDay; // Weekday, 1=monday, 7=sunday int mPos; // week of the day (-1 for last, 1 for first, 0 for all weeks) // Bounded by -366 and +366, 0 means all weeks in that period - friend KCALCORE_EXPORT QDataStream &operator<<(QDataStream &out, const KCalCore::RecurrenceRule::WDayPos &); - friend KCALCORE_EXPORT QDataStream &operator>>(QDataStream &in, KCalCore::RecurrenceRule::WDayPos &); + friend KCALCORE_EXPORT QDataStream &operator<<(QDataStream &out, const KCalendarCore::RecurrenceRule::WDayPos &); + friend KCALCORE_EXPORT QDataStream &operator>>(QDataStream &in, KCalendarCore::RecurrenceRule::WDayPos &); }; RecurrenceRule(); RecurrenceRule(const RecurrenceRule &r); ~RecurrenceRule(); bool operator==(const RecurrenceRule &r) const; bool operator!=(const RecurrenceRule &r) const { return !operator==(r); } RecurrenceRule &operator=(const RecurrenceRule &r); /** Set if recurrence is read-only or can be changed. */ void setReadOnly(bool readOnly); /** Returns true if the recurrence is read-only; false if it can be changed. */ Q_REQUIRED_RESULT bool isReadOnly() const; /** Returns the event's recurrence status. See the enumeration at the top of this file for possible values. */ Q_REQUIRED_RESULT bool recurs() const; void setRecurrenceType(PeriodType period); Q_REQUIRED_RESULT PeriodType recurrenceType() const; /** Turns off recurrence for the event. */ void clear(); /** Returns the recurrence frequency, in terms of the recurrence time period type. */ Q_REQUIRED_RESULT uint frequency() const; /** Sets the recurrence frequency, in terms of the recurrence time period type. */ void setFrequency(int freq); /** Returns the recurrence start date/time. Note that the recurrence does not necessarily occur on the start date/time. For this to happen, it must actually match the rule. */ Q_REQUIRED_RESULT QDateTime startDt() const; /** Sets the recurrence start date/time. Note that setting the start date/time does not make the recurrence occur on that date/time, it simply sets a lower limit to when the recurrences take place (this applies only for the by- rules, not for i.e. an hourly rule where the startDt is the first occurrence). Note that setting @p start to a date-only value does not make an all-day recurrence: to do this, call setAllDay(true). @param start the recurrence's start date and time */ void setStartDt(const QDateTime &start); /** Returns whether the start date has no time associated. All-Day means -- according to rfc2445 -- that the event has no time associate. */ Q_REQUIRED_RESULT bool allDay() const; /** Sets whether the dtstart is all-day (i.e. has no time attached) * * @param allDay Whether start datetime is all-day */ void setAllDay(bool allDay); /** Returns the date and time of the last recurrence. * An invalid date is returned if the recurrence has no end. * @param result if non-null, *result is updated to true if successful, * or false if there is no recurrence or its end date cannot be determined. */ Q_REQUIRED_RESULT QDateTime endDt(bool *result = nullptr) const; /** Sets the date and time of the last recurrence. * @param endDateTime the ending date/time after which to stop recurring. */ void setEndDt(const QDateTime &endDateTime); /** * Returns -1 if the event recurs infinitely, 0 if the end date is set, * otherwise the total number of recurrences, including the initial occurrence. */ Q_REQUIRED_RESULT int duration() const; /** Sets the total number of times the event is to occur, including both the * first and last. */ void setDuration(int duration); /** Returns the number of recurrences up to and including the date/time specified. */ Q_REQUIRED_RESULT int durationTo(const QDateTime &dt) const; /** Returns the number of recurrences up to and including the date specified. */ Q_REQUIRED_RESULT int durationTo(const QDate &date) const; /** Shift the times of the rule so that they appear at the same clock time as before but in a new time zone. The shift is done from a viewing time zone rather than from the actual rule time zone. For example, shifting a rule whose start time is 09:00 America/New York, using an old viewing time zone (@p oldTz) of Europe/London, to a new time zone (@p newTz) of Europe/Paris, will result in the time being shifted from 14:00 (which is the London time of the rule start) to 14:00 Paris time. @param oldTz the time specification which provides the clock times @param newTz the new time specification */ void shiftTimes(const QTimeZone &oldTz, const QTimeZone &newTz); /** Returns true if the date specified is one on which the event will * recur. The start date returns true only if it actually matches the rule. * * @param date date to check * @param timeZone time specification for @p date */ Q_REQUIRED_RESULT bool recursOn(const QDate &date, const QTimeZone &timeZone) const; /** Returns true if the date/time specified is one at which the event will * recur. Times are rounded down to the nearest minute to determine the result. * The start date/time returns true only if it actually matches the rule. * * @param dt the date+time to check for recurrency */ Q_REQUIRED_RESULT bool recursAt(const QDateTime &dt) const; /** Returns true if the date matches the rules. It does not necessarily mean that this is an actual occurrence. In particular, the method does not check if the date is after the end date, or if the frequency interval matches. @param dt the date+time to check for matching the rules */ Q_REQUIRED_RESULT bool dateMatchesRules(const QDateTime &dt) const; /** Returns a list of the times on the specified date at which the * recurrence will occur. The returned times should be interpreted in the * context of @p timeZone. * @param date the date for which to find the recurrence times * @param timeZone time specification for @p date */ Q_REQUIRED_RESULT TimeList recurTimesOn(const QDate &date, const QTimeZone &timeZone) const; /** Returns a list of all the times at which the recurrence will occur * between two specified times. * * There is a (large) maximum limit to the number of times returned. If due to * this limit the list is incomplete, this is indicated by the last entry being * set to an invalid QDateTime value. If you need further values, call the * method again with a start time set to just after the last valid time returned. * @param start inclusive start of interval * @param end inclusive end of interval * @return list of date/time values */ Q_REQUIRED_RESULT QList timesInInterval(const QDateTime &start, const QDateTime &end) const; /** Returns the date and time of the next recurrence, after the specified date/time. * If the recurrence has no time, the next date after the specified date is returned. * @param preDateTime the date/time after which to find the recurrence. * @return date/time of next recurrence, or invalid date if none. */ Q_REQUIRED_RESULT QDateTime getNextDate(const QDateTime &preDateTime) const; /** Returns the date and time of the last previous recurrence, before the specified date/time. * If a time later than 00:00:00 is specified and the recurrence has no time, 00:00:00 on * the specified date is returned if that date recurs. * @param afterDateTime the date/time before which to find the recurrence. * @return date/time of previous recurrence, or invalid date if none. */ Q_REQUIRED_RESULT QDateTime getPreviousDate(const QDateTime &afterDateTime) const; void setBySeconds(const QList &bySeconds); void setByMinutes(const QList &byMinutes); void setByHours(const QList &byHours); void setByDays(const QList &byDays); void setByMonthDays(const QList &byMonthDays); void setByYearDays(const QList &byYearDays); void setByWeekNumbers(const QList &byWeekNumbers); void setByMonths(const QList &byMonths); void setBySetPos(const QList &bySetPos); void setWeekStart(short weekStart); const QList &bySeconds() const; const QList &byMinutes() const; const QList &byHours() const; const QList &byDays() const; const QList &byMonthDays() const; const QList &byYearDays() const; const QList &byWeekNumbers() const; const QList &byMonths() const; const QList &bySetPos() const; short weekStart() const; /** Set the RRULE string for the rule. This is merely stored for future reference. The string is not used in any way by the RecurrenceRule. @param rrule the RRULE string */ void setRRule(const QString &rrule); Q_REQUIRED_RESULT QString rrule() const; void setDirty(); /** Installs an observer. Whenever some setting of this recurrence object is changed, the recurrenceUpdated( Recurrence* ) method of each observer will be called to inform it of changes. @param observer the Recurrence::Observer-derived object, which will be installed as an observer of this object. */ void addObserver(RuleObserver *observer); /** Removes an observer that was added with addObserver. If the given object was not an observer, it does nothing. @param observer the Recurrence::Observer-derived object to be removed from the list of observers of this object. */ void removeObserver(RuleObserver *observer); /** Debug output. */ void dump() const; private: //@cond PRIVATE class Private; Private *const d; //@endcond - friend KCALCORE_EXPORT QDataStream &operator<<(QDataStream &out, const KCalCore::RecurrenceRule *); - friend KCALCORE_EXPORT QDataStream &operator>>(QDataStream &in, const KCalCore::RecurrenceRule *); + friend KCALCORE_EXPORT QDataStream &operator<<(QDataStream &out, const KCalendarCore::RecurrenceRule *); + friend KCALCORE_EXPORT QDataStream &operator>>(QDataStream &in, const KCalendarCore::RecurrenceRule *); }; /** * RecurrenceRule serializer and deserializer. * @since 4.12 */ -KCALCORE_EXPORT QDataStream &operator<<(QDataStream &out, const KCalCore::RecurrenceRule *); -KCALCORE_EXPORT QDataStream &operator>>(QDataStream &in, const KCalCore::RecurrenceRule *); +KCALCORE_EXPORT QDataStream &operator<<(QDataStream &out, const KCalendarCore::RecurrenceRule *); +KCALCORE_EXPORT QDataStream &operator>>(QDataStream &in, const KCalendarCore::RecurrenceRule *); /** * RecurrenceRule::WDayPos serializer and deserializer. * @since 4.12 */ -KCALCORE_EXPORT QDataStream &operator<<(QDataStream &out, const KCalCore::RecurrenceRule::WDayPos &); -KCALCORE_EXPORT QDataStream &operator>>(QDataStream &in, KCalCore::RecurrenceRule::WDayPos &); +KCALCORE_EXPORT QDataStream &operator<<(QDataStream &out, const KCalendarCore::RecurrenceRule::WDayPos &); +KCALCORE_EXPORT QDataStream &operator>>(QDataStream &in, KCalendarCore::RecurrenceRule::WDayPos &); } -Q_DECLARE_TYPEINFO(KCalCore::RecurrenceRule::WDayPos, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(KCalendarCore::RecurrenceRule::WDayPos, Q_MOVABLE_TYPE); #endif diff --git a/src/schedulemessage.cpp b/src/schedulemessage.cpp index 12fcb751a..1b179b974 100644 --- a/src/schedulemessage.cpp +++ b/src/schedulemessage.cpp @@ -1,102 +1,102 @@ /* This file is part of the kcalcore library. Copyright (c) 2001,2004 Cornelius Schumacher Copyright (C) 2004 Reinhold Kainhofer This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "schedulemessage.h" #include "incidencebase.h" #include -using namespace KCalCore; +using namespace KCalendarCore; //@cond PRIVATE -class Q_DECL_HIDDEN KCalCore::ScheduleMessage::Private +class Q_DECL_HIDDEN KCalendarCore::ScheduleMessage::Private { public: Private() {} IncidenceBase::Ptr mIncidence; iTIPMethod mMethod; Status mStatus; QString mError; ~Private() {} }; //@endcond ScheduleMessage::ScheduleMessage(const IncidenceBase::Ptr &incidence, iTIPMethod method, ScheduleMessage::Status status) - : d(new KCalCore::ScheduleMessage::Private) + : d(new KCalendarCore::ScheduleMessage::Private) { d->mIncidence = incidence; d->mMethod = method; d->mStatus = status; } ScheduleMessage::~ScheduleMessage() { delete d; } IncidenceBase::Ptr ScheduleMessage::event() const { return d->mIncidence; } iTIPMethod ScheduleMessage::method() const { return d->mMethod; } QString ScheduleMessage::methodName(iTIPMethod method) { switch (method) { case iTIPPublish: return QStringLiteral("Publish"); case iTIPRequest: return QStringLiteral("Request"); case iTIPRefresh: return QStringLiteral("Refresh"); case iTIPCancel: return QStringLiteral("Cancel"); case iTIPAdd: return QStringLiteral("Add"); case iTIPReply: return QStringLiteral("Reply"); case iTIPCounter: return QStringLiteral("Counter"); case iTIPDeclineCounter: return QStringLiteral("Decline Counter"); default: return QStringLiteral("Unknown"); } } ScheduleMessage::Status ScheduleMessage::status() const { return d->mStatus; } QString ScheduleMessage::error() const { return d->mError; } diff --git a/src/schedulemessage.h b/src/schedulemessage.h index 3ba3c0018..3072915b4 100644 --- a/src/schedulemessage.h +++ b/src/schedulemessage.h @@ -1,124 +1,124 @@ /* This file is part of the kcalcore library. Copyright (c) 2001-2003 Cornelius Schumacher This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KCALCORE_SCHEDULEMESSAGE_H #define KCALCORE_SCHEDULEMESSAGE_H #include "incidencebase.h" #include "kcalcore_export.h" -namespace KCalCore +namespace KCalendarCore { class IncidenceBase; /** iTIP methods. */ enum iTIPMethod { iTIPPublish, /**< Event, to-do, journal or freebusy posting */ iTIPRequest, /**< Event, to-do or freebusy scheduling request */ iTIPReply, /**< Event, to-do or freebusy reply to request */ iTIPAdd, /**< Event, to-do or journal additional property request */ iTIPCancel, /**< Event, to-do or journal cancellation notice */ iTIPRefresh, /**< Event or to-do description update request */ iTIPCounter, /**< Event or to-do submit counter proposal */ iTIPDeclineCounter,/**< Event or to-do decline a counter proposal */ iTIPNoMethod /**< No method */ }; /** @brief A Scheduling message class. This class provides an encapsulation of a scheduling message. It associates an incidence with an iTIPMethod and status information. */ class KCALCORE_EXPORT ScheduleMessage { public: /** Message status. */ enum Status { PublishNew, /**< New message posting */ PublishUpdate, /**< Updated message */ Obsolete, /**< obsolete */ RequestNew, /**< Request new message posting */ RequestUpdate, /**< Request updated message */ Unknown /**< No status */ }; /** A shared pointer to a ScheduleMessage. */ typedef QSharedPointer Ptr; /** Creates a scheduling message with method as defined in iTIPMethod and a status. @param incidence a pointer to a valid Incidence to be associated with this message. @param method an iTIPMethod. @param status a Status. */ ScheduleMessage(const IncidenceBase::Ptr &incidence, iTIPMethod method, Status status); /** Destructor. */ ~ScheduleMessage(); /** Returns the event associated with this message. */ IncidenceBase::Ptr event() const; /** Returns the iTIP method associated with this message. */ Q_REQUIRED_RESULT iTIPMethod method() const; /** Returns a machine-readable (not translatable) name for a iTIP method. @param method an iTIPMethod. */ Q_REQUIRED_RESULT static QString methodName(iTIPMethod method); /** Returns the status of this message. */ Q_REQUIRED_RESULT Status status() const; /** Returns the error message if there is any. */ Q_REQUIRED_RESULT QString error() const; private: //@cond PRIVATE Q_DISABLE_COPY(ScheduleMessage) class Private; Private *const d; //@endcond }; } #endif diff --git a/src/sorting.cpp b/src/sorting.cpp index ea3e5b180..61cd996e5 100644 --- a/src/sorting.cpp +++ b/src/sorting.cpp @@ -1,435 +1,435 @@ /* This file is part of the kcalcore library. Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). All rights reserved. Contact: Alvaro Manera This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "sorting.h" #include "event.h" #include "journal.h" #include "todo.h" #include "utils.h" // PENDING(kdab) Review // The QString::compare() need to be replace by a DUI string comparisons. // See http://qt.gitorious.org/maemo-6-ui-framework/libdui // If not compiled in "meego-mode" should we be using locale compares? -using namespace KCalCore; +using namespace KCalendarCore; /** * How one QDateTime compares with another. * * If any all-day events are involved, comparison of QDateTime values * requires them to be considered as representing time periods. An all-day * instance represents a time period from 00:00:00 to 23:59:59.999 on a given * date, while a date/time instance can be considered to represent a time * period whose start and end times are the same. They may therefore be * earlier or later, or may overlap or be contained one within the other. * * Values may be OR'ed with each other in any combination of 'consecutive' * intervals to represent different types of relationship. * * In the descriptions of the values below, * - s1 = start time of first instance * - e1 = end time of first instance * - s2 = start time of second instance * - e2 = end time of second instance. */ enum DateTimeComparison { Before = 0x01, /**< The first QDateTime is strictly earlier than the second, * i.e. e1 < s2. */ AtStart = 0x02, /**< The first QDateTime starts at the same time as the second, * and ends before the end of the second, * i.e. s1 = s2, e1 < e2. */ Inside = 0x04, /**< The first QDateTime starts after the start of the second, * and ends before the end of the second, * i.e. s1 > s2, e1 < e2. */ AtEnd = 0x08, /**< The first QDateTime starts after the start of the second, * and ends at the same time as the second, * i.e. s1 > s2, e1 = e2. */ After = 0x10, /**< The first QDateTime is strictly later than the second, * i.e. s1 > e2. */ Equal = AtStart | Inside | AtEnd, /**< Simultaneous, i.e. s1 = s2 && e1 = e2. */ Outside = Before | AtStart | Inside | AtEnd | After, /**< The first QDateTime starts before the start of the other, * and ends after the end of the other, * i.e. s1 < s2, e1 > e2. */ StartsAt = AtStart | Inside | AtEnd | After, /**< The first QDateTime starts at the same time as the other, * and ends after the end of the other, * i.e. s1 = s2, e1 > e2. */ EndsAt = Before | AtStart | Inside | AtEnd /**< The first QDateTime starts before the start of the other, * and ends at the same time as the other, * i.e. s1 < s2, e1 = e2. */ }; /** * Compare two QDateTime instances to determine whether they are * simultaneous, earlier or later. * The comparison takes time zones into account: if the two instances have * different time zones, they are first converted to UTC before comparing. * * If both instances are not all-day values, the first instance is considered to * be either simultaneous, earlier or later, and does not overlap. * * If one instance is all-day and the other is a not all-day, the first instance * is either strictly earlier, strictly later, or overlaps. * * If both instance are all-day, they are considered simultaneous if both * their start of day and end of day times are simultaneous with each * other. (Both start and end of day times need to be considered in case a * daylight savings change occurs during that day.) Otherwise, the first instance * can be strictly earlier, earlier but overlapping, later but overlapping, * or strictly later. * * Note that if either instance is a local time (Qt::TimeSpec of Qt::LocalTime), * the result cannot be guaranteed to be correct, since by definition they * contain no information about time zones or daylight savings changes. * * @return DateTimeComparison indicating teh relationship of dt1 to dt2 * @see operator==(), operator!=(), operator<(), operator<=(), operator>=(), operator>() */ DateTimeComparison compare(const QDateTime &dt1, bool isAllDay1, const QDateTime &dt2, bool isAllDay2) { QDateTime start1, start2; //FIXME When secondOccurrence is available in QDateTime //const bool conv = (!d->equalSpec(*other.d) || d->secondOccurrence() != other.d->secondOccurrence()); const bool conv = dt1.timeSpec() != dt2.timeSpec() || (dt1.timeSpec() == Qt::OffsetFromUTC && dt1.offsetFromUtc() != dt2.offsetFromUtc()) || (dt1.timeSpec() == Qt::TimeZone && dt1.timeZone() != dt2.timeZone()); if (conv) { // Different time specs or one is a time which occurs twice, // so convert to UTC before comparing start1 = dt1.toUTC(); start2 = dt2.toUTC(); } else { // Same time specs, so no need to convert to UTC start1 = dt1; start2 = dt2; } if (isAllDay1 || isAllDay2) { // At least one of the instances is date-only, so we need to compare // time periods rather than just times. QDateTime end1, end2; if (conv) { if (isAllDay1) { QDateTime dt(dt1); dt.setTime(QTime(23, 59, 59, 999)); end1 = dt.toUTC(); } else { end1 = start1; } if (isAllDay2) { QDateTime dt(dt2); dt.setTime(QTime(23, 59, 59, 999)); end2 = dt.toUTC(); } else { end2 = start2; } } else { if (isAllDay1) { end1 = QDateTime(dt1.date(), QTime(23, 59, 59, 999), Qt::LocalTime); } else { end1 = dt1; } if (isAllDay2) { end2 = QDateTime(dt2.date(), QTime(23, 59, 59, 999), Qt::LocalTime); } else { end2 = dt2; } } if (start1 == start2) { return !isAllDay1 ? AtStart : (end1 == end2) ? Equal : (end1 < end2) ? static_cast(AtStart | Inside) : static_cast(AtStart | Inside | AtEnd | After); } if (start1 < start2) { return (end1 < start2) ? Before : (end1 == end2) ? static_cast(Before | AtStart | Inside | AtEnd) : (end1 == start2) ? static_cast(Before | AtStart) : (end1 < end2) ? static_cast(Before | AtStart | Inside) : Outside; } else { return (start1 > end2) ? After : (start1 == end2) ? (end1 == end2 ? AtEnd : static_cast(AtEnd | After)) : (end1 == end2) ? static_cast(Inside | AtEnd) : (end1 < end2) ? Inside : static_cast(Inside | AtEnd | After); } } return (start1 == start2) ? Equal : (start1 < start2) ? Before : After; } -bool KCalCore::Events::startDateLessThan(const Event::Ptr &e1, const Event::Ptr &e2) +bool KCalendarCore::Events::startDateLessThan(const Event::Ptr &e1, const Event::Ptr &e2) { DateTimeComparison res = compare(e1->dtStart(), e1->allDay(), e2->dtStart(), e2->allDay()); if (res == Equal) { return Events::summaryLessThan(e1, e2); } else { return (res & Before || res & AtStart); } } -bool KCalCore::Events::startDateMoreThan(const Event::Ptr &e1, const Event::Ptr &e2) +bool KCalendarCore::Events::startDateMoreThan(const Event::Ptr &e1, const Event::Ptr &e2) { DateTimeComparison res = compare(e1->dtStart(), e1->allDay(), e2->dtStart(), e2->allDay()); if (res == Equal) { return Events::summaryMoreThan(e1, e2); } else { return (res & After || res & AtEnd); } } -bool KCalCore::Events::summaryLessThan(const Event::Ptr &e1, const Event::Ptr &e2) +bool KCalendarCore::Events::summaryLessThan(const Event::Ptr &e1, const Event::Ptr &e2) { return QString::compare(e1->summary(), e2->summary(), Qt::CaseInsensitive) < 0; } -bool KCalCore::Events::summaryMoreThan(const Event::Ptr &e1, const Event::Ptr &e2) +bool KCalendarCore::Events::summaryMoreThan(const Event::Ptr &e1, const Event::Ptr &e2) { return QString::compare(e1->summary(), e2->summary(), Qt::CaseInsensitive) > 0; } -bool KCalCore::Events::endDateLessThan(const Event::Ptr &e1, const Event::Ptr &e2) +bool KCalendarCore::Events::endDateLessThan(const Event::Ptr &e1, const Event::Ptr &e2) { DateTimeComparison res = compare(e1->dtEnd(), e1->allDay(), e2->dtEnd(), e2->allDay()); if (res == Equal) { return Events::summaryLessThan(e1, e2); } else { return (res & Before || res & AtStart); } } -bool KCalCore::Events::endDateMoreThan(const Event::Ptr &e1, const Event::Ptr &e2) +bool KCalendarCore::Events::endDateMoreThan(const Event::Ptr &e1, const Event::Ptr &e2) { DateTimeComparison res = compare(e1->dtEnd(), e1->allDay(), e2->dtEnd(), e2->allDay()); if (res == Equal) { return Events::summaryMoreThan(e1, e2); } else { return (res & After || res & AtEnd); } } -bool KCalCore::Journals::dateLessThan(const Journal::Ptr &j1, const Journal::Ptr &j2) +bool KCalendarCore::Journals::dateLessThan(const Journal::Ptr &j1, const Journal::Ptr &j2) { DateTimeComparison res = compare(j1->dtStart(), j1->allDay(), j2->dtStart(), j2->allDay()); return (res & Before || res & AtStart); } -bool KCalCore::Journals::dateMoreThan(const Journal::Ptr &j1, const Journal::Ptr &j2) +bool KCalendarCore::Journals::dateMoreThan(const Journal::Ptr &j1, const Journal::Ptr &j2) { DateTimeComparison res = compare(j1->dtStart(), j1->allDay(), j2->dtStart(), j2->allDay()); return (res & After || res & AtEnd); } -bool KCalCore::Journals::summaryLessThan(const Journal::Ptr &j1, const Journal::Ptr &j2) +bool KCalendarCore::Journals::summaryLessThan(const Journal::Ptr &j1, const Journal::Ptr &j2) { return QString::compare(j1->summary(), j2->summary(), Qt::CaseInsensitive) < 0; } -bool KCalCore::Journals::summaryMoreThan(const Journal::Ptr &j1, const Journal::Ptr &j2) +bool KCalendarCore::Journals::summaryMoreThan(const Journal::Ptr &j1, const Journal::Ptr &j2) { return QString::compare(j1->summary(), j2->summary(), Qt::CaseInsensitive) > 0; } -bool KCalCore::Todos::startDateLessThan(const Todo::Ptr &t1, const Todo::Ptr &t2) +bool KCalendarCore::Todos::startDateLessThan(const Todo::Ptr &t1, const Todo::Ptr &t2) { DateTimeComparison res = compare(t1->dtStart(), t1->allDay(), t2->dtStart(), t2->allDay()); if (res == Equal) { return Todos::summaryLessThan(t1, t2); } else { return (res & Before || res & AtStart); } } -bool KCalCore::Todos::startDateMoreThan(const Todo::Ptr &t1, const Todo::Ptr &t2) +bool KCalendarCore::Todos::startDateMoreThan(const Todo::Ptr &t1, const Todo::Ptr &t2) { DateTimeComparison res = compare(t1->dtStart(), t1->allDay(), t2->dtStart(), t2->allDay()); if (res == Equal) { return Todos::summaryMoreThan(t1, t2); } else { return (res & After || res & AtEnd); } } -bool KCalCore::Todos::dueDateLessThan(const Todo::Ptr &t1, const Todo::Ptr &t2) +bool KCalendarCore::Todos::dueDateLessThan(const Todo::Ptr &t1, const Todo::Ptr &t2) { DateTimeComparison res = compare(t1->dtDue(), t1->allDay(), t2->dtDue(), t2->allDay()); if (res == Equal) { return Todos::summaryLessThan(t1, t2); } else { return (res & Before || res & AtStart); } } -bool KCalCore::Todos::dueDateMoreThan(const Todo::Ptr &t1, const Todo::Ptr &t2) +bool KCalendarCore::Todos::dueDateMoreThan(const Todo::Ptr &t1, const Todo::Ptr &t2) { DateTimeComparison res = compare(t1->dtDue(), t1->allDay(), t2->dtDue(), t2->allDay()); if (res == Equal) { return Todos::summaryMoreThan(t1, t2); } else { return (res & After || res & AtEnd); } } -bool KCalCore::Todos::priorityLessThan(const Todo::Ptr &t1, const Todo::Ptr &t2) +bool KCalendarCore::Todos::priorityLessThan(const Todo::Ptr &t1, const Todo::Ptr &t2) { if (t1->priority() < t2->priority()) { return true; } else if (t1->priority() == t2->priority()) { return Todos::summaryLessThan(t1, t2); } else { return false; } } -bool KCalCore::Todos::priorityMoreThan(const Todo::Ptr &t1, const Todo::Ptr &t2) +bool KCalendarCore::Todos::priorityMoreThan(const Todo::Ptr &t1, const Todo::Ptr &t2) { if (t1->priority() > t2->priority()) { return true; } else if (t1->priority() == t2->priority()) { return Todos::summaryMoreThan(t1, t2); } else { return false; } } -bool KCalCore::Todos::percentLessThan(const Todo::Ptr &t1, const Todo::Ptr &t2) +bool KCalendarCore::Todos::percentLessThan(const Todo::Ptr &t1, const Todo::Ptr &t2) { if (t1->percentComplete() < t2->percentComplete()) { return true; } else if (t1->percentComplete() == t2->percentComplete()) { return Todos::summaryLessThan(t1, t2); } else { return false; } } -bool KCalCore::Todos::percentMoreThan(const Todo::Ptr &t1, const Todo::Ptr &t2) +bool KCalendarCore::Todos::percentMoreThan(const Todo::Ptr &t1, const Todo::Ptr &t2) { if (t1->percentComplete() > t2->percentComplete()) { return true; } else if (t1->percentComplete() == t2->percentComplete()) { return Todos::summaryMoreThan(t1, t2); } else { return false; } } -bool KCalCore::Todos::summaryLessThan(const Todo::Ptr &t1, const Todo::Ptr &t2) +bool KCalendarCore::Todos::summaryLessThan(const Todo::Ptr &t1, const Todo::Ptr &t2) { return QString::compare(t1->summary(), t2->summary(), Qt::CaseInsensitive) < 0; } -bool KCalCore::Todos::summaryMoreThan(const Todo::Ptr &t1, const Todo::Ptr &t2) +bool KCalendarCore::Todos::summaryMoreThan(const Todo::Ptr &t1, const Todo::Ptr &t2) { return QString::compare(t1->summary(), t2->summary(), Qt::CaseInsensitive) > 0; } -bool KCalCore::Todos::createdLessThan(const Todo::Ptr &t1, const Todo::Ptr &t2) +bool KCalendarCore::Todos::createdLessThan(const Todo::Ptr &t1, const Todo::Ptr &t2) { DateTimeComparison res = compare(t1->created(), t1->allDay(), t2->created(), t2->allDay()); if (res == Equal) { return Todos::summaryLessThan(t1, t2); } else { return (res & Before || res & AtStart); } } -bool KCalCore::Todos::createdMoreThan(const Todo::Ptr &t1, const Todo::Ptr &t2) +bool KCalendarCore::Todos::createdMoreThan(const Todo::Ptr &t1, const Todo::Ptr &t2) { DateTimeComparison res = compare(t1->created(), t1->allDay(), t2->created(), t2->allDay()); if (res == Equal) { return Todos::summaryMoreThan(t1, t2); } else { return (res & After || res & AtEnd); } } -bool KCalCore::Incidences::dateLessThan(const Incidence::Ptr &i1, +bool KCalendarCore::Incidences::dateLessThan(const Incidence::Ptr &i1, const Incidence::Ptr &i2) { DateTimeComparison res = compare(i1->dateTime(Incidence::RoleSort), i1->allDay(), i2->dateTime(Incidence::RoleSort), i2->allDay()); if (res == Equal) { return Incidences::summaryLessThan(i1, i2); } else { return (res & Before || res & AtStart); } } -bool KCalCore::Incidences::dateMoreThan(const Incidence::Ptr &i1, +bool KCalendarCore::Incidences::dateMoreThan(const Incidence::Ptr &i1, const Incidence::Ptr &i2) { DateTimeComparison res = compare(i1->dateTime(Incidence::RoleSort), i1->allDay(), i2->dateTime(Incidence::RoleSort), i2->allDay()); if (res == Equal) { return Incidences::summaryMoreThan(i1, i2); } else { return (res & After || res & AtEnd); } } -bool KCalCore::Incidences::createdLessThan(const Incidence::Ptr &i1, +bool KCalendarCore::Incidences::createdLessThan(const Incidence::Ptr &i1, const Incidence::Ptr &i2) { DateTimeComparison res = compare(i1->created(), i1->allDay(), i2->created(), i2->allDay()); if (res == Equal) { return Incidences::summaryLessThan(i1, i2); } else { return (res & Before || res & AtStart); } } -bool KCalCore::Incidences::createdMoreThan(const Incidence::Ptr &i1, +bool KCalendarCore::Incidences::createdMoreThan(const Incidence::Ptr &i1, const Incidence::Ptr &i2) { DateTimeComparison res = compare(i1->created(), i1->allDay(), i2->created(), i2->allDay()); if (res == Equal) { return Incidences::summaryMoreThan(i1, i2); } else { return (res & After || res & AtEnd); } } -bool KCalCore::Incidences::summaryLessThan(const Incidence::Ptr &i1, +bool KCalendarCore::Incidences::summaryLessThan(const Incidence::Ptr &i1, const Incidence::Ptr &i2) { return QString::compare(i1->summary(), i2->summary(), Qt::CaseInsensitive) < 0; } -bool KCalCore::Incidences::summaryMoreThan(const Incidence::Ptr &i1, +bool KCalendarCore::Incidences::summaryMoreThan(const Incidence::Ptr &i1, const Incidence::Ptr &i2) { return QString::compare(i1->summary(), i2->summary(), Qt::CaseInsensitive) > 0; } diff --git a/src/sorting.h b/src/sorting.h index 44f4fd635..6075b8dab 100644 --- a/src/sorting.h +++ b/src/sorting.h @@ -1,100 +1,100 @@ /* This file is part of the kcalcore library. Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). All rights reserved. Contact: Alvaro Manera This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KCALCORE_SORTING_H #define KCALCORE_SORTING_H #include "event.h" #include "journal.h" #include "todo.h" #include "freebusy.h" #include "person.h" #include "kcalcore_export.h" -namespace KCalCore { +namespace KCalendarCore { namespace Events { KCALCORE_EXPORT bool startDateLessThan(const Event::Ptr &e1, const Event::Ptr &e2); KCALCORE_EXPORT bool summaryLessThan(const Event::Ptr &e1, const Event::Ptr &e2); KCALCORE_EXPORT bool summaryMoreThan(const Event::Ptr &e1, const Event::Ptr &e2); KCALCORE_EXPORT bool startDateMoreThan(const Event::Ptr &e1, const Event::Ptr &e2); KCALCORE_EXPORT bool endDateLessThan(const Event::Ptr &e1, const Event::Ptr &e2); KCALCORE_EXPORT bool endDateMoreThan(const Event::Ptr &e1, const Event::Ptr &e2); } namespace Todos { KCALCORE_EXPORT bool startDateLessThan(const Todo::Ptr &t1, const Todo::Ptr &t2); KCALCORE_EXPORT bool startDateMoreThan(const Todo::Ptr &t1, const Todo::Ptr &t2); KCALCORE_EXPORT bool dueDateLessThan(const Todo::Ptr &t1, const Todo::Ptr &t2); KCALCORE_EXPORT bool dueDateMoreThan(const Todo::Ptr &t1, const Todo::Ptr &t2); KCALCORE_EXPORT bool priorityLessThan(const Todo::Ptr &t1, const Todo::Ptr &t2); KCALCORE_EXPORT bool priorityMoreThan(const Todo::Ptr &t1, const Todo::Ptr &t2); KCALCORE_EXPORT bool percentLessThan(const Todo::Ptr &t1, const Todo::Ptr &t2); KCALCORE_EXPORT bool percentMoreThan(const Todo::Ptr &t1, const Todo::Ptr &t2); KCALCORE_EXPORT bool summaryLessThan(const Todo::Ptr &t1, const Todo::Ptr &t2); KCALCORE_EXPORT bool summaryMoreThan(const Todo::Ptr &t1, const Todo::Ptr &t2); KCALCORE_EXPORT bool createdLessThan(const Todo::Ptr &t1, const Todo::Ptr &t2); KCALCORE_EXPORT bool createdMoreThan(const Todo::Ptr &t1, const Todo::Ptr &t2); } namespace Journals { KCALCORE_EXPORT bool dateLessThan(const Journal::Ptr &j1, const Journal::Ptr &j2); KCALCORE_EXPORT bool dateMoreThan(const Journal::Ptr &j1, const Journal::Ptr &j2); KCALCORE_EXPORT bool summaryLessThan(const Journal::Ptr &j1, const Journal::Ptr &j2); KCALCORE_EXPORT bool summaryMoreThan(const Journal::Ptr &j1, const Journal::Ptr &j2); } namespace Incidences { KCALCORE_EXPORT bool dateLessThan(const Incidence::Ptr &i1, const Incidence::Ptr &i2); KCALCORE_EXPORT bool dateMoreThan(const Incidence::Ptr &i1, const Incidence::Ptr &i2); KCALCORE_EXPORT bool createdLessThan(const Incidence::Ptr &i1, const Incidence::Ptr &i2); KCALCORE_EXPORT bool createdMoreThan(const Incidence::Ptr &i1, const Incidence::Ptr &i2); KCALCORE_EXPORT bool summaryLessThan(const Incidence::Ptr &i1, const Incidence::Ptr &i2); KCALCORE_EXPORT bool summaryMoreThan(const Incidence::Ptr &i1, const Incidence::Ptr &i2); } } #endif diff --git a/src/todo.cpp b/src/todo.cpp index 0379f982f..95529555d 100644 --- a/src/todo.cpp +++ b/src/todo.cpp @@ -1,577 +1,577 @@ /* This file is part of the kcalcore library. Copyright (c) 2001-2003 Cornelius Schumacher Copyright (C) 2009 Allen Winter This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the Todo class. @brief Provides a To-do in the sense of RFC2445. @author Cornelius Schumacher \ @author Allen Winter \ */ #include "todo.h" #include "visitor.h" #include "recurrence.h" #include "utils.h" #include "kcalcore_debug.h" #include -using namespace KCalCore; +using namespace KCalendarCore; /** Private class that helps to provide binary compatibility between releases. @internal */ //@cond PRIVATE -class Q_DECL_HIDDEN KCalCore::Todo::Private +class Q_DECL_HIDDEN KCalendarCore::Todo::Private { public: Private() {} - Private(const KCalCore::Todo::Private &other) + Private(const KCalendarCore::Todo::Private &other) { init(other); } - void init(const KCalCore::Todo::Private &other); + void init(const KCalendarCore::Todo::Private &other); QDateTime mDtDue; // to-do due date (if there is one) // ALSO the first occurrence of a recurring to-do QDateTime mDtRecurrence; // next occurrence (for recurring to-dos) QDateTime mCompleted; // to-do completion date (if it has been completed) int mPercentComplete = 0; // to-do percent complete [0,100] /** Returns true if the todo got a new date, else false will be returned. */ bool recurTodo(Todo *todo); }; -void KCalCore::Todo::Private::init(const KCalCore::Todo::Private &other) +void KCalendarCore::Todo::Private::init(const KCalendarCore::Todo::Private &other) { mDtDue = other.mDtDue; mDtRecurrence = other.mDtRecurrence; mCompleted = other.mCompleted; mPercentComplete = other.mPercentComplete; } //@endcond Todo::Todo() - : d(new KCalCore::Todo::Private) + : d(new KCalendarCore::Todo::Private) { } Todo::Todo(const Todo &other) : Incidence(other), - d(new KCalCore::Todo::Private(*other.d)) + d(new KCalendarCore::Todo::Private(*other.d)) { } Todo::Todo(const Incidence &other) : Incidence(other) - , d(new KCalCore::Todo::Private) + , d(new KCalendarCore::Todo::Private) { } Todo::~Todo() { delete d; } Todo *Todo::clone() const { return new Todo(*this); } IncidenceBase &Todo::assign(const IncidenceBase &other) { if (&other != this) { Incidence::assign(other); const Todo *t = static_cast(&other); d->init(*(t->d)); } return *this; } bool Todo::equals(const IncidenceBase &todo) const { if (!Incidence::equals(todo)) { return false; } else { // If they weren't the same type IncidenceBase::equals would had returned false already const Todo *t = static_cast(&todo); return ((dtDue() == t->dtDue()) || (!dtDue().isValid() && !t->dtDue().isValid())) && hasDueDate() == t->hasDueDate() && hasStartDate() == t->hasStartDate() && ((completed() == t->completed()) || (!completed().isValid() && !t->completed().isValid())) && hasCompletedDate() == t->hasCompletedDate() && percentComplete() == t->percentComplete(); } } Incidence::IncidenceType Todo::type() const { return TypeTodo; } QByteArray Todo::typeStr() const { return QByteArrayLiteral("Todo"); } void Todo::setDtDue(const QDateTime &dtDue, bool first) { startUpdates(); //int diffsecs = d->mDtDue.secsTo(dtDue); /*if (mReadOnly) return; const Alarm::List& alarms = alarms(); for (Alarm *alarm = alarms.first(); alarm; alarm = alarms.next()) { if (alarm->enabled()) { alarm->setTime(alarm->time().addSecs(diffsecs)); } }*/ if (recurs() && !first) { d->mDtRecurrence = dtDue; } else { d->mDtDue = dtDue; } if (recurs() && dtDue.isValid() && (!dtStart().isValid() || dtDue < recurrence()->startDateTime())) { qCDebug(KCALCORE_LOG) << "To-do recurrences are now calculated against DTSTART. Fixing legacy to-do."; setDtStart(dtDue); } /*const Alarm::List& alarms = alarms(); for (Alarm *alarm = alarms.first(); alarm; alarm = alarms.next()) alarm->setAlarmStart(d->mDtDue);*/ setFieldDirty(FieldDtDue); endUpdates(); } QDateTime Todo::dtDue(bool first) const { if (!hasDueDate()) { return QDateTime(); } const QDateTime start = IncidenceBase::dtStart(); if (recurs() && !first && d->mDtRecurrence.isValid()) { if (start.isValid()) { // This is the normal case, recurring to-dos have a valid DTSTART. const qint64 duration = start.daysTo(d->mDtDue); QDateTime dt = d->mDtRecurrence.addDays(duration); dt.setTime(d->mDtDue.time()); return dt; } else { // This is a legacy case, where recurrence was calculated against DTDUE return d->mDtRecurrence; } } return d->mDtDue; } bool Todo::hasDueDate() const { return d->mDtDue.isValid(); } bool Todo::hasStartDate() const { return IncidenceBase::dtStart().isValid(); } QDateTime Todo::dtStart() const { return dtStart(/*first=*/false); } QDateTime Todo::dtStart(bool first) const { if (!hasStartDate()) { return QDateTime(); } if (recurs() && !first && d->mDtRecurrence.isValid()) { return d->mDtRecurrence; } else { return IncidenceBase::dtStart(); } } bool Todo::isCompleted() const { return d->mPercentComplete == 100 || status() == StatusCompleted; } void Todo::setCompleted(bool completed) { update(); if (completed) { d->mPercentComplete = 100; setStatus(StatusCompleted); } else { d->mPercentComplete = 0; d->mCompleted = QDateTime(); setStatus(StatusNone); } setFieldDirty(FieldCompleted); setFieldDirty(FieldStatus); updated(); } QDateTime Todo::completed() const { if (hasCompletedDate()) { return d->mCompleted; } else { return QDateTime(); } } void Todo::setCompleted(const QDateTime &completed) { update(); if (!d->recurTodo(this)) { d->mPercentComplete = 100; d->mCompleted = completed.toUTC(); setFieldDirty(FieldCompleted); } updated(); } bool Todo::hasCompletedDate() const { return d->mCompleted.isValid(); } int Todo::percentComplete() const { return d->mPercentComplete; } void Todo::setPercentComplete(int percent) { if (percent > 100) { percent = 100; } else if (percent < 0) { percent = 0; } update(); d->mPercentComplete = percent; if (percent != 100) { d->mCompleted = QDateTime(); } setFieldDirty(FieldPercentComplete); updated(); } bool Todo::isInProgress(bool first) const { if (isOverdue()) { return false; } if (d->mPercentComplete > 0) { return true; } if (hasStartDate() && hasDueDate()) { if (allDay()) { QDate currDate = QDate::currentDate(); if (dtStart(first).date() <= currDate && currDate < dtDue(first).date()) { return true; } } else { QDateTime currDate = QDateTime::currentDateTimeUtc(); if (dtStart(first) <= currDate && currDate < dtDue(first)) { return true; } } } return false; } bool Todo::isOpenEnded() const { if (!hasDueDate() && !isCompleted()) { return true; } return false; } bool Todo::isNotStarted(bool first) const { if (d->mPercentComplete > 0) { return false; } if (!hasStartDate()) { return false; } if (allDay()) { if (dtStart(first).date() >= QDate::currentDate()) { return false; } } else { if (dtStart(first) >= QDateTime::currentDateTimeUtc()) { return false; } } return true; } void Todo::shiftTimes(const QTimeZone &oldZone, const QTimeZone &newZone) { Incidence::shiftTimes(oldZone, newZone); d->mDtDue = d->mDtDue.toTimeZone(oldZone); d->mDtDue.setTimeZone(newZone); if (recurs()) { d->mDtRecurrence = d->mDtRecurrence.toTimeZone(oldZone); d->mDtRecurrence.setTimeZone(newZone); } if (hasCompletedDate()) { d->mCompleted = d->mCompleted.toTimeZone(oldZone); d->mCompleted.setTimeZone(newZone); } } void Todo::setDtRecurrence(const QDateTime &dt) { d->mDtRecurrence = dt; setFieldDirty(FieldRecurrence); } QDateTime Todo::dtRecurrence() const { return d->mDtRecurrence.isValid() ? d->mDtRecurrence : d->mDtDue; } bool Todo::recursOn(const QDate &date, const QTimeZone &timeZone) const { QDate today = QDate::currentDate(); return Incidence::recursOn(date, timeZone) && !(date < today && d->mDtRecurrence.date() < today && d->mDtRecurrence > recurrence()->startDateTime()); } bool Todo::isOverdue() const { if (!dtDue().isValid()) { return false; // if it's never due, it can't be overdue } const bool inPast = allDay() ? dtDue().date() < QDate::currentDate() : dtDue() < QDateTime::currentDateTimeUtc(); return inPast && !isCompleted(); } void Todo::setAllDay(bool allday) { if (allday != allDay() && !mReadOnly) { if (hasDueDate()) { setFieldDirty(FieldDtDue); } Incidence::setAllDay(allday); } } //@cond PRIVATE bool Todo::Private::recurTodo(Todo *todo) { if (todo && todo->recurs()) { Recurrence *r = todo->recurrence(); const QDateTime recurrenceEndDateTime = r->endDateTime(); QDateTime nextOccurrenceDateTime = r->getNextDateTime(todo->dtStart()); if ((r->duration() == -1 || (nextOccurrenceDateTime.isValid() && recurrenceEndDateTime.isValid() && nextOccurrenceDateTime <= recurrenceEndDateTime))) { // We convert to the same timeSpec so we get the correct .date() const auto rightNow = QDateTime::currentDateTimeUtc().toTimeZone(nextOccurrenceDateTime.timeZone()); const bool isDateOnly = todo->allDay(); /* Now we search for the occurrence that's _after_ the currentUtcDateTime, or * if it's dateOnly, the occurrrence that's _during or after today_. * The reason we use "<" for date only, but "<=" for ocurrences with time is that * if it's date only, the user can still complete that ocurrence today, so that's * the current ocurrence that needs completing. */ while (!todo->recursAt(nextOccurrenceDateTime) || (!isDateOnly && nextOccurrenceDateTime <= rightNow) || (isDateOnly && nextOccurrenceDateTime.date() < rightNow.date())) { if (!nextOccurrenceDateTime.isValid() || (nextOccurrenceDateTime > recurrenceEndDateTime && r->duration() != -1)) { return false; } nextOccurrenceDateTime = r->getNextDateTime(nextOccurrenceDateTime); } todo->setDtRecurrence(nextOccurrenceDateTime); todo->setCompleted(false); todo->setRevision(todo->revision() + 1); return true; } } return false; } //@endcond bool Todo::accept(Visitor &v, const IncidenceBase::Ptr &incidence) { return v.visit(incidence.staticCast()); } QDateTime Todo::dateTime(DateTimeRole role) const { switch (role) { case RoleAlarmStartOffset: return dtStart(); case RoleAlarmEndOffset: return dtDue(); case RoleSort: // Sorting to-dos first compares dtDue, then dtStart if // dtDue doesn't exist return hasDueDate() ? dtDue() : dtStart(); case RoleCalendarHashing: return dtDue(); case RoleStartTimeZone: return dtStart(); case RoleEndTimeZone: return dtDue(); case RoleEndRecurrenceBase: return dtDue(); case RoleDisplayStart: case RoleDisplayEnd: return dtDue().isValid() ? dtDue() : dtStart(); case RoleAlarm: if (alarms().isEmpty()) { return QDateTime(); } else { Alarm::Ptr alarm = alarms().at(0); if (alarm->hasStartOffset() && hasStartDate()) { return dtStart(); } else if (alarm->hasEndOffset() && hasDueDate()) { return dtDue(); } else { // The application shouldn't add alarms on to-dos without dates. return QDateTime(); } } case RoleRecurrenceStart: if (dtStart().isValid()) { return dtStart(); } return dtDue(); //For the sake of backwards compatibility //where we calculated recurrences based on dtDue case RoleEnd: return dtDue(); default: return QDateTime(); } } void Todo::setDateTime(const QDateTime &dateTime, DateTimeRole role) { switch (role) { case RoleDnD: setDtDue(dateTime); break; case RoleEnd: setDtDue(dateTime, true); break; default: qCDebug(KCALCORE_LOG) << "Unhandled role" << role; } } void Todo::virtual_hook(VirtualHook id, void *data) { Q_UNUSED(id); Q_UNUSED(data); } QLatin1String Todo::mimeType() const { return Todo::todoMimeType(); } QLatin1String Todo::todoMimeType() { return QLatin1String("application/x-vnd.akonadi.calendar.todo"); } QLatin1String Todo::iconName(const QDateTime &recurrenceId) const { const bool usesCompletedTaskPixmap = isCompleted() || (recurs() && recurrenceId.isValid() && (recurrenceId < dtDue(false))); if (usesCompletedTaskPixmap) { return QLatin1String("task-complete"); } else { return QLatin1String("view-calendar-tasks"); } } void Todo::serialize(QDataStream &out) const { Incidence::serialize(out); serializeQDateTimeAsKDateTime(out, d->mDtDue); serializeQDateTimeAsKDateTime(out, d->mDtRecurrence); serializeQDateTimeAsKDateTime(out, d->mCompleted); out << d->mPercentComplete; } void Todo::deserialize(QDataStream &in) { Incidence::deserialize(in); deserializeKDateTimeAsQDateTime(in, d->mDtDue); deserializeKDateTimeAsQDateTime(in, d->mDtRecurrence); deserializeKDateTimeAsQDateTime(in, d->mCompleted); in >> d->mPercentComplete; } bool Todo::supportsGroupwareCommunication() const { return true; } diff --git a/src/todo.h b/src/todo.h index b5a2fdab0..8d1de35e6 100644 --- a/src/todo.h +++ b/src/todo.h @@ -1,367 +1,367 @@ /* This file is part of the kcalcore library. Copyright (c) 2001-2003 Cornelius Schumacher Copyright (C) 2009 Allen Winter This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the Todo class. @author Cornelius Schumacher \ @author Allen Winter \ */ #ifndef KCALCORE_TODO_H #define KCALCORE_TODO_H #include "kcalcore_export.h" #include "incidence.h" -namespace KCalCore +namespace KCalendarCore { /** @brief Provides a To-do in the sense of RFC2445. */ class KCALCORE_EXPORT Todo : public Incidence { public: /** A shared pointer to a Todo object. */ typedef QSharedPointer Ptr; /** List of to-dos. */ typedef QVector List; ///@cond PRIVATE // needed for Akonadi polymorphic payload support typedef Incidence SuperClass; ///@endcond /** Constructs an empty to-do. */ Todo(); /** Copy constructor. @param other is the to-do to copy. */ Todo(const Todo &other); /** Costructs a todo out of an incidence This constructs allows to make it easy to create a todo from an event. @param other is the incidence to copy. @since 4.14 */ Todo(const Incidence &other); //krazy:exclude=explicit (copy ctor) /** Destroys a to-do. */ ~Todo() override; /** @copydoc IncidenceBase::type() */ Q_REQUIRED_RESULT IncidenceType type() const override; /** @copydoc IncidenceBase::typeStr() */ Q_REQUIRED_RESULT QByteArray typeStr() const override; /** Returns an exact copy of this todo. The returned object is owned by the caller. @return A pointer to a Todo containing an exact copy of this object. */ Todo *clone() const override; /** Sets due date and time. @param dtDue The due date/time. @param first If true and the todo recurs, the due date of the first occurrence will be returned. If false and recurrent, the date of the current occurrence will be returned. If non-recurrent, the normal due date will be returned. */ void setDtDue(const QDateTime &dtDue, bool first = false); /** Returns the todo due datetime. @param first If true and the todo recurs, the due datetime of the first occurrence will be returned. If false and recurrent, the datetime of the current occurrence will be returned. If non-recurrent, the normal due datetime will be returned. @return A QDateTime containing the todo due datetime. */ Q_REQUIRED_RESULT QDateTime dtDue(bool first = false) const; /** Returns if the todo has a due datetime. @return true if the todo has a due datetime; false otherwise. */ Q_REQUIRED_RESULT bool hasDueDate() const; /** Returns if the todo has a start datetime. @return true if the todo has a start datetime; false otherwise. */ Q_REQUIRED_RESULT bool hasStartDate() const; /** @copydoc IncidenceBase::dtStart() */ Q_REQUIRED_RESULT QDateTime dtStart() const override; /** Returns the start datetime of the todo. @param first If true, the start datetime of the todo will be returned; also, if the todo recurs, the start datetime of the first occurrence will be returned. If false and the todo recurs, the relative start datetime will be returned, based on the datetime returned by dtRecurrence(). @return A QDateTime for the start datetime of the todo. */ Q_REQUIRED_RESULT QDateTime dtStart(bool first) const; /** Returns if the todo is 100% completed. @return true if the todo is 100% completed; false otherwise. @see isOverdue, isInProgress(), isOpenEnded(), isNotStarted(bool), setCompleted(), percentComplete() */ Q_REQUIRED_RESULT bool isCompleted() const; /** Sets completed state. @param completed If true set completed state to 100%, if false set completed state to 0%. @see isCompleted(), percentComplete() */ void setCompleted(bool completed); /** Returns what percentage of the to-do is completed. @return The percentage complete of the to-do as an integer between 0 and 100, inclusive. @see setPercentComplete(), isCompleted() */ Q_REQUIRED_RESULT int percentComplete() const; /** Sets what percentage of the to-do is completed. Valid values are in the range from 0 to 100. @param percent is the completion percentage, which as integer value between 0 and 100, inclusive. @see isCompleted(), setCompleted() */ void setPercentComplete(int percent); /** Returns the to-do was completion datetime. @return A QDateTime for the completeion datetime of the to-do. @see hasCompletedDate() */ Q_REQUIRED_RESULT QDateTime completed() const; /** Sets date and time of completion. @param completeDate is the to-do completion date. @see completed(), hasCompletedDate() */ void setCompleted(const QDateTime &completeDate); /** Returns if the to-do has a completion datetime. @return true if the to-do has a date associated with completion; false otherwise. @see setCompleted(), completed() */ bool hasCompletedDate() const; /** Returns true, if the to-do is in-progress (started, or >0% completed); otherwise return false. If the to-do is overdue, then it is not considered to be in-progress. @param first If true, the start and due dates of the todo will be used; also, if the todo recurs, the start date and due date of the first occurrence will be used. If false and the todo recurs, the relative start and due dates will be used, based on the date returned by dtRecurrence(). @see isOverdue(), isCompleted(), isOpenEnded(), isNotStarted(bool) */ Q_REQUIRED_RESULT bool isInProgress(bool first) const; /** Returns true, if the to-do is open-ended (no due date); false otherwise. @see isOverdue(), isCompleted(), isInProgress(), isNotStarted(bool) */ Q_REQUIRED_RESULT bool isOpenEnded() const; /** Returns true, if the to-do has yet to be started (no start date and 0% completed); otherwise return false. @param first If true, the start date of the todo will be used; also, if the todo recurs, the start date of the first occurrence will be used. If false and the todo recurs, the relative start date will be used, based on the date returned by dtRecurrence(). @see isOverdue(), isCompleted(), isInProgress(), isOpenEnded() */ Q_REQUIRED_RESULT bool isNotStarted(bool first) const; /** @copydoc IncidenceBase::shiftTimes() */ void shiftTimes(const QTimeZone &oldZone, const QTimeZone &newZone) override; /** @copydoc IncidenceBase::setAllDay(). */ void setAllDay(bool allDay) override; /** Sets the due date/time of the current occurrence if recurrent. @param dt is the */ void setDtRecurrence(const QDateTime &dt); /** Returns the due date/time of the current occurrence if recurrent. */ Q_REQUIRED_RESULT QDateTime dtRecurrence() const; /** Returns true if the @p date specified is one on which the to-do will recur. Todos are a special case, hence the overload. It adds an extra check, which make it return false if there's an occurrence between the recur start and today. @param date is the date to check. @param timeZone is the time zone */ bool recursOn(const QDate &date, const QTimeZone &timeZone) const override; /** Returns true if this todo is overdue (e.g. due date is lower than today and not completed), else false. @see isCompleted(), isInProgress(), isOpenEnded(), isNotStarted(bool) */ bool isOverdue() const; /** @copydoc IncidenceBase::dateTime() */ Q_REQUIRED_RESULT QDateTime dateTime(DateTimeRole role) const override; /** @copydoc IncidenceBase::setDateTime() */ void setDateTime(const QDateTime &dateTime, DateTimeRole role) override; /** @copydoc IncidenceBase::mimeType() */ Q_REQUIRED_RESULT QLatin1String mimeType() const override; /** @copydoc Incidence::iconName() */ Q_REQUIRED_RESULT QLatin1String iconName(const QDateTime &recurrenceId = {}) const override; /** @copydoc Incidence::supportsGroupwareCommunication() */ bool supportsGroupwareCommunication() const override; /** - Returns the Akonadi specific sub MIME type of a KCalCore::Todo. + Returns the Akonadi specific sub MIME type of a KCalendarCore::Todo. */ Q_REQUIRED_RESULT static QLatin1String todoMimeType(); protected: /** Compare this with @p todo for equality. @param todo is the to-do to compare. */ bool equals(const IncidenceBase &todo) const override; /** @copydoc IncidenceBase::assign() */ IncidenceBase &assign(const IncidenceBase &other) override; /** @copydoc IncidenceBase::virtual_hook() */ void virtual_hook(VirtualHook id, void *data) override; private: /** @copydoc IncidenceBase::accept() */ bool accept(Visitor &v, const IncidenceBase::Ptr &incidence) override; /** Disabled, otherwise could be dangerous if you subclass Todo. Use IncidenceBase::operator= which is safe because it calls virtual function assign(). @param other is another Todo object to assign to this one. */ Todo &operator=(const Todo &other); // For polymorfic serialization void serialize(QDataStream &out) const override; void deserialize(QDataStream &in) override; //@cond PRIVATE class Private; Private *const d; //@endcond }; -} // namespace KCalCore +} // namespace KCalendarCore //@cond PRIVATE -Q_DECLARE_TYPEINFO(KCalCore::Todo::Ptr, Q_MOVABLE_TYPE); -Q_DECLARE_METATYPE(KCalCore::Todo::Ptr) -Q_DECLARE_METATYPE(KCalCore::Todo *) +Q_DECLARE_TYPEINFO(KCalendarCore::Todo::Ptr, Q_MOVABLE_TYPE); +Q_DECLARE_METATYPE(KCalendarCore::Todo::Ptr) +Q_DECLARE_METATYPE(KCalendarCore::Todo *) //@endcond #endif diff --git a/src/utils.cpp b/src/utils.cpp index 0e450da70..5ea52ea24 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -1,133 +1,133 @@ /* This file is part of the kcalcore library. Copyright (c) 2017 Daniel Vrátil This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "utils.h" #include #include // To remain backwards compatible we need to (de)serialize QDateTime the way KDateTime // was (de)serialized -void KCalCore::serializeQDateTimeAsKDateTime(QDataStream &out, const QDateTime &dt) +void KCalendarCore::serializeQDateTimeAsKDateTime(QDataStream &out, const QDateTime &dt) { out << dt.date() << dt.time(); switch (dt.timeSpec()) { case Qt::UTC: out << static_cast('u'); break; case Qt::OffsetFromUTC: out << static_cast('o') << dt.offsetFromUtc(); break; case Qt::TimeZone: serializeQTimeZoneAsSpec(out, dt.timeZone()); break; case Qt::LocalTime: out << static_cast('c'); break; } const bool isDateOnly = dt.date().isValid() && !dt.time().isValid(); out << quint8(isDateOnly ? 0x01 : 0x00); } -void KCalCore::deserializeKDateTimeAsQDateTime(QDataStream &in, QDateTime &dt) +void KCalendarCore::deserializeKDateTimeAsQDateTime(QDataStream &in, QDateTime &dt) { QDate date; QTime time; quint8 ts, flags; in >> date >> time >> ts; switch (static_cast(ts)) { case 'u': dt = QDateTime(date, time, Qt::UTC); break; case 'o': { int offset; in >> offset; dt = QDateTime(date, time, Qt::OffsetFromUTC, offset); break; } case 'z': { QString tzid; in >> tzid; dt = QDateTime(date, time, QTimeZone(tzid.toUtf8())); break; } case 'c': dt = QDateTime(date, time, Qt::LocalTime); break; } // unused, we don't have a special handling for date-only QDateTime in >> flags; } -void KCalCore::serializeQTimeZoneAsSpec(QDataStream &out, const QTimeZone &tz) +void KCalendarCore::serializeQTimeZoneAsSpec(QDataStream &out, const QTimeZone &tz) { out << static_cast('z') << (tz.isValid() ? QString::fromUtf8(tz.id()) : QString()); } -void KCalCore::deserializeSpecAsQTimeZone(QDataStream &in, QTimeZone &tz) +void KCalendarCore::deserializeSpecAsQTimeZone(QDataStream &in, QTimeZone &tz) { quint8 ts; in >> ts; switch (static_cast(ts)) { case 'u': tz = QTimeZone::utc(); return; case 'o': { int offset; in >> offset; tz = QTimeZone(offset); return; } case 'z': { QString tzid; in >> tzid; tz = QTimeZone(tzid.toUtf8()); return; } case 'c': tz = QTimeZone::systemTimeZone(); break; } } -void KCalCore::serializeQDateTimeList(QDataStream &out, const QList &list) +void KCalendarCore::serializeQDateTimeList(QDataStream &out, const QList &list) { out << list.size(); for (const auto &i : list) { serializeQDateTimeAsKDateTime(out, i); } } -void KCalCore::deserializeQDateTimeList(QDataStream &in, QList &list) +void KCalendarCore::deserializeQDateTimeList(QDataStream &in, QList &list) { int size; in >> size; list.clear(); list.reserve(size); for (int i = 0; i < size; ++i) { QDateTime dt; deserializeKDateTimeAsQDateTime(in, dt); list << dt; } } diff --git a/src/utils.h b/src/utils.h index 63992fa45..a33b7570b 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1,47 +1,47 @@ /* This file is part of the kcalcore library. Copyright (c) 2017 Daniel Vrátil This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KCALCORE_UTILS_H #define KCALCORE_UTILS_H #include "kcalcore_export.h" #include class QDataStream; -namespace KCalCore { +namespace KCalendarCore { /** * Helpers to retain backwards compatibility of binary serialization. */ KCALCORE_EXPORT void serializeQDateTimeAsKDateTime(QDataStream &out, const QDateTime &dt); KCALCORE_EXPORT void deserializeKDateTimeAsQDateTime(QDataStream &in, QDateTime &dt); void serializeQDateTimeList(QDataStream &out, const QList &list); void deserializeQDateTimeList(QDataStream &in, QList &list); void serializeQTimeZoneAsSpec(QDataStream &out, const QTimeZone &tz); void deserializeSpecAsQTimeZone(QDataStream &in, QTimeZone &tz); } #endif diff --git a/src/vcalformat.cpp b/src/vcalformat.cpp index cbe325cf2..988690da9 100644 --- a/src/vcalformat.cpp +++ b/src/vcalformat.cpp @@ -1,1728 +1,1728 @@ /* This file is part of the kcalcore library. Copyright (c) 1998 Preston Brown Copyright (c) 2001 Cornelius Schumacher This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the VCalFormat base class. This class implements the vCalendar format. It provides methods for loading/saving/converting vCalendar format data into the internal representation as Calendar and Incidences. @brief vCalendar format implementation. @author Preston Brown \ @author Cornelius Schumacher \ */ #include "vcalformat.h" #include "calendar.h" #include "event.h" #include "exceptions.h" #include "todo.h" #include "utils.h" #include "kcalcore_debug.h" extern "C" { #include #include } #include #include #include // for .toHtmlEscaped() and Qt::mightBeRichText() #include -using namespace KCalCore; +using namespace KCalendarCore; /** Private class that helps to provide binary compatibility between releases. @internal */ //@cond PRIVATE template void removeAllVCal(QVector< QSharedPointer > &c, const QSharedPointer &x) { if (c.count() < 1) { return; } int cnt = c.count(x); if (cnt != 1) { qCritical() << "There number of relatedTos for this incidence is " << cnt << " (there must be 1 relatedTo only)"; Q_ASSERT_X(false, "removeAllVCal", "Count is not 1."); return; } c.remove(c.indexOf(x)); } -class Q_DECL_HIDDEN KCalCore::VCalFormat::Private +class Q_DECL_HIDDEN KCalendarCore::VCalFormat::Private { public: Calendar::Ptr mCalendar; Event::List mEventsRelate; // Events with relations Todo::List mTodosRelate; // To-dos with relations QSet mManuallyWrittenExtensionFields; // X- fields that are manually dumped }; //@endcond -VCalFormat::VCalFormat() : d(new KCalCore::VCalFormat::Private) +VCalFormat::VCalFormat() : d(new KCalendarCore::VCalFormat::Private) { } VCalFormat::~VCalFormat() { delete d; } bool VCalFormat::load(const Calendar::Ptr &calendar, const QString &fileName) { d->mCalendar = calendar; clearException(); // this is not necessarily only 1 vcal. Could be many vcals, or include // a vcard... VObject *vcal = Parse_MIME_FromFileName(const_cast(QFile::encodeName(fileName).data())); if (!vcal) { setException(new Exception(Exception::CalVersionUnknown)); return false; } // any other top-level calendar stuff should be added/initialized here // put all vobjects into their proper places auto savedTimeZoneId = d->mCalendar->timeZoneId(); populate(vcal, false, fileName); d->mCalendar->setTimeZoneId(savedTimeZoneId); // clean up from vcal API stuff cleanVObjects(vcal); cleanStrTbl(); return true; } bool VCalFormat::save(const Calendar::Ptr &calendar, const QString &fileName) { Q_UNUSED(calendar); Q_UNUSED(fileName); qCWarning(KCALCORE_LOG) << "Saving VCAL is not supported"; return false; } bool VCalFormat::fromString(const Calendar::Ptr &calendar, const QString &string, bool deleted, const QString ¬ebook) { return fromRawString(calendar, string.toUtf8(), deleted, notebook); } bool VCalFormat::fromRawString(const Calendar::Ptr &calendar, const QByteArray &string, bool deleted, const QString ¬ebook) { d->mCalendar = calendar; if (!string.size()) { return false; } VObject *vcal = Parse_MIME(string.data(), string.size()); if (!vcal) { return false; } VObjectIterator i; initPropIterator(&i, vcal); // put all vobjects into their proper places auto savedTimeZoneId = d->mCalendar->timeZoneId(); populate(vcal, deleted, notebook); d->mCalendar->setTimeZoneId(savedTimeZoneId); // clean up from vcal API stuff cleanVObjects(vcal); cleanStrTbl(); return true; } QString VCalFormat::toString(const Calendar::Ptr &calendar, const QString ¬ebook, bool deleted) { Q_UNUSED(calendar); Q_UNUSED(notebook); Q_UNUSED(deleted); qCWarning(KCALCORE_LOG) << "Exporting into VCAL is not supported"; return {}; } Todo::Ptr VCalFormat::VTodoToEvent(VObject *vtodo) { VObject *vo = nullptr; VObjectIterator voi; char *s = nullptr; Todo::Ptr anEvent(new Todo); // creation date if ((vo = isAPropertyOf(vtodo, VCDCreatedProp)) != nullptr) { anEvent->setCreated(ISOToQDateTime(QString::fromUtf8(s = fakeCString(vObjectUStringZValue(vo))))); deleteStr(s); } // unique id vo = isAPropertyOf(vtodo, VCUniqueStringProp); // while the UID property is preferred, it is not required. We'll use the // default Event UID if none is given. if (vo) { anEvent->setUid(QString::fromUtf8(s = fakeCString(vObjectUStringZValue(vo)))); deleteStr(s); } // last modification date if ((vo = isAPropertyOf(vtodo, VCLastModifiedProp)) != nullptr) { anEvent->setLastModified(ISOToQDateTime(QString::fromUtf8(s = fakeCString(vObjectUStringZValue(vo))))); deleteStr(s); } else { anEvent->setLastModified(QDateTime::currentDateTimeUtc()); } // organizer // if our extension property for the event's ORGANIZER exists, add it. if ((vo = isAPropertyOf(vtodo, ICOrganizerProp)) != nullptr) { anEvent->setOrganizer(QString::fromUtf8(s = fakeCString(vObjectUStringZValue(vo)))); deleteStr(s); } else { if (d->mCalendar->owner().name() != QLatin1String("Unknown Name")) { anEvent->setOrganizer(d->mCalendar->owner()); } } // attendees. initPropIterator(&voi, vtodo); while (moreIteration(&voi)) { vo = nextVObject(&voi); if (strcmp(vObjectName(vo), VCAttendeeProp) == 0) { Attendee a; VObject *vp; s = fakeCString(vObjectUStringZValue(vo)); QString tmpStr = QString::fromUtf8(s); deleteStr(s); tmpStr = tmpStr.simplified(); int emailPos1; if ((emailPos1 = tmpStr.indexOf(QLatin1Char('<'))) > 0) { // both email address and name int emailPos2 = tmpStr.lastIndexOf(QLatin1Char('>')); a = Attendee(tmpStr.left(emailPos1 - 1), tmpStr.mid(emailPos1 + 1, emailPos2 - (emailPos1 + 1))); } else if (tmpStr.indexOf(QLatin1Char('@')) > 0) { // just an email address a = Attendee(QString(), tmpStr); } else { // just a name // WTF??? Replacing the spaces of a name and using this as email? QString email = tmpStr.replace(QLatin1Char(' '), QLatin1Char('.')); a = Attendee(tmpStr, email); } // is there an RSVP property? if ((vp = isAPropertyOf(vo, VCRSVPProp)) != nullptr) { a.setRSVP(vObjectStringZValue(vp)); } // is there a status property? if ((vp = isAPropertyOf(vo, VCStatusProp)) != nullptr) { a.setStatus(readStatus(vObjectStringZValue(vp))); } // add the attendee anEvent->addAttendee(a); } } // description for todo if ((vo = isAPropertyOf(vtodo, VCDescriptionProp)) != nullptr) { s = fakeCString(vObjectUStringZValue(vo)); anEvent->setDescription(QString::fromUtf8(s), Qt::mightBeRichText(QString::fromUtf8(s))); deleteStr(s); } // summary if ((vo = isAPropertyOf(vtodo, VCSummaryProp))) { s = fakeCString(vObjectUStringZValue(vo)); anEvent->setSummary(QString::fromUtf8(s), Qt::mightBeRichText(QString::fromUtf8(s))); deleteStr(s); } // location if ((vo = isAPropertyOf(vtodo, VCLocationProp)) != nullptr) { s = fakeCString(vObjectUStringZValue(vo)); anEvent->setLocation(QString::fromUtf8(s), Qt::mightBeRichText(QString::fromUtf8(s))); deleteStr(s); } // completed // was: status if ((vo = isAPropertyOf(vtodo, VCStatusProp)) != nullptr) { s = fakeCString(vObjectUStringZValue(vo)); if (s && strcmp(s, "COMPLETED") == 0) { anEvent->setCompleted(true); } else { anEvent->setCompleted(false); } deleteStr(s); } else { anEvent->setCompleted(false); } // completion date if ((vo = isAPropertyOf(vtodo, VCCompletedProp)) != nullptr) { anEvent->setCompleted(ISOToQDateTime(QString::fromUtf8(s = fakeCString(vObjectUStringZValue(vo))))); deleteStr(s); } // priority if ((vo = isAPropertyOf(vtodo, VCPriorityProp))) { s = fakeCString(vObjectUStringZValue(vo)); if (s) { anEvent->setPriority(atoi(s)); deleteStr(s); } } anEvent->setAllDay(false); // due date if ((vo = isAPropertyOf(vtodo, VCDueProp)) != nullptr) { anEvent->setDtDue(ISOToQDateTime(QString::fromUtf8(s = fakeCString(vObjectUStringZValue(vo))))); deleteStr(s); if (anEvent->dtDue().time().hour() == 0 && anEvent->dtDue().time().minute() == 0 && anEvent->dtDue().time().second() == 0) { anEvent->setAllDay(true); } } else { anEvent->setDtDue(QDateTime()); } // start time if ((vo = isAPropertyOf(vtodo, VCDTstartProp)) != nullptr) { anEvent->setDtStart(ISOToQDateTime(QString::fromUtf8(s = fakeCString(vObjectUStringZValue(vo))))); deleteStr(s); if (anEvent->dtStart().time().hour() == 0 && anEvent->dtStart().time().minute() == 0 && anEvent->dtStart().time().second() == 0) { anEvent->setAllDay(true); } } else { anEvent->setDtStart(QDateTime()); } // recurrence stuff if ((vo = isAPropertyOf(vtodo, VCRRuleProp)) != nullptr) { uint recurrenceType = Recurrence::rNone; int recurrenceTypeAbbrLen = 0; QString tmpStr = (QString::fromUtf8(s = fakeCString(vObjectUStringZValue(vo)))); deleteStr(s); tmpStr = tmpStr.simplified(); const int tmpStrLen = tmpStr.length(); if (tmpStrLen > 0) { tmpStr = tmpStr.toUpper(); // first, read the type of the recurrence recurrenceTypeAbbrLen = 1; if (tmpStr.at(0) == QLatin1String("D")) { recurrenceType = Recurrence::rDaily; } else if (tmpStr.at(0) == QLatin1String("W")) { recurrenceType = Recurrence::rWeekly; } else if (tmpStrLen > 1) { recurrenceTypeAbbrLen = 2; if (tmpStr.leftRef(2) == QLatin1String("MP")) { recurrenceType = Recurrence::rMonthlyPos; } else if (tmpStr.leftRef(2) == QLatin1String("MD")) { recurrenceType = Recurrence::rMonthlyDay; } else if (tmpStr.leftRef(2) == QLatin1String("YM")) { recurrenceType = Recurrence::rYearlyMonth; } else if (tmpStr.leftRef(2) == QLatin1String("YD")) { recurrenceType = Recurrence::rYearlyDay; } } } if (recurrenceType != Recurrence::rNone) { // Immediately after the type is the frequency int index = tmpStr.indexOf(QLatin1Char(' ')); int last = tmpStr.lastIndexOf(QLatin1Char(' ')) + 1; // find last entry int rFreq = tmpStr.midRef(recurrenceTypeAbbrLen, (index - 1)).toInt(); ++index; // advance to beginning of stuff after freq // Read the type-specific settings switch (recurrenceType) { case Recurrence::rDaily: anEvent->recurrence()->setDaily(rFreq); break; case Recurrence::rWeekly: { QBitArray qba(7); QString dayStr; if (index == last) { // e.g. W1 #0 qba.setBit(anEvent->dtStart().date().dayOfWeek() - 1); } else { // e.g. W1 SU #0 while (index < last) { dayStr = tmpStr.mid(index, 3); int dayNum = numFromDay(dayStr); if (dayNum >= 0) { qba.setBit(dayNum); } index += 3; // advance to next day, or possibly "#" } } anEvent->recurrence()->setWeekly(rFreq, qba); break; } case Recurrence::rMonthlyPos: { anEvent->recurrence()->setMonthly(rFreq); QBitArray qba(7); short tmpPos; if (index == last) { // e.g. MP1 #0 tmpPos = anEvent->dtStart().date().day() / 7 + 1; if (tmpPos == 5) { tmpPos = -1; } qba.setBit(anEvent->dtStart().date().dayOfWeek() - 1); anEvent->recurrence()->addMonthlyPos(tmpPos, qba); } else { // e.g. MP1 1+ SU #0 while (index < last) { tmpPos = tmpStr.mid(index, 1).toShort(); index += 1; if (tmpStr.mid(index, 1) == QLatin1String("-")) { // convert tmpPos to negative tmpPos = 0 - tmpPos; } index += 2; // advance to day(s) while (numFromDay(tmpStr.mid(index, 3)) >= 0) { int dayNum = numFromDay(tmpStr.mid(index, 3)); qba.setBit(dayNum); index += 3; // advance to next day, or possibly pos or "#" } anEvent->recurrence()->addMonthlyPos(tmpPos, qba); qba.detach(); qba.fill(false); // clear out } // while != "#" } break; } case Recurrence::rMonthlyDay: anEvent->recurrence()->setMonthly(rFreq); if (index == last) { // e.g. MD1 #0 short tmpDay = anEvent->dtStart().date().day(); anEvent->recurrence()->addMonthlyDate(tmpDay); } else { // e.g. MD1 3 #0 while (index < last) { int index2 = tmpStr.indexOf(QLatin1Char(' '), index); if ((tmpStr.mid((index2 - 1), 1) == QLatin1String("-")) || (tmpStr.mid((index2 - 1), 1) == QLatin1String("+"))) { index2 = index2 - 1; } short tmpDay = tmpStr.mid(index, (index2 - index)).toShort(); index = index2; if (tmpStr.mid(index, 1) == QLatin1String("-")) { tmpDay = 0 - tmpDay; } index += 2; // advance the index; anEvent->recurrence()->addMonthlyDate(tmpDay); } // while != # } break; case Recurrence::rYearlyMonth: anEvent->recurrence()->setYearly(rFreq); if (index == last) { // e.g. YM1 #0 short tmpMonth = anEvent->dtStart().date().month(); anEvent->recurrence()->addYearlyMonth(tmpMonth); } else { // e.g. YM1 3 #0 while (index < last) { int index2 = tmpStr.indexOf(QLatin1Char(' '), index); short tmpMonth = tmpStr.mid(index, (index2 - index)).toShort(); index = index2 + 1; anEvent->recurrence()->addYearlyMonth(tmpMonth); } // while != # } break; case Recurrence::rYearlyDay: anEvent->recurrence()->setYearly(rFreq); if (index == last) { // e.g. YD1 #0 short tmpDay = anEvent->dtStart().date().dayOfYear(); anEvent->recurrence()->addYearlyDay(tmpDay); } else { // e.g. YD1 123 #0 while (index < last) { int index2 = tmpStr.indexOf(QLatin1Char(' '), index); short tmpDay = tmpStr.mid(index, (index2 - index)).toShort(); index = index2 + 1; anEvent->recurrence()->addYearlyDay(tmpDay); } // while != # } break; default: break; } // find the last field, which is either the duration or the end date index = last; if (tmpStr.mid(index, 1) == QLatin1String("#")) { // Nr of occurrences index++; int rDuration = tmpStr.midRef(index, tmpStr.length() - index).toInt(); if (rDuration > 0) { anEvent->recurrence()->setDuration(rDuration); } } else if (tmpStr.indexOf(QLatin1Char('T'), index) != -1) { QDateTime rEndDate = ISOToQDateTime(tmpStr.mid(index, tmpStr.length() - index)); anEvent->recurrence()->setEndDateTime(rEndDate); } } else { qCDebug(KCALCORE_LOG) << "we don't understand this type of recurrence!"; } // if known recurrence type } // repeats // recurrence exceptions if ((vo = isAPropertyOf(vtodo, VCExpDateProp)) != nullptr) { s = fakeCString(vObjectUStringZValue(vo)); QStringList exDates = QString::fromUtf8(s).split(QLatin1Char(',')); QStringList::ConstIterator it; for (it = exDates.constBegin(); it != exDates.constEnd(); ++it) { QDateTime exDate = ISOToQDateTime(*it); if (exDate.time().hour() == 0 && exDate.time().minute() == 0 && exDate.time().second() == 0) { anEvent->recurrence()->addExDate(ISOToQDate(*it)); } else { anEvent->recurrence()->addExDateTime(exDate); } } deleteStr(s); } // alarm stuff if ((vo = isAPropertyOf(vtodo, VCDAlarmProp))) { Alarm::Ptr alarm; VObject *a = isAPropertyOf(vo, VCRunTimeProp); VObject *b = isAPropertyOf(vo, VCDisplayStringProp); if (a || b) { alarm = anEvent->newAlarm(); if (a) { alarm->setTime(ISOToQDateTime(QString::fromUtf8(s = fakeCString(vObjectUStringZValue(a))))); deleteStr(s); } alarm->setEnabled(true); if (b) { s = fakeCString(vObjectUStringZValue(b)); alarm->setDisplayAlarm(QString::fromUtf8(s)); deleteStr(s); } else { alarm->setDisplayAlarm(QString()); } } } if ((vo = isAPropertyOf(vtodo, VCAAlarmProp))) { Alarm::Ptr alarm; VObject *a; VObject *b; a = isAPropertyOf(vo, VCRunTimeProp); b = isAPropertyOf(vo, VCAudioContentProp); if (a || b) { alarm = anEvent->newAlarm(); if (a) { alarm->setTime(ISOToQDateTime(QString::fromUtf8(s = fakeCString(vObjectUStringZValue(a))))); deleteStr(s); } alarm->setEnabled(true); if (b) { s = fakeCString(vObjectUStringZValue(b)); alarm->setAudioAlarm(QFile::decodeName(s)); deleteStr(s); } else { alarm->setAudioAlarm(QString()); } } } if ((vo = isAPropertyOf(vtodo, VCPAlarmProp))) { Alarm::Ptr alarm; VObject *a = isAPropertyOf(vo, VCRunTimeProp); VObject *b = isAPropertyOf(vo, VCProcedureNameProp); if (a || b) { alarm = anEvent->newAlarm(); if (a) { alarm->setTime(ISOToQDateTime(QString::fromUtf8(s = fakeCString(vObjectUStringZValue(a))))); deleteStr(s); } alarm->setEnabled(true); if (b) { s = fakeCString(vObjectUStringZValue(b)); alarm->setProcedureAlarm(QFile::decodeName(s)); deleteStr(s); } else { alarm->setProcedureAlarm(QString()); } } } // related todo if ((vo = isAPropertyOf(vtodo, VCRelatedToProp)) != nullptr) { anEvent->setRelatedTo(QString::fromUtf8(s = fakeCString(vObjectUStringZValue(vo)))); deleteStr(s); d->mTodosRelate.append(anEvent); } // secrecy Incidence::Secrecy secrecy = Incidence::SecrecyPublic; if ((vo = isAPropertyOf(vtodo, VCClassProp)) != nullptr) { s = fakeCString(vObjectUStringZValue(vo)); if (s && strcmp(s, "PRIVATE") == 0) { secrecy = Incidence::SecrecyPrivate; } else if (s && strcmp(s, "CONFIDENTIAL") == 0) { secrecy = Incidence::SecrecyConfidential; } deleteStr(s); } anEvent->setSecrecy(secrecy); // categories if ((vo = isAPropertyOf(vtodo, VCCategoriesProp)) != nullptr) { s = fakeCString(vObjectUStringZValue(vo)); QString categories = QString::fromUtf8(s); deleteStr(s); QStringList tmpStrList = categories.split(QLatin1Char(';')); anEvent->setCategories(tmpStrList); } return anEvent; } Event::Ptr VCalFormat::VEventToEvent(VObject *vevent) { VObject *vo = nullptr; VObjectIterator voi; char *s = nullptr; Event::Ptr anEvent(new Event); // creation date if ((vo = isAPropertyOf(vevent, VCDCreatedProp)) != nullptr) { anEvent->setCreated(ISOToQDateTime(QString::fromUtf8(s = fakeCString(vObjectUStringZValue(vo))))); deleteStr(s); } // unique id vo = isAPropertyOf(vevent, VCUniqueStringProp); // while the UID property is preferred, it is not required. We'll use the // default Event UID if none is given. if (vo) { anEvent->setUid(QString::fromUtf8(s = fakeCString(vObjectUStringZValue(vo)))); deleteStr(s); } // revision // again NSCAL doesn't give us much to work with, so we improvise... anEvent->setRevision(0); if ((vo = isAPropertyOf(vevent, VCSequenceProp)) != nullptr) { s = fakeCString(vObjectUStringZValue(vo)); if (s) { anEvent->setRevision(atoi(s)); deleteStr(s); } } // last modification date if ((vo = isAPropertyOf(vevent, VCLastModifiedProp)) != nullptr) { anEvent->setLastModified(ISOToQDateTime(QString::fromUtf8(s = fakeCString(vObjectUStringZValue(vo))))); deleteStr(s); } else { anEvent->setLastModified(QDateTime::currentDateTimeUtc()); } // organizer // if our extension property for the event's ORGANIZER exists, add it. if ((vo = isAPropertyOf(vevent, ICOrganizerProp)) != nullptr) { // FIXME: Also use the full name, not just the email address anEvent->setOrganizer(QString::fromUtf8(s = fakeCString(vObjectUStringZValue(vo)))); deleteStr(s); } else { if (d->mCalendar->owner().name() != QStringLiteral("Unknown Name")) { anEvent->setOrganizer(d->mCalendar->owner()); } } // deal with attendees. initPropIterator(&voi, vevent); while (moreIteration(&voi)) { vo = nextVObject(&voi); if (strcmp(vObjectName(vo), VCAttendeeProp) == 0) { Attendee a; VObject *vp = nullptr; s = fakeCString(vObjectUStringZValue(vo)); QString tmpStr = QString::fromUtf8(s); deleteStr(s); tmpStr = tmpStr.simplified(); int emailPos1; if ((emailPos1 = tmpStr.indexOf(QLatin1Char('<'))) > 0) { // both email address and name int emailPos2 = tmpStr.lastIndexOf(QLatin1Char('>')); a = Attendee(tmpStr.left(emailPos1 - 1), tmpStr.mid(emailPos1 + 1, emailPos2 - (emailPos1 + 1))); } else if (tmpStr.indexOf(QLatin1Char('@')) > 0) { // just an email address a = Attendee(QString(), tmpStr); } else { // just a name QString email = tmpStr.replace(QLatin1Char(' '), QLatin1Char('.')); a = Attendee(tmpStr, email); } // is there an RSVP property? if ((vp = isAPropertyOf(vo, VCRSVPProp)) != nullptr) { a.setRSVP(vObjectStringZValue(vp)); } // is there a status property? if ((vp = isAPropertyOf(vo, VCStatusProp)) != nullptr) { a.setStatus(readStatus(vObjectStringZValue(vp))); } // add the attendee anEvent->addAttendee(a); } } // This isn't strictly true. An event that doesn't have a start time // or an end time isn't all-day, it has an anchor in time but it doesn't // "take up" any time. /*if ((isAPropertyOf(vevent, VCDTstartProp) == 0) || (isAPropertyOf(vevent, VCDTendProp) == 0)) { anEvent->setAllDay(true); } else { }*/ anEvent->setAllDay(false); // start time if ((vo = isAPropertyOf(vevent, VCDTstartProp)) != nullptr) { anEvent->setDtStart(ISOToQDateTime(QString::fromUtf8(s = fakeCString(vObjectUStringZValue(vo))))); deleteStr(s); if (anEvent->dtStart().time().hour() == 0 && anEvent->dtStart().time().minute() == 0 && anEvent->dtStart().time().second() == 0) { anEvent->setAllDay(true); } } // stop time if ((vo = isAPropertyOf(vevent, VCDTendProp)) != nullptr) { anEvent->setDtEnd(ISOToQDateTime(QString::fromUtf8(s = fakeCString(vObjectUStringZValue(vo))))); deleteStr(s); if (anEvent->dtEnd().time().hour() == 0 && anEvent->dtEnd().time().minute() == 0 && anEvent->dtEnd().time().second() == 0) { anEvent->setAllDay(true); } } // at this point, there should be at least a start or end time. // fix up for events that take up no time but have a time associated if (!isAPropertyOf(vevent, VCDTstartProp)) { anEvent->setDtStart(anEvent->dtEnd()); } if (! isAPropertyOf(vevent, VCDTendProp)) { anEvent->setDtEnd(anEvent->dtStart()); } /////////////////////////////////////////////////////////////////////////// // recurrence stuff if ((vo = isAPropertyOf(vevent, VCRRuleProp)) != nullptr) { uint recurrenceType = Recurrence::rNone; int recurrenceTypeAbbrLen = 0; QString tmpStr = (QString::fromUtf8(s = fakeCString(vObjectUStringZValue(vo)))); deleteStr(s); tmpStr = tmpStr.simplified(); const int tmpStrLen = tmpStr.length(); if (tmpStrLen > 0) { tmpStr = tmpStr.toUpper(); // first, read the type of the recurrence recurrenceTypeAbbrLen = 1; if (tmpStr.at(0) == QLatin1String("D")) { recurrenceType = Recurrence::rDaily; } else if (tmpStr.at(0) == QLatin1String("W")) { recurrenceType = Recurrence::rWeekly; } else if (tmpStrLen > 1){ recurrenceTypeAbbrLen = 2; if (tmpStr.leftRef(2) == QLatin1String("MP")) { recurrenceType = Recurrence::rMonthlyPos; } else if (tmpStr.leftRef(2) == QLatin1String("MD")) { recurrenceType = Recurrence::rMonthlyDay; } else if (tmpStr.leftRef(2) == QLatin1String("YM")) { recurrenceType = Recurrence::rYearlyMonth; } else if (tmpStr.leftRef(2) == QLatin1String("YD")) { recurrenceType = Recurrence::rYearlyDay; } } } if (recurrenceType != Recurrence::rNone) { // Immediately after the type is the frequency int index = tmpStr.indexOf(QLatin1Char(' ')); int last = tmpStr.lastIndexOf(QLatin1Char(' ')) + 1; // find last entry int rFreq = tmpStr.midRef(recurrenceTypeAbbrLen, (index - 1)).toInt(); ++index; // advance to beginning of stuff after freq // Read the type-specific settings switch (recurrenceType) { case Recurrence::rDaily: anEvent->recurrence()->setDaily(rFreq); break; case Recurrence::rWeekly: { QBitArray qba(7); QString dayStr; if (index == last) { // e.g. W1 #0 qba.setBit(anEvent->dtStart().date().dayOfWeek() - 1); } else { // e.g. W1 SU #0 while (index < last) { dayStr = tmpStr.mid(index, 3); int dayNum = numFromDay(dayStr); if (dayNum >= 0) { qba.setBit(dayNum); } index += 3; // advance to next day, or possibly "#" } } anEvent->recurrence()->setWeekly(rFreq, qba); break; } case Recurrence::rMonthlyPos: { anEvent->recurrence()->setMonthly(rFreq); QBitArray qba(7); short tmpPos; if (index == last) { // e.g. MP1 #0 tmpPos = anEvent->dtStart().date().day() / 7 + 1; if (tmpPos == 5) { tmpPos = -1; } qba.setBit(anEvent->dtStart().date().dayOfWeek() - 1); anEvent->recurrence()->addMonthlyPos(tmpPos, qba); } else { // e.g. MP1 1+ SU #0 while (index < last) { tmpPos = tmpStr.mid(index, 1).toShort(); index += 1; if (tmpStr.mid(index, 1) == QStringLiteral("-")) { // convert tmpPos to negative tmpPos = 0 - tmpPos; } index += 2; // advance to day(s) while (numFromDay(tmpStr.mid(index, 3)) >= 0) { int dayNum = numFromDay(tmpStr.mid(index, 3)); qba.setBit(dayNum); index += 3; // advance to next day, or possibly pos or "#" } anEvent->recurrence()->addMonthlyPos(tmpPos, qba); qba.detach(); qba.fill(false); // clear out } // while != "#" } break; } case Recurrence::rMonthlyDay: anEvent->recurrence()->setMonthly(rFreq); if (index == last) { // e.g. MD1 #0 short tmpDay = anEvent->dtStart().date().day(); anEvent->recurrence()->addMonthlyDate(tmpDay); } else { // e.g. MD1 3 #0 while (index < last) { int index2 = tmpStr.indexOf(QLatin1Char(' '), index); if ((tmpStr.mid((index2 - 1), 1) == QStringLiteral("-")) || (tmpStr.mid((index2 - 1), 1) == QStringLiteral("+"))) { index2 = index2 - 1; } short tmpDay = tmpStr.mid(index, (index2 - index)).toShort(); index = index2; if (tmpStr.mid(index, 1) == QStringLiteral("-")) { tmpDay = 0 - tmpDay; } index += 2; // advance the index; anEvent->recurrence()->addMonthlyDate(tmpDay); } // while != # } break; case Recurrence::rYearlyMonth: anEvent->recurrence()->setYearly(rFreq); if (index == last) { // e.g. YM1 #0 short tmpMonth = anEvent->dtStart().date().month(); anEvent->recurrence()->addYearlyMonth(tmpMonth); } else { // e.g. YM1 3 #0 while (index < last) { int index2 = tmpStr.indexOf(QLatin1Char(' '), index); short tmpMonth = tmpStr.mid(index, (index2 - index)).toShort(); index = index2 + 1; anEvent->recurrence()->addYearlyMonth(tmpMonth); } // while != # } break; case Recurrence::rYearlyDay: anEvent->recurrence()->setYearly(rFreq); if (index == last) { // e.g. YD1 #0 const int tmpDay = anEvent->dtStart().date().dayOfYear(); anEvent->recurrence()->addYearlyDay(tmpDay); } else { // e.g. YD1 123 #0 while (index < last) { int index2 = tmpStr.indexOf(QLatin1Char(' '), index); short tmpDay = tmpStr.mid(index, (index2 - index)).toShort(); index = index2 + 1; anEvent->recurrence()->addYearlyDay(tmpDay); } // while != # } break; default: break; } // find the last field, which is either the duration or the end date index = last; if (tmpStr.mid(index, 1) == QLatin1String("#")) { // Nr of occurrences index++; int rDuration = tmpStr.midRef(index, tmpStr.length() - index).toInt(); if (rDuration > 0) { anEvent->recurrence()->setDuration(rDuration); } } else if (tmpStr.indexOf(QLatin1Char('T'), index) != -1) { QDateTime rEndDate = ISOToQDateTime(tmpStr.mid(index, tmpStr.length() - index)); anEvent->recurrence()->setEndDateTime(rEndDate); } // anEvent->recurrence()->dump(); } else { qCDebug(KCALCORE_LOG) << "we don't understand this type of recurrence!"; } // if known recurrence type } // repeats // recurrence exceptions if ((vo = isAPropertyOf(vevent, VCExpDateProp)) != nullptr) { s = fakeCString(vObjectUStringZValue(vo)); QStringList exDates = QString::fromUtf8(s).split(QLatin1Char(',')); QStringList::ConstIterator it; for (it = exDates.constBegin(); it != exDates.constEnd(); ++it) { QDateTime exDate = ISOToQDateTime(*it); if (exDate.time().hour() == 0 && exDate.time().minute() == 0 && exDate.time().second() == 0) { anEvent->recurrence()->addExDate(ISOToQDate(*it)); } else { anEvent->recurrence()->addExDateTime(exDate); } } deleteStr(s); } // summary if ((vo = isAPropertyOf(vevent, VCSummaryProp))) { s = fakeCString(vObjectUStringZValue(vo)); anEvent->setSummary(QString::fromUtf8(s), Qt::mightBeRichText(QString::fromUtf8(s))); deleteStr(s); } // description if ((vo = isAPropertyOf(vevent, VCDescriptionProp)) != nullptr) { s = fakeCString(vObjectUStringZValue(vo)); bool isRich = Qt::mightBeRichText(QString::fromUtf8(s)); if (!anEvent->description().isEmpty()) { anEvent->setDescription( anEvent->description() + QLatin1Char('\n') + QString::fromUtf8(s), isRich); } else { anEvent->setDescription(QString::fromUtf8(s), isRich); } deleteStr(s); } // location if ((vo = isAPropertyOf(vevent, VCLocationProp)) != nullptr) { s = fakeCString(vObjectUStringZValue(vo)); anEvent->setLocation(QString::fromUtf8(s), Qt::mightBeRichText(QString::fromUtf8(s))); deleteStr(s); } // some stupid vCal exporters ignore the standard and use Description // instead of Summary for the default field. Correct for this. if (anEvent->summary().isEmpty() && !(anEvent->description().isEmpty())) { QString tmpStr = anEvent->description().simplified(); anEvent->setDescription(QString()); anEvent->setSummary(tmpStr); } #if 0 // status if ((vo = isAPropertyOf(vevent, VCStatusProp)) != 0) { QString tmpStr(s = fakeCString(vObjectUStringZValue(vo))); deleteStr(s); // TODO: Define Event status // anEvent->setStatus( tmpStr ); } else { // anEvent->setStatus( "NEEDS ACTION" ); } #endif // secrecy Incidence::Secrecy secrecy = Incidence::SecrecyPublic; if ((vo = isAPropertyOf(vevent, VCClassProp)) != nullptr) { s = fakeCString(vObjectUStringZValue(vo)); if (s && strcmp(s, "PRIVATE") == 0) { secrecy = Incidence::SecrecyPrivate; } else if (s && strcmp(s, "CONFIDENTIAL") == 0) { secrecy = Incidence::SecrecyConfidential; } deleteStr(s); } anEvent->setSecrecy(secrecy); // categories if ((vo = isAPropertyOf(vevent, VCCategoriesProp)) != nullptr) { s = fakeCString(vObjectUStringZValue(vo)); QString categories = QString::fromUtf8(s); deleteStr(s); QStringList tmpStrList = categories.split(QLatin1Char(',')); anEvent->setCategories(tmpStrList); } // attachments initPropIterator(&voi, vevent); while (moreIteration(&voi)) { vo = nextVObject(&voi); if (strcmp(vObjectName(vo), VCAttachProp) == 0) { s = fakeCString(vObjectUStringZValue(vo)); anEvent->addAttachment(Attachment(QString::fromUtf8(s))); deleteStr(s); } } // resources if ((vo = isAPropertyOf(vevent, VCResourcesProp)) != nullptr) { QString resources = (QString::fromUtf8(s = fakeCString(vObjectUStringZValue(vo)))); deleteStr(s); QStringList tmpStrList = resources.split(QLatin1Char(';')); anEvent->setResources(tmpStrList); } // alarm stuff if ((vo = isAPropertyOf(vevent, VCDAlarmProp))) { Alarm::Ptr alarm; VObject *a = isAPropertyOf(vo, VCRunTimeProp); VObject *b = isAPropertyOf(vo, VCDisplayStringProp); if (a || b) { alarm = anEvent->newAlarm(); if (a) { alarm->setTime(ISOToQDateTime(QString::fromUtf8(s = fakeCString(vObjectUStringZValue(a))))); deleteStr(s); } alarm->setEnabled(true); if (b) { s = fakeCString(vObjectUStringZValue(b)); alarm->setDisplayAlarm(QString::fromUtf8(s)); deleteStr(s); } else { alarm->setDisplayAlarm(QString()); } } } if ((vo = isAPropertyOf(vevent, VCAAlarmProp))) { Alarm::Ptr alarm; VObject *a; VObject *b; a = isAPropertyOf(vo, VCRunTimeProp); b = isAPropertyOf(vo, VCAudioContentProp); if (a || b) { alarm = anEvent->newAlarm(); if (a) { alarm->setTime(ISOToQDateTime(QString::fromUtf8(s = fakeCString(vObjectUStringZValue(a))))); deleteStr(s); } alarm->setEnabled(true); if (b) { s = fakeCString(vObjectUStringZValue(b)); alarm->setAudioAlarm(QFile::decodeName(s)); deleteStr(s); } else { alarm->setAudioAlarm(QString()); } } } if ((vo = isAPropertyOf(vevent, VCPAlarmProp))) { Alarm::Ptr alarm; VObject *a; VObject *b; a = isAPropertyOf(vo, VCRunTimeProp); b = isAPropertyOf(vo, VCProcedureNameProp); if (a || b) { alarm = anEvent->newAlarm(); if (a) { alarm->setTime(ISOToQDateTime(QString::fromUtf8(s = fakeCString(vObjectUStringZValue(a))))); deleteStr(s); } alarm->setEnabled(true); if (b) { s = fakeCString(vObjectUStringZValue(b)); alarm->setProcedureAlarm(QFile::decodeName(s)); deleteStr(s); } else { alarm->setProcedureAlarm(QString()); } } } // priority if ((vo = isAPropertyOf(vevent, VCPriorityProp))) { s = fakeCString(vObjectUStringZValue(vo)); if (s) { anEvent->setPriority(atoi(s)); deleteStr(s); } } // transparency if ((vo = isAPropertyOf(vevent, VCTranspProp)) != nullptr) { s = fakeCString(vObjectUStringZValue(vo)); if (s) { int i = atoi(s); anEvent->setTransparency(i == 1 ? Event::Transparent : Event::Opaque); deleteStr(s); } } // related event if ((vo = isAPropertyOf(vevent, VCRelatedToProp)) != nullptr) { anEvent->setRelatedTo(QString::fromUtf8(s = fakeCString(vObjectUStringZValue(vo)))); deleteStr(s); d->mEventsRelate.append(anEvent); } /* Rest of the custom properties */ readCustomProperties(vevent, anEvent); return anEvent; } QString VCalFormat::parseTZ(const QByteArray &timezone) const { // qCDebug(KCALCORE_LOG) << timezone; QString pZone = QString::fromUtf8(timezone.mid(timezone.indexOf("TZID:VCAL") + 9)); return pZone.mid(0, pZone.indexOf(QLatin1Char('\n'))); } QString VCalFormat::parseDst(QByteArray &timezone) const { if (!timezone.contains("BEGIN:DAYLIGHT")) { return QString(); } timezone = timezone.mid(timezone.indexOf("BEGIN:DAYLIGHT")); timezone = timezone.mid(timezone.indexOf("TZNAME:") + 7); QString sStart = QString::fromUtf8(timezone.mid(0, (timezone.indexOf("COMMENT:")))); sStart.chop(2); timezone = timezone.mid(timezone.indexOf("TZOFFSETTO:") + 11); QString sOffset = QString::fromUtf8(timezone.mid(0, (timezone.indexOf("DTSTART:")))); sOffset.chop(2); sOffset.insert(3, QLatin1Char(':')); timezone = timezone.mid(timezone.indexOf("TZNAME:") + 7); QString sEnd = QString::fromUtf8(timezone.mid(0, (timezone.indexOf("COMMENT:")))); sEnd.chop(2); return QStringLiteral("TRUE;") + sOffset + QLatin1Char(';') + sStart + QLatin1Char(';') + sEnd + QLatin1String(";;"); } QString VCalFormat::qDateToISO(const QDate &qd) { if (!qd.isValid()) { return QString(); } return QString::asprintf("%.2d%.2d%.2d", qd.year(), qd.month(), qd.day()); } QString VCalFormat::qDateTimeToISO(const QDateTime &dt, bool zulu) { if (!dt.isValid()) { return QString(); } QDateTime tmpDT; if (zulu) { tmpDT = dt.toUTC(); } else { tmpDT = dt.toTimeZone(d->mCalendar->timeZone()); } QString tmpStr = QString::asprintf("%.2d%.2d%.2dT%.2d%.2d%.2d", tmpDT.date().year(), tmpDT.date().month(), tmpDT.date().day(), tmpDT.time().hour(), tmpDT.time().minute(), tmpDT.time().second()); if (zulu || dt.timeZone() == QTimeZone::utc()) { tmpStr += QLatin1Char('Z'); } return tmpStr; } QDateTime VCalFormat::ISOToQDateTime(const QString &dtStr) { QDate tmpDate; QTime tmpTime; QString tmpStr; int year, month, day, hour, minute, second; tmpStr = dtStr; year = tmpStr.leftRef(4).toInt(); month = tmpStr.midRef(4, 2).toInt(); day = tmpStr.midRef(6, 2).toInt(); hour = tmpStr.midRef(9, 2).toInt(); minute = tmpStr.midRef(11, 2).toInt(); second = tmpStr.midRef(13, 2).toInt(); tmpDate.setDate(year, month, day); tmpTime.setHMS(hour, minute, second); if (tmpDate.isValid() && tmpTime.isValid()) { // correct for GMT if string is in Zulu format if (dtStr.at(dtStr.length() - 1) == QLatin1Char('Z')) { return QDateTime(tmpDate, tmpTime, Qt::UTC); } else { return QDateTime(tmpDate, tmpTime, d->mCalendar->timeZone()); } } else { return QDateTime(); } } QDate VCalFormat::ISOToQDate(const QString &dateStr) { int year, month, day; year = dateStr.leftRef(4).toInt(); month = dateStr.midRef(4, 2).toInt(); day = dateStr.midRef(6, 2).toInt(); return QDate(year, month, day); } bool VCalFormat::parseTZOffsetISO8601(const QString &s, int &result) { // ISO8601 format(s): // +- hh : mm // +- hh mm // +- hh // We also accept broken one without + int mod = 1; int v = 0; QString str = s.trimmed(); int ofs = 0; result = 0; // Check for end if (str.size() <= ofs) { return false; } if (str[ofs] == QLatin1Char('-')) { mod = -1; ofs++; } else if (str[ofs] == QLatin1Char('+')) { ofs++; } if (str.size() <= ofs) { return false; } // Make sure next two values are numbers bool ok; if (str.size() < (ofs + 2)) { return false; } v = str.midRef(ofs, 2).toInt(&ok) * 60; if (!ok) { return false; } ofs += 2; if (str.size() > ofs) { if (str[ofs] == QLatin1Char(':')) { ofs++; } if (str.size() > ofs) { if (str.size() < (ofs + 2)) { return false; } v += str.midRef(ofs, 2).toInt(&ok); if (!ok) { return false; } } } result = v * mod * 60; return true; } // take a raw vcalendar (i.e. from a file on disk, clipboard, etc. etc. // and break it down from it's tree-like format into the dictionary format // that is used internally in the VCalFormat. void VCalFormat::populate(VObject *vcal, bool deleted, const QString ¬ebook) { Q_UNUSED(notebook); // this function will populate the caldict dictionary and other event // lists. It turns vevents into Events and then inserts them. VObjectIterator i; VObject *curVO; Event::Ptr anEvent; bool hasTimeZone = false; //The calendar came with a TZ and not UTC QTimeZone previousZone; //If we add a new TZ we should leave the spec as it was before if ((curVO = isAPropertyOf(vcal, ICMethodProp)) != nullptr) { char *methodType = fakeCString(vObjectUStringZValue(curVO)); // qCDebug(KCALCORE_LOG) << "This calendar is an iTIP transaction of type '" << methodType << "'"; deleteStr(methodType); } // warn the user that we might have trouble reading non-known calendar. if ((curVO = isAPropertyOf(vcal, VCProdIdProp)) != nullptr) { char *s = fakeCString(vObjectUStringZValue(curVO)); if (!s || strcmp(productId().toUtf8().constData(), s) != 0) { qCDebug(KCALCORE_LOG) << "This vCalendar file was not created by KOrganizer or" << "any other product we support. Loading anyway..."; } setLoadedProductId(QString::fromUtf8(s)); deleteStr(s); } // warn the user we might have trouble reading this unknown version. if ((curVO = isAPropertyOf(vcal, VCVersionProp)) != nullptr) { char *s = fakeCString(vObjectUStringZValue(curVO)); if (!s || strcmp(_VCAL_VERSION, s) != 0) { qCDebug(KCALCORE_LOG) << "This vCalendar file has version" << s << "We only support" << _VCAL_VERSION; } deleteStr(s); } // set the time zone (this is a property of the view, so just discard!) if ((curVO = isAPropertyOf(vcal, VCTimeZoneProp)) != nullptr) { char *s = fakeCString(vObjectUStringZValue(curVO)); QString ts = QString::fromUtf8(s); QString name = QStringLiteral("VCAL") + ts; deleteStr(s); // TODO: While using the timezone-offset + vcal as timezone is is // most likely unique, we should REALLY actually create something // like vcal-tzoffset-daylightoffsets, or better yet, // vcal-hash QStringList tzList; QString tz; int utcOffset; int utcOffsetDst; if (parseTZOffsetISO8601(ts, utcOffset)) { // qCDebug(KCALCORE_LOG) << "got standard offset" << ts << utcOffset; // standard from tz // starting date for now 01011900 QDateTime dt = QDateTime(QDateTime(QDate(1900, 1, 1), QTime(0, 0, 0))); tz = QStringLiteral("STD;%1;false;%2").arg(QString::number(utcOffset), dt.toString()); tzList.append(tz); // go through all the daylight tags initPropIterator(&i, vcal); while (moreIteration(&i)) { curVO = nextVObject(&i); if (strcmp(vObjectName(curVO), VCDayLightProp) == 0) { char *s = fakeCString(vObjectUStringZValue(curVO)); QString dst = QLatin1String(s); QStringList argl = dst.split(QLatin1Char(',')); deleteStr(s); // Too short -> not interesting if (argl.size() < 4) { continue; } // We don't care about the non-DST periods if (argl[0] != QLatin1String("TRUE")) { continue; } if (parseTZOffsetISO8601(argl[1], utcOffsetDst)) { // qCDebug(KCALCORE_LOG) << "got DST offset" << argl[1] << utcOffsetDst; // standard QString strEndDate = argl[3]; QDateTime endDate = ISOToQDateTime(strEndDate); // daylight QString strStartDate = argl[2]; QDateTime startDate = ISOToQDateTime(strStartDate); QString strRealEndDate = strEndDate; QString strRealStartDate = strStartDate; QDateTime realEndDate = endDate; QDateTime realStartDate = startDate; // if we get dates for some reason in wrong order, earlier is used for dst if (endDate < startDate) { strRealEndDate = strStartDate; strRealStartDate = strEndDate; realEndDate = startDate; realStartDate = endDate; } tz = QStringLiteral("%1;%2;false;%3"). arg(strRealEndDate, QString::number(utcOffset), realEndDate.toString()); tzList.append(tz); tz = QStringLiteral("%1;%2;true;%3"). arg(strRealStartDate, QString::number(utcOffsetDst), realStartDate.toString()); tzList.append(tz); } else { qCDebug(KCALCORE_LOG) << "unable to parse dst" << argl[1]; } } } if (!QTimeZone::isTimeZoneIdAvailable(name.toLatin1())) { qCDebug(KCALCORE_LOG) << "zone is not valid, parsing error" << tzList; } else { previousZone = d->mCalendar->timeZone(); d->mCalendar->setTimeZoneId(name.toUtf8()); hasTimeZone = true; } } else { qCDebug(KCALCORE_LOG) << "unable to parse tzoffset" << ts; } } // Store all events with a relatedTo property in a list for post-processing d->mEventsRelate.clear(); d->mTodosRelate.clear(); initPropIterator(&i, vcal); // go through all the vobjects in the vcal while (moreIteration(&i)) { curVO = nextVObject(&i); /************************************************************************/ // now, check to see that the object is an event or todo. if (strcmp(vObjectName(curVO), VCEventProp) == 0) { if (!isAPropertyOf(curVO, VCDTstartProp) && !isAPropertyOf(curVO, VCDTendProp)) { qCDebug(KCALCORE_LOG) << "found a VEvent with no DTSTART and no DTEND! Skipping..."; goto SKIP; } anEvent = VEventToEvent(curVO); if (anEvent) { if (hasTimeZone && !anEvent->allDay() && anEvent->dtStart().timeZone() == QTimeZone::utc()) { //This sounds stupid but is how others are doing it, so here //we go. If there is a TZ in the VCALENDAR even if the dtStart //and dtend are in UTC, clients interpret it using also the TZ defined //in the Calendar. I know it sounds braindead but oh well int utcOffSet = anEvent->dtStart().offsetFromUtc(); QDateTime dtStart(anEvent->dtStart().addSecs(utcOffSet)); dtStart.setTimeZone(d->mCalendar->timeZone()); QDateTime dtEnd(anEvent->dtEnd().addSecs(utcOffSet)); dtEnd.setTimeZone(d->mCalendar->timeZone()); anEvent->setDtStart(dtStart); anEvent->setDtEnd(dtEnd); } Event::Ptr old = !anEvent->hasRecurrenceId() ? d->mCalendar->event(anEvent->uid()) : d->mCalendar->event(anEvent->uid(), anEvent->recurrenceId()); if (old) { if (deleted) { d->mCalendar->deleteEvent(old); // move old to deleted removeAllVCal(d->mEventsRelate, old); } else if (anEvent->revision() > old->revision()) { d->mCalendar->deleteEvent(old); // move old to deleted removeAllVCal(d->mEventsRelate, old); d->mCalendar->addEvent(anEvent); // and replace it with this one } } else if (deleted) { old = !anEvent->hasRecurrenceId() ? d->mCalendar->deletedEvent(anEvent->uid()) : d->mCalendar->deletedEvent(anEvent->uid(), anEvent->recurrenceId()); if (!old) { d->mCalendar->addEvent(anEvent); // add this one d->mCalendar->deleteEvent(anEvent); // and move it to deleted } } else { d->mCalendar->addEvent(anEvent); // just add this one } } } else if (strcmp(vObjectName(curVO), VCTodoProp) == 0) { Todo::Ptr aTodo = VTodoToEvent(curVO); if (aTodo) { if (hasTimeZone && !aTodo->allDay() && aTodo->dtStart().timeZone() == QTimeZone::utc()) { //This sounds stupid but is how others are doing it, so here //we go. If there is a TZ in the VCALENDAR even if the dtStart //and dtend are in UTC, clients interpret it usint alse the TZ defined //in the Calendar. I know it sounds braindead but oh well int utcOffSet = aTodo->dtStart().offsetFromUtc(); QDateTime dtStart(aTodo->dtStart().addSecs(utcOffSet)); dtStart.setTimeZone(d->mCalendar->timeZone()); aTodo->setDtStart(dtStart); if (aTodo->hasDueDate()) { QDateTime dtDue(aTodo->dtDue().addSecs(utcOffSet)); dtDue.setTimeZone(d->mCalendar->timeZone()); aTodo->setDtDue(dtDue); } } Todo::Ptr old = !aTodo->hasRecurrenceId() ? d->mCalendar->todo(aTodo->uid()) : d->mCalendar->todo(aTodo->uid(), aTodo->recurrenceId()); if (old) { if (deleted) { d->mCalendar->deleteTodo(old); // move old to deleted removeAllVCal(d->mTodosRelate, old); } else if (aTodo->revision() > old->revision()) { d->mCalendar->deleteTodo(old); // move old to deleted removeAllVCal(d->mTodosRelate, old); d->mCalendar->addTodo(aTodo); // and replace it with this one } } else if (deleted) { old = d->mCalendar->deletedTodo(aTodo->uid(), aTodo->recurrenceId()); if (!old) { d->mCalendar->addTodo(aTodo); // add this one d->mCalendar->deleteTodo(aTodo); // and move it to deleted } } else { d->mCalendar->addTodo(aTodo); // just add this one } } } else if ((strcmp(vObjectName(curVO), VCVersionProp) == 0) || (strcmp(vObjectName(curVO), VCProdIdProp) == 0) || (strcmp(vObjectName(curVO), VCTimeZoneProp) == 0)) { // do nothing, we know these properties and we want to skip them. // we have either already processed them or are ignoring them. ; } else if (strcmp(vObjectName(curVO), VCDayLightProp) == 0) { // do nothing daylights are already processed ; } else { qCDebug(KCALCORE_LOG) << "Ignoring unknown vObject \"" << vObjectName(curVO) << "\""; } SKIP: ; } // while // Post-Process list of events with relations, put Event objects in relation Event::List::ConstIterator eIt; for (eIt = d->mEventsRelate.constBegin(); eIt != d->mEventsRelate.constEnd(); ++eIt) { (*eIt)->setRelatedTo((*eIt)->relatedTo()); } Todo::List::ConstIterator tIt; for (tIt = d->mTodosRelate.constBegin(); tIt != d->mTodosRelate.constEnd(); ++tIt) { (*tIt)->setRelatedTo((*tIt)->relatedTo()); } //Now lets put the TZ back as it was if we have changed it. if (hasTimeZone) { d->mCalendar->setTimeZone(previousZone); } } int VCalFormat::numFromDay(const QString &day) { if (day == QLatin1String("MO ")) { return 0; } if (day == QLatin1String("TU ")) { return 1; } if (day == QLatin1String("WE ")) { return 2; } if (day == QLatin1String("TH ")) { return 3; } if (day == QLatin1String("FR ")) { return 4; } if (day == QLatin1String("SA ")) { return 5; } if (day == QLatin1String("SU ")) { return 6; } return -1; // something bad happened. :) } Attendee::PartStat VCalFormat::readStatus(const char *s) const { QString statStr = QString::fromUtf8(s); statStr = statStr.toUpper(); Attendee::PartStat status; if (statStr == QLatin1String("X-ACTION")) { status = Attendee::NeedsAction; } else if (statStr == QLatin1String("NEEDS ACTION")) { status = Attendee::NeedsAction; } else if (statStr == QLatin1String("ACCEPTED")) { status = Attendee::Accepted; } else if (statStr == QLatin1String("SENT")) { status = Attendee::NeedsAction; } else if (statStr == QLatin1String("TENTATIVE")) { status = Attendee::Tentative; } else if (statStr == QLatin1String("CONFIRMED")) { status = Attendee::Accepted; } else if (statStr == QLatin1String("DECLINED")) { status = Attendee::Declined; } else if (statStr == QLatin1String("COMPLETED")) { status = Attendee::Completed; } else if (statStr == QLatin1String("DELEGATED")) { status = Attendee::Delegated; } else { qCDebug(KCALCORE_LOG) << "error setting attendee mStatus, unknown mStatus!"; status = Attendee::NeedsAction; } return status; } QByteArray VCalFormat::writeStatus(Attendee::PartStat status) const { switch (status) { default: case Attendee::NeedsAction: return "NEEDS ACTION"; case Attendee::Accepted: return "ACCEPTED"; case Attendee::Declined: return "DECLINED"; case Attendee::Tentative: return "TENTATIVE"; case Attendee::Delegated: return "DELEGATED"; case Attendee::Completed: return "COMPLETED"; case Attendee::InProcess: return "NEEDS ACTION"; } } void VCalFormat::readCustomProperties(VObject *o, const Incidence::Ptr &i) { VObjectIterator iter; char *s; initPropIterator(&iter, o); while (moreIteration(&iter)) { VObject *cur = nextVObject(&iter); const char *curname = vObjectName(cur); Q_ASSERT(curname); if ((curname[0] == 'X' && curname[1] == '-') && strcmp(curname, ICOrganizerProp) != 0) { // TODO - for the time being, we ignore the parameters part // and just do the value handling here i->setNonKDECustomProperty( curname, QString::fromUtf8(s = fakeCString(vObjectUStringZValue(cur)))); deleteStr(s); } } } void VCalFormat::writeCustomProperties(VObject *o, const Incidence::Ptr &i) { const QMap custom = i->customProperties(); for (QMap::ConstIterator c = custom.begin(); c != custom.end(); ++c) { if (d->mManuallyWrittenExtensionFields.contains(c.key()) || c.key().startsWith("X-KDE-VOLATILE")) { //krazy:exclude=strings continue; } addPropValue(o, c.key().constData(), c.value().toUtf8().constData()); } } void VCalFormat::virtual_hook(int id, void *data) { Q_UNUSED(id); Q_UNUSED(data); Q_ASSERT(false); } diff --git a/src/vcalformat.h b/src/vcalformat.h index a87ae3d72..260adf885 100644 --- a/src/vcalformat.h +++ b/src/vcalformat.h @@ -1,231 +1,231 @@ /* This file is part of the kcalcore library. Copyright (c) 1998 Preston Brown Copyright (c) 2001-2003 Cornelius Schumacher This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and defines the VCalFormat base class. This class implements the vCalendar format. It provides methods for loading/saving/converting vCalendar format data into the internal representation as Calendar and Incidences. @brief vCalendar format implementation. @author Preston Brown \ @author Cornelius Schumacher \ */ #ifndef KCALCORE_VCALFORMAT_H #define KCALCORE_VCALFORMAT_H #include "kcalcore_export.h" #include "attendee.h" #include "calformat.h" #include "event.h" #include "todo.h" #include "journal.h" struct VObject; class QDate; #define _VCAL_VERSION "1.0" /* extensions for iMIP / iTIP */ #define ICOrganizerProp "X-ORGANIZER" #define ICMethodProp "X-METHOD" #define ICRequestStatusProp "X-REQUEST-STATUS" -namespace KCalCore +namespace KCalendarCore { class Event; class Todo; /** @brief vCalendar format implementation. This class implements the vCalendar format. It provides methods for loading/saving/converting vCalendar format data into the internal representation as Calendar and Incidences. */ class KCALCORE_EXPORT VCalFormat : public CalFormat { public: /** Constructor a new vCalendar Format object. */ VCalFormat(); /** Destructor. */ ~VCalFormat() override; /** @copydoc CalFormat::load() */ bool load(const Calendar::Ptr &calendar, const QString &fileName) override; /** @copydoc CalFormat::save() */ bool save(const Calendar::Ptr &calendar, const QString &fileName) override; /** @copydoc CalFormat::fromString() */ Q_REQUIRED_RESULT bool fromString(const Calendar::Ptr &calendar, const QString &string, bool deleted = false, const QString ¬ebook = QString()) override; /** @copydoc CalFormat::toString() */ Q_REQUIRED_RESULT QString toString(const Calendar::Ptr &calendar, const QString ¬ebook = QString(), bool deleted = false) override; /** @copydoc CalFormat::fromRawString() */ Q_REQUIRED_RESULT bool fromRawString(const Calendar::Ptr &calendar, const QByteArray &string, bool deleted = false, const QString ¬ebook = QString()) override; protected: /** Translates a VObject of the TODO type into an Event. @param vtodo is a pointer to a valid VObject object. */ Todo::Ptr VTodoToEvent(VObject *vtodo); /** Translates a VObject into a Event and returns a pointer to it. @param vevent is a pointer to a valid VObject object. */ Event::Ptr VEventToEvent(VObject *vevent); /** Parse TZ tag from vtimezone. */ QString parseTZ(const QByteArray &timezone) const; /** Parse DAYLIGHT tag from vtimezone. */ QString parseDst(QByteArray &timezone) const; /** Takes a QDate and returns a string in the format YYYYMMDDTHHMMSS. @param date is the date to format. */ QString qDateToISO(const QDate &date); /** Takes a QDateTime and returns a string in format YYYYMMDDTHHMMSS. @param date is the date to format. @param zulu if true, then shift the date to UTC. */ QString qDateTimeToISO(const QDateTime &date, bool zulu = true); /** Takes a string in YYYYMMDDTHHMMSS format and returns a valid QDateTime. @param dtStr is a QString containing the date to convert. If this value is invalid, then QDateTime() is returned. */ QDateTime ISOToQDateTime(const QString &dtStr); /** Takes a string in the YYYYMMDD format and returns a valid QDate. @param dtStr is a QString containing the date to convert. If this value is invalid, then QDateTime() is returned. */ QDate ISOToQDate(const QString &dtStr); /** Parse one of the myriad of ISO8601 timezone offset formats, e.g. +- hh : mm +- hh mm +- hh @param s string to be parsed. @param result timezone offset in seconds, if parse succeeded. @return Whether the parse succeeded or not. */ bool parseTZOffsetISO8601(const QString &s, int &result); /** Takes a vCalendar tree of VObjects, and puts all of them that have the "event" property into the dictionary, todos in the todo-list, etc. */ void populate(VObject *vcal, bool deleted = false, const QString ¬ebook = QString()); /** Converts a two letter representation of the day (i.e. MO, TU, WE, etc) and returns a number 0-6 corresponding to that ordinal day of the week. @param day is the QString containing the two letter day representation. */ int numFromDay(const QString &day); /** Converts a status string into an Attendee::PartStat. @param s is a null-terminated character string containing the status to convert. @return a valid Attendee::PartStat. If the string provided is empty, null, or the contents are unrecognized, then Attendee::NeedsAction is returned. */ Attendee::PartStat readStatus(const char *s) const; /** Converts an Attendee::PartStat into a QByteArray string. @param status is the Attendee::PartStat to convert. @return a QByteArray containing the status string. */ QByteArray writeStatus(Attendee::PartStat status) const; void readCustomProperties(VObject *o, const Incidence::Ptr &i); void writeCustomProperties(VObject *o, const Incidence::Ptr &i); protected: /** @copydoc IncidenceBase::virtual_hook() */ void virtual_hook(int id, void *data) override; private: //@cond PRIVATE Q_DISABLE_COPY(VCalFormat) class Private; Private *const d; //@endcond }; } #endif diff --git a/src/visitor.cpp b/src/visitor.cpp index 057851a84..2e3e80576 100644 --- a/src/visitor.cpp +++ b/src/visitor.cpp @@ -1,64 +1,64 @@ /* This file is part of the kcalcore library. Copyright (c) 2001-2003 Cornelius Schumacher Copyright (c) 2003-2004 Reinhold Kainhofer Copyright (c) 2005 Rafal Rzepecki Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). All rights reserved. Contact: Alvaro Manera This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "visitor.h" #include "event.h" #include "todo.h" #include "journal.h" #include "freebusy.h" -using namespace KCalCore; +using namespace KCalendarCore; Visitor::Visitor() { } Visitor::~Visitor() { } bool Visitor::visit(const Event::Ptr &event) { Q_UNUSED(event); return false; } bool Visitor::visit(const Todo::Ptr &todo) { Q_UNUSED(todo); return false; } bool Visitor::visit(const Journal::Ptr &journal) { Q_UNUSED(journal); return false; } bool Visitor::visit(const FreeBusy::Ptr &freebusy) { Q_UNUSED(freebusy); return false; } diff --git a/src/visitor.h b/src/visitor.h index c7e86bcbd..f9b479ad2 100644 --- a/src/visitor.h +++ b/src/visitor.h @@ -1,88 +1,88 @@ /* This file is part of the kcalcore library. Copyright (c) 2001-2003 Cornelius Schumacher Copyright (c) 2003-2004 Reinhold Kainhofer Copyright (c) 2005 Rafal Rzepecki Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). All rights reserved. Contact: Alvaro Manera This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KCALCORE_VISITOR_H #define KCALCORE_VISITOR_H #include "event.h" #include "journal.h" #include "todo.h" #include "freebusy.h" -namespace KCalCore +namespace KCalendarCore { /** This class provides the interface for a visitor of calendar components. It serves as base class for concrete visitors, which implement certain actions on calendar components. It allows to add functions, which operate on the concrete types of calendar components, without changing the calendar component classes. */ class KCALCORE_EXPORT Visitor //krazy:exclude=dpointer { public: /** Destruct Incidence::Visitor */ virtual ~Visitor(); /** Reimplement this function in your concrete subclass of IncidenceBase::Visitor to perform actions on an Event object. @param event is a pointer to a valid Event object. */ virtual bool visit(const Event::Ptr &event); /** Reimplement this function in your concrete subclass of IncidenceBase::Visitor to perform actions on a Todo object. @param todo is a pointer to a valid Todo object. */ virtual bool visit(const Todo::Ptr &todo); /** Reimplement this function in your concrete subclass of IncidenceBase::Visitor to perform actions on an Journal object. @param journal is a pointer to a valid Journal object. */ virtual bool visit(const Journal::Ptr &journal); /** Reimplement this function in your concrete subclass of IncidenceBase::Visitor to perform actions on a FreeBusy object. @param freebusy is a pointer to a valid FreeBusy object. */ virtual bool visit(const FreeBusy::Ptr &freebusy); protected: /** Constructor is protected to prevent direct creation of visitor base class. */ Visitor(); }; } // end namespace #endif