summaryrefslogtreecommitdiffabout
path: root/libkcal
Side-by-side diff
Diffstat (limited to 'libkcal') (more/less context) (ignore whitespace changes)
-rw-r--r--libkcal/alarm.cpp407
-rw-r--r--libkcal/alarm.h245
-rw-r--r--libkcal/attachment.cpp86
-rw-r--r--libkcal/attachment.h69
-rw-r--r--libkcal/attendee.cpp167
-rw-r--r--libkcal/attendee.h96
-rw-r--r--libkcal/calendar.cpp426
-rw-r--r--libkcal/calendar.h349
-rw-r--r--libkcal/calendar.moc0
-rw-r--r--libkcal/calendarlocal.cpp655
-rw-r--r--libkcal/calendarlocal.h216
-rw-r--r--libkcal/calendarresources.h17
-rw-r--r--libkcal/calfilter.cpp201
-rw-r--r--libkcal/calfilter.h128
-rw-r--r--libkcal/calformat.cpp98
-rw-r--r--libkcal/calformat.h111
-rw-r--r--libkcal/calstorage.h52
-rw-r--r--libkcal/compat.cpp37
-rw-r--r--libkcal/compat.h53
-rw-r--r--libkcal/customproperties.cpp114
-rw-r--r--libkcal/customproperties.h97
-rw-r--r--libkcal/dndfactory.h62
-rw-r--r--libkcal/dummyscheduler.cpp119
-rw-r--r--libkcal/dummyscheduler.h51
-rw-r--r--libkcal/duration.cpp59
-rw-r--r--libkcal/duration.h48
-rw-r--r--libkcal/event.cpp178
-rw-r--r--libkcal/event.h88
-rw-r--r--libkcal/exceptions.cpp90
-rw-r--r--libkcal/exceptions.h75
-rw-r--r--libkcal/filestorage.cpp140
-rw-r--r--libkcal/filestorage.h58
-rw-r--r--libkcal/freebusy.cpp184
-rw-r--r--libkcal/freebusy.h72
-rw-r--r--libkcal/icaldrag.cpp58
-rw-r--r--libkcal/icaldrag.h46
-rw-r--r--libkcal/icalformat.cpp478
-rw-r--r--libkcal/icalformat.h116
-rw-r--r--libkcal/icalformatimpl.cpp2173
-rw-r--r--libkcal/icalformatimpl.h109
-rw-r--r--libkcal/icalformatimpl.h.bup109
-rw-r--r--libkcal/imipscheduler.cpp58
-rw-r--r--libkcal/imipscheduler.h49
-rw-r--r--libkcal/incidence.cpp594
-rw-r--r--libkcal/incidence.h298
-rw-r--r--libkcal/incidencebase.cpp393
-rw-r--r--libkcal/incidencebase.h170
-rw-r--r--libkcal/journal.cpp49
-rw-r--r--libkcal/journal.h50
-rw-r--r--libkcal/kcal.pro.back84
-rw-r--r--libkcal/libkcal.pro100
-rw-r--r--libkcal/libkcalE.pro88
-rw-r--r--libkcal/listbase.h97
-rw-r--r--libkcal/period.cpp65
-rw-r--r--libkcal/period.h51
-rw-r--r--libkcal/person.cpp77
-rw-r--r--libkcal/person.h50
-rw-r--r--libkcal/qtopiaformat.cpp333
-rw-r--r--libkcal/qtopiaformat.h53
-rw-r--r--libkcal/recurrence.cpp3360
-rw-r--r--libkcal/recurrence.h401
-rw-r--r--libkcal/resourcecalendar.h16
-rw-r--r--libkcal/scheduler.cpp355
-rw-r--r--libkcal/scheduler.h133
-rw-r--r--libkcal/sharpformat.cpp1007
-rw-r--r--libkcal/sharpformat.h61
-rw-r--r--libkcal/todo.cpp316
-rw-r--r--libkcal/todo.h121
-rw-r--r--libkcal/vcaldrag.cpp54
-rw-r--r--libkcal/vcaldrag.h47
-rw-r--r--libkcal/vcalformat.cpp1678
-rw-r--r--libkcal/vcalformat.h108
-rw-r--r--libkcal/versit/port.h75
-rw-r--r--libkcal/versit/vcc.c2162
-rw-r--r--libkcal/versit/vcc.h76
-rw-r--r--libkcal/versit/versit.pro15
-rw-r--r--libkcal/versit/versit.pro.back15
-rw-r--r--libkcal/versit/vobject.c1433
-rw-r--r--libkcal/versit/vobject.h384
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 &noti );
+ void removeAlarm(const QDateTime &qdt, const QString &noti );
+
+ 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__ */
+
+