79 files changed, 22413 insertions, 0 deletions
diff --git a/libkcal/alarm.cpp b/libkcal/alarm.cpp new file mode 100644 index 0000000..07812c2 --- a/dev/null +++ b/libkcal/alarm.cpp @@ -0,0 +1,407 @@ +/* + This file is part of libkcal. + Copyright (c) 1998 Preston Brown + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include <kdebug.h> + +#include "incidence.h" +#include "todo.h" + +#include "alarm.h" + +using namespace KCal; +#include <qwidget.h> +Alarm::Alarm(Incidence *parent) + : mParent(parent), + mType(Audio), + mDescription(""), // to make operator==() not fail + mFile(""), // to make operator==() not fail + mMailSubject(""), // to make operator==() not fail + mAlarmSnoozeTime(5), + mAlarmRepeatCount(0), + mEndOffset(false), + mHasTime(false), + mAlarmEnabled(false) +{ + +} + +Alarm::~Alarm() +{ +} + +bool Alarm::operator==( const Alarm& rhs ) const +{ + if ( mType != rhs.mType || + mAlarmSnoozeTime != rhs.mAlarmSnoozeTime || + mAlarmRepeatCount != rhs.mAlarmRepeatCount || + mAlarmEnabled != rhs.mAlarmEnabled || + mHasTime != rhs.mHasTime) + return false; + + if (mHasTime) { + if (mAlarmTime != rhs.mAlarmTime) + return false; + } else { + if (mOffset != rhs.mOffset || + mEndOffset != rhs.mEndOffset) + return false; + } + + switch (mType) { + case Display: + return mDescription == rhs.mDescription; + + case Email: + return mDescription == rhs.mDescription && + mMailAttachFiles == rhs.mMailAttachFiles && + mMailAddresses == rhs.mMailAddresses && + mMailSubject == rhs.mMailSubject; + + case Procedure: + return mFile == rhs.mFile && + mDescription == rhs.mDescription; + + case Audio: + return mFile == rhs.mFile; + + case Invalid: + break; + } + return false; +} + +void Alarm::setType(Alarm::Type type) +{ + if (type == mType) + return; + + switch (type) { + case Display: + mDescription = ""; + break; + case Procedure: + mFile = mDescription = ""; + break; + case Audio: + mFile = ""; + break; + case Email: + mMailSubject = mDescription = ""; + mMailAddresses.clear(); + mMailAttachFiles.clear(); + break; + case Invalid: + break; + default: + return; + } + mType = type; + mParent->updated(); +} + +Alarm::Type Alarm::type() const +{ + return mType; +} + +void Alarm::setAudioAlarm(const QString &audioFile) +{ + mType = Audio; + mFile = audioFile; + mParent->updated(); +} + +void Alarm::setAudioFile(const QString &audioFile) +{ + if (mType == Audio) { + mFile = audioFile; + mParent->updated(); + } +} + +QString Alarm::audioFile() const +{ + return (mType == Audio) ? mFile : QString::null; +} + +void Alarm::setProcedureAlarm(const QString &programFile, const QString &arguments) +{ + mType = Procedure; + mFile = programFile; + mDescription = arguments; + mParent->updated(); +} + +void Alarm::setProgramFile(const QString &programFile) +{ + if (mType == Procedure) { + mFile = programFile; + mParent->updated(); + } +} + +QString Alarm::programFile() const +{ + return (mType == Procedure) ? mFile : QString::null; +} + +void Alarm::setProgramArguments(const QString &arguments) +{ + if (mType == Procedure) { + mDescription = arguments; + mParent->updated(); + } +} + +QString Alarm::programArguments() const +{ + return (mType == Procedure) ? mDescription : QString::null; +} + +void Alarm::setEmailAlarm(const QString &subject, const QString &text, + const QValueList<Person> &addressees, const QStringList &attachments) +{ + mType = Email; + mMailSubject = subject; + mDescription = text; + mMailAddresses = addressees; + mMailAttachFiles = attachments; + mParent->updated(); +} + +void Alarm::setMailAddress(const Person &mailAddress) +{ + if (mType == Email) { + mMailAddresses.clear(); + mMailAddresses += mailAddress; + mParent->updated(); + } +} + +void Alarm::setMailAddresses(const QValueList<Person> &mailAddresses) +{ + if (mType == Email) { + mMailAddresses = mailAddresses; + mParent->updated(); + } +} + +void Alarm::addMailAddress(const Person &mailAddress) +{ + if (mType == Email) { + mMailAddresses += mailAddress; + mParent->updated(); + } +} + +QValueList<Person> Alarm::mailAddresses() const +{ + return (mType == Email) ? mMailAddresses : QValueList<Person>(); +} + +void Alarm::setMailSubject(const QString &mailAlarmSubject) +{ + if (mType == Email) { + mMailSubject = mailAlarmSubject; + mParent->updated(); + } +} + +QString Alarm::mailSubject() const +{ + return (mType == Email) ? mMailSubject : QString::null; +} + +void Alarm::setMailAttachment(const QString &mailAttachFile) +{ + if (mType == Email) { + mMailAttachFiles.clear(); + mMailAttachFiles += mailAttachFile; + mParent->updated(); + } +} + +void Alarm::setMailAttachments(const QStringList &mailAttachFiles) +{ + if (mType == Email) { + mMailAttachFiles = mailAttachFiles; + mParent->updated(); + } +} + +void Alarm::addMailAttachment(const QString &mailAttachFile) +{ + if (mType == Email) { + mMailAttachFiles += mailAttachFile; + mParent->updated(); + } +} + +QStringList Alarm::mailAttachments() const +{ + return (mType == Email) ? mMailAttachFiles : QStringList(); +} + +void Alarm::setMailText(const QString &text) +{ + if (mType == Email) { + mDescription = text; + mParent->updated(); + } +} + +QString Alarm::mailText() const +{ + return (mType == Email) ? mDescription : QString::null; +} + +void Alarm::setDisplayAlarm(const QString &text) +{ + mType = Display; + mDescription = text; + mParent->updated(); +} + +void Alarm::setText(const QString &text) +{ + if (mType == Display) { + mDescription = text; + mParent->updated(); + } +} + +QString Alarm::text() const +{ + return (mType == Display) ? mDescription : QString::null; +} + +void Alarm::setTime(const QDateTime &alarmTime) +{ + mAlarmTime = alarmTime; + mHasTime = true; + + mParent->updated(); +} + +QDateTime Alarm::time() const +{ + if ( hasTime() ) + return mAlarmTime; + else + { + if (mParent->type()=="Todo") { + Todo *t = static_cast<Todo*>(mParent); + return mOffset.end( t->dtDue() ); + } else if (mEndOffset) { + return mOffset.end( mParent->dtEnd() ); + } else { + return mOffset.end( mParent->dtStart() ); + } + } +} + +bool Alarm::hasTime() const +{ + return mHasTime; +} + +void Alarm::setSnoozeTime(int alarmSnoozeTime) +{ + mAlarmSnoozeTime = alarmSnoozeTime; + mParent->updated(); +} + +int Alarm::snoozeTime() const +{ + return mAlarmSnoozeTime; +} + +void Alarm::setRepeatCount(int alarmRepeatCount) +{ + kdDebug(5800) << "Alarm::setRepeatCount(): " << alarmRepeatCount << endl; + + mAlarmRepeatCount = alarmRepeatCount; + mParent->updated(); +} + +int Alarm::repeatCount() const +{ + kdDebug(5800) << "Alarm::repeatCount(): " << mAlarmRepeatCount << endl; + return mAlarmRepeatCount; +} + +void Alarm::toggleAlarm() +{ + mAlarmEnabled = !mAlarmEnabled; + mParent->updated(); +} + +void Alarm::setEnabled(bool enable) +{ + mAlarmEnabled = enable; + mParent->updated(); +} + +bool Alarm::enabled() const +{ + return mAlarmEnabled; +} + +void Alarm::setStartOffset( const Duration &offset ) +{ + mOffset = offset; + mEndOffset = false; + mHasTime = false; + mParent->updated(); +} + +Duration Alarm::startOffset() const +{ + return (mHasTime || mEndOffset) ? 0 : mOffset; +} + +bool Alarm::hasStartOffset() const +{ + return !mHasTime && !mEndOffset; +} + +bool Alarm::hasEndOffset() const +{ + return !mHasTime && mEndOffset; +} + +void Alarm::setEndOffset( const Duration &offset ) +{ + mOffset = offset; + mEndOffset = true; + mHasTime = false; + mParent->updated(); +} + +Duration Alarm::endOffset() const +{ + return (mHasTime || !mEndOffset) ? 0 : mOffset; +} + +void Alarm::setParent( Incidence *parent ) +{ + mParent = parent; +} diff --git a/libkcal/alarm.h b/libkcal/alarm.h new file mode 100644 index 0000000..ae2eca3 --- a/dev/null +++ b/libkcal/alarm.h @@ -0,0 +1,245 @@ +/* + This file is part of libkcal. + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef KCAL_ALARM_H +#define KCAL_ALARM_H + +#include <qstring.h> +#include <qvaluelist.h> + +#include "customproperties.h" +#include "duration.h" +#include "person.h" + +namespace KCal { + +class Incidence; + +/** + This class represents an alarm notification. +*/ +class Alarm : public CustomProperties +{ + public: + enum Type { Invalid, Display, Procedure, Email, Audio }; + typedef QValueList<Alarm *> List; + + /** Construct a new alarm with variables initialized to "sane" values. */ + explicit Alarm(Incidence *parent); + /** Destruct Alarm object. */ + ~Alarm(); + + /** Compare this alarm with another one. */ + bool operator==(const Alarm &) const; + bool operator!=(const Alarm &a) const { return !operator==(a); } + + /** Set the type of the alarm. + If the specified type is different from the current type of the alarm, + the alarm's type-specific properties are initialised to null. + @param type type of alarm. + */ + void setType(Type type); + /** Return the type of the alarm */ + Type type() const; + + /** Set the alarm to be a display alarm. + @param text text to display when the alarm is triggered. + */ + void setDisplayAlarm(const QString &text); + /** Set the text to be displayed when the alarm is triggered. + Ignored if the alarm is not a display alarm. + */ + void setText(const QString &text); + /** Return the text string that displays when the alarm is triggered. */ + QString text() const; + + /** Set the alarm to be an audio alarm. + @param audioFile optional file to play when the alarm is triggered. + */ + void setAudioAlarm(const QString &audioFile = QString::null); + /** Set the file to play when the audio alarm is triggered. + Ignored if the alarm is not an audio alarm. + */ + void setAudioFile(const QString &audioFile); + /** Return the name of the audio file for the alarm. + @return The audio file for the alarm, or QString::null if not an audio alarm. + */ + QString audioFile() const; + + /** Set the alarm to be a procedure alarm. + @param programFile program to execute when the alarm is triggered. + @param arguments arguments to supply to programFile. + */ + void setProcedureAlarm(const QString &programFile, const QString &arguments = QString::null); + /** Set the program file to execute when the alarm is triggered. + Ignored if the alarm is not a procedure alarm. + */ + void setProgramFile(const QString &programFile); + /** Return the name of the program file to execute when the alarm is triggered. + @return the program file name, or QString::null if not a procedure alarm. + */ + QString programFile() const; + /** Set the arguments to the program to execute when the alarm is triggered. + Ignored if the alarm is not a procedure alarm. + */ + void setProgramArguments(const QString &arguments); + /** Return the arguments to the program to run when the alarm is triggered. + @return the program arguments, or QString::null if not a procedure alarm. + */ + QString programArguments() const; + + /** Set the alarm to be an email alarm. + @param subject subject line of email. + @param text body of email. + @param addressees email addresses of recipient(s). + @param attachments optional names of files to attach to the email. + */ + void setEmailAlarm(const QString &subject, const QString &text, const QValueList<Person> &addressees, + const QStringList &attachments = QStringList()); + + /** Send mail to this address when the alarm is triggered. + Ignored if the alarm is not an email alarm. + */ + void setMailAddress(const Person &mailAlarmAddress); + /** Send mail to these addresses when the alarm is triggered. + Ignored if the alarm is not an email alarm. + */ + void setMailAddresses(const QValueList<Person> &mailAlarmAddresses); + /** Add this address to the list of addresses to send mail to when the alarm is triggered. + Ignored if the alarm is not an email alarm. + */ + void addMailAddress(const Person &mailAlarmAddress); + /** return the addresses to send mail to when an alarm goes off */ + QValueList<Person> mailAddresses() const; + + /** Set the subject line of the mail. + Ignored if the alarm is not an email alarm. + */ + void setMailSubject(const QString &mailAlarmSubject); + /** return the subject line of the mail */ + QString mailSubject() const; + + /** Attach this filename to the email. + Ignored if the alarm is not an email alarm. + */ + void setMailAttachment(const QString &mailAttachFile); + /** Attach these filenames to the email. + Ignored if the alarm is not an email alarm. + */ + void setMailAttachments(const QStringList &mailAttachFiles); + /** Add this filename to the list of files to attach to the email. + Ignored if the alarm is not an email alarm. + */ + void addMailAttachment(const QString &mailAttachFile); + /** return the filenames to attach to the email */ + QStringList mailAttachments() const; + + /** Set the email body text. + Ignored if the alarm is not an email alarm. + */ + void setMailText(const QString &text); + /** Return the email body text. + @return the body text, or QString::null if not an email alarm. + */ + QString mailText() const; + + /** set the time to trigger an alarm */ + void setTime(const QDateTime &alarmTime); + /** return the date/time when an alarm goes off */ + QDateTime time() const; + /** Return true, if the alarm has an explicit date/time. */ + bool hasTime() const; + + /** Set offset of alarm in time relative to the start of the event. */ + void setStartOffset(const Duration &); + /** Return offset of alarm in time relative to the start of the event. + * If the alarm's time is not defined in terms of an offset relative + * to the start of the event, returns zero. + */ + Duration startOffset() const; + /** Return whether the alarm is defined in terms of an offset relative + * to the start of the event. + */ + bool hasStartOffset() const; + + /** Set offset of alarm in time relative to the end of the event. */ + void setEndOffset(const Duration &); + /** Return 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. + */ + Duration endOffset() const; + /** Return whether the alarm is defined in terms of an offset relative + * to the end of the event. + */ + bool hasEndOffset() const; + + /** Set the interval between snoozes for the alarm. + @param snoozeTime the time in minutes between snoozes. + */ + void setSnoozeTime(int alarmSnoozeTime); + /** Get how long the alarm snooze interval is. + @return the number of minutes between snoozes. + */ + int snoozeTime() const; + + /** set how many times an alarm is to repeat itself (w/snoozes) */ + void setRepeatCount(int alarmRepeatCount); + /** get how many times an alarm repeats */ + int repeatCount() const; + + /** toggles the value of alarm to be either on or off. + set's the alarm time to be x minutes before dtStart time. */ + void toggleAlarm(); + + /** set the alarm enabled status */ + void setEnabled(bool enable); + /** get the alarm enabled status */ + bool enabled() const; + + /** Set the alarm's parent incidence */ + void setParent( Incidence * ); + /** get the alarm's parent incidence */ + Incidence *parent() const { return mParent; } + + private: + Incidence *mParent; // the incidence which this alarm belongs to + Type mType; // type of alarm + QString mDescription; // text to display/email body/procedure arguments + QString mFile; // procedure program to run/optional audio file to play + QStringList mMailAttachFiles; // filenames to attach to email + QValueList<Person> mMailAddresses; // who to mail for reminder + QString mMailSubject; // subject of email + + int mAlarmSnoozeTime; // number of minutes after alarm to + // snooze before ringing again + int mAlarmRepeatCount; // number of times for alarm to repeat + // after the initial time + + QDateTime mAlarmTime; // time at which to trigger the alarm + 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; +}; + +} + +#endif diff --git a/libkcal/attachment.cpp b/libkcal/attachment.cpp new file mode 100644 index 0000000..1ead923 --- a/dev/null +++ b/libkcal/attachment.cpp @@ -0,0 +1,86 @@ +/* + This file is part of libkcal. + Copyright (c) 2002 Michael Brade <brade@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include "attachment.h" + +using namespace KCal; + +Attachment::Attachment(const QString& uri, const QString& mime) +{ + mMimeType = mime; + mData = uri; + mBinary = false; +} + +Attachment::Attachment(const char *base64, const QString& mime) +{ + mMimeType = mime; + mData = QString::fromUtf8(base64); + mBinary = true; +} + +bool Attachment::isURI() const +{ + return !mBinary; +} + +QString Attachment::uri() const +{ + if (!mBinary) + return mData; + else + return QString::null; +} + +void Attachment::setURI(const QString& uri) +{ + mData = uri; + mBinary = false; +} + +bool Attachment::isBinary() const +{ + return mBinary; +} + +char *Attachment::data() const +{ + if (mBinary) + return mData.utf8().data(); + else + return 0; +} + +void Attachment::setData(const char *base64) +{ + mData = QString::fromUtf8(base64); + mBinary = true; +} + +QString Attachment::mimeType() const +{ + return mMimeType; +} + +void Attachment::setMimeType(const QString& mime) +{ + mMimeType = mime; +} + diff --git a/libkcal/attachment.h b/libkcal/attachment.h new file mode 100644 index 0000000..cdf2458 --- a/dev/null +++ b/libkcal/attachment.h @@ -0,0 +1,69 @@ +/* + This file is part of libkcal. + Copyright (c) 2002 Michael Brade <brade@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef _ATTACHMENT_H +#define _ATTACHMENT_H + +#include <qstring.h> + + +namespace KCal { + +/** + * This class represents information related to an attachment. + */ +class Attachment +{ +public: + /** + * Create a Reference to some URI. + * @param uri the uri this attachment refers to + * @param mime the mime type of the resource being linked to + */ + Attachment(const QString& uri, const QString& mime = QString::null); + + /** + * Create a binary attachment. + * @param base64 the attachment in base64 format + * @param mime the mime type of the attachment + */ + Attachment(const char *base64, const QString& mime = QString::null); + + /* The VALUE parameter in Cal */ + bool isURI() const; + QString uri() const; + void setURI(const QString& uri); + + bool isBinary() const; + char *data() const; + void setData(const char *base64); + + /* The optional FMTTYPE parameter in iCal */ + QString mimeType() const; + void setMimeType(const QString& mime); +private: + QString mMimeType; + QString mData; + bool mBinary; +}; + +} + +#endif diff --git a/libkcal/attendee.cpp b/libkcal/attendee.cpp new file mode 100644 index 0000000..41c6fcd --- a/dev/null +++ b/libkcal/attendee.cpp @@ -0,0 +1,167 @@ +/* + This file is part of libkcal. + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include <qstringlist.h> + +#include <kdebug.h> +#include <klocale.h> + +#include "attendee.h" + +using namespace KCal; + +Attendee::Attendee(const QString &name, const QString &email, bool _rsvp, Attendee::PartStat s, + Attendee::Role r,const QString &u) : + Person(name,email) +{ + mFlag = TRUE; + mRSVP = _rsvp; + mStatus = s; + mRole = r; + mUid = u; +} + +Attendee::~Attendee() +{ +} + + +bool KCal::operator==( const Attendee& a1, const Attendee& a2 ) +{ + return ( operator==( (const Person&)a1, (const Person&) a2 ) && + a1.RSVP() == a2.RSVP() && + a1.role() == a2.role() && + a1.status() == a2.status() && + a1.uid() == a2.uid() ); +} + + +void Attendee::setStatus(Attendee::PartStat s) +{ + mStatus = s; +} + +Attendee::PartStat Attendee::status() const +{ + return mStatus; +} + +QString Attendee::statusStr() const +{ + return statusName(mStatus); +} + +QString Attendee::statusName( Attendee::PartStat s ) +{ + switch (s) { + default: + case NeedsAction: + return i18n("Needs Action"); + break; + case Accepted: + return i18n("Accepted"); + break; + case Declined: + return i18n("Declined"); + break; + case Tentative: + return i18n("Tentative"); + break; + case Delegated: + return i18n("Delegated"); + break; + case Completed: + return i18n("Completed"); + break; + case InProcess: + return i18n("In Process"); + break; + } +} + +QStringList Attendee::statusList() +{ + QStringList list; + list << statusName(NeedsAction); + list << statusName(Accepted); + list << statusName(Declined); + list << statusName(Tentative); + list << statusName(Delegated); + list << statusName(Completed); + list << statusName(InProcess); + + return list; +} + + +void Attendee::setRole(Attendee::Role r) +{ + mRole = r; +} + +Attendee::Role Attendee::role() const +{ + return mRole; +} + +QString Attendee::roleStr() const +{ + return roleName(mRole); +} + +void Attendee::setUid(QString uid) +{ + mUid = uid; +} + +QString Attendee::uid() const +{ + return mUid; +} + +QString Attendee::roleName( Attendee::Role r ) +{ + switch (r) { + case Chair: + return i18n("Chair"); + break; + default: + case ReqParticipant: + return i18n("Participant"); + break; + case OptParticipant: + return i18n("Optional Participant"); + break; + case NonParticipant: + return i18n("Observer"); + break; + } +} + +QStringList Attendee::roleList() +{ + QStringList list; + list << roleName(ReqParticipant); + list << roleName(OptParticipant); + list << roleName(NonParticipant); + list << roleName(Chair); + + return list; +} diff --git a/libkcal/attendee.h b/libkcal/attendee.h new file mode 100644 index 0000000..1bd2ff3 --- a/dev/null +++ b/libkcal/attendee.h @@ -0,0 +1,96 @@ +/* + This file is part of libkcal. + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef _ATTENDEE_H +#define _ATTENDEE_H + +#include <qstring.h> + +#include "person.h" + +namespace KCal { + +/** + This class represents information related to an attendee of an event. +*/ +class Attendee : public Person +{ + public: + enum PartStat { NeedsAction, Accepted, Declined, Tentative, + Delegated, Completed, InProcess }; + enum Role { ReqParticipant, OptParticipant, NonParticipant, Chair }; + + /** + Create Attendee. + + @param name Name + @param email Email address + @param rsvp Request for reply + @param status Status (see enum for list) + @param role Role + */ + Attendee(const QString& name, const QString &email, + bool rsvp=false, PartStat status=NeedsAction, + Role role=ReqParticipant,const QString& u=QString::null); + /** Destruct Attendee */ + virtual ~Attendee(); + + /** Set role of Attendee. List of roles still has to be documented. */ + void setRole( Role ); + /** Return role of Attendee. */ + Role role() const; + /** Return role as clear text string */ + QString roleStr() const; + static QString roleName( Role ); + static QStringList roleList(); + + /** Holds the uid of the attendee, if applicable **/ + QString uid() const; + void setUid (QString); + + /** Set status. See enum for definitions of possible values */ + void setStatus(PartStat s); + /** Return status. */ + PartStat status() const; + /** Return status as human-readable string. */ + QString statusStr() const; + static QString statusName( PartStat ); + static QStringList statusList(); + + /** Set if Attendee is asked to reply. */ + void setRSVP(bool r) { mRSVP = r; } + /** Return, if Attendee is asked to reply. */ + bool RSVP() const { return mRSVP; } + + private: + bool mRSVP; + Role mRole; + PartStat mStatus; + QString mUid; + + // used to tell whether we have need to mail this person or not. + bool mFlag; +}; + + bool operator==( const Attendee& a1, const Attendee& a2 ); + +} + +#endif diff --git a/libkcal/calendar.cpp b/libkcal/calendar.cpp new file mode 100644 index 0000000..dc198bd --- a/dev/null +++ b/libkcal/calendar.cpp @@ -0,0 +1,426 @@ +/* + This file is part of libkcal. + Copyright (c) 1998 Preston Brown + Copyright (c) 2000,2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include <stdlib.h> +#include <time.h> + +#include <kdebug.h> +#include <kglobal.h> +#include <klocale.h> + +#include "exceptions.h" +#include "calfilter.h" + +#include "calendar.h" + +using namespace KCal; + +Calendar::Calendar() +{ + + init(); + setTimeZoneId( i18n (" 00:00 Europe/London(UTC)") ); +} + +Calendar::Calendar( const QString &timeZoneId ) +{ + + init(); + setTimeZoneId(timeZoneId); +} + +void Calendar::init() +{ + mObserver = 0; + mNewObserver = false; + + mModified = false; + + // Setup default filter, which does nothing + mDefaultFilter = new CalFilter; + mFilter = mDefaultFilter; + mFilter->setEnabled(false); + + // initialize random numbers. This is a hack, and not + // even that good of one at that. +// srandom(time(0)); + + // user information... + setOwner(i18n("Unknown Name")); + setEmail(i18n("unknown@nowhere")); + +#if 0 + tmpStr = KOPrefs::instance()->mTimeZone; +// kdDebug(5800) << "Calendar::Calendar(): TimeZone: " << tmpStr << endl; + int dstSetting = KOPrefs::instance()->mDaylightSavings; + extern long int timezone; + struct tm *now; + time_t curtime; + curtime = time(0); + now = localtime(&curtime); + int hourOff = - ((timezone / 60) / 60); + if (now->tm_isdst) + hourOff += 1; + QString tzStr; + tzStr.sprintf("%.2d%.2d", + hourOff, + abs((timezone / 60) % 60)); + + // if no time zone was in the config file, write what we just discovered. + if (tmpStr.isEmpty()) { +// KOPrefs::instance()->mTimeZone = tzStr; + } else { + tzStr = tmpStr; + } + + // if daylight savings has changed since last load time, we need + // to rewrite these settings to the config file. + if ((now->tm_isdst && !dstSetting) || + (!now->tm_isdst && dstSetting)) { + KOPrefs::instance()->mTimeZone = tzStr; + KOPrefs::instance()->mDaylightSavings = now->tm_isdst; + } + + setTimeZone(tzStr); +#endif + +// KOPrefs::instance()->writeConfig(); +} + +Calendar::~Calendar() +{ + delete mDefaultFilter; +} + +const QString &Calendar::getOwner() const +{ + return mOwner; +} + +void Calendar::setOwner(const QString &os) +{ + int i; + mOwner = os; + i = mOwner.find(','); + if (i != -1) + mOwner = mOwner.left(i); + + setModified( true ); +} + +void Calendar::setTimeZone(const QString & tz) +{ + bool neg = FALSE; + int hours, minutes; + QString tmpStr(tz); + + if (tmpStr.left(1) == "-") + neg = TRUE; + if (tmpStr.left(1) == "-" || tmpStr.left(1) == "+") + tmpStr.remove(0, 1); + hours = tmpStr.left(2).toInt(); + if (tmpStr.length() > 2) + minutes = tmpStr.right(2).toInt(); + else + minutes = 0; + mTimeZone = (60*hours+minutes); + if (neg) + mTimeZone = -mTimeZone; + mLocalTime = false; + + setModified( true ); +} + +QString Calendar::getTimeZoneStr() const +{ + if (mLocalTime) + return ""; + QString tmpStr; + int hours = abs(mTimeZone / 60); + int minutes = abs(mTimeZone % 60); + bool neg = mTimeZone < 0; + + tmpStr.sprintf("%c%.2d%.2d", + (neg ? '-' : '+'), + hours, minutes); + return tmpStr; +} + +void Calendar::setTimeZone(int tz) +{ + mTimeZone = tz; + mLocalTime = false; + + setModified( true ); +} + +int Calendar::getTimeZone() const +{ + return mTimeZone; +} + +void Calendar::setTimeZoneId(const QString &id) +{ + mTimeZoneId = id; + mLocalTime = false; + mTimeZone = KGlobal::locale()->timezoneOffset(mTimeZoneId); + if ( mTimeZone > 1000) + setLocalTime(); + //qDebug("Calendar::setTimeZoneOffset %s %d ",mTimeZoneId.latin1(), mTimeZone); + setModified( true ); +} + +QString Calendar::timeZoneId() const +{ + return mTimeZoneId; +} + +void Calendar::setLocalTime() +{ + //qDebug("Calendar::setLocalTime() "); + mLocalTime = true; + mTimeZone = 0; + mTimeZoneId = ""; + + setModified( true ); +} + +bool Calendar::isLocalTime() const +{ + return mLocalTime; +} + +const QString &Calendar::getEmail() +{ + return mOwnerEmail; +} + +void Calendar::setEmail(const QString &e) +{ + mOwnerEmail = e; + + setModified( true ); +} + +void Calendar::setFilter(CalFilter *filter) +{ + mFilter = filter; +} + +CalFilter *Calendar::filter() +{ + return mFilter; +} + +QPtrList<Incidence> Calendar::incidences() +{ + QPtrList<Incidence> incidences; + + Incidence *i; + + QPtrList<Event> e = events(); + for( i = e.first(); i; i = e.next() ) incidences.append( i ); + + QPtrList<Todo> t = todos(); + for( i = t.first(); i; i = t.next() ) incidences.append( i ); + + QPtrList<Journal> j = journals(); + for( i = j.first(); i; i = j.next() ) incidences.append( i ); + + return incidences; +} + +QPtrList<Incidence> Calendar::rawIncidences() +{ + QPtrList<Incidence> incidences; + + Incidence *i; + + QPtrList<Event> e = rawEvents(); + for( i = e.first(); i; i = e.next() ) incidences.append( i ); + + QPtrList<Todo> t = rawTodos(); + for( i = t.first(); i; i = t.next() ) incidences.append( i ); + + QPtrList<Journal> j = journals(); + for( i = j.first(); i; i = j.next() ) incidences.append( i ); + + return incidences; +} + +QPtrList<Event> Calendar::events( const QDate &date, bool sorted ) +{ + QPtrList<Event> el = rawEventsForDate(date,sorted); + mFilter->apply(&el); + return el; +} + +QPtrList<Event> Calendar::events( const QDateTime &qdt ) +{ + QPtrList<Event> el = rawEventsForDate(qdt); + mFilter->apply(&el); + return el; +} + +QPtrList<Event> Calendar::events( const QDate &start, const QDate &end, + bool inclusive) +{ + QPtrList<Event> el = rawEvents(start,end,inclusive); + mFilter->apply(&el); + return el; +} + +QPtrList<Event> Calendar::events() +{ + QPtrList<Event> el = rawEvents(); + mFilter->apply(&el); + return el; +} + + +bool Calendar::addIncidence(Incidence *i) +{ + Incidence::AddVisitor<Calendar> v(this); + + return i->accept(v); +} +void Calendar::deleteIncidence(Incidence *in) +{ + if ( in->type() == "Event" ) + deleteEvent( (Event*) in ); + else if ( in->type() =="Todo" ) + deleteTodo( (Todo*) in); + else if ( in->type() =="Journal" ) + deleteJournal( (Journal*) in ); +} + +Incidence* Calendar::incidence( const QString& uid ) +{ + Incidence* i; + + if( (i = todo( uid )) != 0 ) + return i; + if( (i = event( uid )) != 0 ) + return i; + if( (i = journal( uid )) != 0 ) + return i; + + return 0; +} + +QPtrList<Todo> Calendar::todos() +{ + QPtrList<Todo> tl = rawTodos(); + mFilter->apply( &tl ); + return tl; +} + +// When this is called, the todo have already been added to the calendar. +// This method is only about linking related todos +void Calendar::setupRelations( Incidence *incidence ) +{ + QString uid = incidence->uid(); + //qDebug("Calendar::setupRelations "); + // First, go over the list of orphans and see if this is their parent + while( Incidence* i = mOrphans[ uid ] ) { + mOrphans.remove( uid ); + i->setRelatedTo( incidence ); + incidence->addRelation( i ); + mOrphanUids.remove( i->uid() ); + } + + // Now see about this incidences parent + if( !incidence->relatedTo() && !incidence->relatedToUid().isEmpty() ) { + // This incidence has a uid it is related to, but is not registered to it yet + // Try to find it + Incidence* parent = this->incidence( incidence->relatedToUid() ); + if( parent ) { + // Found it + incidence->setRelatedTo( parent ); + parent->addRelation( incidence ); + } else { + // Not found, put this in the mOrphans list + mOrphans.insert( incidence->relatedToUid(), incidence ); + mOrphanUids.insert( incidence->uid(), incidence ); + } + } +} + +// If a task with subtasks is deleted, move it's subtasks to the orphans list +void Calendar::removeRelations( Incidence *incidence ) +{ + // qDebug("Calendar::removeRelations "); + QString uid = incidence->uid(); + + QPtrList<Incidence> relations = incidence->relations(); + for( Incidence* i = relations.first(); i; i = relations.next() ) + if( !mOrphanUids.find( i->uid() ) ) { + mOrphans.insert( uid, i ); + mOrphanUids.insert( i->uid(), i ); + i->setRelatedTo( 0 ); + i->setRelatedToUid( uid ); + } + + // If this incidence is related to something else, tell that about it + if( incidence->relatedTo() ) + incidence->relatedTo()->removeRelation( incidence ); + + // Remove this one from the orphans list + if( mOrphanUids.remove( uid ) ) + // This incidence is located in the orphans list - it should be removed + if( !( incidence->relatedTo() != 0 && mOrphans.remove( incidence->relatedTo()->uid() ) ) ) { + // Removing wasn't that easy + for( QDictIterator<Incidence> it( mOrphans ); it.current(); ++it ) { + if( it.current()->uid() == uid ) { + mOrphans.remove( it.currentKey() ); + break; + } + } + } +} + +void Calendar::registerObserver( Observer *observer ) +{ + mObserver = observer; + mNewObserver = true; +} + +void Calendar::setModified( bool modified ) +{ + if ( mObserver ) mObserver->calendarModified( modified, this ); + if ( modified != mModified || mNewObserver ) { + mNewObserver = false; + // if ( mObserver ) mObserver->calendarModified( modified, this ); + mModified = modified; + } +} + +void Calendar::setLoadedProductId( const QString &id ) +{ + mLoadedProductId = id; +} + +QString Calendar::loadedProductId() +{ + return mLoadedProductId; +} + +#include "calendar.moc" diff --git a/libkcal/calendar.h b/libkcal/calendar.h new file mode 100644 index 0000000..7a85e74 --- a/dev/null +++ b/libkcal/calendar.h @@ -0,0 +1,349 @@ +/* + This file is part of libkcal. + Copyright (c) 1998 Preston Brown + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef CALENDAR_H +#define CALENDAR_H + +#include <qobject.h> +#include <qstring.h> +#include <qdatetime.h> +#include <qptrlist.h> +#include <qdict.h> + +#include "customproperties.h" +#include "event.h" +#include "todo.h" +#include "journal.h" + +#define _TIME_ZONE "-0500" /* hardcoded, overridden in config file. */ + +class KConfig; + +namespace KCal { + +class CalFilter; + +/** + This is the main "calendar" object class for KOrganizer. It holds + information like all appointments/events, user information, etc. etc. + one calendar is associated with each CalendarView (@see calendarview.h). + This is an abstract base class defining the interface to a calendar. It is + implemented by subclasses like @see CalendarLocal, which use different + methods to store and access the data. + + Ownership of events etc. is handled by the following policy: As soon as an + event (or any other subclass of IncidenceBase) object is added to the + Calendar by addEvent() it is owned by the Calendar object. The Calendar takes + care of deleting it. All Events returned by the query functions are returned + as pointers, that means all changes to the returned events are immediately + visible in the Calendar. You shouldn't delete any Event object you get from + Calendar. +*/ +class Calendar : public QObject, public CustomProperties, + public IncidenceBase::Observer +{ + Q_OBJECT +public: + Calendar(); + Calendar(const QString &timeZoneId); + virtual ~Calendar(); + void deleteIncidence(Incidence *in); + /** + Clears out the current calendar, freeing all used memory etc. + */ + virtual void close() = 0; + + /** + Sync changes in memory to persistant storage. + */ + virtual void save() = 0; + + virtual bool isSaving() { return false; } + + /** + Return the owner of the calendar's full name. + */ + const QString &getOwner() const; + /** + Set the owner of the calendar. Should be owner's full name. + */ + void setOwner( const QString &os ); + /** + Return the email address of the calendar owner. + */ + const QString &getEmail(); + /** + Set the email address of the calendar owner. + */ + void setEmail( const QString & ); + + /** + Set time zone from a timezone string (e.g. -2:00) + */ + void setTimeZone( const QString &tz ); + /** + Set time zone from a minutes value (e.g. -60) + */ + void setTimeZone( int tz ); + /** + Return time zone as offest in minutes. + */ + int getTimeZone() const; + /** + Compute an ISO 8601 format string from the time zone. + */ + QString getTimeZoneStr() const; + /** + Set time zone id (see /usr/share/zoneinfo/zone.tab for list of legal + values). + */ + void setTimeZoneId( const QString & ); + /** + Return time zone id. + */ + QString timeZoneId() const; + /** + Use local time, not UTC or a time zone. + */ + void setLocalTime(); + /** + Return whether local time is being used. + */ + bool isLocalTime() const; + + /** + Add an incidence to calendar. + + @return true on success, false on error. + */ + virtual bool addIncidence( Incidence * ); + /** + Return filtered list of all incidences of this calendar. + */ + virtual QPtrList<Incidence> incidences(); + + /** + Return unfiltered list of all incidences of this calendar. + */ + virtual QPtrList<Incidence> rawIncidences(); + + /** + Adds a Event to this calendar object. + @param anEvent a pointer to the event to add + + @return true on success, false on error. + */ + virtual bool addEventNoDup( Event *event ) = 0; + virtual bool addEvent( Event *anEvent ) = 0; + /** + Delete event from calendar. + */ + virtual void deleteEvent( Event * ) = 0; + /** + Retrieves an event on the basis of the unique string ID. + */ + virtual Event *event( const QString &UniqueStr ) = 0; + virtual Event *event( int ) = 0; + /** + Builds and then returns a list of all events that match for the + date specified. useful for dayView, etc. etc. + The calendar filter is applied. + */ + QPtrList<Event> events( const QDate &date, bool sorted = false); + /** + Get events, which occur on the given date. + The calendar filter is applied. + */ + QPtrList<Event> events( const QDateTime &qdt ); + /** + Get events in a range of dates. If inclusive is set to true, only events + are returned, which are completely included in the range. + The calendar filter is applied. + */ + QPtrList<Event> events( const QDate &start, const QDate &end, + bool inclusive = false); + /** + Return filtered list of all events in calendar. + */ + virtual QPtrList<Event> events(); + /** + Return unfiltered list of all events in calendar. + */ + virtual QPtrList<Event> rawEvents() = 0; + + /** + Add a todo to the todolist. + + @return true on success, false on error. + */ + virtual bool addTodo( Todo *todo ) = 0; + virtual bool addTodoNoDup( Todo *todo ) = 0; + /** + Remove a todo from the todolist. + */ + virtual void deleteTodo( Todo * ) = 0; + virtual void deleteJournal( Journal * ) = 0; + /** + Return filterd list of todos. + */ + virtual QPtrList<Todo> todos(); + /** + Searches todolist for an event with this unique string identifier, + returns a pointer or null. + */ + virtual Todo *todo( const QString &uid ) = 0; + virtual Todo *todo( int ) = 0; + /** + Returns list of todos due on the specified date. + */ + virtual QPtrList<Todo> todos( const QDate &date ) = 0; + /** + Return unfiltered list of todos. + */ + virtual QPtrList<Todo> rawTodos() = 0; + + /** + Add a Journal entry to calendar. + + @return true on success, false on error. + */ + virtual bool addJournal( Journal * ) = 0; + /** + Return Journal for given date. + */ + virtual Journal *journal( const QDate & ) = 0; + /** + Return Journal with given UID. + */ + virtual Journal *journal( const QString &UID ) = 0; + /** + Return list of all Journal entries. + */ + virtual QPtrList<Journal> journals() = 0; + + /** + Searches all incidence types for an incidence with this unique + string identifier, returns a pointer or null. + */ + Incidence* incidence( const QString&UID ); + + /** + Setup relations for an incidence. + */ + virtual void setupRelations( Incidence * ); + /** + Remove all relations to an incidence + */ + virtual void removeRelations( Incidence * ); + + /** + Set calendar filter, which filters events for the events() functions. + The Filter object is owned by the caller. + */ + void setFilter( CalFilter * ); + /** + Return calendar filter. + */ + CalFilter *filter(); + virtual QDateTime nextAlarm( int daysTo ) = 0; + virtual QString nextSummary( ) const = 0; + virtual void reInitAlarmSettings() = 0; + virtual QDateTime nextAlarmEventDateTime() const = 0; + virtual void checkAlarmForIncidence( Incidence *, bool ) = 0; + /** + Return all alarms, which ocur in the given time interval. + */ + virtual Alarm::List alarms( const QDateTime &from, + const QDateTime &to ) = 0; + + class Observer { + public: + virtual void calendarModified( bool, Calendar * ) = 0; + }; + + void registerObserver( Observer * ); + + void setModified( bool ); + + /** + Set product id returned by loadedProductId(). This function is only + useful for the calendar loading code. + */ + void setLoadedProductId( const QString & ); + /** + Return product id taken from file that has been loaded. Returns + QString::null, if no calendar has been loaded. + */ + QString loadedProductId(); + + signals: + void calendarChanged(); + void calendarSaved(); + void calendarLoaded(); + void addAlarm(const QDateTime &qdt, const QString ¬i ); + void removeAlarm(const QDateTime &qdt, const QString ¬i ); + + protected: + /** + Get unfiltered events, which occur on the given date. + */ + virtual QPtrList<Event> rawEventsForDate( const QDateTime &qdt ) = 0; + /** + Get unfiltered events, which occur on the given date. + */ + virtual QPtrList<Event> rawEventsForDate( const QDate &date, + bool sorted = false ) = 0; + /** + Get events in a range of dates. If inclusive is set to true, only events + are returned, which are completely included in the range. + */ + virtual QPtrList<Event> rawEvents( const QDate &start, const QDate &end, + bool inclusive = false ) = 0; + Incidence *mNextAlarmIncidence; + +private: + void init(); + + QString mOwner; // who the calendar belongs to + QString mOwnerEmail; // email address of the owner + int mTimeZone; // timezone OFFSET from GMT (MINUTES) + bool mLocalTime; // use local time, not UTC or a time zone + + CalFilter *mFilter; + CalFilter *mDefaultFilter; + + QString mTimeZoneId; + + Observer *mObserver; + bool mNewObserver; + + bool mModified; + + QString mLoadedProductId; + + // This list is used to put together related todos + QDict<Incidence> mOrphans; + QDict<Incidence> mOrphanUids; +}; + +} + +#endif diff --git a/libkcal/calendar.moc b/libkcal/calendar.moc new file mode 100644 index 0000000..e69de29 --- a/dev/null +++ b/libkcal/calendar.moc diff --git a/libkcal/calendarlocal.cpp b/libkcal/calendarlocal.cpp new file mode 100644 index 0000000..8ff8b14 --- a/dev/null +++ b/libkcal/calendarlocal.cpp @@ -0,0 +1,655 @@ +/* + This file is part of libkcal. + + Copyright (c) 1998 Preston Brown + Copyright (c) 2001,2003 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include <qdatetime.h> +#include <qstring.h> +#include <qptrlist.h> + +#include <kdebug.h> +#include <kconfig.h> +#include <kglobal.h> +#include <klocale.h> + +#include "vcaldrag.h" +#include "vcalformat.h" +#include "icalformat.h" +#include "exceptions.h" +#include "incidence.h" +#include "journal.h" +#include "filestorage.h" +#include "calfilter.h" + +#include "calendarlocal.h" + +// #ifndef DESKTOP_VERSION +// #include <qtopia/alarmserver.h> +// #endif +using namespace KCal; + +CalendarLocal::CalendarLocal() + : Calendar() +{ + init(); +} + +CalendarLocal::CalendarLocal(const QString &timeZoneId) + : Calendar(timeZoneId) +{ + init(); +} + +void CalendarLocal::init() +{ + mNextAlarmIncidence = 0; +} + + +CalendarLocal::~CalendarLocal() +{ + close(); +} + +bool CalendarLocal::load( const QString &fileName ) +{ + FileStorage storage( this, fileName ); + return storage.load(); +} + +bool CalendarLocal::save( const QString &fileName, CalFormat *format ) +{ + FileStorage storage( this, fileName, format ); + return storage.save(); +} + +void CalendarLocal::close() +{ + mEventList.setAutoDelete( true ); + mTodoList.setAutoDelete( true ); + mJournalList.setAutoDelete( false ); + + mEventList.clear(); + mTodoList.clear(); + mJournalList.clear(); + + mEventList.setAutoDelete( false ); + mTodoList.setAutoDelete( false ); + mJournalList.setAutoDelete( false ); + + setModified( false ); +} +bool CalendarLocal::addEventNoDup( Event *event ) +{ + Event * eve; + for ( eve = mEventList.first(); eve ; eve = mEventList.next() ) { + if ( *eve == *event ) { + //qDebug("CalendarLocal::Duplicate event found! Not inserted! "); + return false; + } + } + return addEvent( event ); +} + +bool CalendarLocal::addEvent( Event *event ) +{ + insertEvent( event ); + + event->registerObserver( this ); + + setModified( true ); + + return true; +} + +void CalendarLocal::deleteEvent( Event *event ) +{ + + + if ( mEventList.removeRef( event ) ) { + setModified( true ); + } +} + + +Event *CalendarLocal::event( const QString &uid ) +{ + + Event *event; + + for ( event = mEventList.first(); event; event = mEventList.next() ) { + if ( event->uid() == uid ) { + return event; + } + } + + return 0; +} +bool CalendarLocal::addTodoNoDup( Todo *todo ) +{ + Todo * eve; + for ( eve = mTodoList.first(); eve ; eve = mTodoList.next() ) { + if ( *eve == *todo ) { + //qDebug("duplicate todo found! not inserted! "); + return false; + } + } + return addTodo( todo ); +} +bool CalendarLocal::addTodo( Todo *todo ) +{ + mTodoList.append( todo ); + + todo->registerObserver( this ); + + // Set up subtask relations + setupRelations( todo ); + + setModified( true ); + + return true; +} + +void CalendarLocal::deleteTodo( Todo *todo ) +{ + // Handle orphaned children + removeRelations( todo ); + + if ( mTodoList.removeRef( todo ) ) { + setModified( true ); + } +} + +QPtrList<Todo> CalendarLocal::rawTodos() +{ + return mTodoList; +} +Todo *CalendarLocal::todo( int id ) +{ + Todo *todo; + for ( todo = mTodoList.first(); todo; todo = mTodoList.next() ) { + if ( todo->zaurusId() == id ) return todo; + } + + return 0; +} + +Event *CalendarLocal::event( int id ) +{ + Event *todo; + for ( todo = mEventList.first(); todo; todo = mEventList.next() ) { + if ( todo->zaurusId() == id ) return todo; + } + + return 0; +} +Todo *CalendarLocal::todo( const QString &uid ) +{ + Todo *todo; + for ( todo = mTodoList.first(); todo; todo = mTodoList.next() ) { + if ( todo->uid() == uid ) return todo; + } + + return 0; +} +QString CalendarLocal::nextSummary() const +{ + return mNextSummary; +} +QDateTime CalendarLocal::nextAlarmEventDateTime() const +{ + return mNextAlarmEventDateTime; +} +void CalendarLocal::checkAlarmForIncidence( Incidence * incidence, bool deleted) +{ + //mNextAlarmIncidence + //mNextAlarmDateTime + //return mNextSummary; + //return mNextAlarmEventDateTime; + bool newNextAlarm = false; + bool computeNextAlarm = false; + bool ok; + int offset; + QDateTime nextA; + // QString nextSum; + //QDateTime nextEvent; + if ( mNextAlarmIncidence == 0 || incidence == 0 ) { + computeNextAlarm = true; + } else { + if ( ! deleted ) { + nextA = incidence->getNextAlarmDateTime(& ok, &offset ) ; + if ( ok ) { + if ( nextA < mNextAlarmDateTime ) { + deRegisterAlarm(); + mNextAlarmDateTime = nextA; + mNextSummary = incidence->summary(); + mNextAlarmEventDateTime = nextA.addSecs(offset ) ; + mNextAlarmEventDateTimeString = KGlobal::locale()->formatDateTime(mNextAlarmEventDateTime); + newNextAlarm = true; + mNextAlarmIncidence = incidence; + } else { + if ( incidence == mNextAlarmIncidence ) { + computeNextAlarm = true; + } + } + } else { + if ( mNextAlarmIncidence == incidence ) { + computeNextAlarm = true; + } + } + } else { // deleted + if ( incidence == mNextAlarmIncidence ) { + computeNextAlarm = true; + } + } + } + if ( computeNextAlarm ) { + deRegisterAlarm(); + nextA = nextAlarm( 1000 ); + if (! mNextAlarmIncidence ) { + return; + } + newNextAlarm = true; + } + if ( newNextAlarm ) + registerAlarm(); +} +QString CalendarLocal:: getAlarmNotification() +{ + QString ret; + // this should not happen + if (! mNextAlarmIncidence ) + return "cal_alarm"+ mNextSummary.left( 25 )+"\n"+mNextAlarmEventDateTimeString; + Alarm* alarm = mNextAlarmIncidence->alarms().first(); + if ( alarm->type() == Alarm::Procedure ) { + ret = "proc_alarm" + alarm->programFile()+"+++"; + } else { + ret = "audio_alarm" +alarm->audioFile() +"+++"; + } + ret += "cal_alarm"+ mNextSummary.left( 25 ); + if ( mNextSummary.length() > 25 ) + ret += "\n" + mNextSummary.mid(25, 25 ); + ret+= "\n"+mNextAlarmEventDateTimeString; + return ret; +} + +void CalendarLocal::registerAlarm() +{ + mLastAlarmNotificationString = getAlarmNotification(); + // qDebug("++ register Alarm %s %s",mNextAlarmDateTime.toString().latin1(), mLastAlarmNotificationString.latin1() ); + emit addAlarm ( mNextAlarmDateTime, mLastAlarmNotificationString ); +// #ifndef DESKTOP_VERSION +// AlarmServer::addAlarm ( mNextAlarmDateTime,"koalarm", mLastAlarmNotificationString.latin1() ); +// #endif +} +void CalendarLocal::deRegisterAlarm() +{ + if ( mLastAlarmNotificationString.isNull() ) + return; + //qDebug("-- deregister Alarm %s ", mLastAlarmNotificationString.latin1() ); + + emit removeAlarm ( mNextAlarmDateTime, mLastAlarmNotificationString ); +// #ifndef DESKTOP_VERSION +// AlarmServer::deleteAlarm (mNextAlarmDateTime ,"koalarm" ,mLastAlarmNotificationString.latin1() ); +// #endif +} + +QPtrList<Todo> CalendarLocal::todos( const QDate &date ) +{ + QPtrList<Todo> todos; + + Todo *todo; + for ( todo = mTodoList.first(); todo; todo = mTodoList.next() ) { + if ( todo->hasDueDate() && todo->dtDue().date() == date ) { + todos.append( todo ); + } + } + + filter()->apply( &todos ); + return todos; +} +void CalendarLocal::reInitAlarmSettings() +{ + if ( !mNextAlarmIncidence ) { + nextAlarm( 1000 ); + } + deRegisterAlarm(); + mNextAlarmIncidence = 0; + checkAlarmForIncidence( 0, false ); + +} + + + +QDateTime CalendarLocal::nextAlarm( int daysTo ) +{ + QDateTime nextA = QDateTime::currentDateTime().addDays( daysTo ); + QDateTime start = QDateTime::currentDateTime().addSecs( 30 ); + QDateTime next; + Event *e; + bool ok; + bool found = false; + int offset; + mNextAlarmIncidence = 0; + for( e = mEventList.first(); e; e = mEventList.next() ) { + next = e->getNextAlarmDateTime(& ok, &offset ) ; + if ( ok ) { + if ( next < nextA ) { + nextA = next; + found = true; + mNextSummary = e->summary(); + mNextAlarmEventDateTime = next.addSecs(offset ) ; + mNextAlarmIncidence = (Incidence *) e; + } + } + } + Todo *t; + for( t = mTodoList.first(); t; t = mTodoList.next() ) { + next = t->getNextAlarmDateTime(& ok, &offset ) ; + if ( ok ) { + if ( next < nextA ) { + nextA = next; + found = true; + mNextSummary = t->summary(); + mNextAlarmEventDateTime = next.addSecs(offset ); + mNextAlarmIncidence = (Incidence *) t; + } + } + } + if ( mNextAlarmIncidence ) { + mNextAlarmEventDateTimeString = KGlobal::locale()->formatDateTime(mNextAlarmEventDateTime); + mNextAlarmDateTime = nextA; + } + return nextA; +} +Alarm::List CalendarLocal::alarmsTo( const QDateTime &to ) +{ + return alarms( QDateTime( QDate( 1900, 1, 1 ) ), to ); +} + +Alarm::List CalendarLocal::alarms( const QDateTime &from, const QDateTime &to ) +{ + kdDebug(5800) << "CalendarLocal::alarms(" << from.toString() << " - " + << to.toString() << ")\n"; + + Alarm::List alarms; + + Event *e; + + for( e = mEventList.first(); e; e = mEventList.next() ) { + if ( e->doesRecur() ) appendRecurringAlarms( alarms, e, from, to ); + else appendAlarms( alarms, e, from, to ); + } + + Todo *t; + for( t = mTodoList.first(); t; t = mTodoList.next() ) { + appendAlarms( alarms, t, from, to ); + } + + return alarms; +} + +void CalendarLocal::appendAlarms( Alarm::List &alarms, Incidence *incidence, + const QDateTime &from, const QDateTime &to ) +{ + QPtrList<Alarm> alarmList = incidence->alarms(); + Alarm *alarm; + for( alarm = alarmList.first(); alarm; alarm = alarmList.next() ) { +// kdDebug(5800) << "CalendarLocal::appendAlarms() '" << alarm->text() +// << "': " << alarm->time().toString() << " - " << alarm->enabled() << endl; + if ( alarm->enabled() ) { + if ( alarm->time() >= from && alarm->time() <= to ) { + kdDebug(5800) << "CalendarLocal::appendAlarms() '" << incidence->summary() + << "': " << alarm->time().toString() << endl; + alarms.append( alarm ); + } + } + } +} + +void CalendarLocal::appendRecurringAlarms( Alarm::List &alarms, + Incidence *incidence, + const QDateTime &from, + const QDateTime &to ) +{ + + QPtrList<Alarm> alarmList = incidence->alarms(); + Alarm *alarm; + QDateTime qdt; + for( alarm = alarmList.first(); alarm; alarm = alarmList.next() ) { + if (incidence->recursOn(from.date())) { + qdt.setTime(alarm->time().time()); + qdt.setDate(from.date()); + } + else qdt = alarm->time(); + // qDebug("1 %s %s %s", qdt.toString().latin1(), from.toString().latin1(), to.toString().latin1()); + if ( alarm->enabled() ) { + if ( qdt >= from && qdt <= to ) { + alarms.append( alarm ); + } + } + } +} + + +/****************************** PROTECTED METHODS ****************************/ + +// after changes are made to an event, this should be called. +void CalendarLocal::update( IncidenceBase *incidence ) +{ + incidence->setSyncStatus( Event::SYNCMOD ); + incidence->setLastModified( QDateTime::currentDateTime() ); + // we should probably update the revision number here, + // or internally in the Event itself when certain things change. + // need to verify with ical documentation. + + setModified( true ); +} + +void CalendarLocal::insertEvent( Event *event ) +{ + if ( mEventList.findRef( event ) < 0 ) mEventList.append( event ); +} + + +QPtrList<Event> CalendarLocal::rawEventsForDate( const QDate &qd, bool sorted ) +{ + QPtrList<Event> eventList; + + Event *event; + for( event = mEventList.first(); event; event = mEventList.next() ) { + if ( event->doesRecur() ) { + if ( event->isMultiDay() ) { + int extraDays = event->dtStart().date().daysTo( event->dtEnd().date() ); + int i; + for ( i = 0; i <= extraDays; i++ ) { + if ( event->recursOn( qd.addDays( -i ) ) ) { + eventList.append( event ); + break; + } + } + } else { + if ( event->recursOn( qd ) ) + eventList.append( event ); + } + } else { + if ( event->dtStart().date() <= qd && event->dtEnd().date() >= qd ) { + eventList.append( event ); + } + } + } + + if ( !sorted ) { + return eventList; + } + + // kdDebug(5800) << "Sorting events for date\n" << endl; + // now, we have to sort it based on dtStart.time() + QPtrList<Event> eventListSorted; + Event *sortEvent; + for ( event = eventList.first(); event; event = eventList.next() ) { + sortEvent = eventListSorted.first(); + int i = 0; + while ( sortEvent && event->dtStart().time()>=sortEvent->dtStart().time() ) + { + i++; + sortEvent = eventListSorted.next(); + } + eventListSorted.insert( i, event ); + } + return eventListSorted; +} + + +QPtrList<Event> CalendarLocal::rawEvents( const QDate &start, const QDate &end, + bool inclusive ) +{ + Event *event = 0; + + QPtrList<Event> eventList; + + // Get non-recurring events + for( event = mEventList.first(); event; event = mEventList.next() ) { + if ( event->doesRecur() ) { + QDate rStart = event->dtStart().date(); + bool found = false; + if ( inclusive ) { + if ( rStart >= start && rStart <= end ) { + // Start date of event is in range. Now check for end date. + // if duration is negative, event recurs forever, so do not include it. + if ( event->recurrence()->duration() == 0 ) { // End date set + QDate rEnd = event->recurrence()->endDate(); + if ( rEnd >= start && rEnd <= end ) { // End date within range + found = true; + } + } else if ( event->recurrence()->duration() > 0 ) { // Duration set + // TODO: Calculate end date from duration. Should be done in Event + // For now exclude all events with a duration. + } + } + } else { + bool founOne; + QDate next = event->getNextOccurence( start, &founOne ).date(); + if ( founOne ) { + if ( next <= end ) { + found = true; + } + } + + /* + // crap !!! + if ( rStart <= end ) { // Start date not after range + if ( rStart >= start ) { // Start date within range + found = true; + } else if ( event->recurrence()->duration() == -1 ) { // Recurs forever + found = true; + } else if ( event->recurrence()->duration() == 0 ) { // End date set + QDate rEnd = event->recurrence()->endDate(); + if ( rEnd >= start && rEnd <= end ) { // End date within range + found = true; + } + } else { // Duration set + // TODO: Calculate end date from duration. Should be done in Event + // For now include all events with a duration. + found = true; + } + } + */ + + } + + if ( found ) eventList.append( event ); + } else { + QDate s = event->dtStart().date(); + QDate e = event->dtEnd().date(); + + if ( inclusive ) { + if ( s >= start && e <= end ) { + eventList.append( event ); + } + } else { + if ( ( s >= start && s <= end ) || ( e >= start && e <= end ) ) { + eventList.append( event ); + } + } + } + } + + return eventList; +} + +QPtrList<Event> CalendarLocal::rawEventsForDate( const QDateTime &qdt ) +{ + return rawEventsForDate( qdt.date() ); +} + +QPtrList<Event> CalendarLocal::rawEvents() +{ + return mEventList; +} + +bool CalendarLocal::addJournal(Journal *journal) +{ + if ( journal->dtStart().isValid()) + kdDebug(5800) << "Adding Journal on " << journal->dtStart().toString() << endl; + else + kdDebug(5800) << "Adding Journal without a DTSTART" << endl; + + mJournalList.append(journal); + + journal->registerObserver( this ); + + setModified( true ); + + return true; +} + +void CalendarLocal::deleteJournal( Journal *journal ) +{ + if ( mJournalList.removeRef(journal) ) { + setModified( true ); + } +} + +Journal *CalendarLocal::journal( const QDate &date ) +{ +// kdDebug(5800) << "CalendarLocal::journal() " << date.toString() << endl; + + for ( Journal *it = mJournalList.first(); it; it = mJournalList.next() ) + if ( it->dtStart().date() == date ) + return it; + + return 0; +} + +Journal *CalendarLocal::journal( const QString &uid ) +{ + for ( Journal *it = mJournalList.first(); it; it = mJournalList.next() ) + if ( it->uid() == uid ) + return it; + + return 0; +} + +QPtrList<Journal> CalendarLocal::journals() +{ + return mJournalList; +} + diff --git a/libkcal/calendarlocal.h b/libkcal/calendarlocal.h new file mode 100644 index 0000000..a17cf11 --- a/dev/null +++ b/libkcal/calendarlocal.h @@ -0,0 +1,216 @@ +/* + This file is part of libkcal. + + Copyright (c) 1998 Preston Brown + Copyright (c) 2001,2003 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef KCAL_CALENDARLOCAL_H +#define KCAL_CALENDARLOCAL_H + +#include "calendar.h" + +namespace KCal { + +class CalFormat; + +/** + This class provides a calendar stored as a local file. +*/ +class CalendarLocal : public Calendar +{ + public: + /** + Constructs a new calendar, with variables initialized to sane values. + */ + CalendarLocal(); + /** + Constructs a new calendar, with variables initialized to sane values. + */ + CalendarLocal( const QString &timeZoneId ); + ~CalendarLocal(); + + /** + Loads a calendar on disk in vCalendar or iCalendar format into the current + calendar. Any information already present is lost. + @return true, if successfull, false on error. + @param fileName the name of the calendar on disk. + */ + bool load( const QString &fileName ); + /** + Writes out the calendar to disk in the specified \a format. + CalendarLocal takes ownership of the CalFormat object. + @return true, if successfull, false on error. + @param fileName the name of the file + */ + bool save( const QString &fileName, CalFormat *format = 0 ); + + /** + Clears out the current calendar, freeing all used memory etc. etc. + */ + void close(); + + void save() {} + + /** + Add Event to calendar. + */ + bool addEventNoDup( Event *event ); + bool addEvent( Event *event ); + /** + Deletes an event from this calendar. + */ + void deleteEvent( Event *event ); + + /** + Retrieves an event on the basis of the unique string ID. + */ + Event *event( const QString &uid ); + /** + Return unfiltered list of all events in calendar. + */ + QPtrList<Event> rawEvents(); + + /** + Add a todo to the todolist. + */ + bool addTodo( Todo *todo ); + bool addTodoNoDup( Todo *todo ); + /** + Remove a todo from the todolist. + */ + void deleteTodo( Todo * ); + /** + Searches todolist for an event with this unique string identifier, + returns a pointer or null. + */ + Todo *todo( const QString &uid ); + /** + Return list of all todos. + */ + QPtrList<Todo> rawTodos(); + /** + Returns list of todos due on the specified date. + */ + QPtrList<Todo> todos( const QDate &date ); + /** + Return list of all todos. + + Workaround because compiler does not recognize function of base class. + */ + QPtrList<Todo> todos() { return Calendar::todos(); } + + /** + Add a Journal entry to calendar. + */ + bool addJournal( Journal * ); + /** + Remove a Journal from the calendar. + */ + void deleteJournal( Journal * ); + /** + Return Journal for given date. + */ + Journal *journal( const QDate & ); + /** + Return Journal with given UID. + */ + Journal *journal( const QString &uid ); + /** + Return list of all Journals stored in calendar. + */ + QPtrList<Journal> journals(); + + /** + Return all alarms, which ocur in the given time interval. + */ + Alarm::List alarms( const QDateTime &from, const QDateTime &to ); + + /** + Return all alarms, which ocur before given date. + */ + Alarm::List alarmsTo( const QDateTime &to ); + + QDateTime nextAlarm( int daysTo ) ; + QDateTime nextAlarmEventDateTime() const; + void checkAlarmForIncidence( Incidence *, bool deleted ) ; + void registerAlarm(); + void deRegisterAlarm(); + QString getAlarmNotification(); + QString nextSummary() const ; + /** + This method should be called whenever a Event is modified directly + via it's pointer. It makes sure that the calendar is internally + consistent. + */ + void update( IncidenceBase *incidence ); + + /** + Builds and then returns a list of all events that match for the + date specified. useful for dayView, etc. etc. + */ + QPtrList<Event> rawEventsForDate( const QDate &date, bool sorted = false ); + /** + Get unfiltered events for date \a qdt. + */ + QPtrList<Event> rawEventsForDate( const QDateTime &qdt ); + /** + Get unfiltered events in a range of dates. If inclusive is set to true, + only events are returned, which are completely included in the range. + */ + QPtrList<Event> rawEvents( const QDate &start, const QDate &end, + bool inclusive = false ); + Todo *CalendarLocal::todo( int uid ); + Event *CalendarLocal::event( int uid ); + + + + protected: + + // Event* mNextAlarmEvent; + QString mNextSummary; + QString mNextAlarmEventDateTimeString; + QString mLastAlarmNotificationString; + QDateTime mNextAlarmEventDateTime; + QDateTime mNextAlarmDateTime; + void reInitAlarmSettings(); + + /** Notification function of IncidenceBase::Observer. */ + void incidenceUpdated( IncidenceBase *i ) { update( i ); } + + /** inserts an event into its "proper place" in the calendar. */ + void insertEvent( Event *event ); + + /** Append alarms of incidence in interval to list of alarms. */ + void appendAlarms( Alarm::List &alarms, Incidence *incidence, + const QDateTime &from, const QDateTime &to ); + + /** Append alarms of recurring events in interval to list of alarms. */ + void appendRecurringAlarms( Alarm::List &alarms, Incidence *incidence, + const QDateTime &from, const QDateTime &to ); + + private: + void init(); + + QPtrList<Event> mEventList; + QPtrList<Todo> mTodoList; + QPtrList<Journal> mJournalList; +}; + +} + +#endif diff --git a/libkcal/calendarresources.h b/libkcal/calendarresources.h new file mode 100644 index 0000000..a218dd7 --- a/dev/null +++ b/libkcal/calendarresources.h @@ -0,0 +1,17 @@ +#ifndef MICRO_KCAL_CALENDARRESOURCES_H +#define MICRO_KCAL_CALENDARRESOURCES_H + +#include "calendar.h" +#include "resourcecalendar.h" + +namespace KCal { + +class CalendarResources : public Calendar +{ + public: + CalendarResourceManager *resourceManager() { return 0; } +}; + +} + +#endif diff --git a/libkcal/calfilter.cpp b/libkcal/calfilter.cpp new file mode 100644 index 0000000..c182db5 --- a/dev/null +++ b/libkcal/calfilter.cpp @@ -0,0 +1,201 @@ +/* + This file is part of libkcal. + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include <kdebug.h> + +#include "calfilter.h" + +using namespace KCal; + +CalFilter::CalFilter() +{ + mEnabled = true; + mCriteria = ShowPublic | ShowPrivate| ShowConfidential ; +} + +CalFilter::CalFilter(const QString &name) +{ + mName = name; + mEnabled = true; + mCriteria = ShowPublic | ShowPrivate| ShowConfidential ; +} + +CalFilter::~CalFilter() +{ +} + +void CalFilter::apply(QPtrList<Event> *eventlist) +{ + if (!mEnabled) return; + +// kdDebug(5800) << "CalFilter::apply()" << endl; + + Event *event = eventlist->first(); + while(event) { + if (!filterEvent(event)) { + eventlist->remove(); + event = eventlist->current(); + } else { + event = eventlist->next(); + } + } + +// kdDebug(5800) << "CalFilter::apply() done" << endl; +} + +// TODO: avoid duplicating apply() code +void CalFilter::apply(QPtrList<Todo> *eventlist) +{ + if (!mEnabled) return; + +// kdDebug(5800) << "CalFilter::apply()" << endl; + + Todo *event = eventlist->first(); + while(event) { + if (!filterTodo(event)) { + eventlist->remove(); + event = eventlist->current(); + } else { + event = eventlist->next(); + } + } + +// kdDebug(5800) << "CalFilter::apply() done" << endl; +} + +bool CalFilter::filterEvent(Event *event) +{ +// kdDebug(5800) << "CalFilter::filterEvent(): " << event->getSummary() << endl; + + if (mCriteria & HideRecurring) { + if (event->recurrence()->doesRecur()) return false; + } + + return filterIncidence(event); +} + +bool CalFilter::filterTodo(Todo *todo) +{ +// kdDebug(5800) << "CalFilter::filterEvent(): " << event->getSummary() << endl; + + if (mCriteria & HideCompleted) { + if (todo->isCompleted()) return false; + } + + return filterIncidence(todo); +} +bool CalFilter::showCategories() +{ + return mCriteria & ShowCategories; +} +int CalFilter::getSecrecy() +{ + if ( (mCriteria & ShowPublic )) + return Incidence::SecrecyPublic; + if ( (mCriteria & ShowPrivate )) + return Incidence::SecrecyPrivate; + if ( (mCriteria & ShowConfidential )) + return Incidence::SecrecyConfidential; + return Incidence::SecrecyPublic; +} +bool CalFilter::filterIncidence(Incidence *incidence) +{ + if ( mCriteria > 7 ) { + switch (incidence->secrecy()) { + case Incidence::SecrecyPublic: + if (! (mCriteria & ShowPublic )) + return false; + break; + case Incidence::SecrecyPrivate: + if (! (mCriteria & ShowPrivate )) + return false; + break; + case Incidence::SecrecyConfidential: + if (! (mCriteria & ShowConfidential )) + return false; + break; + default: + return false; + break; + } + } + + // kdDebug(5800) << "CalFilter::filterEvent(): " << event->getSummary() << endl; + + if (mCriteria & ShowCategories) { + for (QStringList::Iterator it = mCategoryList.begin(); + it != mCategoryList.end(); ++it ) { + QStringList incidenceCategories = incidence->categories(); + for (QStringList::Iterator it2 = incidenceCategories.begin(); + it2 != incidenceCategories.end(); ++it2 ) { + if ((*it) == (*it2)) { + return true; + } + } + } + return false; + } else { + for (QStringList::Iterator it = mCategoryList.begin(); + it != mCategoryList.end(); ++it ) { + QStringList incidenceCategories = incidence->categories(); + for (QStringList::Iterator it2 = incidenceCategories.begin(); + it2 != incidenceCategories.end(); ++it2 ) { + if ((*it) == (*it2)) { + return false; + } + } + } + return true; + } + +// kdDebug(5800) << "CalFilter::filterEvent(): passed" << endl; + + return true; +} + +void CalFilter::setEnabled(bool enabled) +{ + mEnabled = enabled; +} + +bool CalFilter::isEnabled() +{ + return mEnabled; +} + +void CalFilter::setCriteria(int criteria) +{ + mCriteria = criteria; +} + +int CalFilter::criteria() +{ + return mCriteria; +} + +void CalFilter::setCategoryList(const QStringList &categoryList) +{ + mCategoryList = categoryList; +} + +QStringList CalFilter::categoryList() +{ + return mCategoryList; +} diff --git a/libkcal/calfilter.h b/libkcal/calfilter.h new file mode 100644 index 0000000..d6d4717 --- a/dev/null +++ b/libkcal/calfilter.h @@ -0,0 +1,128 @@ +/* + This file is part of libkcal. + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef _CALFILTER_H +#define _CALFILTER_H + +#include <qstring.h> +#include <qptrlist.h> + +#include "event.h" +#include "todo.h" + +namespace KCal { + +/** + Filter for calendar objects. +*/ +class CalFilter { + public: + /** Construct filter. */ + CalFilter(); + /** Construct filter with name */ + CalFilter(const QString &name); + /** Destruct filter. */ + ~CalFilter(); + + /** + Set name of filter. + */ + void setName(const QString &name) { mName = name; } + /** + Return name of filter. + */ + QString name() const { return mName; } + + /** + Apply filter to eventlist, all events not matching filter criterias are + removed from the list. + */ + void apply(QPtrList<Event> *eventlist); + + /** + Apply filter to todolist, all todos not matching filter criterias are + removed from the list. + */ + void apply(QPtrList<Todo> *todolist); + + /** + Apply filter criteria on the specified event. Return true, if event passes + criteria, otherwise return false. + */ + bool filterEvent(Event *); + + /** + Apply filter criteria on the specified todo. Return true, if event passes + criteria, otherwise return false. + */ + bool filterTodo(Todo *); + + /** + Apply filter criteria on the specified incidence. Return true, if event passes + criteria, otherwise return false. + */ + bool filterIncidence(Incidence *); + + /** + Enable or disable filter. + */ + void setEnabled(bool); + /** + Return wheter the filter is enabled or not. + */ + bool isEnabled(); + bool showCategories(); + int getSecrecy(); + /** + Set list of categories, which is used for showing/hiding categories of + events. + See related functions. + */ + void setCategoryList(const QStringList &); + /** + Return category list, used for showing/hiding categories of events. + See related functions. + */ + QStringList categoryList(); + + enum { HideRecurring = 1, HideCompleted = 2, ShowCategories = 4 ,ShowPublic = 8, ShowPrivate = 16, ShowConfidential = 32 }; + + /** + Set criteria, which have to be fulfilled by events passing the filter. + */ + void setCriteria(int); + /** + Get inclusive filter criteria. + */ + int criteria(); + + private: + QString mName; + + int mCriteria; + + bool mEnabled; + + QStringList mCategoryList; +}; + +} + +#endif /* _CALFILTER_H */ diff --git a/libkcal/calformat.cpp b/libkcal/calformat.cpp new file mode 100644 index 0000000..8a3d069 --- a/dev/null +++ b/libkcal/calformat.cpp @@ -0,0 +1,98 @@ +/* + This file is part of libkcal. + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include <klocale.h> +#include <kdebug.h> +#include <kapplication.h> + +#include "calformat.h" + +using namespace KCal; + +QString CalFormat::mApplication = QString::fromLatin1("libkcal"); +QString CalFormat::mProductId = QString::fromLatin1("-//K Desktop Environment//NONSGML libkcal 3.1//EN"); + +// An array containing the PRODID strings indexed against the calendar file format version used. +// Every time the calendar file format is changed, add an entry/entries to this list. +struct CalVersion { + int version; + QString prodId; +}; +static CalVersion prodIds[] = { + { 220, QString::fromLatin1("-//K Desktop Environment//NONSGML KOrganizer 2.2//EN") }, + { 300, QString::fromLatin1("-//K Desktop Environment//NONSGML KOrganizer 3.0//EN") }, + { 310, QString::fromLatin1("-//K Desktop Environment//NONSGML KOrganizer 3.1//EN") }, + { 0 , QString() } +}; + + +CalFormat::CalFormat() +{ + mException = 0; +} + +CalFormat::~CalFormat() +{ + delete mException; +} + +void CalFormat::clearException() +{ + delete mException; + mException = 0; +} + +void CalFormat::setException(ErrorFormat *exception) +{ + delete mException; + mException = exception; +} + +ErrorFormat *CalFormat::exception() +{ + return mException; +} + +void CalFormat::setApplication(const QString& application, const QString& productID) +{ + mApplication = application; + mProductId = productID; +} + +QString CalFormat::createUniqueId() +{ + int hashTime = QTime::currentTime().hour() + + QTime::currentTime().minute() + QTime::currentTime().second() + + QTime::currentTime().msec(); + QString uidStr = QString("%1-%2.%3") + .arg(mApplication) + .arg(KApplication::random()) + .arg(hashTime); + return uidStr; +} + +int CalFormat::calendarVersion(const char* prodId) +{ + for (const CalVersion* cv = prodIds; cv->version; ++cv) { + if (!strcmp(prodId, cv->prodId.local8Bit())) + return cv->version; + } + return 0; +} diff --git a/libkcal/calformat.h b/libkcal/calformat.h new file mode 100644 index 0000000..0c7ee7e --- a/dev/null +++ b/libkcal/calformat.h @@ -0,0 +1,111 @@ +/* + This file is part of libkcal. + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef _CALFORMAT_H +#define _CALFORMAT_H + +#include <qstring.h> +#include <qdatetime.h> +#include <qevent.h> + +#include "exceptions.h" +#include "event.h" + +namespace KCal { + +class VCalDrag; +class Calendar; + +/** + This is the base class for calendar formats. It provides an interface for the + generation/interpretation of a textual representation of a calendar. + + @short Class providing in interface to a calendar format +*/ +class CalFormat { + public: + /** Constructs a new format. */ + CalFormat(); + /** Destruct calendar format. */ + virtual ~CalFormat(); + + /** + loads a calendar on disk into the calendar associated with this format. + Returns TRUE if successful,else returns FALSE. + @param fileName the name of the calendar on disk. + */ + virtual bool load(Calendar *, const QString &fileName) = 0; + /** writes out the calendar to disk. Returns true if + * successful and false on error. + * @param fileName the name of the file + */ + virtual bool save(Calendar *, const QString &fileName) = 0; + + /** + Parse string and populate calendar with that information. + */ + virtual bool fromString(Calendar *, const QString & ) = 0; + /** + Return calendar information as string. + */ + virtual QString toString(Calendar *) = 0; + + /** Clear exception status of this format object */ + void clearException(); + /** + Return exception, if there is any, containing information about the last + error that occured. + */ + ErrorFormat *exception(); + + /** Set the application name for use in unique IDs and error messages, + * and product ID for incidence PRODID property + */ + static void setApplication(const QString& app, const QString& productID); + /** Return the application name used in unique IDs and error messages */ + static const QString& application() { return mApplication; } + /** Return the PRODID string to write into calendar files */ + static const QString& productId() { return mProductId; } + /** Return the KDE calendar format version indicated by a PRODID property */ + static int calendarVersion(const char* prodId); + /** Return the PRODID string loaded from calendar file */ + const QString &loadedProductId() { return mLoadedProductId; } + + /** Create a unique id string. */ + static QString createUniqueId(); + + /** + Set exception for this object. This is used by the functions of this + class to report errors. + */ + void setException(ErrorFormat *error); + + protected: + QString mLoadedProductId; // PRODID string loaded from calendar file + + private: + ErrorFormat *mException; + + static QString mApplication; // name of application for unique ID strings + static QString mProductId; // PRODID string to write to calendar files +}; + +} + +#endif diff --git a/libkcal/calstorage.h b/libkcal/calstorage.h new file mode 100644 index 0000000..72972ea --- a/dev/null +++ b/libkcal/calstorage.h @@ -0,0 +1,52 @@ +/* + This file is part of libkcal. + Copyright (c) 2002 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef KCAL_CALSTORAGE_H +#define KCAL_CALSTORAGE_H + +namespace KCal { + +class Calendar; + +/** + This class provides the interface to the storage of a calendar. +*/ +class CalStorage +{ + public: + CalStorage( Calendar *calendar ) + { + mCalendar = calendar; + } + virtual ~CalStorage() {} + + Calendar *calendar() const { return mCalendar; } + + virtual bool open() = 0; + virtual bool load(bool = false ) = 0; + virtual bool save() = 0; + virtual bool close() = 0; + + private: + Calendar *mCalendar; +}; + +} + +#endif diff --git a/libkcal/compat.cpp b/libkcal/compat.cpp new file mode 100644 index 0000000..070e082 --- a/dev/null +++ b/libkcal/compat.cpp @@ -0,0 +1,37 @@ +/* + This file is part of libkcal. + Copyright (c) 2002 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include "compat.h" + +#include <kdebug.h> + +#include <qregexp.h> + +using namespace KCal; + +Compat *CompatFactory::createCompat( const QString & ) +{ + return new Compat; +} + +void CompatPre31::fixFloatingEnd( QDate &endDate ) +{ + endDate = endDate.addDays( 1 ); +} diff --git a/libkcal/compat.h b/libkcal/compat.h new file mode 100644 index 0000000..42d44fa --- a/dev/null +++ b/libkcal/compat.h @@ -0,0 +1,53 @@ +/* + This file is part of libkcal. + Copyright (c) 2002 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef KCAL_COMPAT_H +#define KCAL_COMPAT_H + +#include <qstring.h> +#include <qdatetime.h> + +namespace KCal { + +class Compat; + +class CompatFactory +{ + public: + static Compat *createCompat( const QString &productId ); +}; + +class Compat +{ + public: + Compat() {}; + virtual ~Compat() {}; + + virtual void fixFloatingEnd( QDate & ) {} +}; + +class CompatPre31 : public Compat +{ + public: + void fixFloatingEnd( QDate & ); +}; + +} + +#endif diff --git a/libkcal/customproperties.cpp b/libkcal/customproperties.cpp new file mode 100644 index 0000000..adc1710 --- a/dev/null +++ b/libkcal/customproperties.cpp @@ -0,0 +1,114 @@ +/* + This file is part of libkcal. + Copyright (c) 2002 David Jarvie <software@astrojar.org.uk> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include "customproperties.h" + +using namespace KCal; + +CustomProperties::CustomProperties() +{ +} + +CustomProperties::CustomProperties(const CustomProperties &cp) + : mProperties(cp.mProperties) +{ +} + +CustomProperties::~CustomProperties() +{ +} + +void CustomProperties::setCustomProperty(const QCString &app, const QCString &key, + const QString &value) +{ + if (value.isNull() || key.isEmpty() || app.isEmpty()) + return; + QCString property = "X-KDE-" + app + "-" + key; + if (!checkName(property)) + return; + mProperties[property] = value; +} + +void CustomProperties::removeCustomProperty(const QCString &app, const QCString &key) +{ + removeNonKDECustomProperty(QCString("X-KDE-" + app + "-" + key)); +} + +QString CustomProperties::customProperty(const QCString &app, const QCString &key) const +{ + return nonKDECustomProperty(QCString("X-KDE-" + app + "-" + key)); +} + +void CustomProperties::setNonKDECustomProperty(const QCString &name, const QString &value) +{ + if (value.isNull() || !checkName(name)) + return; + mProperties[name] = value; +} + +void CustomProperties::removeNonKDECustomProperty(const QCString &name) +{ + QMap<QCString, QString>::Iterator it = mProperties.find(name); + if (it != mProperties.end()) + mProperties.remove(it); +} + +QString CustomProperties::nonKDECustomProperty(const QCString &name) const +{ + QMap<QCString, QString>::ConstIterator it = mProperties.find(name); + if (it == mProperties.end()) + return QString::null; + return it.data(); +} + +void CustomProperties::setCustomProperties(const QMap<QCString, QString> &properties) +{ + for (QMap<QCString, QString>::ConstIterator it = properties.begin(); it != properties.end(); ++it) { + // Validate the property name and convert any null string to empty string + if (checkName(it.key())) { + mProperties[it.key()] = it.data().isNull() ? QString("") : it.data(); + } + } +} + +QMap<QCString, QString> CustomProperties::customProperties() const +{ + return mProperties; +} + +bool CustomProperties::checkName(const QCString &name) +{ + // Check that the property name starts with 'X-' and contains + // only the permitted characters + const char* n = name; + 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; +} diff --git a/libkcal/customproperties.h b/libkcal/customproperties.h new file mode 100644 index 0000000..0cbfdcd --- a/dev/null +++ b/libkcal/customproperties.h @@ -0,0 +1,97 @@ +/* + This file is part of libkcal. + Copyright (c) 2002 David Jarvie <software@astrojar.org.uk> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef KCAL_CUSTOM_PROPERTIES_H +#define KCAL_CUSTOM_PROPERTIES_H + +#include <qstring.h> +#include <qmap.h> + +namespace KCal { + +/** + 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 libkcal 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 CustomProperties +{ + public: + /** Construct a new empty custom properties instance */ + CustomProperties(); + CustomProperties(const CustomProperties &); + ~CustomProperties(); + + /** 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::null + will be ignored. + */ + void setCustomProperty(const QCString &app, const QCString &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. + */ + void removeCustomProperty(const QCString &app, const QCString &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::null if (and only if) the property does not exist. + */ + QString customProperty(const QCString &app, const QCString &key) const; + + /** 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::null + will be ignored. + */ + void setNonKDECustomProperty(const QCString &name, const QString &value); + /** Delete a non-KDE or non-standard custom calendar property. + @param name Full property name + */ + void removeNonKDECustomProperty(const QCString &name); + /** Return the value of a non-KDE or non-standard custom calendar property. + @param name Full property name + @return Property value, or QString::null if (and only if) the property does not exist. + */ + QString nonKDECustomProperty(const QCString& name) const; + + /** Initialise the alarm's custom calendar properties to the specified + key/value pairs. + */ + void setCustomProperties(const QMap<QCString, QString> &properties); + /** Return all custom calendar property key/value pairs. */ + QMap<QCString, QString> customProperties() const; + + private: + static bool checkName(const QCString& name); + + QMap<QCString, QString> mProperties; // custom calendar properties +}; + +} + +#endif diff --git a/libkcal/dndfactory.h b/libkcal/dndfactory.h new file mode 100644 index 0000000..6b73f34 --- a/dev/null +++ b/libkcal/dndfactory.h @@ -0,0 +1,62 @@ +/* + This file is part of libkcal. + Copyright (c) 1998 Preston Brown + Copyright (c) 2001,2002 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +// $Id$ + +#ifndef KCAL_DNDFACTORY_H +#define KCAL_DNDFACTORY_H + +#include "vcalformat.h" + +class QDropEvent; + +namespace KCal { + +/** + This class implements functions to create Drag and Drop objects used for + Drag-and-Drop and Copy-and-Paste. + + @short vCalendar Drag-and-Drop object factory. +*/ +class DndFactory { + public: + DndFactory( Calendar * ) {} + + /** create an object to be used with the Xdnd Drag And Drop protocol. */ + ICalDrag *createDrag(Event *, QWidget *) { return 0; } + /** create an object to be used with the Xdnd Drag And Drop protocol. */ + ICalDrag *createDragTodo(Todo *, QWidget *) { return 0; } + /** Create Todo object from drop event */ + Todo *createDropTodo(QDropEvent *) { return 0; } + /** Create Event object from drop event */ + Event *createDrop(QDropEvent *) { return 0; } + + /** cut event to clipboard */ + void cutEvent(Event *) {} + /** cut, copy, and paste operations follow. */ + bool copyEvent(Event *) { return false; } + /** pastes the event and returns a pointer to the new event pasted. */ + Event *pasteEvent(const QDate &, const QTime *newTime = 0) { return 0; } +}; + +} + +#endif diff --git a/libkcal/dummyscheduler.cpp b/libkcal/dummyscheduler.cpp new file mode 100644 index 0000000..ae40e6d --- a/dev/null +++ b/libkcal/dummyscheduler.cpp @@ -0,0 +1,119 @@ +/* + This file is part of libkcal. + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +// +// DummyScheduler - iMIP implementation of iTIP methods +// + +#include <qfile.h> +#include <qtextstream.h> + +#include <kdebug.h> + +#include "event.h" +#include "icalformat.h" + +#include "dummyscheduler.h" + +using namespace KCal; + +DummyScheduler::DummyScheduler(Calendar *calendar) + : Scheduler(calendar) +{ +} + +DummyScheduler::~DummyScheduler() +{ +} + +bool DummyScheduler::publish (IncidenceBase *incidence,const QString &recipients) +{ + QString messageText = mFormat->createScheduleMessage(incidence, + Scheduler::Publish); + + return saveMessage(messageText); +} + +bool DummyScheduler::performTransaction(IncidenceBase *incidence,Method method,const QString &recipients) +{ + QString messageText = mFormat->createScheduleMessage(incidence,method); + + return saveMessage(messageText); +} + +bool DummyScheduler::performTransaction(IncidenceBase *incidence,Method method) +{ + QString messageText = mFormat->createScheduleMessage(incidence,method); + + return saveMessage(messageText); +} + +bool DummyScheduler::saveMessage(const QString &message) +{ + QFile f("dummyscheduler.store"); + if (f.open(IO_WriteOnly | IO_Append)) { + QTextStream t(&f); + t << message << endl; + f.close(); + return true; + } else { + return false; + } +} + +QPtrList<ScheduleMessage> DummyScheduler::retrieveTransactions() +{ + QPtrList<ScheduleMessage> messageList; + + QFile f("dummyscheduler.store"); + if (!f.open(IO_ReadOnly)) { + kdDebug(5800) << "DummyScheduler::retrieveTransactions(): Can't open file" + << endl; + } else { + QTextStream t(&f); + QString messageString; + QString messageLine = t.readLine(); + while (!messageLine.isNull()) { +// kdDebug(5800) << "++++++++" << messageLine << endl; + messageString += messageLine + "\n"; + if (messageLine.find("END:VCALENDAR") >= 0) { + kdDebug(5800) << "---------------" << messageString << endl; + ScheduleMessage *message = mFormat->parseScheduleMessage(mCalendar, + messageString); + kdDebug(5800) << "--Parsed" << endl; + if (message) { + messageList.append(message); + } else { + QString errorMessage; + if (mFormat->exception()) { + errorMessage = mFormat->exception()->message(); + } + kdDebug(5800) << "DummyScheduler::retrieveTransactions() Error parsing " + "message: " << errorMessage << endl; + } + messageString=""; + } + messageLine = t.readLine(); + } + f.close(); + } + + return messageList; +} diff --git a/libkcal/dummyscheduler.h b/libkcal/dummyscheduler.h new file mode 100644 index 0000000..df42153 --- a/dev/null +++ b/libkcal/dummyscheduler.h @@ -0,0 +1,51 @@ +/* + This file is part of libkcal. + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef DUMMYSCHEDULER_H +#define DUMMYSCHEDULER_H +// +// Dummy implementation of iTIP methods +// + +#include "scheduler.h" + +namespace KCal { + +/** + This class implements the iTIP interface as a primitive local version for + testing. It uses a file dummyscheduler.store as inbox/outbox. +*/ +class DummyScheduler : public Scheduler { + public: + DummyScheduler(Calendar *); + virtual ~DummyScheduler(); + + bool publish (IncidenceBase *incidence,const QString &recipients); + bool performTransaction(IncidenceBase *incidence,Method method); + bool performTransaction(IncidenceBase *incidence,Method method,const QString &recipients); + QPtrList<ScheduleMessage> retrieveTransactions(); + + protected: + bool saveMessage(const QString &); +}; + +} + +#endif // DUMMYSCHEDULER_H + diff --git a/libkcal/duration.cpp b/libkcal/duration.cpp new file mode 100644 index 0000000..3d57136 --- a/dev/null +++ b/libkcal/duration.cpp @@ -0,0 +1,59 @@ +/* + This file is part of libkcal. + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include <kdebug.h> +#include <klocale.h> + +#include "duration.h" + +using namespace KCal; + +Duration::Duration() +{ + mSeconds = 0; +} + +Duration::Duration( const QDateTime &start, const QDateTime &end ) +{ + mSeconds = start.secsTo( end ); +} + +Duration::Duration( int seconds ) +{ + mSeconds = seconds; +} + + +bool KCal::operator==( const Duration& d1, const Duration& d2 ) +{ + return ( d1.asSeconds() == d2.asSeconds() ); +} + + + +QDateTime Duration::end( const QDateTime &start ) const +{ + return start.addSecs( mSeconds ); +} + +int Duration::asSeconds() const +{ + return mSeconds; +} diff --git a/libkcal/duration.h b/libkcal/duration.h new file mode 100644 index 0000000..81274dd --- a/dev/null +++ b/libkcal/duration.h @@ -0,0 +1,48 @@ +/* + This file is part of libkcal. + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef KCAL_DURATION_H +#define KCAL_DURATION_H + +#include <qdatetime.h> + +namespace KCal { + +class Duration +{ + public: + Duration(); + Duration( const QDateTime &start, const QDateTime &end ); + Duration( int seconds ); + + QDateTime end( const QDateTime &start ) const; + + int asSeconds() const; + + private: + int mSeconds; +}; + + bool operator==( const Duration&, const Duration& ); + inline bool operator!=( const Duration &d1, const Duration &d2 ) + { return !operator==( d1, d2 ); } + +} + +#endif diff --git a/libkcal/event.cpp b/libkcal/event.cpp new file mode 100644 index 0000000..dd67252 --- a/dev/null +++ b/libkcal/event.cpp @@ -0,0 +1,178 @@ +/* + This file is part of libkcal. + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include <kglobal.h> +#include <klocale.h> +#include <kdebug.h> + +#include "event.h" + +using namespace KCal; + +Event::Event() : + mHasEndDate( false ), mTransparency( Opaque ) +{ +} + +Event::Event(const Event &e) : Incidence(e) +{ + mDtEnd = e.mDtEnd; + mHasEndDate = e.mHasEndDate; + mTransparency = e.mTransparency; +} + +Event::~Event() +{ +} + +Incidence *Event::clone() +{ + kdDebug(5800) << "Event::clone()" << endl; + return new Event(*this); +} + +bool KCal::operator==( const Event& e1, const Event& e2 ) +{ + return operator==( (const Incidence&)e1, (const Incidence&)e2 ) && + e1.dtEnd() == e2.dtEnd() && + e1.hasEndDate() == e2.hasEndDate() && + e1.transparency() == e2.transparency(); +} + + + +void Event::setDtEnd(const QDateTime &dtEnd) +{ + if (mReadOnly) return; + + mDtEnd = getEvenTime( dtEnd ); + + setHasEndDate(true); + setHasDuration(false); + + updated(); +} + +QDateTime Event::dtEnd() const +{ + if (hasEndDate()) return mDtEnd; + if (hasDuration()) return dtStart().addSecs(duration()); + + kdDebug(5800) << "Warning! Event '" << summary() + << "' does have neither end date nor duration." << endl; + return dtStart(); +} + +QString Event::dtEndTimeStr() const +{ + return KGlobal::locale()->formatTime(mDtEnd.time()); +} + +QString Event::dtEndDateStr(bool shortfmt) const +{ + return KGlobal::locale()->formatDate(mDtEnd.date(),shortfmt); +} + +QString Event::dtEndStr(bool shortfmt) const +{ + return KGlobal::locale()->formatDateTime(mDtEnd, shortfmt); +} + +void Event::setHasEndDate(bool b) +{ + mHasEndDate = b; +} + +bool Event::hasEndDate() const +{ + return mHasEndDate; +} + +bool Event::isMultiDay() const +{ + bool multi = !(dtStart().date() == dtEnd().date()); + return multi; +} + +void Event::setTransparency(Event::Transparency transparency) +{ + if (mReadOnly) return; + mTransparency = transparency; + updated(); +} + +Event::Transparency Event::transparency() const +{ + return mTransparency; +} + +void Event::setDuration(int seconds) +{ + setHasEndDate(false); + Incidence::setDuration(seconds); +} +QDateTime Event::getNextAlarmDateTime( bool * ok, int * offset ) const +{ + + bool yes; + QDateTime incidenceStart = getNextOccurence( QDateTime::currentDateTime(), &yes ); + if ( ! yes || cancelled() ) { + *ok = false; + return QDateTime (); + } + + bool enabled = false; + Alarm* alarm; + int off; + QDateTime alarmStart = QDateTime::currentDateTime().addDays( 3650 );; + // if ( QDateTime::currentDateTime() > incidenceStart ){ +// *ok = false; +// return incidenceStart; +// } + for (QPtrListIterator<Alarm> it(mAlarms); (alarm = it.current()) != 0; ++it) { + if (alarm->enabled()) { + if ( alarm->hasTime () ) { + if ( alarm->time() < alarmStart ) { + alarmStart = alarm->time(); + enabled = true; + off = alarmStart.secsTo( incidenceStart ); + } + + } else { + int secs = alarm->startOffset().asSeconds(); + if ( incidenceStart.addSecs( secs ) < alarmStart ) { + alarmStart = incidenceStart.addSecs( secs ); + enabled = true; + off = -secs; + } + } + } + } + if ( enabled ) { + if ( alarmStart > QDateTime::currentDateTime() ) { + *ok = true; + * offset = off; + return alarmStart; + } + } + *ok = false; + return QDateTime (); + +} diff --git a/libkcal/event.h b/libkcal/event.h new file mode 100644 index 0000000..2a8bd95 --- a/dev/null +++ b/libkcal/event.h @@ -0,0 +1,88 @@ +/* + This file is part of libkcal. + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef EVENT_H +#define EVENT_H +// +// Event component, representing a VEVENT object +// + +#include "incidence.h" +namespace KCal { + +/** + This class provides an Event in the sense of RFC2445. +*/ +class Event : public Incidence +{ + public: + enum Transparency { Opaque, Transparent }; + typedef ListBase<Event> List; + Event(); + Event(const Event &); + ~Event(); + + QCString type() const { return "Event"; } + + Incidence *clone(); + QDateTime getNextAlarmDateTime( bool * ok, int * offset ) const; + + /** for setting an event's ending date/time with a QDateTime. */ + void setDtEnd(const QDateTime &dtEnd); + /** Return the event's ending date/time as a QDateTime. */ + virtual QDateTime dtEnd() const; + /** returns an event's end time as a string formatted according to the + users locale settings */ + QString dtEndTimeStr() const; + /** returns an event's end date as a string formatted according to the + users locale settings */ + QString dtEndDateStr(bool shortfmt=true) const; + /** returns an event's end date and time as a string formatted according + to the users locale settings */ + QString dtEndStr(bool shortfmt=true) const; + void setHasEndDate(bool); + /** Return whether the event has an end date/time. */ + bool hasEndDate() const; + + /** Return true if the event spans multiple days, otherwise return false. */ + bool isMultiDay() const; + + /** set the event's time transparency level. */ + void setTransparency(Transparency transparency); + /** get the event's time transparency level. */ + Transparency transparency() const; + + void setDuration(int seconds); + + private: + bool accept(Visitor &v) { return v.visit(this); } + + QDateTime mDtEnd; + bool mHasEndDate; + Transparency mTransparency; +}; + +bool operator==( const Event&, const Event& ); + + +} + + +#endif diff --git a/libkcal/exceptions.cpp b/libkcal/exceptions.cpp new file mode 100644 index 0000000..bc298f9 --- a/dev/null +++ b/libkcal/exceptions.cpp @@ -0,0 +1,90 @@ +/* + This file is part of libkcal. + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include <klocale.h> + +#include "calformat.h" +#include "exceptions.h" + +using namespace KCal; + +Exception::Exception(const QString &message) +{ + mMessage = message; +} + +Exception::~Exception() +{ +} + +QString Exception::message() +{ + if (mMessage.isEmpty()) return i18n("%1 Error").arg(CalFormat::application()); + else return mMessage; +} + + +ErrorFormat::ErrorFormat(ErrorCodeFormat code,const QString &message) : + Exception(message) +{ + mCode = code; +} + +QString ErrorFormat::message() +{ + QString message = ""; + + switch (mCode) { + case LoadError: + message = i18n("Load Error"); + break; + case SaveError: + message = i18n("Save Error"); + break; + case ParseErrorIcal: + message = i18n("Parse Error in libical"); + break; + case ParseErrorKcal: + message = i18n("Parse Error in libkcal"); + break; + case NoCalendar: + message = i18n("No calendar component found."); + break; + case CalVersion1: + message = i18n("vCalendar Version 1.0 detected."); + break; + case CalVersion2: + message = i18n("iCalendar Version 2.0 detected."); + break; + case Restriction: + message = i18n("Restriction violation"); + default: + break; + } + + if (!mMessage.isEmpty()) message += ": " + mMessage; + + return message; +} + +ErrorFormat::ErrorCodeFormat ErrorFormat::errorCode() +{ + return mCode; +} diff --git a/libkcal/exceptions.h b/libkcal/exceptions.h new file mode 100644 index 0000000..4b75df8 --- a/dev/null +++ b/libkcal/exceptions.h @@ -0,0 +1,75 @@ +/* + This file is part of libkcal. + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef KCAL_EXCEPTIONS_H +#define KCAL_EXCEPTIONS_H +// +// Exception classes of libkcal. +// +// We don't use actual C++ exceptions right now. These classes are currently +// returned by an error function, but we can build upon them, when we start +// to use C++ exceptions. + +#include <qstring.h> + +namespace KCal { + +/** + KOrganizer exceptions base class. This is currently used as a fancy kind of error code not as an + C++ exception. +*/ +class Exception { + public: + /** Construct exception with descriptive message \a message. */ + Exception(const QString &message=QString::null); + virtual ~Exception(); + + /** Return descriptive message of exception. */ + virtual QString message(); + + protected: + QString mMessage; +}; + +/** Calendar format related error class */ +class ErrorFormat : public Exception { + public: + enum ErrorCodeFormat { LoadError, SaveError, + ParseErrorIcal, ParseErrorKcal, + NoCalendar, + CalVersion1,CalVersion2, + CalVersionUnknown, + Restriction }; + + /** Create format error exception. */ + ErrorFormat(ErrorCodeFormat code,const QString &message = QString::null); + + /** Return format error message. */ + QString message(); + /** Return format error code. */ + ErrorCodeFormat errorCode(); + + private: + ErrorCodeFormat mCode; +}; + +} + +#endif diff --git a/libkcal/filestorage.cpp b/libkcal/filestorage.cpp new file mode 100644 index 0000000..00c15d9 --- a/dev/null +++ b/libkcal/filestorage.cpp @@ -0,0 +1,140 @@ +/* + This file is part of libkcal. + Copyright (c) 2002 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include <stdlib.h> + +#include <qdatetime.h> +#include <qstring.h> +#include <qptrlist.h> + +#include <kdebug.h> + +#include "calendar.h" +#include "vcaldrag.h" +#include "vcalformat.h" +#include "icalformat.h" + +#include "filestorage.h" + +using namespace KCal; + +FileStorage::FileStorage( Calendar *cal, const QString &fileName, + CalFormat *format ) + : CalStorage( cal ), + mFileName( fileName ), + mSaveFormat( format ) +{ +} + +FileStorage::~FileStorage() +{ + delete mSaveFormat; +} + +void FileStorage::setFileName( const QString &fileName ) +{ + mFileName = fileName; +} + +QString FileStorage::fileName()const +{ + return mFileName; +} + + +void FileStorage::setSaveFormat( CalFormat *format ) +{ + delete mSaveFormat; + mSaveFormat = format; +} + +CalFormat *FileStorage::saveFormat()const +{ + return mSaveFormat; +} + + +bool FileStorage::open() +{ + return true; +} + +bool FileStorage::load( bool quick ) +{ + kdDebug(5800) << "FileStorage::load(): '" << mFileName << "'" << endl; + + // do we want to silently accept this, or make some noise? Dunno... + // it is a semantical thing vs. a practical thing. + if (mFileName.isEmpty()) return false; + + // Always try to load with iCalendar. It will detect, if it is actually a + // vCalendar file. + ICalFormat iCal (quick ); + + bool success = iCal.load( calendar(), mFileName); + + if ( !success ) { + if ( iCal.exception() ) { +// kdDebug(5800) << "---Error: " << mFormat->exception()->errorCode() << endl; + if ( iCal.exception()->errorCode() == ErrorFormat::CalVersion1 ) { + // Expected non vCalendar file, but detected vCalendar + kdDebug(5800) << "FileStorage::load() Fallback to VCalFormat" << endl; + VCalFormat vCal; + success = vCal.load( calendar(), mFileName ); + calendar()->setLoadedProductId( vCal.productId() ); + } else { + return false; + } + } else { + kdDebug(5800) << "Warning! There should be set an exception." << endl; + return false; + } + } else { +// kdDebug(5800) << "---Success" << endl; + calendar()->setLoadedProductId( iCal.loadedProductId() ); + } + + calendar()->setModified( false ); + + return true; +} + +bool FileStorage::save() +{ + if ( mFileName.isEmpty() ) return false; + + bool success; + + if ( mSaveFormat ) { + success = mSaveFormat->save( calendar(), mFileName); + } else { + ICalFormat iCal; + success = iCal.save( calendar(), mFileName); + } + + if ( success ) calendar()->setModified( false ); + + return success; +} + +bool FileStorage::close() +{ + return true; +} diff --git a/libkcal/filestorage.h b/libkcal/filestorage.h new file mode 100644 index 0000000..e9dc15e --- a/dev/null +++ b/libkcal/filestorage.h @@ -0,0 +1,58 @@ +/* + This file is part of libkcal. + Copyright (c) 2002 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef KCAL_FILESTORAGE_H +#define KCAL_FILESTORAGE_H + +#include "calstorage.h" + +namespace KCal { + +/** + This class provides a calendar storage as a local file. +*/ +class FileStorage : public CalStorage +{ + public: + FileStorage( Calendar *, const QString &fileName = QString::null, + CalFormat *format = 0 ); + virtual ~FileStorage(); + + void setFileName( const QString &mFileName ); + QString fileName()const; + + /** + FileStorage takes ownership of format object. + */ + void setSaveFormat( CalFormat * ); + CalFormat *saveFormat()const; + + bool open(); + bool load(bool quick = false ); + bool save(); + bool close(); + + private: + QString mFileName; + CalFormat *mSaveFormat; +}; + +} + +#endif diff --git a/libkcal/freebusy.cpp b/libkcal/freebusy.cpp new file mode 100644 index 0000000..ba15d6d --- a/dev/null +++ b/libkcal/freebusy.cpp @@ -0,0 +1,184 @@ +/* + This file is part of libkcal. + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include <kdebug.h> + +#include "freebusy.h" + +using namespace KCal; + +FreeBusy::FreeBusy() +{ +} + +FreeBusy::FreeBusy(const QDateTime &start, const QDateTime &end) +{ + setDtStart(start); + setDtEnd(end); +} + +FreeBusy::FreeBusy( Calendar *calendar, const QDateTime &start, const QDateTime &end ) +{ + kdDebug() << "FreeBusy::FreeBusy" << endl; + mCalendar = calendar; + + setDtStart(start); + setDtEnd(end); + + //Gets all the events in the calendar + QPtrList<Event> eventList = mCalendar->events(); + Event *event; + + int extraDays, i, x, duration; + duration = start.daysTo(end); + QDate day; + QDateTime tmpStart; + QDateTime tmpEnd; + //Loops through every event in the calendar + for( event = eventList.first(); event; event = eventList.next() ) { + //This whole for loop is for recurring events, it loops through + //each of the days of the freebusy request + + //First check if this is transparent. If it is, it shouldn't be in the + //freebusy list + if ( event->transparency() == Event::Transparent ) + // Transparent + continue; + + for(i=0; i<=duration; i++) { + day=(start.addDays(i).date()); + tmpStart.setDate(day); + tmpEnd.setDate(day); + + if( (*(event->recurrence())).doesRecur() ) { + if ( event->isMultiDay() ) { + extraDays = event->dtStart().date().daysTo(event->dtEnd().date()); + for (x=0; x<=extraDays; x++) { + if ( event->recursOn(day.addDays(-x))) { + tmpStart.setDate(day.addDays(-x)); + tmpStart.setTime(event->dtStart().time()); + tmpEnd=tmpStart.addSecs( (event->duration()) ); + + addLocalPeriod( tmpStart, tmpEnd ); + break; + } + } + } else { + if (event->recursOn(day)) { + tmpStart.setTime(event->dtStart().time()); + tmpEnd.setTime(event->dtEnd().time()); + + addLocalPeriod (tmpStart, tmpEnd); + } + } + } + + } + //Non-reocurring events + addLocalPeriod(event->dtStart(), event->dtEnd()); + } + + sortList(); +} + +FreeBusy::~FreeBusy() +{ +} + +bool FreeBusy::setDtEnd( const QDateTime &end ) +{ + mDtEnd = end; + return true; +} + +QDateTime FreeBusy::dtEnd() const +{ + return mDtEnd; +} + +QValueList<Period> FreeBusy::busyPeriods() const +{ + return mBusyPeriods; +} + +bool FreeBusy::addLocalPeriod(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. + if (!((((this->dtStart()).secsTo(eventStart)>=0)&&(eventStart.secsTo(this->dtEnd())>=0)) + ||(((this->dtStart()).secsTo(eventEnd) >= 0)&&(eventEnd.secsTo(this->dtEnd()) >= 0)))) + return false; + + if ( eventStart.secsTo(this->dtStart())>=0) { + tmpStart = this->dtStart(); + } else { + tmpStart = eventStart; + } + + if ( eventEnd.secsTo(this->dtEnd())<=0 ) { + tmpEnd = this->dtEnd(); + } else { + tmpEnd = eventEnd; + } + + Period p(tmpStart, tmpEnd); + mBusyPeriods.append( p ); + + return true; +} + +FreeBusy::FreeBusy(QValueList<Period> busyPeriods) +{ + mBusyPeriods = busyPeriods; +} + +void FreeBusy::sortList() +{ + typedef QValueList<Period> PeriodList; + + PeriodList::Iterator tmpPeriod, earlyPeriod; + PeriodList sortedList; + QDateTime earlyTime; + + while( mBusyPeriods.count() > 0 ) { + earlyTime=(*mBusyPeriods.begin()).start(); + for (tmpPeriod=mBusyPeriods.begin(); tmpPeriod!=mBusyPeriods.end(); tmpPeriod++) { + if (earlyTime.secsTo((*tmpPeriod).start()) <= 0) { + earlyTime=(*tmpPeriod).start(); + earlyPeriod=tmpPeriod; + } + } + //Move tmpPeriod to sortedList + Period tmpPeriod( (*earlyPeriod).start(), (*earlyPeriod).end() ); + sortedList.append( tmpPeriod ); + mBusyPeriods.remove( earlyPeriod ); + } + mBusyPeriods=sortedList; +} + +void FreeBusy::addPeriod(const QDateTime &start, const QDateTime &end) +{ + Period p(start, end); + mBusyPeriods.append( p ); + + sortList(); +} diff --git a/libkcal/freebusy.h b/libkcal/freebusy.h new file mode 100644 index 0000000..054feda --- a/dev/null +++ b/libkcal/freebusy.h @@ -0,0 +1,72 @@ +/* + This file is part of libkcal. + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef KCAL_FREEBUSY_H +#define KCAL_FREEBUSY_H +// +// FreeBusy - information about free/busy times +// + +#include <qdatetime.h> +#include <qvaluelist.h> +#include <qptrlist.h> + +#include "period.h" +#include "calendar.h" + +#include "incidencebase.h" + +namespace KCal { + +/** + This class provides information about free/busy time of a calendar user. +*/ +class FreeBusy : public IncidenceBase +{ + public: + FreeBusy(); + FreeBusy(const QDateTime &start, const QDateTime &end); + FreeBusy(Calendar *calendar, const QDateTime &start, const QDateTime &end); + FreeBusy(QValueList<Period> busyPeriods); + + ~FreeBusy(); + + QCString type() const { return "FreeBusy"; } + + virtual QDateTime dtEnd() const; + bool setDtEnd( const QDateTime &end ); + + QValueList<Period> busyPeriods() const; + + void addPeriod(const QDateTime &start, const QDateTime &end); + void sortList(); + + private: + + //This is used for creating a freebusy object for the current user + bool addLocalPeriod(const QDateTime &start, const QDateTime &end); + + QDateTime mDtEnd; + QValueList<Period> mBusyPeriods; + Calendar *mCalendar; +}; + +} + +#endif diff --git a/libkcal/icaldrag.cpp b/libkcal/icaldrag.cpp new file mode 100644 index 0000000..446a115 --- a/dev/null +++ b/libkcal/icaldrag.cpp @@ -0,0 +1,58 @@ +/* + This file is part of libkcal. + Copyright (c) 1998 Preston Brown + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include "icaldrag.h" + +#include "icalformat.h" + +#include <kdebug.h> + +using namespace KCal; + +ICalDrag::ICalDrag( Calendar *cal, QWidget *parent, const char *name ) + : QStoredDrag( "text/calendar", parent, name ) +{ + ICalFormat icf; + QString scal = icf.toString( cal ); + + setEncodedData( scal.utf8() ); +} + +bool ICalDrag::canDecode( QMimeSource *me ) +{ + return me->provides( "text/calendar" ); +} + +bool ICalDrag::decode( QMimeSource *de, Calendar *cal ) +{ + bool success = false; + + QByteArray payload = de->encodedData( "text/calendar" ); + if ( payload.size() ) { + QString txt = QString::fromUtf8( payload.data() ); + + ICalFormat icf; + success = icf.fromString( cal, txt ); + } + + return success; +} + diff --git a/libkcal/icaldrag.h b/libkcal/icaldrag.h new file mode 100644 index 0000000..fdf32b7 --- a/dev/null +++ b/libkcal/icaldrag.h @@ -0,0 +1,46 @@ +/* + This file is part of libkcal. + Copyright (c) 1998 Preston Brown + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef ICALDRAG_H +#define ICALDRAG_H + +#include <qdragobject.h> +#include "calendar.h" + +namespace KCal { + +/** iCalendar drag&drop class. */ +class ICalDrag : public QStoredDrag +{ + public: + /** Create a drag&drop object for iCalendar component \a ical. */ + ICalDrag( Calendar *cal, QWidget *parent = 0, const char *name = 0 ); + ~ICalDrag() {}; + + /** Return, if drag&drop object can be decode to iCalendar. */ + static bool canDecode( QMimeSource * ); + /** Decode drag&drop object to iCalendar component \a cal. */ + static bool decode( QMimeSource *e, Calendar *cal ); +}; + +} + +#endif diff --git a/libkcal/icalformat.cpp b/libkcal/icalformat.cpp new file mode 100644 index 0000000..5893db5 --- a/dev/null +++ b/libkcal/icalformat.cpp @@ -0,0 +1,478 @@ +/* + This file is part of libkcal. + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include <qdatetime.h> +#include <qstring.h> +#include <qptrlist.h> +#include <qregexp.h> +#include <qclipboard.h> +#include <qfile.h> +#include <qtextstream.h> +#include <qtextcodec.h> +#include <stdlib.h> + +#include <kdebug.h> +#include <kglobal.h> +#include <klocale.h> + +extern "C" { + #include <ical.h> + #include <icalss.h> + #include <icalparser.h> + #include <icalrestriction.h> +} + +#include "calendar.h" +#include "calendarlocal.h" +#include "journal.h" + +#include "icalformat.h" +#include "icalformatimpl.h" + +#define _ICAL_VERSION "2.0" + +using namespace KCal; + +ICalFormat::ICalFormat(bool quick ) +{ + mQuicksave = false; //quick; + mImpl = new ICalFormatImpl( this ); + tzOffsetMin = 0; + //qDebug("new ICalFormat() "); +} + +ICalFormat::~ICalFormat() +{ + delete mImpl; + //qDebug("delete ICalFormat "); +} + +bool ICalFormat::load( Calendar *calendar, const QString &fileName) +{ + + clearException(); + + QFile file( fileName ); + if (!file.open( IO_ReadOnly ) ) { + setException(new ErrorFormat(ErrorFormat::LoadError)); + return false; + } + QTextStream ts( &file ); + QString text; +#if 0 + if ( !mQuicksave ) { + qDebug("KO: No quickload!"); + ts.setEncoding( QTextStream::Latin1 ); + text = ts.read(); + } else { + ts.setCodec( QTextCodec::codecForName("utf8") ); + text = ts.read(); + } +#endif + ts.setEncoding( QTextStream::Latin1 ); + text = ts.read(); + file.close(); + + return fromString( calendar, text ); +} + +//#include <qdatetime.h> +bool ICalFormat::save( Calendar *calendar, const QString &fileName ) +{ + //kdDebug(5800) << "ICalFormat::save(): " << fileName << endl; + //qDebug("ICalFormat::save "); + clearException(); + QString text = toString( calendar ); + //return false; + // qDebug("to string takes ms: %d ",is.elapsed() ); + if ( text.isNull() ) return false; + + // TODO: write backup file + //is.restart(); + QFile file( fileName ); + if (!file.open( IO_WriteOnly ) ) { + setException(new ErrorFormat(ErrorFormat::SaveError, + i18n("Could not open file '%1'").arg(fileName))); + return false; + } + QTextStream ts( &file ); + +// #ifdef DESKTOP_VERSION +// mQuicksave = false; +// #endif +// if ( mQuicksave ) { +// ts << text.utf8(); +// } else { +// ts.setEncoding( QTextStream::Latin1 ); +// ts << text; +// //ts << text.latin1(); +// } + ts.setEncoding( QTextStream::Latin1 ); + ts << text; + file.close(); + //qDebug("saving file takes ms: %d ", is.elapsed() ); + return true; +} + +bool ICalFormat::fromString( Calendar *cal, const QString &text ) +{ + setTimeZone( cal->timeZoneId(), !cal->isLocalTime() ); + // qDebug("ICalFormat::fromString tz: %s ", cal->timeZoneId().latin1()); + // Get first VCALENDAR component. + // TODO: Handle more than one VCALENDAR or non-VCALENDAR top components + icalcomponent *calendar; + + //calendar = icalcomponent_new_from_string( text.local8Bit().data()); + // good calendar = icalcomponent_new_from_string( text.utf8().data()); + calendar = icalcomponent_new_from_string( (char*)text.latin1()); + if (!calendar) { + setException(new ErrorFormat(ErrorFormat::ParseErrorIcal)); + return false; + } + + bool success = true; + + if (icalcomponent_isa(calendar) != ICAL_VCALENDAR_COMPONENT) { + setException(new ErrorFormat(ErrorFormat::NoCalendar)); + success = false; + } else { + // put all objects into their proper places + if ( !mImpl->populate( cal, calendar ) ) { + if ( !exception() ) { + setException(new ErrorFormat(ErrorFormat::ParseErrorKcal)); + } + success = false; + } else + mLoadedProductId = mImpl->loadedProductId(); + } + + icalcomponent_free( calendar ); + + return success; +} + +Incidence *ICalFormat::fromString( const QString &text ) +{ + CalendarLocal cal( mTimeZoneId ); + fromString(&cal, text); + + Incidence *ical = 0; + QPtrList<Event> elist = cal.events(); + if ( elist.count() > 0 ) { + ical = elist.first(); + } else { + QPtrList<Todo> tlist = cal.todos(); + if ( tlist.count() > 0 ) { + ical = tlist.first(); + } else { + QPtrList<Journal> jlist = cal.journals(); + if ( jlist.count() > 0 ) { + ical = jlist.first(); + } + } + } + return ical; +} +#include <qapp.h> + +QString ICalFormat::toString( Calendar *cal ) +{ + + setTimeZone( cal->timeZoneId(), !cal->isLocalTime() ); + + icalcomponent *calendar = mImpl->createCalendarComponent(cal); + + icalcomponent *component; + + // todos + QPtrList<Todo> todoList = cal->rawTodos(); + QPtrListIterator<Todo> qlt(todoList); + for (; qlt.current(); ++qlt) { + component = mImpl->writeTodo(qlt.current()); + icalcomponent_add_component(calendar,component); + //qDebug(" todos "); + qApp->processEvents(); + } + // events + QPtrList<Event> events = cal->rawEvents(); + Event *ev; + for(ev=events.first();ev;ev=events.next()) { + component = mImpl->writeEvent(ev); + icalcomponent_add_component(calendar,component); + //qDebug("events "); + qApp->processEvents(); + } + + // journals + QPtrList<Journal> journals = cal->journals(); + Journal *j; + for(j=journals.first();j;j=journals.next()) { + component = mImpl->writeJournal(j); + icalcomponent_add_component(calendar,component); + //qDebug("journals "); + qApp->processEvents(); + } + const char *text; + QString ret =""; + text = icalcomponent_as_ical_string( calendar ); + qApp->processEvents(); + + // text = "BEGIN:VCALENDAR\nPRODID\n :-//K Desktop Environment//NONSGML libkcal 3.1//EN\nVERSION\n :2.0\nBEGIN:VEVENT\nDTSTAMP\n :20031231T213514Z\nORGANIZER\n :MAILTO:lutz@putz.de\nCREATED\n :20031231T213513Z\nUID\n :libkcal-1295166342.120\nSEQUENCE\n :0\nLAST-MODIFIED\n :20031231T213513Z\nSUMMARY\n :test1\nCLASS\n :PUBLIC\nPRIORITY\n :3\nDTSTART\n :20040101T090000Z\nDTEND\n :20040101T110000Z\nTRANSP\n :OPAQUE\nEND:VEVENT\nEND:VCALENDAR\n"; + + + if ( text ) { + ret = QString ( text ); + } + icalcomponent_free( calendar ); + + if (!text) { + setException(new ErrorFormat(ErrorFormat::SaveError, + i18n("libical error"))); + return QString::null; + } + + return ret; +} + +QString ICalFormat::toICalString( Incidence *incidence ) +{ + CalendarLocal cal( mTimeZoneId ); + cal.addIncidence( incidence->clone() ); + return toString( &cal ); +} + +QString ICalFormat::toString( Incidence *incidence ) +{ + icalcomponent *component; + + component = mImpl->writeIncidence( incidence ); + + const char *text = icalcomponent_as_ical_string( component ); + + icalcomponent_free( component ); + + return QString::fromLocal8Bit( text ); +} + +QString ICalFormat::toString( Recurrence *recurrence ) +{ + icalproperty *property; + property = mImpl->writeRecurrenceRule( recurrence ); + const char *text = icalproperty_as_ical_string( property ); + icalproperty_free( property ); + return QString::fromLocal8Bit( text ); +} +/* +bool ICalFormat::fromString( Recurrence * recurrence, const QString& rrule ) +{ + bool success = true; + icalerror_clear_errno(); + struct icalrecurrencetype recur = icalrecurrencetype_from_string( rrule ); + if ( icalerrno != ICAL_NO_ERROR ) { + kdDebug() << "Recurrence parsing error: " << icalerror_strerror( icalerrno ) << endl; + success = false; + } + + if ( success ) { + mImpl->readRecurrence( recur, recurrence ); + } + + return success; +} +*/ + +QString ICalFormat::createScheduleMessage(IncidenceBase *incidence, + Scheduler::Method method) +{ + icalcomponent *message = mImpl->createScheduleComponent(incidence,method); + + QString messageText = icalcomponent_as_ical_string(message); + + + + return messageText; +} + +ScheduleMessage *ICalFormat::parseScheduleMessage( Calendar *cal, + const QString &messageText ) +{ + setTimeZone( cal->timeZoneId(), !cal->isLocalTime() ); + clearException(); + + if (messageText.isEmpty()) return 0; + + icalcomponent *message; + message = icalparser_parse_string(messageText.local8Bit()); + + if (!message) return 0; + + icalproperty *m = icalcomponent_get_first_property(message, + ICAL_METHOD_PROPERTY); + + if (!m) return 0; + + icalcomponent *c; + + IncidenceBase *incidence = 0; + c = icalcomponent_get_first_component(message,ICAL_VEVENT_COMPONENT); + if (c) { + incidence = mImpl->readEvent(c); + } + + if (!incidence) { + c = icalcomponent_get_first_component(message,ICAL_VTODO_COMPONENT); + if (c) { + incidence = mImpl->readTodo(c); + } + } + + if (!incidence) { + c = icalcomponent_get_first_component(message,ICAL_VFREEBUSY_COMPONENT); + if (c) { + incidence = mImpl->readFreeBusy(c); + } + } + + if (!incidence) { + kdDebug() << "ICalFormat:parseScheduleMessage: object is not a freebusy, event or todo" << endl; + return 0; + } + + kdDebug(5800) << "ICalFormat::parseScheduleMessage() getting method..." << endl; + + icalproperty_method icalmethod = icalproperty_get_method(m); + Scheduler::Method method; + + switch (icalmethod) { + case ICAL_METHOD_PUBLISH: + method = Scheduler::Publish; + break; + case ICAL_METHOD_REQUEST: + method = Scheduler::Request; + break; + case ICAL_METHOD_REFRESH: + method = Scheduler::Refresh; + break; + case ICAL_METHOD_CANCEL: + method = Scheduler::Cancel; + break; + case ICAL_METHOD_ADD: + method = Scheduler::Add; + break; + case ICAL_METHOD_REPLY: + method = Scheduler::Reply; + break; + case ICAL_METHOD_COUNTER: + method = Scheduler::Counter; + break; + case ICAL_METHOD_DECLINECOUNTER: + method = Scheduler::Declinecounter; + break; + default: + method = Scheduler::NoMethod; + kdDebug(5800) << "ICalFormat::parseScheduleMessage(): Unknow method" << endl; + break; + } + + + if (!icalrestriction_check(message)) { + setException(new ErrorFormat(ErrorFormat::Restriction, + Scheduler::translatedMethodName(method) + ": " + + mImpl->extractErrorProperty(c))); + return 0; + } + + icalcomponent *calendarComponent = mImpl->createCalendarComponent(cal); + + Incidence *existingIncidence = cal->event(incidence->uid()); + if (existingIncidence) { + // TODO: check, if cast is required, or if it can be done by virtual funcs. + if (existingIncidence->type() == "Todo") { + Todo *todo = static_cast<Todo *>(existingIncidence); + icalcomponent_add_component(calendarComponent, + mImpl->writeTodo(todo)); + } + if (existingIncidence->type() == "Event") { + Event *event = static_cast<Event *>(existingIncidence); + icalcomponent_add_component(calendarComponent, + mImpl->writeEvent(event)); + } + } else { + calendarComponent = 0; + } + + + icalclass result = icalclassify(message,calendarComponent,(char *)""); + + + ScheduleMessage::Status status; + + switch (result) { + case ICAL_PUBLISH_NEW_CLASS: + status = ScheduleMessage::PublishNew; + break; + case ICAL_OBSOLETE_CLASS: + status = ScheduleMessage::Obsolete; + break; + case ICAL_REQUEST_NEW_CLASS: + status = ScheduleMessage::RequestNew; + break; + case ICAL_REQUEST_UPDATE_CLASS: + status = ScheduleMessage::RequestUpdate; + break; + case ICAL_UNKNOWN_CLASS: + default: + status = ScheduleMessage::Unknown; + break; + } + + return new ScheduleMessage(incidence,method,status); +} + +void ICalFormat::setTimeZone( const QString &id, bool utc ) +{ + + + mTimeZoneId = id; + mUtc = utc; + + tzOffsetMin = KGlobal::locale()->timezoneOffset(mTimeZoneId); + + //qDebug("ICalFormat::setTimeZoneOffset %s %d ",mTimeZoneId.latin1(), tzOffsetMin); +} + +QString ICalFormat::timeZoneId() const +{ + return mTimeZoneId; +} + +bool ICalFormat::utc() const +{ + return mUtc; +} +int ICalFormat::timeOffset() +{ + return tzOffsetMin; +} +const char *ICalFormat::tzString() +{ + const char* ret = (const char* ) mTzString; + return ret; +} diff --git a/libkcal/icalformat.h b/libkcal/icalformat.h new file mode 100644 index 0000000..236efbf --- a/dev/null +++ b/libkcal/icalformat.h @@ -0,0 +1,116 @@ +/* + This file is part of libkcal. + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef ICALFORMAT_H +#define ICALFORMAT_H + +#include <qstring.h> + +#include "scheduler.h" + +#include "calformat.h" + +namespace KCal { + +class ICalFormatImpl; + +/** + This class implements the iCalendar format. It provides methods for + loading/saving/converting iCalendar format data into the internal KOrganizer + representation as Calendar and Events. + + @short iCalendar format implementation +*/ +class ICalFormat : public CalFormat { + public: + /** Create new iCalendar format. */ + ICalFormat( bool quick = false ); + virtual ~ICalFormat(); + + /** + Loads a calendar on disk in iCalendar format into calendar. + Returns true if successful, else returns false. Provides more error + information by exception(). + @param calendar Calendar object to be filled. + @param fileName The name of the calendar file on disk. + */ + bool load( Calendar *, const QString &fileName ); + /** + Writes out the calendar to disk in iCalendar format. Returns true if + successful and false on error. + + @param calendar The Calendar object to be written. + @param fileName The name of the calendar file on disk. + */ + bool save( Calendar *, const QString &fileName ); + + /** + Parse string and populate calendar with that information. + */ + bool fromString( Calendar *, const QString & ); + /** + Parse string and return first ical component. + */ + Incidence *fromString( const QString & ); + /** + Return calendar information as string. + */ + QString toString( Calendar * ); + /** + Return incidence as full iCalendar formatted text. + */ + QString toICalString( Incidence * ); + /** + Return incidence as iCalendar formatted text. + */ + QString toString( Incidence * ); + /** + Return recurrence as iCalendar formatted text. + */ + QString toString( Recurrence * ); + /** + Parse string and fill recurrence object with + that information + */ + //bool fromString ( Recurrence *, const QString& ); + + /** Create a scheduling message for event \a e using method \m */ + QString createScheduleMessage(IncidenceBase *e,Scheduler::Method m); + /** Parse scheduling message provided as string \s */ + ScheduleMessage *parseScheduleMessage( Calendar *, const QString &s); + + /** Set id of used time zone and whether this time zone is UTC or not. */ + void setTimeZone( const QString &id, bool utc ); + QString timeZoneId() const; + int timeOffset(); + const char * tzString(); + bool utc() const; + + private: + ICalFormatImpl *mImpl; + bool mQuicksave; + QString mTimeZoneId; + QCString mTzString; + int tzOffsetMin; + bool mUtc; +}; + +} + +#endif diff --git a/libkcal/icalformatimpl.cpp b/libkcal/icalformatimpl.cpp new file mode 100644 index 0000000..e5c27a0 --- a/dev/null +++ b/libkcal/icalformatimpl.cpp @@ -0,0 +1,2173 @@ +/* + This file is part of libkcal. + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include <qdatetime.h> +#include <qstring.h> +#include <qptrlist.h> +#include <qfile.h> + +#include <kdebug.h> +#include <klocale.h> +#include <kglobal.h> + +extern "C" { + #include <ical.h> + #include <icalss.h> + #include <icalparser.h> + #include <icalrestriction.h> +} + +#include "calendar.h" +#include "journal.h" +#include "icalformat.h" +#include "icalformatimpl.h" +#include "compat.h" + +#define _ICAL_VERSION "2.0" + +using namespace KCal; + +const int gSecondsPerMinute = 60; +const int gSecondsPerHour = gSecondsPerMinute * 60; +const int gSecondsPerDay = gSecondsPerHour * 24; +const int gSecondsPerWeek = gSecondsPerDay * 7; + +ICalFormatImpl::ICalFormatImpl( ICalFormat *parent ) : + mParent( parent ), mCalendarVersion( 0 ) +{ + mCompat = new Compat; +} + +ICalFormatImpl::~ICalFormatImpl() +{ + delete mCompat; +} + +class ToStringVisitor : public Incidence::Visitor +{ + public: + ToStringVisitor( ICalFormatImpl *impl ) : mImpl( impl ), mComponent( 0 ) {} + + bool visit( Event *e ) { mComponent = mImpl->writeEvent( e ); return true; } + bool visit( Todo *e ) { mComponent = mImpl->writeTodo( e ); return true; } + bool visit( Journal *e ) { mComponent = mImpl->writeJournal( e ); return true; } + + icalcomponent *component() { return mComponent; } + + private: + ICalFormatImpl *mImpl; + icalcomponent *mComponent; +}; + +icalcomponent *ICalFormatImpl::writeIncidence(Incidence *incidence) +{ + ToStringVisitor v( this ); + incidence->accept(v); + return v.component(); +} + +icalcomponent *ICalFormatImpl::writeTodo(Todo *todo) +{ + QString tmpStr; + QStringList tmpStrList; + + icalcomponent *vtodo = icalcomponent_new(ICAL_VTODO_COMPONENT); + + writeIncidence(vtodo,todo); + + // due date + if (todo->hasDueDate()) { + icaltimetype due; + if (todo->doesFloat()) { + due = writeICalDate(todo->dtDue().date()); + } else { + due = writeICalDateTime(todo->dtDue()); + } + icalcomponent_add_property(vtodo,icalproperty_new_due(due)); + } + + // start time + if (todo->hasStartDate()) { + icaltimetype start; + if (todo->doesFloat()) { +// kdDebug(5800) << "§§ Incidence " << todo->summary() << " floats." << endl; + start = writeICalDate(todo->dtStart().date()); + } else { +// kdDebug(5800) << "§§ incidence " << todo->summary() << " has time." << endl; + start = writeICalDateTime(todo->dtStart()); + } + icalcomponent_add_property(vtodo,icalproperty_new_dtstart(start)); + } + + // completion date + if (todo->isCompleted()) { + if (!todo->hasCompletedDate()) { + // If todo was created by KOrganizer <2.2 it has no correct completion + // date. Set it to now. + todo->setCompleted(QDateTime::currentDateTime()); + } + icaltimetype completed = writeICalDateTime(todo->completed()); + icalcomponent_add_property(vtodo,icalproperty_new_completed(completed)); + } + + icalcomponent_add_property(vtodo, + icalproperty_new_percentcomplete(todo->percentComplete())); + + return vtodo; +} + +icalcomponent *ICalFormatImpl::writeEvent(Event *event) +{ + kdDebug(5800) << "Write Event '" << event->summary() << "' (" << event->uid() + << ")" << endl; + + QString tmpStr; + QStringList tmpStrList; + + icalcomponent *vevent = icalcomponent_new(ICAL_VEVENT_COMPONENT); + + writeIncidence(vevent,event); + + // start time + icaltimetype start; + if (event->doesFloat()) { +// kdDebug(5800) << "§§ Incidence " << event->summary() << " floats." << endl; + start = writeICalDate(event->dtStart().date()); + } else { +// kdDebug(5800) << "§§ incidence " << event->summary() << " has time." << endl; + start = writeICalDateTime(event->dtStart()); + } + icalcomponent_add_property(vevent,icalproperty_new_dtstart(start)); + + if (event->hasEndDate()) { + // end time + icaltimetype end; + if (event->doesFloat()) { +// kdDebug(5800) << "§§ Event " << event->summary() << " floats." << endl; + // +1 day because end date is non-inclusive. + end = writeICalDate( event->dtEnd().date().addDays( 1 ) ); + } else { +// kdDebug(5800) << "§§ Event " << event->summary() << " has time." << endl; + end = writeICalDateTime(event->dtEnd()); + } + icalcomponent_add_property(vevent,icalproperty_new_dtend(end)); + } + +// TODO: attachments, resources +#if 0 + // attachments + tmpStrList = anEvent->attachments(); + for ( QStringList::Iterator it = tmpStrList.begin(); + it != tmpStrList.end(); + ++it ) + addPropValue(vevent, VCAttachProp, (*it).utf8()); + + // resources + tmpStrList = anEvent->resources(); + tmpStr = tmpStrList.join(";"); + if (!tmpStr.isEmpty()) + addPropValue(vevent, VCResourcesProp, tmpStr.utf8()); + +#endif + + // Transparency + switch( event->transparency() ) { + case Event::Transparent: + icalcomponent_add_property(vevent, icalproperty_new_transp("TRANSPARENT")); + break; + case Event::Opaque: + icalcomponent_add_property(vevent, icalproperty_new_transp("OPAQUE")); + break; + } + + return vevent; +} + +icalcomponent *ICalFormatImpl::writeFreeBusy(FreeBusy *freebusy, + Scheduler::Method method) +{ +#if QT_VERSION >= 300 + kdDebug(5800) << "icalformatimpl: writeFreeBusy: startDate: " + << freebusy->dtStart().toString("ddd MMMM d yyyy: h:m:s ap") << " End Date: " + << freebusy->dtEnd().toString("ddd MMMM d yyyy: h:m:s ap") << endl; +#endif + + icalcomponent *vfreebusy = icalcomponent_new(ICAL_VFREEBUSY_COMPONENT); + + writeIncidenceBase(vfreebusy,freebusy); + + icalcomponent_add_property(vfreebusy, icalproperty_new_dtstart( + writeICalDateTime(freebusy->dtStart()))); + + icalcomponent_add_property(vfreebusy, icalproperty_new_dtend( + writeICalDateTime(freebusy->dtEnd()))); + + if (method == Scheduler::Request) { + icalcomponent_add_property(vfreebusy,icalproperty_new_uid( + freebusy->uid().utf8())); + } + + //Loops through all the periods in the freebusy object + QValueList<Period> list = freebusy->busyPeriods(); + QValueList<Period>::Iterator it; + icalperiodtype period; + for (it = list.begin(); it!= list.end(); ++it) { + period.start = writeICalDateTime((*it).start()); + period.end = writeICalDateTime((*it).end()); + icalcomponent_add_property(vfreebusy, icalproperty_new_freebusy(period) ); + } + + return vfreebusy; +} + +icalcomponent *ICalFormatImpl::writeJournal(Journal *journal) +{ + icalcomponent *vjournal = icalcomponent_new(ICAL_VJOURNAL_COMPONENT); + + writeIncidence(vjournal,journal); + + // start time + if (journal->dtStart().isValid()) { + icaltimetype start; + if (journal->doesFloat()) { +// kdDebug(5800) << "§§ Incidence " << event->summary() << " floats." << endl; + start = writeICalDate(journal->dtStart().date()); + } else { +// kdDebug(5800) << "§§ incidence " << event->summary() << " has time." << endl; + start = writeICalDateTime(journal->dtStart()); + } + icalcomponent_add_property(vjournal,icalproperty_new_dtstart(start)); + } + + return vjournal; +} + +void ICalFormatImpl::writeIncidence(icalcomponent *parent,Incidence *incidence) +{ + // pilot sync stuff +// TODO: move this application-specific code to kpilot + if (incidence->pilotId()) { + incidence->setNonKDECustomProperty("X-PILOTID", QString::number(incidence->pilotId())); + incidence->setNonKDECustomProperty("X-PILOTSTAT", QString::number(incidence->syncStatus())); + } + if (incidence->zaurusId() >= 0) { + incidence->setNonKDECustomProperty("X-ZAURUSID", QString::number(incidence->zaurusId())); + } + + if (incidence->zaurusUid() > 0) { + incidence->setNonKDECustomProperty("X-ZAURUSUID", QString::number(incidence->zaurusUid())); + } + if (incidence->zaurusStat() > 0) { + incidence->setNonKDECustomProperty("X-ZAURUSSTAT", QString::number(incidence->zaurusStat())); + } + + writeIncidenceBase(parent,incidence); + if (incidence->cancelled()) { + icalcomponent_add_property(parent,icalproperty_new_status(ICAL_STATUS_CANCELLED)); + } + + // creation date + icalcomponent_add_property(parent,icalproperty_new_created( + writeICalDateTime(incidence->created()))); + + // unique id + icalcomponent_add_property(parent,icalproperty_new_uid( + incidence->uid().utf8())); + + // revision + icalcomponent_add_property(parent,icalproperty_new_sequence( + incidence->revision())); + + // last modification date + icalcomponent_add_property(parent,icalproperty_new_lastmodified( + writeICalDateTime(incidence->lastModified()))); + + // description + if (!incidence->description().isEmpty()) { + icalcomponent_add_property(parent,icalproperty_new_description( + incidence->description().utf8())); + } + + // summary + if (!incidence->summary().isEmpty()) { + icalcomponent_add_property(parent,icalproperty_new_summary( + incidence->summary().utf8())); + } + + // location + if (!incidence->location().isEmpty()) { + icalcomponent_add_property(parent,icalproperty_new_location( + incidence->location().utf8())); + } + +// TODO: + // status +// addPropValue(parent, VCStatusProp, incidence->getStatusStr().utf8()); + + // secrecy + const char *classStr; + switch (incidence->secrecy()) { + case Incidence::SecrecyPublic: + classStr = "PUBLIC"; + break; + case Incidence::SecrecyConfidential: + classStr = "CONFIDENTIAL"; + break; + case Incidence::SecrecyPrivate: + default: + classStr = "PRIVATE"; + break; + } + icalcomponent_add_property(parent,icalproperty_new_class(classStr)); + + // priority + icalcomponent_add_property(parent,icalproperty_new_priority( + incidence->priority())); + + // categories + QStringList categories = incidence->categories(); + QStringList::Iterator it; + for(it = categories.begin(); it != categories.end(); ++it ) { + icalcomponent_add_property(parent,icalproperty_new_categories((*it).utf8())); + } +// TODO: Ensure correct concatenation of categories properties. + +/* + // categories + tmpStrList = incidence->getCategories(); + tmpStr = ""; + QString catStr; + for ( QStringList::Iterator it = tmpStrList.begin(); + it != tmpStrList.end(); + ++it ) { + catStr = *it; + if (catStr[0] == ' ') + tmpStr += catStr.mid(1); + else + tmpStr += catStr; + // this must be a ';' character as the vCalendar specification requires! + // vcc.y has been hacked to translate the ';' to a ',' when the vcal is + // read in. + tmpStr += ";"; + } + if (!tmpStr.isEmpty()) { + tmpStr.truncate(tmpStr.length()-1); + icalcomponent_add_property(parent,icalproperty_new_categories( + writeText(incidence->getCategories().join(";")))); + } +*/ + + // related event + if (incidence->relatedTo()) { + icalcomponent_add_property(parent,icalproperty_new_relatedto( + incidence->relatedTo()->uid().utf8())); + } + + // recurrence rule stuff + Recurrence *recur = incidence->recurrence(); + if (recur->doesRecur()) { + + icalcomponent_add_property(parent,writeRecurrenceRule(recur)); + } + + // recurrence excpetion dates + DateList dateList = incidence->exDates(); + DateList::ConstIterator exIt; + for(exIt = dateList.begin(); exIt != dateList.end(); ++exIt) { + icalcomponent_add_property(parent,icalproperty_new_exdate( + writeICalDate(*exIt))); + } + + // attachments + QPtrList<Attachment> attachments = incidence->attachments(); + for (Attachment *at = attachments.first(); at; at = attachments.next()) + icalcomponent_add_property(parent,writeAttachment(at)); + + // alarms + QPtrList<Alarm> alarms = incidence->alarms(); + Alarm* alarm; + for (alarm = alarms.first(); alarm; alarm = alarms.next()) { + if (alarm->enabled()) { + kdDebug(5800) << "Write alarm for " << incidence->summary() << endl; + icalcomponent_add_component(parent,writeAlarm(alarm)); + } + } + + // duration + +// turned off as it always is set to PTS0 (and must not occur together with DTEND + +// if (incidence->hasDuration()) { +// icaldurationtype duration; +// duration = writeICalDuration(incidence->duration()); +// icalcomponent_add_property(parent,icalproperty_new_duration(duration)); +// } +} + +void ICalFormatImpl::writeIncidenceBase(icalcomponent *parent,IncidenceBase *incidenceBase) +{ + icalcomponent_add_property(parent,icalproperty_new_dtstamp( + writeICalDateTime(QDateTime::currentDateTime()))); + + // organizer stuff + icalcomponent_add_property(parent,icalproperty_new_organizer( + ("MAILTO:" + incidenceBase->organizer()).utf8())); + + // attendees + if (incidenceBase->attendeeCount() != 0) { + QPtrList<Attendee> al = incidenceBase->attendees(); + QPtrListIterator<Attendee> ai(al); + for (; ai.current(); ++ai) { + icalcomponent_add_property(parent,writeAttendee(ai.current())); + } + } + + // custom properties + writeCustomProperties(parent, incidenceBase); +} + +void ICalFormatImpl::writeCustomProperties(icalcomponent *parent,CustomProperties *properties) +{ + QMap<QCString, QString> custom = properties->customProperties(); + for (QMap<QCString, QString>::Iterator c = custom.begin(); c != custom.end(); ++c) { + icalproperty *p = icalproperty_new_x(c.data().utf8()); + icalproperty_set_x_name(p,c.key()); + icalcomponent_add_property(parent,p); + } +} + +icalproperty *ICalFormatImpl::writeAttendee(Attendee *attendee) +{ + icalproperty *p = icalproperty_new_attendee("mailto:" + attendee->email().utf8()); + + if (!attendee->name().isEmpty()) { + icalproperty_add_parameter(p,icalparameter_new_cn(attendee->name().utf8())); + } + + + 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)); + + if (!attendee->uid().isEmpty()) { + icalparameter* icalparameter_uid = icalparameter_new_x(attendee->uid().utf8()); + icalparameter_set_xname(icalparameter_uid,"X-UID"); + icalproperty_add_parameter(p,icalparameter_uid); + } + + return p; +} + +icalproperty *ICalFormatImpl::writeAttachment(Attachment *att) +{ + icalattachtype* attach = icalattachtype_new(); + if (att->isURI()) + icalattachtype_set_url(attach, att->uri().utf8().data()); + else + icalattachtype_set_base64(attach, att->data(), 0); + + icalproperty *p = icalproperty_new_attach(attach); + + if (!att->mimeType().isEmpty()) + icalproperty_add_parameter(p,icalparameter_new_fmttype(att->mimeType().utf8().data())); + + if (att->isBinary()) { + icalproperty_add_parameter(p,icalparameter_new_value(ICAL_VALUE_BINARY)); + icalproperty_add_parameter(p,icalparameter_new_encoding(ICAL_ENCODING_BASE64)); + } + return p; +} + +icalproperty *ICalFormatImpl::writeRecurrenceRule(Recurrence *recur) +{ +// kdDebug(5800) << "ICalFormatImpl::writeRecurrenceRule()" << endl; + + icalrecurrencetype r; + + icalrecurrencetype_clear(&r); + + int index = 0; + int index2 = 0; + + QPtrList<Recurrence::rMonthPos> tmpPositions; + QPtrList<int> tmpDays; + int *tmpDay; + Recurrence::rMonthPos *tmpPos; + bool datetime = false; + int day; + int i; + + switch(recur->doesRecur()) { + case Recurrence::rMinutely: + r.freq = ICAL_MINUTELY_RECURRENCE; + datetime = true; + break; + case Recurrence::rHourly: + r.freq = ICAL_HOURLY_RECURRENCE; + datetime = true; + break; + case Recurrence::rDaily: + r.freq = ICAL_DAILY_RECURRENCE; + break; + case Recurrence::rWeekly: + r.freq = ICAL_WEEKLY_RECURRENCE; + r.week_start = static_cast<icalrecurrencetype_weekday>(recur->weekStart()%7 + 1); + for (i = 0; i < 7; i++) { + if (recur->days().testBit(i)) { + day = (i + 1)%7 + 1; // convert from Monday=0 to Sunday=1 + r.by_day[index++] = icalrecurrencetype_day_day_of_week(day); + } + } +// r.by_day[index] = ICAL_RECURRENCE_ARRAY_MAX; + break; + case Recurrence::rMonthlyPos: + r.freq = ICAL_MONTHLY_RECURRENCE; + + tmpPositions = recur->monthPositions(); + for (tmpPos = tmpPositions.first(); + tmpPos; + tmpPos = tmpPositions.next()) { + for (i = 0; i < 7; i++) { + if (tmpPos->rDays.testBit(i)) { + day = (i + 1)%7 + 1; // convert from Monday=0 to Sunday=1 + day += tmpPos->rPos*8; + if (tmpPos->negative) day = -day; + r.by_day[index++] = day; + } + } + } +// r.by_day[index] = ICAL_RECURRENCE_ARRAY_MAX; + break; + case Recurrence::rMonthlyDay: + r.freq = ICAL_MONTHLY_RECURRENCE; + + tmpDays = recur->monthDays(); + for (tmpDay = tmpDays.first(); + tmpDay; + tmpDay = tmpDays.next()) { + r.by_month_day[index++] = icalrecurrencetype_day_position(*tmpDay*8);//*tmpDay); + } +// r.by_day[index] = ICAL_RECURRENCE_ARRAY_MAX; + break; + case Recurrence::rYearlyMonth: + case Recurrence::rYearlyPos: + r.freq = ICAL_YEARLY_RECURRENCE; + + tmpDays = recur->yearNums(); + for (tmpDay = tmpDays.first(); + tmpDay; + tmpDay = tmpDays.next()) { + r.by_month[index++] = *tmpDay; + } +// r.by_set_pos[index] = ICAL_RECURRENCE_ARRAY_MAX; + if (recur->doesRecur() == Recurrence::rYearlyPos) { + tmpPositions = recur->monthPositions(); + for (tmpPos = tmpPositions.first(); + tmpPos; + tmpPos = tmpPositions.next()) { + for (i = 0; i < 7; i++) { + if (tmpPos->rDays.testBit(i)) { + day = (i + 1)%7 + 1; // convert from Monday=0 to Sunday=1 + day += tmpPos->rPos*8; + if (tmpPos->negative) day = -day; + r.by_day[index2++] = day; + } + } + } +// r.by_day[index2] = ICAL_RECURRENCE_ARRAY_MAX; + } + break; + case Recurrence::rYearlyDay: + r.freq = ICAL_YEARLY_RECURRENCE; + + tmpDays = recur->yearNums(); + for (tmpDay = tmpDays.first(); + tmpDay; + tmpDay = tmpDays.next()) { + r.by_year_day[index++] = *tmpDay; + } +// r.by_year_day[index] = ICAL_RECURRENCE_ARRAY_MAX; + break; + default: + r.freq = ICAL_NO_RECURRENCE; + kdDebug(5800) << "ICalFormatImpl::writeRecurrence(): no recurrence" << endl; + break; + } + + r.interval = recur->frequency(); + + if (recur->duration() > 0) { + r.count = recur->duration(); + } else if (recur->duration() == -1) { + r.count = 0; + } else { + if (datetime) + r.until = writeICalDateTime(recur->endDateTime()); + else + r.until = writeICalDate(recur->endDate()); + } + +// Debug output +#if 0 + const char *str = icalrecurrencetype_as_string(&r); + if (str) { + kdDebug(5800) << " String: " << str << endl; + } else { + kdDebug(5800) << " No String" << endl; + } +#endif + + return icalproperty_new_rrule(r); +} + +icalcomponent *ICalFormatImpl::writeAlarm(Alarm *alarm) +{ + icalcomponent *a = icalcomponent_new(ICAL_VALARM_COMPONENT); + + icalproperty_action action; + icalattachtype *attach = 0; + + switch (alarm->type()) { + case Alarm::Procedure: + action = ICAL_ACTION_PROCEDURE; + attach = icalattachtype_new(); + icalattachtype_set_url(attach,QFile::encodeName(alarm->programFile()).data()); + icalcomponent_add_property(a,icalproperty_new_attach(attach)); + icalattachtype_free(attach); + if (!alarm->programArguments().isEmpty()) { + icalcomponent_add_property(a,icalproperty_new_description(alarm->programArguments().utf8())); + } + break; + case Alarm::Audio: + action = ICAL_ACTION_AUDIO; + if (!alarm->audioFile().isEmpty()) { + attach = icalattachtype_new(); + icalattachtype_set_url(attach,QFile::encodeName( alarm->audioFile() ).data()); + icalcomponent_add_property(a,icalproperty_new_attach(attach)); + icalattachtype_free(attach); + } + break; + case Alarm::Email: { + action = ICAL_ACTION_EMAIL; + QValueList<Person> addresses = alarm->mailAddresses(); + for (QValueList<Person>::Iterator ad = addresses.begin(); ad != addresses.end(); ++ad) { + icalproperty *p = icalproperty_new_attendee("MAILTO:" + (*ad).email().utf8()); + if (!(*ad).name().isEmpty()) { + icalproperty_add_parameter(p,icalparameter_new_cn((*ad).name().utf8())); + } + icalcomponent_add_property(a,p); + } + icalcomponent_add_property(a,icalproperty_new_summary(alarm->mailSubject().utf8())); + icalcomponent_add_property(a,icalproperty_new_description(alarm->text().utf8())); + QStringList attachments = alarm->mailAttachments(); + if (attachments.count() > 0) { + for (QStringList::Iterator at = attachments.begin(); at != attachments.end(); ++at) { + attach = icalattachtype_new(); + icalattachtype_set_url(attach,QFile::encodeName( *at ).data()); + icalcomponent_add_property(a,icalproperty_new_attach(attach)); + icalattachtype_free(attach); + } + } + break; + } + case Alarm::Display: + action = ICAL_ACTION_DISPLAY; + icalcomponent_add_property(a,icalproperty_new_description(alarm->text().utf8())); + break; + case Alarm::Invalid: + default: + kdDebug(5800) << "Unknown type of alarm" << endl; + action = ICAL_ACTION_NONE; + break; + } + icalcomponent_add_property(a,icalproperty_new_action(action)); + + // Trigger time + icaltriggertype trigger; + if ( alarm->hasTime() ) { + trigger.time = writeICalDateTime(alarm->time()); + 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 = icaldurationtype_from_int( offset.asSeconds() ); + } + 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( + icaldurationtype_from_int(alarm->snoozeTime()*60))); + } + + // Custom properties + QMap<QCString, QString> custom = alarm->customProperties(); + for (QMap<QCString, QString>::Iterator c = custom.begin(); c != custom.end(); ++c) { + icalproperty *p = icalproperty_new_x(c.data().utf8()); + icalproperty_set_x_name(p,c.key()); + icalcomponent_add_property(a,p); + } + + return a; +} + +Todo *ICalFormatImpl::readTodo(icalcomponent *vtodo) +{ + Todo *todo = new Todo; + + readIncidence(vtodo,todo); + + icalproperty *p = icalcomponent_get_first_property(vtodo,ICAL_ANY_PROPERTY); + +// int intvalue; + icaltimetype icaltime; + + QStringList categories; + + while (p) { + icalproperty_kind kind = icalproperty_isa(p); + switch (kind) { + + case ICAL_DUE_PROPERTY: // due date + icaltime = icalproperty_get_due(p); + if (icaltime.is_date) { + todo->setDtDue(QDateTime(readICalDate(icaltime),QTime(0,0,0))); + todo->setFloats(true); + + } else { + todo->setDtDue(readICalDateTime(icaltime)); + todo->setFloats(false); + } + todo->setHasDueDate(true); + break; + + case ICAL_COMPLETED_PROPERTY: // completion date + icaltime = icalproperty_get_completed(p); + todo->setCompleted(readICalDateTime(icaltime)); + break; + + case ICAL_PERCENTCOMPLETE_PROPERTY: // Percent completed + todo->setPercentComplete(icalproperty_get_percentcomplete(p)); + break; + + case ICAL_RELATEDTO_PROPERTY: // related todo (parent) + todo->setRelatedToUid(QString::fromUtf8(icalproperty_get_relatedto(p))); + mTodosRelate.append(todo); + break; + + case ICAL_DTSTART_PROPERTY: + // Flag that todo has start date. Value is read in by readIncidence(). + todo->setHasStartDate(true); + break; + + default: +// kdDebug(5800) << "ICALFormat::readTodo(): Unknown property: " << kind +// << endl; + break; + } + + p = icalcomponent_get_next_property(vtodo,ICAL_ANY_PROPERTY); + } + + return todo; +} + +Event *ICalFormatImpl::readEvent(icalcomponent *vevent) +{ + Event *event = new Event; + event->setFloats(false); + + readIncidence(vevent,event); + + icalproperty *p = icalcomponent_get_first_property(vevent,ICAL_ANY_PROPERTY); + +// int intvalue; + icaltimetype icaltime; + + QStringList categories; + QString transparency; + + while (p) { + icalproperty_kind kind = icalproperty_isa(p); + switch (kind) { + + case ICAL_DTEND_PROPERTY: // start date and time + icaltime = icalproperty_get_dtend(p); + if (icaltime.is_date) { + event->setFloats( true ); + // End date is non-inclusive + QDate endDate = readICalDate( icaltime ).addDays( -1 ); + mCompat->fixFloatingEnd( endDate ); + if ( endDate < event->dtStart().date() ) { + endDate = event->dtStart().date(); + } + event->setDtEnd( QDateTime( endDate, QTime( 0, 0, 0 ) ) ); + } else { + event->setDtEnd(readICalDateTime(icaltime)); + } + break; + +// TODO: + // 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 0 + if (!(vo = isAPropertyOf(vevent, VCDTstartProp))) + anEvent->setDtStart(anEvent->dtEnd()); + if (!(vo = isAPropertyOf(vevent, VCDTendProp))) + anEvent->setDtEnd(anEvent->dtStart()); +#endif + +// TODO: exdates +#if 0 + // recurrence exceptions + if ((vo = isAPropertyOf(vevent, VCExDateProp)) != 0) { + anEvent->setExDates(s = fakeCString(vObjectUStringZValue(vo))); + deleteStr(s); + } +#endif + +#if 0 + // secrecy + if ((vo = isAPropertyOf(vevent, VCClassProp)) != 0) { + anEvent->setSecrecy(s = fakeCString(vObjectUStringZValue(vo))); + deleteStr(s); + } + else + anEvent->setSecrecy("PUBLIC"); + + // attachments + tmpStrList.clear(); + initPropIterator(&voi, vevent); + while (moreIteration(&voi)) { + vo = nextVObject(&voi); + if (strcmp(vObjectName(vo), VCAttachProp) == 0) { + tmpStrList.append(s = fakeCString(vObjectUStringZValue(vo))); + deleteStr(s); + } + } + anEvent->setAttachments(tmpStrList); + + // resources + if ((vo = isAPropertyOf(vevent, VCResourcesProp)) != 0) { + QString resources = (s = fakeCString(vObjectUStringZValue(vo))); + deleteStr(s); + tmpStrList.clear(); + index1 = 0; + index2 = 0; + QString resource; + while ((index2 = resources.find(';', index1)) != -1) { + resource = resources.mid(index1, (index2 - index1)); + tmpStrList.append(resource); + index1 = index2; + } + anEvent->setResources(tmpStrList); + } +#endif + + case ICAL_RELATEDTO_PROPERTY: // releated event (parent) + event->setRelatedToUid(QString::fromUtf8(icalproperty_get_relatedto(p))); + mEventsRelate.append(event); + break; + + + case ICAL_TRANSP_PROPERTY: // Transparency + transparency = QString::fromUtf8(icalproperty_get_transp(p)); + if( transparency == "TRANSPARENT" ) + event->setTransparency( Event::Transparent ); + else + event->setTransparency( Event::Opaque ); + break; + + default: +// kdDebug(5800) << "ICALFormat::readEvent(): Unknown property: " << kind +// << endl; + break; + } + + p = icalcomponent_get_next_property(vevent,ICAL_ANY_PROPERTY); + } + + QString msade = event->nonKDECustomProperty("X-MICROSOFT-CDO-ALLDAYEVENT"); + if (!msade.isNull()) { + bool floats = (msade == QString::fromLatin1("TRUE")); + kdDebug(5800) << "ICALFormat::readEvent(): all day event: " << floats << endl; + event->setFloats(floats); + if (floats) { + QDateTime endDate = event->dtEnd(); + event->setDtEnd(endDate.addDays(-1)); + } + } + + // some stupid vCal exporters ignore the standard and use Description + // instead of Summary for the default field. Correct for this. + if (event->summary().isEmpty() && + !(event->description().isEmpty())) { + QString tmpStr = event->description().simplifyWhiteSpace(); + event->setDescription(""); + event->setSummary(tmpStr); + } + + return event; +} + +FreeBusy *ICalFormatImpl::readFreeBusy(icalcomponent *vfreebusy) +{ + FreeBusy *freebusy = new FreeBusy; + + readIncidenceBase(vfreebusy,freebusy); + + icalproperty *p = icalcomponent_get_first_property(vfreebusy,ICAL_ANY_PROPERTY); + + icaltimetype icaltime; + icalperiodtype icalperiod; + QDateTime period_start, period_end; + + while (p) { + icalproperty_kind kind = icalproperty_isa(p); + switch (kind) { + + case ICAL_DTSTART_PROPERTY: // start date and time + icaltime = icalproperty_get_dtstart(p); + freebusy->setDtStart(readICalDateTime(icaltime)); + break; + + case ICAL_DTEND_PROPERTY: // start End Date and Time + icaltime = icalproperty_get_dtend(p); + freebusy->setDtEnd(readICalDateTime(icaltime)); + break; + + case ICAL_FREEBUSY_PROPERTY: //Any FreeBusy Times + icalperiod = icalproperty_get_freebusy(p); + period_start = readICalDateTime(icalperiod.start); + period_end = readICalDateTime(icalperiod.end); + freebusy->addPeriod(period_start, period_end); + break; + + default: + kdDebug(5800) << "ICALFormat::readIncidence(): Unknown property: " << kind + << endl; + break; + } + p = icalcomponent_get_next_property(vfreebusy,ICAL_ANY_PROPERTY); + } + + return freebusy; +} + +Journal *ICalFormatImpl::readJournal(icalcomponent *vjournal) +{ + Journal *journal = new Journal; + + readIncidence(vjournal,journal); + + return journal; +} + +Attendee *ICalFormatImpl::readAttendee(icalproperty *attendee) +{ + icalparameter *p = 0; + + QString email = QString::fromUtf8(icalproperty_get_attendee(attendee)); + + QString name; + QString uid = QString::null; + 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; + } + } + + p = icalproperty_get_first_parameter(attendee,ICAL_X_PARAMETER); + uid = icalparameter_get_xvalue(p); + // This should be added, but there seems to be a libical bug here. + /*while (p) { + // if (icalparameter_get_xname(p) == "X-UID") { + uid = icalparameter_get_xvalue(p); + p = icalproperty_get_next_parameter(attendee,ICAL_X_PARAMETER); + } */ + + return new Attendee( name, email, rsvp, status, role, uid ); +} + +Attachment *ICalFormatImpl::readAttachment(icalproperty *attach) +{ + icalattachtype *a = icalproperty_get_attach(attach); + icalparameter_value v = ICAL_VALUE_NONE; + icalparameter_encoding e = ICAL_ENCODING_NONE; + + Attachment *attachment = 0; + + icalparameter *vp = icalproperty_get_first_parameter(attach, ICAL_VALUE_PARAMETER); + if (vp) + v = icalparameter_get_value(vp); + + icalparameter *ep = icalproperty_get_first_parameter(attach, ICAL_ENCODING_PARAMETER); + if (ep) + e = icalparameter_get_encoding(ep); + + if (v == ICAL_VALUE_BINARY && e == ICAL_ENCODING_BASE64) + attachment = new Attachment(icalattachtype_get_base64(a)); + else if ((v == ICAL_VALUE_NONE || v == ICAL_VALUE_URI) && (e == ICAL_ENCODING_NONE || e == ICAL_ENCODING_8BIT)) { + attachment = new Attachment(QString(icalattachtype_get_url(a))); + } else { + kdWarning(5800) << "Unsupported attachment format, discarding it!" << endl; + return 0; + } + + icalparameter *p = icalproperty_get_first_parameter(attach, ICAL_FMTTYPE_PARAMETER); + if (p) + attachment->setMimeType(QString(icalparameter_get_fmttype(p))); + + return attachment; +} +#include <qtextcodec.h> +void ICalFormatImpl::readIncidence(icalcomponent *parent,Incidence *incidence) +{ + readIncidenceBase(parent,incidence); + + icalproperty *p = icalcomponent_get_first_property(parent,ICAL_ANY_PROPERTY); + bool readrec = false; + const char *text; + int intvalue; + icaltimetype icaltime; + icaldurationtype icalduration; + struct icalrecurrencetype rectype; + QStringList categories; + + while (p) { + icalproperty_kind kind = icalproperty_isa(p); + switch (kind) { + + case ICAL_CREATED_PROPERTY: + icaltime = icalproperty_get_created(p); + incidence->setCreated(readICalDateTime(icaltime)); + break; + + case ICAL_SEQUENCE_PROPERTY: // sequence + intvalue = icalproperty_get_sequence(p); + incidence->setRevision(intvalue); + break; + + case ICAL_LASTMODIFIED_PROPERTY: // last modification date + icaltime = icalproperty_get_lastmodified(p); + incidence->setLastModified(readICalDateTime(icaltime)); + break; + + case ICAL_DTSTART_PROPERTY: // start date and time + icaltime = icalproperty_get_dtstart(p); + if (icaltime.is_date) { + incidence->setDtStart(QDateTime(readICalDate(icaltime),QTime(0,0,0))); + incidence->setFloats(true); + } else { + incidence->setDtStart(readICalDateTime(icaltime)); + } + break; + + case ICAL_DURATION_PROPERTY: // start date and time + icalduration = icalproperty_get_duration(p); + incidence->setDuration(readICalDuration(icalduration)); + break; + + case ICAL_DESCRIPTION_PROPERTY: // description + text = icalproperty_get_description(p); + incidence->setDescription(QString::fromUtf8(text)); + break; + + case ICAL_SUMMARY_PROPERTY: // summary + { + text = icalproperty_get_summary(p); + incidence->setSummary(QString::fromUtf8(text)); + } + break; + case ICAL_STATUS_PROPERTY: // summary + { + if ( ICAL_STATUS_CANCELLED == icalproperty_get_status(p) ) + incidence->setCancelled( true ); + } + break; + + case ICAL_LOCATION_PROPERTY: // location + text = icalproperty_get_location(p); + incidence->setLocation(QString::fromUtf8(text)); + break; + +#if 0 + // status + if ((vo = isAPropertyOf(vincidence, VCStatusProp)) != 0) { + incidence->setStatus(s = fakeCString(vObjectUStringZValue(vo))); + deleteStr(s); + } + else + incidence->setStatus("NEEDS ACTION"); +#endif + + case ICAL_PRIORITY_PROPERTY: // priority + intvalue = icalproperty_get_priority(p); + incidence->setPriority(intvalue); + break; + + case ICAL_CATEGORIES_PROPERTY: // categories + text = icalproperty_get_categories(p); + categories.append(QString::fromUtf8(text)); + break; + //******************************************* + case ICAL_RRULE_PROPERTY: + // we do need (maybe )start datetime of incidence for recurrence + // such that we can read recurrence only after we read incidence completely + readrec = true; + rectype = icalproperty_get_rrule(p); + break; + + case ICAL_EXDATE_PROPERTY: + icaltime = icalproperty_get_exdate(p); + incidence->addExDate(readICalDate(icaltime)); + break; + + case ICAL_CLASS_PROPERTY: + text = icalproperty_get_class(p); + if (strcmp(text,"PUBLIC") == 0) { + incidence->setSecrecy(Incidence::SecrecyPublic); + } else if (strcmp(text,"CONFIDENTIAL") == 0) { + incidence->setSecrecy(Incidence::SecrecyConfidential); + } else { + incidence->setSecrecy(Incidence::SecrecyPrivate); + } + break; + + case ICAL_ATTACH_PROPERTY: // attachments + incidence->addAttachment(readAttachment(p)); + break; + + default: +// kdDebug(5800) << "ICALFormat::readIncidence(): Unknown property: " << kind +// << endl; + break; + } + + p = icalcomponent_get_next_property(parent,ICAL_ANY_PROPERTY); + } + if ( readrec ) { + readRecurrenceRule(rectype,incidence); + } + // kpilot stuff +// TODO: move this application-specific code to kpilot + QString kp = incidence->nonKDECustomProperty("X-PILOTID"); + if (!kp.isNull()) { + incidence->setPilotId(kp.toInt()); + } + kp = incidence->nonKDECustomProperty("X-PILOTSTAT"); + if (!kp.isNull()) { + incidence->setSyncStatus(kp.toInt()); + } + kp = incidence->nonKDECustomProperty("X-ZAURUSID"); + if (!kp.isNull()) { + incidence->setZaurusId(kp.toInt()); + } + + kp = incidence->nonKDECustomProperty("X-ZAURUSUID"); + if (!kp.isNull()) { + incidence->setZaurusUid(kp.toInt()); + } + + kp = incidence->nonKDECustomProperty("X-ZAURUSSTAT"); + if (!kp.isNull()) { + incidence->setZaurusStat(kp.toInt()); + } + + // Cancel backwards compatibility mode for subsequent changes by the application + incidence->recurrence()->setCompatVersion(); + + // 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); + } +} + +void ICalFormatImpl::readIncidenceBase(icalcomponent *parent,IncidenceBase *incidenceBase) +{ + icalproperty *p = icalcomponent_get_first_property(parent,ICAL_ANY_PROPERTY); + + while (p) { + icalproperty_kind kind = icalproperty_isa(p); + switch (kind) { + + case ICAL_UID_PROPERTY: // unique id + incidenceBase->setUid(QString::fromUtf8(icalproperty_get_uid(p))); + break; + + case ICAL_ORGANIZER_PROPERTY: // organizer + incidenceBase->setOrganizer(QString::fromUtf8(icalproperty_get_organizer(p))); + break; + + case ICAL_ATTENDEE_PROPERTY: // attendee + incidenceBase->addAttendee(readAttendee(p)); + break; + + default: + break; + } + + p = icalcomponent_get_next_property(parent,ICAL_ANY_PROPERTY); + } + + // custom properties + readCustomProperties(parent, incidenceBase); +} + +void ICalFormatImpl::readCustomProperties(icalcomponent *parent,CustomProperties *properties) +{ + QMap<QCString, QString> customProperties; + + icalproperty *p = icalcomponent_get_first_property(parent,ICAL_X_PROPERTY); + + while (p) { + + QString value = QString::fromUtf8(icalproperty_get_x(p)); + customProperties[icalproperty_get_name(p)] = value; + + p = icalcomponent_get_next_property(parent,ICAL_X_PROPERTY); + } + + properties->setCustomProperties(customProperties); +} + +void ICalFormatImpl::readRecurrenceRule(struct icalrecurrencetype rrule,Incidence *incidence) +{ +// kdDebug(5800) << "Read recurrence for " << incidence->summary() << endl; + + Recurrence *recur = incidence->recurrence(); + recur->setCompatVersion(mCalendarVersion); + recur->unsetRecurs(); + + struct icalrecurrencetype r = rrule; + + dumpIcalRecurrence(r); + readRecurrence( r, recur, incidence); +} + +void ICalFormatImpl::readRecurrence( const struct icalrecurrencetype &r, Recurrence* recur, Incidence *incidence) +{ + int wkst; + int index = 0; + short day = 0; + QBitArray qba(7); + int frequ = r.freq; + int interv = r.interval; + // preprocessing for odd recurrence definitions + + if ( r.freq == ICAL_MONTHLY_RECURRENCE ) { + if ( r.by_month[0] != ICAL_RECURRENCE_ARRAY_MAX) { + interv = 12; + } + } + if ( r.freq == ICAL_YEARLY_RECURRENCE ) { + if ( r.by_month[0] != ICAL_RECURRENCE_ARRAY_MAX && r.by_day[0] != ICAL_RECURRENCE_ARRAY_MAX ) { + frequ = ICAL_MONTHLY_RECURRENCE; + interv = 12; + } + } + + switch (frequ) { + case ICAL_MINUTELY_RECURRENCE: + if (!icaltime_is_null_time(r.until)) { + recur->setMinutely(interv,readICalDateTime(r.until)); + } else { + if (r.count == 0) + recur->setMinutely(interv,-1); + else + recur->setMinutely(interv,r.count); + } + break; + case ICAL_HOURLY_RECURRENCE: + if (!icaltime_is_null_time(r.until)) { + recur->setHourly(interv,readICalDateTime(r.until)); + } else { + if (r.count == 0) + recur->setHourly(interv,-1); + else + recur->setHourly(interv,r.count); + } + break; + case ICAL_DAILY_RECURRENCE: + if (!icaltime_is_null_time(r.until)) { + recur->setDaily(interv,readICalDate(r.until)); + } else { + if (r.count == 0) + recur->setDaily(interv,-1); + else + recur->setDaily(interv,r.count); + } + break; + case ICAL_WEEKLY_RECURRENCE: + // kdDebug(5800) << "WEEKLY_RECURRENCE" << endl; + wkst = (r.week_start + 5)%7 + 1; + if (!icaltime_is_null_time(r.until)) { + recur->setWeekly(interv,qba,readICalDate(r.until),wkst); + } else { + if (r.count == 0) + recur->setWeekly(interv,qba,-1,wkst); + else + recur->setWeekly(interv,qba,r.count,wkst); + } + if ( r.by_day[0] == ICAL_RECURRENCE_ARRAY_MAX) { + int wday = incidence->dtStart().date().dayOfWeek ()-1; + //qDebug("weekly error found "); + qba.setBit(wday); + } else { + while((day = r.by_day[index++]) != ICAL_RECURRENCE_ARRAY_MAX) { + // kdDebug(5800) << " " << day << endl; + qba.setBit((day+5)%7); // convert from Sunday=1 to Monday=0 + } + } + break; + case ICAL_MONTHLY_RECURRENCE: + + if (r.by_day[0] != ICAL_RECURRENCE_ARRAY_MAX) { + if (!icaltime_is_null_time(r.until)) { + recur->setMonthly(Recurrence::rMonthlyPos,interv, + readICalDate(r.until)); + } else { + if (r.count == 0) + recur->setMonthly(Recurrence::rMonthlyPos,interv,-1); + else + recur->setMonthly(Recurrence::rMonthlyPos,interv,r.count); + } + bool useSetPos = false; + short pos = 0; + while((day = r.by_day[index++]) != ICAL_RECURRENCE_ARRAY_MAX) { + // kdDebug(5800) << "----a " << index << ": " << day << endl; + pos = icalrecurrencetype_day_position(day); + if (pos) { + day = icalrecurrencetype_day_day_of_week(day); + QBitArray ba(7); // don't wipe qba + ba.setBit((day+5)%7); // convert from Sunday=1 to Monday=0 + recur->addMonthlyPos(pos,ba); + } else { + qba.setBit((day+5)%7); // convert from Sunday=1 to Monday=0 + useSetPos = true; + } + } + if (useSetPos) { + if (r.by_set_pos[0] != ICAL_RECURRENCE_ARRAY_MAX) { + recur->addMonthlyPos(r.by_set_pos[0],qba); + } + } + } else if (r.by_month_day[0] != ICAL_RECURRENCE_ARRAY_MAX) { + if (!icaltime_is_null_time(r.until)) { + recur->setMonthly(Recurrence::rMonthlyDay,interv, + readICalDate(r.until)); + } else { + if (r.count == 0) + recur->setMonthly(Recurrence::rMonthlyDay,interv,-1); + else + recur->setMonthly(Recurrence::rMonthlyDay,interv,r.count); + } + while((day = r.by_month_day[index++]) != ICAL_RECURRENCE_ARRAY_MAX) { + // kdDebug(5800) << "----b " << day << endl; + recur->addMonthlyDay(day); + } + } + break; + case ICAL_YEARLY_RECURRENCE: + if (r.by_year_day[0] != ICAL_RECURRENCE_ARRAY_MAX) { + if (!icaltime_is_null_time(r.until)) { + recur->setYearly(Recurrence::rYearlyDay,interv, + readICalDate(r.until)); + } else { + if (r.count == 0) + recur->setYearly(Recurrence::rYearlyDay,interv,-1); + else + recur->setYearly(Recurrence::rYearlyDay,interv,r.count); + } + while((day = r.by_year_day[index++]) != ICAL_RECURRENCE_ARRAY_MAX) { + recur->addYearlyNum(day); + } + } else if ( true /*r.by_month[0] != ICAL_RECURRENCE_ARRAY_MAX*/) { + if (r.by_day[0] != ICAL_RECURRENCE_ARRAY_MAX) { + if (!icaltime_is_null_time(r.until)) { + recur->setYearly(Recurrence::rYearlyPos,interv, + readICalDate(r.until)); + } else { + if (r.count == 0) + recur->setYearly(Recurrence::rYearlyPos,interv,-1); + else + recur->setYearly(Recurrence::rYearlyPos,interv,r.count); + } + bool useSetPos = false; + short pos = 0; + while((day = r.by_day[index++]) != ICAL_RECURRENCE_ARRAY_MAX) { + // kdDebug(5800) << "----a " << index << ": " << day << endl; + pos = icalrecurrencetype_day_position(day); + if (pos) { + day = icalrecurrencetype_day_day_of_week(day); + QBitArray ba(7); // don't wipe qba + ba.setBit((day+5)%7); // convert from Sunday=1 to Monday=0 + recur->addYearlyMonthPos(pos,ba); + } else { + qba.setBit((day+5)%7); // convert from Sunday=1 to Monday=0 + useSetPos = true; + } + } + if (useSetPos) { + if (r.by_set_pos[0] != ICAL_RECURRENCE_ARRAY_MAX) { + recur->addYearlyMonthPos(r.by_set_pos[0],qba); + } + } + } else { + if (!icaltime_is_null_time(r.until)) { + recur->setYearly(Recurrence::rYearlyMonth,interv, + readICalDate(r.until)); + } else { + if (r.count == 0) + recur->setYearly(Recurrence::rYearlyMonth,interv,-1); + else + recur->setYearly(Recurrence::rYearlyMonth,interv,r.count); + } + } + if ( r.by_month[0] != ICAL_RECURRENCE_ARRAY_MAX ) { + index = 0; + while((day = r.by_month[index++]) != ICAL_RECURRENCE_ARRAY_MAX) { + recur->addYearlyNum(day); + } + } else { + recur->addYearlyNum(incidence->dtStart().date().month()); + } + } + break; + default: + ; + break; + } +} + +void ICalFormatImpl::readAlarm(icalcomponent *alarm,Incidence *incidence) +{ + //kdDebug(5800) << "Read alarm for " << incidence->summary() << endl; + + Alarm* 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); + if ( !p ) { + return; + } + + icalproperty_action action = icalproperty_get_action(p); + Alarm::Type type = Alarm::Display; + 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: + ; + return; + } + 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)) { + if (icaldurationtype_is_null_duration(trigger.duration)) { + kdDebug(5800) << "ICalFormatImpl::readAlarm(): Trigger has no time and no duration." << endl; + } else { + Duration duration = icaldurationtype_as_int( 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 { + ialarm->setTime(readICalDateTime(trigger.time)); + } + break; + } + case ICAL_DURATION_PROPERTY: { + icaldurationtype duration = icalproperty_get_duration(p); + ialarm->setSnoozeTime(icaldurationtype_as_int(duration)/60); + break; + } + case ICAL_REPEAT_PROPERTY: + ialarm->setRepeatCount(icalproperty_get_repeat(p)); + break; + + // Only in DISPLAY and EMAIL and PROCEDURE alarms + case ICAL_DESCRIPTION_PROPERTY: { + 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; + } + // Only in EMAIL alarm + case ICAL_SUMMARY_PROPERTY: + ialarm->setMailSubject(QString::fromUtf8(icalproperty_get_summary(p))); + break; + + // Only in EMAIL alarm + case ICAL_ATTENDEE_PROPERTY: { + QString email = QString::fromUtf8(icalproperty_get_attendee(p)); + 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; + } + // Only in AUDIO and EMAIL and PROCEDURE alarms + case ICAL_ATTACH_PROPERTY: { + icalattachtype *attach = icalproperty_get_attach(p); + QString url = QFile::decodeName(icalattachtype_get_url(attach)); + switch ( action ) { + case ICAL_ACTION_AUDIO: + ialarm->setAudioFile( url ); + break; + case ICAL_ACTION_PROCEDURE: + ialarm->setProgramFile( url ); + break; + case ICAL_ACTION_EMAIL: + ialarm->addMailAttachment( url ); + break; + default: + break; + } + break; + } + default: + break; + } + + p = icalcomponent_get_next_property(alarm,ICAL_ANY_PROPERTY); + } + + // custom properties + readCustomProperties(alarm, ialarm); + + // TODO: check for consistency of alarm properties +} + +icaltimetype ICalFormatImpl::writeICalDate(const QDate &date) +{ + icaltimetype t; + + 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; + + t.is_utc = 0; + + t.zone = 0; + + return t; +} + +icaltimetype ICalFormatImpl::writeICalDateTime(const QDateTime &dt ) +{ + icaltimetype t; + t.is_date = 0; + t.zone = 0; + QDateTime datetime; + if ( mParent->utc() ) { + int offset = KGlobal::locale()->localTimeOffset( dt ); + datetime = dt.addSecs ( -offset*60); + t.is_utc = 1; + } + else { + datetime = dt; + t.is_utc = 0; + + } + t.year = datetime.date().year(); + t.month = datetime.date().month(); + t.day = datetime.date().day(); + + t.hour = datetime.time().hour(); + t.minute = datetime.time().minute(); + t.second = datetime.time().second(); + + //qDebug("*** time %s localtime %s ",dt .toString().latin1() ,datetime .toString().latin1() ); + +// if ( mParent->utc() ) { +// datetime = KGlobal::locale()->localTime( dt ); +// qDebug("*** time %s localtime %s ",dt .toString().latin1() ,datetime .toString().latin1() ); +// if (mParent->timeZoneId().isEmpty()) +// t = icaltime_as_utc(t, 0); +// else +// t = icaltime_as_utc(t,mParent->timeZoneId().local8Bit()); +// } + + return t; +} + +QDateTime ICalFormatImpl::readICalDateTime(icaltimetype t) +{ + QDateTime dt (QDate(t.year,t.month,t.day), + QTime(t.hour,t.minute,t.second) ); + + if (t.is_utc) { + int offset = KGlobal::locale()->localTimeOffset( dt ); + dt = dt.addSecs ( offset*60); + } + + return dt; +} + +QDate ICalFormatImpl::readICalDate(icaltimetype t) +{ + return QDate(t.year,t.month,t.day); +} + +icaldurationtype ICalFormatImpl::writeICalDuration(int seconds) +{ + icaldurationtype d; + + d.weeks = seconds % gSecondsPerWeek; + seconds -= d.weeks * gSecondsPerWeek; + d.days = seconds % gSecondsPerDay; + seconds -= d.days * gSecondsPerDay; + d.hours = seconds % gSecondsPerHour; + seconds -= d.hours * gSecondsPerHour; + d.minutes = seconds % gSecondsPerMinute; + seconds -= d.minutes * gSecondsPerMinute; + d.seconds = seconds; + d.is_neg = 0; + + return d; +} + +int ICalFormatImpl::readICalDuration(icaldurationtype d) +{ + int result = 0; + + result += d.weeks * gSecondsPerWeek; + result += d.days * gSecondsPerDay; + result += d.hours * gSecondsPerHour; + result += d.minutes * gSecondsPerMinute; + result += d.seconds; + + if (d.is_neg) result *= -1; + + return result; +} + +icalcomponent *ICalFormatImpl::createCalendarComponent(Calendar *cal) +{ + icalcomponent *calendar; + + // Root component + calendar = icalcomponent_new(ICAL_VCALENDAR_COMPONENT); + + icalproperty *p; + + // Product Identifier + p = icalproperty_new_prodid(CalFormat::productId().utf8()); + icalcomponent_add_property(calendar,p); + + // TODO: Add time zone + + // iCalendar version (2.0) + p = icalproperty_new_version(const_cast<char *>(_ICAL_VERSION)); + icalcomponent_add_property(calendar,p); + + // Custom properties + if( cal != 0 ) + writeCustomProperties(calendar, cal); + + return calendar; +} + + + +// 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( Calendar *cal, icalcomponent *calendar) +{ + // this function will populate the caldict dictionary and other event + // lists. It turns vevents into Events and then inserts them. + + if (!calendar) return false; + +// TODO: check for METHOD +#if 0 + if ((curVO = isAPropertyOf(vcal, ICMethodProp)) != 0) { + char *methodType = 0; + methodType = fakeCString(vObjectUStringZValue(curVO)); + if (mEnableDialogs) + KMessageBox::information(mTopWidget, + i18n("This calendar is an iTIP transaction of type \"%1\".") + .arg(methodType), + i18n("%1: iTIP Transaction").arg(CalFormat::application())); + delete methodType; + } +#endif + + icalproperty *p; + + p = icalcomponent_get_first_property(calendar,ICAL_PRODID_PROPERTY); + if (!p) { +// TODO: does no PRODID really matter? +// mParent->setException(new ErrorFormat(ErrorFormat::CalVersionUnknown)); +// return false; + mLoadedProductId = ""; + mCalendarVersion = 0; + } else { + mLoadedProductId = QString::fromUtf8(icalproperty_get_prodid(p)); + mCalendarVersion = CalFormat::calendarVersion(mLoadedProductId); + + delete mCompat; + mCompat = CompatFactory::createCompat( mLoadedProductId ); + } + +// TODO: check for unknown PRODID +#if 0 + if (!mCalendarVersion + && CalFormat::productId() != mLoadedProductId) { + // warn the user that we might have trouble reading non-known calendar. + if (mEnableDialogs) + KMessageBox::information(mTopWidget, + i18n("This vCalendar file was not created by KOrganizer " + "or any other product we support. Loading anyway..."), + i18n("%1: Unknown vCalendar Vendor").arg(CalFormat::application())); + } +#endif + + p = icalcomponent_get_first_property(calendar,ICAL_VERSION_PROPERTY); + if (!p) { + mParent->setException(new ErrorFormat(ErrorFormat::CalVersionUnknown)); + return false; + } else { + const char *version = icalproperty_get_version(p); + + if (strcmp(version,"1.0") == 0) { + mParent->setException(new ErrorFormat(ErrorFormat::CalVersion1, + i18n("Expected iCalendar format"))); + return false; + } else if (strcmp(version,"2.0") != 0) { + mParent->setException(new ErrorFormat(ErrorFormat::CalVersionUnknown)); + return false; + } + } + + +// TODO: check for calendar format version +#if 0 + // warn the user we might have trouble reading this unknown version. + if ((curVO = isAPropertyOf(vcal, VCVersionProp)) != 0) { + char *s = fakeCString(vObjectUStringZValue(curVO)); + if (strcmp(_VCAL_VERSION, s) != 0) + if (mEnableDialogs) + KMessageBox::sorry(mTopWidget, + i18n("This vCalendar file has version %1.\n" + "We only support %2.") + .arg(s).arg(_VCAL_VERSION), + i18n("%1: Unknown vCalendar Version").arg(CalFormat::application())); + deleteStr(s); + } +#endif + + // custom properties + readCustomProperties(calendar, cal); + +// TODO: set time zone +#if 0 + // set the time zone + if ((curVO = isAPropertyOf(vcal, VCTimeZoneProp)) != 0) { + char *s = fakeCString(vObjectUStringZValue(curVO)); + cal->setTimeZone(s); + deleteStr(s); + } +#endif + + // Store all events with a relatedTo property in a list for post-processing + mEventsRelate.clear(); + mTodosRelate.clear(); + // TODO: make sure that only actually added ecvens go to this lists. + + icalcomponent *c; + + // Iterate through all todos + c = icalcomponent_get_first_component(calendar,ICAL_VTODO_COMPONENT); + while (c) { +// kdDebug(5800) << "----Todo found" << endl; + Todo *todo = readTodo(c); + if (!cal->todo(todo->uid())) cal->addTodo(todo); + c = icalcomponent_get_next_component(calendar,ICAL_VTODO_COMPONENT); + } + + // Iterate through all events + c = icalcomponent_get_first_component(calendar,ICAL_VEVENT_COMPONENT); + while (c) { +// kdDebug(5800) << "----Event found" << endl; + Event *event = readEvent(c); + if (!cal->event(event->uid())) cal->addEvent(event); + c = icalcomponent_get_next_component(calendar,ICAL_VEVENT_COMPONENT); + } + + // Iterate through all journals + c = icalcomponent_get_first_component(calendar,ICAL_VJOURNAL_COMPONENT); + while (c) { +// kdDebug(5800) << "----Journal found" << endl; + Journal *journal = readJournal(c); + if (!cal->journal(journal->uid())) cal->addJournal(journal); + c = icalcomponent_get_next_component(calendar,ICAL_VJOURNAL_COMPONENT); + } + +#if 0 + 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 ((curVOProp = isAPropertyOf(curVO, KPilotStatusProp)) != 0) { + char *s; + s = fakeCString(vObjectUStringZValue(curVOProp)); + // check to see if event was deleted by the kpilot conduit + if (atoi(s) == Event::SYNCDEL) { + deleteStr(s); + goto SKIP; + } + deleteStr(s); + } + + // this code checks to see if we are trying to read in an event + // that we already find to be in the calendar. If we find this + // to be the case, we skip the event. + if ((curVOProp = isAPropertyOf(curVO, VCUniqueStringProp)) != 0) { + char *s = fakeCString(vObjectUStringZValue(curVOProp)); + QString tmpStr(s); + deleteStr(s); + + if (cal->event(tmpStr)) { + goto SKIP; + } + if (cal->todo(tmpStr)) { + goto SKIP; + } + } + + if ((!(curVOProp = isAPropertyOf(curVO, VCDTstartProp))) && + (!(curVOProp = isAPropertyOf(curVO, VCDTendProp)))) { + kdDebug(5800) << "found a VEvent with no DTSTART and no DTEND! Skipping..." << endl; + goto SKIP; + } + + anEvent = VEventToEvent(curVO); + // we now use addEvent instead of insertEvent so that the + // signal/slot get connected. + if (anEvent) + cal->addEvent(anEvent); + else { + // some sort of error must have occurred while in translation. + goto SKIP; + } + } else if (strcmp(vObjectName(curVO), VCTodoProp) == 0) { + anEvent = VTodoToEvent(curVO); + cal->addTodo(anEvent); + } 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 { + ; + } + SKIP: + ; + } // while +#endif + + // Post-Process list of events with relations, put Event objects in relation + Event *ev; + for ( ev=mEventsRelate.first(); ev != 0; ev=mEventsRelate.next() ) { + ev->setRelatedTo(cal->event(ev->relatedToUid())); + } + Todo *todo; + for ( todo=mTodosRelate.first(); todo != 0; todo=mTodosRelate.next() ) { + todo->setRelatedTo(cal->todo(todo->relatedToUid())); + } + + return true; +} + +QString ICalFormatImpl::extractErrorProperty(icalcomponent *c) +{ +// kdDebug(5800) << "ICalFormatImpl:extractErrorProperty: " +// << icalcomponent_as_ical_string(c) << endl; + + QString errorMessage; + + icalproperty *error; + error = icalcomponent_get_first_property(c,ICAL_XLICERROR_PROPERTY); + while(error) { + errorMessage += icalproperty_get_xlicerror(error); + errorMessage += "\n"; + error = icalcomponent_get_next_property(c,ICAL_XLICERROR_PROPERTY); + } + +// kdDebug(5800) << "ICalFormatImpl:extractErrorProperty: " << errorMessage << endl; + + return errorMessage; +} + +void ICalFormatImpl::dumpIcalRecurrence(icalrecurrencetype r) +{ + int i; + + + 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) + " "); + } + } + 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) + " "); + } + } + 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) + " "); + } + } + 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) + " "); + } + } + 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) { + out.append(QString::number(i) + " "); + } + } +} + +icalcomponent *ICalFormatImpl::createScheduleComponent(IncidenceBase *incidence, + Scheduler::Method method) +{ + icalcomponent *message = createCalendarComponent(); + + icalproperty_method icalmethod = ICAL_METHOD_NONE; + + switch (method) { + case Scheduler::Publish: + icalmethod = ICAL_METHOD_PUBLISH; + break; + case Scheduler::Request: + icalmethod = ICAL_METHOD_REQUEST; + break; + case Scheduler::Refresh: + icalmethod = ICAL_METHOD_REFRESH; + break; + case Scheduler::Cancel: + icalmethod = ICAL_METHOD_CANCEL; + break; + case Scheduler::Add: + icalmethod = ICAL_METHOD_ADD; + break; + case Scheduler::Reply: + icalmethod = ICAL_METHOD_REPLY; + break; + case Scheduler::Counter: + icalmethod = ICAL_METHOD_COUNTER; + break; + case Scheduler::Declinecounter: + icalmethod = ICAL_METHOD_DECLINECOUNTER; + break; + default: + + return message; + } + + icalcomponent_add_property(message,icalproperty_new_method(icalmethod)); + + // TODO: check, if dynamic cast is required + if(incidence->type() == "Todo") { + Todo *todo = static_cast<Todo *>(incidence); + icalcomponent_add_component(message,writeTodo(todo)); + } + if(incidence->type() == "Event") { + Event *event = static_cast<Event *>(incidence); + icalcomponent_add_component(message,writeEvent(event)); + } + if(incidence->type() == "FreeBusy") { + FreeBusy *freebusy = static_cast<FreeBusy *>(incidence); + icalcomponent_add_component(message,writeFreeBusy(freebusy, method)); + } + + return message; +} diff --git a/libkcal/icalformatimpl.h b/libkcal/icalformatimpl.h new file mode 100644 index 0000000..2f32365 --- a/dev/null +++ b/libkcal/icalformatimpl.h @@ -0,0 +1,109 @@ +/* + This file is part of libkcal. + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef ICALFORMATIMPL_H +#define ICALFORMATIMPL_H + +#include <qstring.h> + +#include "scheduler.h" +#include "freebusy.h" + +extern "C" { + #include <ical.h> + #include <icalss.h> +} + +namespace KCal { + +class Compat; + +/** + This class provides the libical dependent functions for ICalFormat. +*/ +class ICalFormatImpl { + public: + /** Create new iCal format for calendar object */ + ICalFormatImpl( ICalFormat *parent ); + virtual ~ICalFormatImpl(); + + bool populate( Calendar *, icalcomponent *fs); + + icalcomponent *writeIncidence(Incidence *incidence); + icalcomponent *writeTodo(Todo *todo); + icalcomponent *writeEvent(Event *event); + icalcomponent *writeFreeBusy(FreeBusy *freebusy, + Scheduler::Method method); + icalcomponent *writeJournal(Journal *journal); + void writeIncidence(icalcomponent *parent,Incidence *incidence); + icalproperty *writeAttendee(Attendee *attendee); + icalproperty *writeAttachment(Attachment *attach); + icalproperty *writeRecurrenceRule(Recurrence *); + icalproperty *writeAlarm(Alarm *alarm); + + QString extractErrorProperty(icalcomponent *); + Todo *readTodo(icalcomponent *vtodo); + Event *readEvent(icalcomponent *vevent); + FreeBusy *readFreeBusy(icalcomponent *vfreebusy); + Journal *readJournal(icalcomponent *vjournal); + Attendee *readAttendee(icalproperty *attendee); + Attachment *readAttachment(icalproperty *attach); + void readIncidence(icalcomponent *parent,Incidence *incidence); + void readRecurrenceRule(struct icalrecurrencetype rrule,Incidence *event); + void readRecurrence( const struct icalrecurrencetype &r, Recurrence* recur,Incidence *event ); + void readAlarm(icalcomponent *alarm,Incidence *incidence); + /** Return the PRODID string loaded from calendar file */ + const QString &loadedProductId() { return mLoadedProductId; } + + icaltimetype writeICalDate(const QDate &); + QDate readICalDate(icaltimetype); + icaltimetype writeICalDateTime(const QDateTime &); + QDateTime readICalDateTime(icaltimetype); + icaldurationtype writeICalDuration(int seconds); + int readICalDuration(icaldurationtype); + icalcomponent *createCalendarComponent(Calendar * = 0); + icalcomponent *createScheduleComponent(IncidenceBase *,Scheduler::Method); + + private: + void writeIncidenceBase(icalcomponent *parent,IncidenceBase *); + void readIncidenceBase(icalcomponent *parent,IncidenceBase *); + void writeCustomProperties(icalcomponent *parent,CustomProperties *); + void readCustomProperties(icalcomponent *parent,CustomProperties *); + void dumpIcalRecurrence(icalrecurrencetype); + + ICalFormat *mParent; + Calendar *mCalendar; + + QString mLoadedProductId; // PRODID string loaded from calendar file + int mCalendarVersion; // determines backward compatibility mode on read + + QPtrList<Event> mEventsRelate; // events with relations + QPtrList<Todo> mTodosRelate; // todos with relations + + static const int mSecondsPerWeek; + static const int mSecondsPerDay; + static const int mSecondsPerHour; + static const int mSecondsPerMinute; + + Compat *mCompat; +}; + +} + +#endif diff --git a/libkcal/icalformatimpl.h.bup b/libkcal/icalformatimpl.h.bup new file mode 100644 index 0000000..37cf857 --- a/dev/null +++ b/libkcal/icalformatimpl.h.bup @@ -0,0 +1,109 @@ +/* + This file is part of libkcal. + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef ICALFORMATIMPL_H +#define ICALFORMATIMPL_H + +#include <qstring.h> + +#include "scheduler.h" +#include "freebusy.h" + +extern "C" { + #include <ical.h> + #include <icalss.h> +} + +namespace KCal { + +class Compat; + +/** + This class provides the libical dependent functions for ICalFormat. +*/ +class ICalFormatImpl { + public: + /** Create new iCal format for calendar object */ + ICalFormatImpl( ICalFormat *parent ); + virtual ~ICalFormatImpl(); + + bool populate( Calendar *, icalcomponent *fs); + + icalcomponent *writeIncidence(Incidence *incidence); + icalcomponent *writeTodo(Todo *todo); + icalcomponent *writeEvent(Event *event); + icalcomponent *writeFreeBusy(FreeBusy *freebusy, + Scheduler::Method method); + icalcomponent *writeJournal(Journal *journal); + void writeIncidence(icalcomponent *parent,Incidence *incidence); + icalproperty *writeAttendee(Attendee *attendee); + icalproperty *writeAttachment(Attachment *attach); + icalproperty *writeRecurrenceRule(Recurrence *); + icalproperty *writeAlarm(Alarm *alarm); + + QString extractErrorProperty(icalcomponent *); + Todo *readTodo(icalcomponent *vtodo); + Event *readEvent(icalcomponent *vevent); + FreeBusy *readFreeBusy(icalcomponent *vfreebusy); + Journal *readJournal(icalcomponent *vjournal); + Attendee *readAttendee(icalproperty *attendee); + Attachment *readAttachment(icalproperty *attach); + void readIncidence(icalcomponent *parent,Incidence *incidence); + void readRecurrenceRule(icalproperty *rrule,Incidence *event); + void readRecurrence( const struct icalrecurrencetype &r, Recurrence* recur ); + void readAlarm(icalcomponent *alarm,Incidence *incidence); + /** Return the PRODID string loaded from calendar file */ + const QString &loadedProductId() { return mLoadedProductId; } + + icaltimetype writeICalDate(const QDate &); + QDate readICalDate(icaltimetype); + icaltimetype writeICalDateTime(const QDateTime &); + QDateTime readICalDateTime(icaltimetype); + icaldurationtype writeICalDuration(int seconds); + int readICalDuration(icaldurationtype); + icalcomponent *createCalendarComponent(Calendar * = 0); + icalcomponent *createScheduleComponent(IncidenceBase *,Scheduler::Method); + + private: + void writeIncidenceBase(icalcomponent *parent,IncidenceBase *); + void readIncidenceBase(icalcomponent *parent,IncidenceBase *); + void writeCustomProperties(icalcomponent *parent,CustomProperties *); + void readCustomProperties(icalcomponent *parent,CustomProperties *); + void dumpIcalRecurrence(icalrecurrencetype); + + ICalFormat *mParent; + Calendar *mCalendar; + + QString mLoadedProductId; // PRODID string loaded from calendar file + int mCalendarVersion; // determines backward compatibility mode on read + + QPtrList<Event> mEventsRelate; // events with relations + QPtrList<Todo> mTodosRelate; // todos with relations + + static const int mSecondsPerWeek; + static const int mSecondsPerDay; + static const int mSecondsPerHour; + static const int mSecondsPerMinute; + + Compat *mCompat; +}; + +} + +#endif diff --git a/libkcal/imipscheduler.cpp b/libkcal/imipscheduler.cpp new file mode 100644 index 0000000..e186f8e --- a/dev/null +++ b/libkcal/imipscheduler.cpp @@ -0,0 +1,58 @@ +/* + This file is part of libkcal. + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +// +// IMIPScheduler - iMIP implementation of iTIP methods +// + +#include "event.h" +#include "icalformat.h" + +#include "imipscheduler.h" + +using namespace KCal; + +IMIPScheduler::IMIPScheduler(Calendar *calendar) + : Scheduler(calendar) +{ +} + +IMIPScheduler::~IMIPScheduler() +{ +} + +bool IMIPScheduler::publish (IncidenceBase *incidence,const QString &recipients) +{ + return false; +} + +bool IMIPScheduler::performTransaction(IncidenceBase *incidence,Method method) +{ + mFormat->createScheduleMessage(incidence,method); + + return false; +} + +QPtrList<ScheduleMessage> IMIPScheduler::retrieveTransactions() +{ + QPtrList<ScheduleMessage> messageList; + + return messageList; +} diff --git a/libkcal/imipscheduler.h b/libkcal/imipscheduler.h new file mode 100644 index 0000000..f142060 --- a/dev/null +++ b/libkcal/imipscheduler.h @@ -0,0 +1,49 @@ +/* + This file is part of libkcal. + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef IMIPSCHEDULER_H +#define IMIPSCHEDULER_H +// +// iMIP implementation of iTIP methods +// + +#include <qptrlist.h> + +#include "scheduler.h" + +namespace KCal { + +/* + This class implements the iTIP interface using the email interface specified + as iMIP. +*/ +class IMIPScheduler : public Scheduler { + public: + IMIPScheduler(Calendar *); + virtual ~IMIPScheduler(); + + bool publish (IncidenceBase *incidence,const QString &recipients); + bool performTransaction(IncidenceBase *incidence,Method method); + QPtrList<ScheduleMessage> retrieveTransactions(); +}; + +} + +#endif // IMIPSCHEDULER_H + diff --git a/libkcal/incidence.cpp b/libkcal/incidence.cpp new file mode 100644 index 0000000..d9bda64 --- a/dev/null +++ b/libkcal/incidence.cpp @@ -0,0 +1,594 @@ +/* + This file is part of libkcal. + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include <kglobal.h> +#include <klocale.h> +#include <kdebug.h> + +#include "calformat.h" + +#include "incidence.h" +#include "todo.h" + +using namespace KCal; + +Incidence::Incidence() : + IncidenceBase(), + mRelatedTo(0), mSecrecy(SecrecyPublic), mPriority(3) +{ + mRecurrence = new Recurrence(this); + mCancelled = false; + recreate(); + mHasStartDate = true; + mAlarms.setAutoDelete(true); + mAttachments.setAutoDelete(true); +} + +Incidence::Incidence( const Incidence &i ) : IncidenceBase( i ) +{ +// TODO: reenable attributes currently commented out. + mRevision = i.mRevision; + mCreated = i.mCreated; + mDescription = i.mDescription; + mSummary = i.mSummary; + mCategories = i.mCategories; +// Incidence *mRelatedTo; Incidence *mRelatedTo; + mRelatedTo = 0; + mRelatedToUid = i.mRelatedToUid; +// QPtrList<Incidence> mRelations; QPtrList<Incidence> mRelations; + mExDates = i.mExDates; + mAttachments = i.mAttachments; + mResources = i.mResources; + mSecrecy = i.mSecrecy; + mPriority = i.mPriority; + mLocation = i.mLocation; + mCancelled = i.mCancelled; + mHasStartDate = i.mHasStartDate; + QPtrListIterator<Alarm> it( i.mAlarms ); + const Alarm *a; + while( (a = it.current()) ) { + Alarm *b = new Alarm( *a ); + b->setParent( this ); + mAlarms.append( b ); + + ++it; + } + mAlarms.setAutoDelete(true); + + mRecurrence = new Recurrence( *(i.mRecurrence), this ); +} + +Incidence::~Incidence() +{ + + Incidence *ev; + QPtrList<Incidence> Relations = relations(); + for (ev=Relations.first();ev;ev=Relations.next()) { + if (ev->relatedTo() == this) ev->setRelatedTo(0); + } + if (relatedTo()) relatedTo()->removeRelation(this); + delete mRecurrence; + +} + +bool Incidence::cancelled() const +{ + return mCancelled; +} +void Incidence::setCancelled( bool b ) +{ + mCancelled = b; + updated(); +} +bool Incidence::hasStartDate() const +{ + return mHasStartDate; +} + +void Incidence::setHasStartDate(bool f) +{ + if (mReadOnly) return; + mHasStartDate = f; + updated(); +} + +// A string comparison that considers that null and empty are the same +static bool stringCompare( const QString& s1, const QString& s2 ) +{ + if ( s1.isEmpty() && s2.isEmpty() ) + return true; + return s1 == s2; +} + +bool KCal::operator==( const Incidence& i1, const Incidence& i2 ) +{ + + if( i1.alarms().count() != i2.alarms().count() ) { + return false; // no need to check further + } + if ( i1.alarms().count() > 0 ) { + if ( !( *(i1.alarms().first()) == *(i2.alarms().first())) ) + { + qDebug("alarm not equal "); + return false; + } + } +#if 0 + QPtrListIterator<Alarm> a1( i1.alarms() ); + QPtrListIterator<Alarm> a2( i2.alarms() ); + for( ; a1.current() && a2.current(); ++a1, ++a2 ) { + if( *a1.current() == *a2.current() ) { + continue; + } + else { + return false; + } + } +#endif + + if ( ! operator==( (const IncidenceBase&)i1, (const IncidenceBase&)i2 ) ) + return false; + if ( i1.hasStartDate() == i2.hasStartDate() ) { + if ( i1.hasStartDate() ) { + if ( i1.dtStart() != i2.dtStart() ) + return false; + } + } else { + return false; + } + if (!( *i1.recurrence() == *i2.recurrence()) ) { + qDebug("recurrence is NOT equal "); + return false; + } + return + // i1.created() == i2.created() && + stringCompare( i1.description(), i2.description() ) && + stringCompare( i1.summary(), i2.summary() ) && + i1.categories() == i2.categories() && + // no need to compare mRelatedTo + stringCompare( i1.relatedToUid(), i2.relatedToUid() ) && + // i1.relations() == i2.relations() && + i1.exDates() == i2.exDates() && + i1.attachments() == i2.attachments() && + i1.resources() == i2.resources() && + i1.secrecy() == i2.secrecy() && + i1.priority() == i2.priority() && + stringCompare( i1.location(), i2.location() ); +} + + +void Incidence::recreate() +{ + setCreated(QDateTime::currentDateTime()); + + setUid(CalFormat::createUniqueId()); + + setRevision(0); + + setLastModified(QDateTime::currentDateTime()); +} + +void Incidence::setReadOnly( bool readOnly ) +{ + IncidenceBase::setReadOnly( readOnly ); + recurrence()->setRecurReadOnly( readOnly); +} + +void Incidence::setCreated(QDateTime created) +{ + if (mReadOnly) return; + mCreated = getEvenTime(created); +} + +QDateTime Incidence::created() const +{ + return mCreated; +} + +void Incidence::setRevision(int rev) +{ + if (mReadOnly) return; + mRevision = rev; + + updated(); +} + +int Incidence::revision() const +{ + return mRevision; +} + +void Incidence::setDtStart(const QDateTime &dtStart) +{ + + QDateTime dt = getEvenTime(dtStart); + recurrence()->setRecurStart( dt); + IncidenceBase::setDtStart( dt ); +} + +void Incidence::setDescription(const QString &description) +{ + if (mReadOnly) return; + mDescription = description; + updated(); +} + +QString Incidence::description() const +{ + return mDescription; +} + + +void Incidence::setSummary(const QString &summary) +{ + if (mReadOnly) return; + mSummary = summary; + updated(); +} + +QString Incidence::summary() const +{ + return mSummary; +} + +void Incidence::setCategories(const QStringList &categories) +{ + if (mReadOnly) return; + mCategories = categories; + updated(); +} + +// TODO: remove setCategories(QString) function +void Incidence::setCategories(const QString &catStr) +{ + if (mReadOnly) return; + mCategories.clear(); + + if (catStr.isEmpty()) return; + + mCategories = QStringList::split(",",catStr); + + QStringList::Iterator it; + for(it = mCategories.begin();it != mCategories.end(); ++it) { + *it = (*it).stripWhiteSpace(); + } + + updated(); +} + +QStringList Incidence::categories() const +{ + return mCategories; +} + +QString Incidence::categoriesStr() +{ + return mCategories.join(","); +} + +void Incidence::setRelatedToUid(const QString &relatedToUid) +{ + if (mReadOnly) return; + mRelatedToUid = relatedToUid; +} + +QString Incidence::relatedToUid() const +{ + return mRelatedToUid; +} + +void Incidence::setRelatedTo(Incidence *relatedTo) +{ + //qDebug("Incidence::setRelatedTo %d ", relatedTo); + //qDebug("setRelatedTo(Incidence *relatedTo) %s %s", summary().latin1(), relatedTo->summary().latin1() ); + if (mReadOnly || mRelatedTo == relatedTo) return; + if(mRelatedTo) { + // updated(); + mRelatedTo->removeRelation(this); + } + mRelatedTo = relatedTo; + if (mRelatedTo) mRelatedTo->addRelation(this); +} + +Incidence *Incidence::relatedTo() const +{ + return mRelatedTo; +} + +QPtrList<Incidence> Incidence::relations() const +{ + return mRelations; +} + +void Incidence::addRelation(Incidence *event) +{ + if( mRelations.findRef( event ) == -1 ) { + mRelations.append(event); + //updated(); + } +} + +void Incidence::removeRelation(Incidence *event) +{ + + mRelations.removeRef(event); + +// if (event->getRelatedTo() == this) event->setRelatedTo(0); +} + +bool Incidence::recursOn(const QDate &qd) const +{ + if (recurrence()->recursOnPure(qd) && !isException(qd)) return true; + else return false; +} + +void Incidence::setExDates(const DateList &exDates) +{ + if (mReadOnly) return; + mExDates = exDates; + + recurrence()->setRecurExDatesCount(mExDates.count()); + + updated(); +} + +void Incidence::addExDate(const QDate &date) +{ + if (mReadOnly) return; + mExDates.append(date); + + recurrence()->setRecurExDatesCount(mExDates.count()); + + updated(); +} + +DateList Incidence::exDates() const +{ + return mExDates; +} + +bool Incidence::isException(const QDate &date) const +{ + DateList::ConstIterator it; + for( it = mExDates.begin(); it != mExDates.end(); ++it ) { + if ( (*it) == date ) { + return true; + } + } + + return false; +} + +void Incidence::addAttachment(Attachment *attachment) +{ + if (mReadOnly || !attachment) return; + mAttachments.append(attachment); + updated(); +} + +void Incidence::deleteAttachment(Attachment *attachment) +{ + mAttachments.removeRef(attachment); +} + +void Incidence::deleteAttachments(const QString& mime) +{ + Attachment *at = mAttachments.first(); + while (at) { + if (at->mimeType() == mime) + mAttachments.remove(); + else + at = mAttachments.next(); + } +} + +QPtrList<Attachment> Incidence::attachments() const +{ + return mAttachments; +} + +QPtrList<Attachment> Incidence::attachments(const QString& mime) const +{ + QPtrList<Attachment> attachments; + QPtrListIterator<Attachment> it( mAttachments ); + Attachment *at; + while ( (at = it.current()) ) { + if (at->mimeType() == mime) + attachments.append(at); + ++it; + } + + return attachments; +} + +void Incidence::setResources(const QStringList &resources) +{ + if (mReadOnly) return; + mResources = resources; + updated(); +} + +QStringList Incidence::resources() const +{ + return mResources; +} + + +void Incidence::setPriority(int priority) +{ + if (mReadOnly) return; + mPriority = priority; + updated(); +} + +int Incidence::priority() const +{ + return mPriority; +} + +void Incidence::setSecrecy(int sec) +{ + if (mReadOnly) return; + mSecrecy = sec; + updated(); +} + +int Incidence::secrecy() const +{ + return mSecrecy; +} + +QString Incidence::secrecyStr() const +{ + return secrecyName(mSecrecy); +} + +QString Incidence::secrecyName(int secrecy) +{ + switch (secrecy) { + case SecrecyPublic: + return i18n("Public"); + break; + case SecrecyPrivate: + return i18n("Private"); + break; + case SecrecyConfidential: + return i18n("Confidential"); + break; + default: + return i18n("Undefined"); + break; + } +} + +QStringList Incidence::secrecyList() +{ + QStringList list; + list << secrecyName(SecrecyPublic); + list << secrecyName(SecrecyPrivate); + list << secrecyName(SecrecyConfidential); + + return list; +} + + +QPtrList<Alarm> Incidence::alarms() const +{ + return mAlarms; +} + +Alarm* Incidence::newAlarm() +{ + Alarm* alarm = new Alarm(this); + mAlarms.append(alarm); +// updated(); + return alarm; +} + +void Incidence::addAlarm(Alarm *alarm) +{ + mAlarms.append(alarm); + updated(); +} + +void Incidence::removeAlarm(Alarm *alarm) +{ + mAlarms.removeRef(alarm); + updated(); +} + +void Incidence::clearAlarms() +{ + mAlarms.clear(); + updated(); +} + +bool Incidence::isAlarmEnabled() const +{ + Alarm* alarm; + for (QPtrListIterator<Alarm> it(mAlarms); (alarm = it.current()) != 0; ++it) { + if (alarm->enabled()) + return true; + } + return false; +} + +Recurrence *Incidence::recurrence() const +{ + return mRecurrence; +} + +void Incidence::setLocation(const QString &location) +{ + if (mReadOnly) return; + mLocation = location; + updated(); +} + +QString Incidence::location() const +{ + return mLocation; +} + +ushort Incidence::doesRecur() const +{ + if ( mRecurrence ) return mRecurrence->doesRecur(); + else return Recurrence::rNone; +} + +QDateTime Incidence::getNextOccurence( const QDateTime& dt, bool* ok ) const +{ + QDateTime incidenceStart = dt; + *ok = false; + if ( doesRecur() ) { + bool last; + recurrence()->getPreviousDateTime( incidenceStart , &last ); + int count = 0; + if ( !last ) { + while ( !last ) { + ++count; + incidenceStart = recurrence()->getNextDateTime( incidenceStart, &last ); + if ( recursOn( incidenceStart.date() ) ) { + last = true; // exit while llop + } else { + if ( last ) { // no alarm on last recurrence + return QDateTime (); + } + int year = incidenceStart.date().year(); + // workaround for bug in recurrence + if ( count == 100 || year < 1980 || year > 5000 ) { + return QDateTime (); + } + incidenceStart = incidenceStart.addSecs( 1 ); + } + } + } else { + return QDateTime (); + } + } else { + if ( hasStartDate () ) { + incidenceStart = dtStart(); + + } + } + if ( incidenceStart > dt ) + *ok = true; + return incidenceStart; +} diff --git a/libkcal/incidence.h b/libkcal/incidence.h new file mode 100644 index 0000000..d1972cb --- a/dev/null +++ b/libkcal/incidence.h @@ -0,0 +1,298 @@ +/* + This file is part of libkcal. + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef INCIDENCE_H +#define INCIDENCE_H +// +// Incidence - base class of calendaring components +// + +#include <qdatetime.h> +#include <qstringlist.h> +#include <qvaluelist.h> + +#include "recurrence.h" +#include "alarm.h" +#include "attachment.h" +#include "listbase.h" +#include "incidencebase.h" + +namespace KCal { + +class Event; +class Todo; +class Journal; + +/** + This class provides the base class common to all calendar components. +*/ +class Incidence : public IncidenceBase +{ + public: + /** + 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 Visitor + { + public: + /** Destruct Incidence::Visitor */ + virtual ~Visitor() {} + + /** + Reimplement this function in your concrete subclass of IncidenceVisitor to perform actions + on an Event object. + */ + virtual bool visit(Event *) { return false; } + /** + Reimplement this function in your concrete subclass of IncidenceVisitor to perform actions + on an Todo object. + */ + virtual bool visit(Todo *) { return false; } + /** + Reimplement this function in your concrete subclass of IncidenceVisitor to perform actions + on an Journal object. + */ + virtual bool visit(Journal *) { return false; } + + protected: + /** Constructor is protected to prevent direct creation of visitor base class. */ + Visitor() {} + }; + + /** + This class implements a visitor for adding an Incidence to a resource + supporting addEvent(), addTodo() and addJournal() calls. + */ + template<class T> + class AddVisitor : public Visitor + { + public: + AddVisitor( T *r ) : mResource( r ) {} + bool visit( Event *e ) { return mResource->addEvent( e ); } + bool visit( Todo *t ) { return mResource->addTodo( t ); } + bool visit( Journal *j ) { return mResource->addJournal( j ); } + + private: + T *mResource; + }; + + /** enumeration for describing an event's secrecy. */ + enum { SecrecyPublic = 0, SecrecyPrivate = 1, SecrecyConfidential = 2 }; + typedef ListBase<Incidence> List; + Incidence(); + Incidence(const Incidence &); + ~Incidence(); + + /** + Accept IncidenceVisitor. A class taking part in the visitor mechanism has to + provide this implementation: + <pre> + bool accept(Visitor &v) { return v.visit(this); } + </pre> + */ + virtual bool accept(Visitor &) { return false; } + + virtual Incidence *clone() = 0; + + virtual QDateTime getNextAlarmDateTime( bool * ok, int * offset ) const = 0; + void setReadOnly( bool ); + + /** + Recreate event. The event is made a new unique event, but already stored + event information is preserved. Sets uniquie id, creation date, last + modification date and revision number. + */ + void recreate(); + + /** set creation date */ + void setCreated(QDateTime); + /** return time and date of creation. */ + QDateTime created() const; + + /** set the number of revisions this event has seen */ + void setRevision(int rev); + /** return the number of revisions this event has seen */ + int revision() const; + + /** Set starting date/time. */ + virtual void setDtStart(const QDateTime &dtStart); + /** Return the incidence's ending date/time as a QDateTime. */ + virtual QDateTime dtEnd() const { return QDateTime(); } + + /** sets the event's lengthy description. */ + void setDescription(const QString &description); + /** returns a reference to the event's description. */ + QString description() const; + + /** sets the event's short summary. */ + void setSummary(const QString &summary); + /** returns a reference to the event's summary. */ + QString summary() const; + + /** set event's applicable categories */ + void setCategories(const QStringList &categories); + /** set event's categories based on a comma delimited string */ + void setCategories(const QString &catStr); + /** return categories in a list */ + QStringList categories() const; + /** return categories as a comma separated string */ + QString categoriesStr(); + + /** point at some other event to which the event relates. This function should + * only be used when constructing a calendar before the related Event + * exists. */ + void setRelatedToUid(const QString &); + /** what event does this one relate to? This function should + * only be used when constructing a calendar before the related Event + * exists. */ + QString relatedToUid() const; + /** point at some other event to which the event relates */ + void setRelatedTo(Incidence *relatedTo); + /** what event does this one relate to? */ + Incidence *relatedTo() const; + /** All events that are related to this event */ + QPtrList<Incidence> relations() const; + /** Add an event which is related to this event */ + void addRelation(Incidence *); + /** Remove event that is related to this event */ + void removeRelation(Incidence *); + + /** returns the list of dates which are exceptions to the recurrence rule */ + DateList exDates() const; + /** sets the list of dates which are exceptions to the recurrence rule */ + void setExDates(const DateList &_exDates); + void setExDates(const char *dates); + /** Add a date to the list of exceptions of the recurrence rule. */ + void addExDate(const QDate &date); + + /** returns true if there is an exception for this date in the recurrence + rule set, or false otherwise. */ + bool isException(const QDate &qd) const; + + /** add attachment to this event */ + void addAttachment(Attachment *attachment); + /** remove and delete a specific attachment */ + void deleteAttachment(Attachment *attachment); + /** remove and delete all attachments with this mime type */ + void deleteAttachments(const QString& mime); + /** return list of all associated attachments */ + QPtrList<Attachment> attachments() const; + /** find a list of attachments with this mime type */ + QPtrList<Attachment> attachments(const QString& mime) const; + + /** sets the event's status the value specified. See the enumeration + * above for possible values. */ + void setSecrecy(int); + /** return the event's secrecy. */ + int secrecy() const; + /** return the event's secrecy in string format. */ + QString secrecyStr() const; + /** return list of all availbale secrecy classes */ + static QStringList secrecyList(); + /** return human-readable name of secrecy class */ + static QString secrecyName(int); + + /** returns TRUE if the date specified is one on which the event will + * recur. */ + bool recursOn(const QDate &qd) const; + + // VEVENT and VTODO, but not VJOURNAL (move to EventBase class?): + + /** set resources used, such as Office, Car, etc. */ + void setResources(const QStringList &resources); + /** return list of current resources */ + QStringList resources() const; + + /** set the event's priority, 0 is undefined, 1 highest (decreasing order) */ + void setPriority(int priority); + /** get the event's priority */ + int priority() const; + + /** All alarms that are associated with this incidence */ + QPtrList<Alarm> alarms() const; + /** Create a new alarm which is associated with this incidence */ + Alarm* newAlarm(); + /** Add an alarm which is associated with this incidence */ + void addAlarm(Alarm*); + /** Remove an alarm that is associated with this incidence */ + void removeAlarm(Alarm*); + /** Remove all alarms that are associated with this incidence */ + void clearAlarms(); + /** return whether any alarm associated with this incidence is enabled */ + bool isAlarmEnabled() const; + + /** + Return the recurrence rule associated with this incidence. If there is + none, returns an appropriate (non-0) object. + */ + Recurrence *recurrence() const; + + /** + Forward to Recurrence::doesRecur(). + */ + ushort doesRecur() const; + + /** set the event's/todo's location. Do _not_ use it with journal */ + void setLocation(const QString &location); + /** return the event's/todo's location. Do _not_ use it with journal */ + QString location() const; + /** returns TRUE or FALSE depending on whether the todo has a start date */ + bool hasStartDate() const; + /** sets the event's hasStartDate value. */ + void setHasStartDate(bool f); + QDateTime getNextOccurence( const QDateTime& dt, bool* yes ) const; + bool cancelled() const; + void setCancelled( bool b ); + +protected: + QPtrList<Alarm> mAlarms; + private: + int mRevision; + bool mCancelled; + + // base components of jounal, event and todo + QDateTime mCreated; + QString mDescription; + QString mSummary; + QStringList mCategories; + Incidence *mRelatedTo; + QString mRelatedToUid; + QPtrList<Incidence> mRelations; + DateList mExDates; + QPtrList<Attachment> mAttachments; + QStringList mResources; + bool mHasStartDate; // if todo has associated start date + + int mSecrecy; + int mPriority; // 1 = highest, 2 = less, etc. + + //QPtrList<Alarm> mAlarms; + Recurrence *mRecurrence; + + QString mLocation; +}; + +bool operator==( const Incidence&, const Incidence& ); + +} + +#endif diff --git a/libkcal/incidencebase.cpp b/libkcal/incidencebase.cpp new file mode 100644 index 0000000..9479048 --- a/dev/null +++ b/libkcal/incidencebase.cpp @@ -0,0 +1,393 @@ +/* + This file is part of libkcal. + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include <kglobal.h> +#include <klocale.h> +#include <kdebug.h> + +#include "calformat.h" + +#include "incidencebase.h" + +using namespace KCal; + +IncidenceBase::IncidenceBase() : + mReadOnly(false), mFloats(true), mDuration(0), mHasDuration(false), + mPilotId(0), mSyncStatus(SYNCMOD) +{ + setUid(CalFormat::createUniqueId()); + mOrganizer = ""; + mFloats = false; + mDuration = 0; + mHasDuration = false; + mPilotId = 0; + mZaurusId = -1; + mZaurusUid = 0; + mZaurusStat = 0; + mSyncStatus = 0; + mAttendees.setAutoDelete( true ); +} + +IncidenceBase::IncidenceBase(const IncidenceBase &i) : + CustomProperties( i ) +{ + mReadOnly = i.mReadOnly; + mDtStart = i.mDtStart; + mDuration = i.mDuration; + mHasDuration = i.mHasDuration; + mOrganizer = i.mOrganizer; + mUid = i.mUid; + QPtrList<Attendee> attendees = i.attendees(); + for( Attendee *a = attendees.first(); a; a = attendees.next() ) { + mAttendees.append( new Attendee( *a ) ); + } + mFloats = i.mFloats; + mLastModified = i.mLastModified; + mPilotId = i.mPilotId; + mZaurusId = i.mZaurusId; + mZaurusUid = i.mZaurusUid; + mZaurusStat = i.mZaurusStat; + mSyncStatus = i.mSyncStatus; + + // The copied object is a new one, so it isn't observed by the observer + // of the original object. + mObservers.clear(); + + mAttendees.setAutoDelete( true ); +} + +IncidenceBase::~IncidenceBase() +{ +} + + +bool KCal::operator==( const IncidenceBase& i1, const IncidenceBase& i2 ) +{ + + if( i1.attendees().count() != i2.attendees().count() ) { + return false; // no need to check further + } + if ( i1.attendees().count() > 0 ) { + Attendee * a1 = i1.attendees().first(), *a2 =i2.attendees().first() ; + while ( a1 ) { + if ( !( (*a1) == (*a2)) ) + { + //qDebug("Attendee not equal "); + return false; + } + a1 = i1.attendees().next(); + a2 = i2.attendees().next(); + } + } + //if ( i1.dtStart() != i2.dtStart() ) + // return false; +#if 0 + qDebug("1 %d ",i1.doesFloat() == i2.doesFloat() ); + qDebug("1 %d ",i1.duration() == i2.duration() ); + qDebug("3 %d ",i1.hasDuration() == i2.hasDuration() ); + qDebug("1 %d ",i1.pilotId() == i2.pilotId() ); + qDebug("1 %d %d %d",i1.syncStatus() == i2.syncStatus() , i1.syncStatus(),i2.syncStatus() ); + qDebug("6 %d ",i1.organizer() == i2.organizer() ); + +#endif + return ( i1.organizer() == i2.organizer() && + // i1.uid() == i2.uid() && + // Don't compare lastModified, otherwise the operator is not + // of much use. We are not comparing for identity, after all. + i1.doesFloat() == i2.doesFloat() && + i1.duration() == i2.duration() && + i1.hasDuration() == i2.hasDuration() && + i1.pilotId() == i2.pilotId() );// && i1.syncStatus() == i2.syncStatus() ); + // no need to compare mObserver +} + + +QDateTime IncidenceBase::getEvenTime( QDateTime dt ) +{ + QTime t = dt.time(); + dt.setTime( QTime (t.hour (), t.minute (), t.second () ) ); + return dt; +} + + +void IncidenceBase::setUid(const QString &uid) +{ + mUid = uid; + updated(); +} + +QString IncidenceBase::uid() const +{ + return mUid; +} + +void IncidenceBase::setLastModified(const QDateTime &lm) +{ + // DON'T! updated() because we call this from + // Calendar::updateEvent(). + mLastModified = getEvenTime(lm); + //qDebug("IncidenceBase::setLastModified %s ",lm.toString().latin1()); +} + +QDateTime IncidenceBase::lastModified() const +{ + return mLastModified; +} + +void IncidenceBase::setOrganizer(const QString &o) +{ + // we don't check for readonly here, because it is + // possible that by setting the organizer we are changing + // the event's readonly status... + mOrganizer = o; + if (mOrganizer.left(7).upper() == "MAILTO:") + mOrganizer = mOrganizer.remove(0,7); + + updated(); +} + +QString IncidenceBase::organizer() const +{ + return mOrganizer; +} + +void IncidenceBase::setReadOnly( bool readOnly ) +{ + mReadOnly = readOnly; +} + +void IncidenceBase::setDtStart(const QDateTime &dtStart) +{ +// if (mReadOnly) return; + mDtStart = getEvenTime(dtStart); + updated(); +} + +QDateTime IncidenceBase::dtStart() const +{ + return mDtStart; +} + +QString IncidenceBase::dtStartTimeStr() const +{ + return KGlobal::locale()->formatTime(dtStart().time()); +} + +QString IncidenceBase::dtStartDateStr(bool shortfmt) const +{ + return KGlobal::locale()->formatDate(dtStart().date(),shortfmt); +} + +QString IncidenceBase::dtStartStr(bool shortfmt) const +{ + return KGlobal::locale()->formatDateTime(dtStart(), shortfmt); +} + + +bool IncidenceBase::doesFloat() const +{ + return mFloats; +} + +void IncidenceBase::setFloats(bool f) +{ + if (mReadOnly) return; + mFloats = f; + updated(); +} + + +void IncidenceBase::addAttendee(Attendee *a, bool doupdate) +{ + if (mReadOnly) return; + if (a->name().left(7).upper() == "MAILTO:") + a->setName(a->name().remove(0,7)); + + mAttendees.append(a); + if (doupdate) updated(); +} + +#if 0 +void IncidenceBase::removeAttendee(Attendee *a) +{ + if (mReadOnly) return; + mAttendees.removeRef(a); + updated(); +} + +void IncidenceBase::removeAttendee(const char *n) +{ + Attendee *a; + + if (mReadOnly) return; + for (a = mAttendees.first(); a; a = mAttendees.next()) + if (a->getName() == n) { + mAttendees.remove(); + break; + } +} +#endif + +void IncidenceBase::clearAttendees() +{ + if (mReadOnly) return; + mAttendees.clear(); +} + +#if 0 +Attendee *IncidenceBase::getAttendee(const char *n) const +{ + QPtrListIterator<Attendee> qli(mAttendees); + + qli.toFirst(); + while (qli) { + if (qli.current()->getName() == n) + return qli.current(); + ++qli; + } + return 0L; +} +#endif + +Attendee *IncidenceBase::attendeeByMail(const QString &email) +{ + QPtrListIterator<Attendee> qli(mAttendees); + + qli.toFirst(); + while (qli) { + if (qli.current()->email() == email) + return qli.current(); + ++qli; + } + return 0L; +} + +Attendee *IncidenceBase::attendeeByMails(const QStringList &emails, const QString& email) +{ + QPtrListIterator<Attendee> qli(mAttendees); + + QStringList mails = emails; + if (!email.isEmpty()) { + mails.append(email); + } + qli.toFirst(); + while (qli) { + for ( QStringList::Iterator it = mails.begin(); it != mails.end(); ++it ) { + if (qli.current()->email() == *it) + return qli.current(); + } + + ++qli; + } + return 0L; +} + +void IncidenceBase::setDuration(int seconds) +{ + mDuration = seconds; + setHasDuration(true); +} + +int IncidenceBase::duration() const +{ + return mDuration; +} + +void IncidenceBase::setHasDuration(bool b) +{ + mHasDuration = b; +} + +bool IncidenceBase::hasDuration() const +{ + return mHasDuration; +} + +void IncidenceBase::setSyncStatus(int stat) +{ + if (mReadOnly) return; + mSyncStatus = stat; +} + +int IncidenceBase::syncStatus() const +{ + return mSyncStatus; +} + +void IncidenceBase::setPilotId( int id ) +{ + if (mReadOnly) return; + mPilotId = id; +} + +int IncidenceBase::pilotId() const +{ + return mPilotId; +} +void IncidenceBase::setZaurusId( int id ) +{ + if (mReadOnly) return; + mZaurusId = id; +} + +int IncidenceBase::zaurusId() const +{ + return mZaurusId; +} + +int IncidenceBase::zaurusUid() const +{ + return mZaurusUid; +} +void IncidenceBase::setZaurusUid( int id ) +{ + if (mReadOnly) return; + mZaurusUid = id; +} + +int IncidenceBase::zaurusStat() const +{ + return mZaurusStat; +} +void IncidenceBase::setZaurusStat( int id ) +{ + if (mReadOnly) return; + mZaurusStat = id; +} + +void IncidenceBase::registerObserver( IncidenceBase::Observer *observer ) +{ + if( !mObservers.contains(observer) ) mObservers.append( observer ); +} + +void IncidenceBase::unRegisterObserver( IncidenceBase::Observer *observer ) +{ + mObservers.remove( observer ); +} + +void IncidenceBase::updated() +{ + QPtrListIterator<Observer> it(mObservers); + while( it.current() ) { + Observer *o = it.current(); + ++it; + o->incidenceUpdated( this ); + } +} diff --git a/libkcal/incidencebase.h b/libkcal/incidencebase.h new file mode 100644 index 0000000..0ab7eef --- a/dev/null +++ b/libkcal/incidencebase.h @@ -0,0 +1,170 @@ +/* + This file is part of libkcal. + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef KCAL_INCIDENCEBASE_H +#define KCAL_INCIDENCEBASE_H +// +// Incidence - base class of calendaring components +// + +#include <qdatetime.h> +#include <qstringlist.h> +#include <qvaluelist.h> +#include <qptrlist.h> + +#include "customproperties.h" +#include "attendee.h" + +namespace KCal { + +typedef QValueList<QDate> DateList; + +/** + This class provides the base class common to all calendar components. +*/ +class IncidenceBase : public CustomProperties +{ + public: + class Observer { + public: + virtual void incidenceUpdated( IncidenceBase * ) = 0; + }; + + IncidenceBase(); + IncidenceBase(const IncidenceBase &); + virtual ~IncidenceBase(); + + virtual QCString type() const = 0; + + /** Set the unique id for the event */ + void setUid(const QString &); + /** Return the unique id for the event */ + QString uid() const; + + /** Sets the time the incidence was last modified. */ + void setLastModified(const QDateTime &lm); + /** Return the time the incidence was last modified. */ + QDateTime lastModified() const; + + /** sets the organizer for the event */ + void setOrganizer(const QString &o); + QString organizer() const; + + /** Set readonly status. */ + virtual void setReadOnly( bool ); + /** Return if the object is read-only. */ + bool isReadOnly() const { return mReadOnly; } + + /** for setting the event's starting date/time with a QDateTime. */ + virtual void setDtStart(const QDateTime &dtStart); + /** returns an event's starting date/time as a QDateTime. */ + QDateTime dtStart() const; + /** returns an event's starting time as a string formatted according to the + users locale settings */ + QString dtStartTimeStr() const; + /** returns an event's starting date as a string formatted according to the + users locale settings */ + QString dtStartDateStr(bool shortfmt=true) const; + /** returns an event's starting date and time as a string formatted according + to the users locale settings */ + QString dtStartStr(bool shortfmt=true) const; + + virtual void setDuration(int seconds); + int duration() const; + void setHasDuration(bool); + bool hasDuration() const; + + /** Return true or false depending on whether the incidence "floats," + * i.e. has a date but no time attached to it. */ + bool doesFloat() const; + /** Set whether the incidence floats, i.e. has a date but no time attached to it. */ + void setFloats(bool f); + + /** + Add Attendee to this incidence. IncidenceBase takes ownership of the + Attendee object. + */ + void addAttendee(Attendee *a, bool doupdate=true ); +// void removeAttendee(Attendee *a); +// void removeAttendee(const char *n); + /** Remove all Attendees. */ + void clearAttendees(); + /** Return list of attendees. */ + QPtrList<Attendee> attendees() const { return mAttendees; }; + /** Return number of attendees. */ + int attendeeCount() const { return mAttendees.count(); }; + /** Return the Attendee with this email */ + Attendee* attendeeByMail(const QString &); + /** Return first Attendee with one of this emails */ + Attendee* attendeeByMails(const QStringList &, const QString& email = QString::null); + + /** pilot syncronization states */ + enum { SYNCNONE = 0, SYNCMOD = 1, SYNCDEL = 3 }; + /** Set synchronisation satus. */ + void setSyncStatus(int stat); + /** Return synchronisation status. */ + int syncStatus() const; + + /** Set Pilot Id. */ + void setPilotId(int id); + /** Return Pilot Id. */ + int pilotId() const; + + void setZaurusId(int id); + int zaurusId() const; + void setZaurusUid(int id); + int zaurusUid() const; + void setZaurusStat(int id); + int zaurusStat() const; + + void registerObserver( Observer * ); + void unRegisterObserver( Observer * ); + void updated(); + + protected: + bool mReadOnly; + QDateTime getEvenTime( QDateTime ); + + private: + // base components + QDateTime mDtStart; + QString mOrganizer; + QString mUid; + QDateTime mLastModified; + QPtrList<Attendee> mAttendees; + + bool mFloats; + + int mDuration; + bool mHasDuration; + int mZaurusId; + int mZaurusUid; + int mZaurusStat; + + // PILOT SYNCHRONIZATION STUFF + int mPilotId; // unique id for pilot sync + int mSyncStatus; // status (for sync) + + QPtrList<Observer> mObservers; +}; + +bool operator==( const IncidenceBase&, const IncidenceBase& ); +} + +#endif diff --git a/libkcal/journal.cpp b/libkcal/journal.cpp new file mode 100644 index 0000000..351fb32 --- a/dev/null +++ b/libkcal/journal.cpp @@ -0,0 +1,49 @@ +/* + This file is part of libkcal. + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include "journal.h" + +using namespace KCal; + +Journal::Journal() +{ +} + +Journal::~Journal() +{ +} + +Incidence *Journal::clone() +{ + return new Journal(*this); +} + + +bool KCal::operator==( const Journal& j1, const Journal& j2 ) +{ + return operator==( (const Incidence&)j1, (const Incidence&)j2 ); +} + + +QDateTime Journal::getNextAlarmDateTime( bool * ok, int * offset ) const +{ + *ok = false; + return QDateTime (); +} diff --git a/libkcal/journal.h b/libkcal/journal.h new file mode 100644 index 0000000..cb90c7a --- a/dev/null +++ b/libkcal/journal.h @@ -0,0 +1,50 @@ +/* + This file is part of libkcal. + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef JOURNAL_H +#define JOURNAL_H +// +// Journal component, representing a VJOURNAL object +// + +#include "incidence.h" + +namespace KCal { + +/** + This class provides a Journal in the sense of RFC2445. +*/ +class Journal : public Incidence +{ + public: + Journal(); + ~Journal(); + + QCString type() const { return "Journal"; } + + Incidence *clone(); + QDateTime getNextAlarmDateTime( bool * ok, int * offset ) const; +private: + bool accept(Visitor &v) { return v.visit(this); } +}; + + bool operator==( const Journal&, const Journal& ); +} + +#endif diff --git a/libkcal/kcal.pro.back b/libkcal/kcal.pro.back new file mode 100644 index 0000000..a33c5f9 --- a/dev/null +++ b/libkcal/kcal.pro.back @@ -0,0 +1,84 @@ +TEMPLATE = lib +CONFIG = qt warn_on release +TARGET = kcal +INCLUDEPATH += ../microkde ../qtcompat versit +INCLUDEPATH += ../libical/src/libical +INCLUDEPATH += ../libical/src/libicalss +OBJECTS_DIR = obj/$(PLATFORM) +MOC_DIR = moc +DESTDIR = $(QPEDIR)/lib +LIBS += -lical +LIBS += -licalss + +INTERFACES = \ + +HEADERS = \ + alarm.h \ + attachment.h \ + attendee.h \ + calendar.h \ + calendarlocal.h \ + calfilter.h \ + calformat.h \ + calstorage.h \ + compat.h \ + customproperties.h \ + dummyscheduler.h \ + duration.h \ + event.h \ + exceptions.h \ + filestorage.h \ + freebusy.h \ + icaldrag.h \ + icalformat.h \ + icalformatimpl.h \ + imipscheduler.h \ + incidence.h \ + incidencebase.h \ + journal.h \ + period.h \ + person.h \ + qtopiaformat.h \ + recurrence.h \ + scheduler.h \ + todo.h \ + vcaldrag.h \ + vcalformat.h \ + versit/port.h \ + versit/vcc.h \ + versit/vobject.h \ + +SOURCES = \ + alarm.cpp \ + attachment.cpp \ + attendee.cpp \ + calendar.cpp \ + calendarlocal.cpp \ + calfilter.cpp \ + calformat.cpp \ + compat.cpp \ + customproperties.cpp \ + dummyscheduler.cpp \ + duration.cpp \ + event.cpp \ + exceptions.cpp \ + filestorage.cpp \ + freebusy.cpp \ + icaldrag.cpp \ + icalformat.cpp \ + icalformatimpl.cpp \ + imipscheduler.cpp \ + incidence.cpp \ + incidencebase.cpp \ + journal.cpp \ + period.cpp \ + person.cpp \ + qtopiaformat.cpp \ + recurrence.cpp \ + scheduler.cpp \ + todo.cpp \ + vcaldrag.cpp \ + vcalformat.cpp \ + versit/vcc.c \ + versit/vobject.c \ + diff --git a/libkcal/libkcal.pro b/libkcal/libkcal.pro new file mode 100644 index 0000000..49aa24b --- a/dev/null +++ b/libkcal/libkcal.pro @@ -0,0 +1,100 @@ +TEMPLATE = lib +CONFIG += qt warn_on +TARGET = microkcal + +include( ../variables.pri ) + +INCLUDEPATH += ../microkde versit ../microkde/kdecore +#../qtcompat +INCLUDEPATH += ../libical/src/libical +INCLUDEPATH += ../libical/src/libicalss +DESTDIR = ../bin +DEFINES += DESKTOP_VERSION +unix: { +LIBS += ../libical/lib/libical.a +LIBS += ../libical/lib/libicalss.a +OBJECTS_DIR = obj/unix +MOC_DIR = moc/unix +} +win32: { +DEFINES += _WIN32_ + +LIBS += ../libical/lib/ical.lib +LIBS += ../libical/lib/icalss.lib +OBJECTS_DIR = obj/win +MOC_DIR = moc/win + +} + +INTERFACES = \ + +HEADERS = \ + alarm.h \ + attachment.h \ + attendee.h \ + calendar.h \ + calendarlocal.h \ + calfilter.h \ + calformat.h \ + calstorage.h \ + compat.h \ + customproperties.h \ + dummyscheduler.h \ + duration.h \ + event.h \ + exceptions.h \ + filestorage.h \ + freebusy.h \ + icaldrag.h \ + icalformat.h \ + icalformatimpl.h \ + imipscheduler.h \ + incidence.h \ + incidencebase.h \ + journal.h \ + period.h \ + person.h \ + qtopiaformat.h \ + recurrence.h \ + scheduler.h \ + todo.h \ + vcaldrag.h \ + vcalformat.h \ + versit/port.h \ + versit/vcc.h \ + versit/vobject.h \ + +SOURCES = \ + alarm.cpp \ + attachment.cpp \ + attendee.cpp \ + calendar.cpp \ + calendarlocal.cpp \ + calfilter.cpp \ + calformat.cpp \ + compat.cpp \ + customproperties.cpp \ + dummyscheduler.cpp \ + duration.cpp \ + event.cpp \ + exceptions.cpp \ + filestorage.cpp \ + freebusy.cpp \ + icaldrag.cpp \ + icalformat.cpp \ + icalformatimpl.cpp \ + imipscheduler.cpp \ + incidence.cpp \ + incidencebase.cpp \ + journal.cpp \ + period.cpp \ + person.cpp \ + qtopiaformat.cpp \ + recurrence.cpp \ + scheduler.cpp \ + todo.cpp \ + vcaldrag.cpp \ + vcalformat.cpp \ + versit/vcc.c \ + versit/vobject.c \ + diff --git a/libkcal/libkcalE.pro b/libkcal/libkcalE.pro new file mode 100644 index 0000000..e27c10f --- a/dev/null +++ b/libkcal/libkcalE.pro @@ -0,0 +1,88 @@ +TEMPLATE = lib +CONFIG += qt warn_on +TARGET = microkcal + + +INCLUDEPATH += ../microkde ../qtcompat versit ../microkde/kdecore versit $(QPEDIR)/include +INCLUDEPATH += ../libical/src/libical +INCLUDEPATH += ../libical/src/libicalss +OBJECTS_DIR = obj/$(PLATFORM) +MOC_DIR = moc/$(PLATFORM) +DESTDIR = $(QPEDIR)/lib +LIBS += ../libical/lib/$(PLATFORM)/libical.a +LIBS += ../libical/lib/$(PLATFORM)/libicalss.a + +INTERFACES = \ + +HEADERS = \ + alarm.h \ + attachment.h \ + attendee.h \ + calendar.h \ + calendarlocal.h \ + calfilter.h \ + calformat.h \ + calstorage.h \ + compat.h \ + customproperties.h \ + dummyscheduler.h \ + duration.h \ + event.h \ + exceptions.h \ + filestorage.h \ + freebusy.h \ + icaldrag.h \ + icalformat.h \ + icalformatimpl.h \ + imipscheduler.h \ + incidence.h \ + incidencebase.h \ + journal.h \ + period.h \ + person.h \ + qtopiaformat.h \ + sharpformat.h \ + recurrence.h \ + scheduler.h \ + todo.h \ + vcaldrag.h \ + vcalformat.h \ + versit/port.h \ + versit/vcc.h \ + versit/vobject.h \ + +SOURCES = \ + alarm.cpp \ + attachment.cpp \ + attendee.cpp \ + calendar.cpp \ + calendarlocal.cpp \ + calfilter.cpp \ + calformat.cpp \ + compat.cpp \ + customproperties.cpp \ + dummyscheduler.cpp \ + duration.cpp \ + event.cpp \ + exceptions.cpp \ + filestorage.cpp \ + freebusy.cpp \ + icaldrag.cpp \ + icalformat.cpp \ + icalformatimpl.cpp \ + imipscheduler.cpp \ + incidence.cpp \ + incidencebase.cpp \ + journal.cpp \ + period.cpp \ + person.cpp \ + qtopiaformat.cpp \ + sharpformat.cpp \ + recurrence.cpp \ + scheduler.cpp \ + todo.cpp \ + vcaldrag.cpp \ + vcalformat.cpp \ + versit/vcc.c \ + versit/vobject.c \ + diff --git a/libkcal/listbase.h b/libkcal/listbase.h new file mode 100644 index 0000000..085b13d --- a/dev/null +++ b/libkcal/listbase.h @@ -0,0 +1,97 @@ +/* + This file is part of libkcal. + + Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef KCAL_LISTBASE_H +#define KCAL_LISTBASE_H + +#include <qvaluelist.h> + +namespace KCal { + class Event; + class Todo; +/** + This class provides a template for lists of pointers. It extends QValueList<T + *> by auto delete funtionality known from QPtrList. +*/ +template<class T> +class ListBase : public QValueList<T *> +{ + public: + ListBase() + : QValueList<T *>(), mAutoDelete( false ) + { + } + + ListBase( const ListBase &l ) + : QValueList<T *>( l ), mAutoDelete( false ) + { + } + + ~ListBase() + { + if ( mAutoDelete ) { + QValueListIterator<T *> it; + for( it = QValueList<T*>::begin(); it != QValueList<T*>::end(); ++it ) { + delete *it; + } + } + } + + ListBase &operator=( const ListBase &l ) + { + if ( this == &l ) return *this; + QValueList<T *>::operator=( l ); + return *this; + } + + void setAutoDelete( bool autoDelete ) + { + mAutoDelete = autoDelete; + } + + bool removeRef( T *t ) + { + QValueListIterator<T *> it = find( t ); + if ( it == QValueList<T*>::end() ) { + return false; + } else { + if ( mAutoDelete ) delete t; + remove( it ); + return true; + } + } + void fill ( QPtrList<T> list ) { + QPtrListIterator<T> it (list); + T *item; + while ( (item = it.current()) != 0 ) { + append( item ); + ++it; + } + + } + + + private: + bool mAutoDelete; +}; + +} + +#endif diff --git a/libkcal/period.cpp b/libkcal/period.cpp new file mode 100644 index 0000000..d188a4c --- a/dev/null +++ b/libkcal/period.cpp @@ -0,0 +1,65 @@ +/* + This file is part of libkcal. + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include <kdebug.h> +#include <klocale.h> + +#include "period.h" + +using namespace KCal; + +Period::Period() +{ + mHasDuration = false; +} + +Period::Period( const QDateTime &start, const QDateTime &end ) +{ + mStart = start; + mEnd = end; + mHasDuration = false; +} + +Period::Period( const QDateTime &start, const Duration &duration ) +{ + mStart = start; + mEnd = duration.end( start ); + mHasDuration = true; +} + +QDateTime Period::start() const +{ + return mStart; +} + +QDateTime Period::end()const +{ + return mEnd; +} + +Duration Period::duration() +{ + return Duration( mStart, mEnd ); +} + +bool Period::hasDuration()const +{ + return mHasDuration; +} diff --git a/libkcal/period.h b/libkcal/period.h new file mode 100644 index 0000000..9d40f12 --- a/dev/null +++ b/libkcal/period.h @@ -0,0 +1,51 @@ +/* + This file is part of libkcal. + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef KCAL_PERIOD_H +#define KCAL_PERIOD_H + +#include <qdatetime.h> + +#include "duration.h" + +namespace KCal { + +class Period +{ + public: + Period(); + Period( const QDateTime &start, const QDateTime &end ); + Period( const QDateTime &start, const Duration &duration ); + + QDateTime start()const; + QDateTime end()const; + Duration duration(); + + bool hasDuration()const; + + private: + QDateTime mStart; + QDateTime mEnd; + + bool mHasDuration; +}; + +} + +#endif diff --git a/libkcal/person.cpp b/libkcal/person.cpp new file mode 100644 index 0000000..aca28c2 --- a/dev/null +++ b/libkcal/person.cpp @@ -0,0 +1,77 @@ +/* + This file is part of libkcal. + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include <kdebug.h> +#include <klocale.h> + +#include "person.h" + +using namespace KCal; + +Person::Person( const QString &fullName ) +{ + int emailPos = fullName.find( '<' ); + if ( emailPos < 0 ) { + setEmail(fullName); + } else { + setEmail(fullName.mid( emailPos + 1, fullName.length() - 1 )); + setName(fullName.left( emailPos - 2 )); + } +} + +Person::Person( const QString &name, const QString &email ) +{ + setName(name); + setEmail(email); +} + + +bool KCal::operator==( const Person& p1, const Person& p2 ) +{ + return ( p1.name() == p2.name() && + p1.email() == p2.email() ); +} + + +QString Person::fullName() const +{ + if( mName.isEmpty() ) { + return mEmail; + } else { + if( mEmail.isEmpty() ) + return mName; + else + return mName + " <" + mEmail + ">"; + } +} + +void Person::setName(const QString &name) +{ + mName = name; +} + +void Person::setEmail(const QString &email) +{ + if (email.left(7).lower() == "mailto:") { + mEmail = email.mid(7); + } else { + mEmail = email; + } +} diff --git a/libkcal/person.h b/libkcal/person.h new file mode 100644 index 0000000..c46c5f0 --- a/dev/null +++ b/libkcal/person.h @@ -0,0 +1,50 @@ +/* + This file is part of libkcal. + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef KCAL_PERSON_H +#define KCAL_PERSON_H + +#include <qstring.h> + +namespace KCal { + +class Person +{ + public: + Person() {} + Person( const QString &fullName ); + Person( const QString &name, const QString &email ); + + QString fullName( ) const; + + void setName(const QString &); + QString name() const { return mName; } + + void setEmail(const QString &); + QString email() const { return mEmail; } + + private: + QString mName; + QString mEmail; +}; + + bool operator==( const Person& p1, const Person& p2 ); +} + +#endif diff --git a/libkcal/qtopiaformat.cpp b/libkcal/qtopiaformat.cpp new file mode 100644 index 0000000..0a4a031 --- a/dev/null +++ b/libkcal/qtopiaformat.cpp @@ -0,0 +1,333 @@ +/* + This file is part of libkcal. + + Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include <qdatetime.h> +#include <qstring.h> +#include <qptrlist.h> +#include <qregexp.h> +#include <qclipboard.h> +#include <qfile.h> +#include <qtextstream.h> +#include <qxml.h> + +#include <kdebug.h> +#include <klocale.h> + +#include "calendar.h" +#include "calendarlocal.h" + +#include "qtopiaformat.h" + +using namespace KCal; + +class QtopiaParser : public QXmlDefaultHandler +{ + public: + QtopiaParser( Calendar *calendar ) : mCalendar( calendar ) { + oldCategories = 0; + } + + bool startElement( const QString &, const QString &, const QString & qName, + const QXmlAttributes &attributes ) + { + if ( qName == "event" ) { + Event *event = new Event; + QString uid = "Qtopia" + attributes.value( "uid" ); + // event->setUid( uid ); + + event->setSummary( attributes.value( "description" ) ); + event->setLocation( attributes.value( "location" ) ); + event->setDescription( attributes.value( "note" ) ); + event->setDtStart( toDateTime( attributes.value( "start" ) ) ); + event->setDtEnd( toDateTime( attributes.value( "end" ) ) ); + + if ( attributes.value( "type" ) == "AllDay" ) { + event->setFloats( true ); + } else { + event->setFloats( false ); + } + + QString rtype = attributes.value( "rtype" ); + if ( !rtype.isEmpty() ) { + QDate startDate = event->dtStart().date(); + + QString freqStr = attributes.value( "rfreq" ); + int freq = freqStr.toInt(); + + QString hasEndDateStr = attributes.value( "rhasenddate" ); + bool hasEndDate = hasEndDateStr == "1"; + + QString endDateStr = attributes.value( "enddt" ); + QDate endDate = toDateTime( endDateStr ).date(); + + QString weekDaysStr = attributes.value( "rweekdays" ); + int weekDaysNum = weekDaysStr.toInt(); + if ( weekDaysNum == 0 ) + weekDaysNum = (1 << (event->dtStart().date().dayOfWeek()-1)); + + QBitArray weekDays( 7 ); + weekDays.fill( false ); + int i; + for( i = 0; i < 7; ++i ) { + weekDays.setBit( i , ( 1 << i ) & weekDaysNum ); + qDebug("%d %d %d ",i, weekDaysNum, weekDays.at(i) ); + } + + QString posStr = attributes.value( "rposition" ); + int pos = posStr.toInt(); + + Recurrence *r = event->recurrence(); + + if ( rtype == "Daily" ) { + if ( hasEndDate ) r->setDaily( freq, endDate ); + else r->setDaily( freq, -1 ); + } else if ( rtype == "Weekly" ) { + // fix needed here + // rweekdays not set in XML file + if ( hasEndDate ) r->setWeekly( freq, weekDays, endDate ); + else r->setWeekly( freq, weekDays, -1 ); + } else if ( rtype == "MonthlyDate" ) { + if ( hasEndDate ) + r->setMonthly( Recurrence::rMonthlyDay, freq, endDate ); + else + r->setMonthly( Recurrence::rMonthlyDay, freq, -1 ); + r->addMonthlyDay( startDate.day() ); + } else if ( rtype == "MonthlyDay" ) { + if ( hasEndDate ) + r->setMonthly( Recurrence::rMonthlyPos, freq, endDate ); + else + r->setMonthly( Recurrence::rMonthlyPos, freq, -1 ); + QBitArray days( 7 ); + days.fill( false ); + days.setBit( startDate.dayOfWeek() - 1 ); + r->addMonthlyPos( pos, days ); + } else if ( rtype == "Yearly" ) { + if ( hasEndDate ) + r->setYearly( Recurrence::rYearlyMonth, freq, endDate ); + else + r->setYearly( Recurrence::rYearlyMonth, freq, -1 ); + r->addYearlyNum( startDate.month() ); + } + } + + QString categoryList = attributes.value( "categories" ); + event->setCategories( lookupCategories( categoryList ) ); + + QString alarmStr = attributes.value( "alarm" ); + if ( !alarmStr.isEmpty() ) { + Alarm *alarm = new Alarm( event ); + alarm->setType( Alarm::Display ); + alarm->setEnabled( true ); + int alarmOffset = alarmStr.toInt(); + alarm->setStartOffset( alarmOffset * -60 ); + event->addAlarm( alarm ); + } + // the following may not be + //Event *oldEvent = mCalendar->event( uid ); + //if ( oldEvent ) mCalendar->deleteEvent( oldEvent ); + + mCalendar->addEventNoDup( event ); + } else if ( qName == "Task" ) { + Todo *todo = new Todo; + + QString uid = "Qtopia" + attributes.value( "Uid" ); + //todo->setUid( uid ); + + QString description = attributes.value( "Description" ); + int pos = description.find( '\n' ); + if ( pos > 0 ) { + QString summary = description.left( pos ); + todo->setSummary( summary ); + todo->setDescription( description ); + } else { + todo->setSummary( description ); + } + + int priority = attributes.value( "Priority" ).toInt(); + if ( priority == 0 ) priority = 3; + todo->setPriority( priority ); + + QString categoryList = attributes.value( "Categories" ); + todo->setCategories( lookupCategories( categoryList ) ); + + QString completedStr = attributes.value( "Completed" ); + if ( completedStr == "1" ) todo->setCompleted( true ); + + QString hasDateStr = attributes.value( "HasDate" ); + if ( hasDateStr == "1" ) { + int year = attributes.value( "DateYear" ).toInt(); + int month = attributes.value( "DateMonth" ).toInt(); + int day = attributes.value( "DateDay" ).toInt(); + + todo->setDtDue( QDateTime( QDate( year, month, day ) ) ); + todo->setHasDueDate( true ); + } + + // Todo *oldTodo = mCalendar->todo( uid ); + //if ( oldTodo ) mCalendar->deleteTodo( oldTodo ); + + mCalendar->addTodoNoDup( todo ); + } else if ( qName == "Category" ) { + QString id = attributes.value( "id" ); + QString name = attributes.value( "name" ); + setCategory( id, name ); + } + + return true; + } + + bool warning ( const QXmlParseException &exception ) + { + printException( exception ); + return true; + } + + bool error ( const QXmlParseException &exception ) + { + printException( exception ); + return false; + } + + bool fatalError ( const QXmlParseException &exception ) + { + printException( exception ); + return false; + } + + QString errorString () + { + return "QtopiaParser: Error!"; + } + void setCategoriesList ( QStringList * c ) + { + oldCategories = c; + } + + protected: + void printException( const QXmlParseException &exception ) + { + kdError() << "XML Parse Error (line " << exception.lineNumber() + << ", col " << exception.columnNumber() << "): " + << exception.message() << "(public ID: '" + << exception.publicId() << "' system ID: '" + << exception.systemId() << "')" << endl; + } + + QDateTime toDateTime( const QString &value ) + { + QDateTime dt; + dt.setTime_t( value.toUInt() ); + + return dt; + } + + QStringList lookupCategories( const QString &categoryList ) + { + QStringList categoryIds = QStringList::split( ";", categoryList ); + QStringList categories; + QStringList::ConstIterator it; + for( it = categoryIds.begin(); it != categoryIds.end(); ++it ) { + QString cate = category( *it ); + if ( oldCategories ) { + if ( ! oldCategories->contains( cate ) ) + oldCategories->append( cate ); + } + categories.append(cate ); + } + return categories; + } + + private: + Calendar *mCalendar; + QStringList * oldCategories; + static QString category( const QString &id ) + { + QMap<QString,QString>::ConstIterator it = mCategoriesMap.find( id ); + if ( it == mCategoriesMap.end() ) return id; + else return *it; + } + + static void setCategory( const QString &id, const QString &name ) + { + mCategoriesMap.insert( id, name ); + } + + static QMap<QString,QString> mCategoriesMap; +}; + +QMap<QString,QString> QtopiaParser::mCategoriesMap; + +QtopiaFormat::QtopiaFormat() +{ + mCategories = 0; +} + +QtopiaFormat::~QtopiaFormat() +{ +} +#include <qdom.h> +bool QtopiaFormat::load( Calendar *calendar, const QString &fileName ) +{ + clearException(); + // qDebug("load QtopiaFormat: %s ",fileName.latin1() ); + QtopiaParser handler( calendar ); + handler.setCategoriesList( mCategories ); + QFile xmlFile( fileName ); + QXmlInputSource source( xmlFile ); + QXmlSimpleReader reader; + reader.setContentHandler( &handler ); + return reader.parse( source ); +} + +bool QtopiaFormat::save( Calendar *calendar, const QString &fileName ) +{ + + clearException(); + + QString text = toString( calendar ); + + if ( text.isNull() ) return false; + + // TODO: write backup file + + QFile file( fileName ); + if (!file.open( IO_WriteOnly ) ) { + setException(new ErrorFormat(ErrorFormat::SaveError, + i18n("Could not open file '%1'").arg(fileName))); + return false; + } + QTextStream ts( &file ); + ts << text; + file.close(); + + return true; +} + +bool QtopiaFormat::fromString( Calendar *, const QString & ) +{ + + return false; +} + +QString QtopiaFormat::toString( Calendar * ) +{ + return QString::null; +} diff --git a/libkcal/qtopiaformat.h b/libkcal/qtopiaformat.h new file mode 100644 index 0000000..2c69a4e --- a/dev/null +++ b/libkcal/qtopiaformat.h @@ -0,0 +1,53 @@ +/* + This file is part of libkcal. + + Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef QTOPIAFORMAT_H +#define QTOPIAFORMAT_H + +#include <qstring.h> + +#include "scheduler.h" + +#include "calformat.h" + +namespace KCal { + +/** + This class implements the calendar format used by Qtopia. +*/ +class QtopiaFormat : public CalFormat { + public: + /** Create new iCalendar format. */ + QtopiaFormat(); + virtual ~QtopiaFormat(); + + bool load( Calendar *, const QString &fileName ); + bool save( Calendar *, const QString &fileName ); + void setCategoriesList ( QStringList * cat ){ mCategories = cat; } + bool fromString( Calendar *, const QString & ); + QString toString( Calendar * ); + + private: + QStringList *mCategories; +}; + +} + +#endif diff --git a/libkcal/recurrence.cpp b/libkcal/recurrence.cpp new file mode 100644 index 0000000..5fc5d1f --- a/dev/null +++ b/libkcal/recurrence.cpp @@ -0,0 +1,3360 @@ +/* + This file is part of libkcal. + Copyright (c) 1998 Preston Brown + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + Copyright (c) 2002 David Jarvie <software@astrojar.org.uk> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include <limits.h> + +#include <kdebug.h> +#include <kglobal.h> +#include <klocale.h> + +#include "incidence.h" + +#include "recurrence.h" + +using namespace KCal; + +Recurrence::Feb29Type Recurrence::mFeb29YearlyDefaultType = Recurrence::rMar1; + + +Recurrence::Recurrence(Incidence *parent, int compatVersion) +: recurs(rNone), // by default, it's not a recurring event + rWeekStart(1), // default is Monday + rDays(7), + mFloats(parent ? parent->doesFloat() : false), + mRecurReadOnly(false), + mRecurExDatesCount(0), + mFeb29YearlyType(mFeb29YearlyDefaultType), + mCompatVersion(compatVersion ? compatVersion : INT_MAX), + mCompatRecurs(rNone), + mCompatDuration(0), + mParent(parent) +{ + rMonthDays.setAutoDelete( true ); + rMonthPositions.setAutoDelete( true ); + rYearNums.setAutoDelete( true ); +} + +Recurrence::Recurrence(const Recurrence &r, Incidence *parent) +: recurs(r.recurs), + rWeekStart(r.rWeekStart), + rDays(r.rDays.copy()), + rFreq(r.rFreq), + rDuration(r.rDuration), + rEndDateTime(r.rEndDateTime), + mRecurStart(r.mRecurStart), + mFloats(r.mFloats), + mRecurReadOnly(r.mRecurReadOnly), + mRecurExDatesCount(r.mRecurExDatesCount), + mFeb29YearlyType(r.mFeb29YearlyType), + mCompatVersion(r.mCompatVersion), + mCompatRecurs(r.mCompatRecurs), + mCompatDuration(r.mCompatDuration), + mParent(parent) +{ + for (QPtrListIterator<rMonthPos> mp(r.rMonthPositions); mp.current(); ++mp) { + rMonthPos *tmp = new rMonthPos; + tmp->rPos = mp.current()->rPos; + tmp->negative = mp.current()->negative; + tmp->rDays = mp.current()->rDays.copy(); + rMonthPositions.append(tmp); + } + for (QPtrListIterator<int> md(r.rMonthDays); md.current(); ++md) { + int *tmp = new int; + *tmp = *md.current(); + rMonthDays.append(tmp); + } + for (QPtrListIterator<int> yn(r.rYearNums); yn.current(); ++yn) { + int *tmp = new int; + *tmp = *yn.current(); + rYearNums.append(tmp); + } + rMonthDays.setAutoDelete( true ); + rMonthPositions.setAutoDelete( true ); + rYearNums.setAutoDelete( true ); +} + +Recurrence::~Recurrence() +{ +} + + +bool Recurrence::operator==( const Recurrence& r2 ) const +{ + + // the following line is obvious + if ( recurs == rNone && r2.recurs == rNone ) + return true; + // we need the above line, because two non recurring events may + // differ in the other settings, because one (or both) + // may be not initialized properly + if ( recurs != r2.recurs + || rFreq != r2.rFreq + || rDuration != r2.rDuration + || !rDuration && rEndDateTime != r2.rEndDateTime + || mRecurStart != r2.mRecurStart + || mFloats != r2.mFloats + || mRecurReadOnly != r2.mRecurReadOnly + || mRecurExDatesCount != r2.mRecurExDatesCount ) + return false; + // no need to compare mCompat* and mParent + // OK to compare the pointers + switch ( recurs ) + { + case rWeekly: + return rDays == r2.rDays + && rWeekStart == r2.rWeekStart; + case rMonthlyPos: + return rMonthPositions.count() == r2.rMonthPositions.count(); + case rMonthlyDay: + return rMonthDays.count() == r2.rMonthDays.count(); + case rYearlyPos: + return rYearNums.count() == r2.rYearNums.count() + && rMonthPositions.count() == r2.rMonthPositions.count(); + case rYearlyMonth: + return rYearNums.count() == r2.rYearNums.count() + && mFeb29YearlyType == r2.mFeb29YearlyType; + case rYearlyDay: + return rYearNums == r2.rYearNums; + case rNone: + case rMinutely: + case rHourly: + case rDaily: + default: + return true; + } +} +/* +bool Recurrence::compareLists( const QPtrList<int> &l1 ,const QPtrList<int> &l2) +{ + if ( l1.count() != l2.count() ) + return false; + int count = l1.count(); + int i; + for ( i = 0; i < count ; ++i ) { + // if ( l1.at(i) != l2.at(i) ) + return false; + qDebug("compüare "); + } + return true; +} +*/ +QString Recurrence::recurrenceText() const +{ + QString recurText = i18n("No"); + if ( recurs == Recurrence::rMinutely ) + recurText = i18n("minutely"); + else if ( recurs == Recurrence::rHourly ) + recurText = i18n("hourly"); + else if ( recurs == Recurrence::rDaily ) + recurText = i18n("daily"); + else if ( recurs == Recurrence::rWeekly ) + recurText = i18n("weekly"); + else if ( recurs == Recurrence::rMonthlyPos ) + recurText = i18n("monthly"); + else if ( recurs == Recurrence::rMonthlyDay ) + recurText = i18n("day-monthly"); + else if ( recurs == Recurrence::rYearlyMonth ) + recurText = i18n("month-yearly"); + else if ( recurs == Recurrence::rYearlyDay ) + recurText = i18n("day-yearly"); + else if ( recurs == Recurrence::rYearlyPos ) + recurText = i18n("position-yearly"); + return recurText; +} + +void Recurrence::setCompatVersion(int version) +{ + mCompatVersion = version ? version : INT_MAX; +} + +ushort Recurrence::doesRecur() const +{ + return recurs; +} + +bool Recurrence::recursOnPure(const QDate &qd) const +{ + switch(recurs) { + case rMinutely: + return recursSecondly(qd, rFreq*60); + case rHourly: + return recursSecondly(qd, rFreq*3600); + case rDaily: + return recursDaily(qd); + case rWeekly: + return recursWeekly(qd); + case rMonthlyPos: + case rMonthlyDay: + return recursMonthly(qd); + case rYearlyMonth: + return recursYearlyByMonth(qd); + case rYearlyDay: + return recursYearlyByDay(qd); + case rYearlyPos: + return recursYearlyByPos(qd); + default: + return false; + case rNone: + return false; + } // case + return false; +} + +bool Recurrence::recursAtPure(const QDateTime &dt) const +{ + switch(recurs) { + case rMinutely: + return recursMinutelyAt(dt, rFreq); + case rHourly: + return recursMinutelyAt(dt, rFreq*60); + default: + if (dt.time() != mRecurStart.time()) + return false; + switch(recurs) { + case rDaily: + return recursDaily(dt.date()); + case rWeekly: + return recursWeekly(dt.date()); + case rMonthlyPos: + case rMonthlyDay: + return recursMonthly(dt.date()); + case rYearlyMonth: + return recursYearlyByMonth(dt.date()); + case rYearlyDay: + return recursYearlyByDay(dt.date()); + case rYearlyPos: + return recursYearlyByPos(dt.date()); + default: + return false; + case rNone: + return false; + } + } // case + return false; +} + +QDate Recurrence::endDate() const +{ + int count = 0; + QDate end; + if (recurs != rNone) { + if (rDuration < 0) + return QDate(); // infinite recurrence + if (rDuration == 0) + return rEndDateTime.date(); + + // The end date is determined by the recurrence count + QDate dStart = mRecurStart.date(); + switch (recurs) + { + case rMinutely: + return mRecurStart.addSecs((rDuration-1+mRecurExDatesCount)*rFreq*60).date(); + case rHourly: + return mRecurStart.addSecs((rDuration-1+mRecurExDatesCount)*rFreq*3600).date(); + case rDaily: + return dStart.addDays((rDuration-1+mRecurExDatesCount)*rFreq); + + case rWeekly: + count = weeklyCalc(END_DATE_AND_COUNT, end); + break; + case rMonthlyPos: + case rMonthlyDay: + count = monthlyCalc(END_DATE_AND_COUNT, end); + break; + case rYearlyMonth: + count = yearlyMonthCalc(END_DATE_AND_COUNT, end); + break; + case rYearlyDay: + count = yearlyDayCalc(END_DATE_AND_COUNT, end); + break; + case rYearlyPos: + count = yearlyPosCalc(END_DATE_AND_COUNT, end); + break; + default: + // catch-all. Should never get here. + kdDebug(5800) << "Control should never reach here in endDate()!" << endl; + break; + } + } + if (!count) + return QDate(); // error - there is no recurrence + return end; +} + +QDateTime Recurrence::endDateTime() const +{ + int count = 0; + QDate end; + if (recurs != rNone) { + if (rDuration < 0) + return QDateTime(); // infinite recurrence + if (rDuration == 0) + return rEndDateTime; + + // The end date is determined by the recurrence count + QDate dStart = mRecurStart.date(); + switch (recurs) + { + case rMinutely: + return mRecurStart.addSecs((rDuration-1+mRecurExDatesCount)*rFreq*60); + case rHourly: + return mRecurStart.addSecs((rDuration-1+mRecurExDatesCount)*rFreq*3600); + case rDaily: + return dStart.addDays((rDuration-1+mRecurExDatesCount)*rFreq); + + case rWeekly: + count = weeklyCalc(END_DATE_AND_COUNT, end); + break; + case rMonthlyPos: + case rMonthlyDay: + count = monthlyCalc(END_DATE_AND_COUNT, end); + break; + case rYearlyMonth: + count = yearlyMonthCalc(END_DATE_AND_COUNT, end); + break; + case rYearlyDay: + count = yearlyDayCalc(END_DATE_AND_COUNT, end); + break; + case rYearlyPos: + count = yearlyPosCalc(END_DATE_AND_COUNT, end); + break; + default: + // catch-all. Should never get here. + kdDebug(5800) << "Control should never reach here in endDate()!" << endl; + break; + } + } + if (!count) + return QDateTime(); // error - there is no recurrence + return QDateTime(end, mRecurStart.time()); +} + +int Recurrence::durationTo(const QDate &date) const +{ + QDate d = date; + return recurCalc(COUNT_TO_DATE, d); +} + +int Recurrence::durationTo(const QDateTime &datetime) const +{ + QDateTime dt = datetime; + return recurCalc(COUNT_TO_DATE, dt); +} + +void Recurrence::unsetRecurs() +{ + if (mRecurReadOnly) return; + recurs = rNone; + rMonthPositions.clear(); + rMonthDays.clear(); + rYearNums.clear(); +} + +void Recurrence::setRecurStart(const QDateTime &start) +{ + mRecurStart = start; + mFloats = false; + switch (recurs) + { + case rMinutely: + case rHourly: + break; + case rDaily: + case rWeekly: + case rMonthlyPos: + case rMonthlyDay: + case rYearlyMonth: + case rYearlyDay: + case rYearlyPos: + default: + rEndDateTime.setTime(start.time()); + break; + } +} + +void Recurrence::setRecurStart(const QDate &start) +{ + mRecurStart.setDate(start); + mRecurStart.setTime(QTime(0,0,0)); + switch (recurs) + { + case rMinutely: + case rHourly: + break; + case rDaily: + case rWeekly: + case rMonthlyPos: + case rMonthlyDay: + case rYearlyMonth: + case rYearlyDay: + case rYearlyPos: + default: + mFloats = true; + break; + } +} + +void Recurrence::setFloats(bool f) +{ + switch (recurs) + { + case rDaily: + case rWeekly: + case rMonthlyPos: + case rMonthlyDay: + case rYearlyMonth: + case rYearlyDay: + case rYearlyPos: + break; + case rMinutely: + case rHourly: + default: + return; // can't set sub-daily to floating + } + mFloats = f; + if (f) { + mRecurStart.setTime(QTime(0,0,0)); + rEndDateTime.setTime(QTime(0,0,0)); + } +} + +int Recurrence::frequency() const +{ + return rFreq; +} + +int Recurrence::duration() const +{ + return rDuration; +} + +void Recurrence::setDuration(int _rDuration) +{ + if (mRecurReadOnly) return; + if (_rDuration > 0) { + rDuration = _rDuration; + // Compatibility mode is only needed when reading the calendar in ICalFormatImpl, + // so explicitly setting the duration means no backwards compatibility is needed. + mCompatDuration = 0; + } +} + +QString Recurrence::endDateStr(bool shortfmt) const +{ + return KGlobal::locale()->formatDate(rEndDateTime.date(),shortfmt); +} + +const QBitArray &Recurrence::days() const +{ + return rDays; +} + +const QPtrList<Recurrence::rMonthPos> &Recurrence::monthPositions() const +{ + return rMonthPositions; +} + +const QPtrList<Recurrence::rMonthPos> &Recurrence::yearMonthPositions() const +{ + return rMonthPositions; +} + +const QPtrList<int> &Recurrence::monthDays() const +{ + return rMonthDays; +} + +void Recurrence::setMinutely(int _rFreq, int _rDuration) +{ + if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1) + return; + setDailySub(rMinutely, _rFreq, _rDuration); +} + +void Recurrence::setMinutely(int _rFreq, const QDateTime &_rEndDateTime) +{ + if (mRecurReadOnly) return; + rEndDateTime = _rEndDateTime; + setDailySub(rMinutely, _rFreq, 0); +} + +void Recurrence::setHourly(int _rFreq, int _rDuration) +{ + if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1) + return; + setDailySub(rHourly, _rFreq, _rDuration); +} + +void Recurrence::setHourly(int _rFreq, const QDateTime &_rEndDateTime) +{ + if (mRecurReadOnly) return; + rEndDateTime = _rEndDateTime; + setDailySub(rHourly, _rFreq, 0); +} + +void Recurrence::setDaily(int _rFreq, int _rDuration) +{ + if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1) + return; + setDailySub(rDaily, _rFreq, _rDuration); +} + +void Recurrence::setDaily(int _rFreq, const QDate &_rEndDate) +{ + if (mRecurReadOnly) return; + rEndDateTime.setDate(_rEndDate); + rEndDateTime.setTime(mRecurStart.time()); + setDailySub(rDaily, _rFreq, 0); +} + +void Recurrence::setWeekly(int _rFreq, const QBitArray &_rDays, + int _rDuration, int _rWeekStart) +{ + if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1) + return; + recurs = rWeekly; + + rFreq = _rFreq; + rDays = _rDays; + rWeekStart = _rWeekStart; + rDuration = _rDuration; + if (mCompatVersion < 310 && _rDuration > 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. + mCompatDuration = _rDuration; + int weeks = ((mCompatDuration-1+mRecurExDatesCount)*7) + (7 - mRecurStart.date().dayOfWeek()); + QDate end(mRecurStart.date().addDays(weeks * rFreq)); + rDuration = INT_MAX; // ensure that weeklyCalc() does its job correctly + rDuration = weeklyCalc(COUNT_TO_DATE, end); + } else { + mCompatDuration = 0; + } + rMonthPositions.clear(); + rMonthDays.clear(); + if (mParent) mParent->updated(); +} + +void Recurrence::setWeekly(int _rFreq, const QBitArray &_rDays, + const QDate &_rEndDate, int _rWeekStart) +{ + if (mRecurReadOnly) return; + recurs = rWeekly; + + rFreq = _rFreq; + rDays = _rDays; + rWeekStart = _rWeekStart; + rEndDateTime.setDate(_rEndDate); + rEndDateTime.setTime(mRecurStart.time()); + rDuration = 0; // set to 0 because there is an end date + mCompatDuration = 0; + rMonthPositions.clear(); + rMonthDays.clear(); + rYearNums.clear(); + if (mParent) mParent->updated(); +} + +void Recurrence::setMonthly(short type, int _rFreq, int _rDuration) +{ + if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1) + return; + recurs = type; + + rFreq = _rFreq; + rDuration = _rDuration; + if (mCompatVersion < 310) + mCompatDuration = (_rDuration > 0) ? _rDuration : 0; + rYearNums.clear(); + if (mParent) mParent->updated(); +} + +void Recurrence::setMonthly(short type, int _rFreq, + const QDate &_rEndDate) +{ + if (mRecurReadOnly) return; + recurs = type; + + rFreq = _rFreq; + rEndDateTime.setDate(_rEndDate); + rEndDateTime.setTime(mRecurStart.time()); + rDuration = 0; // set to 0 because there is an end date + mCompatDuration = 0; + rYearNums.clear(); + if (mParent) mParent->updated(); +} + +void Recurrence::addMonthlyPos(short _rPos, const QBitArray &_rDays) +{ + if (recurs == rMonthlyPos) + addMonthlyPos_(_rPos, _rDays); +} + +void Recurrence::addMonthlyPos_(short _rPos, const QBitArray &_rDays) +{ + if (mRecurReadOnly + || _rPos == 0 || _rPos > 5 || _rPos < -5) // invalid week number + return; + + for (rMonthPos* it = rMonthPositions.first(); it; it = rMonthPositions.next()) { + int itPos = it->negative ? -it->rPos : it->rPos; + if (_rPos == itPos) { + // This week is already in the list. + // Combine the specified days with those in the list. + it->rDays |= _rDays; + if (mParent) mParent->updated(); + return; + } + } + // Add the new position to the list + rMonthPos *tmpPos = new rMonthPos; + if (_rPos > 0) { + tmpPos->rPos = _rPos; + tmpPos->negative = false; + } else { + tmpPos->rPos = -_rPos; // take abs() + tmpPos->negative = true; + } + tmpPos->rDays = _rDays; + tmpPos->rDays.detach(); + rMonthPositions.append(tmpPos); + + if (mCompatVersion < 310 && mCompatDuration > 0) { + // Backwards compatibility for KDE < 3.1. + // rDuration was set to the number of time periods to recur. + // Convert this to the number of occurrences. + int monthsAhead = (mCompatDuration-1+mRecurExDatesCount) * rFreq; + int month = mRecurStart.date().month() - 1 + monthsAhead; + QDate end(mRecurStart.date().year() + month/12, month%12 + 1, 31); + rDuration = INT_MAX; // ensure that recurCalc() does its job correctly + rDuration = recurCalc(COUNT_TO_DATE, end); + } + + if (mParent) mParent->updated(); +} + +void Recurrence::addMonthlyDay(short _rDay) +{ + if (mRecurReadOnly || recurs != rMonthlyDay + || _rDay == 0 || _rDay > 31 || _rDay < -31) // invalid day number + return; + for (int* it = rMonthDays.first(); it; it = rMonthDays.next()) { + if (_rDay == *it) + return; // this day is already in the list - avoid duplication + } + int *tmpDay = new int; + *tmpDay = _rDay; + rMonthDays.append(tmpDay); + + if (mCompatVersion < 310 && mCompatDuration > 0) { + // Backwards compatibility for KDE < 3.1. + // rDuration was set to the number of time periods to recur. + // Convert this to the number of occurrences. + int monthsAhead = (mCompatDuration-1+mRecurExDatesCount) * rFreq; + int month = mRecurStart.date().month() - 1 + monthsAhead; + QDate end(mRecurStart.date().year() + month/12, month%12 + 1, 31); + rDuration = INT_MAX; // ensure that recurCalc() does its job correctly + rDuration = recurCalc(COUNT_TO_DATE, end); + } + + if (mParent) mParent->updated(); +} + +void Recurrence::setYearly(int type, int _rFreq, int _rDuration) +{ + if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1) + return; + if (mCompatVersion < 310) + mCompatDuration = (_rDuration > 0) ? _rDuration : 0; + setYearly_(type, mFeb29YearlyDefaultType, _rFreq, _rDuration); +} + +void Recurrence::setYearly(int type, int _rFreq, const QDate &_rEndDate) +{ + if (mRecurReadOnly) return; + rEndDateTime.setDate(_rEndDate); + rEndDateTime.setTime(mRecurStart.time()); + mCompatDuration = 0; + setYearly_(type, mFeb29YearlyDefaultType, _rFreq, 0); +} + +void Recurrence::setYearlyByDate(Feb29Type type, int _rFreq, int _rDuration) +{ + if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1) + return; + if (mCompatVersion < 310) + mCompatDuration = (_rDuration > 0) ? _rDuration : 0; + setYearly_(rYearlyMonth, type, _rFreq, _rDuration); +} + +void Recurrence::setYearlyByDate(Feb29Type type, int _rFreq, const QDate &_rEndDate) +{ + if (mRecurReadOnly) return; + rEndDateTime.setDate(_rEndDate); + rEndDateTime.setTime(mRecurStart.time()); + mCompatDuration = 0; + setYearly_(rYearlyMonth, type, _rFreq, 0); +} + +void Recurrence::addYearlyMonthPos(short _rPos, const QBitArray &_rDays) +{ + if (recurs == rYearlyPos) + addMonthlyPos_(_rPos, _rDays); +} + +const QPtrList<int> &Recurrence::yearNums() const +{ + return rYearNums; +} + +void Recurrence::addYearlyNum(short _rNum) +{ + if (mRecurReadOnly + || (recurs != rYearlyMonth && recurs != rYearlyDay && recurs != rYearlyPos) + || _rNum <= 0) // invalid day/month number + return; + + if (mCompatVersion < 310 && mCompatRecurs == rYearlyDay) { + // Backwards compatibility for KDE < 3.1. + // Dates were stored as day numbers, with a fiddle to take account of leap years. + // Convert the day number to a month. + if (_rNum <= 0 || _rNum > 366 || (_rNum == 366 && mRecurStart.date().daysInYear() < 366)) + return; // invalid day number + _rNum = QDate(mRecurStart.date().year(), 1, 1).addDays(_rNum - 1).month(); + } else + if ((recurs == rYearlyMonth || recurs == rYearlyPos) && _rNum > 12 + || recurs == rYearlyDay && _rNum > 366) + return; // invalid day number + + uint i = 0; + for (int* it = rYearNums.first(); it && _rNum >= *it; it = rYearNums.next()) { + if (_rNum == *it) + return; // this day/month is already in the list - avoid duplication + ++i; + } + + int *tmpNum = new int; + *tmpNum = _rNum; + rYearNums.insert(i, tmpNum); // insert the day/month in a sorted position + + if (mCompatVersion < 310 && mCompatDuration > 0) { + // Backwards compatibility for KDE < 3.1. + // rDuration was set to the number of time periods to recur. + // Convert this to the number of occurrences. + QDate end(mRecurStart.date().year() + (mCompatDuration-1+mRecurExDatesCount)*rFreq, 12, 31); + rDuration = INT_MAX; // ensure that recurCalc() does its job correctly + rDuration = recurCalc(COUNT_TO_DATE, end); + } + + if (mParent) mParent->updated(); +} + + +QDateTime Recurrence::getNextDateTime(const QDateTime &preDateTime, bool *last) const +{ + if (last) + *last = false; + int freq; + switch (recurs) + { + case rMinutely: + freq = rFreq * 60; + break; + case rHourly: + freq = rFreq * 3600; + break; + case rDaily: + case rWeekly: + case rMonthlyPos: + case rMonthlyDay: + case rYearlyMonth: + case rYearlyDay: + case rYearlyPos: { + QDate preDate = preDateTime.date(); + if (!mFloats && mRecurStart.time() > preDateTime.time()) + preDate = preDate.addDays(-1); + return QDateTime(getNextDateNoTime(preDate, last), mRecurStart.time()); + } + default: + return QDateTime(); + } + + // It's a sub-daily recurrence + if (preDateTime < mRecurStart) + return mRecurStart; + int count = mRecurStart.secsTo(preDateTime) / freq + 2; + if (rDuration > 0) { + if (count > rDuration) + return QDateTime(); + if (last && count == rDuration) + *last = true; + } + QDateTime endtime = mRecurStart.addSecs((count - 1)*freq); + if (rDuration == 0) { + if (endtime > rEndDateTime) + return QDateTime(); + if (last && endtime == rEndDateTime) + *last = true; + } + return endtime; +} + +QDate Recurrence::getNextDate(const QDate &preDate, bool *last) const +{ + if (last) + *last = false; + switch (recurs) + { + case rMinutely: + case rHourly: + return getNextDateTime(QDateTime(preDate, QTime(23,59,59)), last).date(); + case rDaily: + case rWeekly: + case rMonthlyPos: + case rMonthlyDay: + case rYearlyMonth: + case rYearlyDay: + case rYearlyPos: + return getNextDateNoTime(preDate, last); + default: + return QDate(); + } +} + + +QDateTime Recurrence::getPreviousDateTime(const QDateTime &afterDateTime, bool *last) const +{ + if (last) + *last = false; + int freq; + switch (recurs) + { + case rMinutely: + freq = rFreq * 60; + break; + case rHourly: + freq = rFreq * 3600; + break; + case rDaily: + case rWeekly: + case rMonthlyPos: + case rMonthlyDay: + case rYearlyMonth: + case rYearlyDay: + case rYearlyPos: { + QDate afterDate = afterDateTime.date(); + if (!mFloats && mRecurStart.time() < afterDateTime.time()) + afterDate = afterDate.addDays(1); + return QDateTime(getPreviousDateNoTime(afterDate, last), mRecurStart.time()); + } + default: + return QDateTime(); + } + + // It's a sub-daily recurrence + if (afterDateTime <= mRecurStart) + return QDateTime(); + int count = (mRecurStart.secsTo(afterDateTime) - 1) / freq + 1; + if (rDuration > 0) { + if (count > rDuration) + count = rDuration; + if (last && count == rDuration) + *last = true; + } + QDateTime endtime = mRecurStart.addSecs((count - 1)*freq); + if (rDuration == 0) { + if (endtime > rEndDateTime) + endtime = rEndDateTime; + if (last && endtime == rEndDateTime) + *last = true; + } + return endtime; +} + +QDate Recurrence::getPreviousDate(const QDate &afterDate, bool *last) const +{ + if (last) + *last = false; + switch (recurs) + { + case rMinutely: + case rHourly: + return getPreviousDateTime(QDateTime(afterDate, QTime(0,0,0)), last).date(); + case rDaily: + case rWeekly: + case rMonthlyPos: + case rMonthlyDay: + case rYearlyMonth: + case rYearlyDay: + case rYearlyPos: + return getPreviousDateNoTime(afterDate, last); + default: + return QDate(); + } +} + + +/***************************** PROTECTED FUNCTIONS ***************************/ + +bool Recurrence::recursSecondly(const QDate &qd, int secondFreq) const +{ + if ((qd >= mRecurStart.date()) && + ((rDuration > 0) && (qd <= endDate()) || + ((rDuration == 0) && (qd <= rEndDateTime.date())) || + (rDuration == -1))) { + // The date queried falls within the range of the event. + if (secondFreq < 24*3600) + return true; // the event recurs at least once each day + int after = mRecurStart.secsTo(QDateTime(qd)); + if (after / secondFreq != (after + 24*3600) / secondFreq) + return true; + } + return false; +} + +bool Recurrence::recursMinutelyAt(const QDateTime &dt, int minuteFreq) const +{ + if ((dt >= mRecurStart) && + ((rDuration > 0) && (dt <= endDateTime()) || + ((rDuration == 0) && (dt <= rEndDateTime)) || + (rDuration == -1))) { + // The time queried falls within the range of the event. + if (((mRecurStart.secsTo(dt) / 60) % minuteFreq) == 0) + return true; + } + return false; +} + +bool Recurrence::recursDaily(const QDate &qd) const +{ + QDate dStart = mRecurStart.date(); + if ((dStart.daysTo(qd) % rFreq) == 0) { + // The date is a day which recurs + if (qd >= dStart + && ((rDuration > 0 && qd <= endDate()) || + (rDuration == 0 && qd <= rEndDateTime.date()) || + rDuration == -1)) { + // The date queried falls within the range of the event. + return true; + } + } + return false; +} + +bool Recurrence::recursWeekly(const QDate &qd) const +{ + QDate dStart = mRecurStart.date(); + if ((dStart.daysTo(qd)/7) % rFreq == 0) { + // The date is in a week which recurs + if (qd >= dStart + && ((rDuration > 0 && qd <= endDate()) || + (rDuration == 0 && qd <= rEndDateTime.date()) || + rDuration == -1)) { + // The date queried falls within the range of the event. + // check if the bits set match today. + int i = qd.dayOfWeek()-1; + if (rDays.testBit((uint) i)) + return true; + } + } + return false; +} + +bool Recurrence::recursMonthly(const QDate &qd) const +{ + QDate dStart = mRecurStart.date(); + int year = qd.year(); + int month = qd.month(); + int day = qd.day(); + // calculate how many months ahead this date is from the original + // event's date + int monthsAhead = (year - dStart.year()) * 12 + (month - dStart.month()); + if ((monthsAhead % rFreq) == 0) { + // The date is in a month which recurs + if (qd >= dStart + && ((rDuration > 0 && qd <= endDate()) || + (rDuration == 0 && qd <= rEndDateTime.date()) || + rDuration == -1)) { + // The date queried falls within the range of the event. + QValueList<int> days; + int daysInMonth = qd.daysInMonth(); + if (recurs == rMonthlyDay) + getMonthlyDayDays(days, daysInMonth); + else if (recurs == rMonthlyPos) + getMonthlyPosDays(days, daysInMonth, QDate(year, month, 1).dayOfWeek()); + for (QValueList<int>::Iterator it = days.begin(); it != days.end(); ++it) { + if (*it == day) + return true; + } + // no dates matched + } + } + return false; +} + +bool Recurrence::recursYearlyByMonth(const QDate &qd) const +{ + QDate dStart = mRecurStart.date(); + int startDay = dStart.day(); + int qday = qd.day(); + int qmonth = qd.month(); + int qyear = qd.year(); + bool match = (qday == startDay); + if (!match && startDay == 29 && dStart.month() == 2) { + // It's a recurrence on February 29th + switch (mFeb29YearlyType) { + case rFeb28: + if (qday == 28 && qmonth == 2 && !QDate::leapYear(qyear)) + match = true; + break; + case rMar1: + if (qday == 1 && qmonth == 3 && !QDate::leapYear(qyear)) { + qmonth = 2; + match = true; + } + break; + case rFeb29: + break; + } + } + + if (match) { + // The day of the month matches. Calculate how many years ahead + // this date is from the original event's date. + int yearsAhead = (qyear - dStart.year()); + if (yearsAhead % rFreq == 0) { + // The date is in a year which recurs + if (qd >= dStart + && ((rDuration > 0 && qd <= endDate()) || + (rDuration == 0 && qd <= rEndDateTime.date()) || + rDuration == -1)) { + // The date queried falls within the range of the event. + int i = qmonth; + for (QPtrListIterator<int> qlin(rYearNums); qlin.current(); ++qlin) { + if (i == *qlin.current()) + return true; + } + } + } + } + return false; +} + +bool Recurrence::recursYearlyByPos(const QDate &qd) const +{ + QDate dStart = mRecurStart.date(); + int year = qd.year(); + int month = qd.month(); + int day = qd.day(); + // calculate how many years ahead this date is from the original + // event's date + int yearsAhead = (year - dStart.year()); + if (yearsAhead % rFreq == 0) { + // The date is in a year which recurs + if (qd >= dStart + && ((rDuration > 0 && qd <= endDate()) || + (rDuration == 0 && qd <= rEndDateTime.date()) || + rDuration == -1)) { + // The date queried falls within the range of the event. + for (QPtrListIterator<int> qlin(rYearNums); qlin.current(); ++qlin) { + if (month == *qlin.current()) { + // The month recurs + QValueList<int> days; + getMonthlyPosDays(days, qd.daysInMonth(), QDate(year, month, 1).dayOfWeek()); + for (QValueList<int>::Iterator it = days.begin(); it != days.end(); ++it) { + if (*it == day) + return true; + } + } + } + } + } + return false; +} + +bool Recurrence::recursYearlyByDay(const QDate &qd) const +{ + QDate dStart = mRecurStart.date(); + // calculate how many years ahead this date is from the original + // event's date + int yearsAhead = (qd.year() - dStart.year()); + if (yearsAhead % rFreq == 0) { + // The date is in a year which recurs + if (qd >= dStart + && ((rDuration > 0 && qd <= endDate()) || + (rDuration == 0 && qd <= rEndDateTime.date()) || + rDuration == -1)) { + // The date queried falls within the range of the event. + int i = qd.dayOfYear(); + for (QPtrListIterator<int> qlin(rYearNums); qlin.current(); ++qlin) { + if (i == *qlin.current()) + return true; + } + } + } + return false; +} + +/* Get the date of the next recurrence, after the specified date. + * If 'last' is non-null, '*last' is set to true if the next recurrence is the + * last recurrence, else false. + * Reply = date of next recurrence, or invalid date if none. + */ +QDate Recurrence::getNextDateNoTime(const QDate &preDate, bool *last) const +{ + if (last) + *last = false; + QDate dStart = mRecurStart.date(); + if (preDate < dStart) + return dStart; + QDate earliestDate = preDate.addDays(1); + QDate nextDate; + + switch (recurs) { + case rDaily: + nextDate = dStart.addDays((dStart.daysTo(preDate)/rFreq + 1) * rFreq); + break; + + case rWeekly: { + QDate start = dStart.addDays(1 - dStart.dayOfWeek()); // start of week for dStart + int earliestDayOfWeek = earliestDate.dayOfWeek(); + int weeksAhead = start.daysTo(earliestDate) / 7; + int notThisWeek = weeksAhead % rFreq; // zero if this week is a recurring week + weeksAhead -= notThisWeek; // latest week which recurred + int weekday = 0; + // First check for any remaining day this week, if this week is a recurring week + if (!notThisWeek) + weekday = getFirstDayInWeek(earliestDayOfWeek); + // Check for a day in the next scheduled week + if (!weekday && earliestDayOfWeek > 1) + weekday = getFirstDayInWeek(rWeekStart) + rFreq*7; + if (weekday) + nextDate = start.addDays(weeksAhead*7 + weekday - 1); + break; + } + case rMonthlyDay: + case rMonthlyPos: { + int startYear = dStart.year(); + int startMonth = dStart.month(); // 1..12 + int earliestYear = earliestDate.year(); + int monthsAhead = (earliestYear - startYear)*12 + earliestDate.month() - startMonth; + int notThisMonth = monthsAhead % rFreq; // zero if this month is a recurring month + monthsAhead -= notThisMonth; // latest month which recurred + // Check for the first later day in the current month + if (!notThisMonth) + nextDate = getFirstDateInMonth(earliestDate); + if (!nextDate.isValid() && earliestDate.day() > 1) { + // Check for a day in the next scheduled month + int months = startMonth - 1 + monthsAhead + rFreq; + nextDate = getFirstDateInMonth(QDate(startYear + months/12, months%12 + 1, 1)); + } + break; + } + case rYearlyMonth: + case rYearlyPos: + case rYearlyDay: { + int startYear = dStart.year(); + int yearsAhead = earliestDate.year() - startYear; + int notThisYear = yearsAhead % rFreq; // zero if this year is a recurring year + yearsAhead -= notThisYear; // latest year which recurred + // Check for the first later date in the current year + if (!notThisYear) + nextDate = getFirstDateInYear(earliestDate); + // Check for a date in the next scheduled year + if (!nextDate.isValid() && earliestDate.dayOfYear() > 1) + nextDate = getFirstDateInYear(QDate(startYear + yearsAhead + rFreq, 1, 1)); + break; + } + case rNone: + default: + return QDate(); + } + + if (rDuration >= 0 && nextDate.isValid()) { + // Check that the date found is within the range of the recurrence + QDate end = endDate(); + if (nextDate > end) + return QDate(); + if (last && nextDate == end) + *last = true; + } + return nextDate; +} + +/* Get the date of the last previous recurrence, before the specified date. + * Reply = date of previous recurrence, or invalid date if none. + */ +QDate Recurrence::getPreviousDateNoTime(const QDate &afterDate, bool *last) const +{ + if (last) + *last = false; + QDate dStart = mRecurStart.date(); + QDate latestDate = afterDate.addDays(-1); + if (latestDate < dStart) + return QDate(); + QDate prevDate; + + switch (recurs) { + case rDaily: + prevDate = dStart.addDays((dStart.daysTo(latestDate) / rFreq) * rFreq); + break; + + case rWeekly: { + QDate start = dStart.addDays(1 - dStart.dayOfWeek()); // start of week for dStart + int latestDayOfWeek = latestDate.dayOfWeek(); + int weeksAhead = start.daysTo(latestDate) / 7; + int notThisWeek = weeksAhead % rFreq; // zero if this week is a recurring week + weeksAhead -= notThisWeek; // latest week which recurred + int weekday = 0; + // First check for any previous day this week, if this week is a recurring week + if (!notThisWeek) + weekday = getLastDayInWeek(latestDayOfWeek); + // Check for a day in the previous scheduled week + if (!weekday) { + int weekEnd = (rWeekStart + 5)%7 + 1; + if (latestDayOfWeek < weekEnd) { + if (!notThisWeek) + weeksAhead -= rFreq; + weekday = getLastDayInWeek(weekEnd); + } + } + if (weekday) + prevDate = start.addDays(weeksAhead*7 + weekday - 1); + break; + } + case rMonthlyDay: + case rMonthlyPos: { + int startYear = dStart.year(); + int startMonth = dStart.month(); // 1..12 + int latestYear = latestDate.year(); + int monthsAhead = (latestYear - startYear)*12 + latestDate.month() - startMonth; + int notThisMonth = monthsAhead % rFreq; // zero if this month is a recurring month + monthsAhead -= notThisMonth; // latest month which recurred + // Check for the last earlier day in the current month + if (!notThisMonth) + prevDate = getLastDateInMonth(latestDate); + if (!prevDate.isValid() && latestDate.day() < latestDate.daysInMonth()) { + // Check for a day in the previous scheduled month + if (!notThisMonth) + monthsAhead -= rFreq; + int months = startMonth + monthsAhead; // get the month after the one that recurs + prevDate = getLastDateInMonth(QDate(startYear + months/12, months%12 + 1, 1).addDays(-1)); + } + break; + } + case rYearlyMonth: + case rYearlyPos: + case rYearlyDay: { + int startYear = dStart.year(); + int yearsAhead = latestDate.year() - startYear; + int notThisYear = yearsAhead % rFreq; // zero if this year is a recurring year + yearsAhead -= notThisYear; // latest year which recurred + // Check for the first later date in the current year + if (!notThisYear) + prevDate = getLastDateInYear(latestDate); + if (!prevDate.isValid() && latestDate.dayOfYear() < latestDate.daysInYear()) { + // Check for a date in the next scheduled year + if (!notThisYear) + yearsAhead -= rFreq; + prevDate = getLastDateInYear(QDate(startYear + yearsAhead, 12, 31)); + } + break; + } + case rNone: + default: + return QDate(); + } + + if (prevDate.isValid()) { + // Check that the date found is within the range of the recurrence + if (prevDate < dStart) + return QDate(); + if (rDuration >= 0) { + QDate end = endDate(); + if (prevDate >= end) { + if (last) + *last = true; + return end; + } + } + } + return prevDate; +} + +void Recurrence::setDailySub(short type, int freq, int duration) +{ + recurs = type; + rFreq = freq; + rDuration = duration; + rMonthPositions.clear(); + rMonthDays.clear(); + rYearNums.clear(); + if (type != rDaily) + mFloats = false; // sub-daily types can't be floating + + if (mParent) mParent->updated(); +} + +void Recurrence::setYearly_(short type, Feb29Type feb29type, int freq, int duration) +{ + recurs = type; + if (mCompatVersion < 310 && type == rYearlyDay) { + mCompatRecurs = rYearlyDay; + recurs = rYearlyMonth; // convert old yearly-by-day to yearly-by-month + feb29type = rMar1; // retain the same day number in the year + } + + mFeb29YearlyType = feb29type; + rFreq = freq; + rDuration = duration; + if (type != rYearlyPos) + rMonthPositions.clear(); + rMonthDays.clear(); + if (mParent) mParent->updated(); +} + +int Recurrence::recurCalc(PeriodFunc func, QDateTime &endtime) const +{ + QDate enddate = endtime.date(); + switch (func) { + case END_DATE_AND_COUNT: + if (rDuration < 0) { + endtime = QDateTime(); + return 0; // infinite recurrence + } + if (rDuration == 0) { + endtime = rEndDateTime; + func = COUNT_TO_DATE; + } + break; + case COUNT_TO_DATE: + // Count recurrences up to and including the specified date/time. + if (endtime < mRecurStart) + return 0; + if (rDuration == 0 && endtime > rEndDateTime) + enddate = rEndDateTime.date(); + else if (!mFloats && mRecurStart.time() > endtime.time()) + enddate = enddate.addDays(-1); + break; + case NEXT_AFTER_DATE: + // Find next recurrence AFTER endtime + if (endtime < mRecurStart) { + endtime = mRecurStart; + return 1; + } + if (rDuration == 0 && endtime >= rEndDateTime) { + endtime = QDateTime(); + return 0; + } + if (!mFloats && mRecurStart.time() > endtime.time()) + enddate = enddate.addDays(-1); + break; + default: + endtime = QDateTime(); + return 0; + } + + int count = 0; // default = error + bool timed = false; + switch (recurs) { + case rMinutely: + timed = true; + count = secondlyCalc(func, endtime, rFreq*60); + break; + case rHourly: + timed = true; + count = secondlyCalc(func, endtime, rFreq*3600); + break; + case rDaily: + count = dailyCalc(func, enddate); + break; + case rWeekly: + count = weeklyCalc(func, enddate); + break; + case rMonthlyPos: + case rMonthlyDay: + count = monthlyCalc(func, enddate); + break; + case rYearlyMonth: + count = yearlyMonthCalc(func, enddate); + break; + case rYearlyPos: + count = yearlyPosCalc(func, enddate); + break; + case rYearlyDay: + count = yearlyDayCalc(func, enddate); + break; + default: + break; + } + + switch (func) { + case END_DATE_AND_COUNT: + case NEXT_AFTER_DATE: + if (count == 0) + endtime = QDateTime(); + else if (!timed) { + endtime.setDate(enddate); + endtime.setTime(mRecurStart.time()); + } + break; + case COUNT_TO_DATE: + break; + } + return count; +} + +int Recurrence::recurCalc(PeriodFunc func, QDate &enddate) const +{ + QDateTime endtime(enddate, QTime(23,59,59)); + switch (func) { + case END_DATE_AND_COUNT: + if (rDuration < 0) { + enddate = QDate(); + return 0; // infinite recurrence + } + if (rDuration == 0) { + enddate = rEndDateTime.date(); + func = COUNT_TO_DATE; + } + break; + case COUNT_TO_DATE: + // Count recurrences up to and including the specified date. + if (enddate < mRecurStart.date()) + return 0; + if (rDuration == 0 && enddate > rEndDateTime.date()) { + enddate = rEndDateTime.date(); + endtime.setDate(enddate); + } + break; + case NEXT_AFTER_DATE: + if (enddate < mRecurStart.date()) { + enddate = mRecurStart.date(); + return 1; + } + if (rDuration == 0 && enddate >= rEndDateTime.date()) { + enddate = QDate(); + return 0; + } + break; + default: + enddate = QDate(); + return 0; + } + + int count = 0; // default = error + bool timed = false; + switch (recurs) { + case rMinutely: + timed = true; + count = secondlyCalc(func, endtime, rFreq*60); + break; + case rHourly: + timed = true; + count = secondlyCalc(func, endtime, rFreq*3600); + break; + case rDaily: + count = dailyCalc(func, enddate); + break; + case rWeekly: + count = weeklyCalc(func, enddate); + break; + case rMonthlyPos: + case rMonthlyDay: + count = monthlyCalc(func, enddate); + break; + case rYearlyMonth: + count = yearlyMonthCalc(func, enddate); + break; + case rYearlyPos: + count = yearlyPosCalc(func, enddate); + break; + case rYearlyDay: + count = yearlyDayCalc(func, enddate); + break; + default: + break; + } + + switch (func) { + case END_DATE_AND_COUNT: + case NEXT_AFTER_DATE: + if (count == 0) + endtime = QDate(); + else if (timed) + enddate = endtime.date(); + break; + case COUNT_TO_DATE: + break; + } + return count; +} + +/* Find count and, depending on 'func', the end date/time of a secondly recurrence. + * Reply = total number of occurrences up to 'endtime', or 0 if error. + * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'endtime' is updated to the + * recurrence end date/time. + */ +int Recurrence::secondlyCalc(PeriodFunc func, QDateTime &endtime, int freq) const +{ + switch (func) { + case END_DATE_AND_COUNT: + endtime = mRecurStart.addSecs((rDuration + mRecurExDatesCount - 1) * freq); + return rDuration + mRecurExDatesCount; + case COUNT_TO_DATE: { + int n = mRecurStart.secsTo(endtime)/freq + 1; + if (rDuration > 0 && n > rDuration + mRecurExDatesCount) + return rDuration + mRecurExDatesCount; + return n; + } + case NEXT_AFTER_DATE: { + int count = mRecurStart.secsTo(endtime) / freq + 2; + if (rDuration > 0 && count > rDuration) + return 0; + endtime = mRecurStart.addSecs((count - 1)*freq); + return count; + } + } + return 0; +} + +/* Find count and, depending on 'func', the end date of a daily recurrence. + * Reply = total number of occurrences up to 'enddate', or 0 if error. + * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the + * recurrence end date. + */ +int Recurrence::dailyCalc(PeriodFunc func, QDate &enddate) const +{ + QDate dStart = mRecurStart.date(); + switch (func) { + case END_DATE_AND_COUNT: + enddate = dStart.addDays((rDuration + mRecurExDatesCount - 1) * rFreq); + return rDuration + mRecurExDatesCount; + case COUNT_TO_DATE: { + int n = dStart.daysTo(enddate)/rFreq + 1; + if (rDuration > 0 && n > rDuration + mRecurExDatesCount) + return rDuration + mRecurExDatesCount; + return n; + } + case NEXT_AFTER_DATE: { + int count = dStart.daysTo(enddate) / rFreq + 2; + if (rDuration > 0 && count > rDuration) + return 0; + enddate = dStart.addDays((count - 1)*rFreq); + return count; + } + } + return 0; +} + +/* Find count and, depending on 'func', the end date of a weekly recurrence. + * Reply = total number of occurrences up to 'enddate', or 0 if error. + * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the + * recurrence end date. + */ +int Recurrence::weeklyCalc(PeriodFunc func, QDate &enddate) const +{ + int daysPerWeek = 0; + for (int i = 0; i < 7; ++i) { + if (rDays.testBit((uint)i)) + ++daysPerWeek; + } + if (!daysPerWeek) + return 0; // there are no days to recur on + + switch (func) { + case END_DATE_AND_COUNT: + return weeklyCalcEndDate(enddate, daysPerWeek); + case COUNT_TO_DATE: + return weeklyCalcToDate(enddate, daysPerWeek); + case NEXT_AFTER_DATE: + return weeklyCalcNextAfter(enddate, daysPerWeek); + } + return 0; +} + +int Recurrence::weeklyCalcEndDate(QDate &enddate, int daysPerWeek) const +{ + int startDayOfWeek = mRecurStart.date().dayOfWeek(); // 1..7 + int countGone = 0; + int daysGone = 0; + uint countTogo = rDuration + mRecurExDatesCount; + if (startDayOfWeek != rWeekStart) { + // Check what remains of the start week + for (int i = startDayOfWeek - 1; i != rWeekStart - 1; i = (i + 1) % 7) { + ++daysGone; + if (rDays.testBit((uint)i)) { + ++countGone; + if (--countTogo == 0) + break; + } + } + daysGone += 7 * (rFreq - 1); + } + if (countTogo) { + // Skip the remaining whole weeks + // Leave at least 1 recurrence remaining, in order to get its date + int wholeWeeks = (countTogo - 1) / daysPerWeek; + daysGone += wholeWeeks * 7 * rFreq; + countGone += wholeWeeks * daysPerWeek; + countTogo -= wholeWeeks * daysPerWeek; + // Check the last week in the recurrence + for (int i = rWeekStart - 1; ; i = (i + 1) % 7) { + ++daysGone; + if (rDays.testBit((uint)i)) { + ++countGone; + if (--countTogo == 0) + break; + } + } + } + enddate = mRecurStart.date().addDays(daysGone); + return countGone; +} + +int Recurrence::weeklyCalcToDate(const QDate &enddate, int daysPerWeek) const +{ + QDate dStart = mRecurStart.date(); + int startDayOfWeek = dStart.dayOfWeek(); // 1..7 + int countGone = 0; + int daysGone = 0; + int totalDays = dStart.daysTo(enddate) + 1; + int countMax = (rDuration > 0) ? rDuration + mRecurExDatesCount : INT_MAX; + + if (startDayOfWeek != rWeekStart) { + // Check what remains of the start week + for (int i = startDayOfWeek - 1; i != rWeekStart - 1; i = (i + 1) % 7) { + if (rDays.testBit((uint)i)) { + if (++countGone >= countMax) + return countMax; + } + if (++daysGone == totalDays) + return countGone; + } + daysGone += 7 * (rFreq - 1); + if (daysGone >= totalDays) + return countGone; + } + // Skip the remaining whole weeks + int wholeWeeks = (totalDays - daysGone) / 7; + countGone += (wholeWeeks / rFreq) * daysPerWeek; + if (countGone >= countMax) + return countMax; + daysGone += wholeWeeks * 7; + if (daysGone >= totalDays // have we reached the end date? + || wholeWeeks % rFreq) // is end week a recurrence week? + return countGone; + + // Check the last week in the recurrence + for (int i = rWeekStart - 1; ; i = (i + 1) % 7) { + if (rDays.testBit((uint)i)) { + if (++countGone >= countMax) + return countMax; + } + if (++daysGone == totalDays) + return countGone; + } + return countGone; +} + +int Recurrence::weeklyCalcNextAfter(QDate &enddate, int daysPerWeek) const +{ + QDate dStart = mRecurStart.date(); + int startDayOfWeek = dStart.dayOfWeek(); // 1..7 + int totalDays = dStart.daysTo(enddate) + 1; + uint countTogo = (rDuration > 0) ? rDuration + mRecurExDatesCount : UINT_MAX; + int countGone = 0; + int daysGone = 0; + int recurWeeks; + + if (startDayOfWeek != rWeekStart) { + // Check what remains of the start week + for (int i = startDayOfWeek - 1; i != rWeekStart - 1; i = (i + 1) % 7) { + ++daysGone; + if (rDays.testBit((uint)i)) { + ++countGone; + if (daysGone > totalDays) + goto ex; + if (--countTogo == 0) + return 0; + } + } + daysGone += 7 * (rFreq - 1); + } + + // Skip the remaining whole weeks + recurWeeks = (totalDays - daysGone) / (7 * rFreq); + if (recurWeeks) { + int n = recurWeeks * daysPerWeek; + if (static_cast<uint>(n) > countTogo) + return 0; // reached end of recurrence + countGone += n; + countTogo -= n; + daysGone += recurWeeks * 7 * rFreq; + } + + // Check the last week or two in the recurrence + for ( ; ; ) { + for (int i = rWeekStart - 1; ; i = (i + 1) % 7) { + ++daysGone; + if (rDays.testBit((uint)i)) { + ++countGone; + if (daysGone > totalDays) + goto ex; + if (--countTogo == 0) + return 0; + } + } + daysGone += 7 * (rFreq - 1); + } +ex: + enddate = dStart.addDays(daysGone); + return countGone; +} + +/* Find count and, depending on 'func', the end date of a monthly recurrence. + * Reply = total number of occurrences up to 'enddate', or 0 if error. + * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the + * recurrence end date. + */ +struct Recurrence::MonthlyData { + const Recurrence *recurrence; + int year; // current year + int month; // current month 0..11 + int day; // current day of month 1..31 + bool varies; // true if recurring days vary between different months + private: + QValueList<int> days28, days29, days30, days31; // recurring days in months of each length + QValueList<int> *recurDays[4]; + public: + MonthlyData(const Recurrence* r, const QDate &date) + : recurrence(r), year(date.year()), month(date.month()-1), day(date.day()) + { recurDays[0] = &days28; + recurDays[1] = &days29; + recurDays[2] = &days30; + recurDays[3] = &days31; + varies = (recurrence->recurs == rMonthlyPos) + ? true : recurrence->getMonthlyDayDays(days31, 31); + } + const QValueList<int>* dayList() const { + if (!varies) + return &days31; + QDate startOfMonth(year, month + 1, 1); + int daysInMonth = startOfMonth.daysInMonth(); + QValueList<int>* days = recurDays[daysInMonth - 28]; + if (recurrence->recurs == rMonthlyPos) + recurrence->getMonthlyPosDays(*days, daysInMonth, startOfMonth.dayOfWeek()); + else if (days->isEmpty()) + recurrence->getMonthlyDayDays(*days, daysInMonth); + return days; + } + int yearMonth() const { return year*12 + month; } + void addMonths(int diff) { month += diff; year += month / 12; month %= 12; } + QDate date() const { return QDate(year, month + 1, day); } +}; + +int Recurrence::monthlyCalc(PeriodFunc func, QDate &enddate) const +{ + if (recurs == rMonthlyPos && rMonthPositions.isEmpty() + || recurs == rMonthlyDay && rMonthDays.isEmpty()) + return 0; + + MonthlyData data(this, mRecurStart.date()); + switch (func) { + case END_DATE_AND_COUNT: + return monthlyCalcEndDate(enddate, data); + case COUNT_TO_DATE: + return monthlyCalcToDate(enddate, data); + case NEXT_AFTER_DATE: + return monthlyCalcNextAfter(enddate, data); + } + return 0; +} + +int Recurrence::monthlyCalcEndDate(QDate &enddate, MonthlyData &data) const +{ + uint countTogo = rDuration + mRecurExDatesCount; + int countGone = 0; + QValueList<int>::ConstIterator it; + const QValueList<int>* days = data.dayList(); + + if (data.day > 1) { + // Check what remains of the start month + for (it = days->begin(); it != days->end(); ++it) { + if (*it >= data.day) { + ++countGone; + if (--countTogo == 0) { + data.day = *it; + break; + } + } + } + if (countTogo) { + data.day = 1; + data.addMonths(rFreq); + } + } + if (countTogo) { + if (data.varies) { + // The number of recurrence days varies from month to month, + // so we need to check month by month. + for ( ; ; ) { + days = data.dayList(); + uint n = days->count(); // number of recurrence days in this month + if (n >= countTogo) + break; + countTogo -= n; + countGone += n; + data.addMonths(rFreq); + } + } else { + // The number of recurrences is the same every month, + // so skip the month-by-month check. + // Skip the remaining whole months, but leave at least + // 1 recurrence remaining, in order to get its date. + int daysPerMonth = days->count(); + int wholeMonths = (countTogo - 1) / daysPerMonth; + data.addMonths(wholeMonths * rFreq); + countGone += wholeMonths * daysPerMonth; + countTogo -= wholeMonths * daysPerMonth; + } + if (countTogo) { + // Check the last month in the recurrence + for (it = days->begin(); it != days->end(); ++it) { + ++countGone; + if (--countTogo == 0) { + data.day = *it; + break; + } + } + } + } + enddate = data.date(); + return countGone; +} + +int Recurrence::monthlyCalcToDate(const QDate &enddate, MonthlyData &data) const +{ + int countGone = 0; + int countMax = (rDuration > 0) ? rDuration + mRecurExDatesCount : INT_MAX; + int endYear = enddate.year(); + int endMonth = enddate.month() - 1; // zero-based + int endDay = enddate.day(); + int endYearMonth = endYear*12 + endMonth; + QValueList<int>::ConstIterator it; + const QValueList<int>* days = data.dayList(); + + if (data.day > 1) { + // Check what remains of the start month + for (it = days->begin(); it != days->end(); ++it) { + if (*it >= data.day) { + if (data.yearMonth() == endYearMonth && *it > endDay) + return countGone; + if (++countGone >= countMax) + return countMax; + } + } + data.day = 1; + data.addMonths(rFreq); + } + + if (data.varies) { + // The number of recurrence days varies from month to month, + // so we need to check month by month. + while (data.yearMonth() < endYearMonth) { + countGone += data.dayList()->count(); + if (countGone >= countMax) + return countMax; + data.addMonths(rFreq); + } + days = data.dayList(); + } else { + // The number of recurrences is the same every month, + // so skip the month-by-month check. + // Skip the remaining whole months. + int daysPerMonth = days->count(); + int wholeMonths = endYearMonth - data.yearMonth(); + countGone += (wholeMonths / rFreq) * daysPerMonth; + if (countGone >= countMax) + return countMax; + if (wholeMonths % rFreq) + return countGone; // end year isn't a recurrence year + data.year = endYear; + data.month = endMonth; + } + + // Check the last month in the recurrence + for (it = days->begin(); it != days->end(); ++it) { + if (*it > endDay) + return countGone; + if (++countGone >= countMax) + return countMax; + } + return countGone; +} + +int Recurrence::monthlyCalcNextAfter(QDate &enddate, MonthlyData &data) const +{ + uint countTogo = (rDuration > 0) ? rDuration + mRecurExDatesCount : UINT_MAX; + int countGone = 0; + int endYear = enddate.year(); + int endDay = enddate.day(); + int endYearMonth = endYear*12 + enddate.month() - 1; + QValueList<int>::ConstIterator it; + const QValueList<int>* days = data.dayList(); + + if (data.day > 1) { + // Check what remains of the start month + for (it = days->begin(); it != days->end(); ++it) { + if (*it >= data.day) { + ++countGone; + if (data.yearMonth() == endYearMonth && *it > endDay) { + data.day = *it; + goto ex; + } + if (--countTogo == 0) + return 0; + } + } + data.day = 1; + data.addMonths(rFreq); + } + + if (data.varies) { + // The number of recurrence days varies from month to month, + // so we need to check month by month. + while (data.yearMonth() <= endYearMonth) { + days = data.dayList(); + uint n = days->count(); // number of recurrence days in this month + if (data.yearMonth() == endYearMonth && days->last() > endDay) + break; + if (n >= countTogo) + return 0; + countGone += n; + countTogo -= n; + data.addMonths(rFreq); + } + days = data.dayList(); + } else { + // The number of recurrences is the same every month, + // so skip the month-by-month check. + // Skip the remaining whole months to at least end year/month. + int daysPerMonth = days->count(); + int elapsed = endYearMonth - data.yearMonth(); + int recurMonths = (elapsed + rFreq - 1) / rFreq; + if (elapsed % rFreq == 0 && days->last() <= endDay) + ++recurMonths; // required month is after endYearMonth + if (recurMonths) { + int n = recurMonths * daysPerMonth; + if (static_cast<uint>(n) > countTogo) + return 0; // reached end of recurrence + countTogo -= n; + countGone += n; + data.addMonths(recurMonths * rFreq); + } + } + + // Check the last month in the recurrence + for (it = days->begin(); it != days->end(); ++it) { + ++countGone; + if (data.yearMonth() > endYearMonth || *it > endDay) { + data.day = *it; + break; + } + if (--countTogo == 0) + return 0; + } +ex: + enddate = data.date(); + return countGone; +} + + +/* Find count and, depending on 'func', the end date of an annual recurrence by date. + * Reply = total number of occurrences up to 'enddate', or 0 if error. + * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the + * recurrence end date. + */ +struct Recurrence::YearlyMonthData { + const Recurrence *recurrence; + int year; // current year + int month; // current month 1..12 + int day; // current day of month 1..31 + bool leapyear; // true if February 29th recurs and current year is a leap year + bool feb29; // true if February 29th recurs + private: + QValueList<int> months; // recurring months in non-leap years 1..12 + QValueList<int> leapMonths; // recurring months in leap years 1..12 + public: + YearlyMonthData(const Recurrence* r, const QDate &date) + : recurrence(r), year(date.year()), month(date.month()), day(date.day()) + { feb29 = recurrence->getYearlyMonthMonths(day, months, leapMonths); + leapyear = feb29 && QDate::leapYear(year); + } + const QValueList<int>* monthList() const + { return leapyear ? &leapMonths : &months; } + const QValueList<int>* leapMonthList() const { return &leapMonths; } + QDate date() const { return QDate(year, month, day); } +}; + +int Recurrence::yearlyMonthCalc(PeriodFunc func, QDate &enddate) const +{ + if (rYearNums.isEmpty()) + return 0; + YearlyMonthData data(this, mRecurStart.date()); + switch (func) { + case END_DATE_AND_COUNT: + return yearlyMonthCalcEndDate(enddate, data); + case COUNT_TO_DATE: + return yearlyMonthCalcToDate(enddate, data); + case NEXT_AFTER_DATE: + return yearlyMonthCalcNextAfter(enddate, data); + } + return 0; +} + +// Find total count and end date of an annual recurrence by date. +// Reply = total number of occurrences. +int Recurrence::yearlyMonthCalcEndDate(QDate &enddate, YearlyMonthData &data) const +{ + uint countTogo = rDuration + mRecurExDatesCount; + int countGone = 0; + QValueList<int>::ConstIterator it; + const QValueList<int>* mons = data.monthList(); // get recurring months for this year + + if (data.month > 1) { + // Check what remains of the start year + for (it = mons->begin(); it != mons->end(); ++it) { + if (*it >= data.month) { + ++countGone; + if (--countTogo == 0) { + data.month = *it; + if (data.month == 2 && data.feb29 && !data.leapyear) { + // The recurrence should end on February 29th, but it's a non-leap year + switch (mFeb29YearlyType) { + case rFeb28: + data.day = 28; + break; + case rMar1: + data.month = 3; + data.day = 1; + break; + case rFeb29: + break; + } + } + break; + } + } + } + if (countTogo) { + data.month = 1; + data.year += rFreq; + } + } + if (countTogo) { + if (data.feb29 && mFeb29YearlyType == rFeb29) { + // The number of recurrences is different on leap years, + // so check year-by-year. + for ( ; ; ) { + mons = data.monthList(); + uint n = mons->count(); + if (n >= countTogo) + break; + countTogo -= n; + countGone += n; + data.year += rFreq; + } + } else { + // The number of recurrences is the same every year, + // so skip the year-by-year check. + // Skip the remaining whole years, but leave at least + // 1 recurrence remaining, in order to get its date. + int monthsPerYear = mons->count(); + int wholeYears = (countTogo - 1) / monthsPerYear; + data.year += wholeYears * rFreq; + countGone += wholeYears * monthsPerYear; + countTogo -= wholeYears * monthsPerYear; + } + if (countTogo) { + // Check the last year in the recurrence + for (it = mons->begin(); it != mons->end(); ++it) { + ++countGone; + if (--countTogo == 0) { + data.month = *it; + if (data.month == 2 && data.feb29 && !QDate::leapYear(data.year)) { + // The recurrence should end on February 29th, but it's a non-leap year + switch (mFeb29YearlyType) { + case rFeb28: + data.day = 28; + break; + case rMar1: + data.month = 3; + data.day = 1; + break; + case rFeb29: + break; + } + } + break; + } + } + } + } + enddate = data.date(); + return countGone; +} + +// Find count of an annual recurrence by date. +// Reply = total number of occurrences up to 'enddate'. +int Recurrence::yearlyMonthCalcToDate(const QDate &enddate, YearlyMonthData &data) const +{ + int countGone = 0; + int countMax = (rDuration > 0) ? rDuration + mRecurExDatesCount : INT_MAX; + int endYear = enddate.year(); + int endMonth = enddate.month(); + int endDay = enddate.day(); + if (endDay < data.day) { + /* The end day of the month is earlier than the recurrence day of the month. + * If Feb 29th recurs and: + * 1) it recurs on Feb 28th in non-leap years, don't adjust the end month + * if enddate is Feb 28th on a non-leap year. + * 2) it recurs on Mar 1st in non-leap years, allow the end month to be + * adjusted to February, to simplify calculations. + */ + if (data.feb29 && !QDate::leapYear(endYear) + && mFeb29YearlyType == rFeb28 && endDay == 28 && endMonth == 2) { + } + else if (--endMonth == 0) { + endMonth = 12; + --endYear; + } + } + QValueList<int>::ConstIterator it; + const QValueList<int>* mons = data.monthList(); + + if (data.month > 1) { + // Check what remains of the start year + for (it = mons->begin(); it != mons->end(); ++it) { + if (*it >= data.month) { + if (data.year == endYear && *it > endMonth) + return countGone; + if (++countGone >= countMax) + return countMax; + } + } + data.month = 1; + data.year += rFreq; + } + if (data.feb29 && mFeb29YearlyType == rFeb29) { + // The number of recurrences is different on leap years, + // so check year-by-year. + while (data.year < endYear) { + countGone += data.monthList()->count(); + if (countGone >= countMax) + return countMax; + data.year += rFreq; + } + mons = data.monthList(); + } else { + // The number of recurrences is the same every year, + // so skip the year-by-year check. + // Skip the remaining whole years. + int monthsPerYear = mons->count(); + int wholeYears = endYear - data.year; + countGone += (wholeYears / rFreq) * monthsPerYear; + if (countGone >= countMax) + return countMax; + if (wholeYears % rFreq) + return countGone; // end year isn't a recurrence year + data.year = endYear; + } + + // Check the last year in the recurrence + for (it = mons->begin(); it != mons->end(); ++it) { + if (*it > endMonth) + return countGone; + if (++countGone >= countMax) + return countMax; + } + return countGone; +} + +// Find count and date of first recurrence after 'enddate' of an annual recurrence by date. +// Reply = total number of occurrences up to 'enddate'. +int Recurrence::yearlyMonthCalcNextAfter(QDate &enddate, YearlyMonthData &data) const +{ + uint countTogo = (rDuration > 0) ? rDuration + mRecurExDatesCount : UINT_MAX; + int countGone = 0; + int endYear = enddate.year(); + int endMonth = enddate.month(); + int endDay = enddate.day(); + bool mar1TooEarly = false; + bool feb28ok = false; + if (endDay < data.day) { + if (data.feb29 && mFeb29YearlyType == rMar1 && endMonth == 3) + mar1TooEarly = true; + if (data.feb29 && mFeb29YearlyType == rFeb28 && endMonth == 2 && endDay == 28) + feb28ok = true; + else if (--endMonth == 0) { + endMonth = 12; + --endYear; + } + } + QValueList<int>::ConstIterator it; + const QValueList<int>* mons = data.monthList(); + + if (data.month > 1) { + // Check what remains of the start year + for (it = mons->begin(); it != mons->end(); ++it) { + if (*it >= data.month) { + ++countGone; + if (data.year == endYear + && ( *it > endMonth && (*it > 3 || !mar1TooEarly) + || *it == 2 && feb28ok && data.leapyear)) { + if (*it == 2 && data.feb29 && !data.leapyear) { + // The next recurrence should be on February 29th, but it's a non-leap year + switch (mFeb29YearlyType) { + case rFeb28: + data.month = 2; + data.day = 28; + break; + case rMar1: + data.month = 3; + data.day = 1; + break; + case rFeb29: // impossible in this context! + break; + } + } + else + data.month = *it; + goto ex; + } + if (--countTogo == 0) + return 0; + } + } + data.month = 1; + data.year += rFreq; + } + + if (data.feb29 && mFeb29YearlyType == rFeb29) { + // The number of recurrences is different on leap years, + // so check year-by-year. + while (data.year <= endYear) { + mons = data.monthList(); + if (data.year == endYear && mons->last() > endMonth) + break; + uint n = mons->count(); + if (n >= countTogo) + break; + countTogo -= n; + countGone += n; + data.year += rFreq; + } + mons = data.monthList(); + } else { + // The number of recurrences is the same every year, + // so skip the year-by-year check. + // Skip the remaining whole years to at least endYear. + int monthsPerYear = mons->count(); + int recurYears = (endYear - data.year + rFreq - 1) / rFreq; + if ((endYear - data.year)%rFreq == 0 + && mons->last() <= endMonth) + ++recurYears; // required year is after endYear + if (recurYears) { + int n = recurYears * monthsPerYear; + if (static_cast<uint>(n) > countTogo) + return 0; // reached end of recurrence + countTogo -= n; + countGone += n; + data.year += recurYears * rFreq; + } + } + + // Check the last year in the recurrence + for (it = mons->begin(); it != mons->end(); ++it) { + ++countGone; + if (data.year > endYear + || ( *it > endMonth && (*it > 3 || !mar1TooEarly) + || *it == 2 && feb28ok && QDate::leapYear(data.year))) { + if (*it == 2 && data.feb29 && !QDate::leapYear(data.year)) { + // The next recurrence should be on February 29th, but it's a non-leap year + switch (mFeb29YearlyType) { + case rFeb28: + data.month = 2; + data.day = 28; + break; + case rMar1: + data.month = 3; + data.day = 1; + break; + case rFeb29: // impossible in this context! + break; + } + } + else + data.month = *it; + break; + } + if (--countTogo == 0) + return 0; + } +ex: + enddate = data.date(); + return countGone; +} + + +/* Find count and, depending on 'func', the end date of an annual recurrence by date. + * Reply = total number of occurrences up to 'enddate', or 0 if error. + * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the + * recurrence end date. + */ +struct Recurrence::YearlyPosData { + const Recurrence *recurrence; + int year; // current year + int month; // current month 1..12 + int day; // current day of month 1..31 + int daysPerMonth; // number of days which recur each month, or -1 if variable + int count; // number of days which recur each year, or -1 if variable + bool varies; // true if number of days varies from year to year + private: + mutable QValueList<int> days; + public: + YearlyPosData(const Recurrence* r, const QDate &date) + : recurrence(r), year(date.year()), month(date.month()), day(date.day()), count(-1) + { if ((daysPerMonth = r->countMonthlyPosDays()) > 0) + count = daysPerMonth * r->rYearNums.count(); + varies = (daysPerMonth < 0); + } + const QValueList<int>* dayList() const { + QDate startOfMonth(year, month, 1); + recurrence->getMonthlyPosDays(days, startOfMonth.daysInMonth(), startOfMonth.dayOfWeek()); + return &days; + } + int yearMonth() const { return year*12 + month - 1; } + void addMonths(int diff) { month += diff - 1; year += month / 12; month = month % 12 + 1; } + QDate date() const { return QDate(year, month, day); } +}; + +int Recurrence::yearlyPosCalc(PeriodFunc func, QDate &enddate) const +{ + if (rYearNums.isEmpty() || rMonthPositions.isEmpty()) + return 0; + YearlyPosData data(this, mRecurStart.date()); + switch (func) { + case END_DATE_AND_COUNT: + return yearlyPosCalcEndDate(enddate, data); + case COUNT_TO_DATE: + return yearlyPosCalcToDate(enddate, data); + case NEXT_AFTER_DATE: + return yearlyPosCalcNextAfter(enddate, data); + } + return 0; +} + +int Recurrence::yearlyPosCalcEndDate(QDate &enddate, YearlyPosData &data) const +{ + uint countTogo = rDuration + mRecurExDatesCount; + int countGone = 0; + QValueList<int>::ConstIterator id; + const QValueList<int>* days; + + if (data.month > 1 || data.day > 1) { + // Check what remains of the start year + for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) { + if (*im.current() >= data.month) { + // Check what remains of the start month + if (data.day > 1 || data.varies + || static_cast<uint>(data.daysPerMonth) >= countTogo) { + data.month = *im.current(); + days = data.dayList(); + for (id = days->begin(); id != days->end(); ++id) { + if (*id >= data.day) { + ++countGone; + if (--countTogo == 0) { + data.month = *im.current(); + data.day = *id; + goto ex; + } + } + } + data.day = 1; + } else { + // The number of days per month is constant, so skip + // the whole month. + countTogo -= data.daysPerMonth; + countGone += data.daysPerMonth; + } + } + } + data.month = 1; + data.year += rFreq; + } + + if (data.varies) { + // The number of recurrences varies from year to year. + for ( ; ; ) { + for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) { + data.month = *im.current(); + days = data.dayList(); + int n = days->count(); + if (static_cast<uint>(n) >= countTogo) { + // Check the last month in the recurrence + for (id = days->begin(); id != days->end(); ++id) { + ++countGone; + if (--countTogo == 0) { + data.day = *id; + goto ex; + } + } + } + countTogo -= n; + countGone += n; + } + data.year += rFreq; + } + } else { + // The number of recurrences is the same every year, + // so skip the year-by-year check. + // Skip the remaining whole years, but leave at least + // 1 recurrence remaining, in order to get its date. + int wholeYears = (countTogo - 1) / data.count; + data.year += wholeYears * rFreq; + countGone += wholeYears * data.count; + countTogo -= wholeYears * data.count; + + // Check the last year in the recurrence. + for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) { + if (static_cast<uint>(data.daysPerMonth) >= countTogo) { + // Check the last month in the recurrence + data.month = *im.current(); + days = data.dayList(); + for (id = days->begin(); id != days->end(); ++id) { + ++countGone; + if (--countTogo == 0) { + data.day = *id; + goto ex; + } + } + } + countTogo -= data.daysPerMonth; + countGone += data.daysPerMonth; + } + data.year += rFreq; + } +ex: + enddate = data.date(); + return countGone; +} + +int Recurrence::yearlyPosCalcToDate(const QDate &enddate, YearlyPosData &data) const +{ + int countGone = 0; + int countMax = (rDuration > 0) ? rDuration + mRecurExDatesCount : INT_MAX; + int endYear = enddate.year(); + int endMonth = enddate.month(); + int endDay = enddate.day(); + if (endDay < data.day && --endMonth == 0) { + endMonth = 12; + --endYear; + } + int endYearMonth = endYear*12 + endMonth; + QValueList<int>::ConstIterator id; + const QValueList<int>* days; + + if (data.month > 1 || data.day > 1) { + // Check what remains of the start year + for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) { + if (*im.current() >= data.month) { + data.month = *im.current(); + if (data.yearMonth() > endYearMonth) + return countGone; + // Check what remains of the start month + bool lastMonth = (data.yearMonth() == endYearMonth); + if (lastMonth || data.day > 1 || data.varies) { + days = data.dayList(); + if (lastMonth || data.day > 1) { + for (id = days->begin(); id != days->end(); ++id) { + if (*id >= data.day) { + if (lastMonth && *id > endDay) + return countGone; + if (++countGone >= countMax) + return countMax; + } + } + } else { + countGone += days->count(); + if (countGone >= countMax) + return countMax; + } + data.day = 1; + } else { + // The number of days per month is constant, so skip + // the whole month. + countGone += data.daysPerMonth; + if (countGone >= countMax) + return countMax; + } + } + } + data.month = 1; + data.year += rFreq; + } + + if (data.varies) { + // The number of recurrences varies from year to year. + for ( ; ; ) { + for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) { + data.month = *im.current(); + days = data.dayList(); + if (data.yearMonth() >= endYearMonth) { + if (data.yearMonth() > endYearMonth) + return countGone; + // Check the last month in the recurrence + for (id = days->begin(); id != days->end(); ++id) { + if (*id > endDay) + return countGone; + if (++countGone >= countMax) + return countMax; + } + } else { + countGone += days->count(); + if (countGone >= countMax) + return countMax; + } + } + data.year += rFreq; + } + } else { + // The number of recurrences is the same every year, + // so skip the year-by-year check. + // Skip the remaining whole years, but leave at least + // 1 recurrence remaining, in order to get its date. + int wholeYears = endYear - data.year; + countGone += (wholeYears / rFreq) * data.count; + if (countGone >= countMax) + return countMax; + if (wholeYears % rFreq) + return countGone; // end year isn't a recurrence year + data.year = endYear; + + // Check the last year in the recurrence. + for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) { + data.month = *im.current(); + if (data.month >= endMonth) { + if (data.month > endMonth) + return countGone; + // Check the last month in the recurrence + days = data.dayList(); + for (id = days->begin(); id != days->end(); ++id) { + if (*id > endDay) + return countGone; + if (++countGone >= countMax) + return countMax; + } + } else { + countGone += data.daysPerMonth; + if (countGone >= countMax) + return countMax; + } + } + } + return countGone; +} + +int Recurrence::yearlyPosCalcNextAfter(QDate &enddate, YearlyPosData &data) const +{ + uint countTogo = (rDuration > 0) ? rDuration + mRecurExDatesCount : UINT_MAX; + int countGone = 0; + int endYear = enddate.year(); + int endMonth = enddate.month(); + int endDay = enddate.day(); + if (endDay < data.day && --endMonth == 0) { + endMonth = 12; + --endYear; + } + int endYearMonth = endYear*12 + endMonth; + QValueList<int>::ConstIterator id; + const QValueList<int>* days; + + if (data.varies) { + // The number of recurrences varies from year to year. + for ( ; ; ) { + // Check the next year + for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) { + if (*im.current() >= data.month) { + // Check the next month + data.month = *im.current(); + int ended = data.yearMonth() - endYearMonth; + days = data.dayList(); + if (ended >= 0 || data.day > 1) { + // This is the start or end month, so check each day + for (id = days->begin(); id != days->end(); ++id) { + if (*id >= data.day) { + ++countGone; + if (ended > 0 || (ended == 0 && *id > endDay)) { + data.day = *id; + goto ex; + } + if (--countTogo == 0) + return 0; + } + } + } else { + // Skip the whole month + uint n = days->count(); + if (n >= countTogo) + return 0; + countGone += n; + } + data.day = 1; // we've checked the start month now + } + } + data.month = 1; // we've checked the start year now + data.year += rFreq; + } + } else { + // The number of recurrences is the same every year. + if (data.month > 1 || data.day > 1) { + // Check what remains of the start year + for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) { + if (*im.current() >= data.month) { + // Check what remains of the start month + data.month = *im.current(); + int ended = data.yearMonth() - endYearMonth; + if (ended >= 0 || data.day > 1) { + // This is the start or end month, so check each day + days = data.dayList(); + for (id = days->begin(); id != days->end(); ++id) { + if (*id >= data.day) { + ++countGone; + if (ended > 0 || (ended == 0 && *id > endDay)) { + data.day = *id; + goto ex; + } + if (--countTogo == 0) + return 0; + } + } + data.day = 1; // we've checked the start month now + } else { + // Skip the whole month. + if (static_cast<uint>(data.daysPerMonth) >= countTogo) + return 0; + countGone += data.daysPerMonth; + } + } + } + data.year += rFreq; + } + // Skip the remaining whole years to at least endYear. + int recurYears = (endYear - data.year + rFreq - 1) / rFreq; + if ((endYear - data.year)%rFreq == 0 + && *rYearNums.getLast() <= endMonth) + ++recurYears; // required year is after endYear + if (recurYears) { + int n = recurYears * data.count; + if (static_cast<uint>(n) > countTogo) + return 0; // reached end of recurrence + countTogo -= n; + countGone += n; + data.year += recurYears * rFreq; + } + + // Check the last year in the recurrence + for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) { + data.month = *im.current(); + int ended = data.yearMonth() - endYearMonth; + if (ended >= 0) { + // This is the end month, so check each day + days = data.dayList(); + for (id = days->begin(); id != days->end(); ++id) { + ++countGone; + if (ended > 0 || (ended == 0 && *id > endDay)) { + data.day = *id; + goto ex; + } + if (--countTogo == 0) + return 0; + } + } else { + // Skip the whole month. + if (static_cast<uint>(data.daysPerMonth) >= countTogo) + return 0; + countGone += data.daysPerMonth; + } + } + } +ex: + enddate = data.date(); + return countGone; +} + + +/* Find count and, depending on 'func', the end date of an annual recurrence by day. + * Reply = total number of occurrences up to 'enddate', or 0 if error. + * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the + * recurrence end date. + */ +struct Recurrence::YearlyDayData { + int year; // current year + int day; // current day of year 1..366 + bool varies; // true if day 366 recurs + private: + int daycount; + public: + YearlyDayData(const Recurrence* r, const QDate &date) + : year(date.year()), day(date.dayOfYear()), varies(*r->rYearNums.getLast() == 366), + daycount(r->rYearNums.count()) { } + bool leapYear() const { return QDate::leapYear(year); } + int dayCount() const { return daycount - (varies && !QDate::leapYear(year) ? 1 : 0); } + bool isMaxDayCount() const { return !varies || QDate::leapYear(year); } + QDate date() const { return QDate(year, 1, 1).addDays(day - 1); } +}; + +int Recurrence::yearlyDayCalc(PeriodFunc func, QDate &enddate) const +{ + if (rYearNums.isEmpty()) + return 0; + YearlyDayData data(this, mRecurStart.date()); + switch (func) { + case END_DATE_AND_COUNT: + return yearlyDayCalcEndDate(enddate, data); + case COUNT_TO_DATE: + return yearlyDayCalcToDate(enddate, data); + case NEXT_AFTER_DATE: + return yearlyDayCalcNextAfter(enddate, data); + } + return 0; +} + +int Recurrence::yearlyDayCalcEndDate(QDate &enddate, YearlyDayData &data) const +{ + uint countTogo = rDuration + mRecurExDatesCount; + int countGone = 0; + + if (data.day > 1) { + // Check what remains of the start year + bool leapOK = data.isMaxDayCount(); + for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) { + int d = *it.current(); + if (d >= data.day && (leapOK || d < 366)) { + ++countGone; + if (--countTogo == 0) { + data.day = d; + goto ex; + } + } + } + data.day = 1; + data.year += rFreq; + } + + if (data.varies) { + // The number of recurrences is different in leap years, + // so check year-by-year. + for ( ; ; ) { + uint n = data.dayCount(); + if (n >= countTogo) + break; + countTogo -= n; + countGone += n; + data.year += rFreq; + } + } else { + // The number of recurrences is the same every year, + // so skip the year-by-year check. + // Skip the remaining whole years, but leave at least + // 1 recurrence remaining, in order to get its date. + int daysPerYear = rYearNums.count(); + int wholeYears = (countTogo - 1) / daysPerYear; + data.year += wholeYears * rFreq; + countGone += wholeYears * daysPerYear; + countTogo -= wholeYears * daysPerYear; + } + if (countTogo) { + // Check the last year in the recurrence + for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) { + ++countGone; + if (--countTogo == 0) { + data.day = *it.current(); + break; + } + } + } +ex: + enddate = data.date(); + return countGone; +} + +int Recurrence::yearlyDayCalcToDate(const QDate &enddate, YearlyDayData &data) const +{ + int countGone = 0; + int countMax = (rDuration > 0) ? rDuration + mRecurExDatesCount : INT_MAX; + int endYear = enddate.year(); + int endDay = enddate.dayOfYear(); + + if (data.day > 1) { + // Check what remains of the start year + bool leapOK = data.isMaxDayCount(); + for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) { + int d = *it.current(); + if (d >= data.day && (leapOK || d < 366)) { + if (data.year == endYear && d > endDay) + return countGone; + if (++countGone >= countMax) + return countMax; + } + } + data.day = 1; + data.year += rFreq; + } + + if (data.varies) { + // The number of recurrences is different in leap years, + // so check year-by-year. + while (data.year < endYear) { + uint n = data.dayCount(); + countGone += n; + if (countGone >= countMax) + return countMax; + data.year += rFreq; + } + if (data.year > endYear) + return countGone; + } else { + // The number of recurrences is the same every year. + // Skip the remaining whole years. + int wholeYears = endYear - data.year; + countGone += (wholeYears / rFreq) * rYearNums.count(); + if (countGone >= countMax) + return countMax; + if (wholeYears % rFreq) + return countGone; // end year isn't a recurrence year + data.year = endYear; + } + + if (data.year <= endYear) { + // Check the last year in the recurrence + for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) { + if (*it.current() > endDay) + return countGone; + if (++countGone >= countMax) + return countMax; + } + } + return countGone; +} + +int Recurrence::yearlyDayCalcNextAfter(QDate &enddate, YearlyDayData &data) const +{ + uint countTogo = (rDuration > 0) ? rDuration + mRecurExDatesCount : UINT_MAX; + int countGone = 0; + int endYear = enddate.year(); + int endDay = enddate.dayOfYear(); + + if (data.day > 1) { + // Check what remains of the start year + bool leapOK = data.isMaxDayCount(); + for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) { + int d = *it.current(); + if (d >= data.day && (leapOK || d < 366)) { + ++countGone; + if (data.year == endYear && d > endDay) { + data.day = d; + goto ex; + } + if (--countTogo == 0) + return 0; + } + } + data.day = 1; + data.year += rFreq; + } + + if (data.varies) { + // The number of recurrences is different in leap years, + // so check year-by-year. + while (data.year <= endYear) { + uint n = data.dayCount(); + if (data.year == endYear && *rYearNums.getLast() > endDay) + break; + if (n >= countTogo) + break; + countTogo -= n; + countGone += n; + data.year += rFreq; + } + } else { + // The number of recurrences is the same every year, + // so skip the year-by-year check. + // Skip the remaining whole years to at least endYear. + int daysPerYear = rYearNums.count(); + int recurYears = (endYear - data.year + rFreq - 1) / rFreq; + if ((endYear - data.year)%rFreq == 0 + && *rYearNums.getLast() <= endDay) + ++recurYears; // required year is after endYear + if (recurYears) { + int n = recurYears * daysPerYear; + if (static_cast<uint>(n) > countTogo) + return 0; // reached end of recurrence + countTogo -= n; + countGone += n; + data.year += recurYears * rFreq; + } + } + + // Check the last year in the recurrence + for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) { + ++countGone; + int d = *it.current(); + if (data.year > endYear || d > endDay) { + data.day = d; + break; + } + if (--countTogo == 0) + return 0; + } +ex: + enddate = data.date(); + return countGone; +} + +// Get the days in this month which recur, in numerical order. +// Parameters: daysInMonth = number of days in this month +// startDayOfWeek = day of week for first day of month. +void Recurrence::getMonthlyPosDays(QValueList<int> &list, int daysInMonth, int startDayOfWeek) const +{ + list.clear(); + int endDayOfWeek = (startDayOfWeek + daysInMonth - 2) % 7 + 1; + // Go through the list, compiling a bit list of actual day numbers + Q_UINT32 days = 0; + for (QPtrListIterator<rMonthPos> pos(rMonthPositions); pos.current(); ++pos) { + int weeknum = pos.current()->rPos - 1; // get 0-based week number + QBitArray &rdays = pos.current()->rDays; + if (pos.current()->negative) { + // nth days before the end of the month + for (uint i = 1; i <= 7; ++i) { + if (rdays.testBit(i - 1)) { + int day = daysInMonth - weeknum*7 - (endDayOfWeek - i + 7) % 7; + if (day > 0) + days |= 1 << (day - 1); + } + } + } else { + // nth days after the start of the month + for (uint i = 1; i <= 7; ++i) { + if (rdays.testBit(i - 1)) { + int day = 1 + weeknum*7 + (i - startDayOfWeek + 7) % 7; + if (day <= daysInMonth) + days |= 1 << (day - 1); + } + } + } + } + // Compile the ordered list + Q_UINT32 mask = 1; + for (int i = 0; i < daysInMonth; mask <<= 1, ++i) { + if (days & mask) + list.append(i + 1); + } +} + +// Get the number of days in the month which recur. +// Reply = -1 if the number varies from month to month. +int Recurrence::countMonthlyPosDays() const +{ + int count = 0; + Q_UINT8 positive[5] = { 0, 0, 0, 0, 0 }; + Q_UINT8 negative[4] = { 0, 0, 0, 0 }; + for (QPtrListIterator<rMonthPos> pos(rMonthPositions); pos.current(); ++pos) { + int weeknum = pos.current()->rPos; + Q_UINT8* wk; + if (pos.current()->negative) { + // nth days before the end of the month + if (weeknum > 4) + return -1; // days in 5th week are often missing + wk = &negative[4 - weeknum]; + } else { + // nth days after the start of the month + if (weeknum > 4) + return -1; // days in 5th week are often missing + wk = &positive[weeknum - 1]; + } + QBitArray &rdays = pos.current()->rDays; + for (uint i = 0; i < 7; ++i) { + if (rdays.testBit(i)) { + ++count; + *wk |= (1 << i); + } + } + } + // Check for any possible days which could be duplicated by + // a positive and a negative position. + for (int i = 0; i < 4; ++i) { + if (negative[i] & (positive[i] | positive[i+1])) + return -1; + } + return count; +} + +// Get the days in this month which recur, in numerical order. +// Reply = true if day numbers varies from month to month. +bool Recurrence::getMonthlyDayDays(QValueList<int> &list, int daysInMonth) const +{ + list.clear(); + bool variable = false; + Q_UINT32 days = 0; + for (QPtrListIterator<int> it(rMonthDays); it.current(); ++it) { + int day = *it.current(); + if (day > 0) { + // date in the month + if (day <= daysInMonth) + days |= 1 << (day - 1); + if (day > 28 && day <= 31) + variable = true; // this date does not appear in some months + } else if (day < 0) { + // days before the end of the month + variable = true; // this date varies depending on the month length + day = daysInMonth + day; // zero-based day of month + if (day >= 0) + days |= 1 << day; + } + } + // Compile the ordered list + Q_UINT32 mask = 1; + for (int i = 0; i < daysInMonth; mask <<= 1, ++i) { + if (days & mask) + list.append(i + 1); + } + return variable; +} + +// Get the months which recur, in numerical order, for both leap years and non-leap years. +// N.B. If February 29th recurs on March 1st in non-leap years, February (not March) is +// included in the non-leap year month list. +// Reply = true if February 29th also recurs. +bool Recurrence::getYearlyMonthMonths(int day, QValueList<int> &list, QValueList<int> &leaplist) const +{ + list.clear(); + leaplist.clear(); + bool feb29 = false; + for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) { + int month = *it.current(); + if (month == 2) { + if (day <= 28) { + list.append(month); // date appears in February + leaplist.append(month); + } + else if (day == 29) { + // February 29th + leaplist.append(month); + switch (mFeb29YearlyType) { + case rFeb28: + case rMar1: + list.append(2); + break; + case rFeb29: + break; + } + feb29 = true; + } + } + else if (day <= 30 || QDate(2000, month, 1).daysInMonth() == 31) { + list.append(month); // date appears in every month + leaplist.append(month); + } + } + return feb29; +} + +/* From the recurrence day of the week list, get the earliest day in the + * specified week which is >= the startDay. + * Parameters: startDay = 1..7 (Monday..Sunday) + * useWeekStart = true to end search at day before next rWeekStart + * = false to search for a full 7 days + * Reply = day of the week (1..7), or 0 if none found. + */ +int Recurrence::getFirstDayInWeek(int startDay, bool useWeekStart) const +{ + int last = ((useWeekStart ? rWeekStart : startDay) + 5)%7; + for (int i = startDay - 1; ; i = (i + 1)%7) { + if (rDays.testBit(i)) + return i + 1; + if (i == last) + return 0; + } +} + +/* From the recurrence day of the week list, get the latest day in the + * specified week which is <= the endDay. + * Parameters: endDay = 1..7 (Monday..Sunday) + * useWeekStart = true to end search at rWeekStart + * = false to search for a full 7 days + * Reply = day of the week (1..7), or 0 if none found. + */ +int Recurrence::getLastDayInWeek(int endDay, bool useWeekStart) const +{ + int last = useWeekStart ? rWeekStart - 1 : endDay%7; + for (int i = endDay - 1; ; i = (i + 6)%7) { + if (rDays.testBit(i)) + return i + 1; + if (i == last) + return 0; + } +} + +/* From the recurrence monthly day number list or monthly day of week/week of + * month list, get the earliest day in the specified month which is >= the + * earliestDate. + */ +QDate Recurrence::getFirstDateInMonth(const QDate &earliestDate) const +{ + int earliestDay = earliestDate.day(); + int daysInMonth = earliestDate.daysInMonth(); + switch (recurs) { + case rMonthlyDay: { + int minday = daysInMonth + 1; + for (QPtrListIterator<int> it(rMonthDays); it.current(); ++it) { + int day = *it.current(); + if (day < 0) + day = daysInMonth + day + 1; + if (day >= earliestDay && day < minday) + minday = day; + } + if (minday <= daysInMonth) + return earliestDate.addDays(minday - earliestDay); + break; + } + case rMonthlyPos: + case rYearlyPos: { + QDate monthBegin(earliestDate.addDays(1 - earliestDay)); + QValueList<int> dayList; + getMonthlyPosDays(dayList, daysInMonth, monthBegin.dayOfWeek()); + for (QValueList<int>::ConstIterator id = dayList.begin(); id != dayList.end(); ++id) { + if (*id >= earliestDay) + return monthBegin.addDays(*id - 1); + } + break; + } + } + return QDate(); +} + +/* From the recurrence monthly day number list or monthly day of week/week of + * month list, get the latest day in the specified month which is <= the + * latestDate. + */ +QDate Recurrence::getLastDateInMonth(const QDate &latestDate) const +{ + int latestDay = latestDate.day(); + int daysInMonth = latestDate.daysInMonth(); + switch (recurs) { + case rMonthlyDay: { + int maxday = -1; + for (QPtrListIterator<int> it(rMonthDays); it.current(); ++it) { + int day = *it.current(); + if (day < 0) + day = daysInMonth + day + 1; + if (day <= latestDay && day > maxday) + maxday = day; + } + if (maxday > 0) + return QDate(latestDate.year(), latestDate.month(), maxday); + break; + } + case rMonthlyPos: + case rYearlyPos: { + QDate monthBegin(latestDate.addDays(1 - latestDay)); + QValueList<int> dayList; + getMonthlyPosDays(dayList, daysInMonth, monthBegin.dayOfWeek()); + for (QValueList<int>::ConstIterator id = dayList.fromLast(); id != dayList.end(); --id) { + if (*id <= latestDay) + return monthBegin.addDays(*id - 1); + } + break; + } + } + return QDate(); +} + +/* From the recurrence yearly month list or yearly day list, get the earliest + * month or day in the specified year which is >= the earliestDate. + * Note that rYearNums is sorted in numerical order. + */ +QDate Recurrence::getFirstDateInYear(const QDate &earliestDate) const +{ + QPtrListIterator<int> it(rYearNums); + switch (recurs) { + case rYearlyMonth: { + int day = recurStart().date().day(); + int earliestYear = earliestDate.year(); + int earliestMonth = earliestDate.month(); + int earliestDay = earliestDate.day(); + if (earliestDay > day) { + // The earliest date is later in the month than the recurrence date, + // so skip to the next month before starting to check + if (++earliestMonth > 12) + return QDate(); + } + for ( ; it.current(); ++it) { + int month = *it.current(); + if (month >= earliestMonth) { + if (day <= 28 || QDate::isValid(earliestYear, month, day)) + return QDate(earliestYear, month, day); + if (day == 29 && month == 2) { + // It's a recurrence on February 29th, in a non-leap year + switch (mFeb29YearlyType) { + case rMar1: + return QDate(earliestYear, 3, 1); + case rFeb28: + if (earliestDay <= 28) + return QDate(earliestYear, 2, 28); + break; + case rFeb29: + break; + } + } + } + } + break; + } + case rYearlyPos: { + QValueList<int> dayList; + int earliestYear = earliestDate.year(); + int earliestMonth = earliestDate.month(); + int earliestDay = earliestDate.day(); + for ( ; it.current(); ++it) { + int month = *it.current(); + if (month >= earliestMonth) { + QDate monthBegin(earliestYear, month, 1); + getMonthlyPosDays(dayList, monthBegin.daysInMonth(), monthBegin.dayOfWeek()); + for (QValueList<int>::ConstIterator id = dayList.begin(); id != dayList.end(); ++id) { + if (*id >= earliestDay) + return monthBegin.addDays(*id - 1); + } + earliestDay = 1; + } + } + break; + } + case rYearlyDay: { + int earliestDay = earliestDate.dayOfYear(); + for ( ; it.current(); ++it) { + int day = *it.current(); + if (day >= earliestDay && (day <= 365 || day <= earliestDate.daysInYear())) + return earliestDate.addDays(day - earliestDay); + } + break; + } + } + return QDate(); +} + +/* From the recurrence yearly month list or yearly day list, get the latest + * month or day in the specified year which is <= the latestDate. + * Note that rYearNums is sorted in numerical order. + */ +QDate Recurrence::getLastDateInYear(const QDate &latestDate) const +{ + QPtrListIterator<int> it(rYearNums); + switch (recurs) { + case rYearlyMonth: { + int day = recurStart().date().day(); + int latestYear = latestDate.year(); + int latestMonth = latestDate.month(); + if (latestDate.day() > day) { + // The latest date is earlier in the month than the recurrence date, + // so skip to the previous month before starting to check + if (--latestMonth <= 0) + return QDate(); + } + for (it.toLast(); it.current(); --it) { + int month = *it.current(); + if (month <= latestMonth) { + if (day <= 28 || QDate::isValid(latestYear, month, day)) + return QDate(latestYear, month, day); + if (day == 29 && month == 2) { + // It's a recurrence on February 29th, in a non-leap year + switch (mFeb29YearlyType) { + case rMar1: + if (latestMonth >= 3) + return QDate(latestYear, 3, 1); + break; + case rFeb28: + return QDate(latestYear, 2, 28); + case rFeb29: + break; + } + } + } + } + break; + } + case rYearlyPos: { + QValueList<int> dayList; + int latestYear = latestDate.year(); + int latestMonth = latestDate.month(); + int latestDay = latestDate.day(); + for (it.toLast(); it.current(); --it) { + int month = *it.current(); + if (month <= latestMonth) { + QDate monthBegin(latestYear, month, 1); + getMonthlyPosDays(dayList, monthBegin.daysInMonth(), monthBegin.dayOfWeek()); + for (QValueList<int>::ConstIterator id = dayList.fromLast(); id != dayList.end(); --id) { + if (*id <= latestDay) + return monthBegin.addDays(*id - 1); + } + latestDay = 31; + } + } + break; + } + case rYearlyDay: { + int latestDay = latestDate.dayOfYear(); + for (it.toLast(); it.current(); --it) { + int day = *it.current(); + if (day <= latestDay) + return latestDate.addDays(day - latestDay); + } + break; + } + } + return QDate(); +} + +void Recurrence::dump() const +{ + kdDebug() << "Recurrence::dump():" << endl; + + kdDebug() << " type: " << recurs << endl; + + kdDebug() << " rDays: " << endl; + int i; + for( i = 0; i < 7; ++i ) { + kdDebug() << " " << i << ": " + << ( rDays.testBit( i ) ? "true" : "false" ) << endl; + } +} diff --git a/libkcal/recurrence.h b/libkcal/recurrence.h new file mode 100644 index 0000000..a0f6d84 --- a/dev/null +++ b/libkcal/recurrence.h @@ -0,0 +1,401 @@ +/* + This file is part of libkcal. + Copyright (c) 1998 Preston Brown + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + Copyright (c) 2002 David Jarvie <software@astrojar.org.uk> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef KCAL_RECURRENCE_H +#define KCAL_RECURRENCE_H + +#include <qstring.h> +#include <qbitarray.h> +#include <qptrlist.h> + +namespace KCal { + +class Incidence; + +/** + This class represents a recurrence rule for a calendar incidence. +*/ +class Recurrence +{ + public: + /** 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 }; + + /** Enumeration for specifying what date yearly recurrences of February 29th occur + * in non-leap years. */ + enum Feb29Type { + rMar1, // recur on March 1st (default) + rFeb28, // recur on February 28th + rFeb29 // only recur on February 29th, i.e. don't recur in non-leap years + }; + + /** structure for Recurs rMonthlyPos */ + struct rMonthPos { + QBitArray rDays; + short rPos; + bool negative; + }; + + Recurrence(Incidence *parent, int compatVersion = 0); + Recurrence(const Recurrence&, Incidence *parent); + ~Recurrence(); + + bool operator==( const Recurrence& ) const; + bool operator!=( const Recurrence& r ) const { return !operator==(r); } + + Incidence *parent() { return mParent; } + + /** Return the start of the recurrence */ + QDateTime recurStart() const { return mRecurStart; } + /** Returns the number of exception dates for the recurrence */ + int recurExDatesCount() const { return mRecurExDatesCount; } + /** Set start of recurrence, as a date and time. */ + void setRecurStart(const QDateTime &start); + /** Set start of recurrence, as a date with no time. + * Recurrence types which are sub-daily (e.g. rHourly) always have a time; + * the time is set to 00:00:00 in these cases. */ + void setRecurStart(const QDate &start); + /** Set whether the recurrence has no time, just a date. + * Recurrence types which are sub-daily (e.g. rHourly) always have a time + * and cannot be set to float. + * N.B. This property is derived by default from the parent incidence, + * or according to whether a time is specified in setRecurStart(). */ + void setFloats(bool f); + /** + Returns whether the recurrence has no time, just a date. + */ + bool doesFloat() const { + return mFloats; + } + + /** Set if recurrence is read-only or can be changed. */ + void setRecurReadOnly(bool readOnly) { mRecurReadOnly = readOnly; } + bool recurReadOnly() const + { + return mRecurReadOnly; + } + + + /** Set number of exception dates. */ + void setRecurExDatesCount(int count) { if (count >= 0) mRecurExDatesCount = count; } + /** Set the calendar file version for backwards compatibility. + * @var version is the KOrganizer/libkcal version, e.g. 220 for KDE 2.2.0. + * Specify version = 0 to cancel compatibility mode. + */ + void setCompatVersion(int version = 0); + + /** Returns the event's recurrence status. See the enumeration at the top + * of this file for possible values. */ + ushort doesRecur() const; + /** Returns true if the date specified is one on which the event will + * recur. */ + bool recursOnPure(const QDate &qd) 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. */ + bool recursAtPure(const QDateTime &) const; + /** Turns off recurrence for the event. */ + void unsetRecurs(); + + /** Returns the date of the next recurrence, after the specified date. + * @var preDate the date after which to find the recurrence. + * @var last if non-null, *last is set to true if the next recurrence is the + * last recurrence, else false. + * Reply = date of next recurrence, or invalid date if none. + */ + QDate getNextDate(const QDate& preDate, bool* last = 0) 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. + * @var preDate the date/time after which to find the recurrence. + * @var last if non-null, *last is set to true if the next recurrence is the + * last recurrence, else false. + * Reply = date/time of next recurrence, or invalid date if none. + */ + QDateTime getNextDateTime(const QDateTime& preDateTime, bool* last = 0) const; + /** Returns the date of the last previous recurrence, before the specified date. + * @var afterDate the date before which to find the recurrence. + * @var last if non-null, *last is set to true if the previous recurrence is the + * last recurrence, else false. + * Reply = date of previous recurrence, or invalid date if none. + */ + QDate getPreviousDate(const QDate& afterDate, bool* last = 0) 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. + * @var afterDate the date/time before which to find the recurrence. + * @var last if non-null, *last is set to true if the previous recurrence is the + * last recurrence, else false. + * Reply = date/time of previous recurrence, or invalid date if none. + */ + QDateTime getPreviousDateTime(const QDateTime& afterDateTime, bool* last = 0) const; + + /** Returns frequency of recurrence, in terms of the recurrence time period type. */ + int frequency() const; + /** Returns the total number of recurrences, including the initial occurrence. */ + 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 specified. */ + int durationTo(const QDate &) const; + /** Returns the number of recurrences up to and including the date/time specified. */ + int durationTo(const QDateTime &) const; + + /** Returns the date of the last recurrence. + * An invalid date is returned if the recurrence has no end. + * Note: for some recurrence types, endDate() can involve significant calculation. + */ + QDate endDate() const; + /** Returns the date and time of the last recurrence. + * An invalid date is returned if the recurrence has no end. + * Note: for some recurrence types, endDateTime() can involve significant calculation. + */ + QDateTime endDateTime() const; + /** Returns a string representing the recurrence end date in the format + according to the user's locale settings. */ + QString endDateStr(bool shortfmt=true) const; + + /** Sets an event to recur minutely. + * @var _rFreq the frequency to recur, e.g. 2 is every other minute + * @var duration the number of times the event is to occur, or -1 to recur indefinitely. + */ + void setMinutely(int _rFreq, int duration); + /** Sets an event to recur minutely. + * @var _rFreq the frequency to recur, e.g. 2 is every other minute + * @var endDateTime the ending date/time after which to stop recurring + */ + void setMinutely(int _rFreq, const QDateTime &endDateTime); + + /** Sets an event to recur hourly. + * @var _rFreq the frequency to recur, e.g. 2 is every other hour + * @var duration the number of times the event is to occur, or -1 to recur indefinitely. + */ + void setHourly(int _rFreq, int duration); + /** Sets an event to recur hourly. + * @var _rFreq the frequency to recur, e.g. 2 is every other hour + * @var endDateTime the ending date/time after which to stop recurring + */ + void setHourly(int _rFreq, const QDateTime &endDateTime); + + /** Sets an event to recur daily. + * @var _rFreq the frequency to recur, e.g. 2 is every other day + * @var duration the number of times the event is to occur, or -1 to recur indefinitely. + */ + void setDaily(int _rFreq, int duration); + /** Sets an event to recur daily. + * @var _rFreq the frequency to recur, e.g. 2 is every other day + * @var endDate the ending date after which to stop recurring + */ + void setDaily(int _rFreq, const QDate &endDate); + + /** Sets an event to recur weekly. + * @var _rFreq the frequency to recur, e.g. every other week etc. + * @var _rDays a 7 bit array indicating which days on which to recur (bit 0 = Monday). + * @var duration the number of times the event is to occur, or -1 to recur indefinitely. + * @var weekStart the first day of the week (Monday=1 .. Sunday=7, default is Monday). + */ + void setWeekly(int _rFreq, const QBitArray &_rDays, int duration, int weekStart = 1); + /** Sets an event to recur weekly. + * @var _rFreq the frequency to recur, e.g. every other week etc. + * @var _rDays a 7 bit array indicating which days on which to recur (bit 0 = Monday). + * @var endDate the date on which to stop recurring. + * @var weekStart the first day of the week (Monday=1 .. Sunday=7, default is Monday). + */ + void setWeekly(int _rFreq, const QBitArray &_rDays, const QDate &endDate, int weekStart = 1); + /** Returns the first day of the week. Monday=1 .. Sunday=7. */ + int weekStart() const { return rWeekStart; } + /** Returns week day mask (bit 0 = Monday). */ + const QBitArray &days() const; + + /** Sets an event to recur monthly. + * @var type rMonthlyPos or rMonthlyDay + * @var _rFreq the frequency to recur, e.g. 3 for every third month. + * @var duration the number of times the event is to occur, or -1 to recur indefinitely. + */ + void setMonthly(short type, int _rFreq, int duration); + /** same as above, but with ending date not number of recurrences */ + void setMonthly(short type, int _rFreq, const QDate &endDate); + /** Adds a position to the recursMonthlyPos recurrence rule, if it is + * set. + * @var _rPos the position in the month for the recurrence, with valid + * values being 1-5 (5 weeks max in a month). + * @var _rDays the days for the position to recur on (bit 0 = Monday). + * Example: _rPos = 2, and bits 0 and 2 are set in _rDays: + * the rule is to repeat every 2nd Monday and Wednesday in the month. + */ + void addMonthlyPos(short _rPos, const QBitArray &_rDays); + /** Adds a position the the recursMonthlyDay list. + * @var _rDay the date in the month to recur. + */ + void addMonthlyDay(short _rDay); + /** Returns list of day positions in months. */ + const QPtrList<rMonthPos> &monthPositions() const; + /** Returns list of day numbers of a month. */ + const QPtrList<int> &monthDays() const; + + /** Sets an event to recur yearly. + * @var type rYearlyMonth, rYearlyPos or rYearlyDay + * @var freq the frequency to recur, e.g. 3 for every third year. + * @var duration the number of times the event is to occur, or -1 to recur indefinitely. + */ + void setYearly(int type, int freq, int duration); + /** Sets an event to recur yearly ending at \a endDate. */ + void setYearly(int type, int freq, const QDate &endDate); + /** Sets an event to recur yearly on specified dates. + * The dates must be specified by calling addYearlyNum(). + * @var type the way recurrences of February 29th are to be handled in non-leap years. + * @var freq the frequency to recur, e.g. 3 for every third year. + * @var duration the number of times the event is to occur, or -1 to recur indefinitely. + */ + void setYearlyByDate(Feb29Type type, int freq, int duration); + /** Sets an event to recur yearly ending at \a endDate. */ + void setYearlyByDate(Feb29Type type, int freq, const QDate &endDate); + /** Adds position of day or month in year. + * N.B. for recursYearlyPos, addYearlyMonthPos() must also be called + * to add positions within the month. */ + void addYearlyNum(short _rNum); + /** Adds a position to the recursYearlyPos recurrence rule, if it is set. + * N.B. addYearlyNum() must also be called to add recurrence months. + * Parameters are the same as for addMonthlyPos(). + */ + void addYearlyMonthPos(short _rPos, const QBitArray &_rDays); + /** Returns positions of days or months in year. */ + const QPtrList<int> &yearNums() const; + /** Returns list of day positions in months, for a recursYearlyPos recurrence rule. */ + const QPtrList<rMonthPos> &yearMonthPositions() const; + /** Returns how yearly recurrences of February 29th are handled. */ + Feb29Type feb29YearlyType() const { return mFeb29YearlyType; } + /** Sets the default method for handling yearly recurrences of February 29th. */ + static void setFeb29YearlyTypeDefault(Feb29Type t) { mFeb29YearlyDefaultType = t; } + /** Returns the default method for handling yearly recurrences of February 29th. */ + static Feb29Type setFeb29YearlyTypeDefault() { return mFeb29YearlyDefaultType; } + + /** + Debug output. + */ + void dump() const; + QString recurrenceText() const; + + protected: + enum PeriodFunc { END_DATE_AND_COUNT, COUNT_TO_DATE, NEXT_AFTER_DATE }; + struct MonthlyData; friend struct MonthlyData; + struct YearlyMonthData; friend struct YearlyMonthData; + struct YearlyPosData; friend struct YearlyPosData; + struct YearlyDayData; friend struct YearlyDayData; + + bool recursSecondly(const QDate &, int secondFreq) const; + bool recursMinutelyAt(const QDateTime &dt, int minuteFreq) const; + bool recursDaily(const QDate &) const; + bool recursWeekly(const QDate &) const; + bool recursMonthly(const QDate &) const; + bool recursYearlyByMonth(const QDate &) const; + bool recursYearlyByPos(const QDate &) const; + bool recursYearlyByDay(const QDate &) const; + + QDate getNextDateNoTime(const QDate& preDate, bool* last) const; + QDate getPreviousDateNoTime(const QDate& afterDate, bool* last) const; + + void addMonthlyPos_(short _rPos, const QBitArray &_rDays); + void setDailySub(short type, int freq, int duration); + void setYearly_(short type, Feb29Type, int freq, int duration); + int recurCalc(PeriodFunc, QDate &enddate) const; + int recurCalc(PeriodFunc, QDateTime &endtime) const; + int secondlyCalc(PeriodFunc, QDateTime& endtime, int freq) const; + int dailyCalc(PeriodFunc, QDate &enddate) const; + int weeklyCalc(PeriodFunc, QDate &enddate) const; + int weeklyCalcEndDate(QDate& enddate, int daysPerWeek) const; + int weeklyCalcToDate(const QDate& enddate, int daysPerWeek) const; + int weeklyCalcNextAfter(QDate& enddate, int daysPerWeek) const; + int monthlyCalc(PeriodFunc, QDate &enddate) const; + int monthlyCalcEndDate(QDate& enddate, MonthlyData&) const; + int monthlyCalcToDate(const QDate& enddate, MonthlyData&) const; + int monthlyCalcNextAfter(QDate& enddate, MonthlyData&) const; + int yearlyMonthCalc(PeriodFunc, QDate &enddate) const; + int yearlyMonthCalcEndDate(QDate& enddate, YearlyMonthData&) const; + int yearlyMonthCalcToDate(const QDate& enddate, YearlyMonthData&) const; + int yearlyMonthCalcNextAfter(QDate& enddate, YearlyMonthData&) const; + int yearlyPosCalc(PeriodFunc, QDate &enddate) const; + int yearlyPosCalcEndDate(QDate& enddate, YearlyPosData&) const; + int yearlyPosCalcToDate(const QDate& enddate, YearlyPosData&) const; + int yearlyPosCalcNextAfter(QDate& enddate, YearlyPosData&) const; + int yearlyDayCalc(PeriodFunc, QDate &enddate) const; + int yearlyDayCalcEndDate(QDate& enddate, YearlyDayData&) const; + int yearlyDayCalcToDate(const QDate& enddate, YearlyDayData&) const; + int yearlyDayCalcNextAfter(QDate& enddate, YearlyDayData&) const; + + int countMonthlyPosDays() const; + void getMonthlyPosDays(QValueList<int>&, int daysInMonth, + int startDayOfWeek) const; + bool getMonthlyDayDays(QValueList<int>&, int daysInMonth) const; + bool getYearlyMonthMonths(int day, QValueList<int>&, + QValueList<int> &leaplist) const; + + int getFirstDayInWeek(int startDay, bool useWeekStart = true) const; + int getLastDayInWeek(int endDay, bool useWeekStart = true) const; + QDate getFirstDateInMonth(const QDate& earliestDate) const; + QDate getLastDateInMonth(const QDate& latestDate) const; + QDate getFirstDateInYear(const QDate& earliestDate) const; + QDate getLastDateInYear(const QDate& latestDate) const; + + private: + // Prohibit copying + Recurrence(const Recurrence&); + Recurrence &operator=(const Recurrence&); + + short recurs; // should be one of the enums. + + int rWeekStart; // day which starts the week, Monday=1 .. Sunday=7 + QBitArray rDays; // array of days during week it recurs + + QPtrList<rMonthPos> rMonthPositions; // list of positions during a month + // on which an event recurs + + QPtrList<int> rMonthDays; // list of days during a month on + // which the event recurs + + QPtrList<int> rYearNums; // either months/days to recur on for rYearly, + // sorted in numerical order + + int rFreq; // frequency of period + + // one of the following must be specified + int rDuration; // num times to recur (inc. first occurrence), -1 = infinite + QDateTime rEndDateTime; // date/time at which to end recurrence + + QDateTime mRecurStart; // date/time of first recurrence + bool mFloats; // the recurrence has no time, just a date + bool mRecurReadOnly; + int mRecurExDatesCount; // number of recurrences (in addition to rDuration) which are excluded + Feb29Type mFeb29YearlyType; // how to handle yearly recurrences of February 29th + static Feb29Type mFeb29YearlyDefaultType; // default value for mFeb29YearlyType + + // Backwards compatibility for KDE < 3.1. + int mCompatVersion; // calendar file version for backwards compatibility + short mCompatRecurs; // original 'recurs' in old calendar format, or rNone + int mCompatDuration; // original 'rDuration' in old calendar format, or 0 + + Incidence *mParent; +}; + +} + +#endif diff --git a/libkcal/resourcecalendar.h b/libkcal/resourcecalendar.h new file mode 100644 index 0000000..26b3831 --- a/dev/null +++ b/libkcal/resourcecalendar.h @@ -0,0 +1,16 @@ +#ifndef MICRO_KCAL_RESOURCECALENDAR_H +#define MICRO_KCAL_RESOURCECALENDAR_H + +namespace KCal { + +class ResourceCalendar +{ +}; + +class CalendarResourceManager +{ +}; + +} + +#endif diff --git a/libkcal/scheduler.cpp b/libkcal/scheduler.cpp new file mode 100644 index 0000000..253d8b7 --- a/dev/null +++ b/libkcal/scheduler.cpp @@ -0,0 +1,355 @@ +/* + This file is part of libkcal. + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include <qdir.h> +#include <qfile.h> +#include <qtextstream.h> + +#include <klocale.h> +#include <kdebug.h> +#include <kstandarddirs.h> + +#include "event.h" +#include "todo.h" +#include "freebusy.h" +#include "icalformat.h" +#include "calendar.h" + +#include "scheduler.h" + +using namespace KCal; + +ScheduleMessage::ScheduleMessage(IncidenceBase *incidence,int method,ScheduleMessage::Status status) +{ + mIncidence = incidence; + mMethod = method; + mStatus = status; +} + +QString ScheduleMessage::statusName(ScheduleMessage::Status status) +{ + switch (status) { + case PublishNew: + return i18n("Publish"); + case Obsolete: + return i18n("Obsolete"); + case RequestNew: + return i18n("New Request"); + case RequestUpdate: + return i18n("Updated Request"); + default: + return i18n("Unknown Status: %1").arg(QString::number(status)); + } +} + +Scheduler::Scheduler(Calendar *calendar) +{ + mCalendar = calendar; + mFormat = new ICalFormat(); +} + +Scheduler::~Scheduler() +{ + delete mFormat; +} + +bool Scheduler::acceptTransaction(IncidenceBase *incidence,Method method,ScheduleMessage::Status status) +{ + kdDebug() << "Scheduler::acceptTransaction " << endl; + switch (method) { + case Publish: + return acceptPublish(incidence, status, method); + case Request: + return acceptRequest(incidence, status); + case Add: + return acceptAdd(incidence, status); + case Cancel: + return acceptCancel(incidence, status); + case Declinecounter: + return acceptDeclineCounter(incidence, status); + case Reply: + return acceptReply(incidence, status, method); + case Refresh: + return acceptRefresh(incidence, status); + case Counter: + return acceptCounter(incidence, status); + default: + deleteTransaction(incidence); + return false; + } + deleteTransaction(incidence); + return false; +} + +QString Scheduler::methodName(Method method) +{ + switch (method) { + case Publish: + return QString::fromLatin1("Publish"); + case Request: + return QString::fromLatin1("Request"); + case Refresh: + return QString::fromLatin1("Refresh"); + case Cancel: + return QString::fromLatin1("Cancel"); + case Add: + return QString::fromLatin1("Add"); + case Reply: + return QString::fromLatin1("Reply"); + case Counter: + return QString::fromLatin1("Counter"); + case Declinecounter: + return QString::fromLatin1("Decline Counter"); + default: + return QString::fromLatin1("Unknown"); + } +} + +QString Scheduler::translatedMethodName(Method method) +{ + switch (method) { + case Publish: + return i18n("Publish"); + case Request: + return i18n("Request"); + case Refresh: + return i18n("Refresh"); + case Cancel: + return i18n("Cancel"); + case Add: + return i18n("Add"); + case Reply: + return i18n("Reply"); + case Counter: + return i18n("counter proposal","Counter"); + case Declinecounter: + return i18n("decline counter proposal","Decline Counter"); + default: + return i18n("Unknown"); + } +} + +bool Scheduler::deleteTransaction(IncidenceBase *) +{ + return true; +} + +bool Scheduler::acceptPublish(IncidenceBase *incidence,ScheduleMessage::Status status, Method method) +{ + if(incidence->type()=="FreeBusy") { + return acceptFreeBusy(incidence, method); + } + switch (status) { + case ScheduleMessage::Unknown: + case ScheduleMessage::PublishNew: + if (!mCalendar->event(incidence->uid())) { + Incidence *inc = static_cast<Incidence *>(incidence); + mCalendar->addIncidence(inc); + deleteTransaction(incidence); + } + return true; + case ScheduleMessage::Obsolete: + return true; + default: + deleteTransaction(incidence); + return false; + } + deleteTransaction(incidence); + return false; +} + +bool Scheduler::acceptRequest(IncidenceBase *incidence,ScheduleMessage::Status status) +{ + Incidence *inc = static_cast<Incidence *>(incidence); + if (inc->type()=="FreeBusy") { + // reply to this request is handled in korganizer's incomingdialog + return true; + } else { + Event *even = mCalendar->event(incidence->uid()); + if (even) { + if ( even->revision()<=inc->revision() ) { + if ( even->revision()==inc->revision() && + even->lastModified()>inc->lastModified()) { + deleteTransaction(incidence); + return false; + } + mCalendar->deleteEvent(even); + } else { + deleteTransaction(incidence); + return false; + } + } else { + Todo *todo = mCalendar->todo(incidence->uid()); + if (todo) { + if ( todo->revision()<=inc->revision() ) { + if ( todo->revision()==inc->revision() && + todo->lastModified()>inc->lastModified()) { + deleteTransaction(incidence); + return false; + } + mCalendar->deleteTodo(todo); + } else { + deleteTransaction(incidence); + return false; + } + } + } + } + mCalendar->addIncidence(inc); + deleteTransaction(incidence); + return true; +} + +bool Scheduler::acceptAdd(IncidenceBase *incidence,ScheduleMessage::Status status) +{ + deleteTransaction(incidence); + return false; +} + +bool Scheduler::acceptCancel(IncidenceBase *incidence,ScheduleMessage::Status status) +{ + bool ret = false; + Event *even = mCalendar->event(incidence->uid()); + if (even) { + mCalendar->deleteEvent(even); + ret = true; + } else { + Todo *todo = mCalendar->todo(incidence->uid()); + if (todo) { + mCalendar->deleteTodo(todo); + ret = true; + } + } + deleteTransaction(incidence); + return ret; +} + +bool Scheduler::acceptDeclineCounter(IncidenceBase *incidence,ScheduleMessage::Status status) +{ + deleteTransaction(incidence); + return false; +} + +//bool Scheduler::acceptFreeBusy(Incidence *incidence,ScheduleMessage::Status status) +//{ +// deleteTransaction(incidence); +// return false; +//} + +bool Scheduler::acceptReply(IncidenceBase *incidence,ScheduleMessage::Status status, Method method) +{ + if(incidence->type()=="FreeBusy") { + return acceptFreeBusy(incidence, method); + } + bool ret = false; + Event *ev = mCalendar->event(incidence->uid()); + Todo *to = mCalendar->todo(incidence->uid()); + if (ev || to) { + //get matching attendee in calendar + kdDebug(5800) << "Scheduler::acceptTransaction match found!" << endl; + QPtrList<Attendee> attendeesIn = incidence->attendees(); + QPtrList<Attendee> attendeesEv; + if (ev) attendeesEv = ev->attendees(); + if (to) attendeesEv = to->attendees(); + Attendee *attIn; + Attendee *attEv; + for ( attIn = attendeesIn.first(); attIn; attIn = attendeesIn.next() ) { + for ( attEv = attendeesEv.first(); attEv; attEv = attendeesEv.next() ) { + if (attIn->email()==attEv->email()) { + //update attendee-info + kdDebug(5800) << "Scheduler::acceptTransaction update attendee" << endl; + attEv->setStatus(attIn->status()); + attEv->setRSVP(false); + // better to not update the sequence number with replys + //if (ev) ev->setRevision(ev->revision()+1); + //if (to) to->setRevision(to->revision()+1); + ret = true; + } + } + } + } + if (ret) deleteTransaction(incidence); + return ret; +} + +bool Scheduler::acceptRefresh(IncidenceBase *incidence,ScheduleMessage::Status status) +{ + // handled in korganizer's IncomingDialog + deleteTransaction(incidence); + return false; +} + +bool Scheduler::acceptCounter(IncidenceBase *incidence,ScheduleMessage::Status status) +{ + deleteTransaction(incidence); + return false; +} + +bool Scheduler::acceptFreeBusy(IncidenceBase *incidence, Method method) +{ + FreeBusy *freebusy = static_cast<FreeBusy *>(incidence); + + QString freeBusyDirName = locateLocal("appdata","freebusy"); + kdDebug() << "acceptFreeBusy:: freeBusyDirName: " << freeBusyDirName << endl; + + QString from; + if(method == Scheduler::Publish) { + from = freebusy->organizer(); + } + if((method == Scheduler::Reply) && (freebusy->attendeeCount() == 1)) { + Attendee *attendee = freebusy->attendees().first(); + from = attendee->email(); + } + + QDir freeBusyDir(freeBusyDirName); + if (!freeBusyDir.exists()) { + kdDebug() << "Directory " << freeBusyDirName << " does not exist!" << endl; + kdDebug() << "Creating directory: " << freeBusyDirName << endl; + + if(!freeBusyDir.mkdir(freeBusyDirName, TRUE)) { + kdDebug() << "Could not create directory: " << freeBusyDirName << endl; + return false; + } + } + + QString filename(freeBusyDirName); + filename += "/"; + filename += from; + filename += ".ifb"; + QFile f(filename); + + kdDebug() << "acceptFreeBusy: filename" << filename << endl; + + freebusy->clearAttendees(); + freebusy->setOrganizer(from); + + QString messageText = mFormat->createScheduleMessage(freebusy, Publish); + + if (!f.open(IO_ReadWrite)) { + kdDebug() << "acceptFreeBusy: Can't open:" << filename << " for writing" << endl; + return false; + } + QTextStream t(&f); + t << messageText; + f.close(); + + deleteTransaction(incidence); + return true; +} diff --git a/libkcal/scheduler.h b/libkcal/scheduler.h new file mode 100644 index 0000000..a9f43b9 --- a/dev/null +++ b/libkcal/scheduler.h @@ -0,0 +1,133 @@ +/* + This file is part of libkcal. + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef SCHEDULER_H +#define SCHEDULER_H + +// iTIP transactions base class + +#include <qstring.h> +#include <qptrlist.h> + +namespace KCal { + +class IncidenceBase; +class Event; +class Calendar; +class ICalFormat; + +/** + This class provides an encapsulation of a scheduling message. It associates an + incidence with a method and status information. This class is used by the + Scheduler class. + + @short A Scheduling message +*/ +class ScheduleMessage { + public: + /** Message status. */ + enum Status { PublishNew, Obsolete, RequestNew, RequestUpdate, Unknown }; + + /** + Create a scheduling message with method as defined in Scheduler::Method + and a status. + */ + ScheduleMessage(IncidenceBase *,int method,Status status); + ~ScheduleMessage() {}; + + /** Return event associated with this message. */ + IncidenceBase *event() { return mIncidence; } + /** Return iTIP method associated with this message. */ + int method() { return mMethod; } + /** Return status of this message. */ + Status status() { return mStatus; } + /** Return error message if there is any. */ + QString error() { return mError; } + + /** Return a human-readable name for an ical message status. */ + static QString statusName(Status status); + + private: + IncidenceBase *mIncidence; + int mMethod; + Status mStatus; + QString mError; +}; + +/** + This class provides an encapsulation of iTIP transactions. It is an abstract + base class for inheritance by implementations of the iTIP scheme like iMIP or + iRIP. +*/ +class Scheduler { + public: + /** iTIP methods. */ + enum Method { Publish,Request,Refresh,Cancel,Add,Reply,Counter, + Declinecounter,NoMethod }; + + /** Create scheduler for calendar specified as argument. */ + Scheduler(Calendar *calendar); + virtual ~Scheduler(); + + /** iTIP publish action */ + virtual bool publish (IncidenceBase *incidence,const QString &recipients) = 0; + /** Perform iTIP transaction on incidence. The method is specified as the + method argumanet and can be any valid iTIP method. */ + virtual bool performTransaction(IncidenceBase *incidence,Method method) = 0; + /** Perform iTIP transaction on incidence to specified recipient(s). The + method is specified as the method argumanet and can be any valid iTIP + method. */ + virtual bool performTransaction(IncidenceBase *incidence,Method method,const QString &recipients) = 0; + /** Retrieve incoming iTIP transactions */ + virtual QPtrList<ScheduleMessage> retrieveTransactions() = 0; + + /** + Accept transaction. The incidence argument specifies the iCal compoennt + on which the transaction acts. The status is the result of processing a + iTIP message with the current calendar and specifies the action to be + taken for this incidence. + */ + bool acceptTransaction(IncidenceBase *,Method method,ScheduleMessage::Status status); + + /** Return a machine-readable name for a iTIP method. */ + static QString methodName(Method); + /** Return a translated and human-readable name for a iTIP method. */ + static QString translatedMethodName(Method); + + virtual bool deleteTransaction(IncidenceBase *incidence); + + protected: + + bool acceptPublish(IncidenceBase *,ScheduleMessage::Status status, Method method); + bool acceptRequest(IncidenceBase *,ScheduleMessage::Status status); + bool acceptAdd(IncidenceBase *,ScheduleMessage::Status status); + bool acceptCancel(IncidenceBase *,ScheduleMessage::Status status); + bool acceptDeclineCounter(IncidenceBase *,ScheduleMessage::Status status); + bool acceptReply(IncidenceBase *,ScheduleMessage::Status status, Method method); + bool acceptRefresh(IncidenceBase *,ScheduleMessage::Status status); + bool acceptCounter(IncidenceBase *,ScheduleMessage::Status status); + bool acceptFreeBusy(IncidenceBase *,Method method); + + Calendar *mCalendar; + ICalFormat *mFormat; +}; + +} + +#endif // SCHEDULER_H diff --git a/libkcal/sharpformat.cpp b/libkcal/sharpformat.cpp new file mode 100644 index 0000000..f83f72e --- a/dev/null +++ b/libkcal/sharpformat.cpp @@ -0,0 +1,1007 @@ +/* + This file is part of libkcal. + + Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include <qdatetime.h> +#include <qstring.h> +#include <qapplication.h> +#include <qptrlist.h> +#include <qregexp.h> +#include <qmessagebox.h> +#include <qclipboard.h> +#include <qfile.h> +#include <qtextstream.h> +#include <qtextcodec.h> +#include <qxml.h> +#include <qlabel.h> + +#include <kdebug.h> +#include <klocale.h> +#include <kglobal.h> + +#include "calendar.h" +#include "alarm.h" +#include "recurrence.h" +#include "calendarlocal.h" + +#include "sharpformat.h" + +using namespace KCal; + +//CARDID,CATEGORY,DSRP,PLCE,MEM1,TIM1,TIM2,ADAY,ARON,ARMN,ARSD,RTYP,RFRQ,RPOS,RDYS,REND,REDT,ALSD,ALED,MDAY +// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 + +//ARSD silentalarm = 0 +// 11 RTYP 225 no /0 dialy/ 1 weekly/ 3 month by date/ 2 month by day(pos)/ yearly +// 12 RFRQ +// 13 RPOS pos = 4. monday in month +// 14 RDYS days: 1 mon/ 2 tue .. 64 sun +// 15 REND 0 = no end/ 1 = end +// 16 REDT rec end dt +//ALSD +//ALED +//MDAY + +class SharpParser : public QObject +{ + public: + SharpParser( Calendar *calendar ) : mCalendar( calendar ) { + oldCategories = 0; + } + + bool startElement( Calendar *existingCalendar, const QStringList & attList, QString qName ) + { + int i = 1; + bool skip = true; + int max = attList.count() -2; + while ( i < max ) { + if ( !attList[i].isEmpty() ) { + skip = false; + break; + } + ++i ; + } + if ( skip ) + return false; + ulong cSum = SharpFormat::getCsum(attList ); + + if ( qName == "Event" ) { + Event *event; + event = existingCalendar->event( attList[0].toInt() ); + if ( event ) + event = (Event*)event->clone(); + else + event = new Event; + event->setZaurusId( attList[0].toInt() ); + event->setZaurusUid( cSum ); + event->setZaurusStat( -2 ); + + event->setSummary( attList[2] ); + event->setLocation( attList[3] ); + event->setDescription( attList[4] ); + if ( attList[7] == "1" ) { + event->setDtStart( QDateTime(fromString( attList[17]+"000000", false ).date(),QTime(0,0,0 ) )); + event->setDtEnd( QDateTime(fromString( attList[18]+"000000", false ).date(),QTime(0,0,0 ))); + event->setFloats( true ); + } else { + event->setFloats( false ); + event->setDtStart( fromString( attList[5] ) ); + event->setDtEnd( fromString( attList[6] )); + } + + QString rtype = attList[11]; + if ( rtype != "255" ) { + // qDebug("recurs "); + QDate startDate = event->dtStart().date(); + + QString freqStr = attList[12]; + int freq = freqStr.toInt(); + + QString hasEndDateStr = attList[15] ; + bool hasEndDate = hasEndDateStr == "1"; + + QString endDateStr = attList[16]; + QDate endDate = fromString( endDateStr ).date(); + + QString weekDaysStr = attList[14]; + uint weekDaysNum = weekDaysStr.toInt(); + + QBitArray weekDays( 7 ); + int i; + int bb = 1; + for( i = 1; i <= 7; ++i ) { + weekDays.setBit( i - 1, ( bb & weekDaysNum )); + bb = 2 << (i-1); + //qDebug(" %d bit %d ",i-1,weekDays.at(i-1) ); + } + // qDebug("next "); + QString posStr = attList[13]; + int pos = posStr.toInt(); + Recurrence *r = event->recurrence(); + + if ( rtype == "0" ) { + if ( hasEndDate ) r->setDaily( freq, endDate ); + else r->setDaily( freq, -1 ); + } else if ( rtype == "1" ) { + if ( hasEndDate ) r->setWeekly( freq, weekDays, endDate ); + else r->setWeekly( freq, weekDays, -1 ); + } else if ( rtype == "3" ) { + if ( hasEndDate ) + r->setMonthly( Recurrence::rMonthlyDay, freq, endDate ); + else + r->setMonthly( Recurrence::rMonthlyDay, freq, -1 ); + r->addMonthlyDay( startDate.day() ); + } else if ( rtype == "2" ) { + if ( hasEndDate ) + r->setMonthly( Recurrence::rMonthlyPos, freq, endDate ); + else + r->setMonthly( Recurrence::rMonthlyPos, freq, -1 ); + QBitArray days( 7 ); + days.fill( false ); + days.setBit( startDate.dayOfWeek() - 1 ); + r->addMonthlyPos( pos, days ); + } else if ( rtype == "4" ) { + if ( hasEndDate ) + r->setYearly( Recurrence::rYearlyMonth, freq, endDate ); + else + r->setYearly( Recurrence::rYearlyMonth, freq, -1 ); + r->addYearlyNum( startDate.month() ); + } + } + + QString categoryList = attList[1] ; + event->setCategories( lookupCategories( categoryList ) ); + + // strange 0 semms to mean: alarm enabled + if ( attList[8] == "0" ) { + Alarm *alarm; + if ( event->alarms().count() > 0 ) + alarm = event->alarms().first(); + else { + alarm = new Alarm( event ); + event->addAlarm( alarm ); + } + alarm->setType( Alarm::Audio ); + alarm->setEnabled( true ); + int alarmOffset = attList[9].toInt(); + alarm->setStartOffset( alarmOffset * -60 ); + } + + mCalendar->addEvent( event); + } else if ( qName == "Todo" ) { + Todo *todo; + + todo = existingCalendar->todo( attList[0].toInt() ); + if (todo ) + todo = (Todo*)todo->clone(); + else + todo = new Todo; + +//CARDID,CATEGORY,ETDY,LTDY,FNDY,MARK,PRTY,TITL,MEM1 +// 0 1 2 3 4 5 6 7 8 +//1,,,,,1,4,Loch zumachen,"" +//3,Privat,20040317T000000,20040318T000000,20040319T000000,0,5,Call bbb,"notes123 bbb gggg ""bb "" " +//2,"Familie,Freunde,Holiday",20040318T000000,20040324T000000,20040317T000000,1,2,tod2,notes + + todo->setZaurusId( attList[0].toInt() ); + todo->setZaurusUid( cSum ); + todo->setZaurusStat( -2 ); + + todo->setSummary( attList[7] ); + todo->setDescription( attList[8]); + + int priority = attList[6].toInt(); + if ( priority == 0 ) priority = 3; + todo->setPriority( priority ); + + QString categoryList = attList[1]; + todo->setCategories( lookupCategories( categoryList ) ); + + + + QString hasDateStr = attList[3]; // due + if ( !hasDateStr.isEmpty() ) { + if ( hasDateStr.right(6) == "000000" ) { + todo->setDtDue( QDateTime(fromString( hasDateStr, false ).date(), QTime(0,0,0 )) ); + todo->setFloats( true ); + } + else { + todo->setDtDue( fromString( hasDateStr ) ); + todo->setFloats( false ); + } + + todo->setHasDueDate( true ); + } + hasDateStr = attList[2];//start + if ( !hasDateStr.isEmpty() ) { + + todo->setDtStart( fromString( hasDateStr ) ); + todo->setHasStartDate( true); + } else + todo->setHasStartDate( false ); + hasDateStr = attList[4];//completed + if ( !hasDateStr.isEmpty() ) { + todo->setCompleted(fromString( hasDateStr ) ); + } + QString completedStr = attList[5]; + if ( completedStr == "0" ) + todo->setCompleted( true ); + else + todo->setCompleted( false ); + mCalendar->addTodo( todo ); + + } else if ( qName == "Category" ) { + /* + QString id = attributes.value( "id" ); + QString name = attributes.value( "name" ); + setCategory( id, name ); + */ + } + //qDebug("end "); + return true; + } + + + void setCategoriesList ( QStringList * c ) + { + oldCategories = c; + } + + QDateTime fromString ( QString s, bool useTz = true ) { + QDateTime dt; + int y,m,t,h,min,sec; + y = s.mid(0,4).toInt(); + m = s.mid(4,2).toInt(); + t = s.mid(6,2).toInt(); + h = s.mid(9,2).toInt(); + min = s.mid(11,2).toInt(); + sec = s.mid(13,2).toInt(); + dt = QDateTime(QDate(y,m,t), QTime(h,min,sec)); + int offset = KGlobal::locale()->localTimeOffset( dt ); + if ( useTz ) + dt = dt.addSecs ( offset*60); + return dt; + + } + protected: + QDateTime toDateTime( const QString &value ) + { + QDateTime dt; + dt.setTime_t( value.toUInt() ); + + return dt; + } + + QStringList lookupCategories( const QString &categoryList ) + { + QStringList categoryIds = QStringList::split( ";", categoryList ); + QStringList categories; + QStringList::ConstIterator it; + for( it = categoryIds.begin(); it != categoryIds.end(); ++it ) { + QString cate = category( *it ); + if ( oldCategories ) { + if ( ! oldCategories->contains( cate ) ) + oldCategories->append( cate ); + } + categories.append(cate ); + } + return categories; + } + + private: + Calendar *mCalendar; + QStringList * oldCategories; + static QString category( const QString &id ) + { + QMap<QString,QString>::ConstIterator it = mCategoriesMap.find( id ); + if ( it == mCategoriesMap.end() ) return id; + else return *it; + } + + static void setCategory( const QString &id, const QString &name ) + { + mCategoriesMap.insert( id, name ); + } + + static QMap<QString,QString> mCategoriesMap; +}; + +QMap<QString,QString> SharpParser::mCategoriesMap; + +SharpFormat::SharpFormat() +{ + mCategories = 0; +} + +SharpFormat::~SharpFormat() +{ +} +ulong SharpFormat::getCsum( const QStringList & attList) +{ + int max = attList.count() -1; + ulong cSum = 0; + int j,k,i; + int add; + for ( i = 1; i < max ; ++i ) { + QString s = attList[i]; + if ( ! s.isEmpty() ){ + j = s.length(); + for ( k = 0; k < j; ++k ) { + int mul = k +1; + add = s[k].unicode (); + if ( k < 16 ) + mul = mul * mul; + add = add * mul *i*i*i; + cSum += add; + } + } + } + return cSum; + +} +#include <stdlib.h> +#define DEBUGMODE false +bool SharpFormat::load( Calendar *calendar, Calendar *existngCal ) +{ + + + bool debug = DEBUGMODE; + //debug = true; + QString text; + QString codec = "utf8"; + QLabel status ( i18n("Reading events ..."), 0 ); + + int w = status.sizeHint().width()+20 ; + if ( w < 200 ) w = 200; + int h = status.sizeHint().height()+20 ; + int dw = QApplication::desktop()->width(); + int dh = QApplication::desktop()->height(); + status.setCaption(i18n("Reading DTM Data") ); + status.setGeometry( (dw-w)/2, (dh - h )/2 ,w,h ); + status.show(); + status.raise(); + qApp->processEvents(); + QString fileName; + if ( ! debug ) { + fileName = "/tmp/kopitempout"; + QString command ="db2file datebook -r -c "+ codec + " > " + fileName; + system ( command.latin1() ); + } else { + fileName = "/tmp/events.txt"; + + } + QFile file( fileName ); + if (!file.open( IO_ReadOnly ) ) { + return false; + + } + QTextStream ts( &file ); + ts.setCodec( QTextCodec::codecForName("utf8") ); + text = ts.read(); + file.close(); + status.setText( i18n("Processing events ...") ); + status.raise(); + qApp->processEvents(); + fromString2Cal( calendar, existngCal, text, "Event" ); + status.setText( i18n("Reading todos ...") ); + qApp->processEvents(); + if ( ! debug ) { + fileName = "/tmp/kopitempout"; + QString command = "db2file todo -r -c " + codec+ " > " + fileName; + system ( command.latin1() ); + } else { + fileName = "/tmp/todo.txt"; + } + file.setName( fileName ); + if (!file.open( IO_ReadOnly ) ) { + return false; + + } + ts.setDevice( &file ); + text = ts.read(); + file.close(); + + status.setText( i18n("Processing todos ...") ); + status.raise(); + qApp->processEvents(); + fromString2Cal( calendar, existngCal, text, "Todo" ); + return true; +} +int SharpFormat::getNumFromRecord( QString answer, Incidence* inc ) +{ + int retval = -1; + QStringList templist; + QString tempString; + int start = 0; + int len = answer.length(); + int end = answer.find ("\n",start)+1; + bool ok = true; + start = end; + int ccc = 0; + while ( start > 0 ) { + templist.clear(); + ok = true; + int loopCount = 0; + while ( ok ) { + ++loopCount; + if ( loopCount > 25 ) { + qDebug("KO: Error in while loop"); + ok = false; + start = 0; + break; + } + if ( ok ) + tempString = getPart( answer, ok, start ); + if ( start >= len || start == 0 ) { + start = 0; + ok = false; + } + if ( tempString.right(1) =="\n" ) + tempString = tempString.left( tempString.length()-1); + + templist.append( tempString ); + } + ++ccc; + if ( ccc == 2 && loopCount < 25 ) { + start = 0; + bool ok; + int newnum = templist[0].toInt( &ok ); + if ( ok && newnum > 0) { + retval = newnum; + inc->setZaurusId( newnum ); + inc->setZaurusUid( getCsum( templist ) ); + inc->setZaurusStat( -4 ); + } + } + } + //qDebug("getNumFromRecord returning : %d ", retval); + return retval; +} +bool SharpFormat::save( Calendar *calendar) +{ + + QLabel status ( i18n("Processing/adding events ..."), 0 ); + int w = status.sizeHint().width()+20 ; + if ( w < 200 ) w = 200; + int h = status.sizeHint().height()+20 ; + int dw = QApplication::desktop()->width(); + int dh = QApplication::desktop()->height(); + status.setCaption(i18n("Writing DTM Data") ); + status.setGeometry( (dw-w)/2, (dh - h )/2 ,w,h ); + status.show(); + status.raise(); + qApp->processEvents(); + bool debug = DEBUGMODE; + QString codec = "utf8"; + QString answer; + QString ePrefix = "CARDID,CATEGORY,DSRP,PLCE,MEM1,TIM1,TIM2,ADAY,ARON,ARMN,ARSD,RTYP,RFRQ,RPOS,RDYS,REND,REDT,ALSD,ALED,MDAY\n"; + QString tPrefix = "CARDID,CATEGORY,ETDY,LTDY,FNDY,MARK,PRTY,TITL,MEM1\n"; + QString command; + QPtrList<Event> er = calendar->rawEvents(); + Event* ev = er.first(); + QString fileName = "/tmp/kopitempout"; + int i = 0; + QString changeString = ePrefix; + QString deleteString = ePrefix; + bool deleteEnt = false; + bool changeEnt = false; + QString message = i18n("Processing event # "); + int procCount = 0; + while ( ev ) { + //qDebug("i %d ", ++i); + if ( ev->zaurusStat() != -2 ) { + status.setText ( message + QString::number ( ++procCount ) ); + qApp->processEvents(); + QString eString = getEventString( ev ); + if ( ev->zaurusStat() == -3 ) { // delete + // deleting empty strings does not work. + // we write first and x and then delete the record with the x + eString = eString.replace( QRegExp(",\"\""),",\"x\"" ); + changeString += eString + "\n"; + deleteString += eString + "\n"; + deleteEnt = true; + changeEnt = true; + } + else if ( ev->zaurusId() == -1 ) { // add new + command = "(echo \"" + ePrefix + eString + "\" ) | db2file datebook -w -g -c " + codec+ " > "+ fileName; + system ( command.utf8() ); + QFile file( fileName ); + if (!file.open( IO_ReadOnly ) ) { + return false; + + } + QTextStream ts( &file ); + ts.setCodec( QTextCodec::codecForName("utf8") ); + answer = ts.read(); + file.close(); + //qDebug("answer \n%s ", answer.latin1()); + getNumFromRecord( answer, ev ) ; + + } + else { // change existing + //qDebug("canging %d %d",ev->zaurusStat() ,ev->zaurusId() ); + //command = "(echo \"" + ePrefix + eString + "\" ) | db2file datebook -w -g -c " + codec+ " > "+ fileName; + changeString += eString + "\n"; + changeEnt = true; + + } + } + ev = er.next(); + } + status.setText ( i18n("Changing events ...") ); + qApp->processEvents(); + //qDebug("changing... "); + if ( changeEnt ) { + QFile file( fileName ); + if (!file.open( IO_WriteOnly ) ) { + return false; + + } + QTextStream ts( &file ); + ts.setCodec( QTextCodec::codecForName("utf8") ); + ts << changeString ; + file.close(); + command = "db2file datebook -w -g -c " + codec+ " < "+ fileName; + system ( command.latin1() ); + //qDebug("command %s file :\n%s ", command.latin1(), changeString.latin1()); + + } + status.setText ( i18n("Deleting events ...") ); + qApp->processEvents(); + //qDebug("deleting... "); + if ( deleteEnt ) { + QFile file( fileName ); + if (!file.open( IO_WriteOnly ) ) { + return false; + + } + QTextStream ts( &file ); + ts.setCodec( QTextCodec::codecForName("utf8") ); + ts << deleteString; + file.close(); + command = "db2file datebook -d -c " + codec+ " < "+ fileName; + system ( command.latin1() ); + // qDebug("command %s file :\n%s ", command.latin1(), deleteString.latin1()); + } + + + changeString = tPrefix; + deleteString = tPrefix; + status.setText ( i18n("Processing todos ...") ); + qApp->processEvents(); + QPtrList<Todo> tl = calendar->rawTodos(); + Todo* to = tl.first(); + i = 0; + message = i18n("Processing todo # "); + procCount = 0; + while ( to ) { + if ( to->zaurusStat() != -2 ) { + status.setText ( message + QString::number ( ++procCount ) ); + qApp->processEvents(); + QString eString = getTodoString( to ); + if ( to->zaurusStat() == -3 ) { // delete + // deleting empty strings does not work. + // we write first and x and then delete the record with the x + eString = eString.replace( QRegExp(",\"\""),",\"x\"" ); + changeString += eString + "\n"; + deleteString += eString + "\n"; + deleteEnt = true; + changeEnt = true; + } + else if ( to->zaurusId() == -1 ) { // add new + command = "(echo \"" + tPrefix + eString + "\" ) | db2file todo -w -g -c " + codec+ " > "+ fileName; + system ( command.utf8() ); + QFile file( fileName ); + if (!file.open( IO_ReadOnly ) ) { + return false; + + } + QTextStream ts( &file ); + ts.setCodec( QTextCodec::codecForName("utf8") ); + answer = ts.read(); + file.close(); + //qDebug("answer \n%s ", answer.latin1()); + getNumFromRecord( answer, to ) ; + + } + else { // change existing + //qDebug("canging %d %d",to->zaurusStat() ,to->zaurusId() ); + //command = "(echo \"" + ePrefix + eString + "\" ) | db2file datebook -w -g -c " + codec+ " > "+ fileName; + changeString += eString + "\n"; + changeEnt = true; + + } + } + + to = tl.next(); + } + status.setText ( i18n("Changing todos ...") ); + qApp->processEvents(); + //qDebug("changing... "); + if ( changeEnt ) { + QFile file( fileName ); + if (!file.open( IO_WriteOnly ) ) { + return false; + + } + QTextStream ts( &file ); + ts.setCodec( QTextCodec::codecForName("utf8") ); + ts << changeString ; + file.close(); + command = "db2file todo -w -g -c " + codec+ " < "+ fileName; + system ( command.latin1() ); + //qDebug("command %s file :\n%s ", command.latin1(), changeString.latin1()); + + } + status.setText ( i18n("Deleting todos ...") ); + qApp->processEvents(); + //qDebug("deleting... "); + if ( deleteEnt ) { + QFile file( fileName ); + if (!file.open( IO_WriteOnly ) ) { + return false; + + } + QTextStream ts( &file ); + ts.setCodec( QTextCodec::codecForName("utf8") ); + ts << deleteString; + file.close(); + command = "db2file todo -d -c " + codec+ " < "+ fileName; + system ( command.latin1() ); + // qDebug("command %s file :\n%s ", command.latin1(), deleteString.latin1()); + } + + return true; +} +QString SharpFormat::dtToString( const QDateTime& dti, bool useTZ ) +{ + QString datestr; + QString timestr; + int offset = KGlobal::locale()->localTimeOffset( dti ); + QDateTime dt; + if (useTZ) + dt = dti.addSecs ( -(offset*60)); + else + dt = dti; + if(dt.date().isValid()){ + const QDate& date = dt.date(); + datestr.sprintf("%04d%02d%02d", + date.year(), date.month(), date.day()); + } + if(dt.time().isValid()){ + const QTime& time = dt.time(); + timestr.sprintf("T%02d%02d%02d", + time.hour(), time.minute(), time.second()); + } + return datestr + timestr; +} +QString SharpFormat::getEventString( Event* event ) +{ + QStringList list; + list.append( QString::number(event->zaurusId() ) ); + list.append( event->categories().join(",") ); + if ( !event->summary().isEmpty() ) + list.append( event->summary() ); + else + list.append("" ); + if ( !event->location().isEmpty() ) + list.append( event->location() ); + else + list.append("" ); + if ( !event->description().isEmpty() ) + list.append( event->description() ); + else + list.append( "" ); + if ( event->doesFloat () ) { + list.append( dtToString( QDateTime(event->dtStart().date(), QTime(0,0,0)), false )); + list.append( dtToString( QDateTime(event->dtEnd().date(),QTime(23,59,59)), false )); //6 + list.append( "1" ); + + } + else { + list.append( dtToString( event->dtStart()) ); + list.append( dtToString( event->dtEnd()) ); //6 + list.append( "0" ); + } + bool noAlarm = true; + if ( event->alarms().count() > 0 ) { + Alarm * al = event->alarms().first(); + if ( al->enabled() ) { + noAlarm = false; + list.append( "0" ); // yes, 0 == alarm + list.append( QString::number( al->startOffset().asSeconds()/(-60) ) ); + if ( al->type() == Alarm::Audio ) + list.append( "1" ); // type audio + else + list.append( "0" ); // type silent + } + } + if ( noAlarm ) { + list.append( "1" ); // yes, 1 == no alarm + list.append( "0" ); // no alarm offset + list.append( "1" ); // type + } + // next is: 11 + // next is: 11-16 are recurrence + Recurrence* rec = event->recurrence(); + + bool writeEndDate = false; + switch ( rec->doesRecur() ) + { + case Recurrence::rDaily: // 0 + list.append( "0" ); + list.append( QString::number( rec->frequency() ));//12 + list.append( "0" ); + list.append( "0" ); + writeEndDate = true; + break; + case Recurrence::rWeekly:// 1 + list.append( "1" ); + list.append( QString::number( rec->frequency()) );//12 + list.append( "0" ); + { + int days = 0; + QBitArray weekDays = rec->days(); + int i; + for( i = 1; i <= 7; ++i ) { + if ( weekDays[i-1] ) { + days += 1 << (i-1); + } + } + list.append( QString::number( days ) ); + } + //pending weekdays + writeEndDate = true; + + break; + case Recurrence::rMonthlyPos:// 2 + list.append( "2" ); + list.append( QString::number( rec->frequency()) );//12 + + writeEndDate = true; + { + int count = 1; + QPtrList<Recurrence::rMonthPos> rmp; + rmp = rec->monthPositions(); + if ( rmp.first()->negative ) + count = 5 - rmp.first()->rPos - 1; + else + count = rmp.first()->rPos - 1; + list.append( QString::number( count ) ); + + } + + list.append( "0" ); + break; + case Recurrence::rMonthlyDay:// 3 + list.append( "3" ); + list.append( QString::number( rec->frequency()) );//12 + list.append( "0" ); + list.append( "0" ); + writeEndDate = true; + break; + case Recurrence::rYearlyMonth://4 + list.append( "4" ); + list.append( QString::number( rec->frequency()) );//12 + list.append( "0" ); + list.append( "0" ); + writeEndDate = true; + break; + + default: + list.append( "255" ); + list.append( QString() ); + list.append( "0" ); + list.append( QString() ); + list.append( "0" ); + list.append( "20991231T000000" ); + break; + } + if ( writeEndDate ) { + + if ( rec->endDate().isValid() ) { // 15 + 16 + list.append( "1" ); + list.append( dtToString( rec->endDate()) ); + } else { + list.append( "0" ); + list.append( "20991231T000000" ); + } + + } + if ( event->doesFloat () ) { + list.append( dtToString( event->dtStart(), false ).left( 8 )); + list.append( dtToString( event->dtEnd(), false ).left( 8 )); //6 + + } + else { + list.append( QString() ); + list.append( QString() ); + + } + if (event->dtStart().date() == event->dtEnd().date() ) + list.append( "0" ); + else + list.append( "1" ); + + + for(QStringList::Iterator it=list.begin(); + it!=list.end(); ++it){ + QString& s = (*it); + s.replace(QRegExp("\""), "\"\""); + if(s.contains(QRegExp("[,\"\r\n]")) || s.stripWhiteSpace() != s){ + s.prepend('\"'); + s.append('\"'); + } else if(s.isEmpty() && !s.isNull()){ + s = "\"\""; + } + } + return list.join(","); + + +} +QString SharpFormat::getTodoString( Todo* todo ) +{ + QStringList list; + list.append( QString::number( todo->zaurusId() ) ); + list.append( todo->categories().join(",") ); + + if ( todo->hasStartDate() ) { + list.append( dtToString( todo->dtStart()) ); + } else + list.append( QString() ); + + if ( todo->hasDueDate() ) { + QTime tim; + if ( todo->doesFloat()) { + list.append( dtToString( QDateTime(todo->dtDue().date(),QTime( 0,0,0 )), false)) ; + } else { + list.append( dtToString(todo->dtDue() ) ); + } + } else + list.append( QString() ); + + if ( todo->isCompleted() ) { + list.append( dtToString( todo->completed()) ); + list.append( "0" ); // yes 0 == completed + } else { + list.append( dtToString( todo->completed()) ); + list.append( "1" ); + } + list.append( QString::number( todo->priority() )); + if( ! todo->summary().isEmpty() ) + list.append( todo->summary() ); + else + list.append( "" ); + if (! todo->description().isEmpty() ) + list.append( todo->description() ); + else + list.append( "" ); + for(QStringList::Iterator it=list.begin(); + it!=list.end(); ++it){ + QString& s = (*it); + s.replace(QRegExp("\""), "\"\""); + if(s.contains(QRegExp("[,\"\r\n]")) || s.stripWhiteSpace() != s){ + s.prepend('\"'); + s.append('\"'); + } else if(s.isEmpty() && !s.isNull()){ + s = "\"\""; + } + } + return list.join(","); +} +QString SharpFormat::getPart( const QString & text, bool &ok, int &start ) +{ + //qDebug("start %d ", start); + + QString retval =""; + if ( text.at(start) == '"' ) { + if ( text.mid( start,2) == "\"\"" && !( text.mid( start+2,1) == "\"")) { + start = start +2; + if ( text.mid( start,1) == "," ) { + start += 1; + } + retval = ""; + if ( text.mid( start,1) == "\n" ) { + start += 1; + ok = false; + } + return retval; + } + int hk = start+1; + hk = text.find ('"',hk); + while ( text.at(hk+1) == '"' ) + hk = text.find ('"',hk+2); + retval = text.mid( start+1, hk-start-1); + start = hk+1; + retval.replace( QRegExp("\"\""), "\""); + if ( text.mid( start,1) == "," ) { + start += 1; + } + if ( text.mid( start,1) == "\n" ) { + start += 1; + ok = false; + } + //qDebug("retval***%s*** ",retval.latin1() ); + return retval; + + } else { + int nl = text.find ("\n",start); + int kom = text.find (',',start); + if ( kom < nl ) { + // qDebug("kom < nl %d ", kom); + retval = text.mid(start, kom-start); + start = kom+1; + return retval; + } else { + if ( nl == kom ) { + // qDebug(" nl == kom "); + start = 0; + ok = false; + return "0"; + } + // qDebug(" nl < kom ", nl); + retval = text.mid( start, nl-start); + ok = false; + start = nl+1; + return retval; + } + } +} +bool SharpFormat::fromString( Calendar *calendar, const QString & text) +{ + return false; +} +bool SharpFormat::fromString2Cal( Calendar *calendar,Calendar *existingCalendar, const QString & text, const QString & type) +{ + // qDebug("test %s ", text.latin1()); + QStringList templist; + QString tempString; + int start = 0; + int len = text.length(); + int end = text.find ("\n",start)+1; + bool ok = true; + start = end; + SharpParser handler( calendar ); + handler.setCategoriesList( mCategories ); + while ( start > 0 ) { + templist.clear(); + ok = true; + while ( ok ) { + tempString = getPart( text, ok, start ); + if ( start >= len || start == 0 ) { + start = 0; + ok = false; + } + if ( tempString.right(1) =="\n" ) + tempString = tempString.left( tempString.length()-1); + //if ( ok ) + templist.append( tempString ); + //qDebug("%d ---%s---", templist.count(),tempString.latin1() ); + } + handler.startElement( existingCalendar, templist, type ); + } + + return false; +} + +QString SharpFormat::toString( Calendar * ) +{ + return QString::null; +} diff --git a/libkcal/sharpformat.h b/libkcal/sharpformat.h new file mode 100644 index 0000000..0b13862 --- a/dev/null +++ b/libkcal/sharpformat.h @@ -0,0 +1,61 @@ +/* + This file is part of libkcal. + + Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef SHARPFORMAT_H +#define SHARPAFORMAT_H + +#include <qstring.h> + +#include "scheduler.h" + +#include "calformat.h" + +namespace KCal { + +/** + This class implements the calendar format used by Sharp. +*/ +class SharpFormat : public QObject { + public: + /** Create new iCalendar format. */ + SharpFormat(); + virtual ~SharpFormat(); + + bool load( Calendar * ,Calendar *); + bool save( Calendar * ); + void setCategoriesList ( QStringList * cat ){ mCategories = cat; } + bool fromString2Cal( Calendar *, Calendar *, const QString & , const QString & ); + bool fromString( Calendar *, const QString & ); + QString toString( Calendar * ); + static ulong getCsum( const QStringList & ); + + private: + QString getEventString( Event* ); + QString getTodoString( Todo* ); + QString dtToString( const QDateTime& dt, bool useTZ = true ); + + QStringList *mCategories; + int getNumFromRecord( QString answer,Incidence* inc ) ; + QString getPart( const QString & text, bool &ok, int &start ); +}; + +} + +#endif diff --git a/libkcal/todo.cpp b/libkcal/todo.cpp new file mode 100644 index 0000000..0c1e3e4 --- a/dev/null +++ b/libkcal/todo.cpp @@ -0,0 +1,316 @@ +/* + This file is part of libkcal. + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include <kglobal.h> +#include <klocale.h> +#include <kdebug.h> + +#include "todo.h" + +using namespace KCal; + +Todo::Todo(): Incidence() +{ +// mStatus = TENTATIVE; + + mHasDueDate = false; + setHasStartDate( false ); + mCompleted = getEvenTime(QDateTime::currentDateTime()); + mHasCompletedDate = false; + mPercentComplete = 0; +} + +Todo::Todo(const Todo &t) : Incidence(t) +{ + mDtDue = t.mDtDue; + mHasDueDate = t.mHasDueDate; + mCompleted = t.mCompleted; + mHasCompletedDate = t.mHasCompletedDate; + mPercentComplete = t.mPercentComplete; +} + +Todo::~Todo() +{ + +} + +Incidence *Todo::clone() +{ + return new Todo(*this); +} + + +bool KCal::operator==( const Todo& t1, const Todo& t2 ) +{ + + bool ret = operator==( (const Incidence&)t1, (const Incidence&)t2 ); + if ( ! ret ) + return false; + if ( t1.hasDueDate() == t2.hasDueDate() ) { + if ( t1.hasDueDate() ) { + if ( t1.doesFloat() == t2.doesFloat() ) { + if ( t1.doesFloat() ) { + if ( t1.dtDue().date() != t2.dtDue().date() ) + return false; + } else + if ( t1.dtDue() != t2.dtDue() ) + return false; + } else + return false;// float != + } + + } else + return false; + if ( t1.percentComplete() != t2.percentComplete() ) + return false; + if ( t1.isCompleted() ) { + if ( t1.hasCompletedDate() == t2.hasCompletedDate() ) { + if ( t1.hasCompletedDate() ) { + if ( t1.completed() != t2.completed() ) + return false; + } + + } else + return false; + } + return true; + +} + +void Todo::setDtDue(const QDateTime &dtDue) +{ + //int diffsecs = mDtDue.secsTo(dtDue); + + /*if (mReadOnly) return; + const QPtrList<Alarm>& alarms = alarms(); + for (Alarm* alarm = alarms.first(); alarm; alarm = alarms.next()) { + if (alarm->enabled()) { + alarm->setTime(alarm->time().addSecs(diffsecs)); + } + }*/ + mDtDue = getEvenTime(dtDue); + + //kdDebug(5800) << "setDtDue says date is " << mDtDue.toString() << endl; + + /*const QPtrList<Alarm>& alarms = alarms(); + for (Alarm* alarm = alarms.first(); alarm; alarm = alarms.next()) + alarm->setAlarmStart(mDtDue);*/ + + updated(); +} + +QDateTime Todo::dtDue() const +{ + return mDtDue; +} + +QString Todo::dtDueTimeStr() const +{ + return KGlobal::locale()->formatTime(mDtDue.time()); +} + +QString Todo::dtDueDateStr(bool shortfmt) const +{ + return KGlobal::locale()->formatDate(mDtDue.date(),shortfmt); +} + +QString Todo::dtDueStr(bool shortfmt) const +{ + return KGlobal::locale()->formatDateTime(mDtDue, shortfmt); +} + +bool Todo::hasDueDate() const +{ + return mHasDueDate; +} + +void Todo::setHasDueDate(bool f) +{ + if (mReadOnly) return; + mHasDueDate = f; + updated(); +} + + +#if 0 +void Todo::setStatus(const QString &statStr) +{ + if (mReadOnly) return; + QString ss(statStr.upper()); + + if (ss == "X-ACTION") + mStatus = NEEDS_ACTION; + else if (ss == "NEEDS ACTION") + mStatus = NEEDS_ACTION; + else if (ss == "ACCEPTED") + mStatus = ACCEPTED; + else if (ss == "SENT") + mStatus = SENT; + else if (ss == "TENTATIVE") + mStatus = TENTATIVE; + else if (ss == "CONFIRMED") + mStatus = CONFIRMED; + else if (ss == "DECLINED") + mStatus = DECLINED; + else if (ss == "COMPLETED") + mStatus = COMPLETED; + else if (ss == "DELEGATED") + mStatus = DELEGATED; + + updated(); +} + +void Todo::setStatus(int status) +{ + if (mReadOnly) return; + mStatus = status; + updated(); +} + +int Todo::status() const +{ + return mStatus; +} + +QString Todo::statusStr() const +{ + switch(mStatus) { + case NEEDS_ACTION: + return QString("NEEDS ACTION"); + break; + case ACCEPTED: + return QString("ACCEPTED"); + break; + case SENT: + return QString("SENT"); + break; + case TENTATIVE: + return QString("TENTATIVE"); + break; + case CONFIRMED: + return QString("CONFIRMED"); + break; + case DECLINED: + return QString("DECLINED"); + break; + case COMPLETED: + return QString("COMPLETED"); + break; + case DELEGATED: + return QString("DELEGATED"); + break; + } + return QString(""); +} +#endif + +bool Todo::isCompleted() const +{ + if (mPercentComplete == 100) return true; + else return false; +} + +void Todo::setCompleted(bool completed) +{ + if (completed) mPercentComplete = 100; + else mPercentComplete = 0; + updated(); +} + +QDateTime Todo::completed() const +{ + return mCompleted; +} + +QString Todo::completedStr() const +{ + return KGlobal::locale()->formatDateTime(mCompleted); +} + +void Todo::setCompleted(const QDateTime &completed) +{ + mHasCompletedDate = true; + mPercentComplete = 100; + mCompleted = getEvenTime(completed); + updated(); +} + +bool Todo::hasCompletedDate() const +{ + return mHasCompletedDate; +} + +int Todo::percentComplete() const +{ + return mPercentComplete; +} + +void Todo::setPercentComplete(int v) +{ + mPercentComplete = v; + updated(); +} +QDateTime Todo::getNextAlarmDateTime( bool * ok, int * offset ) const +{ + if ( isCompleted() || ! hasDueDate() || cancelled() ) { + *ok = false; + return QDateTime (); + } + QDateTime incidenceStart; + incidenceStart = dtDue(); + bool enabled = false; + Alarm* alarm; + int off; + QDateTime alarmStart = QDateTime::currentDateTime().addDays( 3650 );; + // if ( QDateTime::currentDateTime() > incidenceStart ){ +// *ok = false; +// return incidenceStart; +// } + for (QPtrListIterator<Alarm> it(mAlarms); (alarm = it.current()) != 0; ++it) { + if (alarm->enabled()) { + if ( alarm->hasTime () ) { + if ( alarm->time() < alarmStart ) { + alarmStart = alarm->time(); + enabled = true; + off = alarmStart.secsTo( incidenceStart ); + } + + } else { + int secs = alarm->startOffset().asSeconds(); + if ( incidenceStart.addSecs( secs ) < alarmStart ) { + alarmStart = incidenceStart.addSecs( secs ); + enabled = true; + off = -secs; + } + } + } + } + if ( enabled ) { + if ( alarmStart > QDateTime::currentDateTime() ) { + *ok = true; + * offset = off; + return alarmStart; + } + } + *ok = false; + return QDateTime (); + +} + diff --git a/libkcal/todo.h b/libkcal/todo.h new file mode 100644 index 0000000..9aa92f8 --- a/dev/null +++ b/libkcal/todo.h @@ -0,0 +1,121 @@ +/* + This file is part of libkcal. + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef TODO_H +#define TODO_H +// +// Todo component, representing a VTODO object +// + +#include "incidence.h" + +namespace KCal { + +/** + This class provides a Todo in the sense of RFC2445. +*/ +class Todo : public Incidence +{ + public: + Todo(); + Todo(const Todo &); + ~Todo(); + typedef ListBase<Todo> List; + QCString type() const { return "Todo"; } + + /** Return an exact copy of this todo. */ + Incidence *clone(); + QDateTime getNextAlarmDateTime( bool * ok, int * offset ) const; + + /** for setting the todo's due date/time with a QDateTime. */ + void setDtDue(const QDateTime &dtDue); + /** returns an event's Due date/time as a QDateTime. */ + QDateTime dtDue() const; + /** returns an event's due time as a string formatted according to the + users locale settings */ + QString dtDueTimeStr() const; + /** returns an event's due date as a string formatted according to the + users locale settings */ + QString dtDueDateStr(bool shortfmt=true) const; + /** returns an event's due date and time as a string formatted according + to the users locale settings */ + QString dtDueStr(bool shortfmt=true) const; + + /** returns TRUE or FALSE depending on whether the todo has a due date */ + bool hasDueDate() const; + /** sets the event's hasDueDate value. */ + void setHasDueDate(bool f); + + + /** sets the event's status to the string specified. The string + * must be a recognized value for the status field, i.e. a string + * equivalent of the possible status enumerations previously described. */ +// void setStatus(const QString &statStr); + /** sets the event's status to the value specified. See the enumeration + * above for possible values. */ +// void setStatus(int); + /** return the event's status. */ +// int status() const; + /** return the event's status in string format. */ +// QString statusStr() const; + + /** return, if this todo is completed */ + bool isCompleted() const; + /** set completed state of this todo */ + void setCompleted(bool); + + /** + Return how many percent of the task are completed. Returns a value + between 0 and 100. + */ + int percentComplete() const; + /** + Set how many percent of the task are completed. Valid values are in the + range from 0 to 100. + */ + void setPercentComplete(int); + + /** return date and time when todo was completed */ + QDateTime completed() const; + QString completedStr() const; + /** set date and time of completion */ + void setCompleted(const QDateTime &completed); + + /** Return true, if todo has a date associated with completion */ + bool hasCompletedDate() const; + + private: + bool accept(Visitor &v) { return v.visit(this); } + + QDateTime mDtDue; // due date of todo + + bool mHasDueDate; // if todo has associated due date + +// int mStatus; // confirmed/delegated/tentative/etc + + QDateTime mCompleted; + bool mHasCompletedDate; + + int mPercentComplete; +}; + + bool operator==( const Todo&, const Todo& ); +} + +#endif diff --git a/libkcal/vcaldrag.cpp b/libkcal/vcaldrag.cpp new file mode 100644 index 0000000..f01f332 --- a/dev/null +++ b/libkcal/vcaldrag.cpp @@ -0,0 +1,54 @@ +/* + This file is part of libkcal. + Copyright (c) 1998 Preston Brown + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include "vcaldrag.h" + +#include "vcalformat.h" + +using namespace KCal; + +VCalDrag::VCalDrag( Calendar *cal, QWidget *parent, const char *name ) + : QStoredDrag( "text/x-vCalendar", parent, name ) +{ + VCalFormat format; + setEncodedData( format.toString( cal ).utf8() ); +} + +bool VCalDrag::canDecode( QMimeSource *me ) +{ + return me->provides( "text/x-vCalendar" ); +} + +bool VCalDrag::decode( QMimeSource *de, Calendar *cal ) +{ + bool success = false; + + QByteArray payload = de->encodedData( "text/x-vCalendar" ); + if ( payload.size() ) { + QString txt = QString::fromUtf8( payload.data() ); + + VCalFormat format; + success = format.fromString( cal, txt ); + } + + return success; +} + diff --git a/libkcal/vcaldrag.h b/libkcal/vcaldrag.h new file mode 100644 index 0000000..3048124 --- a/dev/null +++ b/libkcal/vcaldrag.h @@ -0,0 +1,47 @@ +/* + This file is part of libkcal. + Copyright (c) 1998 Preston Brown + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef VCALDRAG_H +#define VCALDRAG_H + +#include <qdragobject.h> + + +namespace KCal { + +class Calendar; + +/** vCalendar drag&drop class. */ +class VCalDrag : public QStoredDrag { + public: + /** Create a drag&drop object for vCalendar component \a vcal. */ + VCalDrag( Calendar *vcal, QWidget *parent = 0, const char *name = 0 ); + ~VCalDrag() {}; + + /** Return, if drag&drop object can be decode to vCalendar. */ + static bool canDecode( QMimeSource * ); + /** Decode drag&drop object to vCalendar component \a vcal. */ + static bool decode( QMimeSource *e, Calendar *cal ); +}; + +} + +#endif diff --git a/libkcal/vcalformat.cpp b/libkcal/vcalformat.cpp new file mode 100644 index 0000000..59030d5 --- a/dev/null +++ b/libkcal/vcalformat.cpp @@ -0,0 +1,1678 @@ +/* + This file is part of libkcal. + Copyright (c) 1998 Preston Brwon + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include <qapplication.h> +#include <qdatetime.h> +#include <qstring.h> +#include <qptrlist.h> +#include <qregexp.h> +#include <qclipboard.h> +#include <qdialog.h> +#include <qfile.h> + +#include <kdebug.h> +#include <kmessagebox.h> +#include <kiconloader.h> +#include <klocale.h> + +#include "vcc.h" +#include "vobject.h" + +#include "vcaldrag.h" +#include "calendar.h" + +#include "vcalformat.h" + +using namespace KCal; + +VCalFormat::VCalFormat() +{ +} + +VCalFormat::~VCalFormat() +{ +} + +bool VCalFormat::load(Calendar *calendar, const QString &fileName) +{ + mCalendar = calendar; + + clearException(); + + kdDebug(5800) << "VCalFormat::load() " << fileName << endl; + + VObject *vcal = 0; + + // this is not necessarily only 1 vcal. Could be many vcals, or include + // a vcard... + vcal = Parse_MIME_FromFileName(const_cast<char *>(QFile::encodeName(fileName).data())); + + if (!vcal) { + setException(new ErrorFormat(ErrorFormat::CalVersionUnknown)); + return FALSE; + } + + // any other top-level calendar stuff should be added/initialized here + + // put all vobjects into their proper places + populate(vcal); + + // clean up from vcal API stuff + cleanVObjects(vcal); + cleanStrTbl(); + + return true; +} + + +bool VCalFormat::save(Calendar *calendar, const QString &fileName) +{ + mCalendar = calendar; + + QString tmpStr; + VObject *vcal, *vo; + + kdDebug(5800) << "VCalFormat::save(): " << fileName << endl; + + vcal = newVObject(VCCalProp); + + // addPropValue(vcal,VCLocationProp, "0.0"); + addPropValue(vcal,VCProdIdProp, productId()); + tmpStr = mCalendar->getTimeZoneStr(); + //qDebug("mCalendar->getTimeZoneStr() %s",tmpStr.latin1() ); + addPropValue(vcal,VCTimeZoneProp, tmpStr.local8Bit()); + addPropValue(vcal,VCVersionProp, _VCAL_VERSION); + + // TODO STUFF + QPtrList<Todo> todoList = mCalendar->rawTodos(); + QPtrListIterator<Todo> qlt(todoList); + for (; qlt.current(); ++qlt) { + vo = eventToVTodo(qlt.current()); + addVObjectProp(vcal, vo); + } + + // EVENT STUFF + QPtrList<Event> events = mCalendar->rawEvents(); + Event *ev; + for(ev=events.first();ev;ev=events.next()) { + vo = eventToVEvent(ev); + addVObjectProp(vcal, vo); + } + + writeVObjectToFile(QFile::encodeName(fileName).data() ,vcal); + cleanVObjects(vcal); + cleanStrTbl(); + + if (QFile::exists(fileName)) { + kdDebug(5800) << "No error" << endl; + return true; + } else { + kdDebug(5800) << "Error" << endl; + return false; // error + } +} + +bool VCalFormat::fromString( Calendar *calendar, const QString &text ) +{ + // TODO: Factor out VCalFormat::fromString() + + QCString data = text.utf8(); + + if ( !data.size() ) return false; + + VObject *vcal = Parse_MIME( data.data(), data.size()); + if ( !vcal ) return false; + + VObjectIterator i; + VObject *curvo; + initPropIterator( &i, vcal ); + + // we only take the first object. TODO: parse all incidences. + do { + curvo = nextVObject( &i ); + } while ( strcmp( vObjectName( curvo ), VCEventProp ) && + strcmp( vObjectName( curvo ), VCTodoProp ) ); + + if ( strcmp( vObjectName( curvo ), VCEventProp ) == 0 ) { + Event *event = VEventToEvent( curvo ); + calendar->addEvent( event ); + } else { + kdDebug(5800) << "VCalFormat::fromString(): Unknown object type." << endl; + deleteVObject( vcal ); + return false; + } + + deleteVObject( vcal ); + + return true; +} + +QString VCalFormat::toString( Calendar *calendar ) +{ + // TODO: Factor out VCalFormat::asString() + + VObject *vcal = newVObject(VCCalProp); + + addPropValue( vcal, VCProdIdProp, CalFormat::productId() ); + QString tmpStr = mCalendar->getTimeZoneStr(); + addPropValue( vcal, VCTimeZoneProp, tmpStr.local8Bit() ); + addPropValue( vcal, VCVersionProp, _VCAL_VERSION ); + + // TODO: Use all data. + QPtrList<Event> events = calendar->events(); + Event *event = events.first(); + if ( !event ) return QString::null; + + VObject *vevent = eventToVEvent( event ); + + addVObjectProp( vcal, vevent ); + + char *buf = writeMemVObject( 0, 0, vcal ); + + QString result( buf ); + + cleanVObject( vcal ); + + return result; +} + +VObject *VCalFormat::eventToVTodo(const Todo *anEvent) +{ + VObject *vtodo; + QString tmpStr; + QStringList tmpStrList; + + vtodo = newVObject(VCTodoProp); + + // due date + if (anEvent->hasDueDate()) { + tmpStr = qDateTimeToISO(anEvent->dtDue(), + !anEvent->doesFloat()); + addPropValue(vtodo, VCDueProp, tmpStr.local8Bit()); + } + + // start date + if (anEvent->hasStartDate()) { + tmpStr = qDateTimeToISO(anEvent->dtStart(), + !anEvent->doesFloat()); + addPropValue(vtodo, VCDTstartProp, tmpStr.local8Bit()); + } + + // creation date + tmpStr = qDateTimeToISO(anEvent->created()); + addPropValue(vtodo, VCDCreatedProp, tmpStr.local8Bit()); + + // unique id + addPropValue(vtodo, VCUniqueStringProp, + anEvent->uid().local8Bit()); + + // revision + tmpStr.sprintf("%i", anEvent->revision()); + addPropValue(vtodo, VCSequenceProp, tmpStr.local8Bit()); + + // last modification date + tmpStr = qDateTimeToISO(anEvent->lastModified()); + addPropValue(vtodo, VCLastModifiedProp, tmpStr.local8Bit()); + + // organizer stuff + tmpStr = "MAILTO:" + anEvent->organizer(); + addPropValue(vtodo, ICOrganizerProp, tmpStr.local8Bit()); + + // attendees + if (anEvent->attendeeCount() != 0) { + QPtrList<Attendee> al = anEvent->attendees(); + QPtrListIterator<Attendee> ai(al); + Attendee *curAttendee; + + for (; ai.current(); ++ai) { + curAttendee = ai.current(); + if (!curAttendee->email().isEmpty() && + !curAttendee->name().isEmpty()) + tmpStr = "MAILTO:" + curAttendee->name() + " <" + + curAttendee->email() + ">"; + else if (curAttendee->name().isEmpty()) + tmpStr = "MAILTO: " + curAttendee->email(); + else if (curAttendee->email().isEmpty()) + tmpStr = "MAILTO: " + curAttendee->name(); + else if (curAttendee->name().isEmpty() && + curAttendee->email().isEmpty()) + kdDebug(5800) << "warning! this Event has an attendee w/o name or email!" << endl; + VObject *aProp = addPropValue(vtodo, VCAttendeeProp, tmpStr.local8Bit()); + addPropValue(aProp, VCRSVPProp, curAttendee->RSVP() ? "TRUE" : "FALSE"); + addPropValue(aProp, VCStatusProp, writeStatus(curAttendee->status())); + } + } + + // description BL: + if (!anEvent->description().isEmpty()) { + VObject *d = addPropValue(vtodo, VCDescriptionProp, + anEvent->description().local8Bit()); + if (anEvent->description().find('\n') != -1) + addProp(d, VCQuotedPrintableProp); + } + + // summary + if (!anEvent->summary().isEmpty()) + addPropValue(vtodo, VCSummaryProp, anEvent->summary().local8Bit()); + + if (!anEvent->location().isEmpty()) + addPropValue(vtodo, VCLocationProp, anEvent->location().local8Bit()); + + // completed + // status + // backward compatibility, KOrganizer used to interpret only these two values + addPropValue(vtodo, VCStatusProp, anEvent->isCompleted() ? "COMPLETED" : + "NEEDS_ACTION"); + // completion date + if (anEvent->hasCompletedDate()) { + tmpStr = qDateTimeToISO(anEvent->completed()); + addPropValue(vtodo, VCCompletedProp, tmpStr.local8Bit()); + } + + // priority + tmpStr.sprintf("%i",anEvent->priority()); + addPropValue(vtodo, VCPriorityProp, tmpStr.local8Bit()); + + // related event + if (anEvent->relatedTo()) { + addPropValue(vtodo, VCRelatedToProp, + anEvent->relatedTo()->uid().local8Bit()); + } + + // categories + tmpStrList = anEvent->categories(); + tmpStr = ""; + QString catStr; + for ( QStringList::Iterator it = tmpStrList.begin(); + it != tmpStrList.end(); + ++it ) { + catStr = *it; + if (catStr[0] == ' ') + tmpStr += catStr.mid(1); + else + tmpStr += catStr; + // this must be a ';' character as the vCalendar specification requires! + // vcc.y has been hacked to translate the ';' to a ',' when the vcal is + // read in. + tmpStr += ";"; + } + if (!tmpStr.isEmpty()) { + tmpStr.truncate(tmpStr.length()-1); + addPropValue(vtodo, VCCategoriesProp, tmpStr.local8Bit()); + } + + // alarm stuff + kdDebug(5800) << "vcalformat::eventToVTodo was called" << endl; + QPtrList<Alarm> alarms = anEvent->alarms(); + Alarm* alarm; + for (alarm = alarms.first(); alarm; alarm = alarms.next()) { + if (alarm->enabled()) { + VObject *a = addProp(vtodo, VCDAlarmProp); + tmpStr = qDateTimeToISO(alarm->time()); + addPropValue(a, VCRunTimeProp, tmpStr.local8Bit()); + addPropValue(a, VCRepeatCountProp, "1"); + addPropValue(a, VCDisplayStringProp, "beep!"); + if (alarm->type() == Alarm::Audio) { + a = addProp(vtodo, VCAAlarmProp); + addPropValue(a, VCRunTimeProp, tmpStr.local8Bit()); + addPropValue(a, VCRepeatCountProp, "1"); + addPropValue(a, VCAudioContentProp, QFile::encodeName(alarm->audioFile())); + } + else if (alarm->type() == Alarm::Procedure) { + a = addProp(vtodo, VCPAlarmProp); + addPropValue(a, VCRunTimeProp, tmpStr.local8Bit()); + addPropValue(a, VCRepeatCountProp, "1"); + addPropValue(a, VCProcedureNameProp, QFile::encodeName(alarm->programFile())); + } + } + } + + if (anEvent->pilotId()) { + // pilot sync stuff + tmpStr.sprintf("%i",anEvent->pilotId()); + addPropValue(vtodo, KPilotIdProp, tmpStr.local8Bit()); + tmpStr.sprintf("%i",anEvent->syncStatus()); + addPropValue(vtodo, KPilotStatusProp, tmpStr.local8Bit()); + } + + return vtodo; +} + +VObject* VCalFormat::eventToVEvent(const Event *anEvent) +{ + VObject *vevent; + QString tmpStr; + QStringList tmpStrList; + + vevent = newVObject(VCEventProp); + + // start and end time + tmpStr = qDateTimeToISO(anEvent->dtStart(), + !anEvent->doesFloat()); + addPropValue(vevent, VCDTstartProp, tmpStr.local8Bit()); + + // events that have time associated but take up no time should + // not have both DTSTART and DTEND. + if (anEvent->dtStart() != anEvent->dtEnd()) { + tmpStr = qDateTimeToISO(anEvent->dtEnd(), + !anEvent->doesFloat()); + addPropValue(vevent, VCDTendProp, tmpStr.local8Bit()); + } + + // creation date + tmpStr = qDateTimeToISO(anEvent->created()); + addPropValue(vevent, VCDCreatedProp, tmpStr.local8Bit()); + + // unique id + addPropValue(vevent, VCUniqueStringProp, + anEvent->uid().local8Bit()); + + // revision + tmpStr.sprintf("%i", anEvent->revision()); + addPropValue(vevent, VCSequenceProp, tmpStr.local8Bit()); + + // last modification date + tmpStr = qDateTimeToISO(anEvent->lastModified()); + addPropValue(vevent, VCLastModifiedProp, tmpStr.local8Bit()); + + // attendee and organizer stuff + tmpStr = "MAILTO:" + anEvent->organizer(); + addPropValue(vevent, ICOrganizerProp, tmpStr.local8Bit()); + + if (anEvent->attendeeCount() != 0) { + QPtrList<Attendee> al = anEvent->attendees(); + QPtrListIterator<Attendee> ai(al); + Attendee *curAttendee; + + // TODO: Put this functionality into Attendee class + for (; ai.current(); ++ai) { + curAttendee = ai.current(); + if (!curAttendee->email().isEmpty() && + !curAttendee->name().isEmpty()) + tmpStr = "MAILTO:" + curAttendee->name() + " <" + + curAttendee->email() + ">"; + else if (curAttendee->name().isEmpty()) + tmpStr = "MAILTO: " + curAttendee->email(); + else if (curAttendee->email().isEmpty()) + tmpStr = "MAILTO: " + curAttendee->name(); + else if (curAttendee->name().isEmpty() && + curAttendee->email().isEmpty()) + kdDebug(5800) << "warning! this Event has an attendee w/o name or email!" << endl; + VObject *aProp = addPropValue(vevent, VCAttendeeProp, tmpStr.local8Bit()); + addPropValue(aProp, VCRSVPProp, curAttendee->RSVP() ? "TRUE" : "FALSE");; + addPropValue(aProp, VCStatusProp, writeStatus(curAttendee->status())); + } + } + + // recurrence rule stuff + if (anEvent->recurrence()->doesRecur()) { + // some more variables + QPtrList<Recurrence::rMonthPos> tmpPositions; + QPtrList<int> tmpDays; + int *tmpDay; + Recurrence::rMonthPos *tmpPos; + QString tmpStr2; + int i; + + switch(anEvent->recurrence()->doesRecur()) { + case Recurrence::rDaily: + tmpStr.sprintf("D%i ",anEvent->recurrence()->frequency()); +// if (anEvent->rDuration > 0) +// tmpStr += "#"; + break; + case Recurrence::rWeekly: + tmpStr.sprintf("W%i ",anEvent->recurrence()->frequency()); + for (i = 0; i < 7; i++) { + if (anEvent->recurrence()->days().testBit(i)) + tmpStr += dayFromNum(i); + } + break; + case Recurrence::rMonthlyPos: + tmpStr.sprintf("MP%i ", anEvent->recurrence()->frequency()); + // write out all rMonthPos's + tmpPositions = anEvent->recurrence()->monthPositions(); + for (tmpPos = tmpPositions.first(); + tmpPos; + tmpPos = tmpPositions.next()) { + + tmpStr2.sprintf("%i", tmpPos->rPos); + if (tmpPos->negative) + tmpStr2 += "- "; + else + tmpStr2 += "+ "; + tmpStr += tmpStr2; + for (i = 0; i < 7; i++) { + if (tmpPos->rDays.testBit(i)) + tmpStr += dayFromNum(i); + } + } // loop for all rMonthPos's + break; + case Recurrence::rMonthlyDay: + tmpStr.sprintf("MD%i ", anEvent->recurrence()->frequency()); + // write out all rMonthDays; + tmpDays = anEvent->recurrence()->monthDays(); + for (tmpDay = tmpDays.first(); + tmpDay; + tmpDay = tmpDays.next()) { + tmpStr2.sprintf("%i ", *tmpDay); + tmpStr += tmpStr2; + } + break; + case Recurrence::rYearlyMonth: + tmpStr.sprintf("YM%i ", anEvent->recurrence()->frequency()); + // write out all the rYearNums; + tmpDays = anEvent->recurrence()->yearNums(); + for (tmpDay = tmpDays.first(); + tmpDay; + tmpDay = tmpDays.next()) { + tmpStr2.sprintf("%i ", *tmpDay); + tmpStr += tmpStr2; + } + break; + case Recurrence::rYearlyDay: + tmpStr.sprintf("YD%i ", anEvent->recurrence()->frequency()); + // write out all the rYearNums; + tmpDays = anEvent->recurrence()->yearNums(); + for (tmpDay = tmpDays.first(); + tmpDay; + tmpDay = tmpDays.next()) { + tmpStr2.sprintf("%i ", *tmpDay); + tmpStr += tmpStr2; + } + break; + default: + kdDebug(5800) << "ERROR, it should never get here in eventToVEvent!" << endl; + break; + } // switch + + if (anEvent->recurrence()->duration() > 0) { + tmpStr2.sprintf("#%i",anEvent->recurrence()->duration()); + tmpStr += tmpStr2; + } else if (anEvent->recurrence()->duration() == -1) { + tmpStr += "#0"; // defined as repeat forever + } else { + tmpStr += qDateTimeToISO(anEvent->recurrence()->endDate(), FALSE); + } + addPropValue(vevent,VCRRuleProp, tmpStr.local8Bit()); + + } // event repeats + + // exceptions to recurrence + DateList dateList = anEvent->exDates(); + DateList::ConstIterator it; + QString tmpStr2; + + for (it = dateList.begin(); it != dateList.end(); ++it) { + tmpStr = qDateToISO(*it) + ";"; + tmpStr2 += tmpStr; + } + if (!tmpStr2.isEmpty()) { + tmpStr2.truncate(tmpStr2.length()-1); + addPropValue(vevent, VCExDateProp, tmpStr2.local8Bit()); + } + + // description + if (!anEvent->description().isEmpty()) { + VObject *d = addPropValue(vevent, VCDescriptionProp, + anEvent->description().local8Bit()); + if (anEvent->description().find('\n') != -1) + addProp(d, VCQuotedPrintableProp); + } + + // summary + if (!anEvent->summary().isEmpty()) + addPropValue(vevent, VCSummaryProp, anEvent->summary().local8Bit()); + + if (!anEvent->location().isEmpty()) + addPropValue(vevent, VCLocationProp, anEvent->location().local8Bit()); + + // status +// TODO: define Event status +// addPropValue(vevent, VCStatusProp, anEvent->statusStr().local8Bit()); + + // secrecy + const char *text = 0; + switch (anEvent->secrecy()) { + case Incidence::SecrecyPublic: + text = "PUBLIC"; + break; + case Incidence::SecrecyPrivate: + text = "PRIVATE"; + break; + case Incidence::SecrecyConfidential: + text = "CONFIDENTIAL"; + break; + } + if (text) { + addPropValue(vevent, VCClassProp, text); + } + + // categories + tmpStrList = anEvent->categories(); + tmpStr = ""; + QString catStr; + for ( QStringList::Iterator it = tmpStrList.begin(); + it != tmpStrList.end(); + ++it ) { + catStr = *it; + if (catStr[0] == ' ') + tmpStr += catStr.mid(1); + else + tmpStr += catStr; + // this must be a ';' character as the vCalendar specification requires! + // vcc.y has been hacked to translate the ';' to a ',' when the vcal is + // read in. + tmpStr += ";"; + } + if (!tmpStr.isEmpty()) { + tmpStr.truncate(tmpStr.length()-1); + addPropValue(vevent, VCCategoriesProp, tmpStr.local8Bit()); + } + + // attachments + // TODO: handle binary attachments! + QPtrList<Attachment> attachments = anEvent->attachments(); + for ( Attachment *at = attachments.first(); at; at = attachments.next() ) + addPropValue(vevent, VCAttachProp, at->uri().local8Bit()); + + // resources + tmpStrList = anEvent->resources(); + tmpStr = tmpStrList.join(";"); + if (!tmpStr.isEmpty()) + addPropValue(vevent, VCResourcesProp, tmpStr.local8Bit()); + + // alarm stuff + QPtrList<Alarm> alarms = anEvent->alarms(); + Alarm* alarm; + for (alarm = alarms.first(); alarm; alarm = alarms.next()) { + if (alarm->enabled()) { + VObject *a = addProp(vevent, VCDAlarmProp); + tmpStr = qDateTimeToISO(alarm->time()); + addPropValue(a, VCRunTimeProp, tmpStr.local8Bit()); + addPropValue(a, VCRepeatCountProp, "1"); + addPropValue(a, VCDisplayStringProp, "beep!"); + if (alarm->type() == Alarm::Audio) { + a = addProp(vevent, VCAAlarmProp); + addPropValue(a, VCRunTimeProp, tmpStr.local8Bit()); + addPropValue(a, VCRepeatCountProp, "1"); + addPropValue(a, VCAudioContentProp, QFile::encodeName(alarm->audioFile())); + } + if (alarm->type() == Alarm::Procedure) { + a = addProp(vevent, VCPAlarmProp); + addPropValue(a, VCRunTimeProp, tmpStr.local8Bit()); + addPropValue(a, VCRepeatCountProp, "1"); + addPropValue(a, VCProcedureNameProp, QFile::encodeName(alarm->programFile())); + } + } + } + + // priority + tmpStr.sprintf("%i",anEvent->priority()); + addPropValue(vevent, VCPriorityProp, tmpStr.local8Bit()); + + // transparency + tmpStr.sprintf("%i",anEvent->transparency()); + addPropValue(vevent, VCTranspProp, tmpStr.local8Bit()); + + // related event + if (anEvent->relatedTo()) { + addPropValue(vevent, VCRelatedToProp, + anEvent->relatedTo()->uid().local8Bit()); + } + + if (anEvent->pilotId()) { + // pilot sync stuff + tmpStr.sprintf("%i",anEvent->pilotId()); + addPropValue(vevent, KPilotIdProp, tmpStr.local8Bit()); + tmpStr.sprintf("%i",anEvent->syncStatus()); + addPropValue(vevent, KPilotStatusProp, tmpStr.local8Bit()); + } + + return vevent; +} + +Todo *VCalFormat::VTodoToEvent(VObject *vtodo) +{ + VObject *vo; + VObjectIterator voi; + char *s; + + Todo *anEvent = new Todo; + + // creation date + if ((vo = isAPropertyOf(vtodo, VCDCreatedProp)) != 0) { + anEvent->setCreated(ISOToQDateTime(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(s = fakeCString(vObjectUStringZValue(vo))); + deleteStr(s); + } + + // last modification date + if ((vo = isAPropertyOf(vtodo, VCLastModifiedProp)) != 0) { + anEvent->setLastModified(ISOToQDateTime(s = fakeCString(vObjectUStringZValue(vo)))); + deleteStr(s); + } + else + anEvent->setLastModified(QDateTime(QDate::currentDate(), + QTime::currentTime())); + + // organizer + // if our extension property for the event's ORGANIZER exists, add it. + if ((vo = isAPropertyOf(vtodo, ICOrganizerProp)) != 0) { + anEvent->setOrganizer(s = fakeCString(vObjectUStringZValue(vo))); + deleteStr(s); + } else { + anEvent->setOrganizer(mCalendar->getEmail()); + } + + // 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::fromLocal8Bit(s); + deleteStr(s); + tmpStr = tmpStr.simplifyWhiteSpace(); + int emailPos1, emailPos2; + if ((emailPos1 = tmpStr.find('<')) > 0) { + // both email address and name + emailPos2 = tmpStr.findRev('>'); + a = new Attendee(tmpStr.left(emailPos1 - 1), + tmpStr.mid(emailPos1 + 1, + emailPos2 - (emailPos1 + 1))); + } else if (tmpStr.find('@') > 0) { + // just an email address + a = new Attendee(0, tmpStr); + } else { + // just a name + QString email = tmpStr.replace( QRegExp(" "), "." ); + a = new Attendee(tmpStr,email); + } + + // is there an RSVP property? + if ((vp = isAPropertyOf(vo, VCRSVPProp)) != 0) + a->setRSVP(vObjectStringZValue(vp)); + // is there a status property? + if ((vp = isAPropertyOf(vo, VCStatusProp)) != 0) + a->setStatus(readStatus(vObjectStringZValue(vp))); + // add the attendee + anEvent->addAttendee(a); + } + } + + // description for todo + if ((vo = isAPropertyOf(vtodo, VCDescriptionProp)) != 0) { + s = fakeCString(vObjectUStringZValue(vo)); + anEvent->setDescription(QString::fromLocal8Bit(s)); + deleteStr(s); + } + + // summary + if ((vo = isAPropertyOf(vtodo, VCSummaryProp))) { + s = fakeCString(vObjectUStringZValue(vo)); + anEvent->setSummary(QString::fromLocal8Bit(s)); + deleteStr(s); + } + if ((vo = isAPropertyOf(vtodo, VCLocationProp))) { + s = fakeCString(vObjectUStringZValue(vo)); + anEvent->setLocation(QString::fromLocal8Bit(s)); + deleteStr(s); + } + + + // completed + // was: status + if ((vo = isAPropertyOf(vtodo, VCStatusProp)) != 0) { + s = fakeCString(vObjectUStringZValue(vo)); + if (strcmp(s,"COMPLETED") == 0) { + anEvent->setCompleted(true); + } else { + anEvent->setCompleted(false); + } + deleteStr(s); + } + else + anEvent->setCompleted(false); + + // completion date + if ((vo = isAPropertyOf(vtodo, VCCompletedProp)) != 0) { + anEvent->setCompleted(ISOToQDateTime(s = fakeCString(vObjectUStringZValue(vo)))); + deleteStr(s); + } + + // priority + if ((vo = isAPropertyOf(vtodo, VCPriorityProp))) { + anEvent->setPriority(atoi(s = fakeCString(vObjectUStringZValue(vo)))); + deleteStr(s); + } + + // due date + if ((vo = isAPropertyOf(vtodo, VCDueProp)) != 0) { + anEvent->setDtDue(ISOToQDateTime(s = fakeCString(vObjectUStringZValue(vo)))); + deleteStr(s); + anEvent->setHasDueDate(true); + } else { + anEvent->setHasDueDate(false); + } + + // start time + if ((vo = isAPropertyOf(vtodo, VCDTstartProp)) != 0) { + anEvent->setDtStart(ISOToQDateTime(s = fakeCString(vObjectUStringZValue(vo)))); + // kdDebug(5800) << "s is " << // s << ", ISO is " << ISOToQDateTime(s = fakeCString(vObjectUStringZValue(vo))).toString() << endl; + deleteStr(s); + anEvent->setHasStartDate(true); + } else { + anEvent->setHasStartDate(false); + } + + /* alarm stuff */ + //kdDebug(5800) << "vcalformat::VTodoToEvent called" << endl; + if ((vo = isAPropertyOf(vtodo, VCDAlarmProp))) { + Alarm* alarm = anEvent->newAlarm(); + VObject *a; + if ((a = isAPropertyOf(vo, VCRunTimeProp))) { + alarm->setTime(ISOToQDateTime(s = fakeCString(vObjectUStringZValue(a)))); + deleteStr(s); + } + alarm->setEnabled(true); + if ((vo = isAPropertyOf(vtodo, VCPAlarmProp))) { + if ((a = isAPropertyOf(vo, VCProcedureNameProp))) { + s = fakeCString(vObjectUStringZValue(a)); + alarm->setProcedureAlarm(QFile::decodeName(s)); + deleteStr(s); + } + } + if ((vo = isAPropertyOf(vtodo, VCAAlarmProp))) { + if ((a = isAPropertyOf(vo, VCAudioContentProp))) { + s = fakeCString(vObjectUStringZValue(a)); + alarm->setAudioAlarm(QFile::decodeName(s)); + deleteStr(s); + } + } + } + + // related todo + if ((vo = isAPropertyOf(vtodo, VCRelatedToProp)) != 0) { + anEvent->setRelatedToUid(s = fakeCString(vObjectUStringZValue(vo))); + deleteStr(s); + mTodosRelate.append(anEvent); + } + + // categories + QStringList tmpStrList; + int index1 = 0; + int index2 = 0; + if ((vo = isAPropertyOf(vtodo, VCCategoriesProp)) != 0) { + s = fakeCString(vObjectUStringZValue(vo)); + QString categories = QString::fromLocal8Bit(s); + deleteStr(s); + //const char* category; + QString category; + while ((index2 = categories.find(',', index1)) != -1) { + //category = (const char *) categories.mid(index1, (index2 - index1)); + category = categories.mid(index1, (index2 - index1)); + tmpStrList.append(category); + index1 = index2+1; + } + // get last category + category = categories.mid(index1, (categories.length()-index1)); + tmpStrList.append(category); + anEvent->setCategories(tmpStrList); + } + + /* PILOT SYNC STUFF */ + if ((vo = isAPropertyOf(vtodo, KPilotIdProp))) { + anEvent->setPilotId(atoi(s = fakeCString(vObjectUStringZValue(vo)))); + deleteStr(s); + } + else + anEvent->setPilotId(0); + + if ((vo = isAPropertyOf(vtodo, KPilotStatusProp))) { + anEvent->setSyncStatus(atoi(s = fakeCString(vObjectUStringZValue(vo)))); + deleteStr(s); + } + else + anEvent->setSyncStatus(Event::SYNCMOD); + + return anEvent; +} + +Event* VCalFormat::VEventToEvent(VObject *vevent) +{ + VObject *vo; + VObjectIterator voi; + char *s; + + Event *anEvent = new Event; + + // creation date + if ((vo = isAPropertyOf(vevent, VCDCreatedProp)) != 0) { + anEvent->setCreated(ISOToQDateTime(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(s = fakeCString(vObjectUStringZValue(vo))); + deleteStr(s); + } + + // revision + // again NSCAL doesn't give us much to work with, so we improvise... + if ((vo = isAPropertyOf(vevent, VCSequenceProp)) != 0) { + anEvent->setRevision(atoi(s = fakeCString(vObjectUStringZValue(vo)))); + deleteStr(s); + } + else + anEvent->setRevision(0); + + // last modification date + if ((vo = isAPropertyOf(vevent, VCLastModifiedProp)) != 0) { + anEvent->setLastModified(ISOToQDateTime(s = fakeCString(vObjectUStringZValue(vo)))); + deleteStr(s); + } + else + anEvent->setLastModified(QDateTime(QDate::currentDate(), + QTime::currentTime())); + + // organizer + // if our extension property for the event's ORGANIZER exists, add it. + if ((vo = isAPropertyOf(vevent, ICOrganizerProp)) != 0) { + anEvent->setOrganizer(s = fakeCString(vObjectUStringZValue(vo))); + deleteStr(s); + } else { + anEvent->setOrganizer(mCalendar->getEmail()); + } + + // deal with attendees. + initPropIterator(&voi, vevent); + while (moreIteration(&voi)) { + vo = nextVObject(&voi); + if (strcmp(vObjectName(vo), VCAttendeeProp) == 0) { + Attendee *a; + VObject *vp; + s = fakeCString(vObjectUStringZValue(vo)); + QString tmpStr = QString::fromLocal8Bit(s); + deleteStr(s); + tmpStr = tmpStr.simplifyWhiteSpace(); + int emailPos1, emailPos2; + if ((emailPos1 = tmpStr.find('<')) > 0) { + // both email address and name + emailPos2 = tmpStr.findRev('>'); + a = new Attendee(tmpStr.left(emailPos1 - 1), + tmpStr.mid(emailPos1 + 1, + emailPos2 - (emailPos1 + 1))); + } else if (tmpStr.find('@') > 0) { + // just an email address + a = new Attendee(0, tmpStr); + } else { + // just a name + QString email = tmpStr.replace( QRegExp(" "), "." ); + a = new Attendee(tmpStr,email); + } + + // is there an RSVP property? + if ((vp = isAPropertyOf(vo, VCRSVPProp)) != 0) + a->setRSVP(vObjectStringZValue(vp)); + // is there a status property? + if ((vp = isAPropertyOf(vo, VCStatusProp)) != 0) + 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 doesn't "float", it has an anchor in time but it doesn't + // "take up" any time. + /*if ((isAPropertyOf(vevent, VCDTstartProp) == 0) || + (isAPropertyOf(vevent, VCDTendProp) == 0)) { + anEvent->setFloats(TRUE); + } else { + }*/ + + anEvent->setFloats(FALSE); + + // start time + if ((vo = isAPropertyOf(vevent, VCDTstartProp)) != 0) { + anEvent->setDtStart(ISOToQDateTime(s = fakeCString(vObjectUStringZValue(vo)))); + // kdDebug(5800) << "s is " << // s << ", ISO is " << ISOToQDateTime(s = fakeCString(vObjectUStringZValue(vo))).toString() << endl; + deleteStr(s); + if (anEvent->dtStart().time().isNull()) + anEvent->setFloats(TRUE); + } + + // stop time + if ((vo = isAPropertyOf(vevent, VCDTendProp)) != 0) { + anEvent->setDtEnd(ISOToQDateTime(s = fakeCString(vObjectUStringZValue(vo)))); + deleteStr(s); + if (anEvent->dtEnd().time().isNull()) + anEvent->setFloats(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 (!(vo = isAPropertyOf(vevent, VCDTstartProp))) + anEvent->setDtStart(anEvent->dtEnd()); + if (!(vo = isAPropertyOf(vevent, VCDTendProp))) + anEvent->setDtEnd(anEvent->dtStart()); + + /////////////////////////////////////////////////////////////////////////// + + // repeat stuff + if ((vo = isAPropertyOf(vevent, VCRRuleProp)) != 0) { + QString tmpStr = (s = fakeCString(vObjectUStringZValue(vo))); + deleteStr(s); + tmpStr.simplifyWhiteSpace(); + tmpStr = tmpStr.upper(); + + /********************************* DAILY ******************************/ + if (tmpStr.left(1) == "D") { + int index = tmpStr.find(' '); + int rFreq = tmpStr.mid(1, (index-1)).toInt(); + index = tmpStr.findRev(' ') + 1; // advance to last field + if (tmpStr.mid(index,1) == "#") index++; + if (tmpStr.find('T', index) != -1) { + QDate rEndDate = (ISOToQDateTime(tmpStr.mid(index, tmpStr.length()-index))).date(); + anEvent->recurrence()->setDaily(rFreq, rEndDate); + } else { + int rDuration = tmpStr.mid(index, tmpStr.length()-index).toInt(); + if (rDuration == 0) // VEvents set this to 0 forever, we use -1 + anEvent->recurrence()->setDaily(rFreq, -1); + else + anEvent->recurrence()->setDaily(rFreq, rDuration); + } + } + /********************************* WEEKLY ******************************/ + else if (tmpStr.left(1) == "W") { + int index = tmpStr.find(' '); + int last = tmpStr.findRev(' ') + 1; + int rFreq = tmpStr.mid(1, (index-1)).toInt(); + index += 1; // advance to beginning of stuff after freq + 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); + qba.setBit(dayNum); + index += 3; // advance to next day, or possibly "#" + } + } + index = last; if (tmpStr.mid(index,1) == "#") index++; + if (tmpStr.find('T', index) != -1) { + QDate rEndDate = (ISOToQDateTime(tmpStr.mid(index, tmpStr.length()-index))).date(); + anEvent->recurrence()->setWeekly(rFreq, qba, rEndDate); + } else { + int rDuration = tmpStr.mid(index, tmpStr.length()-index).toInt(); + if (rDuration == 0) + anEvent->recurrence()->setWeekly(rFreq, qba, -1); + else + anEvent->recurrence()->setWeekly(rFreq, qba, rDuration); + } + } + /**************************** MONTHLY-BY-POS ***************************/ + else if (tmpStr.left(2) == "MP") { + int index = tmpStr.find(' '); + int last = tmpStr.findRev(' ') + 1; + int rFreq = tmpStr.mid(2, (index-1)).toInt(); + index += 1; // advance to beginning of stuff after freq + 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) == "-") + // 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 != "#" + } + index = last; if (tmpStr.mid(index,1) == "#") index++; + if (tmpStr.find('T', index) != -1) { + QDate rEndDate = (ISOToQDateTime(tmpStr.mid(index, tmpStr.length() - + index))).date(); + anEvent->recurrence()->setMonthly(Recurrence::rMonthlyPos, rFreq, rEndDate); + } else { + int rDuration = tmpStr.mid(index, tmpStr.length()-index).toInt(); + if (rDuration == 0) + anEvent->recurrence()->setMonthly(Recurrence::rMonthlyPos, rFreq, -1); + else + anEvent->recurrence()->setMonthly(Recurrence::rMonthlyPos, rFreq, rDuration); + } + } + + /**************************** MONTHLY-BY-DAY ***************************/ + else if (tmpStr.left(2) == "MD") { + int index = tmpStr.find(' '); + int last = tmpStr.findRev(' ') + 1; + int rFreq = tmpStr.mid(2, (index-1)).toInt(); + index += 1; + short tmpDay; + if( index == last ) { + // e.g. MD1 #0 + tmpDay = anEvent->dtStart().date().day(); + anEvent->recurrence()->addMonthlyDay(tmpDay); + } + else { + // e.g. MD1 3 #0 + while (index < last) { + int index2 = tmpStr.find(' ', index); + tmpDay = tmpStr.mid(index, (index2-index)).toShort(); + index = index2-1; + if (tmpStr.mid(index, 1) == "-") + tmpDay = 0 - tmpDay; + index += 2; // advance the index; + anEvent->recurrence()->addMonthlyDay(tmpDay); + } // while != # + } + index = last; if (tmpStr.mid(index,1) == "#") index++; + if (tmpStr.find('T', index) != -1) { + QDate rEndDate = (ISOToQDateTime(tmpStr.mid(index, tmpStr.length()-index))).date(); + anEvent->recurrence()->setMonthly(Recurrence::rMonthlyDay, rFreq, rEndDate); + } else { + int rDuration = tmpStr.mid(index, tmpStr.length()-index).toInt(); + if (rDuration == 0) + anEvent->recurrence()->setMonthly(Recurrence::rMonthlyDay, rFreq, -1); + else + anEvent->recurrence()->setMonthly(Recurrence::rMonthlyDay, rFreq, rDuration); + } + } + + /*********************** YEARLY-BY-MONTH *******************************/ + else if (tmpStr.left(2) == "YM") { + int index = tmpStr.find(' '); + int last = tmpStr.findRev(' ') + 1; + int rFreq = tmpStr.mid(2, (index-1)).toInt(); + index += 1; + short tmpMonth; + if( index == last ) { + // e.g. YM1 #0 + tmpMonth = anEvent->dtStart().date().month(); + anEvent->recurrence()->addYearlyNum(tmpMonth); + } + else { + // e.g. YM1 3 #0 + while (index < last) { + int index2 = tmpStr.find(' ', index); + tmpMonth = tmpStr.mid(index, (index2-index)).toShort(); + index = index2+1; + anEvent->recurrence()->addYearlyNum(tmpMonth); + } // while != # + } + index = last; if (tmpStr.mid(index,1) == "#") index++; + if (tmpStr.find('T', index) != -1) { + QDate rEndDate = (ISOToQDateTime(tmpStr.mid(index, tmpStr.length()-index))).date(); + anEvent->recurrence()->setYearly(Recurrence::rYearlyMonth, rFreq, rEndDate); + } else { + int rDuration = tmpStr.mid(index, tmpStr.length()-index).toInt(); + if (rDuration == 0) + anEvent->recurrence()->setYearly(Recurrence::rYearlyMonth, rFreq, -1); + else + anEvent->recurrence()->setYearly(Recurrence::rYearlyMonth, rFreq, rDuration); + } + } + + /*********************** YEARLY-BY-DAY *********************************/ + else if (tmpStr.left(2) == "YD") { + int index = tmpStr.find(' '); + int last = tmpStr.findRev(' ') + 1; + int rFreq = tmpStr.mid(2, (index-1)).toInt(); + index += 1; + short tmpDay; + if( index == last ) { + // e.g. YD1 #0 + tmpDay = anEvent->dtStart().date().dayOfYear(); + anEvent->recurrence()->addYearlyNum(tmpDay); + } + else { + // e.g. YD1 123 #0 + while (index < last) { + int index2 = tmpStr.find(' ', index); + tmpDay = tmpStr.mid(index, (index2-index)).toShort(); + index = index2+1; + anEvent->recurrence()->addYearlyNum(tmpDay); + } // while != # + } + index = last; if (tmpStr.mid(index,1) == "#") index++; + if (tmpStr.find('T', index) != -1) { + QDate rEndDate = (ISOToQDateTime(tmpStr.mid(index, tmpStr.length()-index))).date(); + anEvent->recurrence()->setYearly(Recurrence::rYearlyDay, rFreq, rEndDate); + } else { + int rDuration = tmpStr.mid(index, tmpStr.length()-index).toInt(); + if (rDuration == 0) + anEvent->recurrence()->setYearly(Recurrence::rYearlyDay, rFreq, -1); + else + anEvent->recurrence()->setYearly(Recurrence::rYearlyDay, rFreq, rDuration); + } + } else { + kdDebug(5800) << "we don't understand this type of recurrence!" << endl; + } // if + } // repeats + + + // recurrence exceptions + if ((vo = isAPropertyOf(vevent, VCExDateProp)) != 0) { + s = fakeCString(vObjectUStringZValue(vo)); + QStringList exDates = QStringList::split(",",s); + QStringList::ConstIterator it; + for(it = exDates.begin(); it != exDates.end(); ++it ) { + anEvent->addExDate(ISOToQDate(*it)); + } + deleteStr(s); + } + + // summary + if ((vo = isAPropertyOf(vevent, VCSummaryProp))) { + s = fakeCString(vObjectUStringZValue(vo)); + anEvent->setSummary(QString::fromLocal8Bit(s)); + deleteStr(s); + } + if ((vo = isAPropertyOf(vevent, VCLocationProp))) { + s = fakeCString(vObjectUStringZValue(vo)); + anEvent->setLocation(QString::fromLocal8Bit(s)); + deleteStr(s); + } + + // description + if ((vo = isAPropertyOf(vevent, VCDescriptionProp)) != 0) { + s = fakeCString(vObjectUStringZValue(vo)); + if (!anEvent->description().isEmpty()) { + anEvent->setDescription(anEvent->description() + "\n" + + QString::fromLocal8Bit(s)); + } else { + anEvent->setDescription(QString::fromLocal8Bit(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().simplifyWhiteSpace(); + anEvent->setDescription(""); + 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 + int secrecy = Incidence::SecrecyPublic; + if ((vo = isAPropertyOf(vevent, VCClassProp)) != 0) { + s = fakeCString(vObjectUStringZValue(vo)); + if (strcmp(s,"PRIVATE") == 0) { + secrecy = Incidence::SecrecyPrivate; + } else if (strcmp(s,"CONFIDENTIAL") == 0) { + secrecy = Incidence::SecrecyConfidential; + } + deleteStr(s); + } + anEvent->setSecrecy(secrecy); + + // categories + QStringList tmpStrList; + int index1 = 0; + int index2 = 0; + if ((vo = isAPropertyOf(vevent, VCCategoriesProp)) != 0) { + s = fakeCString(vObjectUStringZValue(vo)); + QString categories = QString::fromLocal8Bit(s); + deleteStr(s); + //const char* category; + QString category; + while ((index2 = categories.find(',', index1)) != -1) { + //category = (const char *) categories.mid(index1, (index2 - index1)); + category = categories.mid(index1, (index2 - index1)); + tmpStrList.append(category); + index1 = index2+1; + } + // get last category + category = categories.mid(index1, (categories.length()-index1)); + tmpStrList.append(category); + anEvent->setCategories(tmpStrList); + } + + // attachments + tmpStrList.clear(); + initPropIterator(&voi, vevent); + while (moreIteration(&voi)) { + vo = nextVObject(&voi); + if (strcmp(vObjectName(vo), VCAttachProp) == 0) { + s = fakeCString(vObjectUStringZValue(vo)); + anEvent->addAttachment(new Attachment(QString(s))); + deleteStr(s); + } + } + + // resources + if ((vo = isAPropertyOf(vevent, VCResourcesProp)) != 0) { + QString resources = (s = fakeCString(vObjectUStringZValue(vo))); + deleteStr(s); + tmpStrList.clear(); + index1 = 0; + index2 = 0; + QString resource; + while ((index2 = resources.find(';', index1)) != -1) { + resource = resources.mid(index1, (index2 - index1)); + tmpStrList.append(resource); + index1 = index2; + } + anEvent->setResources(tmpStrList); + } + + /* alarm stuff */ + if ((vo = isAPropertyOf(vevent, VCDAlarmProp))) { + Alarm* alarm = anEvent->newAlarm(); + VObject *a; + if ((a = isAPropertyOf(vo, VCRunTimeProp))) { + alarm->setTime(ISOToQDateTime(s = fakeCString(vObjectUStringZValue(a)))); + deleteStr(s); + } + alarm->setEnabled(true); + if ((vo = isAPropertyOf(vevent, VCPAlarmProp))) { + if ((a = isAPropertyOf(vo, VCProcedureNameProp))) { + s = fakeCString(vObjectUStringZValue(a)); + alarm->setProcedureAlarm(QFile::decodeName(s)); + deleteStr(s); + } + } + if ((vo = isAPropertyOf(vevent, VCAAlarmProp))) { + if ((a = isAPropertyOf(vo, VCAudioContentProp))) { + s = fakeCString(vObjectUStringZValue(a)); + alarm->setAudioAlarm(QFile::decodeName(s)); + deleteStr(s); + } + } + } + + // priority + if ((vo = isAPropertyOf(vevent, VCPriorityProp))) { + anEvent->setPriority(atoi(s = fakeCString(vObjectUStringZValue(vo)))); + deleteStr(s); + } + + // transparency + if ((vo = isAPropertyOf(vevent, VCTranspProp)) != 0) { + int i = atoi(s = fakeCString(vObjectUStringZValue(vo))); + anEvent->setTransparency( i == 1 ? Event::Transparent : Event::Opaque ); + deleteStr(s); + } + + // related event + if ((vo = isAPropertyOf(vevent, VCRelatedToProp)) != 0) { + anEvent->setRelatedToUid(s = fakeCString(vObjectUStringZValue(vo))); + deleteStr(s); + mEventsRelate.append(anEvent); + } + + /* PILOT SYNC STUFF */ + if ((vo = isAPropertyOf(vevent, KPilotIdProp))) { + anEvent->setPilotId(atoi(s = fakeCString(vObjectUStringZValue(vo)))); + deleteStr(s); + } + else + anEvent->setPilotId(0); + + if ((vo = isAPropertyOf(vevent, KPilotStatusProp))) { + anEvent->setSyncStatus(atoi(s = fakeCString(vObjectUStringZValue(vo)))); + deleteStr(s); + } + else + anEvent->setSyncStatus(Event::SYNCMOD); + + return anEvent; +} + + +QString VCalFormat::qDateToISO(const QDate &qd) +{ + QString tmpStr; + + ASSERT(qd.isValid()); + + tmpStr.sprintf("%.2d%.2d%.2d", + qd.year(), qd.month(), qd.day()); + return tmpStr; + +} + +QString VCalFormat::qDateTimeToISO(const QDateTime &qdt, bool zulu) +{ + QString tmpStr; + + ASSERT(qdt.date().isValid()); + ASSERT(qdt.time().isValid()); + if (zulu) { + QDateTime tmpDT(qdt); + tmpDT = tmpDT.addSecs(60*(-mCalendar->getTimeZone())); // correct to GMT. + tmpStr.sprintf("%.2d%.2d%.2dT%.2d%.2d%.2dZ", + tmpDT.date().year(), tmpDT.date().month(), + tmpDT.date().day(), tmpDT.time().hour(), + tmpDT.time().minute(), tmpDT.time().second()); + } else { + tmpStr.sprintf("%.2d%.2d%.2dT%.2d%.2d%.2d", + qdt.date().year(), qdt.date().month(), + qdt.date().day(), qdt.time().hour(), + qdt.time().minute(), qdt.time().second()); + } + 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.left(4).toInt(); + month = tmpStr.mid(4,2).toInt(); + day = tmpStr.mid(6,2).toInt(); + hour = tmpStr.mid(9,2).toInt(); + minute = tmpStr.mid(11,2).toInt(); + second = tmpStr.mid(13,2).toInt(); + tmpDate.setYMD(year, month, day); + tmpTime.setHMS(hour, minute, second); + + ASSERT(tmpDate.isValid()); + ASSERT(tmpTime.isValid()); + QDateTime tmpDT(tmpDate, tmpTime); + // correct for GMT if string is in Zulu format + if (dtStr.at(dtStr.length()-1) == 'Z') + tmpDT = tmpDT.addSecs(60*mCalendar->getTimeZone()); + return tmpDT; +} + +QDate VCalFormat::ISOToQDate(const QString &dateStr) +{ + int year, month, day; + + year = dateStr.left(4).toInt(); + month = dateStr.mid(4,2).toInt(); + day = dateStr.mid(6,2).toInt(); + + return(QDate(year, month, day)); +} + +// 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) +{ + // this function will populate the caldict dictionary and other event + // lists. It turns vevents into Events and then inserts them. + + VObjectIterator i; + VObject *curVO, *curVOProp; + Event *anEvent; + + if ((curVO = isAPropertyOf(vcal, ICMethodProp)) != 0) { + char *methodType = 0; + methodType = fakeCString(vObjectUStringZValue(curVO)); + kdDebug() << "This calendar is an iTIP transaction of type '" + << methodType << "'" << endl; + delete methodType; + } + + // warn the user that we might have trouble reading non-known calendar. + if ((curVO = isAPropertyOf(vcal, VCProdIdProp)) != 0) { + char *s = fakeCString(vObjectUStringZValue(curVO)); + if (strcmp(productId().local8Bit(), s) != 0) + kdDebug() << "This vCalendar file was not created by KOrganizer " + "or any other product we support. Loading anyway..." << endl; + mLoadedProductId = s; + deleteStr(s); + } + + // warn the user we might have trouble reading this unknown version. + if ((curVO = isAPropertyOf(vcal, VCVersionProp)) != 0) { + char *s = fakeCString(vObjectUStringZValue(curVO)); + if (strcmp(_VCAL_VERSION, s) != 0) + kdDebug() << "This vCalendar file has version " << s + << "We only support " << _VCAL_VERSION << endl; + deleteStr(s); + } + + // set the time zone + if ((curVO = isAPropertyOf(vcal, VCTimeZoneProp)) != 0) { + char *s = fakeCString(vObjectUStringZValue(curVO)); + mCalendar->setTimeZone(s); + deleteStr(s); + } + + + // Store all events with a relatedTo property in a list for post-processing + mEventsRelate.clear(); + 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 ((curVOProp = isAPropertyOf(curVO, KPilotStatusProp)) != 0) { + char *s; + s = fakeCString(vObjectUStringZValue(curVOProp)); + // check to see if event was deleted by the kpilot conduit + if (atoi(s) == Event::SYNCDEL) { + deleteStr(s); + kdDebug(5800) << "skipping pilot-deleted event" << endl; + goto SKIP; + } + deleteStr(s); + } + + // this code checks to see if we are trying to read in an event + // that we already find to be in the calendar. If we find this + // to be the case, we skip the event. + if ((curVOProp = isAPropertyOf(curVO, VCUniqueStringProp)) != 0) { + char *s = fakeCString(vObjectUStringZValue(curVOProp)); + QString tmpStr(s); + deleteStr(s); + + if (mCalendar->event(tmpStr)) { + goto SKIP; + } + if (mCalendar->todo(tmpStr)) { + goto SKIP; + } + } + + if ((!(curVOProp = isAPropertyOf(curVO, VCDTstartProp))) && + (!(curVOProp = isAPropertyOf(curVO, VCDTendProp)))) { + kdDebug(5800) << "found a VEvent with no DTSTART and no DTEND! Skipping..." << endl; + goto SKIP; + } + + anEvent = VEventToEvent(curVO); + // we now use addEvent instead of insertEvent so that the + // signal/slot get connected. + if (anEvent) { + if ( !anEvent->dtStart().isValid() || !anEvent->dtEnd().isValid() ) { + kdDebug() << "VCalFormat::populate(): Event has invalid dates." + << endl; + } else { + mCalendar->addEvent(anEvent); + } + } else { + // some sort of error must have occurred while in translation. + goto SKIP; + } + } else if (strcmp(vObjectName(curVO), VCTodoProp) == 0) { + Todo *aTodo = VTodoToEvent(curVO); + mCalendar->addTodo(aTodo); + } 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 { + kdDebug(5800) << "Ignoring unknown vObject \"" << vObjectName(curVO) << "\"" << endl; + } + SKIP: + ; + } // while + + // Post-Process list of events with relations, put Event objects in relation + Event *ev; + for ( ev=mEventsRelate.first(); ev != 0; ev=mEventsRelate.next() ) { + ev->setRelatedTo(mCalendar->event(ev->relatedToUid())); + } + Todo *todo; + for ( todo=mTodosRelate.first(); todo != 0; todo=mTodosRelate.next() ) { + todo->setRelatedTo(mCalendar->todo(todo->relatedToUid())); + } +} + +const char *VCalFormat::dayFromNum(int day) +{ + const char *days[7] = { "MO ", "TU ", "WE ", "TH ", "FR ", "SA ", "SU " }; + + return days[day]; +} + +int VCalFormat::numFromDay(const QString &day) +{ + if (day == "MO ") return 0; + if (day == "TU ") return 1; + if (day == "WE ") return 2; + if (day == "TH ") return 3; + if (day == "FR ") return 4; + if (day == "SA ") return 5; + if (day == "SU ") return 6; + + return -1; // something bad happened. :) +} + +Attendee::PartStat VCalFormat::readStatus(const char *s) const +{ + QString statStr = s; + statStr = statStr.upper(); + Attendee::PartStat status; + + if (statStr == "X-ACTION") + status = Attendee::NeedsAction; + else if (statStr == "NEEDS ACTION") + status = Attendee::NeedsAction; + else if (statStr== "ACCEPTED") + status = Attendee::Accepted; + else if (statStr== "SENT") + status = Attendee::NeedsAction; + else if (statStr== "TENTATIVE") + status = Attendee::Tentative; + else if (statStr== "CONFIRMED") + status = Attendee::Accepted; + else if (statStr== "DECLINED") + status = Attendee::Declined; + else if (statStr== "COMPLETED") + status = Attendee::Completed; + else if (statStr== "DELEGATED") + status = Attendee::Delegated; + else { + kdDebug(5800) << "error setting attendee mStatus, unknown mStatus!" << endl; + status = Attendee::NeedsAction; + } + + return status; +} + +QCString VCalFormat::writeStatus(Attendee::PartStat status) const +{ + switch(status) { + default: + case Attendee::NeedsAction: + return "NEEDS ACTION"; + break; + case Attendee::Accepted: + return "ACCEPTED"; + break; + case Attendee::Declined: + return "DECLINED"; + break; + case Attendee::Tentative: + return "TENTATIVE"; + break; + case Attendee::Delegated: + return "DELEGATED"; + break; + case Attendee::Completed: + return "COMPLETED"; + break; + case Attendee::InProcess: + return "NEEDS ACTION"; + break; + } +} diff --git a/libkcal/vcalformat.h b/libkcal/vcalformat.h new file mode 100644 index 0000000..d4cecbc --- a/dev/null +++ b/libkcal/vcalformat.h @@ -0,0 +1,108 @@ +/* + This file is part of libkcal. + Copyright (c) 1998 Preston Brown + Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef _VCALFORMAT_H +#define _VCALFORMAT_H + +#include "calformat.h" + +#define _VCAL_VERSION "1.0" + +class VObject; + +namespace KCal { + +/** + This class implements the vCalendar format. It provides methods for + loading/saving/converting vCalendar format data into the internal KOrganizer + representation as Calendar and Events. + + @short vCalendar format implementation +*/ +class VCalFormat : public CalFormat { + public: + VCalFormat(); + virtual ~VCalFormat(); + + /** loads a calendar on disk in vCalendar format into the current calendar. + * any information already present is lost. Returns TRUE if successful, + * else returns FALSE. + * @param fileName the name of the calendar on disk. + */ + bool load(Calendar *,const QString &fileName); + /** writes out the calendar to disk in vCalendar format. Returns true if + * successful and false on error. + * @param fileName the name of the file + */ + bool save(Calendar *,const QString &fileName); + + /** + Parse string and populate calendar with that information. + */ + bool fromString( Calendar *, const QString & ); + /** + Return calendar information as string. + */ + QString toString( Calendar * ); + + protected: + /** translates a VObject of the TODO type into a Event */ + Todo *VTodoToEvent(VObject *vtodo); + /** translates a VObject into a Event and returns a pointer to it. */ + Event *VEventToEvent(VObject *vevent); + /** translate a Event into a VTodo-type VObject and return pointer */ + VObject *eventToVTodo(const Todo *anEvent); + /** translate a Event into a VObject and returns a pointer to it. */ + VObject* eventToVEvent(const Event *anEvent); + + /** takes a QDate and returns a string in the format YYYYMMDDTHHMMSS */ + QString qDateToISO(const QDate &); + /** takes a QDateTime and returns a string in format YYYYMMDDTHHMMSS */ + QString qDateTimeToISO(const QDateTime &, bool zulu=TRUE); + /** takes a string in the format YYYYMMDDTHHMMSS and returns a + * valid QDateTime. */ + QDateTime ISOToQDateTime(const QString & dtStr); + /** takes a string in the format YYYYMMDD and returns a + * valid QDate. */ + QDate ISOToQDate(const QString & dtStr); + /** 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); + + /** takes a number 0 - 6 and returns the two letter string of that day, + * i.e. MO, TU, WE, etc. */ + const char *dayFromNum(int day); + /** the reverse of the above function. */ + int numFromDay(const QString &day); + + Attendee::PartStat readStatus(const char *s) const; + QCString writeStatus(Attendee::PartStat status) const; + + private: + Calendar *mCalendar; + + QPtrList<Event> mEventsRelate; // events with relations + QPtrList<Todo> mTodosRelate; // todos with relations +}; + +} + +#endif diff --git a/libkcal/versit/port.h b/libkcal/versit/port.h new file mode 100644 index 0000000..afc16dd --- a/dev/null +++ b/libkcal/versit/port.h @@ -0,0 +1,75 @@ +/*************************************************************************** +(C) Copyright 1996 Apple Computer, Inc., AT&T Corp., International +Business Machines Corporation and Siemens Rolm Communications Inc. + +For purposes of this license notice, the term Licensors shall mean, +collectively, Apple Computer, Inc., AT&T Corp., International +Business Machines Corporation and Siemens Rolm Communications Inc. +The term Licensor shall mean any of the Licensors. + +Subject to acceptance of the following conditions, permission is hereby +granted by Licensors without the need for written agreement and without +license or royalty fees, to use, copy, modify and distribute this +software for any purpose. + +The above copyright notice and the following four paragraphs must be +reproduced in all copies of this software and any software including +this software. + +THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS AND NO LICENSOR SHALL HAVE +ANY OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS OR +MODIFICATIONS. + +IN NO EVENT SHALL ANY LICENSOR BE LIABLE TO ANY PARTY FOR DIRECT, +INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT +OF THE USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +EACH LICENSOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO ANY WARRANTY OF NONINFRINGEMENT OR THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. + +The software is provided with RESTRICTED RIGHTS. Use, duplication, or +disclosure by the government are subject to restrictions set forth in +DFARS 252.227-7013 or 48 CFR 52.227-19, as applicable. + +***************************************************************************/ + +#ifndef __PORT_H__ +#define __PORT_H__ 1 + +#if defined(__CPLUSPLUS__) || defined(__cplusplus) +extern "C" { +#endif + +#define vCardClipboardFormat "+//ISBN 1-887687-00-9::versit::PDI//vCard" +#define vCalendarClipboardFormat "+//ISBN 1-887687-00-9::versit::PDI//vCalendar" + +/* The above strings vCardClipboardFormat and vCalendarClipboardFormat +are globally unique IDs which can be used to generate clipboard format +ID's as per the requirements of a specific platform. For example, in +Windows they are used as the parameter in a call to RegisterClipboardFormat. +For example: + + CLIPFORMAT foo = RegisterClipboardFormat(vCardClipboardFormat); + +*/ + +#define vCardMimeType "text/x-vCard" +#define vCalendarMimeType "text/x-vCalendar" + +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif + +#define Parse_Debug(t) + +#if defined(__CPLUSPLUS__) || defined(__cplusplus) +} +#endif + +#endif /* __PORT_H__ */ diff --git a/libkcal/versit/vcc.c b/libkcal/versit/vcc.c new file mode 100644 index 0000000..350cac3 --- a/dev/null +++ b/libkcal/versit/vcc.c @@ -0,0 +1,2162 @@ + +/* A Bison parser, made from ./vcc.y + by GNU Bison version 1.28 */ + +#define YYBISON 1 /* Identify Bison output. */ + +#ifdef _WIN32_ +#define strcasecmp _stricmp +#endif + +#define EQ 257 +#define COLON 258 +#define DOT 259 +#define SEMICOLON 260 +#define SPACE 261 +#define HTAB 262 +#define LINESEP 263 +#define NEWLINE 264 +#define BEGIN_VCARD 265 +#define END_VCARD 266 +#define BEGIN_VCAL 267 +#define END_VCAL 268 +#define BEGIN_VEVENT 269 +#define END_VEVENT 270 +#define BEGIN_VTODO 271 +#define END_VTODO 272 +#define ID 273 +#define STRING 274 + +#line 1 "./vcc.y" + + +/*************************************************************************** +(C) Copyright 1996 Apple Computer, Inc., AT&T Corp., International +Business Machines Corporation and Siemens Rolm Communications Inc. + +For purposes of this license notice, the term Licensors shall mean, +collectively, Apple Computer, Inc., AT&T Corp., International +Business Machines Corporation and Siemens Rolm Communications Inc. +The term Licensor shall mean any of the Licensors. + +Subject to acceptance of the following conditions, permission is hereby +granted by Licensors without the need for written agreement and without +license or royalty fees, to use, copy, modify and distribute this +software for any purpose. + +The above copyright notice and the following four paragraphs must be +reproduced in all copies of this software and any software including +this software. + +THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS AND NO LICENSOR SHALL HAVE +ANY OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS OR +MODIFICATIONS. + +IN NO EVENT SHALL ANY LICENSOR BE LIABLE TO ANY PARTY FOR DIRECT, +INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT +OF THE USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +EACH LICENSOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO ANY WARRANTY OF NONINFRINGEMENT OR THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. + +The software is provided with RESTRICTED RIGHTS. Use, duplication, or +disclosure by the government are subject to restrictions set forth in +DFARS 252.227-7013 or 48 CFR 52.227-19, as applicable. + +***************************************************************************/ + +/* + * src: vcc.c + * doc: Parser for vCard and vCalendar. Note that this code is + * generated by a yacc parser generator. Generally it should not + * be edited by hand. The real source is vcc.y. The #line directives + * can be commented out here to make it easier to trace through + * in a debugger. However, if a bug is found it should + * be fixed in vcc.y and this file regenerated. + */ + + +/* debugging utilities */ +#if __DEBUG +#define DBG_(x) printf x +#else +#define DBG_(x) +#endif + +/**** External Functions ****/ + +/* assign local name to parser variables and functions so that + we can use more than one yacc based parser. +*/ + +#define yyparse mime_parse +#define yylex mime_lex +#define yyerror mime_error +#define yychar mime_char +/* #define p_yyval p_mime_val */ +#undef yyval +#define yyval mime_yyval +/* #define p_yylval p_mime_lval */ +#undef yylval +#define yylval mime_yylval +#define yydebug mime_debug +#define yynerrs mime_nerrs +#define yyerrflag mime_errflag +#define yyss mime_ss +#define yyssp mime_ssp +#define yyvs mime_vs +#define yyvsp mime_vsp +#define yylhs mime_lhs +#define yylen mime_len +#define yydefred mime_defred +#define yydgoto mime_dgoto +#define yysindex mime_sindex +#define yyrindex mime_rindex +#define yygindex mime_gindex +#define yytable mime_table +#define yycheck mime_check +#define yyname mime_name +#define yyrule mime_rule +#undef YYPREFIX +#define YYPREFIX "mime_" + + +#ifndef _NO_LINE_FOLDING +#define _SUPPORT_LINE_FOLDING 1 +#endif + +#include <string.h> +#ifndef __FreeBSD__ +#include <malloc.h> +#endif +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include "vcc.h" + +/* The following is a hack that I hope will get things compiling + * on SunOS 4.1.x systems + */ +#ifndef SEEK_SET +#define SEEK_SET 0 /* Seek from beginning of file. */ +#define SEEK_CUR 1 /* Seek from current position. */ +#define SEEK_END 2 /* Seek from end of file. */ +#endif + +/**** Types, Constants ****/ + +#define YYDEBUG 0 /* 1 to compile in some debugging code */ +#define MAXTOKEN 256 /* maximum token (line) length */ +#define YYSTACKSIZE 1000 /* ~unref ? */ +#define MAXLEVEL 10 /* max # of nested objects parseable */ + /* (includes outermost) */ + + +/**** Global Variables ****/ +int mime_lineNum, mime_numErrors; /* yyerror() can use these */ +static VObject* vObjList; +static VObject *curProp; +static VObject *curObj; +static VObject* ObjStack[MAXLEVEL]; +static int ObjStackTop; + + +/* A helpful utility for the rest of the app. */ +#if __CPLUSPLUS__ +extern "C" { +#endif + + /* static void Parse_Debug(const char *s);*/ + static void yyerror(char *s); + +#if __CPLUSPLUS__ + }; +#endif + +int yyparse(); +static int yylex(); +enum LexMode { + L_NORMAL, + L_VCARD, + L_VCAL, + L_VEVENT, + L_VTODO, + L_VALUES, + L_BASE64, + L_QUOTED_PRINTABLE + }; + +/**** Private Forward Declarations ****/ +static int pushVObject(const char *prop); +static VObject* popVObject(); +char* lexDataFromBase64(); +static void lexPopMode(int top); +static int lexWithinMode(enum LexMode mode); +static void lexPushMode(enum LexMode mode); +static void enterProps(const char *s); +static void enterAttr(const char *s1, const char *s2); +/* static void enterValues(const char *value); */ +static void appendValue(const char *value); +static void mime_error_(char *s); + + +#line 181 "./vcc.y" +typedef union { + char *str; + VObject *vobj; + } YYSTYPE; +#include <stdio.h> + +#ifndef __cplusplus +#ifndef __STDC__ +#define const +#endif +#endif + + + +#define YYFINAL 62 +#define YYFLAG -32768 +#define YYNTBASE 21 + +#define YYTRANSLATE(x) ((unsigned)(x) <= 274 ? yytranslate[x] : 51) + +static const char yytranslate[] = { 0, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 1, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20 +}; + +#if YYDEBUG != 0 +static const short yyprhs[] = { 0, + 0, 2, 3, 7, 9, 11, 13, 14, 19, 20, + 24, 27, 29, 30, 36, 38, 39, 43, 45, 48, + 50, 53, 55, 59, 61, 62, 67, 69, 71, 72, + 73, 78, 79, 83, 86, 88, 90, 92, 94, 95, + 100, 101, 105, 106, 111, 112 +}; + +static const short yyrhs[] = { 22, + 0, 0, 24, 23, 22, 0, 24, 0, 25, 0, + 40, 0, 0, 11, 26, 28, 12, 0, 0, 11, + 27, 12, 0, 29, 28, 0, 29, 0, 0, 31, + 4, 30, 37, 9, 0, 1, 0, 0, 36, 32, + 33, 0, 36, 0, 34, 33, 0, 34, 0, 6, + 35, 0, 36, 0, 36, 3, 36, 0, 19, 0, + 0, 39, 6, 38, 37, 0, 39, 0, 20, 0, + 0, 0, 13, 41, 43, 14, 0, 0, 13, 42, + 14, 0, 44, 43, 0, 44, 0, 45, 0, 48, + 0, 28, 0, 0, 15, 46, 28, 16, 0, 0, + 15, 47, 16, 0, 0, 17, 49, 28, 18, 0, + 0, 17, 50, 18, 0 +}; + +#endif + +#if YYDEBUG != 0 +static const short yyrline[] = { 0, + 209, 212, 215, 215, 219, 220, 223, 229, 234, 240, + 246, 247, 250, 254, 260, 263, 268, 268, 274, 275, + 278, 281, 285, 292, 295, 296, 296, 300, 301, 305, + 309, 311, 314, 317, 318, 321, 323, 324, 327, 334, + 339, 345, 351, 358, 363, 369 +}; +#endif + + +#if YYDEBUG != 0 || defined (YYERROR_VERBOSE) + +static const char * const yytname[] = { "$","error","$undefined.","EQ","COLON", +"DOT","SEMICOLON","SPACE","HTAB","LINESEP","NEWLINE","BEGIN_VCARD","END_VCARD", +"BEGIN_VCAL","END_VCAL","BEGIN_VEVENT","END_VEVENT","BEGIN_VTODO","END_VTODO", +"ID","STRING","mime","vobjects","@1","vobject","vcard","@2","@3","items","item", +"@4","prop","@5","attr_params","attr_param","attr","name","values","@6","value", +"vcal","@7","@8","calitems","calitem","eventitem","@9","@10","todoitem","@11", +"@12", NULL +}; +#endif + +static const short yyr1[] = { 0, + 21, 23, 22, 22, 24, 24, 26, 25, 27, 25, + 28, 28, 30, 29, 29, 32, 31, 31, 33, 33, + 34, 35, 35, 36, 38, 37, 37, 39, 39, 41, + 40, 42, 40, 43, 43, 44, 44, 44, 46, 45, + 47, 45, 49, 48, 50, 48 +}; + +static const short yyr2[] = { 0, + 1, 0, 3, 1, 1, 1, 0, 4, 0, 3, + 2, 1, 0, 5, 1, 0, 3, 1, 2, 1, + 2, 1, 3, 1, 0, 4, 1, 1, 0, 0, + 4, 0, 3, 2, 1, 1, 1, 1, 0, 4, + 0, 3, 0, 4, 0, 3 +}; + +static const short yydefact[] = { 0, + 7, 30, 1, 2, 5, 6, 0, 0, 0, 0, + 0, 15, 24, 0, 0, 0, 16, 10, 39, 43, + 38, 0, 0, 36, 37, 33, 3, 8, 11, 13, + 0, 0, 0, 0, 0, 31, 34, 29, 0, 17, + 20, 0, 42, 0, 46, 28, 0, 27, 21, 22, + 19, 40, 44, 14, 25, 0, 29, 23, 26, 0, + 0, 0 +}; + +static const short yydefgoto[] = { 60, + 3, 11, 4, 5, 7, 8, 21, 15, 38, 16, + 31, 40, 41, 49, 17, 47, 57, 48, 6, 9, + 10, 22, 23, 24, 32, 33, 25, 34, 35 +}; + +static const short yypact[] = { -9, + -6, -5,-32768, 7,-32768,-32768, 2, -1, 19, 15, + -9,-32768,-32768, 1, 0, 26, 27,-32768, 16, 17, +-32768, 23, 9,-32768,-32768,-32768,-32768,-32768,-32768,-32768, + 33, 2, 24, 2, 25,-32768,-32768, 13, 22,-32768, + 33, 28,-32768, 29,-32768,-32768, 36, 40,-32768, 39, +-32768,-32768,-32768,-32768,-32768, 22, 13,-32768,-32768, 48, + 49,-32768 +}; + +static const short yypgoto[] = {-32768, + 41,-32768,-32768,-32768,-32768,-32768, -7,-32768,-32768,-32768, +-32768, 10,-32768,-32768, -34, -4,-32768,-32768,-32768,-32768, +-32768, 31,-32768,-32768,-32768,-32768,-32768,-32768,-32768 +}; + + +#define YYLAST 54 + + +static const short yytable[] = { 14, + 12, 1, 12, 2, 50, -9, -4, 29, -32, 12, + 18, -12, 28, -12, -12, -12, -12, -12, 13, 12, + 13, 58, -35, 19, 42, 20, 44, 13, 26, 30, + -18, -41, 46, 19, -45, 20, 36, 13, 39, 43, + 13, 56, 45, 52, 54, 55, 53, 61, 62, 0, + 51, 27, 59, 37 +}; + +static const short yycheck[] = { 7, + 1, 11, 1, 13, 39, 12, 0, 15, 14, 1, + 12, 12, 12, 14, 15, 16, 17, 18, 19, 1, + 19, 56, 14, 15, 32, 17, 34, 19, 14, 4, + 4, 16, 20, 15, 18, 17, 14, 19, 6, 16, + 19, 3, 18, 16, 9, 6, 18, 0, 0, -1, + 41, 11, 57, 23 +}; +/* -*-C-*- Note some compilers choke on comments on `#line' lines. */ +#line 3 "/usr/share/bison.simple" +/* This file comes from bison-1.28. */ + +/* Skeleton output parser for bison, + Copyright (C) 1984, 1989, 1990 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* As a special exception, when this file is copied by Bison into a + Bison output file, you may use that output file without restriction. + This special exception was added by the Free Software Foundation + in version 1.24 of Bison. */ + +/* This is the parser code that is written into each bison parser + when the %semantic_parser declaration is not specified in the grammar. + It was written by Richard Stallman by simplifying the hairy parser + used when %semantic_parser is specified. */ + +#ifndef YYSTACK_USE_ALLOCA +#ifdef alloca +#define YYSTACK_USE_ALLOCA +#else /* alloca not defined */ +#ifdef __GNUC__ +#define YYSTACK_USE_ALLOCA +#define alloca __builtin_alloca +#else /* not GNU C. */ +#if (!defined (__STDC__) && defined (sparc)) || defined (__sparc__) || defined (__sparc) || defined (__sgi) || (defined (__sun) && defined (__i386)) +#define YYSTACK_USE_ALLOCA +#include <alloca.h> +#else /* not sparc */ +/* We think this test detects Watcom and Microsoft C. */ +/* This used to test MSDOS, but that is a bad idea + since that symbol is in the user namespace. */ +#if (defined (_MSDOS) || defined (_MSDOS_)) && !defined (__TURBOC__) +#if 0 /* No need for malloc.h, which pollutes the namespace; + instead, just don't use alloca. */ +#include <malloc.h> +#endif +#else /* not MSDOS, or __TURBOC__ */ +#if defined(_AIX) +/* I don't know what this was needed for, but it pollutes the namespace. + So I turned it off. rms, 2 May 1997. */ +/* #include <malloc.h> */ + #pragma alloca +#define YYSTACK_USE_ALLOCA +#else /* not MSDOS, or __TURBOC__, or _AIX */ +#if 0 +#ifdef __hpux /* haible@ilog.fr says this works for HPUX 9.05 and up, + and on HPUX 10. Eventually we can turn this on. */ +#define YYSTACK_USE_ALLOCA +#define alloca __builtin_alloca +#endif /* __hpux */ +#endif +#endif /* not _AIX */ +#endif /* not MSDOS, or __TURBOC__ */ +#endif /* not sparc */ +#endif /* not GNU C */ +#endif /* alloca not defined */ +#endif /* YYSTACK_USE_ALLOCA not defined */ + +#ifdef YYSTACK_USE_ALLOCA +#define YYSTACK_ALLOC alloca +#else +#define YYSTACK_ALLOC malloc +#endif + +/* Note: there must be only one dollar sign in this file. + It is replaced by the list of actions, each action + as one case of the switch. */ + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY -2 +#define YYEOF 0 +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrlab1 +/* Like YYERROR except do call yyerror. + This remains here temporarily to ease the + transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. */ +#define YYFAIL goto yyerrlab +#define YYRECOVERING() (!!yyerrstatus) +#define YYBACKUP(token, value) \ +do \ + if (yychar == YYEMPTY && yylen == 1) \ + { yychar = (token), yylval = (value); \ + yychar1 = YYTRANSLATE (yychar); \ + YYPOPSTACK; \ + goto yybackup; \ + } \ + else \ + { yyerror ("syntax error: cannot back up"); YYERROR; } \ +while (0) + +#define YYTERROR 1 +#define YYERRCODE 256 + +#ifndef YYPURE +#define YYLEX yylex() +#endif + +#ifdef YYPURE +#ifdef YYLSP_NEEDED +#ifdef YYLEX_PARAM +#define YYLEX yylex(&yylval, &yylloc, YYLEX_PARAM) +#else +#define YYLEX yylex(&yylval, &yylloc) +#endif +#else /* not YYLSP_NEEDED */ +#ifdef YYLEX_PARAM +#define YYLEX yylex(&yylval, YYLEX_PARAM) +#else +#define YYLEX yylex(&yylval) +#endif +#endif /* not YYLSP_NEEDED */ +#endif + +/* If nonreentrant, generate the variables here */ + +#ifndef YYPURE + +int yychar; /* the lookahead symbol */ +YYSTYPE yylval; /* the semantic value of the */ + /* lookahead symbol */ + +#ifdef YYLSP_NEEDED +YYLTYPE yylloc; /* location data for the lookahead */ + /* symbol */ +#endif + +int yynerrs; /* number of parse errors so far */ +#endif /* not YYPURE */ + +#if YYDEBUG != 0 +int yydebug; /* nonzero means print parse trace */ +/* Since this is uninitialized, it does not stop multiple parsers + from coexisting. */ +#endif + +/* YYINITDEPTH indicates the initial size of the parser's stacks */ + +#ifndef YYINITDEPTH +#define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH is the maximum size the stacks can grow to + (effective only if the built-in stack extension method is used). */ + +#if YYMAXDEPTH == 0 +#undef YYMAXDEPTH +#endif + +#ifndef YYMAXDEPTH +#define YYMAXDEPTH 10000 +#endif + +/* Define __yy_memcpy. Note that the size argument + should be passed with type unsigned int, because that is what the non-GCC + definitions require. With GCC, __builtin_memcpy takes an arg + of type size_t, but it can handle unsigned int. */ + +#if __GNUC__ > 1 /* GNU C and GNU C++ define this. */ +#define __yy_memcpy(TO,FROM,COUNT) __builtin_memcpy(TO,FROM,COUNT) +#else /* not GNU C or C++ */ +#ifndef __cplusplus + +/* This is the most reliable way to avoid incompatibilities + in available built-in functions on various systems. */ +static void +__yy_memcpy (to, from, count) + char *to; + char *from; + unsigned int count; +{ + register char *f = from; + register char *t = to; + register int i = count; + + while (i-- > 0) + *t++ = *f++; +} + +#else /* __cplusplus */ + +/* This is the most reliable way to avoid incompatibilities + in available built-in functions on various systems. */ +static void +__yy_memcpy (char *to, char *from, unsigned int count) +{ + register char *t = to; + register char *f = from; + register int i = count; + + while (i-- > 0) + *t++ = *f++; +} + +#endif +#endif + +#line 217 "/usr/share/bison.simple" + +/* The user can define YYPARSE_PARAM as the name of an argument to be passed + into yyparse. The argument should have type void *. + It should actually point to an object. + Grammar actions can access the variable by casting it + to the proper pointer type. */ + +#ifdef YYPARSE_PARAM +#ifdef __cplusplus +#define YYPARSE_PARAM_ARG void *YYPARSE_PARAM +#define YYPARSE_PARAM_DECL +#else /* not __cplusplus */ +#define YYPARSE_PARAM_ARG YYPARSE_PARAM +#define YYPARSE_PARAM_DECL void *YYPARSE_PARAM; +#endif /* not __cplusplus */ +#else /* not YYPARSE_PARAM */ +#define YYPARSE_PARAM_ARG +#define YYPARSE_PARAM_DECL +#endif /* not YYPARSE_PARAM */ + +/* Prevent warning if -Wstrict-prototypes. */ +#if defined (__GNUC__) && ! defined (__cplusplus) +#ifdef YYPARSE_PARAM +int yyparse (void *); +#else +int yyparse (void); +#endif +#endif + +int +yyparse(YYPARSE_PARAM_ARG) + YYPARSE_PARAM_DECL +{ + register int yystate; + register int yyn; + register short *yyssp; + register YYSTYPE *yyvsp; + int yyerrstatus; /* number of tokens to shift before error messages enabled */ + int yychar1 = 0; /* lookahead token as an internal (translated) token number */ + + short yyssa[YYINITDEPTH]; /* the state stack */ + YYSTYPE yyvsa[YYINITDEPTH]; /* the semantic value stack */ + + short *yyss = yyssa; /* refer to the stacks thru separate pointers */ + YYSTYPE *yyvs = yyvsa; /* to allow yyoverflow to reallocate them elsewhere */ + +#ifdef YYLSP_NEEDED + YYLTYPE yylsa[YYINITDEPTH]; /* the location stack */ + YYLTYPE *yyls = yylsa; + YYLTYPE *yylsp; + +#define YYPOPSTACK (yyvsp--, yyssp--, yylsp--) +#else +#define YYPOPSTACK (yyvsp--, yyssp--) +#endif + + int yystacksize = YYINITDEPTH; + int yyfree_stacks = 0; + +#ifdef YYPURE + int yychar; + YYSTYPE yylval; + int yynerrs; +#ifdef YYLSP_NEEDED + YYLTYPE yylloc; +#endif +#endif + + YYSTYPE yyval; /* the variable used to return */ + /* semantic values from the action */ + /* routines */ + + int yylen; + +#if YYDEBUG != 0 + if (yydebug) + fprintf(stderr, "Starting parse\n"); +#endif + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + + yyssp = yyss - 1; + yyvsp = yyvs; +#ifdef YYLSP_NEEDED + yylsp = yyls; +#endif + +/* Push a new state, which is found in yystate . */ +/* In all cases, when you get here, the value and location stacks + have just been pushed. so pushing a state here evens the stacks. */ +yynewstate: + + *++yyssp = yystate; + + if (yyssp >= yyss + yystacksize - 1) + { + /* Give user a chance to reallocate the stack */ + /* Use copies of these so that the &'s don't force the real ones into memory. */ + YYSTYPE *yyvs1 = yyvs; + short *yyss1 = yyss; +#ifdef YYLSP_NEEDED + YYLTYPE *yyls1 = yyls; +#endif + + /* Get the current used size of the three stacks, in elements. */ + int size = yyssp - yyss + 1; + +#ifdef yyoverflow + /* Each stack pointer address is followed by the size of + the data in use in that stack, in bytes. */ +#ifdef YYLSP_NEEDED + /* This used to be a conditional around just the two extra args, + but that might be undefined if yyoverflow is a macro. */ + yyoverflow("parser stack overflow", + &yyss1, size * sizeof (*yyssp), + &yyvs1, size * sizeof (*yyvsp), + &yyls1, size * sizeof (*yylsp), + &yystacksize); +#else + yyoverflow("parser stack overflow", + &yyss1, size * sizeof (*yyssp), + &yyvs1, size * sizeof (*yyvsp), + &yystacksize); +#endif + + yyss = yyss1; yyvs = yyvs1; +#ifdef YYLSP_NEEDED + yyls = yyls1; +#endif +#else /* no yyoverflow */ + /* Extend the stack our own way. */ + if (yystacksize >= YYMAXDEPTH) + { + yyerror("parser stack overflow"); + if (yyfree_stacks) + { + free (yyss); + free (yyvs); +#ifdef YYLSP_NEEDED + free (yyls); +#endif + } + return 2; + } + yystacksize *= 2; + if (yystacksize > YYMAXDEPTH) + yystacksize = YYMAXDEPTH; +#ifndef YYSTACK_USE_ALLOCA + yyfree_stacks = 1; +#endif + yyss = (short *) YYSTACK_ALLOC (yystacksize * sizeof (*yyssp)); + __yy_memcpy ((char *)yyss, (char *)yyss1, + size * (unsigned int) sizeof (*yyssp)); + yyvs = (YYSTYPE *) YYSTACK_ALLOC (yystacksize * sizeof (*yyvsp)); + __yy_memcpy ((char *)yyvs, (char *)yyvs1, + size * (unsigned int) sizeof (*yyvsp)); +#ifdef YYLSP_NEEDED + yyls = (YYLTYPE *) YYSTACK_ALLOC (yystacksize * sizeof (*yylsp)); + __yy_memcpy ((char *)yyls, (char *)yyls1, + size * (unsigned int) sizeof (*yylsp)); +#endif +#endif /* no yyoverflow */ + + yyssp = yyss + size - 1; + yyvsp = yyvs + size - 1; +#ifdef YYLSP_NEEDED + yylsp = yyls + size - 1; +#endif + +#if YYDEBUG != 0 + if (yydebug) + fprintf(stderr, "Stack size increased to %d\n", yystacksize); +#endif + + if (yyssp >= yyss + yystacksize - 1) + YYABORT; + } + +#if YYDEBUG != 0 + if (yydebug) + fprintf(stderr, "Entering state %d\n", yystate); +#endif + + goto yybackup; + yybackup: + +/* Do appropriate processing given the current state. */ +/* Read a lookahead token if we need one and don't already have one. */ +/* yyresume: */ + + /* First try to decide what to do without reference to lookahead token. */ + + yyn = yypact[yystate]; + if (yyn == YYFLAG) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* yychar is either YYEMPTY or YYEOF + or a valid token in external form. */ + + if (yychar == YYEMPTY) + { +#if YYDEBUG != 0 + if (yydebug) + fprintf(stderr, "Reading a token: "); +#endif + yychar = YYLEX; + } + + /* Convert token to internal form (in yychar1) for indexing tables with */ + + if (yychar <= 0) /* This means end of input. */ + { + yychar1 = 0; + yychar = YYEOF; /* Don't call YYLEX any more */ + +#if YYDEBUG != 0 + if (yydebug) + fprintf(stderr, "Now at end of input.\n"); +#endif + } + else + { + yychar1 = YYTRANSLATE(yychar); + +#if YYDEBUG != 0 + if (yydebug) + { + fprintf (stderr, "Next token is %d (%s", yychar, yytname[yychar1]); + /* Give the individual parser a way to print the precise meaning + of a token, for further debugging info. */ +#ifdef YYPRINT + YYPRINT (stderr, yychar, yylval); +#endif + fprintf (stderr, ")\n"); + } +#endif + } + + yyn += yychar1; + if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar1) + goto yydefault; + + yyn = yytable[yyn]; + + /* yyn is what to do for this token type in this state. + Negative => reduce, -yyn is rule number. + Positive => shift, yyn is new state. + New state is final state => don't bother to shift, + just return success. + 0, or most negative number => error. */ + + if (yyn < 0) + { + if (yyn == YYFLAG) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + else if (yyn == 0) + goto yyerrlab; + + if (yyn == YYFINAL) + YYACCEPT; + + /* Shift the lookahead token. */ + +#if YYDEBUG != 0 + if (yydebug) + fprintf(stderr, "Shifting token %d (%s), ", yychar, yytname[yychar1]); +#endif + + /* Discard the token being shifted unless it is eof. */ + if (yychar != YYEOF) + yychar = YYEMPTY; + + *++yyvsp = yylval; +#ifdef YYLSP_NEEDED + *++yylsp = yylloc; +#endif + + /* count tokens shifted since error; after three, turn off error status. */ + if (yyerrstatus) yyerrstatus--; + + yystate = yyn; + goto yynewstate; + +/* Do the default action for the current state. */ +yydefault: + + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + +/* Do a reduction. yyn is the number of a rule to reduce with. */ +yyreduce: + yylen = yyr2[yyn]; + if (yylen > 0) + yyval = yyvsp[1-yylen]; /* implement default value of the action */ + +#if YYDEBUG != 0 + if (yydebug) + { + int i; + + fprintf (stderr, "Reducing via rule %d (line %d), ", + yyn, yyrline[yyn]); + + /* Print the symbols being reduced, and their result. */ + for (i = yyprhs[yyn]; yyrhs[i] > 0; i++) + fprintf (stderr, "%s ", yytname[yyrhs[i]]); + fprintf (stderr, " -> %s\n", yytname[yyr1[yyn]]); + } +#endif + + + switch (yyn) { + +case 2: +#line 213 "./vcc.y" +{ addList(&vObjList, yyvsp[0].vobj); curObj = 0; ; + break;} +case 4: +#line 216 "./vcc.y" +{ addList(&vObjList, yyvsp[0].vobj); curObj = 0; ; + break;} +case 7: +#line 225 "./vcc.y" +{ + lexPushMode(L_VCARD); + if (!pushVObject(VCCardProp)) YYERROR; + ; + break;} +case 8: +#line 230 "./vcc.y" +{ + lexPopMode(0); + yyval.vobj = popVObject(); + ; + break;} +case 9: +#line 235 "./vcc.y" +{ + lexPushMode(L_VCARD); + if (!pushVObject(VCCardProp)) YYERROR; + ; + break;} +case 10: +#line 240 "./vcc.y" +{ + lexPopMode(0); + yyval.vobj = popVObject(); + ; + break;} +case 13: +#line 251 "./vcc.y" +{ + lexPushMode(L_VALUES); + ; + break;} +case 14: +#line 255 "./vcc.y" +{ + if (lexWithinMode(L_BASE64) || lexWithinMode(L_QUOTED_PRINTABLE)) + lexPopMode(0); + lexPopMode(0); + ; + break;} +case 16: +#line 264 "./vcc.y" +{ + enterProps(yyvsp[0].str); + ; + break;} +case 18: +#line 269 "./vcc.y" +{ + enterProps(yyvsp[0].str); + ; + break;} +case 22: +#line 282 "./vcc.y" +{ + enterAttr(yyvsp[0].str,0); + ; + break;} +case 23: +#line 286 "./vcc.y" +{ + enterAttr(yyvsp[-2].str,yyvsp[0].str); + + ; + break;} +case 25: +#line 295 "./vcc.y" +{ appendValue(yyvsp[-1].str); ; + break;} +case 27: +#line 297 "./vcc.y" +{ appendValue(yyvsp[0].str); ; + break;} +case 29: +#line 302 "./vcc.y" +{ yyval.str = 0; ; + break;} +case 30: +#line 307 "./vcc.y" +{ if (!pushVObject(VCCalProp)) YYERROR; ; + break;} +case 31: +#line 310 "./vcc.y" +{ yyval.vobj = popVObject(); ; + break;} +case 32: +#line 312 "./vcc.y" +{ if (!pushVObject(VCCalProp)) YYERROR; ; + break;} +case 33: +#line 314 "./vcc.y" +{ yyval.vobj = popVObject(); ; + break;} +case 39: +#line 329 "./vcc.y" +{ + lexPushMode(L_VEVENT); + if (!pushVObject(VCEventProp)) YYERROR; + ; + break;} +case 40: +#line 335 "./vcc.y" +{ + lexPopMode(0); + popVObject(); + ; + break;} +case 41: +#line 340 "./vcc.y" +{ + lexPushMode(L_VEVENT); + if (!pushVObject(VCEventProp)) YYERROR; + ; + break;} +case 42: +#line 345 "./vcc.y" +{ + lexPopMode(0); + popVObject(); + ; + break;} +case 43: +#line 353 "./vcc.y" +{ + lexPushMode(L_VTODO); + if (!pushVObject(VCTodoProp)) YYERROR; + ; + break;} +case 44: +#line 359 "./vcc.y" +{ + lexPopMode(0); + popVObject(); + ; + break;} +case 45: +#line 364 "./vcc.y" +{ + lexPushMode(L_VTODO); + if (!pushVObject(VCTodoProp)) YYERROR; + ; + break;} +case 46: +#line 369 "./vcc.y" +{ + lexPopMode(0); + popVObject(); + ; + break;} +} + /* the action file gets copied in in place of this dollarsign */ +#line 543 "/usr/share/bison.simple" + + yyvsp -= yylen; + yyssp -= yylen; +#ifdef YYLSP_NEEDED + yylsp -= yylen; +#endif + +#if YYDEBUG != 0 + if (yydebug) + { + short *ssp1 = yyss - 1; + fprintf (stderr, "state stack now"); + while (ssp1 != yyssp) + fprintf (stderr, " %d", *++ssp1); + fprintf (stderr, "\n"); + } +#endif + + *++yyvsp = yyval; + +#ifdef YYLSP_NEEDED + yylsp++; + if (yylen == 0) + { + yylsp->first_line = yylloc.first_line; + yylsp->first_column = yylloc.first_column; + yylsp->last_line = (yylsp-1)->last_line; + yylsp->last_column = (yylsp-1)->last_column; + yylsp->text = 0; + } + else + { + yylsp->last_line = (yylsp+yylen-1)->last_line; + yylsp->last_column = (yylsp+yylen-1)->last_column; + } +#endif + + /* Now "shift" the result of the reduction. + Determine what state that goes to, + based on the state we popped back to + and the rule number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTBASE] + *yyssp; + if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTBASE]; + + goto yynewstate; + +yyerrlab: /* here on detecting error */ + + if (! yyerrstatus) + /* If not already recovering from an error, report this error. */ + { + ++yynerrs; + +#ifdef YYERROR_VERBOSE + yyn = yypact[yystate]; + + if (yyn > YYFLAG && yyn < YYLAST) + { + int size = 0; + char *msg; + int x, count; + + count = 0; + /* Start X at -yyn if nec to avoid negative indexes in yycheck. */ + for (x = (yyn < 0 ? -yyn : 0); + x < (sizeof(yytname) / sizeof(char *)); x++) + if (yycheck[x + yyn] == x) + size += strlen(yytname[x]) + 15, count++; + msg = (char *) malloc(size + 15); + if (msg != 0) + { + strcpy(msg, "parse error"); + + if (count < 5) + { + count = 0; + for (x = (yyn < 0 ? -yyn : 0); + x < (sizeof(yytname) / sizeof(char *)); x++) + if (yycheck[x + yyn] == x) + { + strcat(msg, count == 0 ? ", expecting `" : " or `"); + strcat(msg, yytname[x]); + strcat(msg, "'"); + count++; + } + } + yyerror(msg); + free(msg); + } + else + yyerror ("parse error; also virtual memory exceeded"); + } + else +#endif /* YYERROR_VERBOSE */ + yyerror("parse error"); + } + + goto yyerrlab1; +yyerrlab1: /* here on error raised explicitly by an action */ + + if (yyerrstatus == 3) + { + /* if just tried and failed to reuse lookahead token after an error, discard it. */ + + /* return failure if at end of input */ + if (yychar == YYEOF) + YYABORT; + +#if YYDEBUG != 0 + if (yydebug) + fprintf(stderr, "Discarding token %d (%s).\n", yychar, yytname[yychar1]); +#endif + + yychar = YYEMPTY; + } + + /* Else will try to reuse lookahead token + after shifting the error token. */ + + yyerrstatus = 3; /* Each real token shifted decrements this */ + + goto yyerrhandle; + +yyerrdefault: /* current state does not do anything special for the error token. */ + +#if 0 + /* This is wrong; only states that explicitly want error tokens + should shift them. */ + yyn = yydefact[yystate]; /* If its default is to accept any token, ok. Otherwise pop it.*/ + if (yyn) goto yydefault; +#endif + +yyerrpop: /* pop the current state because it cannot handle the error token */ + + if (yyssp == yyss) YYABORT; + yyvsp--; + yystate = *--yyssp; +#ifdef YYLSP_NEEDED + yylsp--; +#endif + +#if YYDEBUG != 0 + if (yydebug) + { + short *ssp1 = yyss - 1; + fprintf (stderr, "Error: state stack now"); + while (ssp1 != yyssp) + fprintf (stderr, " %d", *++ssp1); + fprintf (stderr, "\n"); + } +#endif + +yyerrhandle: + + yyn = yypact[yystate]; + if (yyn == YYFLAG) + goto yyerrdefault; + + yyn += YYTERROR; + if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != YYTERROR) + goto yyerrdefault; + + yyn = yytable[yyn]; + if (yyn < 0) + { + if (yyn == YYFLAG) + goto yyerrpop; + yyn = -yyn; + goto yyreduce; + } + else if (yyn == 0) + goto yyerrpop; + + if (yyn == YYFINAL) + YYACCEPT; + +#if YYDEBUG != 0 + if (yydebug) + fprintf(stderr, "Shifting error token, "); +#endif + + *++yyvsp = yylval; +#ifdef YYLSP_NEEDED + *++yylsp = yylloc; +#endif + + yystate = yyn; + goto yynewstate; + + yyacceptlab: + /* YYACCEPT comes here. */ + if (yyfree_stacks) + { + free (yyss); + free (yyvs); +#ifdef YYLSP_NEEDED + free (yyls); +#endif + } + return 0; + + yyabortlab: + /* YYABORT comes here. */ + if (yyfree_stacks) + { + free (yyss); + free (yyvs); +#ifdef YYLSP_NEEDED + free (yyls); +#endif + } + return 1; +} +#line 375 "./vcc.y" + +/****************************************************************************/ +static int pushVObject(const char *prop) + { + VObject *newObj; + if (ObjStackTop == MAXLEVEL) + return FALSE; + + ObjStack[++ObjStackTop] = curObj; + + if (curObj) { + newObj = addProp(curObj,prop); + curObj = newObj; + } + else + curObj = newVObject(prop); + + return TRUE; + } + + +/****************************************************************************/ +/* This pops the recently built vCard off the stack and returns it. */ +static VObject* popVObject() + { + VObject *oldObj; + if (ObjStackTop < 0) { + yyerror("pop on empty Object Stack\n"); + return 0; + } + oldObj = curObj; + curObj = ObjStack[ObjStackTop--]; + + return oldObj; + } + + +/* static void enterValues(const char *value) */ +/* { */ +/* if (fieldedProp && *fieldedProp) { */ +/* if (value) { */ +/* addPropValue(curProp,*fieldedProp,value); */ +/* } */ + /* else this field is empty, advance to next field */ +/* fieldedProp++; */ +/* } */ +/* else { */ +/* if (value) { */ +/* setVObjectUStringZValue_(curProp,fakeUnicode(value,0)); */ +/* } */ +/* } */ +/* deleteStr(value); */ +/* } */ + +static void appendValue(const char *value) +{ + char *p1, *p2; + wchar_t *p3; + int i; + + if (fieldedProp && *fieldedProp) { + if (value) { + addPropValue(curProp, *fieldedProp, value); + } + /* else this field is empty, advance to next field */ + fieldedProp++; + } else { + if (value) { + if (vObjectUStringZValue(curProp)) { + p1 = fakeCString(vObjectUStringZValue(curProp)); + p2 = malloc(sizeof(char *) * (strlen(p1)+strlen(value)+1)); + strcpy(p2, p1); + deleteStr(p1); + + i = strlen(p2); + p2[i] = ','; + p2[i+1] = '\0'; + p2 = strcat(p2, value); + p3 = (wchar_t *) vObjectUStringZValue(curProp); + free(p3); + setVObjectUStringZValue_(curProp,fakeUnicode(p2,0)); + deleteStr(p2); + } else { + setVObjectUStringZValue_(curProp,fakeUnicode(value,0)); + } + } + } + deleteStr(value); +} + + +static void enterProps(const char *s) + { + curProp = addGroup(curObj,s); + deleteStr(s); + } + +static void enterAttr(const char *s1, const char *s2) + { + const char *p1=0L, *p2=0L; + p1 = lookupProp_(s1); + if (s2) { + VObject *a; + p2 = lookupProp_(s2); + a = addProp(curProp,p1); + setVObjectStringZValue(a,p2); + } + else + addProp(curProp,p1); + if (strcasecmp(p1,VCBase64Prop) == 0 || (s2 && strcasecmp(p2,VCBase64Prop)==0)) + lexPushMode(L_BASE64); + else if (strcasecmp(p1,VCQuotedPrintableProp) == 0 + || (s2 && strcasecmp(p2,VCQuotedPrintableProp)==0)) + lexPushMode(L_QUOTED_PRINTABLE); + deleteStr(s1); deleteStr(s2); + } + + +#define MAX_LEX_LOOKAHEAD_0 32 +#define MAX_LEX_LOOKAHEAD 64 +#define MAX_LEX_MODE_STACK_SIZE 10 +#define LEXMODE() (lexBuf.lexModeStack[lexBuf.lexModeStackTop]) + +struct LexBuf { + /* input */ + FILE *inputFile; + char *inputString; + unsigned long curPos; + unsigned long inputLen; + /* lookahead buffer */ + /* -- lookahead buffer is short instead of char so that EOF + / can be represented correctly. + */ + unsigned long len; + short buf[MAX_LEX_LOOKAHEAD]; + unsigned long getPtr; + /* context stack */ + unsigned long lexModeStackTop; + enum LexMode lexModeStack[MAX_LEX_MODE_STACK_SIZE]; + /* token buffer */ + unsigned long maxToken; + char *strs; + unsigned long strsLen; + } lexBuf; + +static void lexPushMode(enum LexMode mode) + { + if (lexBuf.lexModeStackTop == (MAX_LEX_MODE_STACK_SIZE-1)) + yyerror("lexical context stack overflow"); + else { + lexBuf.lexModeStack[++lexBuf.lexModeStackTop] = mode; + } + } + +static void lexPopMode(int top) + { + /* special case of pop for ease of error recovery -- this + version will never underflow */ + if (top) + lexBuf.lexModeStackTop = 0; + else + if (lexBuf.lexModeStackTop > 0) lexBuf.lexModeStackTop--; + } + +static int lexWithinMode(enum LexMode mode) { + unsigned long i; + for (i=0;i<lexBuf.lexModeStackTop;i++) + if (mode == lexBuf.lexModeStack[i]) return 1; + return 0; + } + +static int lexGetc_() + { + /* get next char from input, no buffering. */ + if (lexBuf.curPos == lexBuf.inputLen) + return EOF; + else if (lexBuf.inputString) + return *(lexBuf.inputString + lexBuf.curPos++); + else { + if (!feof(lexBuf.inputFile)) + return fgetc(lexBuf.inputFile); + else + return EOF; + } + } + +static int lexGeta() + { + ++lexBuf.len; + return (lexBuf.buf[lexBuf.getPtr] = lexGetc_()); + } + +static int lexGeta_(int i) + { + ++lexBuf.len; + return (lexBuf.buf[(lexBuf.getPtr+i)%MAX_LEX_LOOKAHEAD] = lexGetc_()); + } + +static void lexSkipLookahead() { + if (lexBuf.len > 0 && lexBuf.buf[lexBuf.getPtr]!=EOF) { + /* don't skip EOF. */ + lexBuf.getPtr = (lexBuf.getPtr + 1) % MAX_LEX_LOOKAHEAD; + lexBuf.len--; + } + } + +static int lexLookahead() { + int c = (lexBuf.len)? + lexBuf.buf[lexBuf.getPtr]: + lexGeta(); + /* do the \r\n -> \n or \r -> \n translation here */ + if (c == '\r') { + int a = (lexBuf.len>1)? + lexBuf.buf[(lexBuf.getPtr+1)%MAX_LEX_LOOKAHEAD]: + lexGeta_(1); + if (a == '\n') { + lexSkipLookahead(); + } + lexBuf.buf[lexBuf.getPtr] = c = '\n'; + } + else if (c == '\n') { + int a; + if (lexBuf.len > 1) + a = lexBuf.buf[lexBuf.getPtr]; + else + a = lexGeta_(1); + if (a == '\r') { + lexSkipLookahead(); + } + lexBuf.buf[lexBuf.getPtr] = '\n'; + } + return c; + } + +static int lexGetc() { + int c = lexLookahead(); + if (lexBuf.len > 0 && lexBuf.buf[lexBuf.getPtr]!=EOF) { + /* EOF will remain in lookahead buffer */ + lexBuf.getPtr = (lexBuf.getPtr + 1) % MAX_LEX_LOOKAHEAD; + lexBuf.len--; + } + return c; + } + +static void lexSkipLookaheadWord() { + if (lexBuf.strsLen <= lexBuf.len) { + lexBuf.len -= lexBuf.strsLen; + lexBuf.getPtr = (lexBuf.getPtr + lexBuf.strsLen) % MAX_LEX_LOOKAHEAD; + } + } + +static void lexClearToken() + { + lexBuf.strsLen = 0; + } + +static void lexAppendc(int c) + { + /* not sure if I am doing this right to fix purify report -- PGB */ + lexBuf.strs = (char *) realloc(lexBuf.strs, (size_t) lexBuf.strsLen + 1); + lexBuf.strs[lexBuf.strsLen] = c; + /* append up to zero termination */ + if (c == 0) return; + lexBuf.strsLen++; + if (lexBuf.strsLen > lexBuf.maxToken) { + /* double the token string size */ + lexBuf.maxToken <<= 1; + lexBuf.strs = (char*) realloc(lexBuf.strs,(size_t)lexBuf.maxToken); + } + } + +static char* lexStr() { + return dupStr(lexBuf.strs,(size_t)lexBuf.strsLen+1); + } + +static void lexSkipWhite() { + int c = lexLookahead(); + while (c == ' ' || c == '\t') { + lexSkipLookahead(); + c = lexLookahead(); + } + } + +static char* lexGetWord() { + int c; + lexSkipWhite(); + lexClearToken(); + c = lexLookahead(); + /* some "words" have a space in them, like "NEEDS ACTION". + this may be an oversight of the spec, but it is true nevertheless. + while (c != EOF && !strchr("\t\n ;:=",c)) { */ + while (c != EOF && !strchr("\n;:=",c)) { + lexAppendc(c); + lexSkipLookahead(); + c = lexLookahead(); + } + lexAppendc(0); + return lexStr(); + } + +void lexPushLookahead(char *s, int len) { + int putptr; + if (len == 0) len = strlen(s); + putptr = (int)lexBuf.getPtr - len; + /* this function assumes that length of word to push back + / is not greater than MAX_LEX_LOOKAHEAD. + */ + if (putptr < 0) putptr += MAX_LEX_LOOKAHEAD; + lexBuf.getPtr = putptr; + while (*s) { + lexBuf.buf[putptr] = *s++; + putptr = (putptr + 1) % MAX_LEX_LOOKAHEAD; + } + lexBuf.len += len; + } + +static void lexPushLookaheadc(int c) { + int putptr; + /* can't putback EOF, because it never leaves lookahead buffer */ + if (c == EOF) return; + putptr = (int)lexBuf.getPtr - 1; + if (putptr < 0) putptr += MAX_LEX_LOOKAHEAD; + lexBuf.getPtr = putptr; + lexBuf.buf[putptr] = c; + lexBuf.len += 1; + } + +static char* lexLookaheadWord() { + /* this function can lookahead word with max size of MAX_LEX_LOOKAHEAD_0 + / and thing bigger than that will stop the lookahead and return 0; + / leading white spaces are not recoverable. + */ + int c; + int len = 0; + int curgetptr = 0; + lexSkipWhite(); + lexClearToken(); + curgetptr = (int)lexBuf.getPtr; /* remember! */ + while (len < (MAX_LEX_LOOKAHEAD_0)) { + c = lexGetc(); + len++; + if (c == EOF || strchr("\t\n ;:=", c)) { + lexAppendc(0); + /* restore lookahead buf. */ + lexBuf.len += len; + lexBuf.getPtr = curgetptr; + return lexStr(); + } + else + lexAppendc(c); + } + lexBuf.len += len; /* char that has been moved to lookahead buffer */ + lexBuf.getPtr = curgetptr; + return 0; + } + +#ifdef _SUPPORT_LINE_FOLDING +static void handleMoreRFC822LineBreak(int c) { + /* suport RFC 822 line break in cases like + * ADR: foo; + * morefoo; + * more foo; + */ + if (c == ';') { + int a; + lexSkipLookahead(); + /* skip white spaces */ + a = lexLookahead(); + while (a == ' ' || a == '\t') { + lexSkipLookahead(); + a = lexLookahead(); + } + if (a == '\n') { + lexSkipLookahead(); + a = lexLookahead(); + if (a == ' ' || a == '\t') { + /* continuation, throw away all the \n and spaces read so + * far + */ + lexSkipWhite(); + lexPushLookaheadc(';'); + } + else { + lexPushLookaheadc('\n'); + lexPushLookaheadc(';'); + } + } + else { + lexPushLookaheadc(';'); + } + } + } + +static char* lexGet1Value() { + int c; + lexSkipWhite(); + c = lexLookahead(); + lexClearToken(); + while (c != EOF && c != ';') { + if (c == '\n') { + int a; + lexSkipLookahead(); + a = lexLookahead(); + if (a == ' ' || a == '\t') { + lexAppendc(' '); + lexSkipLookahead(); + } + else { + lexPushLookaheadc('\n'); + break; + } + } + else { + lexAppendc(c); + lexSkipLookahead(); + } + c = lexLookahead(); + } + lexAppendc(0); + handleMoreRFC822LineBreak(c); + return c==EOF?0:lexStr(); + } +#endif + +char* lexGetStrUntil(char *termset) { + int c = lexLookahead(); + lexClearToken(); + while (c != EOF && !strchr(termset,c)) { + lexAppendc(c); + lexSkipLookahead(); + c = lexLookahead(); + } + lexAppendc(0); + return c==EOF?0:lexStr(); + } + +static int match_begin_name(int end) { + char *n = lexLookaheadWord(); + int token = ID; + if (n) { + if (!strcasecmp(n,"vcard")) token = end?END_VCARD:BEGIN_VCARD; + else if (!strcasecmp(n,"vcalendar")) token = end?END_VCAL:BEGIN_VCAL; + else if (!strcasecmp(n,"vevent")) token = end?END_VEVENT:BEGIN_VEVENT; + else if (!strcasecmp(n,"vtodo")) token = end?END_VTODO:BEGIN_VTODO; + deleteStr(n); + return token; + } + return 0; + } + + +void initLex(const char *inputstring, unsigned long inputlen, FILE *inputfile) + { + /* initialize lex mode stack */ + lexBuf.lexModeStack[lexBuf.lexModeStackTop=0] = L_NORMAL; + + /* iniatialize lex buffer. */ + lexBuf.inputString = (char*) inputstring; + lexBuf.inputLen = inputlen; + lexBuf.curPos = 0; + lexBuf.inputFile = inputfile; + + lexBuf.len = 0; + lexBuf.getPtr = 0; + + lexBuf.maxToken = MAXTOKEN; + lexBuf.strs = (char*)malloc(MAXTOKEN); + lexBuf.strsLen = 0; + + } + +static void finiLex() { + free(lexBuf.strs); + } + + +/****************************************************************************/ +/* This parses and converts the base64 format for binary encoding into + * a decoded buffer (allocated with new). See RFC 1521. + */ +static char * lexGetDataFromBase64() + { + unsigned long bytesLen = 0, bytesMax = 0; + int quadIx = 0, pad = 0; + unsigned long trip = 0; + unsigned char b; + int c; + unsigned char *bytes = NULL; + unsigned char *oldBytes = NULL; + + DBG_(("db: lexGetDataFromBase64\n")); + while (1) { + c = lexGetc(); + if (c == '\n') { + ++mime_lineNum; + if (lexLookahead() == '\n') { + /* a '\n' character by itself means end of data */ + break; + } + else continue; /* ignore '\n' */ + } + else { + if ((c >= 'A') && (c <= 'Z')) + b = (unsigned char)(c - 'A'); + else if ((c >= 'a') && (c <= 'z')) + b = (unsigned char)(c - 'a') + 26; + else if ((c >= '0') && (c <= '9')) + b = (unsigned char)(c - '0') + 52; + else if (c == '+') + b = 62; + else if (c == '/') + b = 63; + else if (c == '=') { + b = 0; + pad++; + } else if ((c == ' ') || (c == '\t')) { + continue; + } else { /* error condition */ + if (bytes) free(bytes); + else if (oldBytes) free(oldBytes); + /* error recovery: skip until 2 adjacent newlines. */ + DBG_(("db: invalid character 0x%x '%c'\n", c,c)); + if (c != EOF) { + c = lexGetc(); + while (c != EOF) { + if (c == '\n' && lexLookahead() == '\n') { + ++mime_lineNum; + break; + } + c = lexGetc(); + } + } + return NULL; + } + trip = (trip << 6) | b; + if (++quadIx == 4) { + unsigned char outBytes[3]; + int numOut; + int i; + for (i = 0; i < 3; i++) { + outBytes[2-i] = (unsigned char)(trip & 0xFF); + trip >>= 8; + } + numOut = 3 - pad; + if (bytesLen + numOut > bytesMax) { + if (!bytes) { + bytesMax = 1024; + bytes = (unsigned char*)malloc((size_t)bytesMax); + } + else { + bytesMax <<= 2; + oldBytes = bytes; + bytes = (unsigned char*)realloc(bytes,(size_t)bytesMax); + } + if (bytes == 0) { + mime_error("out of memory while processing BASE64 data\n"); + } + } + if (bytes) { + memcpy(bytes + bytesLen, outBytes, numOut); + bytesLen += numOut; + } + trip = 0; + quadIx = 0; + } + } + } /* while */ + DBG_(("db: bytesLen = %d\n", bytesLen)); + /* kludge: all this won't be necessary if we have tree form + representation */ + if (bytes) { + setValueWithSize(curProp,bytes,(unsigned int)bytesLen); + free(bytes); + } + else if (oldBytes) { + setValueWithSize(curProp,oldBytes,(unsigned int)bytesLen); + free(oldBytes); + } + return 0; + } + +static int match_begin_end_name(int end) { + int token; + lexSkipWhite(); + if (lexLookahead() != ':') return ID; + lexSkipLookahead(); + lexSkipWhite(); + token = match_begin_name(end); + if (token == ID) { + lexPushLookaheadc(':'); + DBG_(("db: ID '%s'\n", yylval.str)); + return ID; + } + else if (token != 0) { + lexSkipLookaheadWord(); + deleteStr(yylval.str); + DBG_(("db: begin/end %d\n", token)); + return token; + } + return 0; + } + +static char* lexGetQuotedPrintable() + { + char cur; + + lexClearToken(); + do { + cur = lexGetc(); + switch (cur) { + case '=': { + int c = 0; + int next[2]; + int i; + for (i = 0; i < 2; i++) { + next[i] = lexGetc(); + if (next[i] >= '0' && next[i] <= '9') + c = c * 16 + next[i] - '0'; + else if (next[i] >= 'A' && next[i] <= 'F') + c = c * 16 + next[i] - 'A' + 10; + else + break; + } + if (i == 0) { + /* single '=' follow by LINESEP is continuation sign? */ + if (next[0] == '\n') { + ++mime_lineNum; + } + else { + lexPushLookaheadc('='); + goto EndString; + } + } + else if (i == 1) { + lexPushLookaheadc(next[1]); + lexPushLookaheadc(next[0]); + lexAppendc('='); + } else { + lexAppendc(c); + } + break; + } /* '=' */ + case '\n': { + lexPushLookaheadc('\n'); + goto EndString; + } + case (char)EOF: + break; + default: + lexAppendc(cur); + break; + } /* switch */ + } while (cur != (char)EOF); + +EndString: + lexAppendc(0); + return lexStr(); + } /* LexQuotedPrintable */ + +static int yylex() { + + int lexmode = LEXMODE(); + if (lexmode == L_VALUES) { + int c = lexGetc(); + if (c == ';') { + DBG_(("db: SEMICOLON\n")); + lexPushLookaheadc(c); + handleMoreRFC822LineBreak(c); + lexSkipLookahead(); + return SEMICOLON; + } + else if (strchr("\n",c)) { + ++mime_lineNum; + /* consume all line separator(s) adjacent to each other */ + c = lexLookahead(); + while (strchr("\n",c)) { + lexSkipLookahead(); + c = lexLookahead(); + ++mime_lineNum; + } + DBG_(("db: LINESEP\n")); + return LINESEP; + } + else { + char *p = 0; + lexPushLookaheadc(c); + if (lexWithinMode(L_BASE64)) { + /* get each char and convert to bin on the fly... */ + p = lexGetDataFromBase64(); + yylval.str = p; + return STRING; + } + else if (lexWithinMode(L_QUOTED_PRINTABLE)) { + p = lexGetQuotedPrintable(); + } + else { +#ifdef _SUPPORT_LINE_FOLDING + p = lexGet1Value(); +#else + p = lexGetStrUntil(";\n"); +#endif + } + if (p) { + DBG_(("db: STRING: '%s'\n", p)); + yylval.str = p; + return STRING; + } + else return 0; + } + } + + else { + /* normal mode */ + while (1) { + int c = lexGetc(); + switch(c) { + case ':': { + /* consume all line separator(s) adjacent to each other */ + /* ignoring linesep immediately after colon. */ + c = lexLookahead(); + while (strchr("\n",c)) { + lexSkipLookahead(); + c = lexLookahead(); + ++mime_lineNum; + } + DBG_(("db: COLON\n")); + return COLON; + } + case ';': + DBG_(("db: SEMICOLON\n")); + return SEMICOLON; + case '=': + DBG_(("db: EQ\n")); + return EQ; + /* ignore tabs/newlines in this mode. We can't ignore + * spaces, because values like NEEDS ACTION have a space. */ + case '\t': continue; + case '\n': { + ++mime_lineNum; + continue; + } + case EOF: return 0; + break; + default: { + lexPushLookaheadc(c); + /* pending lutz : why linker error with isalpha(c)? */ + /*if ( isalpha(c) || c == ' ') { */ + if ( ( c >= 'A' && c <= 'Z') || ( c >= 'a' && c <= 'z') || c == ' ') { + + char *t = lexGetWord(); + yylval.str = t; + if (!strcasecmp(t, "begin")) { + return match_begin_end_name(0); + } + else if (!strcasecmp(t,"end")) { + return match_begin_end_name(1); + } + else { + DBG_(("db: ID '%s'\n", t)); + return ID; + } + } + else { + /* unknown token */ + return 0; + } + break; + } + } + } + } + + return 0; + } + + +/***************************************************************************/ +/*** Public Functions ****/ +/***************************************************************************/ + +static VObject* Parse_MIMEHelper() + { + ObjStackTop = -1; + mime_numErrors = 0; + mime_lineNum = 1; + vObjList = 0; + curObj = 0; + + if (yyparse() != 0) + return 0; + + finiLex(); + return vObjList; + } + +/****************************************************************************/ +VObject* Parse_MIME(const char *input, unsigned long len) + { + initLex(input, len, 0); + return Parse_MIMEHelper(); + } + + +VObject* Parse_MIME_FromFile(FILE *file) + { + VObject *result; + long startPos; + + initLex(0,(unsigned long)-1,file); + startPos = ftell(file); + if (!(result = Parse_MIMEHelper())) { + fseek(file,startPos,SEEK_SET); + } + return result; + } + +VObject* Parse_MIME_FromFileName(const char *fname) + { + FILE *fp = fopen(fname,"r"); + if (fp) { + VObject* o = Parse_MIME_FromFile(fp); + fclose(fp); + return o; + } + else { + char msg[255]; + sprintf(msg, "can't open file '%s' for reading\n", fname); + mime_error_(msg); + return 0; + } + } + +/****************************************************************************/ +void YYDebug(const char *s) +{ + Parse_Debug(s); +} + + +static MimeErrorHandler mimeErrorHandler; + +void registerMimeErrorHandler(MimeErrorHandler me) + { + mimeErrorHandler = me; + } + +static void mime_error(char *s) + { + char msg[256]; + if (mimeErrorHandler) { + sprintf(msg,"%s at line %d", s, mime_lineNum); + mimeErrorHandler(msg); + } + } + +static void mime_error_(char *s) + { + if (mimeErrorHandler) { + mimeErrorHandler(s); + } + } + diff --git a/libkcal/versit/vcc.h b/libkcal/versit/vcc.h new file mode 100644 index 0000000..03886d1 --- a/dev/null +++ b/libkcal/versit/vcc.h @@ -0,0 +1,76 @@ +/*************************************************************************** +(C) Copyright 1996 Apple Computer, Inc., AT&T Corp., International +Business Machines Corporation and Siemens Rolm Communications Inc. + +For purposes of this license notice, the term Licensors shall mean, +collectively, Apple Computer, Inc., AT&T Corp., International +Business Machines Corporation and Siemens Rolm Communications Inc. +The term Licensor shall mean any of the Licensors. + +Subject to acceptance of the following conditions, permission is hereby +granted by Licensors without the need for written agreement and without +license or royalty fees, to use, copy, modify and distribute this +software for any purpose. + +The above copyright notice and the following four paragraphs must be +reproduced in all copies of this software and any software including +this software. + +THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS AND NO LICENSOR SHALL HAVE +ANY OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS OR +MODIFICATIONS. + +IN NO EVENT SHALL ANY LICENSOR BE LIABLE TO ANY PARTY FOR DIRECT, +INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT +OF THE USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +EACH LICENSOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO ANY WARRANTY OF NONINFRINGEMENT OR THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. + +The software is provided with RESTRICTED RIGHTS. Use, duplication, or +disclosure by the government are subject to restrictions set forth in +DFARS 252.227-7013 or 48 CFR 52.227-19, as applicable. + +***************************************************************************/ + +#ifndef __VCC_H__ +#define __VCC_H__ 1 + +#include "vobject.h" + + +#if defined(__CPLUSPLUS__) || defined(__cplusplus) +extern "C" { +#endif + +typedef void (*MimeErrorHandler)(char *); + +extern void registerMimeErrorHandler(MimeErrorHandler); + +extern VObject* Parse_MIME(const char *input, unsigned long len); +extern VObject* Parse_MIME_FromFileName(const char* fname); + + +/* NOTE regarding Parse_MIME_FromFile +The function below, Parse_MIME_FromFile, come in two flavors, +neither of which is exported from the DLL. Each version takes +a CFile or FILE* as a parameter, neither of which can be +passed across a DLL interface (at least that is my experience). +If you are linking this code into your build directly then +you may find them a more convenient API that the other flavors +that take a file name. If you use them with the DLL LIB you +will get a link error. +*/ + + +extern VObject* Parse_MIME_FromFile(FILE *file); + +#if defined(__CPLUSPLUS__) || defined(__cplusplus) +} +#endif + +#endif /* __VCC_H__ */ + diff --git a/libkcal/versit/versit.pro b/libkcal/versit/versit.pro new file mode 100644 index 0000000..915c25c --- a/dev/null +++ b/libkcal/versit/versit.pro @@ -0,0 +1,15 @@ +TEMPLATE = lib +CONFIG = qt warn_on release +TARGET = versit +INTERFACES = \ + +HEADERS = \ + port.h \ + vcc.h \ + vobject.h \ + +SOURCES = \ + \ \ + vcc.c \ + vobject.c \ + diff --git a/libkcal/versit/versit.pro.back b/libkcal/versit/versit.pro.back new file mode 100644 index 0000000..915c25c --- a/dev/null +++ b/libkcal/versit/versit.pro.back @@ -0,0 +1,15 @@ +TEMPLATE = lib +CONFIG = qt warn_on release +TARGET = versit +INTERFACES = \ + +HEADERS = \ + port.h \ + vcc.h \ + vobject.h \ + +SOURCES = \ + \ \ + vcc.c \ + vobject.c \ + diff --git a/libkcal/versit/vobject.c b/libkcal/versit/vobject.c new file mode 100644 index 0000000..637efb2 --- a/dev/null +++ b/libkcal/versit/vobject.c @@ -0,0 +1,1433 @@ +/*************************************************************************** +(C) Copyright 1996 Apple Computer, Inc., AT&T Corp., International +Business Machines Corporation and Siemens Rolm Communications Inc. + +For purposes of this license notice, the term Licensors shall mean, +collectively, Apple Computer, Inc., AT&T Corp., International +Business Machines Corporation and Siemens Rolm Communications Inc. +The term Licensor shall mean any of the Licensors. + +Subject to acceptance of the following conditions, permission is hereby +granted by Licensors without the need for written agreement and without +license or royalty fees, to use, copy, modify and distribute this +software for any purpose. + +The above copyright notice and the following four paragraphs must be +reproduced in all copies of this software and any software including +this software. + +THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS AND NO LICENSOR SHALL HAVE +ANY OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS OR +MODIFICATIONS. + +IN NO EVENT SHALL ANY LICENSOR BE LIABLE TO ANY PARTY FOR DIRECT, +INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT +OF THE USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +EACH LICENSOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO ANY WARRANTY OF NONINFRINGEMENT OR THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. + +The software is provided with RESTRICTED RIGHTS. Use, duplication, or +disclosure by the government are subject to restrictions set forth in +DFARS 252.227-7013 or 48 CFR 52.227-19, as applicable. + +***************************************************************************/ + +/* + * src: vobject.c + * doc: vobject and APIs to construct vobject, APIs pretty print + * vobject, and convert a vobject into its textual representation. + */ + +#include <stdlib.h> + +#include "vobject.h" +#include <string.h> +#include <stdio.h> +#ifdef _WIN32_ + + #define strcasecmp _stricmp + +#endif + +#define NAME_OF(o) o->id +#define VALUE_TYPE(o) o->valType +#define STRINGZ_VALUE_OF(o) o->val.strs +#define USTRINGZ_VALUE_OF(o) o->val.ustrs +#define INTEGER_VALUE_OF(o) o->val.i +#define LONG_VALUE_OF(o) o->val.l +#define ANY_VALUE_OF(o) o->val.any +#define VOBJECT_VALUE_OF(o) o->val.vobj + +const char** fieldedProp; + + + +/*---------------------------------------------------------------------- + The following functions involve with memory allocation: + newVObject + deleteVObject + dupStr + deleteStr + newStrItem + deleteStrItem + ----------------------------------------------------------------------*/ + +VObject* newVObject_(const char *id) +{ + VObject *p = (VObject*)malloc(sizeof(VObject)); + p->next = 0; + p->id = id; + p->prop = 0; + VALUE_TYPE(p) = 0; + ANY_VALUE_OF(p) = 0; + return p; +} + +VObject* newVObject(const char *id) +{ + return newVObject_(lookupStr(id)); +} + +void deleteVObject(VObject *p) +{ + if (p->id) + unUseStr(p->id); + if (p) + free(p); + p = NULL; +} + +char* dupStr(const char *s, unsigned int size) +{ + char *t; + if (size == 0) { + size = strlen(s); + } + t = (char*)malloc(size+1); + if (t) { + memcpy(t,s,size); + t[size] = 0; + return t; + } + else { + return (char*)0; + } +} + +void deleteStr(const char *p) +{ + if (p) + free((void*)p); + p = NULL; +} + + +static StrItem* newStrItem(const char *s, StrItem *next) +{ + StrItem *p = (StrItem*)malloc(sizeof(StrItem)); + p->next = next; + p->s = s; + p->refCnt = 1; + return p; +} + +static void deleteStrItem(StrItem *p) +{ + if (p) + free((void*)p); + p = NULL; +} + + +/*---------------------------------------------------------------------- + The following function provide accesses to VObject's value. + ----------------------------------------------------------------------*/ + +const char* vObjectName(VObject *o) +{ + return NAME_OF(o); +} + +void setVObjectName(VObject *o, const char* id) +{ + NAME_OF(o) = id; +} + +const char* vObjectStringZValue(VObject *o) +{ + return STRINGZ_VALUE_OF(o); +} + +void setVObjectStringZValue(VObject *o, const char *s) +{ + STRINGZ_VALUE_OF(o) = dupStr(s,0); + VALUE_TYPE(o) = VCVT_STRINGZ; +} + +void setVObjectStringZValue_(VObject *o, const char *s) +{ + STRINGZ_VALUE_OF(o) = s; + VALUE_TYPE(o) = VCVT_STRINGZ; +} + +const wchar_t* vObjectUStringZValue(VObject *o) +{ + return USTRINGZ_VALUE_OF(o); +} + +void setVObjectUStringZValue(VObject *o, const wchar_t *s) +{ + USTRINGZ_VALUE_OF(o) = (wchar_t*) dupStr((char*)s,(uStrLen(s)+1)*2); + VALUE_TYPE(o) = VCVT_USTRINGZ; +} + +void setVObjectUStringZValue_(VObject *o, const wchar_t *s) +{ + USTRINGZ_VALUE_OF(o) = s; + VALUE_TYPE(o) = VCVT_USTRINGZ; +} + +unsigned int vObjectIntegerValue(VObject *o) +{ + return INTEGER_VALUE_OF(o); +} + +void setVObjectIntegerValue(VObject *o, unsigned int i) +{ + INTEGER_VALUE_OF(o) = i; + VALUE_TYPE(o) = VCVT_UINT; +} + +unsigned long vObjectLongValue(VObject *o) +{ + return LONG_VALUE_OF(o); +} + +void setVObjectLongValue(VObject *o, unsigned long l) +{ + LONG_VALUE_OF(o) = l; + VALUE_TYPE(o) = VCVT_ULONG; +} + +void* vObjectAnyValue(VObject *o) +{ + return ANY_VALUE_OF(o); +} + +void setVObjectAnyValue(VObject *o, void *t) +{ + ANY_VALUE_OF(o) = t; + VALUE_TYPE(o) = VCVT_RAW; +} + +VObject* vObjectVObjectValue(VObject *o) +{ + return VOBJECT_VALUE_OF(o); +} + +void setVObjectVObjectValue(VObject *o, VObject *p) +{ + VOBJECT_VALUE_OF(o) = p; + VALUE_TYPE(o) = VCVT_VOBJECT; +} + +int vObjectValueType(VObject *o) +{ + return VALUE_TYPE(o); +} + + +/*---------------------------------------------------------------------- + The following functions can be used to build VObject. + ----------------------------------------------------------------------*/ + +VObject* addVObjectProp(VObject *o, VObject *p) +{ + /* circular link list pointed to tail */ + /* + o {next,id,prop,val} + V + pn {next,id,prop,val} + V + ... + p1 {next,id,prop,val} + V + pn + --> + o {next,id,prop,val} + V + pn {next,id,prop,val} + V + p {next,id,prop,val} + ... + p1 {next,id,prop,val} + V + pn + */ + + VObject *tail = o->prop; + if (tail) { + p->next = tail->next; + o->prop = tail->next = p; + } + else { + o->prop = p->next = p; + } + return p; +} + +VObject* addProp(VObject *o, const char *id) +{ + return addVObjectProp(o,newVObject(id)); +} + +VObject* addProp_(VObject *o, const char *id) +{ + return addVObjectProp(o,newVObject_(id)); +} + +void addList(VObject **o, VObject *p) +{ + p->next = 0; + if (*o == 0) { + *o = p; + } + else { + VObject *t = *o; + while (t->next) { + t = t->next; + } + t->next = p; + } +} + +VObject* nextVObjectInList(VObject *o) +{ + return o->next; +} + +VObject* setValueWithSize_(VObject *prop, void *val, unsigned int size) +{ + VObject *sizeProp; + setVObjectAnyValue(prop, val); + sizeProp = addProp(prop,VCDataSizeProp); + setVObjectLongValue(sizeProp, size); + return prop; +} + +VObject* setValueWithSize(VObject *prop, void *val, unsigned int size) +{ + void *p = dupStr(val,size); + return setValueWithSize_(prop,p,p?size:0); +} + +void initPropIterator(VObjectIterator *i, VObject *o) +{ + i->start = o->prop; + i->next = 0; +} + +void initVObjectIterator(VObjectIterator *i, VObject *o) +{ + i->start = o->next; + i->next = 0; +} + +int moreIteration(VObjectIterator *i) +{ + return (i->start && (i->next==0 || i->next!=i->start)); +} + +VObject* nextVObject(VObjectIterator *i) +{ + if (i->start && i->next != i->start) { + if (i->next == 0) { + i->next = i->start->next; + return i->next; + } + else { + i->next = i->next->next; + return i->next; + } + } + else return (VObject*)0; +} + +VObject* isAPropertyOf(VObject *o, const char *id) +{ + VObjectIterator i; + initPropIterator(&i,o); + while (moreIteration(&i)) { + VObject *each = nextVObject(&i); + if (!strcasecmp(id,each->id)) + return each; + } + return (VObject*)0; +} + +VObject* addGroup(VObject *o, const char *g) +{ + /* + a.b.c + --> + prop(c) + prop(VCGrouping=b) + prop(VCGrouping=a) + */ + char *dot = strrchr(g,'.'); + if (dot) { + VObject *p, *t; + char *gs, *n = dot+1; + gs = dupStr(g,0); /* so we can write to it. */ + /* used to be + * t = p = addProp_(o,lookupProp_(n)); + */ + t = p = addProp_(o,lookupProp(n)); + dot = strrchr(gs,'.'); + *dot = 0; + do { + dot = strrchr(gs,'.'); + if (dot) { + n = dot+1; + *dot=0; + } + else + n = gs; + /* property(VCGroupingProp=n); + * and the value may have VCGrouping property + */ + t = addProp(t,VCGroupingProp); + setVObjectStringZValue(t,lookupProp_(n)); + } while (n != gs); + deleteStr(gs); + return p; + } + else + return addProp_(o,lookupProp(g)); +} + +VObject* addPropValue(VObject *o, const char *p, const char *v) +{ + VObject *prop; + prop = addProp(o,p); + setVObjectUStringZValue_(prop, fakeUnicode(v,0)); + return prop; +} + +VObject* addPropSizedValue_(VObject *o, const char *p, const char *v, + unsigned int size) +{ + VObject *prop; + prop = addProp(o,p); + setValueWithSize_(prop, (void*)v, size); + return prop; +} + +VObject* addPropSizedValue(VObject *o, const char *p, const char *v, + unsigned int size) +{ + return addPropSizedValue_(o,p,dupStr(v,size),size); +} + + + +/*---------------------------------------------------------------------- + The following pretty print a VObject + ----------------------------------------------------------------------*/ + +static void printVObject_(FILE *fp, VObject *o, int level); + +static void indent(FILE *fp, int level) +{ + int i; + for (i=0;i<level*4;i++) { + fputc(' ', fp); + } +} + +static void printValue(FILE *fp, VObject *o, int level) +{ + switch (VALUE_TYPE(o)) { + case VCVT_USTRINGZ: { + char c; + char *t,*s; + s = t = fakeCString(USTRINGZ_VALUE_OF(o)); + fputc('"',fp); + while (c=*t,c) { + fputc(c,fp); + if (c == '\n') indent(fp,level+2); + t++; + } + fputc('"',fp); + deleteStr(s); + break; + } + case VCVT_STRINGZ: { + char c; + const char *s = STRINGZ_VALUE_OF(o); + fputc('"',fp); + while (c=*s,c) { + fputc(c,fp); + if (c == '\n') indent(fp,level+2); + s++; + } + fputc('"',fp); + break; + } + case VCVT_UINT: + fprintf(fp,"%d", INTEGER_VALUE_OF(o)); break; + case VCVT_ULONG: + fprintf(fp,"%ld", LONG_VALUE_OF(o)); break; + case VCVT_RAW: + fprintf(fp,"[raw data]"); break; + case VCVT_VOBJECT: + fprintf(fp,"[vobject]\n"); + printVObject_(fp,VOBJECT_VALUE_OF(o),level+1); + break; + case 0: + fprintf(fp,"[none]"); break; + default: + fprintf(fp,"[unknown]"); break; + } +} + +static void printNameValue(FILE *fp,VObject *o, int level) +{ + indent(fp,level); + if (NAME_OF(o)) { + fprintf(fp,"%s", NAME_OF(o)); + } + if (VALUE_TYPE(o)) { + fputc('=',fp); + printValue(fp,o, level); + } + fprintf(fp,"\n"); +} + +static void printVObject_(FILE *fp, VObject *o, int level) + { + VObjectIterator t; + if (o == 0) { + fprintf(fp,"[NULL]\n"); + return; + } + printNameValue(fp,o,level); + initPropIterator(&t,o); + while (moreIteration(&t)) { + VObject *eachProp = nextVObject(&t); + printVObject_(fp,eachProp,level+1); + } + } + +void printVObject(FILE *fp,VObject *o) +{ + printVObject_(fp,o,0); +} + +void printVObjectToFile(char *fname,VObject *o) +{ + FILE *fp = fopen(fname,"w"); + if (fp) { + printVObject(fp,o); + fclose(fp); + } +} + +void printVObjectsToFile(char *fname,VObject *list) +{ + FILE *fp = fopen(fname,"w"); + if (fp) { + while (list) { + printVObject(fp,list); + list = nextVObjectInList(list); + } + fclose(fp); + } +} + +void cleanVObject(VObject *o) +{ + if (o == 0) return; + if (o->prop) { + /* destroy time: cannot use the iterator here. + Have to break the cycle in the circular link + list and turns it into regular NULL-terminated + list -- since at some point of destruction, + the reference entry for the iterator to work + will not longer be valid. + */ + VObject *p; + p = o->prop->next; + o->prop->next = 0; + do { + VObject *t = p->next; + cleanVObject(p); + p = t; + } while (p); + } + switch (VALUE_TYPE(o)) { + case VCVT_USTRINGZ: + case VCVT_STRINGZ: + case VCVT_RAW: + /* assume they are all allocated by malloc. */ + free((char*)STRINGZ_VALUE_OF(o)); + break; + case VCVT_VOBJECT: + cleanVObject(VOBJECT_VALUE_OF(o)); + break; + } + deleteVObject(o); +} + +void cleanVObjects(VObject *list) +{ + while (list) { + VObject *t = list; + list = nextVObjectInList(list); + cleanVObject(t); + } +} + +/*---------------------------------------------------------------------- + The following is a String Table Facilities. + ----------------------------------------------------------------------*/ + +#define STRTBLSIZE 255 + +static StrItem *strTbl[STRTBLSIZE]; + +static unsigned int hashStr(const char *s) +{ + unsigned int h = 0; + int i; + for (i=0;s[i];i++) { + h += s[i]*i; + } + return h % STRTBLSIZE; +} + +const char* lookupStr(const char *s) +{ + char *newS; + + StrItem *t; + unsigned int h = hashStr(s); + if ((t = strTbl[h]) != 0) { + do { + if (strcasecmp(t->s,s) == 0) { + t->refCnt++; + return t->s; + } + t = t->next; + } while (t); + } + newS = dupStr(s,0); + strTbl[h] = newStrItem(newS,strTbl[h]); + return newS; +} + +void unUseStr(const char *s) +{ + StrItem *cur, *prev; + + unsigned int h = hashStr(s); + cur = strTbl[h]; + prev = cur; + while (cur != 0) { + if (strcasecmp(cur->s,s) == 0) { + cur->refCnt--; + /* if that was the last reference to this string, kill it. */ + if (cur->refCnt == 0) { + if (cur == strTbl[h]) { + strTbl[h] = cur->next; + deleteStr(prev->s); + deleteStrItem(prev); + } else { + prev->next = cur->next; + deleteStr(cur->s); + deleteStrItem(cur); + } + return; + } + } + prev = cur; + cur = cur->next; + } +} + +void cleanStrTbl() +{ + int i; + for (i=0; i<STRTBLSIZE;i++) { + StrItem *t = strTbl[i]; + while (t) { + StrItem *p; + deleteStr(t->s); + p = t; + t = t->next; + deleteStrItem(p); + } + strTbl[i] = 0; + } +} + + +struct PreDefProp { + const char *name; + const char *alias; + const char** fields; + unsigned int flags; + }; + +/* flags in PreDefProp */ +#define PD_BEGIN 0x1 +#define PD_INTERNAL 0x2 + +static const char *adrFields[] = { + VCPostalBoxProp, + VCExtAddressProp, + VCStreetAddressProp, + VCCityProp, + VCRegionProp, + VCPostalCodeProp, + VCCountryNameProp, + 0 +}; + +static const char *nameFields[] = { + VCFamilyNameProp, + VCGivenNameProp, + VCAdditionalNamesProp, + VCNamePrefixesProp, + VCNameSuffixesProp, + NULL + }; + +static const char *orgFields[] = { + VCOrgNameProp, + VCOrgUnitProp, + VCOrgUnit2Prop, + VCOrgUnit3Prop, + VCOrgUnit4Prop, + NULL + }; + +static const char *AAlarmFields[] = { + VCRunTimeProp, + VCSnoozeTimeProp, + VCRepeatCountProp, + VCAudioContentProp, + 0 + }; + +/* ExDate -- has unamed fields */ +/* RDate -- has unamed fields */ + +static const char *DAlarmFields[] = { + VCRunTimeProp, + VCSnoozeTimeProp, + VCRepeatCountProp, + VCDisplayStringProp, + 0 + }; + +static const char *MAlarmFields[] = { + VCRunTimeProp, + VCSnoozeTimeProp, + VCRepeatCountProp, + VCEmailAddressProp, + VCNoteProp, + 0 + }; + +static const char *PAlarmFields[] = { + VCRunTimeProp, + VCSnoozeTimeProp, + VCRepeatCountProp, + VCProcedureNameProp, + 0 + }; + +static struct PreDefProp propNames[] = { + { VC7bitProp, 0, 0, 0 }, + { VC8bitProp, 0, 0, 0 }, + { VCAAlarmProp, 0, AAlarmFields, 0 }, + { VCAdditionalNamesProp, 0, 0, 0 }, + { VCAdrProp, 0, adrFields, 0 }, + { VCAgentProp, 0, 0, 0 }, + { VCAIFFProp, 0, 0, 0 }, + { VCAOLProp, 0, 0, 0 }, + { VCAppleLinkProp, 0, 0, 0 }, + { VCAttachProp, 0, 0, 0 }, + { VCAttendeeProp, 0, 0, 0 }, + { VCATTMailProp, 0, 0, 0 }, + { VCAudioContentProp, 0, 0, 0 }, + { VCAVIProp, 0, 0, 0 }, + { VCBase64Prop, 0, 0, 0 }, + { VCBBSProp, 0, 0, 0 }, + { VCBirthDateProp, 0, 0, 0 }, + { VCBMPProp, 0, 0, 0 }, + { VCBodyProp, 0, 0, 0 }, + { VCBusinessRoleProp, 0, 0, 0 }, + { VCCalProp, 0, 0, PD_BEGIN }, + { VCCaptionProp, 0, 0, 0 }, + { VCCardProp, 0, 0, PD_BEGIN }, + { VCCarProp, 0, 0, 0 }, + { VCCategoriesProp, 0, 0, 0 }, + { VCCellularProp, 0, 0, 0 }, + { VCCGMProp, 0, 0, 0 }, + { VCCharSetProp, 0, 0, 0 }, + { VCCIDProp, VCContentIDProp, 0, 0 }, + { VCCISProp, 0, 0, 0 }, + { VCCityProp, 0, 0, 0 }, + { VCClassProp, 0, 0, 0 }, + { VCCommentProp, 0, 0, 0 }, + { VCCompletedProp, 0, 0, 0 }, + { VCContentIDProp, 0, 0, 0 }, + { VCCountryNameProp, 0, 0, 0 }, + { VCDAlarmProp, 0, DAlarmFields, 0 }, + { VCDataSizeProp, 0, 0, PD_INTERNAL }, + { VCDayLightProp, 0, 0, 0 }, + { VCDCreatedProp, 0, 0, 0 }, + { VCDeliveryLabelProp, 0, 0, 0 }, + { VCDescriptionProp, 0, 0, 0 }, + { VCDIBProp, 0, 0, 0 }, + { VCDisplayStringProp, 0, 0, 0 }, + { VCDomesticProp, 0, 0, 0 }, + { VCDTendProp, 0, 0, 0 }, + { VCDTstartProp, 0, 0, 0 }, + { VCDueProp, 0, 0, 0 }, + { VCEmailAddressProp, 0, 0, 0 }, + { VCEncodingProp, 0, 0, 0 }, + { VCEndProp, 0, 0, 0 }, + { VCEventProp, 0, 0, PD_BEGIN }, + { VCEWorldProp, 0, 0, 0 }, + { VCExNumProp, 0, 0, 0 }, + { VCExDateProp, 0, 0, 0 }, + { VCExpectProp, 0, 0, 0 }, + { VCExtAddressProp, 0, 0, 0 }, + { VCFamilyNameProp, 0, 0, 0 }, + { VCFaxProp, 0, 0, 0 }, + { VCFullNameProp, 0, 0, 0 }, + { VCGeoLocationProp, 0, 0, 0 }, + { VCGeoProp, 0, 0, 0 }, + { VCGIFProp, 0, 0, 0 }, + { VCGivenNameProp, 0, 0, 0 }, + { VCGroupingProp, 0, 0, 0 }, + { VCHomeProp, 0, 0, 0 }, + { VCIBMMailProp, 0, 0, 0 }, + { VCInlineProp, 0, 0, 0 }, + { VCInternationalProp, 0, 0, 0 }, + { VCInternetProp, 0, 0, 0 }, + { VCISDNProp, 0, 0, 0 }, + { VCJPEGProp, 0, 0, 0 }, + { VCLanguageProp, 0, 0, 0 }, + { VCLastModifiedProp, 0, 0, 0 }, + { VCLastRevisedProp, 0, 0, 0 }, + { VCLocationProp, 0, 0, 0 }, + { VCLogoProp, 0, 0, 0 }, + { VCMailerProp, 0, 0, 0 }, + { VCMAlarmProp, 0, MAlarmFields, 0 }, + { VCMCIMailProp, 0, 0, 0 }, + { VCMessageProp, 0, 0, 0 }, + { VCMETProp, 0, 0, 0 }, + { VCModemProp, 0, 0, 0 }, + { VCMPEG2Prop, 0, 0, 0 }, + { VCMPEGProp, 0, 0, 0 }, + { VCMSNProp, 0, 0, 0 }, + { VCNamePrefixesProp, 0, 0, 0 }, + { VCNameProp, 0, nameFields, 0 }, + { VCNameSuffixesProp, 0, 0, 0 }, + { VCNoteProp, 0, 0, 0 }, + { VCOrgNameProp, 0, 0, 0 }, + { VCOrgProp, 0, orgFields, 0 }, + { VCOrgUnit2Prop, 0, 0, 0 }, + { VCOrgUnit3Prop, 0, 0, 0 }, + { VCOrgUnit4Prop, 0, 0, 0 }, + { VCOrgUnitProp, 0, 0, 0 }, + { VCPagerProp, 0, 0, 0 }, + { VCPAlarmProp, 0, PAlarmFields, 0 }, + { VCParcelProp, 0, 0, 0 }, + { VCPartProp, 0, 0, 0 }, + { VCPCMProp, 0, 0, 0 }, + { VCPDFProp, 0, 0, 0 }, + { VCPGPProp, 0, 0, 0 }, + { VCPhotoProp, 0, 0, 0 }, + { VCPICTProp, 0, 0, 0 }, + { VCPMBProp, 0, 0, 0 }, + { VCPostalBoxProp, 0, 0, 0 }, + { VCPostalCodeProp, 0, 0, 0 }, + { VCPostalProp, 0, 0, 0 }, + { VCPowerShareProp, 0, 0, 0 }, + { VCPreferredProp, 0, 0, 0 }, + { VCPriorityProp, 0, 0, 0 }, + { VCProcedureNameProp, 0, 0, 0 }, + { VCProdIdProp, 0, 0, 0 }, + { VCProdigyProp, 0, 0, 0 }, + { VCPronunciationProp, 0, 0, 0 }, + { VCPSProp, 0, 0, 0 }, + { VCPublicKeyProp, 0, 0, 0 }, + { VCQPProp, VCQuotedPrintableProp, 0, 0 }, + { VCQuickTimeProp, 0, 0, 0 }, + { VCQuotedPrintableProp, 0, 0, 0 }, + { VCRDateProp, 0, 0, 0 }, + { VCRegionProp, 0, 0, 0 }, + { VCRelatedToProp, 0, 0, 0 }, + { VCRepeatCountProp, 0, 0, 0 }, + { VCResourcesProp, 0, 0, 0 }, + { VCRNumProp, 0, 0, 0 }, + { VCRoleProp, 0, 0, 0 }, + { VCRRuleProp, 0, 0, 0 }, + { VCRSVPProp, 0, 0, 0 }, + { VCRunTimeProp, 0, 0, 0 }, + { VCSequenceProp, 0, 0, 0 }, + { VCSnoozeTimeProp, 0, 0, 0 }, + { VCStartProp, 0, 0, 0 }, + { VCStatusProp, 0, 0, 0 }, + { VCStreetAddressProp, 0, 0, 0 }, + { VCSubTypeProp, 0, 0, 0 }, + { VCSummaryProp, 0, 0, 0 }, + { VCTelephoneProp, 0, 0, 0 }, + { VCTIFFProp, 0, 0, 0 }, + { VCTimeZoneProp, 0, 0, 0 }, + { VCTitleProp, 0, 0, 0 }, + { VCTLXProp, 0, 0, 0 }, + { VCTodoProp, 0, 0, PD_BEGIN }, + { VCTranspProp, 0, 0, 0 }, + { VCUniqueStringProp, 0, 0, 0 }, + { VCURLProp, 0, 0, 0 }, + { VCURLValueProp, 0, 0, 0 }, + { VCValueProp, 0, 0, 0 }, + { VCVersionProp, 0, 0, 0 }, + { VCVideoProp, 0, 0, 0 }, + { VCVoiceProp, 0, 0, 0 }, + { VCWAVEProp, 0, 0, 0 }, + { VCWMFProp, 0, 0, 0 }, + { VCWorkProp, 0, 0, 0 }, + { VCX400Prop, 0, 0, 0 }, + { VCX509Prop, 0, 0, 0 }, + { VCXRuleProp, 0, 0, 0 }, + { 0,0,0,0 } + }; + + +static struct PreDefProp* lookupPropInfo(const char* str) +{ + /* brute force for now, could use a hash table here. */ + int i; + + for (i = 0; propNames[i].name; i++) + if (strcasecmp(str, propNames[i].name) == 0) { + return &propNames[i]; + } + + return 0; +} + + +const char* lookupProp_(const char* str) +{ + int i; + + for (i = 0; propNames[i].name; i++) + if (strcasecmp(str, propNames[i].name) == 0) { + const char* s; + s = propNames[i].alias?propNames[i].alias:propNames[i].name; + return lookupStr(s); + } + return lookupStr(str); +} + + +const char* lookupProp(const char* str) +{ + int i; + + for (i = 0; propNames[i].name; i++) + if (strcasecmp(str, propNames[i].name) == 0) { + const char *s; + fieldedProp = propNames[i].fields; + s = propNames[i].alias?propNames[i].alias:propNames[i].name; + return lookupStr(s); + } + fieldedProp = 0; + return lookupStr(str); +} + + +/*---------------------------------------------------------------------- + APIs to Output text form. + ----------------------------------------------------------------------*/ +#define OFILE_REALLOC_SIZE 256 +typedef struct OFile { + FILE *fp; + char *s; + int len; + int limit; + int alloc:1; + int fail:1; + } OFile; + + +/* vCalendar files need crlf linebreaks. The disabled functions didn't provide + that. */ +#if 0 + +static void appendsOFile(OFile *fp, const char *s) +{ + int slen; + if (fp->fail) return; + slen = strlen(s); + if (fp->fp) { + fwrite(s,1,slen,fp->fp); + } + else { +stuff: + if (fp->len + slen < fp->limit) { + memcpy(fp->s+fp->len,s,slen); + fp->len += slen; + return; + } + else if (fp->alloc) { + fp->limit = fp->limit + OFILE_REALLOC_SIZE; + if (OFILE_REALLOC_SIZE <= slen) fp->limit += slen; + if (fp->s) + fp->s = realloc(fp->s,fp->limit); + else + fp->s = malloc(fp->limit); + if (fp->s) goto stuff; + } + if (fp->alloc) + free(fp->s); + fp->s = 0; + fp->fail = 1; + } +} + +static void appendcOFile(OFile *fp, char c) +{ + if (fp->fail) return; + if (fp->fp) { + fputc(c,fp->fp); + } + else { +stuff: + if (fp->len+1 < fp->limit) { + fp->s[fp->len] = c; + fp->len++; + return; + } + else if (fp->alloc) { + fp->limit = fp->limit + OFILE_REALLOC_SIZE; + fp->s = realloc(fp->s,fp->limit); + if (fp->s) goto stuff; + } + if (fp->alloc) + free(fp->s); + fp->s = 0; + fp->fail = 1; + } +} + +#else + +static void appendcOFile_(OFile *fp, char c) +{ + if (fp->fail) return; + if (fp->fp) { + fputc(c,fp->fp); + } + else { +stuff: + if (fp->len+1 < fp->limit) { + fp->s[fp->len] = c; + fp->len++; + return; + } + else if (fp->alloc) { + fp->limit = fp->limit + OFILE_REALLOC_SIZE; + fp->s = realloc(fp->s,fp->limit); + if (fp->s) goto stuff; + } + if (fp->alloc) + free(fp->s); + fp->s = 0; + fp->fail = 1; + } +} + +static void appendcOFile(OFile *fp, char c) +{ + if (c == '\n') { + /* write out as <CR><LF> */ + appendcOFile_(fp,0xd); + appendcOFile_(fp,0xa); + } + else + appendcOFile_(fp,c); +} + +static void appendsOFile(OFile *fp, const char *s) +{ + int i, slen; + slen = strlen(s); + for (i=0; i<slen; i++) { + appendcOFile(fp,s[i]); + } +} + +#endif + +static void initOFile(OFile *fp, FILE *ofp) +{ + fp->fp = ofp; + fp->s = 0; + fp->len = 0; + fp->limit = 0; + fp->alloc = 0; + fp->fail = 0; +} + +static void initMemOFile(OFile *fp, char *s, int len) +{ + fp->fp = 0; + fp->s = s; + fp->len = 0; + fp->limit = s?len:0; + fp->alloc = s?0:1; + fp->fail = 0; +} + + +static int writeBase64(OFile *fp, unsigned char *s, long len) +{ + long cur = 0; + int i, numQuads = 0; + unsigned long trip; + unsigned char b; + char quad[5]; +#define MAXQUADS 16 + + quad[4] = 0; + + while (cur < len) { + /* collect the triplet of bytes into 'trip' */ + trip = 0; + for (i = 0; i < 3; i++) { + b = (cur < len) ? *(s + cur) : 0; + cur++; + trip = trip << 8 | b; + } + /* fill in 'quad' with the appropriate four characters */ + for (i = 3; i >= 0; i--) { + b = (unsigned char)(trip & 0x3F); + trip = trip >> 6; + if ((3 - i) < (cur - len)) + quad[i] = '='; /* pad char */ + else if (b < 26) quad[i] = (char)b + 'A'; + else if (b < 52) quad[i] = (char)(b - 26) + 'a'; + else if (b < 62) quad[i] = (char)(b - 52) + '0'; + else if (b == 62) quad[i] = '+'; + else quad[i] = '/'; + } + /* now output 'quad' with appropriate whitespace and line ending */ + appendsOFile(fp, (numQuads == 0 ? " " : "")); + appendsOFile(fp, quad); + appendsOFile(fp, ((cur >= len)?"\n" :(numQuads==MAXQUADS-1?"\n" : ""))); + numQuads = (numQuads + 1) % MAXQUADS; + } + appendcOFile(fp,'\n'); + + return 1; +} + +/* this function really sucks. Too basic. */ +static void writeQPString(OFile *fp, const char *s, int qp) +{ + const char *p = s; + while (*p) { + if (*p == '\n') { + if (p[1]) appendsOFile(fp,"=0A="); + } + if (*p == '=' && qp) + appendsOFile(fp,"=3D"); + else + appendcOFile(fp,*p); + p++; + } +} + +static void writeVObject_(OFile *fp, VObject *o); + +static void writeValue(OFile *fp, VObject *o, unsigned long size) +{ + if (o == 0) return; + switch (VALUE_TYPE(o)) { + case VCVT_USTRINGZ: { + char *s = fakeCString(USTRINGZ_VALUE_OF(o)); + if (isAPropertyOf(o, VCQuotedPrintableProp)) + writeQPString(fp, s, 1); + else + writeQPString(fp, s, 0); + deleteStr(s); + break; + } + case VCVT_STRINGZ: { + if (isAPropertyOf(o, VCQuotedPrintableProp)) + writeQPString(fp, STRINGZ_VALUE_OF(o), 1); + else + writeQPString(fp, STRINGZ_VALUE_OF(o), 0); + break; + } + case VCVT_UINT: { + char buf[16]; + sprintf(buf,"%u", INTEGER_VALUE_OF(o)); + appendsOFile(fp,buf); + break; + } + case VCVT_ULONG: { + char buf[16]; + sprintf(buf,"%lu", LONG_VALUE_OF(o)); + appendsOFile(fp,buf); + break; + } + case VCVT_RAW: { + appendcOFile(fp,'\n'); + writeBase64(fp,(unsigned char*)(ANY_VALUE_OF(o)),size); + break; + } + case VCVT_VOBJECT: + appendcOFile(fp,'\n'); + writeVObject_(fp,VOBJECT_VALUE_OF(o)); + break; + } +} + +static void writeAttrValue(OFile *fp, VObject *o) +{ + if (NAME_OF(o)) { + struct PreDefProp *pi; + pi = lookupPropInfo(NAME_OF(o)); + if (pi && ((pi->flags & PD_INTERNAL) != 0)) return; + appendcOFile(fp,';'); + appendsOFile(fp,NAME_OF(o)); + } + else + appendcOFile(fp,';'); + if (VALUE_TYPE(o)) { + appendcOFile(fp,'='); + writeValue(fp,o,0); + } +} + +static void writeGroup(OFile *fp, VObject *o) +{ + char buf1[256]; + char buf2[256]; + strcpy(buf1,NAME_OF(o)); + while ((o=isAPropertyOf(o,VCGroupingProp)) != 0) { + strncpy(buf2,STRINGZ_VALUE_OF(o),sizeof(buf2)); + buf2[sizeof(buf2)] = '\0'; + strncat(buf2,".",sizeof(buf2)-strlen(buf2)-1); + strncat(buf2,buf1,sizeof(buf2)-strlen(buf2)-1); + strcpy(buf1,buf2); + } + appendsOFile(fp,buf1); +} + +static int inList(const char **list, const char *s) +{ + if (list == 0) return 0; + while (*list) { + if (strcasecmp(*list,s) == 0) return 1; + list++; + } + return 0; +} + +static void writeProp(OFile *fp, VObject *o) +{ + if (NAME_OF(o)) { + struct PreDefProp *pi; + VObjectIterator t; + const char **fields_ = 0; + pi = lookupPropInfo(NAME_OF(o)); + if (pi && ((pi->flags & PD_BEGIN) != 0)) { + writeVObject_(fp,o); + return; + } + if (isAPropertyOf(o,VCGroupingProp)) + writeGroup(fp,o); + else + appendsOFile(fp,NAME_OF(o)); + if (pi) fields_ = pi->fields; + initPropIterator(&t,o); + while (moreIteration(&t)) { + const char *s; + VObject *eachProp = nextVObject(&t); + s = NAME_OF(eachProp); + if (strcasecmp(VCGroupingProp,s) && !inList(fields_,s)) + writeAttrValue(fp,eachProp); + } + if (fields_) { + int i = 0, n = 0; + const char** fields = fields_; + /* output prop as fields */ + appendcOFile(fp,':'); + while (*fields) { + VObject *tl = isAPropertyOf(o,*fields); + i++; + if (tl) n = i; + fields++; + } + fields = fields_; + for (i=0;i<n;i++) { + writeValue(fp,isAPropertyOf(o,*fields),0); + fields++; + if (i<(n-1)) appendcOFile(fp,';'); + } + } + } + + if (VALUE_TYPE(o)) { + unsigned long size = 0; + VObject *p = isAPropertyOf(o,VCDataSizeProp); + if (p) size = LONG_VALUE_OF(p); + appendcOFile(fp,':'); + writeValue(fp,o,size); + } + + appendcOFile(fp,'\n'); +} + +static void writeVObject_(OFile *fp, VObject *o) +{ + if (NAME_OF(o)) { + struct PreDefProp *pi; + pi = lookupPropInfo(NAME_OF(o)); + + if (pi && ((pi->flags & PD_BEGIN) != 0)) { + VObjectIterator t; + const char *begin = NAME_OF(o); + appendsOFile(fp,"BEGIN:"); + appendsOFile(fp,begin); + appendcOFile(fp,'\n'); + initPropIterator(&t,o); + while (moreIteration(&t)) { + VObject *eachProp = nextVObject(&t); + writeProp(fp, eachProp); + } + appendsOFile(fp,"END:"); + appendsOFile(fp,begin); + appendsOFile(fp,"\n\n"); + } + } +} + +void writeVObject(FILE *fp, VObject *o) +{ + OFile ofp; + initOFile(&ofp,fp); + writeVObject_(&ofp,o); +} + +void writeVObjectToFile(char *fname, VObject *o) +{ + FILE *fp = fopen(fname,"w"); + if (fp) { + writeVObject(fp,o); + fclose(fp); + } +} + +void writeVObjectsToFile(char *fname, VObject *list) +{ + FILE *fp = fopen(fname,"w"); + if (fp) { + while (list) { + writeVObject(fp,list); + list = nextVObjectInList(list); + } + fclose(fp); + } +} + +char* writeMemVObject(char *s, int *len, VObject *o) +{ + OFile ofp; + initMemOFile(&ofp,s,len?*len:0); + writeVObject_(&ofp,o); + if (len) *len = ofp.len; + appendcOFile(&ofp,0); + return ofp.s; +} + +char* writeMemVObjects(char *s, int *len, VObject *list) +{ + OFile ofp; + initMemOFile(&ofp,s,len?*len:0); + while (list) { + writeVObject_(&ofp,list); + list = nextVObjectInList(list); + } + if (len) *len = ofp.len; + appendcOFile(&ofp,0); + return ofp.s; +} + +/*---------------------------------------------------------------------- + APIs to do fake Unicode stuff. + ----------------------------------------------------------------------*/ +wchar_t* fakeUnicode(const char *ps, int *bytes) +{ + wchar_t *r, *pw; + int len = strlen(ps)+1; + + pw = r = (wchar_t*)malloc(sizeof(wchar_t)*len); + if (bytes) + *bytes = len * sizeof(wchar_t); + + while (*ps) { + if (*ps == '\n') + *pw = (wchar_t)0x2028; + else if (*ps == '\r') + *pw = (wchar_t)0x2029; + else + *pw = (wchar_t)(unsigned char)*ps; + ps++; pw++; + } + *pw = (wchar_t)0; + + return r; +} + +int uStrLen(const wchar_t *u) +{ + int i = 0; + while (*u != (wchar_t)0) { u++; i++; } + return i; +} + +char* fakeCString(const wchar_t *u) +{ + char *s, *t; + int len = uStrLen(u) + 1; + t = s = (char*)malloc(len+1); + while (*u) { + if (*u == (wchar_t)0x2028) + *t = '\n'; + else if (*u == (wchar_t)0x2029) + *t = '\r'; + else + *t = (char)*u; + u++; t++; + } + *t = 0; + return s; +} + +/* end of source file vobject.c */ diff --git a/libkcal/versit/vobject.h b/libkcal/versit/vobject.h new file mode 100644 index 0000000..0ec8b31 --- a/dev/null +++ b/libkcal/versit/vobject.h @@ -0,0 +1,384 @@ +/*************************************************************************** +(C) Copyright 1996 Apple Computer, Inc., AT&T Corp., International +Business Machines Corporation and Siemens Rolm Communications Inc. + +For purposes of this license notice, the term Licensors shall mean, +collectively, Apple Computer, Inc., AT&T Corp., International +Business Machines Corporation and Siemens Rolm Communications Inc. +The term Licensor shall mean any of the Licensors. + +Subject to acceptance of the following conditions, permission is hereby +granted by Licensors without the need for written agreement and without +license or royalty fees, to use, copy, modify and distribute this +software for any purpose. + +The above copyright notice and the following four paragraphs must be +reproduced in all copies of this software and any software including +this software. + +THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS AND NO LICENSOR SHALL HAVE +ANY OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS OR +MODIFICATIONS. + +IN NO EVENT SHALL ANY LICENSOR BE LIABLE TO ANY PARTY FOR DIRECT, +INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT +OF THE USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +EACH LICENSOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO ANY WARRANTY OF NONINFRINGEMENT OR THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. + +The software is provided with RESTRICTED RIGHTS. Use, duplication, or +disclosure by the government are subject to restrictions set forth in +DFARS 252.227-7013 or 48 CFR 52.227-19, as applicable. + +***************************************************************************/ + +/* + +The vCard/vCalendar C interface is implemented in the set +of files as follows: + +vcc.y, yacc source, and vcc.c, the yacc output you will use +implements the core parser + +vobject.c implements an API that insulates the caller from +the parser and changes in the vCard/vCalendar BNF + +port.h defines compilation environment dependent stuff + +vcc.h and vobject.h are header files for their .c counterparts + +vcaltmp.h and vcaltmp.c implement vCalendar "macro" functions +which you may find useful. + +test.c is a standalone test driver that exercises some of +the features of the APIs provided. Invoke test.exe on a +VCARD/VCALENDAR input text file and you will see the pretty +print output of the internal representation (this pretty print +output should give you a good idea of how the internal +representation looks like -- there is one such output in the +following too). Also, a file with the .out suffix is generated +to show that the internal representation can be written back +in the original text format. + +For more information on this API see the readme.txt file +which accompanied this distribution. + + Also visit: + + http://www.versit.com + http://www.ralden.com + +*/ + + +#ifndef __VOBJECT_H__ +#define __VOBJECT_H__ 1 + + +#include "port.h" +#include <stdlib.h> +#include <stdio.h> + +#if defined(__CPLUSPLUS__) || defined(__cplusplus) +extern "C" { +#endif + + +#define VC7bitProp "7BIT" +#define VC8bitProp "8BIT" +#define VCAAlarmProp "AALARM" +#define VCAdditionalNamesProp "ADDN" +#define VCAdrProp "ADR" +#define VCAgentProp "AGENT" +#define VCAIFFProp "AIFF" +#define VCAOLProp "AOL" +#define VCAppleLinkProp "APPLELINK" +#define VCAttachProp "ATTACH" +#define VCAttendeeProp "ATTENDEE" +#define VCATTMailProp "ATTMAIL" +#define VCAudioContentProp "AUDIOCONTENT" +#define VCAVIProp "AVI" +#define VCBase64Prop "BASE64" +#define VCBBSProp "BBS" +#define VCBirthDateProp "BDAY" +#define VCBMPProp "BMP" +#define VCBodyProp "BODY" +#define VCBusinessRoleProp "ROLE" +#define VCCalProp "VCALENDAR" +#define VCCaptionProp "CAP" +#define VCCardProp "VCARD" +#define VCCarProp "CAR" +#define VCCategoriesProp "CATEGORIES" +#define VCCellularProp "CELL" +#define VCCGMProp "CGM" +#define VCCharSetProp "CS" +#define VCCIDProp "CID" +#define VCCISProp "CIS" +#define VCCityProp "L" +#define VCClassProp "CLASS" +#define VCCommentProp "NOTE" +#define VCCompletedProp "COMPLETED" +#define VCContentIDProp "CONTENT-ID" +#define VCCountryNameProp "C" +#define VCDAlarmProp "DALARM" +#define VCDataSizeProp "DATASIZE" +#define VCDayLightProp "DAYLIGHT" +#define VCDCreatedProp "DCREATED" +#define VCDeliveryLabelProp "LABEL" +#define VCDescriptionProp "DESCRIPTION" +#define VCDIBProp "DIB" +#define VCDisplayStringProp "DISPLAYSTRING" +#define VCDomesticProp "DOM" +#define VCDTendProp "DTEND" +#define VCDTstartProp "DTSTART" +#define VCDueProp "DUE" +#define VCEmailAddressProp "EMAIL" +#define VCEncodingProp "ENCODING" +#define VCEndProp "END" +#define VCEventProp "VEVENT" +#define VCEWorldProp "EWORLD" +#define VCExNumProp "EXNUM" +#define VCExDateProp "EXDATE" +#define VCExpectProp "EXPECT" +#define VCExtAddressProp "EXT ADD" +#define VCFamilyNameProp "F" +#define VCFaxProp "FAX" +#define VCFullNameProp "FN" +#define VCGeoProp "GEO" +#define VCGeoLocationProp "GEO" +#define VCGIFProp "GIF" +#define VCGivenNameProp "G" +#define VCGroupingProp "Grouping" +#define VCHomeProp "HOME" +#define VCIBMMailProp "IBMMail" +#define VCInlineProp "INLINE" +#define VCInternationalProp "INTL" +#define VCInternetProp "INTERNET" +#define VCISDNProp "ISDN" +#define VCJPEGProp "JPEG" +#define VCLanguageProp "LANG" +#define VCLastModifiedProp "LAST-MODIFIED" +#define VCLastRevisedProp "REV" +#define VCLocationProp "LOCATION" +#define VCLogoProp "LOGO" +#define VCMailerProp "MAILER" +#define VCMAlarmProp "MALARM" +#define VCMCIMailProp "MCIMAIL" +#define VCMessageProp "MSG" +#define VCMETProp "MET" +#define VCModemProp "MODEM" +#define VCMPEG2Prop "MPEG2" +#define VCMPEGProp "MPEG" +#define VCMSNProp "MSN" +#define VCNamePrefixesProp "NPRE" +#define VCNameProp "N" +#define VCNameSuffixesProp "NSUF" +#define VCNoteProp "NOTE" +#define VCOrgNameProp "ORGNAME" +#define VCOrgProp "ORG" +#define VCOrgUnit2Prop "OUN2" +#define VCOrgUnit3Prop "OUN3" +#define VCOrgUnit4Prop "OUN4" +#define VCOrgUnitProp "OUN" +#define VCPagerProp "PAGER" +#define VCPAlarmProp "PALARM" +#define VCParcelProp "PARCEL" +#define VCPartProp "PART" +#define VCPCMProp "PCM" +#define VCPDFProp "PDF" +#define VCPGPProp "PGP" +#define VCPhotoProp "PHOTO" +#define VCPICTProp "PICT" +#define VCPMBProp "PMB" +#define VCPostalBoxProp "BOX" +#define VCPostalCodeProp "PC" +#define VCPostalProp "POSTAL" +#define VCPowerShareProp "POWERSHARE" +#define VCPreferredProp "PREF" +#define VCPriorityProp "PRIORITY" +#define VCProcedureNameProp "PROCEDURENAME" +#define VCProdIdProp "PRODID" +#define VCProdigyProp "PRODIGY" +#define VCPronunciationProp "SOUND" +#define VCPSProp "PS" +#define VCPublicKeyProp "KEY" +#define VCQPProp "QP" +#define VCQuickTimeProp "QTIME" +#define VCQuotedPrintableProp "QUOTED-PRINTABLE" +#define VCRDateProp "RDATE" +#define VCRegionProp "R" +#define VCRelatedToProp "RELATED-TO" +#define VCRepeatCountProp "REPEATCOUNT" +#define VCResourcesProp "RESOURCES" +#define VCRNumProp "RNUM" +#define VCRoleProp "ROLE" +#define VCRRuleProp "RRULE" +#define VCRSVPProp "RSVP" +#define VCRunTimeProp "RUNTIME" +#define VCSequenceProp "SEQUENCE" +#define VCSnoozeTimeProp "SNOOZETIME" +#define VCStartProp "START" +#define VCStatusProp "STATUS" +#define VCStreetAddressProp "STREET" +#define VCSubTypeProp "SUBTYPE" +#define VCSummaryProp "SUMMARY" +#define VCTelephoneProp "TEL" +#define VCTIFFProp "TIFF" +#define VCTimeZoneProp "TZ" +#define VCTitleProp "TITLE" +#define VCTLXProp "TLX" +#define VCTodoProp "VTODO" +#define VCTranspProp "TRANSP" +#define VCUniqueStringProp "UID" +#define VCURLProp "URL" +#define VCURLValueProp "URLVAL" +#define VCValueProp "VALUE" +#define VCVersionProp "VERSION" +#define VCVideoProp "VIDEO" +#define VCVoiceProp "VOICE" +#define VCWAVEProp "WAVE" +#define VCWMFProp "WMF" +#define VCWorkProp "WORK" +#define VCX400Prop "X400" +#define VCX509Prop "X509" +#define VCXRuleProp "XRULE" + +/* extensions for KOrganizer / KPilot */ +#define KPilotIdProp "X-PILOTID" +#define KPilotStatusProp "X-PILOTSTAT" + +/* extensions for iMIP / iTIP */ +#define ICOrganizerProp "X-ORGANIZER" +#define ICMethodProp "X-METHOD" +#define ICRequestStatusProp "X-REQUEST-STATUS" + +typedef struct VObject VObject; + +typedef union ValueItem { + const char *strs; + const wchar_t *ustrs; + unsigned int i; + unsigned long l; + void *any; + VObject *vobj; + } ValueItem; + +struct VObject { + VObject *next; + const char *id; + VObject *prop; + unsigned short valType; + ValueItem val; + }; + +typedef struct StrItem StrItem; + +struct StrItem { + StrItem *next; + const char *s; + unsigned int refCnt; + }; + +typedef struct VObjectIterator { + VObject* start; + VObject* next; + } VObjectIterator; + +extern VObject* newVObject(const char *id); +extern void deleteVObject(VObject *p); +extern char* dupStr(const char *s, unsigned int size); +extern void deleteStr(const char *p); +extern void unUseStr(const char *s); + +extern void setVObjectName(VObject *o, const char* id); +extern void setVObjectStringZValue(VObject *o, const char *s); +extern void setVObjectStringZValue_(VObject *o, const char *s); +extern void setVObjectUStringZValue(VObject *o, const wchar_t *s); +extern void setVObjectUStringZValue_(VObject *o, const wchar_t *s); +extern void setVObjectIntegerValue(VObject *o, unsigned int i); +extern void setVObjectLongValue(VObject *o, unsigned long l); +extern void setVObjectAnyValue(VObject *o, void *t); +extern VObject* setValueWithSize(VObject *prop, void *val, unsigned int size); +extern VObject* setValueWithSize_(VObject *prop, void *val, unsigned int size); + +extern const char* vObjectName(VObject *o); +extern const char* vObjectStringZValue(VObject *o); +extern const wchar_t* vObjectUStringZValue(VObject *o); +extern unsigned int vObjectIntegerValue(VObject *o); +extern unsigned long vObjectLongValue(VObject *o); +extern void* vObjectAnyValue(VObject *o); +extern VObject* vObjectVObjectValue(VObject *o); +extern void setVObjectVObjectValue(VObject *o, VObject *p); + +extern VObject* addVObjectProp(VObject *o, VObject *p); +extern VObject* addProp(VObject *o, const char *id); +extern VObject* addProp_(VObject *o, const char *id); +extern VObject* addPropValue(VObject *o, const char *p, const char *v); +extern VObject* addPropSizedValue_(VObject *o, const char *p, const char *v, unsigned int size); +extern VObject* addPropSizedValue(VObject *o, const char *p, const char *v, unsigned int size); +extern VObject* addGroup(VObject *o, const char *g); +extern void addList(VObject **o, VObject *p); + +extern VObject* isAPropertyOf(VObject *o, const char *id); + +extern VObject* nextVObjectInList(VObject *o); +extern void initPropIterator(VObjectIterator *i, VObject *o); +extern int moreIteration(VObjectIterator *i); +extern VObject* nextVObject(VObjectIterator *i); + +extern char* writeMemVObject(char *s, int *len, VObject *o); +extern char* writeMemVObjects(char *s, int *len, VObject *list); + +extern const char* lookupStr(const char *s); +extern void cleanStrTbl(); + +extern void cleanVObject(VObject *o); +extern void cleanVObjects(VObject *list); + +extern const char* lookupProp(const char* str); +extern const char* lookupProp_(const char* str); + +extern wchar_t* fakeUnicode(const char *ps, int *bytes); +extern int uStrLen(const wchar_t *u); +extern char* fakeCString(const wchar_t *u); + +extern void printVObjectToFile(char *fname,VObject *o); +extern void printVObjectsToFile(char *fname,VObject *list); +extern void writeVObjectToFile(char *fname, VObject *o); +extern void writeVObjectsToFile(char *fname, VObject *list); + +extern int vObjectValueType(VObject *o); + +/* return type of vObjectValueType: */ +#define VCVT_NOVALUE 0 + /* if the VObject has no value associated with it. */ +#define VCVT_STRINGZ 1 + /* if the VObject has value set by setVObjectStringZValue. */ +#define VCVT_USTRINGZ 2 + /* if the VObject has value set by setVObjectUStringZValue. */ +#define VCVT_UINT 3 + /* if the VObject has value set by setVObjectIntegerValue. */ +#define VCVT_ULONG 4 + /* if the VObject has value set by setVObjectLongValue. */ +#define VCVT_RAW 5 + /* if the VObject has value set by setVObjectAnyValue. */ +#define VCVT_VOBJECT 6 + /* if the VObject has value set by setVObjectVObjectValue. */ + +extern const char** fieldedProp; + +extern void printVObject(FILE *fp,VObject *o); +extern void writeVObject(FILE *fp, VObject *o); + + +#if defined(__CPLUSPLUS__) || defined(__cplusplus) +} +#endif + +#endif /* __VOBJECT_H__ */ + + |