summaryrefslogtreecommitdiffstats
path: root/libkcal
diff options
context:
space:
mode:
Diffstat (limited to 'libkcal')
-rw-r--r--libkcal/Makefile.am12
-rw-r--r--libkcal/alarm.cpp60
-rw-r--r--libkcal/alarm.h48
-rw-r--r--libkcal/assignmentvisitor.cpp123
-rw-r--r--libkcal/assignmentvisitor.h122
-rw-r--r--libkcal/attachment.cpp111
-rw-r--r--libkcal/attachment.h57
-rw-r--r--libkcal/attachmenthandler.cpp258
-rw-r--r--libkcal/attachmenthandler.h178
-rw-r--r--libkcal/attendee.cpp3
-rw-r--r--libkcal/attendee.h4
-rw-r--r--libkcal/calendar.cpp185
-rw-r--r--libkcal/calendar.h87
-rw-r--r--libkcal/calendarlocal.cpp145
-rw-r--r--libkcal/calendarresources.cpp234
-rw-r--r--libkcal/calendarresources.h143
-rw-r--r--libkcal/calformat.cpp2
-rw-r--r--libkcal/calformat.h2
-rw-r--r--libkcal/calhelper.cpp167
-rw-r--r--libkcal/calhelper.h135
-rw-r--r--libkcal/calselectdialog.cpp101
-rw-r--r--libkcal/calselectdialog.h69
-rw-r--r--libkcal/comparisonvisitor.cpp107
-rw-r--r--libkcal/comparisonvisitor.h122
-rw-r--r--libkcal/dndfactory.cpp197
-rw-r--r--libkcal/dndfactory.h12
-rw-r--r--libkcal/duration.cpp140
-rw-r--r--libkcal/duration.h235
-rw-r--r--libkcal/event.cpp4
-rw-r--r--libkcal/event.h9
-rw-r--r--libkcal/exceptions.cpp77
-rw-r--r--libkcal/exceptions.h32
-rw-r--r--libkcal/freebusy.cpp9
-rw-r--r--libkcal/freebusy.h11
-rw-r--r--libkcal/htmlexport.cpp34
-rw-r--r--libkcal/icalformatimpl.cpp255
-rw-r--r--libkcal/incidence.h30
-rw-r--r--libkcal/incidencebase.cpp8
-rw-r--r--libkcal/incidencebase.h30
-rw-r--r--libkcal/incidenceformatter.cpp3526
-rw-r--r--libkcal/incidenceformatter.h65
-rw-r--r--libkcal/kcal_manager.desktop4
-rw-r--r--libkcal/libical/configure.in.in20
-rw-r--r--libkcal/libical/src/libical/icalattach.c151
-rw-r--r--libkcal/local.desktop1
-rw-r--r--libkcal/localdir.desktop1
-rw-r--r--libkcal/period.cpp8
-rw-r--r--libkcal/period.h18
-rw-r--r--libkcal/recurrence.cpp177
-rw-r--r--libkcal/recurrence.h15
-rw-r--r--libkcal/recurrencerule.cpp326
-rw-r--r--libkcal/recurrencerule.h121
-rw-r--r--libkcal/resourcecached.cpp21
-rw-r--r--libkcal/resourcecached.h16
-rw-r--r--libkcal/resourcecalendar.cpp46
-rw-r--r--libkcal/resourcecalendar.h60
-rw-r--r--libkcal/resourcelocaldir.cpp51
-rw-r--r--libkcal/resourcelocaldir.h1
-rw-r--r--libkcal/resourcelocaldirconfig.cpp11
-rw-r--r--libkcal/scheduler.cpp356
-rw-r--r--libkcal/scheduler.h27
-rw-r--r--libkcal/tests/Makefile.am7
-rw-r--r--libkcal/tests/data/RecurrenceRule/ConnectDaily/ConnectDaily11.ics.recurson.ref1
-rw-r--r--libkcal/tests/data/RecurrenceRule/KAlarm_3.4/KAlarm_TestCase06.ics.recurson.ref1
-rw-r--r--libkcal/tests/data/RecurrenceRule/LibICal/LibICal_TestCase02.ics.recurson.ref1
-rw-r--r--libkcal/tests/data/RecurrenceRule/LibICal/LibICal_TestCase24.ics.recurson.ref1
-rw-r--r--libkcal/tests/data/RecurrenceRule/LibICal/LibICal_TestCase42.ics.recurson.ref1
-rw-r--r--libkcal/tests/data/RecurrenceRule/RFC2445/RFC2445_RRULETestCase12.ics.recurson.ref2
-rw-r--r--libkcal/tests/data/RecurrenceRule/UntilInUTC/Until_TestCase02.ics.recurson.ref1
-rw-r--r--libkcal/tests/data/RecurrenceRule/UntilInUTC/Until_TestCase04.ics.recurson.ref1
-rw-r--r--libkcal/tests/data/RecurrenceRule/unsorted/lastworkday.ics.recurson.ref1
-rw-r--r--libkcal/tests/data/RecurrenceRule/unsorted/monthly.ics.recurson.ref1
-rw-r--r--libkcal/tests/data/RecurrenceRule/unsorted/rdate.ics.recurson.ref1
-rw-r--r--libkcal/tests/data/RecurrenceRule/unsorted/test1.ics.recurson.ref1
-rw-r--r--libkcal/tests/data/RecurrenceRule/unsorted/weekly.ics.recurson.ref208
-rwxr-xr-xlibkcal/tests/runtestcase.pl8
-rw-r--r--libkcal/tests/testcalselectdialog.cpp45
-rw-r--r--libkcal/todo.cpp33
-rw-r--r--libkcal/todo.h20
79 files changed, 7452 insertions, 1462 deletions
diff --git a/libkcal/Makefile.am b/libkcal/Makefile.am
index f76d3204..2fa8c288 100644
--- a/libkcal/Makefile.am
+++ b/libkcal/Makefile.am
@@ -10,15 +10,17 @@ libkcal_la_LDFLAGS = $(all_libraries) -no-undefined -version-info 2:0:0 $(LIB_QT
libkcal_la_LIBADD = versit/libversit.la $(LIB_KIO) \
-lical -licalss \
$(top_builddir)/ktnef/lib/libktnef.la \
+ ../libkmime/libkmime.la \
../libemailfunctions/libemailfunctions.la \
-lkresources -lkabc
libkcal_la_SOURCES = \
+ assignmentvisitor.cpp comparisonvisitor.cpp \
incidencebase.cpp incidence.cpp journal.cpp todo.cpp event.cpp \
freebusy.cpp attendee.cpp attachment.cpp recurrencerule.cpp recurrence.cpp alarm.cpp \
customproperties.cpp calendar.cpp calendarlocal.cpp \
calformat.cpp vcalformat.cpp icalformat.cpp icalformatimpl.cpp \
- incidenceformatter.cpp \
+ incidenceformatter.cpp calhelper.cpp calselectdialog.cpp \
vcaldrag.cpp icaldrag.cpp \
exceptions.cpp \
scheduler.cpp imipscheduler.cpp dummyscheduler.cpp \
@@ -34,18 +36,20 @@ libkcal_la_SOURCES = \
qtopiaformat.cpp \
htmlexportsettings.kcfgc htmlexport.cpp calendarnull.cpp \
freebusyurlstore.cpp \
- confirmsavedialog.cpp
+ confirmsavedialog.cpp \
+ attachmenthandler.cpp
libkcalincludedir = $(includedir)/libkcal
libkcalinclude_HEADERS = alarm.h attachment.h attendee.h calendar.h \
calendarlocal.h calendarnull.h calendarresources.h calfilter.h calformat.h \
calstorage.h customproperties.h dndfactory.h duration.h event.h \
exceptions.h filestorage.h freebusy.h htmlexportsettings.h htmlexport.h icaldrag.h icalformat.h \
- incidencebase.h incidence.h incidenceformatter.h journal.h kcalversion.h listbase.h period.h person.h \
+ incidencebase.h incidence.h incidenceformatter.h calhelper.h calselectdialog.h \
+ journal.h kcalversion.h listbase.h period.h person.h \
qtopiaformat.h recurrencerule.h recurrence.h resourcecached.h resourcecalendar.h \
resourcelocalconfig.h resourcelocaldirconfig.h resourcelocaldir.h \
resourcelocal.h scheduler.h libkcal_export.h \
- todo.h vcaldrag.h vcalformat.h
+ todo.h vcaldrag.h vcalformat.h attachmenthandler.h
kde_module_LTLIBRARIES = kcal_local.la kcal_localdir.la
diff --git a/libkcal/alarm.cpp b/libkcal/alarm.cpp
index b5b9b515..8bc40f00 100644
--- a/libkcal/alarm.cpp
+++ b/libkcal/alarm.cpp
@@ -47,6 +47,30 @@ Alarm::~Alarm()
{
}
+Alarm *Alarm::clone()
+{
+ return new Alarm( *this );
+}
+
+Alarm &Alarm::operator=( const Alarm &a )
+{
+ mParent = a.mParent;
+ mType = a.mType;
+ mDescription = a.mDescription;
+ mFile = a.mFile;
+ mMailAttachFiles = a.mMailAttachFiles;
+ mMailAddresses = a.mMailAddresses;
+ mMailSubject = a.mMailSubject;
+ mAlarmSnoozeTime = a.mAlarmSnoozeTime;
+ mAlarmRepeatCount = a.mAlarmRepeatCount;
+ mAlarmTime = a.mAlarmTime;
+ mOffset = a.mOffset;
+ mEndOffset = a.mEndOffset;
+ mHasTime = a.mHasTime;
+ mAlarmEnabled = a.mAlarmEnabled;
+ return *this;
+}
+
bool Alarm::operator==( const Alarm& rhs ) const
{
if ( mType != rhs.mType ||
@@ -304,19 +328,22 @@ void Alarm::setTime(const TQDateTime &alarmTime)
TQDateTime Alarm::time() const
{
- if ( hasTime() )
+ if ( hasTime() ) {
return mAlarmTime;
- else if ( mParent )
- {
- if (mParent->type()=="Todo") {
- Todo *t = static_cast<Todo*>(mParent);
- return mOffset.end( t->dtDue() );
- } else if (mEndOffset) {
- return mOffset.end( mParent->dtEnd() );
+ } else if ( mParent ) {
+ if ( mEndOffset ) {
+ if ( mParent->type() == "Todo" ) {
+ Todo *t = static_cast<Todo*>( mParent );
+ return mOffset.end( t->dtDue() );
+ } else {
+ return mOffset.end( mParent->dtEnd() );
+ }
} else {
return mOffset.end( mParent->dtStart() );
}
- } else return TQDateTime();
+ } else {
+ return TQDateTime();
+ }
}
bool Alarm::hasTime() const
@@ -324,15 +351,15 @@ bool Alarm::hasTime() const
return mHasTime;
}
-void Alarm::setSnoozeTime(int alarmSnoozeTime)
+void Alarm::setSnoozeTime(const Duration &alarmSnoozeTime)
{
- if (alarmSnoozeTime > 0) {
+ if (alarmSnoozeTime.value() > 0) {
mAlarmSnoozeTime = alarmSnoozeTime;
if ( mParent ) mParent->updated();
}
}
-int Alarm::snoozeTime() const
+Duration Alarm::snoozeTime() const
{
return mAlarmSnoozeTime;
}
@@ -348,9 +375,10 @@ int Alarm::repeatCount() const
return mAlarmRepeatCount;
}
-int Alarm::duration() const
+Duration Alarm::duration() const
{
- return mAlarmRepeatCount * mAlarmSnoozeTime * 60;
+ return Duration( mAlarmSnoozeTime.value() * mAlarmRepeatCount,
+ mAlarmSnoozeTime.type() );
}
TQDateTime Alarm::nextRepetition(const TQDateTime& preTime) const
@@ -422,7 +450,7 @@ void Alarm::setStartOffset( const Duration &offset )
Duration Alarm::startOffset() const
{
- return (mHasTime || mEndOffset) ? 0 : mOffset;
+ return (mHasTime || mEndOffset) ? Duration( 0 ) : mOffset;
}
bool Alarm::hasStartOffset() const
@@ -445,7 +473,7 @@ void Alarm::setEndOffset( const Duration &offset )
Duration Alarm::endOffset() const
{
- return (mHasTime || !mEndOffset) ? 0 : mOffset;
+ return (mHasTime || !mEndOffset) ? Duration( 0 ) : mOffset;
}
void Alarm::setParent( Incidence *parent )
diff --git a/libkcal/alarm.h b/libkcal/alarm.h
index 7d82cf65..893e7be4 100644
--- a/libkcal/alarm.h
+++ b/libkcal/alarm.h
@@ -2,6 +2,7 @@
This file is part of libkcal.
Copyright (c) 2001-2003 Cornelius Schumacher <schumacher@kde.org>
+ Copyright (c) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
@@ -58,6 +59,18 @@ class LIBKCAL_EXPORT Alarm : public CustomProperties
~Alarm();
/**
+ Returns an exact copy of this alarm. The returned object is owned by the caller.
+ @since 4.5
+ */
+ Alarm *clone();
+
+ /**
+ Copy operator.
+ @since 4.5
+ */
+ Alarm &operator=( const Alarm & );
+
+ /**
Compare this alarm with another one.
*/
bool operator==( const Alarm & ) const;
@@ -67,10 +80,11 @@ class LIBKCAL_EXPORT Alarm : public CustomProperties
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.
*/
@@ -78,15 +92,17 @@ class LIBKCAL_EXPORT Alarm : public CustomProperties
/**
Set the alarm to be a display alarm.
-
+
@param text text to display when the alarm is triggered.
*/
void setDisplayAlarm( const TQString &text = TQString::null );
+
/**
Set the text to be displayed when the alarm is triggered.
Ignored if the alarm is not a display alarm.
*/
void setText( const TQString &text );
+
/**
Return the text string that displays when the alarm is triggered.
*/
@@ -94,7 +110,7 @@ class LIBKCAL_EXPORT Alarm : public CustomProperties
/**
Set the alarm to be an audio alarm.
-
+
@param audioFile optional file to play when the alarm is triggered.
*/
void setAudioAlarm( const TQString &audioFile = TQString::null );
@@ -105,14 +121,14 @@ class LIBKCAL_EXPORT Alarm : public CustomProperties
void setAudioFile( const TQString &audioFile );
/**
Return the name of the audio file for the alarm.
-
+
@return The audio file for the alarm, or TQString::null if not an audio alarm.
*/
TQString 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.
*/
@@ -125,7 +141,7 @@ class LIBKCAL_EXPORT Alarm : public CustomProperties
void setProgramFile( const TQString &programFile );
/**
Return the name of the program file to execute when the alarm is triggered.
-
+
@return the program file name, or TQString::null if not a procedure alarm.
*/
TQString programFile() const;
@@ -136,14 +152,14 @@ class LIBKCAL_EXPORT Alarm : public CustomProperties
void setProgramArguments( const TQString &arguments );
/**
Return the arguments to the program to run when the alarm is triggered.
-
+
@return the program arguments, or TQString::null if not a procedure alarm.
*/
TQString 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).
@@ -210,7 +226,7 @@ class LIBKCAL_EXPORT Alarm : public CustomProperties
void setMailText( const TQString &text );
/**
Return the email body text.
-
+
@return the body text, or TQString::null if not an email alarm.
*/
TQString mailText() const;
@@ -267,17 +283,17 @@ class LIBKCAL_EXPORT Alarm : public CustomProperties
/**
Set the interval between snoozes for the alarm.
-
+
@param alarmSnoozeTime the time in minutes between snoozes.
*/
- void setSnoozeTime( int alarmSnoozeTime );
+ void setSnoozeTime( const Duration &alarmSnoozeTime );
/**
Get how long the alarm snooze interval is.
-
+
@return the number of minutes between snoozes.
*/
- int snoozeTime() const;
+ Duration snoozeTime() const;
/**
Set how many times an alarm is to repeat itself after its initial
@@ -308,7 +324,7 @@ class LIBKCAL_EXPORT Alarm : public CustomProperties
Get how long between the alarm's initial occurrence and its final repetition.
@return the number of seconds between the initial occurrence and final repetition.
*/
- int duration() const;
+ Duration duration() const;
/**
Toggles the value of alarm to be either on or off.
@@ -350,8 +366,8 @@ class LIBKCAL_EXPORT Alarm : public CustomProperties
TQValueList<Person> mMailAddresses; // who to mail for reminder
TQString mMailSubject; // subject of email
- int mAlarmSnoozeTime; // number of minutes after alarm to
- // snooze before ringing again
+ Duration mAlarmSnoozeTime; // how long after alarm to snooze before
+ // triggering again
int mAlarmRepeatCount; // number of times for alarm to repeat
// after the initial time
diff --git a/libkcal/assignmentvisitor.cpp b/libkcal/assignmentvisitor.cpp
new file mode 100644
index 00000000..023df857
--- /dev/null
+++ b/libkcal/assignmentvisitor.cpp
@@ -0,0 +1,123 @@
+/*
+ Copyright (c) 2009 Kevin Krammer <kevin.krammer@gmx.at>
+
+ This library is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ This library is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
+ License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+*/
+
+#include "assignmentvisitor.h"
+
+#include "event.h"
+#include "freebusy.h"
+#include "journal.h"
+#include "todo.h"
+
+#include <kdebug.h>
+
+using namespace KCal;
+
+class AssignmentVisitor::Private
+{
+ public:
+ Private() : mSource( 0 ) {}
+
+ public:
+ const IncidenceBase *mSource;
+};
+
+AssignmentVisitor::AssignmentVisitor() : d( new Private() )
+{
+}
+
+AssignmentVisitor::~AssignmentVisitor()
+{
+ delete d;
+}
+
+bool AssignmentVisitor::assign( IncidenceBase *target, const IncidenceBase *source )
+{
+ Q_ASSERT( target != 0 );
+ Q_ASSERT( source != 0 );
+
+ d->mSource = source;
+
+ bool result = target->accept( *this );
+
+ d->mSource = 0;
+
+ return result;
+}
+
+bool AssignmentVisitor::visit( Event *event )
+{
+ Q_ASSERT( event != 0 );
+
+ const Event *source = dynamic_cast<const Event*>( d->mSource );
+ if ( source == 0 ) {
+ kdError(5800) << "Type mismatch: source is" << d->mSource->type()
+ << "target is" << event->type();
+ return false;
+ }
+
+ *event = *source;
+ return true;
+}
+
+bool AssignmentVisitor::visit( Todo *todo )
+{
+ Q_ASSERT( todo != 0 );
+
+ const Todo *source = dynamic_cast<const Todo*>( d->mSource );
+ if ( source == 0 ) {
+ kdError(5800) << "Type mismatch: source is" << d->mSource->type()
+ << "target is" << todo->type();
+ return false;
+ }
+
+ *todo = *source;
+ return true;
+}
+
+bool AssignmentVisitor::visit( Journal *journal )
+{
+ Q_ASSERT( journal != 0 );
+
+ const Journal *source = dynamic_cast<const Journal*>( d->mSource );
+ if ( source == 0 ) {
+ kdError(5800) << "Type mismatch: source is" << d->mSource->type()
+ << "target is" << journal->type();
+ return false;
+ }
+
+ *journal = *source;
+ return true;
+}
+
+bool AssignmentVisitor::visit( FreeBusy *freebusy )
+{
+ Q_ASSERT( freebusy != 0 );
+
+ const FreeBusy *source = dynamic_cast<const FreeBusy*>( d->mSource );
+ if ( source == 0 ) {
+ kdError(5800) << "Type mismatch: source is" << d->mSource->type()
+ << "target is" << freebusy->type();
+ return false;
+ }
+
+ *freebusy = *source;
+ return true;
+}
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/libkcal/assignmentvisitor.h b/libkcal/assignmentvisitor.h
new file mode 100644
index 00000000..2be0215c
--- /dev/null
+++ b/libkcal/assignmentvisitor.h
@@ -0,0 +1,122 @@
+/*
+ Copyright (c) 2009 Kevin Krammer <kevin.krammer@gmx.at>
+
+ This library is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ This library is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
+ License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+*/
+
+#ifndef KCAL_ASSIGNMENTVISITOR_H
+#define KCAL_ASSIGNMENTVISITOR_H
+
+#include "incidencebase.h"
+
+namespace KCal {
+
+/**
+ Helper for type correct assignment of incidences via pointers.
+
+ This class provides a way of correctly assigning one incidence to another,
+ given two IncidenceBase derived pointers. It effectively provides a virtual
+ assignment method which first type checks the two pointers to ensure they
+ reference the same incidence type, before performing the assignment.
+
+ Usage example:
+ @code
+ KCal::Incidence *currentIncidence; // assume this is set somewhere else
+ KCal::Incidence *updatedIncidence; // assume this is set somewhere else
+
+ KCal::AssignmentVisitor visitor;
+
+ // assign
+ if ( !visitor.assign(currentIncidence, updatedIncidence) ) {
+ // not of same type
+ }
+ @endcode
+
+ @author Kevin Krammer \<kevin.krammer@gmx.at\>
+ */
+class AssignmentVisitor : public IncidenceBase::Visitor
+{
+ public:
+ /**
+ Creates a visitor instance.
+ */
+ AssignmentVisitor();
+
+ /**
+ Destroys the instance.
+ */
+ virtual ~AssignmentVisitor();
+
+ /**
+ Assigns the incidence referenced by @p source to the incidence referenced
+ by @p target, first ensuring that the @p source incidence can be cast to
+ the same class as the @p target incidence.
+
+ Basically it is a virtual equivalent of
+ @code
+ *target = *source
+ @endcode
+
+ @param target pointer to the instance to assign to
+ @param source pointer to the instance to assign from
+
+ @return @c false if the two objects are of different type
+ */
+ bool assign( IncidenceBase *target, const IncidenceBase *source );
+
+ /**
+ Tries to assign to the given @p event, using the source passed to
+ assign().
+
+ @return @c false if the source passed to assign() is not an Event
+ */
+ virtual bool visit( Event *event );
+
+ /**
+ Tries to assign to the given @p todo, using the source passed to
+ assign().
+
+ @return @c false if the source passed to assign() is not a Todo
+ */
+ virtual bool visit( Todo *todo );
+
+ /**
+ Tries to assign to the given @p journal, using the source passed to
+ assign().
+
+ @return @c false if the source passed to assign() is not a Journal
+ */
+ virtual bool visit( Journal *journal );
+
+ /**
+ Tries to assign to the given @p freebusy, using the source passed to
+ assign().
+
+ @return @c false if the source passed to assign() is not a FreeBusy
+ */
+ virtual bool visit( FreeBusy *freebusy );
+
+ private:
+ //@cond PRIVATE
+ class Private;
+ Private *const d;
+ //@endcond
+};
+
+}
+
+#endif
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/libkcal/attachment.cpp b/libkcal/attachment.cpp
index fc319b73..51858161 100644
--- a/libkcal/attachment.cpp
+++ b/libkcal/attachment.cpp
@@ -20,34 +20,46 @@
*/
#include "attachment.h"
+#include <kmdcodec.h>
using namespace KCal;
-Attachment::Attachment( const Attachment &attachment)
+Attachment::Attachment( const Attachment &attachment )
{
+ mSize = attachment.mSize;
mMimeType = attachment.mMimeType;
- mData = attachment.mData;
+ mUri = attachment.mUri;
+ mData = qstrdup( attachment.mData );
+ mLabel = attachment.mLabel;
mBinary = attachment.mBinary;
- mShowInline = attachment.mShowInline;
- mLabel = attachment.mLabel;
+ mLocal = attachment.mLocal;
+ mShowInline = attachment.mShowInline;
}
-Attachment::Attachment(const TQString& uri, const TQString& mime)
+Attachment::Attachment( const TQString &uri, const TQString &mime )
{
+ mSize = 0;
mMimeType = mime;
- mData = uri;
+ mUri = uri;
+ mData = 0;
mBinary = false;
- mShowInline = false;
- mLabel = TQString::null;
+ mLocal = false;
+ mShowInline = false;
}
-Attachment::Attachment(const char *base64, const TQString& mime)
+Attachment::Attachment( const char *base64, const TQString &mime )
{
+ mSize = 0;
mMimeType = mime;
- mData = TQString::fromUtf8(base64);
+ mData = qstrdup( base64 );
mBinary = true;
- mShowInline = false;
- mLabel = TQString::null;
+ mLocal = false;
+ mShowInline = false;
+}
+
+Attachment::~Attachment()
+{
+ delete[] mData;
}
bool Attachment::isUri() const
@@ -57,15 +69,16 @@ bool Attachment::isUri() const
TQString Attachment::uri() const
{
- if (!mBinary)
- return mData;
- else
+ if ( !mBinary ) {
+ return mUri;
+ } else {
return TQString::null;
+ }
}
-void Attachment::setUri(const TQString& uri)
+void Attachment::setUri( const TQString &uri )
{
- mData = uri;
+ mUri = uri;
mBinary = false;
}
@@ -76,17 +89,60 @@ bool Attachment::isBinary() const
char *Attachment::data() const
{
- if (mBinary)
- // this method actually return a const char*, but that can't be done because of the uneededly non-const libical API
- return const_cast<char*>( mData.latin1() ); //mData.utf8().data();
- else
+ if ( mBinary ) {
+ return mData;
+ } else {
return 0;
+ }
+}
+
+TQByteArray &Attachment::decodedData()
+{
+ if ( mDataCache.isNull() && mData ) {
+ // base64Decode() sometimes appends a null byte when called
+ // with a TQCString so work on TQByteArray's instead
+ TQByteArray encoded;
+ encoded.duplicate( mData, strlen( mData ) );
+ TQByteArray decoded;
+ KCodecs::base64Decode( encoded, decoded );
+ mDataCache = decoded;
+ }
+
+ return mDataCache;
}
-void Attachment::setData(const char *base64)
+void Attachment::setDecodedData( const TQByteArray &data )
{
- mData = TQString::fromUtf8(base64);
+ TQByteArray encoded;
+ KCodecs::base64Encode( data, encoded );
+
+ encoded.resize( encoded.count() + 1 );
+ encoded[encoded.count()-1] = '\0';
+
+ setData( encoded.data() );
+ mDataCache = data;
+ mSize = mDataCache.size();
+}
+
+void Attachment::setData( const char *base64 )
+{
+ delete[] mData;
+ mData = qstrdup( base64 );
mBinary = true;
+ mDataCache = TQByteArray();
+ mSize = 0;
+}
+
+uint Attachment::size()
+{
+ if ( isUri() ) {
+ return 0;
+ }
+ if ( !mSize ) {
+ mSize = decodedData().size();
+ }
+
+ return mSize;
}
TQString Attachment::mimeType() const
@@ -119,3 +175,12 @@ void Attachment::setLabel( const TQString& label )
mLabel = label;
}
+bool Attachment::isLocal() const
+{
+ return mLocal;
+}
+
+void Attachment::setLocal( bool local )
+{
+ mLocal = local;
+}
diff --git a/libkcal/attachment.h b/libkcal/attachment.h
index 41670185..7727a5c6 100644
--- a/libkcal/attachment.h
+++ b/libkcal/attachment.h
@@ -38,14 +38,14 @@ class KDE_EXPORT Attachment
/**
Create a Reference to some URI by copying an existing Attachment.
-
+
@param attachment the attachment to be duplicated
*/
Attachment( const Attachment &attachment );
/**
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
*/
@@ -53,39 +53,66 @@ class KDE_EXPORT Attachment
/**
Create a binary attachment.
-
+
@param base64 the attachment in base64 format
@param mime the mime type of the attachment
*/
Attachment( const char *base64, const TQString &mime = TQString::null );
+ ~Attachment();
+
/* The VALUE parameter in iCal */
bool isUri() const;
TQString uri() const;
void setUri( const TQString &uri );
-
+
bool isBinary() const;
char *data() const;
void setData( const char *base64 );
+ void setDecodedData( const TQByteArray &data );
+ TQByteArray &decodedData();
+
+ uint size();
+
/* The optional FMTTYPE parameter in iCal */
TQString mimeType() const;
void setMimeType( const TQString &mime );
-
- /* The custom X-CONTENT-DISPOSITION parameter, used by OGo etc. */
- bool showInline() const;
- void setShowInline( bool showinline );
-
- /* The custom X-LABEL parameter to show a human-readable title */
- TQString label() const;
- void setLabel( const TQString &label );
+
+ /* The custom X-CONTENT-DISPOSITION parameter, used by OGo etc. */
+ bool showInline() const;
+ void setShowInline( bool showinline );
+
+ /* The custom X-LABEL parameter to show a human-readable title */
+ TQString label() const;
+ void setLabel( const TQString &label );
+
+ /**
+ Sets the attachment "local" option, which is derived from the
+ Calendar Incidence @b X-KONTACT-TYPE parameter.
+
+ @param local is the flag to set (true) or unset (false) for the
+ attachment "local" option.
+
+ @see local()
+ */
+ void setLocal( bool local );
+
+ /**
+ Returns the attachment "local" flag.
+ */
+ bool isLocal() const;
private:
+ TQByteArray mDataCache;
+ uint mSize;
TQString mMimeType;
- TQString mData;
+ TQString mUri;
+ char *mData;
+ TQString mLabel;
bool mBinary;
- bool mShowInline;
- TQString mLabel;
+ bool mLocal;
+ bool mShowInline;
class Private;
Private *d;
diff --git a/libkcal/attachmenthandler.cpp b/libkcal/attachmenthandler.cpp
new file mode 100644
index 00000000..3163def0
--- /dev/null
+++ b/libkcal/attachmenthandler.cpp
@@ -0,0 +1,258 @@
+/*
+ This file is part of the kcal library.
+
+ Copyright (c) 2010 Klar�lvdalens Datakonsult AB, a KDAB Group company <info@kdab.net>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/**
+ @file
+ This file is part of the API for handling calendar data and provides
+ static functions for dealing with calendar incidence attachments.
+
+ @brief
+ vCalendar/iCalendar attachment handling.
+
+ @author Allen Winter \<winter@kde.org\>
+*/
+#include "attachmenthandler.h"
+#include "attachment.h"
+#include "calendarresources.h"
+#include "incidence.h"
+#include "scheduler.h"
+
+#include <kapplication.h>
+#include <kfiledialog.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kmimetype.h>
+#include <krun.h>
+#include <ktempfile.h>
+#include <kio/netaccess.h>
+
+#include <tqfile.h>
+
+namespace KCal {
+
+Attachment *AttachmentHandler::find( TQWidget *parent, const TQString &attachmentName,
+ Incidence *incidence )
+{
+ if ( !incidence ) {
+ return 0;
+ }
+
+ // get the attachment by name from the incidence
+ Attachment::List as = incidence->attachments();
+ Attachment *a = 0;
+ if ( as.count() > 0 ) {
+ Attachment::List::ConstIterator it;
+ for ( it = as.begin(); it != as.end(); ++it ) {
+ if ( (*it)->label() == attachmentName ) {
+ a = *it;
+ break;
+ }
+ }
+ }
+
+ if ( !a ) {
+ KMessageBox::error(
+ parent,
+ i18n( "No attachment named \"%1\" found in the incidence." ).arg( attachmentName ) );
+ return 0;
+ }
+
+ if ( a->isUri() ) {
+ if ( !KIO::NetAccess::exists( a->uri(), true, parent ) ) {
+ KMessageBox::sorry(
+ parent,
+ i18n( "The attachment \"%1\" is a web link that is inaccessible from this computer. " ).
+ arg( KURL::decode_string( a->uri() ) ) );
+ return 0;
+ }
+ }
+ return a;
+}
+
+Attachment *AttachmentHandler::find( TQWidget *parent,
+ const TQString &attachmentName, const TQString &uid )
+{
+ if ( uid.isEmpty() ) {
+ return 0;
+ }
+
+ CalendarResources *cal = new CalendarResources( "UTC" );
+ cal->readConfig();
+ cal->load();
+ Incidence *incidence = cal->incidence( uid );
+ if ( !incidence ) {
+ KMessageBox::error(
+ parent,
+ i18n( "The incidence that owns the attachment named \"%1\" could not be found. "
+ "Perhaps it was removed from your calendar?" ).arg( attachmentName ) );
+ return 0;
+ }
+
+ return find( parent, attachmentName, incidence );
+}
+
+Attachment *AttachmentHandler::find( TQWidget *parent, const TQString &attachmentName,
+ ScheduleMessage *message )
+{
+ if ( !message ) {
+ return 0;
+ }
+
+ Incidence *incidence = dynamic_cast<Incidence*>( message->event() );
+ if ( !incidence ) {
+ KMessageBox::error(
+ parent,
+ i18n( "The calendar invitation stored in this email message is broken in some way. "
+ "Unable to continue." ) );
+ return 0;
+ }
+
+ return find( parent, attachmentName, incidence );
+}
+
+static KTempFile *s_tempFile = 0;
+
+static KURL tempFileForAttachment( Attachment *attachment )
+{
+ KURL url;
+ TQStringList patterns = KMimeType::mimeType( attachment->mimeType() )->patterns();
+ if ( !patterns.empty() ) {
+ s_tempFile = new KTempFile( TQString::null,
+ TQString( patterns.first() ).remove( '*' ), 0600 );
+ } else {
+ s_tempFile = new KTempFile( TQString::null, TQString::null, 0600 );
+ }
+
+ TQFile *qfile = s_tempFile->file();
+ qfile->open( IO_WriteOnly );
+ TQTextStream stream( qfile );
+ stream.writeRawBytes( attachment->decodedData().data(), attachment->size() );
+ s_tempFile->close();
+ TQFile tf( s_tempFile->name() );
+ if ( tf.size() != attachment->size() ) {
+ //whoops. failed to write the entire attachment. return an invalid URL.
+ delete s_tempFile;
+ s_tempFile = 0;
+ return url;
+ }
+
+ url.setPath( s_tempFile->name() );
+ return url;
+}
+
+bool AttachmentHandler::view( TQWidget *parent, Attachment *attachment )
+{
+ if ( !attachment ) {
+ return false;
+ }
+
+ bool stat = true;
+ if ( attachment->isUri() ) {
+ kapp->invokeBrowser( attachment->uri() );
+ } else {
+ // put the attachment in a temporary file and launch it
+ KURL tempUrl = tempFileForAttachment( attachment );
+ if ( tempUrl.isValid() ) {
+ stat = KRun::runURL( tempUrl, attachment->mimeType(), false, true );
+ } else {
+ stat = false;
+ KMessageBox::error(
+ parent,
+ i18n( "Unable to create a temporary file for the attachment." ) );
+ }
+ delete s_tempFile;
+ s_tempFile = 0;
+ }
+ return stat;
+}
+
+bool AttachmentHandler::view( TQWidget *parent, const TQString &attachmentName, Incidence *incidence )
+{
+ return view( parent, find( parent, attachmentName, incidence ) );
+}
+
+bool AttachmentHandler::view( TQWidget *parent, const TQString &attachmentName, const TQString &uid )
+{
+ return view( parent, find( parent, attachmentName, uid ) );
+}
+
+bool AttachmentHandler::view( TQWidget *parent, const TQString &attachmentName,
+ ScheduleMessage *message )
+{
+ return view( parent, find( parent, attachmentName, message ) );
+}
+
+bool AttachmentHandler::saveAs( TQWidget *parent, Attachment *attachment )
+{
+ // get the saveas file name
+ TQString saveAsFile = KFileDialog::getSaveFileName( attachment->label(), TQString::null, parent,
+ i18n( "Save Attachment" ) );
+ if ( saveAsFile.isEmpty() ||
+ ( TQFile( saveAsFile ).exists() &&
+ ( KMessageBox::warningYesNo(
+ parent,
+ i18n( "%1 already exists. Do you want to overwrite it?").
+ arg( saveAsFile ) ) == KMessageBox::No ) ) ) {
+ return false;
+ }
+
+ bool stat = false;
+ if ( attachment->isUri() ) {
+ // save the attachment url
+ stat = KIO::NetAccess::file_copy( attachment->uri(), KURL( saveAsFile ), -1, true );
+ } else {
+ // put the attachment in a temporary file and save it
+ KURL tempUrl = tempFileForAttachment( attachment );
+ if ( tempUrl.isValid() ) {
+ stat = KIO::NetAccess::file_copy( tempUrl, KURL( saveAsFile ), -1, true );
+ if ( !stat && KIO::NetAccess::lastError() ) {
+ KMessageBox::error( parent, KIO::NetAccess::lastErrorString() );
+ }
+ } else {
+ stat = false;
+ KMessageBox::error(
+ parent,
+ i18n( "Unable to create a temporary file for the attachment." ) );
+ }
+ delete s_tempFile;
+ s_tempFile = 0;
+ }
+ return stat;
+}
+
+bool AttachmentHandler::saveAs( TQWidget *parent, const TQString &attachmentName,
+ Incidence *incidence )
+{
+ return saveAs( parent, find( parent, attachmentName, incidence ) );
+}
+
+bool AttachmentHandler::saveAs( TQWidget *parent, const TQString &attachmentName, const TQString &uid )
+{
+ return saveAs( parent, find( parent, attachmentName, uid ) );
+}
+
+bool AttachmentHandler::saveAs( TQWidget *parent, const TQString &attachmentName,
+ ScheduleMessage *message )
+{
+ return saveAs( parent, find( parent, attachmentName, message ) );
+}
+
+}
+
diff --git a/libkcal/attachmenthandler.h b/libkcal/attachmenthandler.h
new file mode 100644
index 00000000..6116f15a
--- /dev/null
+++ b/libkcal/attachmenthandler.h
@@ -0,0 +1,178 @@
+/*
+ This file is part of the kcal library.
+
+ Copyright (c) 2010 Klar�lvdalens Datakonsult AB, a KDAB Group company <info@kdab.net>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/**
+ @file
+ This file is part of the API for handling calendar data and provides
+ static functions for dealing with calendar incidence attachments.
+
+ @author Allen Winter \<winter@kde.org\>
+*/
+
+#ifndef KCAL_ATTACHMENTHANDLER_H
+#define KCAL_ATTACHMENTHANDLER_H
+
+class TQString;
+class TQWidget;
+
+namespace KCal {
+
+class Attachment;
+class Incidence;
+class ScheduleMessage;
+
+/**
+ @brief
+ Provides methods to handle incidence attachments.
+
+ Includes functions to view and save attachments.
+*/
+namespace AttachmentHandler {
+
+ /**
+ Finds the attachment in the user's calendar, by @p attachmentName and @p incidence.
+
+ @param parent is the parent widget for the dialogs used in this function.
+ @param attachmentName is the name of the attachment
+ @param incidence is a pointer to a valid Incidence object containing the attachment.
+
+ @return a pointer to the Attachment object located; 0 if no such attachment could be found.
+ */
+ Attachment *find( TQWidget *parent, const TQString &attachmentName, Incidence *incidence );
+
+ /**
+ Finds the attachment in the user's calendar, by @p attachmentName and a scheduler message;
+ in other words, this function is intended to retrieve attachments from calendar invitations.
+
+ @param parent is the parent widget for the dialogs used in this function.
+ @param attachmentName is the name of the attachment
+ @param message is a pointer to a valid ScheduleMessage object containing the attachment.
+
+ @return a pointer to the Attachment object located; 0 if no such attachment could be found.
+ */
+ Attachment *find( TQWidget *parent, const TQString &attachmentName, ScheduleMessage *message );
+
+ /**
+ Finds the attachment in the user's calendar, by @p attachmentName and @p uid.
+
+ @param parent is the parent widget for the dialogs used in this function.
+ @param attachmentName is the name of the attachment
+ @param uid is a TQString containing a UID of the incidence containing the attachment.
+
+ @return a pointer to the Attachment object located; 0 if no such attachment could be found.
+ */
+ Attachment *find( TQWidget *parent, const TQString &attachmentName, const TQString &uid );
+
+ /**
+ Launches a viewer on the specified attachment.
+
+ @param parent is the parent widget for the dialogs used in this function.
+ @param attachment is a pointer to a valid Attachment object.
+
+ @return true if the viewer program successfully launched; false otherwise.
+ */
+ bool view( TQWidget *parent, Attachment *attachment );
+
+ /**
+ Launches a viewer on the specified attachment.
+
+ @param parent is the parent widget for the dialogs used in this function.
+ @param attachmentName is the name of the attachment
+ @param incidence is a pointer to a valid Incidence object containing the attachment.
+
+ @return true if the attachment could be found and the viewer program successfully launched;
+ false otherwise.
+ */
+ bool view( TQWidget *parent, const TQString &attachmentName, Incidence *incidence );
+
+ /**
+ Launches a viewer on the specified attachment.
+
+ @param parent is the parent widget for the dialogs used in this function.
+ @param attachmentName is the name of the attachment
+ @param uid is a TQString containing a UID of the incidence containing the attachment.
+
+ @return true if the attachment could be found and the viewer program successfully launched;
+ false otherwise.
+ */
+ bool view( TQWidget *parent, const TQString &attachmentName, const TQString &uid );
+
+ /**
+ Launches a viewer on the specified attachment.
+
+ @param parent is the parent widget for the dialogs used in this function.
+ @param attachmentName is the name of the attachment
+ @param message is a pointer to a valid ScheduleMessage object containing the attachment.
+
+ @return true if the attachment could be found and the viewer program successfully launched;
+ false otherwise.
+ */
+ bool view( TQWidget *parent, const TQString &attachmentName, ScheduleMessage *message );
+
+ /**
+ Saves the specified attachment to a file of the user's choice.
+
+ @param parent is the parent widget for the dialogs used in this function.
+ @param attachment is a pointer to a valid Attachment object.
+
+ @return true if the save operation was successful; false otherwise.
+ */
+ bool saveAs( TQWidget *parent, Attachment *attachment );
+
+ /**
+ Saves the specified attachment to a file of the user's choice.
+
+ @param parent is the parent widget for the dialogs used in this function.
+ @param attachmentName is the name of the attachment
+ @param incidence is a pointer to a valid Incidence object containing the attachment.
+
+ @return true if the attachment could be found and the save operation was successful;
+ false otherwise.
+ */
+ bool saveAs( TQWidget *parent, const TQString &attachmentName, Incidence *incidence );
+
+ /**
+ Saves the specified attachment to a file of the user's choice.
+
+ @param parent is the parent widget for the dialogs used in this function.
+ @param attachmentName is the name of the attachment
+ @param uid is a TQString containing a UID of the incidence containing the attachment.
+
+ @return true if the attachment could be found and the save operation was successful;
+ false otherwise.
+ */
+ bool saveAs( TQWidget *parent, const TQString &attachmentName, const TQString &uid );
+
+ /**
+ Saves the specified attachment to a file of the user's choice.
+
+ @param parent is the parent widget for the dialogs used in this function.
+ @param attachmentName is the name of the attachment
+ @param message is a pointer to a valid ScheduleMessage object containing the attachment.
+
+ @return true if the attachment could be found and the save operation was successful;
+ false otherwise.
+ */
+ bool saveAs( TQWidget *parent, const TQString &attachmentName, ScheduleMessage *message );
+}
+
+}
+
+#endif
diff --git a/libkcal/attendee.cpp b/libkcal/attendee.cpp
index 3cb2afed..9aa3d46f 100644
--- a/libkcal/attendee.cpp
+++ b/libkcal/attendee.cpp
@@ -93,6 +93,9 @@ TQString Attendee::statusName( Attendee::PartStat s )
case InProcess:
return i18n("In Process");
break;
+ case None:
+ return i18n("attendee status unknown", "Unknown");
+ break;
}
}
diff --git a/libkcal/attendee.h b/libkcal/attendee.h
index f460665b..161c77ed 100644
--- a/libkcal/attendee.h
+++ b/libkcal/attendee.h
@@ -37,7 +37,7 @@ class LIBKCAL_EXPORT Attendee : public Person
{
public:
enum PartStat { NeedsAction, Accepted, Declined, Tentative,
- Delegated, Completed, InProcess };
+ Delegated, Completed, InProcess, None };
enum Role { ReqParticipant, OptParticipant, NonParticipant, Chair };
typedef ListBase<Attendee> List;
@@ -53,7 +53,7 @@ class LIBKCAL_EXPORT Attendee : public Person
@param u the uid for the attendee
*/
Attendee( const TQString &name, const TQString &email,
- bool rsvp = false, PartStat status = NeedsAction,
+ bool rsvp = false, PartStat status = None,
Role role = ReqParticipant, const TQString &u = TQString::null );
/**
Destruct Attendee.
diff --git a/libkcal/calendar.cpp b/libkcal/calendar.cpp
index c3c3a9e6..711cdc12 100644
--- a/libkcal/calendar.cpp
+++ b/libkcal/calendar.cpp
@@ -50,6 +50,7 @@ Calendar::Calendar( const TQString &timeZoneId )
void Calendar::init()
{
+ mException = 0;
mNewObserver = false;
mObserversEnabled = true;
@@ -66,9 +67,27 @@ void Calendar::init()
Calendar::~Calendar()
{
+ clearException();
delete mDefaultFilter;
}
+void Calendar::clearException()
+{
+ delete mException;
+ mException = 0;
+}
+
+ErrorFormat *Calendar::exception() const
+{
+ return mException;
+}
+
+void Calendar::setException( ErrorFormat *e )
+{
+ delete mException;
+ mException = e;
+}
+
const Person &Calendar::getOwner() const
{
return mOwner;
@@ -122,6 +141,16 @@ CalFilter *Calendar::filter()
return mFilter;
}
+void Calendar::beginBatchAdding()
+{
+ emit batchAddingBegins();
+}
+
+void Calendar::endBatchAdding()
+{
+ emit batchAddingEnds();
+}
+
TQStringList Calendar::categories()
{
Incidence::List rawInc( rawIncidences() );
@@ -161,7 +190,7 @@ Event::List Calendar::sortEvents( Event::List *eventList,
SortDirection sortDirection )
{
Event::List eventListSorted;
- Event::List tempList, t;
+ Event::List tempList;
Event::List alphaList;
Event::List::Iterator sortIt;
Event::List::Iterator eit;
@@ -177,6 +206,10 @@ Event::List Calendar::sortEvents( Event::List *eventList,
case EventSortStartDate:
alphaList = sortEvents( eventList, EventSortSummary, sortDirection );
for ( eit = alphaList.begin(); eit != alphaList.end(); ++eit ) {
+ if ( (*eit)->doesFloat() ) {
+ tempList.append( *eit );
+ continue;
+ }
sortIt = eventListSorted.begin();
if ( sortDirection == SortDirectionAscending ) {
while ( sortIt != eventListSorted.end() &&
@@ -191,6 +224,14 @@ Event::List Calendar::sortEvents( Event::List *eventList,
}
eventListSorted.insert( sortIt, *eit );
}
+ if ( sortDirection == SortDirectionAscending ) {
+ // Prepend the list of all-day Events
+ tempList += eventListSorted;
+ eventListSorted = tempList;
+ } else {
+ // Append the list of all-day Events
+ eventListSorted += tempList;
+ }
break;
case EventSortEndDate:
@@ -245,7 +286,149 @@ Event::List Calendar::sortEvents( Event::List *eventList,
}
return eventListSorted;
+}
+
+Event::List Calendar::sortEventsForDate( Event::List *eventList,
+ const TQDate &date,
+ EventSortField sortField,
+ SortDirection sortDirection )
+{
+ Event::List eventListSorted;
+ Event::List tempList;
+ Event::List alphaList;
+ Event::List::Iterator sortIt;
+ Event::List::Iterator eit;
+
+ switch( sortField ) {
+ case EventSortStartDate:
+ alphaList = sortEvents( eventList, EventSortSummary, sortDirection );
+ for ( eit = alphaList.begin(); eit != alphaList.end(); ++eit ) {
+ if ( (*eit)->doesFloat() ) {
+ tempList.append( *eit );
+ continue;
+ }
+ sortIt = eventListSorted.begin();
+ if ( sortDirection == SortDirectionAscending ) {
+ while ( sortIt != eventListSorted.end() ) {
+ if ( !(*eit)->doesRecur() ) {
+ if ( (*eit)->dtStart().time() >= (*sortIt)->dtStart().time() ) {
+ ++sortIt;
+ } else {
+ break;
+ }
+ } else {
+ if ( (*eit)->recursOn( date ) ) {
+ if ( (*eit)->dtStart().time() >= (*sortIt)->dtStart().time() ) {
+ ++sortIt;
+ } else {
+ break;
+ }
+ } else {
+ ++sortIt;
+ }
+ }
+ }
+ } else { // descending
+ while ( sortIt != eventListSorted.end() ) {
+ if ( !(*eit)->doesRecur() ) {
+ if ( (*eit)->dtStart().time() < (*sortIt)->dtStart().time() ) {
+ ++sortIt;
+ } else {
+ break;
+ }
+ } else {
+ if ( (*eit)->recursOn( date ) ) {
+ if ( (*eit)->dtStart().time() < (*sortIt)->dtStart().time() ) {
+ ++sortIt;
+ } else {
+ break;
+ }
+ } else {
+ ++sortIt;
+ }
+ }
+ }
+ }
+ eventListSorted.insert( sortIt, *eit );
+ }
+ if ( sortDirection == SortDirectionAscending ) {
+ // Prepend the list of all-day Events
+ tempList += eventListSorted;
+ eventListSorted = tempList;
+ } else {
+ // Append the list of all-day Events
+ eventListSorted += tempList;
+ }
+ break;
+ case EventSortEndDate:
+ alphaList = sortEvents( eventList, EventSortSummary, sortDirection );
+ for ( eit = alphaList.begin(); eit != alphaList.end(); ++eit ) {
+ if ( (*eit)->hasEndDate() ) {
+ sortIt = eventListSorted.begin();
+ if ( sortDirection == SortDirectionAscending ) {
+ while ( sortIt != eventListSorted.end() ) {
+ if ( !(*eit)->doesRecur() ) {
+ if ( (*eit)->dtEnd().time() >= (*sortIt)->dtEnd().time() ) {
+ ++sortIt;
+ } else {
+ break;
+ }
+ } else {
+ if ( (*eit)->recursOn( date ) ) {
+ if ( (*eit)->dtEnd().time() >= (*sortIt)->dtEnd().time() ) {
+ ++sortIt;
+ } else {
+ break;
+ }
+ } else {
+ ++sortIt;
+ }
+ }
+ }
+ } else { // descending
+ while ( sortIt != eventListSorted.end() ) {
+ if ( !(*eit)->doesRecur() ) {
+ if ( (*eit)->dtEnd().time() < (*sortIt)->dtEnd().time() ) {
+ ++sortIt;
+ } else {
+ break;
+ }
+ } else {
+ if ( (*eit)->recursOn( date ) ) {
+ if ( (*eit)->dtEnd().time() < (*sortIt)->dtEnd().time() ) {
+ ++sortIt;
+ } else {
+ break;
+ }
+ } else {
+ ++sortIt;
+ }
+ }
+ }
+ }
+ } else {
+ // Keep a list of the Events without End DateTimes
+ tempList.append( *eit );
+ }
+ eventListSorted.insert( sortIt, *eit );
+ }
+ if ( sortDirection == SortDirectionAscending ) {
+ // Prepend the list of Events without End DateTimes
+ tempList += eventListSorted;
+ eventListSorted = tempList;
+ } else {
+ // Append the list of Events without End DateTimes
+ eventListSorted += tempList;
+ }
+ break;
+
+ default:
+ eventListSorted = sortEvents( eventList, sortField, sortDirection );
+ break;
+ }
+
+ return eventListSorted;
}
Event::List Calendar::events( const TQDate &date,
diff --git a/libkcal/calendar.h b/libkcal/calendar.h
index a53ef5a0..005b513e 100644
--- a/libkcal/calendar.h
+++ b/libkcal/calendar.h
@@ -31,13 +31,7 @@
#ifndef KCAL_CALENDAR_H
#define KCAL_CALENDAR_H
-#include <tqobject.h>
-#include <tqstring.h>
-#include <tqdatetime.h>
-#include <tqptrlist.h>
-#include <tqdict.h>
-#include <kdepimmacros.h>
-
+#include "exceptions.h"
#include "customproperties.h"
#include "event.h"
#include "todo.h"
@@ -45,6 +39,14 @@
#include "kcalversion.h"
#include "person.h"
+#include <kdepimmacros.h>
+
+#include <tqobject.h>
+#include <tqstring.h>
+#include <tqdatetime.h>
+#include <tqptrlist.h>
+#include <tqdict.h>
+
/**
@namespace KCal
Namespace KCal is for global classes, objects and/or functions in libkcal.
@@ -207,6 +209,17 @@ class LIBKCAL_EXPORT Calendar : public TQObject, public CustomProperties,
TQString productId();
/**
+ Clears the exception status.
+ */
+ void clearException();
+
+ /**
+ Returns an exception, if there is any, containing information about the
+ last error that occurred.
+ */
+ ErrorFormat *exception() const;
+
+ /**
Set the owner of the Calendar.
@param owner is a Person object.
@@ -472,6 +485,22 @@ class LIBKCAL_EXPORT Calendar : public TQObject, public CustomProperties,
static Event::List sortEvents( Event::List *eventList,
EventSortField sortField,
SortDirection sortDirection );
+
+ /**
+ Sort a list of Events that occur on a specified date.
+
+ @param eventList is a pointer to a list of Events occurring on @p date.
+ @param date is the date.
+ @param sortField specifies the EventSortField.
+ @param sortDirection specifies the SortDirection.
+
+ @return a list of Events sorted as specified.
+ */
+ static Event::List sortEventsForDate( Event::List *eventList,
+ const TQDate &date,
+ EventSortField sortField,
+ SortDirection sortDirection );
+
/**
Return a sorted, filtered list of all Events for this Calendar.
@@ -755,6 +784,30 @@ class LIBKCAL_EXPORT Calendar : public TQObject, public CustomProperties,
*/
virtual Journal *journal( const TQString &uid ) = 0;
+ /**
+ Emits the beginBatchAdding() signal.
+
+ This should be called before adding a batch of incidences with
+ addIncidence( Incidence *), addTodo( Todo *), addEvent( Event *)
+ or addJournal( Journal *). Some Calendars are connected to this
+ signal, e.g: CalendarResources uses it to know a series of
+ incidenceAdds are related so the user isn't prompted multiple
+ times which resource to save the incidence to
+
+ @since 4.4
+ */
+ void beginBatchAdding();
+
+ /**
+ Emits the endBatchAdding() signal.
+
+ Used with beginBatchAdding(). Should be called after
+ adding all incidences.
+
+ @since 4.4
+ */
+ void endBatchAdding();
+
// Relations Specific Methods //
/**
@@ -879,8 +932,26 @@ class LIBKCAL_EXPORT Calendar : public TQObject, public CustomProperties,
*/
void calendarLoaded();
+ /**
+ @see beginBatchAdding()
+ @since 4.4
+ */
+ void batchAddingBegins();
+
+ /**
+ @see endBatchAdding()
+ @since 4.4
+ */
+ void batchAddingEnds();
+
protected:
/**
+ Sets information about the last error occurred.
+ The previous exception is freed.
+ */
+ void setException( ErrorFormat *e );
+
+ /**
The Observer interface. So far not implemented.
@param incidenceBase is a pointer an IncidenceBase object.
@@ -941,6 +1012,7 @@ class LIBKCAL_EXPORT Calendar : public TQObject, public CustomProperties,
// returning static Alarm::List
private:
+
/**
Intialize a Calendar object with starting values.
*/
@@ -964,6 +1036,7 @@ class LIBKCAL_EXPORT Calendar : public TQObject, public CustomProperties,
TQDict<Incidence> mOrphans;
TQDict<Incidence> mOrphanUids;
+ ErrorFormat *mException;
class Private;
Private *d;
};
diff --git a/libkcal/calendarlocal.cpp b/libkcal/calendarlocal.cpp
index 27b27e0e..2f2d7739 100644
--- a/libkcal/calendarlocal.cpp
+++ b/libkcal/calendarlocal.cpp
@@ -327,7 +327,12 @@ Alarm::List CalendarLocal::alarms( const TQDateTime &from, const TQDateTime &to
Todo::List::ConstIterator it2;
for( it2 = mTodoList.begin(); it2 != mTodoList.end(); ++it2 ) {
- if (! (*it2)->isCompleted() ) appendAlarms( alarms, *it2, from, to );
+ Todo *t = *it2;
+ if ( t->isCompleted() ) {
+ continue;
+ }
+ if ( t->doesRecur() ) appendRecurringAlarms( alarms, t, from, to );
+ else appendAlarms( alarms, t, from, to );
}
return alarms;
@@ -340,13 +345,14 @@ void CalendarLocal::appendAlarms( Alarm::List &alarms, Incidence *incidence,
Alarm::List::ConstIterator it;
for( it = incidence->alarms().begin(); it != incidence->alarms().end();
++it ) {
- if ( (*it)->enabled() ) {
- TQDateTime dt = (*it)->nextRepetition(preTime);
+ Alarm *alarm = *it;
+ if ( alarm->enabled() ) {
+ TQDateTime dt = alarm->nextRepetition( preTime );
if ( dt.isValid() && dt <= to ) {
kdDebug(5800) << "CalendarLocal::appendAlarms() '"
<< incidence->summary() << "': "
<< dt.toString() << endl;
- alarms.append( *it );
+ alarms.append( alarm );
}
}
}
@@ -357,10 +363,14 @@ void CalendarLocal::appendRecurringAlarms( Alarm::List &alarms,
const TQDateTime &from,
const TQDateTime &to )
{
- TQDateTime qdt;
- int endOffset = 0;
+ TQDateTime dt;
+ Duration endOffset( 0 );
bool endOffsetValid = false;
- int period = from.secsTo(to);
+ Duration period( from, to );
+
+ Event *e = static_cast<Event *>( incidence );
+ Todo *t = static_cast<Todo *>( incidence );
+
Alarm::List::ConstIterator it;
for( it = incidence->alarms().begin(); it != incidence->alarms().end();
++it ) {
@@ -368,59 +378,118 @@ void CalendarLocal::appendRecurringAlarms( Alarm::List &alarms,
if ( alarm->enabled() ) {
if ( alarm->hasTime() ) {
// The alarm time is defined as an absolute date/time
- qdt = alarm->nextRepetition( from.addSecs(-1) );
- if ( !qdt.isValid() || qdt > to )
+ dt = alarm->nextRepetition( from.addSecs(-1) );
+ if ( !dt.isValid() || dt > to ) {
continue;
+ }
} else {
- // The alarm time is defined by an offset from the event start or end time.
+ // Alarm time is defined by an offset from the event start or end time.
// Find the offset from the event start time, which is also used as the
// offset from the recurrence time.
- int offset = 0;
+ Duration offset( 0 );
if ( alarm->hasStartOffset() ) {
offset = alarm->startOffset().asSeconds();
} else if ( alarm->hasEndOffset() ) {
+ offset = alarm->endOffset().asSeconds();
if ( !endOffsetValid ) {
- endOffset = incidence->dtStart().secsTo( incidence->dtEnd() );
- endOffsetValid = true;
+ if ( incidence->type() == "Event" ) {
+ endOffset = Duration( e->dtStart(), e->dtEnd() );
+ endOffsetValid = true;
+ } else if ( incidence->type() == "Todo" &&
+ t->hasStartDate() && t->hasDueDate() ) {
+ endOffset = Duration( t->dtStart(), t->dtDue() );
+ endOffsetValid = true;
+ }
}
- offset = alarm->endOffset().asSeconds() + endOffset;
}
- // Adjust the 'from' date/time and find the next recurrence at or after it
- qdt = incidence->recurrence()->getNextDateTime( from.addSecs(-offset - 1) );
- if ( !qdt.isValid()
- || (qdt = qdt.addSecs( offset )) > to ) // remove the adjustment to get the alarm time
+ // Find the incidence's earliest alarm
+ TQDateTime alarmStart;
+ if ( incidence->type() == "Event" ) {
+ alarmStart =
+ offset.end( alarm->hasEndOffset() ? e->dtEnd() : e->dtStart() );
+ } else if ( incidence->type() == "Todo" ) {
+ alarmStart =
+ offset.end( alarm->hasEndOffset() ? t->dtDue() : t->dtStart() );
+ }
+
+ if ( alarmStart.isValid() && alarmStart > to ) {
+ continue;
+ }
+
+ TQDateTime baseStart;
+ if ( incidence->type() == "Event" ) {
+ baseStart = e->dtStart();
+ } else if ( incidence->type() == "Todo" ) {
+ baseStart = t->dtDue();
+ }
+ if ( alarmStart.isValid() && from > alarmStart ) {
+ alarmStart = from; // don't look earlier than the earliest alarm
+ baseStart = (-offset).end( (-endOffset).end( alarmStart ) );
+ }
+
+ // Adjust the 'alarmStart' date/time and find the next recurrence
+ // at or after it. Treat the two offsets separately in case one
+ // is daily and the other not.
+ dt = incidence->recurrence()->getNextDateTime( baseStart.addSecs(-1) );
+ if ( !dt.isValid() ||
+ ( dt = endOffset.end( offset.end( dt ) ) ) > to ) // adjust 'dt' to get the alarm time
{
// The next recurrence is too late.
- if ( !alarm->repeatCount() )
+ if ( !alarm->repeatCount() ) {
continue;
- // The alarm has repetitions, so check whether repetitions of previous
- // recurrences fall within the time period.
+ }
+
+ // The alarm has repetitions, so check whether repetitions of
+ // previous recurrences fall within the time period.
bool found = false;
- qdt = from.addSecs( -offset );
- while ( (qdt = incidence->recurrence()->getPreviousDateTime( qdt )).isValid() ) {
- int toFrom = qdt.secsTo( from ) - offset;
- if ( toFrom > alarm->duration() )
- break; // this recurrence's last repetition is too early, so give up
- // The last repetition of this recurrence is at or after 'from' time.
- // Check if a repetition occurs between 'from' and 'to'.
- int snooze = alarm->snoozeTime() * 60; // in seconds
- if ( period >= snooze
- || toFrom % snooze == 0
- || (toFrom / snooze + 1) * snooze <= toFrom + period ) {
- found = true;
+ Duration alarmDuration = alarm->duration();
+ for ( TQDateTime base = baseStart;
+ ( dt = incidence->recurrence()->getPreviousDateTime( base ) ).isValid();
+ base = dt ) {
+ if ( alarm->duration().end( dt ) < base ) {
+ break; // recurrence's last repetition is too early, so give up
+ }
+
+ // The last repetition of this recurrence is on or after
+ // 'alarmStart' time. Check if a repetition occurs between
+ // 'alarmStart' and 'to'.
+ int snooze = alarm->snoozeTime().value(); // in seconds or days
+ if ( alarm->snoozeTime().isDaily() ) {
+ Duration toFromDuration( dt, base );
+ int toFrom = toFromDuration.asDays();
+ if ( alarm->snoozeTime().end( from ) <= to ||
+ ( toFromDuration.isDaily() && toFrom % snooze == 0 ) ||
+ ( toFrom / snooze + 1 ) * snooze <= toFrom + period.asDays() ) {
+ found = true;
+#ifndef NDEBUG
+ // for debug output
+ dt = offset.end( dt ).addDays( ( ( toFrom - 1 ) / snooze + 1 ) * snooze );
+#endif
+ break;
+ }
+ } else {
+ int toFrom = dt.secsTo( base );
+ if ( period.asSeconds() >= snooze ||
+ toFrom % snooze == 0 ||
+ ( toFrom / snooze + 1 ) * snooze <= toFrom + period.asSeconds() )
+ {
+ found = true;
#ifndef NDEBUG
- qdt = qdt.addSecs( offset + ((toFrom-1) / snooze + 1) * snooze ); // for debug output
+ // for debug output
+ dt = offset.end( dt ).addSecs( ( ( toFrom - 1 ) / snooze + 1 ) * snooze );
#endif
- break;
+ break;
+ }
}
}
- if ( !found )
+ if ( !found ) {
continue;
+ }
}
}
kdDebug(5800) << "CalendarLocal::appendAlarms() '" << incidence->summary()
- << "': " << qdt.toString() << endl;
+ << "': " << dt.toString() << endl;
alarms.append( alarm );
}
}
@@ -485,7 +554,7 @@ Event::List CalendarLocal::rawEventsForDate( const TQDate &qd,
}
}
- return sortEvents( &eventList, sortField, sortDirection );
+ return sortEventsForDate( &eventList, qd, sortField, sortDirection );
}
Event::List CalendarLocal::rawEvents( const TQDate &start, const TQDate &end,
diff --git a/libkcal/calendarresources.cpp b/libkcal/calendarresources.cpp
index 42f618dc..abd1cc21 100644
--- a/libkcal/calendarresources.cpp
+++ b/libkcal/calendarresources.cpp
@@ -55,6 +55,35 @@
using namespace KCal;
+
+class CalendarResources::Private {
+ public:
+
+ Private() : mLastUsedResource( 0 ), mBatchAddingInProgress( false )
+ {
+ }
+
+ ResourceCalendar *mLastUsedResource;
+ bool mBatchAddingInProgress;
+};
+
+bool CalendarResources::DestinationPolicy::hasCalendarResources( )
+{
+ CalendarResourceManager::ActiveIterator it;
+ for ( it = resourceManager()->activeBegin();
+ it != resourceManager()->activeEnd(); ++it ) {
+ if ( !(*it)->readOnly() ) {
+ //Insert the first the Standard resource to get be the default selected.
+ if ( resourceManager()->standardResource() == *it ) {
+ return true;
+ } else {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
ResourceCalendar
*CalendarResources::StandardDestinationPolicy::destination( Incidence * )
{
@@ -85,7 +114,7 @@ ResourceCalendar
CalendarResources::CalendarResources( const TQString &timeZoneId,
const TQString &family )
- : Calendar( timeZoneId )
+ : Calendar( timeZoneId ), d( new Private() )
{
init( family );
}
@@ -100,6 +129,10 @@ void CalendarResources::init( const TQString &family )
mStandardPolicy = new StandardDestinationPolicy( mManager );
mAskPolicy = new AskDestinationPolicy( mManager );
mDestinationPolicy = mStandardPolicy;
+ mPendingDeleteFromResourceMap = false;
+
+ connect( this, TQT_SIGNAL(batchAddingBegins()), this, TQT_SLOT(beginAddingIncidences()) );
+ connect( this, TQT_SIGNAL(batchAddingEnds()), this, TQT_SLOT(endAddingIncidences()) );
}
CalendarResources::~CalendarResources()
@@ -158,6 +191,7 @@ void CalendarResources::load()
}
mOpen = true;
+ emit calendarLoaded();
}
bool CalendarResources::reload( const TQString &tz )
@@ -276,7 +310,8 @@ bool CalendarResources::isSaving()
}
bool CalendarResources::addIncidence( Incidence *incidence,
- ResourceCalendar *resource )
+ ResourceCalendar *resource,
+ const TQString &subresource )
{
// FIXME: Use proper locking via begin/endChange!
bool validRes = false;
@@ -285,52 +320,90 @@ bool CalendarResources::addIncidence( Incidence *incidence,
if ( (*it) == resource )
validRes = true;
}
+
+ kdDebug(5800)<< "CalendarResources: validRes is " << validRes << endl;
+
ResourceCalendar *oldResource = 0;
if ( mResourceMap.contains( incidence ) ) {
oldResource = mResourceMap[incidence];
}
mResourceMap[incidence] = resource;
- if ( validRes && beginChange( incidence ) &&
- resource->addIncidence( incidence ) ) {
-// mResourceMap[incidence] = resource;
+ if ( validRes && beginChange( incidence, resource, subresource ) &&
+ resource->addIncidence( incidence, subresource ) ) {
+ // mResourceMap[incidence] = resource;
incidence->registerObserver( this );
notifyIncidenceAdded( incidence );
setModified( true );
- endChange( incidence );
+ endChange( incidence, resource, subresource );
return true;
} else {
- if ( oldResource )
+ if ( oldResource ) {
mResourceMap[incidence] = oldResource;
- else
+ } else {
mResourceMap.remove( incidence );
+ }
}
return false;
}
+bool CalendarResources::addIncidence( Incidence *incidence,
+ ResourceCalendar *resource )
+{
+ return addIncidence( incidence, resource, TQString() );
+}
+
bool CalendarResources::addIncidence( Incidence *incidence )
{
- kdDebug(5800) << "CalendarResources::addIncidence" << this << endl;
+ kdDebug(5800) << "CalendarResources::addIncidence "
+ << incidence->summary()
+ << "; addingInProgress = " << d->mBatchAddingInProgress
+ << "; lastUsedResource = " << d->mLastUsedResource
+ << endl;
- ResourceCalendar *resource = mDestinationPolicy->destination( incidence );
+ clearException();
- if ( resource ) {
- mResourceMap[ incidence ] = resource;
+ ResourceCalendar *resource = d->mLastUsedResource;
+
+ if ( !d->mBatchAddingInProgress || d->mLastUsedResource == 0 ) {
+ resource = mDestinationPolicy->destination( incidence );
+ d->mLastUsedResource = resource;
+
+ if ( resource && d->mBatchAddingInProgress ) {
+ d->mLastUsedResource->beginAddingIncidences();
+ }
+ }
- if ( beginChange( incidence ) && resource->addIncidence( incidence ) ) {
+ if ( resource ) {
+ kdDebug(5800) << "CalendarResources:: resource= "
+ << resource->resourceName()
+ << " with id = " << resource->identifier()
+ << " with type = " << resource->type()
+ << endl;
+ mResourceMap[incidence] = resource;
+
+ if ( beginChange( incidence, resource, TQString() ) &&
+ resource->addIncidence( incidence ) ) {
incidence->registerObserver( this );
notifyIncidenceAdded( incidence );
-
mResourceMap[ incidence ] = resource;
setModified( true );
- endChange( incidence );
+ endChange( incidence, resource, TQString() );
return true;
} else {
+ if ( resource->exception() ) {
+ setException( new ErrorFormat( resource->exception()->errorCode() ) );
+ }
+
+ // the incidence isn't going to be added, do cleanup:
mResourceMap.remove( incidence );
+ d->mLastUsedResource->endAddingIncidences();
+ d->mLastUsedResource = 0;
}
- } else
- kdDebug(5800) << "CalendarResources::addIncidence(): no resource" << endl;
+ } else {
+ setException( new ErrorFormat( ErrorFormat::UserCancel ) );
+ }
return false;
}
@@ -343,7 +416,13 @@ bool CalendarResources::addEvent( Event *event )
bool CalendarResources::addEvent( Event *Event, ResourceCalendar *resource )
{
- return addIncidence( Event, resource );
+ return addIncidence( Event, resource, TQString() );
+}
+
+bool CalendarResources::addEvent( Event *Event, ResourceCalendar *resource,
+ const TQString &subresource )
+{
+ return addIncidence( Event, resource, subresource );
}
bool CalendarResources::deleteEvent( Event *event )
@@ -354,7 +433,7 @@ bool CalendarResources::deleteEvent( Event *event )
if ( mResourceMap.find( event ) != mResourceMap.end() ) {
status = mResourceMap[event]->deleteEvent( event );
if ( status )
- mResourceMap.remove( event );
+ mPendingDeleteFromResourceMap = true;
} else {
status = false;
CalendarResourceManager::ActiveIterator it;
@@ -363,6 +442,10 @@ bool CalendarResources::deleteEvent( Event *event )
}
}
+ if ( status ) {
+ notifyIncidenceDeleted( event );
+ }
+
setModified( status );
return status;
}
@@ -390,7 +473,13 @@ bool CalendarResources::addTodo( Todo *todo )
bool CalendarResources::addTodo( Todo *todo, ResourceCalendar *resource )
{
- return addIncidence( todo, resource );
+ return addIncidence( todo, resource, TQString() );
+}
+
+bool CalendarResources::addTodo( Todo *todo, ResourceCalendar *resource,
+ const TQString &subresource )
+{
+ return addIncidence( todo, resource, subresource );
}
bool CalendarResources::deleteTodo( Todo *todo )
@@ -401,7 +490,7 @@ bool CalendarResources::deleteTodo( Todo *todo )
if ( mResourceMap.find( todo ) != mResourceMap.end() ) {
status = mResourceMap[todo]->deleteTodo( todo );
if ( status )
- mResourceMap.remove( todo );
+ mPendingDeleteFromResourceMap = true;
} else {
CalendarResourceManager::ActiveIterator it;
status = false;
@@ -494,6 +583,11 @@ Alarm::List CalendarResources::alarms( const TQDateTime &from,
return result;
}
+bool CalendarResources::hasCalendarResources()
+{
+ return mDestinationPolicy->hasCalendarResources();
+}
+
/****************************** PROTECTED METHODS ****************************/
Event::List CalendarResources::rawEventsForDate( const TQDate &date,
@@ -510,7 +604,7 @@ Event::List CalendarResources::rawEventsForDate( const TQDate &date,
mResourceMap[ *it2 ] = *it;
}
}
- return sortEvents( &result, sortField, sortDirection );
+ return sortEventsForDate( &result, date, sortField, sortDirection );
}
Event::List CalendarResources::rawEvents( const TQDate &start, const TQDate &end,
@@ -574,6 +668,19 @@ bool CalendarResources::addJournal( Journal *journal )
return addIncidence( journal );
}
+bool CalendarResources::addJournal( Journal *journal, ResourceCalendar *resource )
+{
+ return addIncidence( journal, resource, TQString() );
+}
+
+
+bool CalendarResources::addJournal( Journal *journal, ResourceCalendar *resource,
+ const TQString &subresource )
+{
+ return addIncidence( journal, resource, subresource );
+}
+
+
bool CalendarResources::deleteJournal( Journal *journal )
{
kdDebug(5800) << "CalendarResources::deleteJournal" << endl;
@@ -582,7 +689,7 @@ bool CalendarResources::deleteJournal( Journal *journal )
if ( mResourceMap.find( journal ) != mResourceMap.end() ) {
status = mResourceMap[journal]->deleteJournal( journal );
if ( status )
- mResourceMap.remove( journal );
+ mPendingDeleteFromResourceMap = true;
} else {
CalendarResourceManager::ActiveIterator it;
status = false;
@@ -595,13 +702,6 @@ bool CalendarResources::deleteJournal( Journal *journal )
return status;
}
-bool CalendarResources::addJournal( Journal *journal,
- ResourceCalendar *resource
- )
-{
- return addIncidence( journal, resource );
-}
-
Journal *CalendarResources::journal( const TQString &uid )
{
kdDebug(5800) << "CalendarResources::journal(uid)" << endl;
@@ -762,28 +862,40 @@ void CalendarResources::releaseSaveTicket( Ticket *ticket )
bool CalendarResources::beginChange( Incidence *incidence )
{
+ return beginChange( incidence, 0, TQString() );
+}
+
+bool CalendarResources::beginChange( Incidence *incidence,
+ ResourceCalendar *res,
+ const TQString &subres )
+{
+ Q_UNUSED( subres ); // possible future use
+
kdDebug(5800) << "CalendarResources::beginChange()" << endl;
- ResourceCalendar *r = resource( incidence );
- if ( !r ) {
- r = mDestinationPolicy->destination( incidence );
- if ( !r ) {
+ if ( !res ) {
+ res = resource( incidence );
+ }
+ if ( !res ) {
+ res = mDestinationPolicy->destination( incidence );
+ if ( !res ) {
kdError() << "Unable to get destination resource." << endl;
return false;
}
- mResourceMap[ incidence ] = r;
+ mResourceMap[ incidence ] = res;
}
+ mPendingDeleteFromResourceMap = false;
- int count = incrementChangeCount( r );
+ int count = incrementChangeCount( res );
if ( count == 1 ) {
- Ticket *ticket = requestSaveTicket( r );
+ Ticket *ticket = requestSaveTicket( res );
if ( !ticket ) {
kdDebug(5800) << "CalendarResources::beginChange(): unable to get ticket."
<< endl;
- decrementChangeCount( r );
+ decrementChangeCount( res );
return false;
} else {
- mTickets[ r ] = ticket;
+ mTickets[ res ] = ticket;
}
}
@@ -792,18 +904,34 @@ bool CalendarResources::beginChange( Incidence *incidence )
bool CalendarResources::endChange( Incidence *incidence )
{
+ return endChange( incidence, 0, TQString() );
+}
+
+bool CalendarResources::endChange( Incidence *incidence,
+ ResourceCalendar *res,
+ const TQString &subres )
+{
+ Q_UNUSED( subres ); // possible future use
+
kdDebug(5800) << "CalendarResource::endChange()" << endl;
- ResourceCalendar *r = resource( incidence );
- if ( !r )
+ if ( !res ) {
+ res = resource( incidence );
+ }
+ if ( !res )
return false;
- int count = decrementChangeCount( r );
+ int count = decrementChangeCount( res );
+
+ if ( mPendingDeleteFromResourceMap ) {
+ mResourceMap.remove( incidence );
+ mPendingDeleteFromResourceMap = false;
+ }
if ( count == 0 ) {
- bool ok = save( mTickets[ r ], incidence );
+ bool ok = save( mTickets[ res ], incidence );
if ( ok ) {
- mTickets.remove( r );
+ mTickets.remove( res );
} else {
return false;
}
@@ -812,6 +940,24 @@ bool CalendarResources::endChange( Incidence *incidence )
return true;
}
+void CalendarResources::beginAddingIncidences()
+{
+ kdDebug(5800) << "CalendarResources: beginAddingIncidences() " << endl;
+ d->mBatchAddingInProgress = true;
+}
+
+void CalendarResources::endAddingIncidences()
+{
+ kdDebug(5800) << "CalendarResources: endAddingIncidences() " << endl;
+ d->mBatchAddingInProgress = false;
+
+ if ( d->mLastUsedResource ) {
+ d->mLastUsedResource->endAddingIncidences();
+ }
+
+ d->mLastUsedResource = 0;
+}
+
int CalendarResources::incrementChangeCount( ResourceCalendar *r )
{
if ( !mChangeCounts.contains( r ) ) {
diff --git a/libkcal/calendarresources.h b/libkcal/calendarresources.h
index 32c48ba4..a78b77bb 100644
--- a/libkcal/calendarresources.h
+++ b/libkcal/calendarresources.h
@@ -80,7 +80,7 @@ class LIBKCAL_EXPORT CalendarResources :
virtual ResourceCalendar *destination( Incidence *incidence ) = 0;
virtual TQWidget *parent() { return mParent; }
virtual void setParent( TQWidget *newparent ) { mParent = newparent; }
-
+ bool hasCalendarResources();
protected:
CalendarResourceManager *resourceManager()
{ return mManager; }
@@ -268,20 +268,20 @@ class LIBKCAL_EXPORT CalendarResources :
Resource which is queried.
*/
void setAskDestinationPolicy();
-
- /**
+
+ /**
Returns the current parent for new dialogs. This is a bad hack, but we need
to properly set the parent for the resource selection dialog. Otherwise
- the dialog will not be modal to the editor dialog in korganizer and
+ the dialog will not be modal to the editor dialog in korganizer and
the user can still work in the editor dialog (and thus crash korganizer).
- Afterwards we need to reset it (to avoid pointers to widgets that are
+ Afterwards we need to reset it (to avoid pointers to widgets that are
already deleted) so we also need the accessor
*/
TQWidget *dialogParentWidget();
- /**
+ /**
Set the widget parent for new dialogs. This is a bad hack, but we need
to properly set the parent for the resource selection dialog. Otherwise
- the dialog will not be modal to the editor dialog in korganizer and
+ the dialog will not be modal to the editor dialog in korganizer and
the user can still work in the editor dialog (and thus crash korganizer).
*/
void setDialogParentWidget( TQWidget *parent );
@@ -333,22 +333,70 @@ class LIBKCAL_EXPORT CalendarResources :
@param resource is a pointer to the ResourceCalendar to be added to.
@return true if the Incidence was successfully inserted; false otherwise.
+ @deprecated use
+ addIncidence(Incidence *,ResourceCalendar *,const TQString &) instead.
+ */
+ KDE_DEPRECATED bool addIncidence( Incidence *incidence, ResourceCalendar *resource );
+
+ /**
+ Insert an Incidence into a Calendar Resource.
+
+ @param incidence is a pointer to the Incidence to insert.
+ @param resource is a pointer to the ResourceCalendar to be added to.
+ @param subresource is the subresource name, which may not be used
+ by some calendar resources.
+
+ @return true if the Incidence was successfully inserted; false otherwise.
*/
- bool addIncidence( Incidence *incidence, ResourceCalendar *resource );
+ bool addIncidence( Incidence *incidence,
+ ResourceCalendar *resource, const TQString &subresource );
/**
Flag that a change to a Calendar Incidence is starting.
+ @param incidence is a pointer to the Incidence that will be changing.
+
+ @return false if the resource could not be computed or if a ticket
+ request fails; true otherwise.
+ */
+ KDE_DEPRECATED bool beginChange( Incidence *incidence );
+ /**
+ Flag that a change to a Calendar Incidence is starting.
@param incidence is a pointer to the Incidence that will be changing.
+ @param resource is a pointer to the ResourceCalendar that @p incidence
+ belongs to; if this is 0 then the resource is queried via the
+ DestinationPolicy.
+ @param subresource is the @p Incidence subresource name, which may not
+ be used by some calendar resources.
+
+ @return false if the resource could not be computed or if a ticket
+ request fails; true otherwise.
*/
- bool beginChange( Incidence *incidence );
+ bool beginChange( Incidence *incidence, ResourceCalendar *resource, const TQString &subresource );
/**
Flag that a change to a Calendar Incidence has completed.
+ @param incidence is a pointer to the Incidence that was changed.
+ @return false if the resource could not be computed or if a ticket
+ save fails; true otherwise.
+ */
+ KDE_DEPRECATED bool endChange( Incidence *incidence );
+
+ /**
+ Flag that a change to a Calendar Incidence has completed.
@param incidence is a pointer to the Incidence that was changed.
+ @param resource is a pointer to the ResourceCalendar that @p incidence
+ belongs to; if this is 0 then the resource is queried via the
+ DestinationPolicy.
+ @param subresource is the @p Incidence subresource name, which may not
+ be used by some calendar resources.
+
+ @return false if the resource could not be computed or if a ticket
+ save fails; true otherwise.
*/
- bool endChange( Incidence *incidence );
+ bool endChange( Incidence *incidence,
+ ResourceCalendar *resource, const TQString &subresource );
// Event Specific Methods //
@@ -372,10 +420,25 @@ class LIBKCAL_EXPORT CalendarResources :
@return true if the Event was successfully inserted; false otherwise.
+ @deprecated use
+ addIncidence(Incidence *,ResourceCalendar *,const TQString&) instead.
+ */
+ KDE_DEPRECATED bool addEvent( Event *event, ResourceCalendar *resource );
+
+ /**
+ Insert an Event into a Calendar Resource.
+
+ @param event is a pointer to the Event to insert.
+ @param resource is a pointer to the ResourceCalendar to be added to.
+ @param subresource is the subresource name, which may not be used
+ by some calendar resources.
+
+ @return true if the Event was successfully inserted; false otherwise.
+
@note In most cases use
- addIncidence( Incidence *incidence, ResourceCalendar *resource ) instead.
+ addIncidence(Incidence *,ResourceCalendar *,const TQString &) instead.
*/
- bool addEvent( Event *event, ResourceCalendar *resource );
+ bool addEvent( Event *event, ResourceCalendar *resource, const TQString &subresource );
/**
Remove an Event from the Calendar.
@@ -474,10 +537,25 @@ class LIBKCAL_EXPORT CalendarResources :
@return true if the Todo was successfully inserted; false otherwise.
+ @deprecated use
+ addIncidence(Incidence *,ResourceCalendar *,const TQString &) instead.
+ */
+ KDE_DEPRECATED bool addTodo( Todo *todo, ResourceCalendar *resource );
+
+ /**
+ Insert an Todo into a Calendar Resource.
+
+ @param todo is a pointer to the Todo to insert.
+ @param resource is a pointer to the ResourceCalendar to be added to.
+ @param subresource is the subresource name, which may not be used
+ by some calendar resources.
+
+ @return true if the Todo was successfully inserted; false otherwise.
+
@note In most cases use
- addIncidence( Incidence *incidence, ResourceCalendar *resource ) instead.
+ addIncidence(Incidence *, ResourceCalendar *,const TQString &) instead.
*/
- bool addTodo( Todo *todo, ResourceCalendar *resource );
+ bool addTodo( Todo *todo, ResourceCalendar *resource, const TQString &subresource );
/**
Remove an Todo from the Calendar.
@@ -544,10 +622,25 @@ class LIBKCAL_EXPORT CalendarResources :
@return true if the Journal was successfully inserted; false otherwise.
+ @deprecated use
+ addIncidence(Incidence *,ResourceCalendar *,const TQString &) instead.
+ */
+ KDE_DEPRECATED bool addJournal( Journal *journal, ResourceCalendar *resource );
+
+ /**
+ Insert a Journal into a Calendar Resource.
+
+ @param journal is a pointer to the Journal to insert.
+ @param resource is a pointer to the ResourceCalendar to be added to.
+ @param subresource is the subresource name, which may not be used
+ by some calendar resources.
+
+ @return true if the Journal was successfully inserted; false otherwise.
+
@note In most cases use
- addIncidence( Incidence *incidence, ResourceCalendar *resource ) instead.
+ addIncidence(Incidence *,ResourceCalendar *,const TQString &) instead.
*/
- bool addJournal( Journal *journal, ResourceCalendar *resource );
+ bool addJournal( Journal *journal, ResourceCalendar *resource, const TQString &subresource );
/**
Remove a Journal from the Calendar.
@@ -622,6 +715,8 @@ class LIBKCAL_EXPORT CalendarResources :
*/
void setTimeZoneIdViewOnly( const TQString& tz );
+ //issue 2508
+ bool hasCalendarResources();
signals:
/**
Signal that the Resource has been modified.
@@ -684,7 +779,22 @@ class LIBKCAL_EXPORT CalendarResources :
void slotLoadError( ResourceCalendar *resource, const TQString &err );
void slotSaveError( ResourceCalendar *resource, const TQString &err );
+ /**
+ All addIncidence( Incidence * ), addTodo( Todo * ) addEvent( Event * )
+ and addJournal( Journal * ) calls made between beginAddingIncidences()
+ and endAddingIncidences() will only ask the user to choose a resource once.
+ @since 4.4
+ */
+ void beginAddingIncidences();
+
+ /**
+ @see beginAddingIncidences()
+ @since 4.4
+ */
+ void endAddingIncidences();
+
private:
+
/**
Initialize the Resource object with starting values.
*/
@@ -698,6 +808,7 @@ class LIBKCAL_EXPORT CalendarResources :
DestinationPolicy *mDestinationPolicy;
StandardDestinationPolicy *mStandardPolicy;
AskDestinationPolicy *mAskPolicy;
+ bool mPendingDeleteFromResourceMap;
TQMap<ResourceCalendar *, Ticket *> mTickets;
TQMap<ResourceCalendar *, int> mChangeCounts;
diff --git a/libkcal/calformat.cpp b/libkcal/calformat.cpp
index 6ec0260c..e506b8df 100644
--- a/libkcal/calformat.cpp
+++ b/libkcal/calformat.cpp
@@ -47,7 +47,7 @@ void CalFormat::clearException()
mException = 0;
}
-void CalFormat::setException(ErrorFormat *exception)
+void CalFormat::setException( ErrorFormat *exception )
{
delete mException;
mException = exception;
diff --git a/libkcal/calformat.h b/libkcal/calformat.h
index 59c58553..b3b7160d 100644
--- a/libkcal/calformat.h
+++ b/libkcal/calformat.h
@@ -95,7 +95,7 @@ class LIBKCAL_EXPORT CalFormat
Set exception for this object. This is used by the functions of this
class to report errors.
*/
- void setException(ErrorFormat *error);
+ void setException( ErrorFormat *error );
protected:
TQString mLoadedProductId; // PRODID string loaded from calendar file
diff --git a/libkcal/calhelper.cpp b/libkcal/calhelper.cpp
new file mode 100644
index 00000000..cae5c102
--- /dev/null
+++ b/libkcal/calhelper.cpp
@@ -0,0 +1,167 @@
+/*
+ This file is part of the kcal library.
+
+ Copyright (c) 2009-2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.net>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/**
+ @file
+ This file is part of the API for handling calendar data and provides
+ static convenience functions for making decisions about calendar data.
+
+ @brief
+ Provides methods for making decisions about calendar data.
+
+ @author Allen Winter \<allen@kdab.net\>
+*/
+
+#include "calhelper.h"
+#include "calendarresources.h"
+
+using namespace KCal;
+
+bool CalHelper::isMyKolabIncidence( Calendar *calendar, Incidence *incidence )
+{
+ CalendarResources *cal = dynamic_cast<CalendarResources*>( calendar );
+ if ( !cal || !incidence ) {
+ return true;
+ }
+
+ CalendarResourceManager *manager = cal->resourceManager();
+ CalendarResourceManager::Iterator it;
+ for ( it = manager->begin(); it != manager->end(); ++it ) {
+ TQString subRes = (*it)->subresourceIdentifier( incidence );
+ if ( !subRes.isEmpty() && !subRes.contains( "/.INBOX.directory/" ) ) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool CalHelper::isMyCalendarIncidence( Calendar *calendar, Incidence *incidence )
+{
+ return isMyKolabIncidence( calendar, incidence );
+}
+
+Incidence *CalHelper::findMyCalendarIncidenceByUid( Calendar *calendar, const TQString &uid )
+{
+ // Determine if this incidence is in my calendar (and owned by me)
+ Incidence *existingIncidence = 0;
+ if ( calendar ) {
+ existingIncidence = calendar->incidence( uid );
+ if ( !isMyCalendarIncidence( calendar, existingIncidence ) ) {
+ existingIncidence = 0;
+ }
+ if ( !existingIncidence ) {
+ const Incidence::List list = calendar->incidences();
+ for ( Incidence::List::ConstIterator it = list.begin(), end = list.end(); it != end; ++it ) {
+ if ( (*it)->schedulingID() == uid && isMyCalendarIncidence( calendar, *it ) ) {
+ existingIncidence = *it;
+ break;
+ }
+ }
+ }
+ }
+ return existingIncidence;
+}
+
+bool CalHelper::usingGroupware( Calendar *calendar )
+{
+ CalendarResources *cal = dynamic_cast<CalendarResources*>( calendar );
+ if ( !cal ) {
+ return true;
+ }
+
+ CalendarResourceManager *manager = cal->resourceManager();
+ CalendarResourceManager::Iterator it;
+ for ( it = manager->begin(); it != manager->end(); ++it ) {
+ TQString res = (*it)->type();
+ if ( res == "imap" ) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool CalHelper::hasMyWritableEventsFolders( const TQString &family )
+{
+ TQString myfamily = family;
+ if ( family.isEmpty() ) {
+ myfamily = "calendar";
+ }
+
+ CalendarResourceManager manager( myfamily );
+ manager.readConfig();
+
+ CalendarResourceManager::ActiveIterator it;
+ for ( it=manager.activeBegin(); it != manager.activeEnd(); ++it ) {
+ if ( (*it)->readOnly() ) {
+ continue;
+ }
+
+ const TQStringList subResources = (*it)->subresources();
+ if ( subResources.isEmpty() ) {
+ return true;
+ }
+
+ TQStringList::ConstIterator subIt;
+ for ( subIt=subResources.begin(); subIt != subResources.end(); ++subIt ) {
+ if ( !(*it)->subresourceActive( (*subIt) ) ) {
+ continue;
+ }
+ if ( (*it)->type() == "imap" || (*it)->type() == "kolab" ) {
+ if ( (*it)->subresourceType( ( *subIt ) ) == "todo" ||
+ (*it)->subresourceType( ( *subIt ) ) == "journal" ||
+ !(*subIt).contains( "/.INBOX.directory/" ) ) {
+ continue;
+ }
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+ResourceCalendar *CalHelper::incResourceCalendar( Calendar *calendar, Incidence *incidence )
+{
+ CalendarResources *cal = dynamic_cast<CalendarResources*>( calendar );
+ if ( !cal || !incidence ) {
+ return 0;
+ }
+
+ return cal->resource( incidence );
+}
+
+QPair<ResourceCalendar *, TQString> CalHelper::incSubResourceCalendar( Calendar *calendar,
+ Incidence *incidence )
+{
+ QPair<ResourceCalendar *, TQString> p( 0, TQString() );
+
+ CalendarResources *cal = dynamic_cast<CalendarResources*>( calendar );
+ if ( !cal || !incidence ) {
+ return p;
+ }
+
+ ResourceCalendar *res = cal->resource( incidence );
+
+ TQString subRes;
+ if ( res && res->canHaveSubresources() ) {
+ subRes = res->subresourceIdentifier( incidence );
+ }
+ p = qMakePair( res, subRes );
+ return p;
+}
diff --git a/libkcal/calhelper.h b/libkcal/calhelper.h
new file mode 100644
index 00000000..edebecf9
--- /dev/null
+++ b/libkcal/calhelper.h
@@ -0,0 +1,135 @@
+/*
+ This file is part of libkcal.
+
+ Copyright (c) 2009-2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.net>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/**
+ @file
+ This file is part of the API for handling calendar data and provides
+ static convenience functions for making decisions about calendar data.
+
+ @author Allen Winter \<allen@kdab.net\>
+*/
+
+#ifndef KCAL_CALHELPER_H
+#define KCAL_CALHELPER_H
+
+class TQString;
+#include <tqpair.h>
+
+namespace KCal {
+class Calendar;
+class Incidence;
+class ResourceCalendar;
+
+/**
+ @brief
+ Provides methods for making decisions about calendar data.
+*/
+namespace CalHelper {
+
+ /**
+ Determine if the specified incidence is likely a Kolab incidence
+ owned by the the user.
+
+ @param calendar is a pointer to a valid Calendar object.
+ @param incidence is a pointer to an Incidence object.
+
+ @return true if it is likely that the specified incidence belongs
+ to the user in their Kolab resource; false otherwise.
+ */
+ bool isMyKolabIncidence( Calendar *calendar, Incidence *incidence );
+
+ /**
+ Determine if the specified incidence is likely owned by the the user,
+ independent of the Resource type.
+
+ @param calendar is a pointer to a valid Calendar object.
+ @param incidence is a pointer to an Incidence object.
+
+ @return true if it is likely that the specified incidence belongs
+ to the user; false otherwise.
+ */
+ bool isMyCalendarIncidence( Calendar *calendar, Incidence *incidence );
+
+ /**
+ Searches for the specified Incidence by UID, returning an Incidence pointer
+ if and only if the found Incidence is owned by the user.
+
+ @param calendar is a pointer to a valid Calendar object.
+ @param Uid is a TQString containing an Incidence UID.
+
+ @return a pointer to the Incidence found; 0 if the Incidence is not found
+ or the Incidence is found but is not owned by the user.
+ */
+ Incidence *findMyCalendarIncidenceByUid( Calendar *calendar, const TQString &uid );
+
+ /**
+ Determines if the Calendar is using a Groupware resource type.
+ @param calendar is a pointer to a valid Calendar object.
+
+ @return true if the Calendar is using a known Groupware resource type;
+ false otherwise.
+ @since 4.4
+ */
+ bool usingGroupware( Calendar *calendar );
+
+ /**
+ Determines if the Calendar has any writable folders with Events content
+ that are owned by me.
+ @param family is the resource family name or "calendar" if empty.
+
+ @return true if the any such writable folders are found; false otherwise.
+ @since 4.5
+ */
+ bool hasMyWritableEventsFolders( const TQString &family );
+
+ /**
+ Returns the ResourceCalendar where the Incidence is stored, if any.
+ @param calendar is a pointer to a valid Calendar object.
+ @param incidence is a pointer to an Incidence object.
+
+ @return a pointer to the ResourceCalendar where the Incidence is stored;
+ else 0 if none can be found.
+ @since 4.5
+ */
+ ResourceCalendar *incResourceCalendar( Calendar *calendar, Incidence *incidence );
+
+ /**
+ Returns the (ResourceCalendar, SubResourceCalendar) pair where the
+ Incidence is stored, if any.
+ @param calendar is a pointer to a valid Calendar object.
+ @param incidence is a pointer to an Incidence object.
+
+ @return a QPair containing a pointer to the Incidence's ResourceCalendar
+ in the 'first' element of the QPair and the SubResourceCalendar in the
+ 'second' element.
+
+ @note many resource types do not support subresources, so the 'second'
+ element will be an empty TQString in those situations.
+ @since 4.5
+ */
+ QPair<ResourceCalendar *, TQString> incSubResourceCalendar( Calendar *calendar,
+ Incidence *incidence );
+
+}
+
+}
+
+#endif
+
diff --git a/libkcal/calselectdialog.cpp b/libkcal/calselectdialog.cpp
new file mode 100644
index 00000000..9c346f48
--- /dev/null
+++ b/libkcal/calselectdialog.cpp
@@ -0,0 +1,101 @@
+/*
+ This file is part of libkcal.
+
+ Copyright (c) 2008 Kevin Ottens <ervin@kde.org>
+ Copyright (c) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.net>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ In addition, as a special exception, the copyright holders give
+ permission to link the code of this program with any edition of
+ the TQt library by Trolltech AS, Norway (or with modified versions
+ of TQt that use the same license as TQt), and distribute linked
+ combinations including the two. You must obey the GNU General
+ Public License in all respects for all of the code used other than
+ TQt. If you modify this file, you may extend this exception to
+ your version of the file, but you are not obligated to do so. If
+ you do not wish to do so, delete this exception statement from
+ your version.
+*/
+/**
+ @file
+ This file is part of the API for handling calendar data and provides a
+ dialog for asking the user to select from a list of available calendars.
+
+ @brief
+ Calendar selector dialog.
+
+ @author Kevin Ottens \<ervin@kde.org\>
+ @author Allen Winter \<allen@kdab.net\>
+*/
+
+#include "calselectdialog.h"
+
+#include <tqlabel.h>
+#include <tqlayout.h>
+
+using namespace KCal;
+
+CalSelectDialog::CalSelectDialog( const TQString &caption, const TQString &label,
+ const TQStringList &list )
+ : KDialogBase( 0, 0, true, caption, Ok|Cancel, Ok, true )
+{
+ TQFrame *frame = makeMainWidget();
+ TQVBoxLayout *layout = new TQVBoxLayout( frame, 0, spacingHint() );
+
+ TQLabel *labelWidget = new TQLabel( label, frame );
+ layout->addWidget( labelWidget );
+
+ mListBox = new KListBox( frame );
+ mListBox->insertStringList( list );
+ mListBox->setSelected( 0, true );
+ mListBox->ensureCurrentVisible();
+ layout->addWidget( mListBox, 10 );
+
+ connect( mListBox, TQT_SIGNAL(doubleClicked(TQListBoxItem *)),
+ TQT_SLOT(slotOk()) );
+ connect( mListBox, TQT_SIGNAL(returnPressed(TQListBoxItem *)),
+ TQT_SLOT(slotOk()) );
+
+ mListBox->setFocus();
+
+ layout->addStretch();
+
+ setMinimumWidth( 320 );
+}
+
+TQString CalSelectDialog::getItem( const TQString &caption, const TQString &label,
+ const TQStringList &list )
+{
+ CalSelectDialog dlg( caption, label, list );
+
+ TQString result;
+ if ( dlg.exec() == Accepted ) {
+ result = dlg.mListBox->currentText();
+ }
+
+ return result;
+}
+
+void CalSelectDialog::closeEvent( TQCloseEvent *event )
+{
+ event->ignore();
+}
+
+void CalSelectDialog::reject()
+{
+}
+
diff --git a/libkcal/calselectdialog.h b/libkcal/calselectdialog.h
new file mode 100644
index 00000000..40b2811b
--- /dev/null
+++ b/libkcal/calselectdialog.h
@@ -0,0 +1,69 @@
+/*
+ This file is part of libkcal.
+
+ Copyright (c) 2008 Kevin Ottens <ervin@kde.org>
+ Copyright (c) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.net>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ In addition, as a special exception, the copyright holders give
+ permission to link the code of this program with any edition of
+ the TQt library by Trolltech AS, Norway (or with modified versions
+ of TQt that use the same license as TQt), and distribute linked
+ combinations including the two. You must obey the GNU General
+ Public License in all respects for all of the code used other than
+ TQt. If you modify this file, you may extend this exception to
+ your version of the file, but you are not obligated to do so. If
+ you do not wish to do so, delete this exception statement from
+ your version.
+*/
+/**
+ @file
+ This file is part of the API for handling calendar data and provides a
+ dialog for asking the user to select from a list of available calendars.
+
+ @author Kevin Ottens \<ervin@kde.org\>
+ @author Allen Winter \<allen@kdab.net\>
+*/
+
+#ifndef CALSELECTDIALOG_H
+#define CALSELECTDIALOG_H
+
+#include <kdialogbase.h>
+
+namespace KCal {
+
+class CalSelectDialog : public KDialogBase
+{
+ private:
+ CalSelectDialog( const TQString &caption, const TQString &label,
+ const TQStringList &list );
+
+ public:
+ static TQString getItem( const TQString &caption, const TQString &label,
+ const TQStringList &list );
+
+ protected:
+ virtual void closeEvent( TQCloseEvent *event );
+ void reject();
+
+ private:
+ KListBox *mListBox;
+};
+
+}
+
+#endif
diff --git a/libkcal/comparisonvisitor.cpp b/libkcal/comparisonvisitor.cpp
new file mode 100644
index 00000000..79a61e2f
--- /dev/null
+++ b/libkcal/comparisonvisitor.cpp
@@ -0,0 +1,107 @@
+/*
+ Copyright 2009 Ingo Klöcker <kloecker@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., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+*/
+
+#include "comparisonvisitor.h"
+#include "event.h"
+#include "freebusy.h"
+#include "journal.h"
+#include "todo.h"
+
+using namespace KCal;
+
+class ComparisonVisitor::Private
+{
+ public:
+ Private() : mReference( 0 ) {}
+
+ public:
+ const IncidenceBase *mReference;
+};
+
+ComparisonVisitor::ComparisonVisitor() : d( new Private() )
+{
+}
+
+ComparisonVisitor::~ComparisonVisitor()
+{
+ delete d;
+}
+
+bool ComparisonVisitor::compare( IncidenceBase *incidence, const IncidenceBase *reference )
+{
+ d->mReference = reference;
+
+ const bool result = incidence ? incidence->accept( *this ) : reference == 0;
+
+ d->mReference = 0;
+
+ return result;
+}
+
+bool ComparisonVisitor::visit( Event *event )
+{
+ Q_ASSERT( event != 0 );
+
+ const Event *refEvent = dynamic_cast<const Event*>( d->mReference );
+ if ( refEvent ) {
+ return *event == *refEvent;
+ } else {
+ // refEvent is no Event and thus cannot be equal to event
+ return false;
+ }
+}
+
+bool ComparisonVisitor::visit( Todo *todo )
+{
+ Q_ASSERT( todo != 0 );
+
+ const Todo *refTodo = dynamic_cast<const Todo*>( d->mReference );
+ if ( refTodo ) {
+ return *todo == *refTodo;
+ } else {
+ // refTodo is no Todo and thus cannot be equal to todo
+ return false;
+ }
+}
+
+bool ComparisonVisitor::visit( Journal *journal )
+{
+ Q_ASSERT( journal != 0 );
+
+ const Journal *refJournal = dynamic_cast<const Journal*>( d->mReference );
+ if ( refJournal ) {
+ return *journal == *refJournal;
+ } else {
+ // refJournal is no Journal and thus cannot be equal to journal
+ return false;
+ }
+}
+
+bool ComparisonVisitor::visit( FreeBusy *freebusy )
+{
+ Q_ASSERT( freebusy != 0 );
+
+ const FreeBusy *refFreeBusy = dynamic_cast<const FreeBusy*>( d->mReference );
+ if ( refFreeBusy ) {
+ return *freebusy == *refFreeBusy;
+ } else {
+ // refFreeBusy is no FreeBusy and thus cannot be equal to freebusy
+ return false;
+ }
+}
diff --git a/libkcal/comparisonvisitor.h b/libkcal/comparisonvisitor.h
new file mode 100644
index 00000000..0712f9c0
--- /dev/null
+++ b/libkcal/comparisonvisitor.h
@@ -0,0 +1,122 @@
+/*
+ Copyright 2009 Ingo Klöcker <kloecker@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., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+*/
+
+#ifndef KCAL_COMPARISONVISITOR_H
+#define KCAL_COMPARISONVISITOR_H
+
+#include "incidencebase.h"
+
+namespace KCal {
+
+/**
+ Helper for type correct comparison of incidences via pointers.
+
+ This class provides a way of correctly comparing one incidence to another,
+ given two IncidenceBase derived pointers. It effectively provides a virtual
+ comparison method which first type checks the two pointers to ensure they
+ reference the same incidence type, before performing the comparison.
+
+ Usage example:
+ @code
+ KCal::Incidence *incidence; // assume this is set somewhere else
+ KCal::Incidence *referenceIncidence; // assume this is set somewhere else
+
+ KCal::ComparisonVisitor visitor;
+
+ // compare
+ if ( visitor.compare( incidence, referenceIncidence ) ) {
+ // incidence and referenceIncidence point to identical incidences
+ }
+ @endcode
+
+ @author Ingo Klöcker <kloecker@kde.org>
+ */
+class ComparisonVisitor : public IncidenceBase::Visitor
+{
+ public:
+ /**
+ Creates a visitor instance.
+ */
+ ComparisonVisitor();
+
+ /**
+ Destroys the instance.
+ */
+ virtual ~ComparisonVisitor();
+
+ /**
+ Compares the incidence referenced by @p incidence to the incidence
+ referenced by @p reference. Returns true, if the incidence referenced
+ by @p incidence is identical to the incidence referenced by @p reference.
+ Also returns true, if @p incidence and @p reference are both @c 0.
+
+ Basically it is a virtual equivalent of
+ @code
+ *incidence == *reference
+ @endcode
+
+ @param incidence pointer to the incidence to compare with the reference incidence
+ @param reference pointer to the reference incidence
+
+ @return @c true if the two incidences are identical or both @c 0
+ */
+ bool compare( IncidenceBase *incidence, const IncidenceBase *reference );
+
+ /**
+ Compares the event referenced by @p event to the incidence passed to
+ compare().
+
+ @return @c true if the event is identical to the reference incidence
+ */
+ virtual bool visit( Event *event );
+
+ /**
+ Compares the todo referenced by @p todo to the incidence passed to
+ compare().
+
+ @return @c true if the todo is identical to the reference incidence
+ */
+ virtual bool visit( Todo *todo );
+
+ /**
+ Compares the journal referenced by @p journal to the incidence passed to
+ compare().
+
+ @return @c true if the journal is identical to the reference incidence
+ */
+ virtual bool visit( Journal *journal );
+
+ /**
+ Compares the freebusy object referenced by @p freebusy to the incidence passed to
+ compare().
+
+ @return @c true if the freebusy object is identical to the reference incidence
+ */
+ virtual bool visit( FreeBusy *freebusy );
+
+ private:
+ //@cond PRIVATE
+ class Private;
+ Private *const d;
+ //@endcond
+};
+
+}
+
+#endif // KCAL_COMPARISONVISITOR_H
diff --git a/libkcal/dndfactory.cpp b/libkcal/dndfactory.cpp
index 9f5292d1..b94c1707 100644
--- a/libkcal/dndfactory.cpp
+++ b/libkcal/dndfactory.cpp
@@ -23,6 +23,7 @@
#include <tqapplication.h>
#include <tqclipboard.h>
+#include <tqmap.h>
#include <kiconloader.h>
#include <kdebug.h>
@@ -40,11 +41,68 @@
using namespace KCal;
+class DndFactory::Private
+{
+ public:
+ Incidence * pasteIncidence( Incidence *inc,
+ const TQDate &newDate,
+ const TQTime *newTime = 0 )
+ {
+ if ( inc ) {
+ inc = inc->clone();
+ inc->recreate();
+ }
+
+ if ( inc && newDate.isValid() ) {
+ if ( inc->type() == "Event" ) {
+ Event *anEvent = static_cast<Event*>( inc );
+ // Calculate length of event
+ int daysOffset = anEvent->dtStart().date().daysTo(
+ anEvent->dtEnd().date() );
+ // new end date if event starts at the same time on the new day
+ TQDateTime endDate( newDate.addDays(daysOffset), anEvent->dtEnd().time() );
+
+ if ( newTime ) {
+ // additional offset for new time of day
+ int addSecsOffset( anEvent->dtStart().time().secsTo( *newTime ));
+ endDate=endDate.addSecs( addSecsOffset );
+ anEvent->setDtStart( TQDateTime( newDate, *newTime ) );
+ } else {
+ anEvent->setDtStart( TQDateTime( newDate, anEvent->dtStart().time() ) );
+ }
+ anEvent->setDtEnd( endDate );
+ } else if ( inc->type() == "Todo" ) {
+ Todo *anTodo = static_cast<Todo*>( inc );
+ if ( newTime ) {
+ anTodo->setDtDue( TQDateTime( newDate, *newTime ) );
+ } else {
+ anTodo->setDtDue( TQDateTime( newDate, anTodo->dtDue().time() ) );
+ }
+ } else if ( inc->type() == "Journal" ) {
+ Journal *anJournal = static_cast<Journal*>( inc );
+ if ( newTime ) {
+ anJournal->setDtStart( TQDateTime( newDate, *newTime ) );
+ } else {
+ anJournal->setDtStart( TQDateTime( newDate ) );
+ }
+ } else {
+ kdDebug(5850) << "Trying to paste unknown incidence of type " << inc->type() << endl;
+ }
+ }
+ return inc;
+ }
+};
+
DndFactory::DndFactory( Calendar *cal ) :
- mCalendar( cal )
+ mCalendar( cal ), d( new Private )
{
}
+DndFactory::~DndFactory()
+{
+ delete d;
+}
+
ICalDrag *DndFactory::createDrag( Incidence *incidence, TQWidget *owner )
{
CalendarLocal cal( mCalendar->timeZoneId() );
@@ -98,90 +156,111 @@ Todo *DndFactory::createDropTodo(TQDropEvent *de)
return 0;
}
-
void DndFactory::cutIncidence( Incidence *selectedInc )
{
- if ( copyIncidence( selectedInc ) ) {
- mCalendar->deleteIncidence( selectedInc );
- }
+ Incidence::List list;
+ list.append( selectedInc );
+ cutIncidences( list );
}
-bool DndFactory::copyIncidence( Incidence *selectedInc )
+bool DndFactory::cutIncidences( const Incidence::List &incidences )
{
- if ( !selectedInc )
+ if ( copyIncidences( incidences ) ) {
+ Incidence::List::ConstIterator it;
+ for ( it = incidences.constBegin(); it != incidences.constEnd(); ++it ) {
+ mCalendar->deleteIncidence( *it );
+ }
+ return true;
+ } else {
return false;
- QClipboard *cb = TQApplication::clipboard();
+ }
+}
+bool DndFactory::copyIncidences( const Incidence::List &incidences )
+{
+ QClipboard *cb = TQApplication::clipboard();
CalendarLocal cal( mCalendar->timeZoneId() );
- Incidence *inc = selectedInc->clone();
- cal.addIncidence( inc );
- cb->setData( new ICalDrag( &cal ) );
+ Incidence::List::ConstIterator it;
- return true;
+ for ( it = incidences.constBegin(); it != incidences.constEnd(); ++it ) {
+ if ( *it ) {
+ cal.addIncidence( ( *it )->clone() );
+ }
+ }
+
+ if ( cal.incidences().isEmpty() ) {
+ return false;
+ } else {
+ cb->setData( new ICalDrag( &cal ) );
+ return true;
+ }
}
-Incidence *DndFactory::pasteIncidence(const TQDate &newDate, const TQTime *newTime)
+bool DndFactory::copyIncidence( Incidence *selectedInc )
{
-// kdDebug(5800) << "DnDFactory::pasteEvent()" << endl;
+ Incidence::List list;
+ list.append( selectedInc );
+ return copyIncidences( list );
+}
+Incidence::List DndFactory::pasteIncidences( const TQDate &newDate, const TQTime *newTime )
+{
CalendarLocal cal( mCalendar->timeZoneId() );
-
QClipboard *cb = TQApplication::clipboard();
+ Incidence::List list;
if ( !ICalDrag::decode( cb->data(), &cal ) &&
!VCalDrag::decode( cb->data(), &cal ) ) {
kdDebug(5800) << "Can't parse clipboard" << endl;
- return 0;
+ return list;
}
- Incidence::List incList = cal.incidences();
- Incidence *inc = incList.first();
-
- if ( !incList.isEmpty() && inc ) {
- inc = inc->clone();
-
- inc->recreate();
-
- if ( inc->type() == "Event" ) {
-
- Event *anEvent = static_cast<Event*>( inc );
- // Calculate length of event
- int daysOffset = anEvent->dtStart().date().daysTo(
- anEvent->dtEnd().date() );
- // new end date if event starts at the same time on the new day
- TQDateTime endDate( newDate.addDays(daysOffset), anEvent->dtEnd().time() );
+ // All pasted incidences get new uids, must keep track of old uids,
+ // so we can update child's parents
+ TQMap<TQString,Incidence*> oldUidToNewInc;
+
+ Incidence::List::ConstIterator it;
+ const Incidence::List incs = cal.incidences();
+ for ( it = incs.constBegin(); it != incs.constEnd(); ++it ) {
+ Incidence *inc = d->pasteIncidence( *it, newDate, newTime );
+ if ( inc ) {
+ list.append( inc );
+ oldUidToNewInc[( *it )->uid()] = inc;
+ }
+ }
- if ( newTime ) {
- // additional offset for new time of day
- int addSecsOffset( anEvent->dtStart().time().secsTo( *newTime ));
- endDate=endDate.addSecs( addSecsOffset );
- anEvent->setDtStart( TQDateTime( newDate, *newTime ) );
- } else {
- anEvent->setDtStart( TQDateTime( newDate, anEvent->dtStart().time() ) );
- }
- anEvent->setDtEnd( endDate );
-
- } else if ( inc->type() == "Todo" ) {
- Todo *anTodo = static_cast<Todo*>( inc );
- if ( newTime ) {
- anTodo->setDtDue( TQDateTime( newDate, *newTime ) );
- } else {
- anTodo->setDtDue( TQDateTime( newDate, anTodo->dtDue().time() ) );
- }
- } else if ( inc->type() == "Journal" ) {
- Journal *anJournal = static_cast<Journal*>( inc );
- if ( newTime ) {
- anJournal->setDtStart( TQDateTime( newDate, *newTime ) );
- } else {
- anJournal->setDtStart( TQDateTime( newDate ) );
- }
+ // update relations
+ for ( it = list.constBegin(); it != list.constEnd(); ++it ) {
+ Incidence *inc = *it;
+ if ( oldUidToNewInc.contains( inc->relatedToUid() ) ) {
+ Incidence *parentInc = oldUidToNewInc[inc->relatedToUid()];
+ inc->setRelatedToUid( parentInc->uid() );
+ inc->setRelatedTo( parentInc );
} else {
- kdDebug(5850) << "Trying to paste unknown incidence of type " << inc->type() << endl;
+ // not related to anything in the clipboard
+ inc->setRelatedToUid( TQString() );
+ inc->setRelatedTo( 0 );
}
+ }
- return inc;
+ return list;
+}
+
+Incidence *DndFactory::pasteIncidence( const TQDate &newDate, const TQTime *newTime )
+{
+ CalendarLocal cal( mCalendar->timeZoneId() );
+ QClipboard *cb = TQApplication::clipboard();
+ if ( !ICalDrag::decode( cb->data(), &cal ) &&
+ !VCalDrag::decode( cb->data(), &cal ) ) {
+ kdDebug(5800) << "Can't parse clipboard" << endl;
+ return 0;
}
- return 0;
+ Incidence::List incList = cal.incidences();
+ Incidence *inc = incList.isEmpty() ? 0 : incList.first();
+
+ Incidence *newInc = d->pasteIncidence( inc, newDate, newTime );
+ newInc->setRelatedTo( 0 );
+ return newInc;
}
diff --git a/libkcal/dndfactory.h b/libkcal/dndfactory.h
index 441a0a40..a8712f2b 100644
--- a/libkcal/dndfactory.h
+++ b/libkcal/dndfactory.h
@@ -43,6 +43,7 @@ class LIBKCAL_EXPORT DndFactory
{
public:
DndFactory( Calendar * );
+ ~DndFactory();
/**
Create a drag object.
@@ -58,9 +59,20 @@ class LIBKCAL_EXPORT DndFactory
void cutIncidence( Incidence * );
/** copy the incidence to clipboard */
bool copyIncidence( Incidence * );
+
+ /** cuts a list of incidences to the clipboard */
+ bool cutIncidences( const Incidence::List &incidences );
+
+ /** copies a list of incidences to the clipboard */
+ bool copyIncidences( const Incidence::List &incidences );
+
/** pastes the event or todo and returns a pointer to the new incidence pasted. */
Incidence *pasteIncidence( const TQDate &, const TQTime *newTime = 0 );
+ /** pastes and returns the incidences from the clipboard
+ If no date and time are given, the incidences will be pasted at their original time */
+ Incidence::List pasteIncidences( const TQDate &newDate = TQDate(), const TQTime *newTime = 0 );
+
private:
Calendar *mCalendar;
diff --git a/libkcal/duration.cpp b/libkcal/duration.cpp
index 0dfd8781..fdb5a370 100644
--- a/libkcal/duration.cpp
+++ b/libkcal/duration.cpp
@@ -19,42 +19,160 @@
Boston, MA 02110-1301, USA.
*/
-#include <kdebug.h>
-#include <klocale.h>
-
#include "duration.h"
using namespace KCal;
Duration::Duration()
{
- mSeconds = 0;
+ mDuration = 0;
}
Duration::Duration( const TQDateTime &start, const TQDateTime &end )
{
- mSeconds = start.secsTo( end );
+ if ( start.time() == end.time() ) {
+ mDuration = start.daysTo( end );
+ mDaily = true;
+ } else {
+ mDuration = start.secsTo( end );
+ mDaily = false;
+ }
+}
+
+Duration::Duration( const TQDateTime &start, const TQDateTime &end, Type type )
+{
+ if ( type == Days ) {
+ mDuration = start.daysTo( end );
+ if ( mDuration ) {
+ // Round down to whole number of days if necessary
+ if ( start < end ) {
+ if ( end.time() < start.time() ) {
+ --mDuration;
+ }
+ } else {
+ if ( end.time() > start.time() ) {
+ ++mDuration;
+ }
+ }
+ }
+ mDaily = true;
+ } else {
+ mDuration = start.secsTo( end );
+ mDaily = false;
+ }
+}
+
+Duration::Duration( int duration, Type type )
+{
+ mDuration = duration;
+ mDaily = ( type == Days );
+}
+
+Duration::Duration( const Duration &duration )
+{
+ mDuration = duration.mDuration;
+ mDaily = duration.mDaily;
+}
+
+Duration &Duration::operator=( const Duration &duration )
+{
+ // check for self assignment
+ if ( &duration == this ) {
+ return *this;
+ }
+
+ mDuration = duration.mDuration;
+ mDaily = duration.mDaily;
+
+ return *this;
+}
+
+Duration::operator bool() const
+{
+ return mDuration;
+}
+
+bool Duration::operator<( const Duration &other ) const
+{
+ if ( mDaily == other.mDaily ) {
+ // guard against integer overflow for two daily durations
+ return mDuration < other.mDuration;
+ }
+ return seconds() < other.seconds();
}
-Duration::Duration( int seconds )
+bool Duration::operator==( const Duration &other ) const
{
- mSeconds = seconds;
+ // Note: daily and non-daily durations are always unequal, since a day's
+ // duration may differ from 24 hours if it happens to span a daylight saving
+ // time change.
+ return
+ mDuration == other.mDuration &&
+ mDaily == other.mDaily;
}
-bool KCal::operator==( const Duration& d1, const Duration& d2 )
+Duration &Duration::operator+=( const Duration &other )
{
- return ( d1.asSeconds() == d2.asSeconds() );
+ if ( mDaily == other.mDaily ) {
+ mDuration += other.mDuration;
+ } else if ( mDaily ) {
+ mDuration = mDuration * 86400 + other.mDuration;
+ mDaily = false;
+ } else {
+ mDuration += other.mDuration + 86400;
+ }
+ return *this;
}
+Duration Duration::operator-() const
+{
+ return Duration( -mDuration, ( mDaily ? Days : Seconds ) );
+}
+Duration &Duration::operator-=( const Duration &duration )
+{
+ return operator+=( -duration );
+}
+
+Duration &Duration::operator*=( int value )
+{
+ mDuration *= value;
+ return *this;
+}
+
+Duration &Duration::operator/=( int value )
+{
+ mDuration /= value;
+ return *this;
+}
TQDateTime Duration::end( const TQDateTime &start ) const
{
- return start.addSecs( mSeconds );
+ return mDaily ? start.addDays( mDuration )
+ : start.addSecs( mDuration );
+}
+Duration::Type Duration::type() const
+{
+ return mDaily ? Days : Seconds;
+}
+
+bool Duration::isDaily() const
+{
+ return mDaily;
}
int Duration::asSeconds() const
{
- return mSeconds;
+ return seconds();
+}
+
+int Duration::asDays() const
+{
+ return mDaily ? mDuration : mDuration / 86400;
+}
+
+int Duration::value() const
+{
+ return mDuration;
}
diff --git a/libkcal/duration.h b/libkcal/duration.h
index b4655d6c..9cddf695 100644
--- a/libkcal/duration.h
+++ b/libkcal/duration.h
@@ -33,27 +33,246 @@ namespace KCal {
class LIBKCAL_EXPORT Duration
{
public:
+ /**
+ The unit of time used to define the duration.
+ */
+ enum Type {
+ Seconds, /**< duration is a number of seconds */
+ Days /**< duration is a number of days */
+ };
+
+ /**
+ Constructs a duration of 0 seconds.
+ */
Duration();
+
+ /**
+ Constructs a duration from @p start to @p end.
+
+ If the time of day in @p start and @p end is equal, and their time
+ specifications (i.e. time zone etc.) are the same, the duration will be
+ set in terms of days. Otherwise, the duration will be set in terms of
+ seconds.
+
+ @param start is the time the duration begins.
+ @param end is the time the duration ends.
+ */
Duration( const TQDateTime &start, const TQDateTime &end );
- Duration( int seconds );
+ /**
+ Constructs a duration from @p start to @p end.
+
+ If @p type is Days, and the time of day in @p start's time zone differs
+ between @p start and @p end, the duration will be rounded down to the
+ nearest whole number of days.
+
+ @param start is the time the duration begins.
+ @param end is the time the duration ends.
+ @param type the unit of time to use (seconds or days)
+ */
+ Duration( const TQDateTime &start, const TQDateTime &end, Type type );
+
+ /**
+ Constructs a duration with a number of seconds or days.
+
+ @param duration the number of seconds or days in the duration
+ @param type the unit of time to use (seconds or days)
+ */
+ Duration( int duration, Type type = Seconds ); //krazy:exclude=explicit
+
+ /**
+ Constructs a duration by copying another duration object.
+
+ @param duration is the duration to copy.
+ */
+ Duration( const Duration &duration );
+
+ /**
+ Sets this duration equal to @p duration.
+
+ @param duration is the duration to copy.
+ */
+ Duration &operator=( const Duration &duration );
+
+ /**
+ Returns true if this duration is non-zero.
+ */
+ operator bool() const;
+
+ /**
+ Returns true if this duration is zero.
+ */
+ bool operator!() const { return !operator bool(); }
+
+ /**
+ Returns true if this duration is smaller than the @p other.
+ @param other is the other duration to compare.
+ */
+ bool operator<( const Duration &other ) const;
+
+ /**
+ Returns true if this duration is smaller than or equal to the @p other.
+ @param other is the other duration to compare.
+ */
+ bool operator<=( const Duration &other ) const
+ { return !other.operator<( *this ); }
+
+
+ /**
+ Returns true if this duration is greater than the @p other.
+ @param other is the other duration to compare.
+ */
+ bool operator>( const Duration &other ) const
+ { return other.operator<( *this ); }
+
+ /**
+ Returns true if this duration is greater than or equal to the @p other.
+ @param other is the other duration to compare.
+ */
+ bool operator>=( const Duration &other ) const
+ { return !operator<( other ); }
+
+ /**
+ Returns true if this duration is equal to the @p other.
+ Daily and non-daily durations are always considered unequal, since a
+ day's duration may differ from 24 hours if it happens to span a daylight
+ saving time change.
+ @param other the other duration to compare
+ */
+ bool operator==( const Duration &other ) const;
+
+ /**
+ Returns true if this duration is not equal to the @p other.
+ Daily and non-daily durations are always considered unequal, since a
+ day's duration may differ from 24 hours if it happens to span a daylight
+ saving time change.
+ @param other is the other duration to compare.
+ */
+ bool operator!=( const Duration &other ) const
+ { return !operator==( other ); }
+
+ /**
+ Adds another duration to this one.
+ If one is in terms of days and the other in terms of seconds,
+ the result is in terms of seconds.
+ @param other the other duration to add
+ */
+ Duration &operator+=( const Duration &other );
+
+ /**
+ Adds two durations.
+ If one is in terms of days and the other in terms of seconds,
+ the result is in terms of seconds.
+
+ @param other the other duration to add
+ @return combined duration
+ */
+ Duration operator+( const Duration &other ) const
+ { return Duration( *this ) += other; }
+
+ /**
+ Returns the negative of this duration.
+ */
+ Duration operator-() const;
+
+ /**
+ Subtracts another duration from this one.
+ If one is in terms of days and the other in terms of seconds,
+ the result is in terms of seconds.
+
+ @param other the other duration to subtract
+ */
+ Duration &operator-=( const Duration &other );
+
+ /**
+ Returns the difference between another duration and this.
+ If one is in terms of days and the other in terms of seconds,
+ the result is in terms of seconds.
+
+ @param other the other duration to subtract
+ @return difference in durations
+ */
+ Duration operator-( const Duration &other ) const
+ { return Duration( *this ) += other; }
+
+ /**
+ Multiplies this duration by a value.
+ @param value value to multiply by
+ */
+ Duration &operator*=( int value );
+
+ /**
+ Multiplies a duration by a value.
+
+ @param value value to multiply by
+ @return resultant duration
+ */
+ Duration operator*( int value ) const
+ { return Duration( *this ) *= value; }
+
+ /**
+ Divides this duration by a value.
+ @param value value to divide by
+ */
+ Duration &operator/=( int value );
+
+ /**
+ Divides a duration by a value.
+
+ @param value value to divide by
+ @return resultant duration
+ */
+ Duration operator/( int value ) const
+ { return Duration( *this ) /= value; }
+
+ /**
+ Computes a duration end time by adding the number of seconds or
+ days in the duration to the specified @p start time.
+
+ @param start is the start time.
+ @return end time.
+ */
TQDateTime end( const TQDateTime &start ) const;
+ /**
+ Returns the time units (seconds or days) used to specify the duration.
+ */
+ Type type() const;
+
+ /**
+ Returns whether the duration is specified in terms of days rather
+ than seconds.
+ */
+ bool isDaily() const;
+
+ /**
+ Returns the length of the duration in seconds.
+ */
int asSeconds() const;
+ /**
+ Returns the length of the duration in days. If the duration is
+ not an exact number of days, it is rounded down to return the
+ number of whole days.
+ */
+ int asDays() const;
+
+ /**
+ Returns the length of the duration in seconds or days.
+
+ @return if isDaily(), duration in days, else duration in seconds
+ */
+ int value() const;
+
private:
- int mSeconds;
+ int seconds() const { return mDaily ? mDuration * 86400 : mDuration; }
+ int mDuration;
+ bool mDaily;
class Private;
Private *d;
};
-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
index 0f286e7f..5bd3a818 100644
--- a/libkcal/event.cpp
+++ b/libkcal/event.cpp
@@ -87,8 +87,8 @@ TQDateTime Event::dtEnd() const
if (hasEndDate()) return mDtEnd;
if (hasDuration()) return dtStart().addSecs(duration());
- kdDebug(5800) << "Warning! Event '" << summary()
- << "' has neither end date nor duration." << endl;
+ // It is valid for a VEVENT to be without a DTEND. See RFC2445, Sect4.6.1.
+ // Be careful to use Event::dateEnd() as appropriate due to this possibility.
return dtStart();
}
diff --git a/libkcal/event.h b/libkcal/event.h
index 39980556..fed0e41a 100644
--- a/libkcal/event.h
+++ b/libkcal/event.h
@@ -72,21 +72,24 @@ class LIBKCAL_EXPORT Event : public Incidence
/**
Return end time as string formatted according to the users locale
settings.
+ @deprecated use IncidenceFormatter::timeToString()
*/
- TQString dtEndTimeStr() const;
+ TQString KDE_DEPRECATED dtEndTimeStr() const;
/**
Return end date as string formatted according to the users locale
settings.
@param shortfmt if true return string in short format, if false return
long format
+ @deprecated use IncidenceFormatter::dateToString()
*/
- TQString dtEndDateStr( bool shortfmt = true ) const;
+ TQString KDE_DEPRECATED dtEndDateStr( bool shortfmt = true ) const;
/**
Return end date and time as string formatted according to the users locale
settings.
+ @deprecated use IncidenceFormatter::dateTimeToString()
*/
- TQString dtEndStr() const;
+ TQString KDE_DEPRECATED dtEndStr() const;
/**
Set whether the event has an end date/time.
diff --git a/libkcal/exceptions.cpp b/libkcal/exceptions.cpp
index 98d7153e..99c804dd 100644
--- a/libkcal/exceptions.cpp
+++ b/libkcal/exceptions.cpp
@@ -26,7 +26,7 @@
using namespace KCal;
-Exception::Exception(const TQString &message)
+Exception::Exception( const TQString &message )
{
mMessage = message;
}
@@ -37,13 +37,16 @@ Exception::~Exception()
TQString Exception::message()
{
- if (mMessage.isEmpty()) return i18n("%1 Error").arg(CalFormat::application());
- else return mMessage;
+ if ( mMessage.isEmpty() ) {
+ return i18n( "%1 Error" ).arg( CalFormat::application() );
+ } else {
+ return mMessage;
+ }
}
-ErrorFormat::ErrorFormat(ErrorCodeFormat code,const TQString &message) :
- Exception(message)
+ErrorFormat::ErrorFormat( ErrorCodeFormat code, const TQString &message )
+ : Exception( message )
{
mCode = code;
}
@@ -52,35 +55,45 @@ TQString ErrorFormat::message()
{
TQString 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;
+ 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 CalVersionUnknown:
+ message = i18n( "Unknown calendar format detected." );
+ break;
+ case Restriction:
+ message = i18n( "Restriction violation" );
+ break;
+ case NoWritableFound:
+ message = i18n( "No writable resource found" );
+ break;
+ case UserCancel:
+ // no real error; the user canceled the operation
+ break;
}
- if (!mMessage.isEmpty()) message += ": " + mMessage;
+ if ( !mMessage.isEmpty() ) {
+ message += ": " + mMessage;
+ }
return message;
}
diff --git a/libkcal/exceptions.h b/libkcal/exceptions.h
index 1ae90d51..8bdf7fa0 100644
--- a/libkcal/exceptions.h
+++ b/libkcal/exceptions.h
@@ -47,9 +47,9 @@ class Exception
/**
Return descriptive message of exception.
- */
+ */
virtual TQString message();
-
+
protected:
TQString mMessage;
@@ -64,18 +64,28 @@ class Exception
class ErrorFormat : public Exception
{
public:
- enum ErrorCodeFormat { LoadError, SaveError,
- ParseErrorIcal, ParseErrorKcal,
- NoCalendar,
- CalVersion1,CalVersion2,
- CalVersionUnknown,
- Restriction };
-
+ /**
+ The different types of Calendar format errors.
+ */
+ enum ErrorCodeFormat {
+ LoadError, /**< Load error */
+ SaveError, /**< Save error */
+ ParseErrorIcal, /**< Parse error in libical */
+ ParseErrorKcal, /**< Parse error in libkcal */
+ NoCalendar, /**< No calendar component found */
+ CalVersion1, /**< vCalendar v1.0 detected */
+ CalVersion2, /**< iCalendar v2.0 detected */
+ CalVersionUnknown, /**< Unknown calendar format detected */
+ Restriction, /**< Restriction violation */
+ NoWritableFound, /**< No writable resource is available */
+ UserCancel /**< User canceled the operation */
+ };
+
/**
Create format error exception.
*/
ErrorFormat( ErrorCodeFormat code, const TQString &message = TQString::null );
-
+
/**
Return format error message.
*/
@@ -84,7 +94,7 @@ class ErrorFormat : public Exception
Return format error code.
*/
ErrorCodeFormat errorCode();
-
+
private:
ErrorCodeFormat mCode;
diff --git a/libkcal/freebusy.cpp b/libkcal/freebusy.cpp
index 75d0e7db..2935f4ba 100644
--- a/libkcal/freebusy.cpp
+++ b/libkcal/freebusy.cpp
@@ -222,3 +222,12 @@ void FreeBusy::merge( FreeBusy *freeBusy )
for ( it = periods.begin(); it != periods.end(); ++it )
addPeriod( (*it).start(), (*it).end() );
}
+
+bool FreeBusy::operator==( const FreeBusy &freebusy ) const
+{
+ return
+ static_cast<const IncidenceBase &>( *this ) == static_cast<const IncidenceBase &>( freebusy ) &&
+ dtEnd() == freebusy.dtEnd() &&
+ mCalendar == freebusy.mCalendar &&
+ mBusyPeriods == freebusy.mBusyPeriods;
+}
diff --git a/libkcal/freebusy.h b/libkcal/freebusy.h
index d87234f3..f736092d 100644
--- a/libkcal/freebusy.h
+++ b/libkcal/freebusy.h
@@ -47,7 +47,7 @@ class LIBKCAL_EXPORT FreeBusy : public IncidenceBase
FreeBusy( PeriodList busyPeriods );
~FreeBusy();
-
+
TQCString type() const { return "FreeBusy"; }
virtual TQDateTime dtEnd() const;
@@ -65,7 +65,14 @@ class LIBKCAL_EXPORT FreeBusy : public IncidenceBase
void sortList();
void merge( FreeBusy *freebusy );
-
+
+ /**
+ Compare this with @p freebusy for equality.
+
+ @param freebusy is the FreeBusy to compare.
+ */
+ bool operator==( const FreeBusy &freebusy ) const;
+
private:
bool accept( Visitor &v ) { return v.visit( this ); }
//This is used for creating a freebusy object for the current user
diff --git a/libkcal/htmlexport.cpp b/libkcal/htmlexport.cpp
index fd994fca..c945b88f 100644
--- a/libkcal/htmlexport.cpp
+++ b/libkcal/htmlexport.cpp
@@ -34,6 +34,7 @@
#include <libkcal/calendar.h>
#include <libkcal/event.h>
+#include <libkcal/incidenceformatter.h>
#include <libkcal/todo.h>
#ifndef KORG_NOKABC
@@ -184,20 +185,23 @@ void HtmlExport::createMonthView(TQTextStream *ts)
*ts << "</td></tr><tr><td valign=\"top\">";
- Event::List events = mCalendar->events( start,
- EventSortStartDate,
- SortDirectionAscending );
- if (events.count()) {
- *ts << "<table>";
- Event::List::ConstIterator it;
- for( it = events.begin(); it != events.end(); ++it ) {
+ // Only print events within the from-to range
+ if ( start >= fromDate() && start <= toDate() ) {
+ Event::List events = mCalendar->events( start,
+ EventSortStartDate,
+ SortDirectionAscending );
+ if (events.count()) {
+ *ts << "<table>";
+ Event::List::ConstIterator it;
+ for( it = events.begin(); it != events.end(); ++it ) {
if ( checkSecrecy( *it ) ) {
createEvent( ts, *it, start, false );
}
+ }
+ *ts << "</table>";
+ } else {
+ *ts << "&nbsp;";
}
- *ts << "</table>";
- } else {
- *ts << "&nbsp;";
}
*ts << "</td></tr></table></td>\n";
@@ -275,12 +279,16 @@ void HtmlExport::createEvent (TQTextStream *ts, Event *event,
if (event->isMultiDay() && (event->dtStart().date() != date)) {
*ts << " <td>&nbsp;</td>\n";
} else {
- *ts << " <td valign=\"top\">" << event->dtStartTimeStr() << "</td>\n";
+ *ts << " <td valign=\"top\">"
+ << IncidenceFormatter::timeToString( event->dtStart(), true )
+ << "</td>\n";
}
if (event->isMultiDay() && (event->dtEnd().date() != date)) {
*ts << " <td>&nbsp;</td>\n";
} else {
- *ts << " <td valign=\"top\">" << event->dtEndTimeStr() << "</td>\n";
+ *ts << " <td valign=\"top\">"
+ << IncidenceFormatter::timeToString( event->dtEnd(), true )
+ << "</td>\n";
}
} else {
*ts << " <td>&nbsp;</td><td>&nbsp;</td>\n";
@@ -459,7 +467,7 @@ void HtmlExport::createTodo (TQTextStream *ts,Todo *todo)
if (completed) *ts << " class=\"done\"";
*ts << ">\n";
if (todo->hasDueDate()) {
- *ts << " " << todo->dtDueDateStr() << "\n";
+ *ts << " " << IncidenceFormatter::dateToString( todo->dtDue( true ) ) << "\n";
} else {
*ts << " &nbsp;\n";
}
diff --git a/libkcal/icalformatimpl.cpp b/libkcal/icalformatimpl.cpp
index a1655409..613d492e 100644
--- a/libkcal/icalformatimpl.cpp
+++ b/libkcal/icalformatimpl.cpp
@@ -55,6 +55,7 @@ static TQDateTime ICalDate2TQDate(const icaltimetype& t)
return TQDateTime(TQDate(year,t.month,t.day), TQTime(t.hour,t.minute,t.second));
}
+/*
static void _dumpIcaltime( const icaltimetype& t)
{
kdDebug(5800) << "--- Y: " << t.year << " M: " << t.month << " D: " << t.day
@@ -64,6 +65,16 @@ static void _dumpIcaltime( const icaltimetype& t)
kdDebug(5800) << "--- isUtc: " << icaltime_is_utc( t )<< endl;
kdDebug(5800) << "--- zoneId: " << icaltimezone_get_tzid( const_cast<icaltimezone*>( t.zone ) )<< endl;
}
+*/
+
+static TQString quoteForParam( const TQString &text )
+{
+ TQString tmp = text;
+ tmp.remove( '"' );
+ if ( tmp.contains( ';' ) || tmp.contains( ':' ) || tmp.contains( ',' ) )
+ return tmp; // libical quotes in this case already, see icalparameter_as_ical_string()
+ return TQString::fromLatin1( "\"" ) + tmp + TQString::fromLatin1( "\"" );
+}
const int gSecondsPerMinute = 60;
const int gSecondsPerHour = gSecondsPerMinute * 60;
@@ -565,7 +576,7 @@ icalproperty *ICalFormatImpl::writeOrganizer( const Person &organizer )
icalproperty *p = icalproperty_new_organizer("MAILTO:" + organizer.email().utf8());
if (!organizer.name().isEmpty()) {
- icalproperty_add_parameter( p, icalparameter_new_cn(organizer.name().utf8()) );
+ icalproperty_add_parameter( p, icalparameter_new_cn(quoteForParam(organizer.name()).utf8()) );
}
// TODO: Write dir, sent-by and language
@@ -578,7 +589,7 @@ 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_cn(quoteForParam(attendee->name()).utf8()));
}
@@ -649,14 +660,15 @@ icalproperty *ICalFormatImpl::writeAttendee(Attendee *attendee)
return p;
}
-icalproperty *ICalFormatImpl::writeAttachment(Attachment *att)
+icalproperty *ICalFormatImpl::writeAttachment( Attachment *att )
{
icalattach *attach;
- if (att->isUri())
- attach = icalattach_new_from_url( att->uri().utf8().data());
- else
- attach = icalattach_new_from_data ( (unsigned char *)att->data(), 0, 0);
- icalproperty *p = icalproperty_new_attach(attach);
+ if ( att->isUri() ) {
+ attach = icalattach_new_from_url( att->uri().utf8().data() );
+ } else {
+ attach = icalattach_new_from_data ( (unsigned char *)att->data(), 0, 0 );
+ }
+ icalproperty *p = icalproperty_new_attach( attach );
if ( !att->mimeType().isEmpty() ) {
icalproperty_add_parameter( p,
@@ -853,7 +865,7 @@ icalcomponent *ICalFormatImpl::writeAlarm(Alarm *alarm)
for (TQValueList<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()));
+ icalproperty_add_parameter(p,icalparameter_new_cn(quoteForParam((*ad).name()).utf8()));
}
icalcomponent_add_property(a,p);
}
@@ -892,7 +904,7 @@ icalcomponent *ICalFormatImpl::writeAlarm(Alarm *alarm)
offset = alarm->startOffset();
else
offset = alarm->endOffset();
- trigger.duration = icaldurationtype_from_int( offset.asSeconds() );
+ trigger.duration = writeICalDuration( offset.asSeconds() );
}
icalproperty *p = icalproperty_new_trigger(trigger);
if ( alarm->hasEndOffset() )
@@ -903,7 +915,7 @@ icalcomponent *ICalFormatImpl::writeAlarm(Alarm *alarm)
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)));
+ writeICalDuration(alarm->snoozeTime().value())));
}
// Custom properties
@@ -1000,9 +1012,9 @@ Event *ICalFormatImpl::readEvent( icalcomponent *vevent, icalcomponent *vtimezon
readIncidence( vevent, tz, event);
- icalproperty *p = icalcomponent_get_first_property(vevent,ICAL_ANY_PROPERTY);
+ icalproperty *p = icalcomponent_get_first_property( vevent, ICAL_ANY_PROPERTY );
-// int intvalue;
+ // int intvalue;
icaltimetype icaltime;
TQStringList categories;
@@ -1010,16 +1022,19 @@ Event *ICalFormatImpl::readEvent( icalcomponent *vevent, icalcomponent *vtimezon
bool dtEndProcessed = false;
- while (p) {
- icalproperty_kind kind = icalproperty_isa(p);
- switch (kind) {
+ 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) {
+ icaltime = icalproperty_get_dtend( p );
+ if ( icaltime.is_date ) {
// End date is non-inclusive
TQDate endDate = readICalDate( icaltime ).addDays( -1 );
- if ( mCompat ) mCompat->fixFloatingEnd( endDate );
+ if ( mCompat ) {
+ mCompat->fixFloatingEnd( endDate );
+ }
+
if ( endDate < event->dtStart().date() ) {
endDate = event->dtStart().date();
}
@@ -1032,26 +1047,26 @@ Event *ICalFormatImpl::readEvent( icalcomponent *vevent, icalcomponent *vtimezon
break;
case ICAL_RELATEDTO_PROPERTY: // related event (parent)
- event->setRelatedToUid(TQString::fromUtf8(icalproperty_get_relatedto(p)));
- mEventsRelate.append(event);
+ event->setRelatedToUid( TQString::fromUtf8( icalproperty_get_relatedto( p ) ) );
+ mEventsRelate.append( event );
break;
-
case ICAL_TRANSP_PROPERTY: // Transparency
- transparency = icalproperty_get_transp(p);
- if( transparency == ICAL_TRANSP_TRANSPARENT )
+ transparency = icalproperty_get_transp( p );
+ if ( transparency == ICAL_TRANSP_TRANSPARENT ) {
event->setTransparency( Event::Transparent );
- else
+ } else {
event->setTransparency( Event::Opaque );
+ }
break;
default:
-// kdDebug(5800) << "ICALFormat::readEvent(): Unknown property: " << kind
-// << endl;
+ // kdDebug(5800) << "ICALFormat::readEvent(): Unknown property: " << kind
+ // << endl;
break;
}
- p = icalcomponent_get_next_property(vevent,ICAL_ANY_PROPERTY);
+ p = icalcomponent_get_next_property( vevent, ICAL_ANY_PROPERTY );
}
// according to rfc2445 the dtend shouldn't be written when it equals
@@ -1060,13 +1075,15 @@ Event *ICalFormatImpl::readEvent( icalcomponent *vevent, icalcomponent *vtimezon
event->setDtEnd( event->dtStart() );
}
- TQString msade = event->nonKDECustomProperty("X-MICROSOFT-CDO-ALLDAYEVENT");
- if (!msade.isEmpty()) {
- bool floats = (msade == TQString::fromLatin1("TRUE"));
+ const TQString msade = event->nonKDECustomProperty("X-MICROSOFT-CDO-ALLDAYEVENT");
+ if ( !msade.isEmpty() ) {
+ const bool floats = ( msade == TQString::fromLatin1("TRUE") );
event->setFloats(floats);
}
- if ( mCompat ) mCompat->fixEmptySummary( event );
+ if ( mCompat ) {
+ mCompat->fixEmptySummary( event );
+ }
return event;
}
@@ -1096,7 +1113,8 @@ FreeBusy *ICalFormatImpl::readFreeBusy(icalcomponent *vfreebusy)
freebusy->setDtEnd(readICalDateTime(p, icaltime));
break;
- case ICAL_FREEBUSY_PROPERTY: { //Any FreeBusy Times
+ case ICAL_FREEBUSY_PROPERTY: //Any FreeBusy Times
+ {
icalperiodtype icalperiod = icalproperty_get_freebusy(p);
TQDateTime period_start = readICalDateTime(p, icalperiod.start);
Period period;
@@ -1107,12 +1125,21 @@ FreeBusy *ICalFormatImpl::readFreeBusy(icalcomponent *vfreebusy)
Duration duration = readICalDuration( icalperiod.duration );
period = Period(period_start, duration);
}
- TQCString param = icalproperty_get_parameter_as_string( p, "X-SUMMARY" );
- period.setSummary( TQString::fromUtf8( KCodecs::base64Decode( param ) ) );
- param = icalproperty_get_parameter_as_string( p, "X-LOCATION" );
- period.setLocation( TQString::fromUtf8( KCodecs::base64Decode( param ) ) );
+ icalparameter *param = icalproperty_get_first_parameter( p, ICAL_X_PARAMETER );
+ while ( param ) {
+ if ( strncmp( icalparameter_get_xname( param ), "X-SUMMARY", 9 ) == 0 ) {
+ period.setSummary( TQString::fromUtf8(
+ KCodecs::base64Decode( icalparameter_get_xvalue( param ) ) ) );
+ }
+ if ( strncmp( icalparameter_get_xname( param ), "X-LOCATION", 10 ) == 0 ) {
+ period.setLocation( TQString::fromUtf8(
+ KCodecs::base64Decode( icalparameter_get_xvalue( param ) ) ) );
+ }
+ param = icalproperty_get_next_parameter( p, ICAL_X_PARAMETER );
+ }
periods.append( period );
- break;}
+ break;
+ }
default:
// kdDebug(5800) << "ICalFormatImpl::readFreeBusy(): Unknown property: "
@@ -1256,31 +1283,70 @@ Attachment *ICalFormatImpl::readAttachment(icalproperty *attach)
{
Attachment *attachment = 0;
- icalvalue_kind value_kind = icalvalue_isa(icalproperty_get_value(attach));
+ const char *p;
+ icalvalue *value = icalproperty_get_value( attach );
- if ( value_kind == ICAL_ATTACH_VALUE || value_kind == ICAL_BINARY_VALUE ) {
- icalattach *a = icalproperty_get_attach(attach);
-
- int isurl = icalattach_get_is_url (a);
- if (isurl == 0)
- attachment = new Attachment((const char*)icalattach_get_data(a));
- else {
- attachment = new Attachment(TQString::fromUtf8(icalattach_get_url(a)));
+ switch( icalvalue_isa( value ) ) {
+ case ICAL_ATTACH_VALUE:
+ {
+ icalattach *a = icalproperty_get_attach( attach );
+ if ( !icalattach_get_is_url( a ) ) {
+ p = (const char *)icalattach_get_data( a );
+ if ( p ) {
+ attachment = new Attachment( p );
+ }
+ } else {
+ p = icalattach_get_url( a );
+ if ( p ) {
+ attachment = new Attachment( TQString::fromUtf8( p ) );
+ }
+ }
+ break;
+ }
+ case ICAL_BINARY_VALUE:
+ {
+ icalattach *a = icalproperty_get_attach( attach );
+ p = (const char *)icalattach_get_data( a );
+ if ( p ) {
+ attachment = new Attachment( p );
}
+ break;
}
- else if ( value_kind == ICAL_URI_VALUE ) {
- attachment = new Attachment(TQString::fromUtf8(icalvalue_get_uri(icalproperty_get_value(attach))));
+ case ICAL_URI_VALUE:
+ p = icalvalue_get_uri( value );
+ attachment = new Attachment( TQString::fromUtf8( p ) );
+ break;
+ default:
+ break;
}
- icalparameter *p = icalproperty_get_first_parameter(attach, ICAL_FMTTYPE_PARAMETER);
- if (p && attachment)
- attachment->setMimeType(TQString(icalparameter_get_fmttype(p)));
+ if ( attachment ) {
+ icalparameter *p =
+ icalproperty_get_first_parameter( attach, ICAL_FMTTYPE_PARAMETER );
+ if ( p ) {
+ attachment->setMimeType( TQString( icalparameter_get_fmttype( p ) ) );
+ }
- p = icalproperty_get_first_parameter(attach,ICAL_X_PARAMETER);
- while (p) {
- if ( strncmp (icalparameter_get_xname(p), "X-LABEL", 7) == 0 )
- attachment->setLabel( icalparameter_get_xvalue(p) );
- p = icalproperty_get_next_parameter(attach, ICAL_X_PARAMETER);
+ p = icalproperty_get_first_parameter( attach, ICAL_X_PARAMETER );
+ while ( p ) {
+ TQString xname = TQString( icalparameter_get_xname( p ) ).upper();
+ TQString xvalue = TQString::fromUtf8( icalparameter_get_xvalue( p ) );
+ if ( xname == "X-CONTENT-DISPOSITION" ) {
+ attachment->setShowInline( xvalue.lower() == "inline" );
+ }
+ if ( xname == "X-LABEL" ) {
+ attachment->setLabel( xvalue );
+ }
+ p = icalproperty_get_next_parameter( attach, ICAL_X_PARAMETER );
+ }
+
+ p = icalproperty_get_first_parameter( attach, ICAL_X_PARAMETER );
+ while ( p ) {
+ if ( strncmp( icalparameter_get_xname( p ), "X-LABEL", 7 ) == 0 ) {
+ attachment->setLabel( TQString::fromUtf8( icalparameter_get_xvalue( p ) ) );
+ }
+ p = icalproperty_get_next_parameter( attach, ICAL_X_PARAMETER );
+ }
}
return attachment;
@@ -1482,32 +1548,48 @@ void ICalFormatImpl::readIncidenceBase(icalcomponent *parent,IncidenceBase *inci
{
icalproperty *p = icalcomponent_get_first_property(parent,ICAL_ANY_PROPERTY);
- while (p) {
- icalproperty_kind kind = icalproperty_isa(p);
+ bool uidProcessed = false;
+
+ while ( p ) {
+ icalproperty_kind kind = icalproperty_isa( p );
switch (kind) {
case ICAL_UID_PROPERTY: // unique id
- incidenceBase->setUid(TQString::fromUtf8(icalproperty_get_uid(p)));
+ uidProcessed = true;
+ incidenceBase->setUid( TQString::fromUtf8(icalproperty_get_uid( p ) ) );
break;
case ICAL_ORGANIZER_PROPERTY: // organizer
- incidenceBase->setOrganizer( readOrganizer(p));
+ incidenceBase->setOrganizer( readOrganizer( p ) );
break;
case ICAL_ATTENDEE_PROPERTY: // attendee
- incidenceBase->addAttendee(readAttendee(p));
+ incidenceBase->addAttendee( readAttendee( p ) );
break;
case ICAL_COMMENT_PROPERTY:
incidenceBase->addComment(
- TQString::fromUtf8(icalproperty_get_comment(p)));
+ TQString::fromUtf8( icalproperty_get_comment( p ) ) );
break;
default:
break;
}
- p = icalcomponent_get_next_property(parent,ICAL_ANY_PROPERTY);
+ p = icalcomponent_get_next_property( parent, ICAL_ANY_PROPERTY );
+ }
+
+ if ( !uidProcessed ) {
+ kdWarning() << "The incidence didn't have any UID! Report a bug "
+ << "to the application that generated this file."
+ << endl;
+
+ // Our in-memory incidence has a random uid generated in Event's ctor.
+ // Make it empty so it matches what's in the file:
+ incidenceBase->setUid( TQString() );
+
+ // Otherwise, next time we read the file, this function will return
+ // an event with another random uid and we will have two events in the calendar.
}
// kpilot stuff
@@ -1736,7 +1818,7 @@ void ICalFormatImpl::readAlarm(icalcomponent *alarm,Incidence *incidence)
}
case ICAL_DURATION_PROPERTY: {
icaldurationtype duration = icalproperty_get_duration(p);
- ialarm->setSnoozeTime(icaldurationtype_as_int(duration)/60);
+ ialarm->setSnoozeTime( readICalDuration( duration ) );
break;
}
case ICAL_REPEAT_PROPERTY:
@@ -1937,13 +2019,16 @@ TQDate ICalFormatImpl::readICalDate(icaltimetype t)
icaldurationtype ICalFormatImpl::writeICalDuration(int seconds)
{
+ // should be able to use icaldurationtype_from_int(), except we know
+ // that some older tools do not properly support weeks. So we never
+ // set a week duration, only days
+
icaldurationtype d;
d.is_neg = (seconds<0)?1:0;
if (seconds<0) seconds = -seconds;
- d.weeks = seconds / gSecondsPerWeek;
- seconds %= gSecondsPerWeek;
+ d.weeks = 0;
d.days = seconds / gSecondsPerDay;
seconds %= gSecondsPerDay;
d.hours = seconds / gSecondsPerHour;
@@ -2001,7 +2086,7 @@ icalcomponent *ICalFormatImpl::createCalendarComponent(Calendar *cal)
// 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)
+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.
@@ -2031,6 +2116,14 @@ bool ICalFormatImpl::populate( Calendar *cal, icalcomponent *calendar)
return false;
} else {
const char *version = icalproperty_get_version(p);
+ if ( !version ) {
+ kdDebug(5800) << "No VERSION property found" << endl;
+ mParent->setException( new ErrorFormat(
+ ErrorFormat::CalVersionUnknown,
+ i18n( "No VERSION property found" ) ) );
+ return false;
+ }
+
// kdDebug(5800) << "VCALENDAR version: '" << version << "'" << endl;
if (strcmp(version,"1.0") == 0) {
@@ -2071,7 +2164,11 @@ bool ICalFormatImpl::populate( Calendar *cal, icalcomponent *calendar)
TQString originalUid = todo->uid();
todo->setUid(originalUid + QString("-recur-%1").arg(todo->recurrenceID().toTime_t()));
if (!cal->todo(todo->uid())) {
- cal->addTodo(todo);
+ if ( !cal->addTodo( todo ) ) {
+ cal->endBatchAdding();
+ // If the user pressed cancel, return true, it's not an error.
+ return cal->exception() && cal->exception()->errorCode() == ErrorFormat::UserCancel;
+ }
if (!cal->event(originalUid)) {
printf("FIXME! [WARNING] Parent for child event does not yet exist!\n\r");
}
@@ -2085,7 +2182,11 @@ bool ICalFormatImpl::populate( Calendar *cal, icalcomponent *calendar)
}
else {
if (!cal->todo(todo->uid())) {
- cal->addTodo(todo);
+ if ( !cal->addTodo( todo ) ) {
+ cal->endBatchAdding();
+ // If the user pressed cancel, return true, it's not an error.
+ return cal->exception() && cal->exception()->errorCode() == ErrorFormat::UserCancel;
+ }
} else {
delete todo;
mTodosRelate.remove( todo );
@@ -2119,7 +2220,11 @@ bool ICalFormatImpl::populate( Calendar *cal, icalcomponent *calendar)
}
else {
if (!cal->event(event->uid())) {
- cal->addEvent(event);
+ if ( !cal->addEvent( event ) ) {
+ cal->endBatchAdding();
+ // If the user pressed cancel, return true, it's not an error.
+ return cal->exception() && cal->exception()->errorCode() == ErrorFormat::UserCancel;
+ }
} else {
delete event;
mEventsRelate.remove( event );
@@ -2153,7 +2258,11 @@ bool ICalFormatImpl::populate( Calendar *cal, icalcomponent *calendar)
}
else {
if (!cal->journal(journal->uid())) {
- cal->addJournal(journal);
+ if ( !cal->addJournal(journal) ) {
+ cal->endBatchAdding();
+ // If the user pressed cancel, return true, it's not an error.
+ return cal->exception() && cal->exception()->errorCode() == ErrorFormat::UserCancel;
+ }
} else {
delete journal;
}
@@ -2162,6 +2271,8 @@ bool ICalFormatImpl::populate( Calendar *cal, icalcomponent *calendar)
c = icalcomponent_get_next_component(calendar,ICAL_VJOURNAL_COMPONENT);
}
+ cal->endBatchAdding();
+
// Post-Process list of events with relations, put Event objects in relation
Event::List::ConstIterator eIt;
for ( eIt = mEventsRelate.begin(); eIt != mEventsRelate.end(); ++eIt ) {
diff --git a/libkcal/incidence.h b/libkcal/incidence.h
index 477b758c..89d45b08 100644
--- a/libkcal/incidence.h
+++ b/libkcal/incidence.h
@@ -66,6 +66,36 @@ class LIBKCAL_EXPORT Incidence : public IncidenceBase, public Recurrence::Observ
};
/**
+ This class implements a visitor for adding an Incidence to a resource
+ plus subresource supporting addEvent(), addTodo() and addJournal() calls.
+ */
+ template<class T>
+ class AddSubResourceVisitor : public IncidenceBase::Visitor
+ {
+ public:
+ AddSubResourceVisitor( T *r, const TQString &subResource )
+ : mResource( r ), mSubResource( subResource ) {}
+
+ protected:
+ bool visit( Event *e )
+ {
+ return mResource->addEvent( e, mSubResource );
+ }
+ bool visit( Todo *t )
+ {
+ return mResource->addTodo( t, mSubResource );
+ }
+ bool visit( Journal *j )
+ {
+ return mResource->addJournal( j, mSubResource );
+ }
+
+ private:
+ T *mResource;
+ TQString mSubResource;
+ };
+
+ /**
This class implements a visitor for deleting an Incidence from a resource
supporting deleteEvent(), deleteTodo() and deleteJournal() calls.
*/
diff --git a/libkcal/incidencebase.cpp b/libkcal/incidencebase.cpp
index 2aeba830..a8a6bdb0 100644
--- a/libkcal/incidencebase.cpp
+++ b/libkcal/incidencebase.cpp
@@ -59,6 +59,10 @@ IncidenceBase::IncidenceBase(const IncidenceBase &i) :
mSyncStatus = i.mSyncStatus;
mComments = i.mComments;
+ // The copied object is a new one, so it isn't observed by the observer
+ // of the original object.
+ mObservers.clear();
+
mAttendees.setAutoDelete( true );
}
@@ -404,7 +408,9 @@ void IncidenceBase::updated()
while( it.current() ) {
Observer *o = it.current();
++it;
- o->incidenceUpdated( this );
+ if ( o ) {
+ o->incidenceUpdated( this );
+ }
}
}
diff --git a/libkcal/incidencebase.h b/libkcal/incidencebase.h
index 72455b43..fa3a1a61 100644
--- a/libkcal/incidencebase.h
+++ b/libkcal/incidencebase.h
@@ -131,16 +131,28 @@ class LIBKCAL_EXPORT IncidenceBase : public CustomProperties
/** for setting the event's starting date/time with a TQDateTime. */
virtual void setDtStart( const TQDateTime &dtStart );
/** returns an event's starting date/time as a TQDateTime. */
+
virtual TQDateTime dtStart() const;
- /** returns an event's starting time as a string formatted according to the
- users locale settings */
- virtual TQString dtStartTimeStr() const;
- /** returns an event's starting date as a string formatted according to the
- users locale settings */
- virtual TQString dtStartDateStr( bool shortfmt = true ) const;
- /** returns an event's starting date and time as a string formatted according
- to the users locale settings */
- virtual TQString dtStartStr() const;
+
+ /**
+ returns an event's starting time as a string formatted according to the
+ users locale settings.
+ @deprecated use IncidenceFormatter::timeToString()
+ */
+ virtual KDE_DEPRECATED TQString dtStartTimeStr() const;
+
+ /**
+ returns an event's starting date as a string formatted according to the
+ users locale settings
+ @deprecated use IncidenceFormatter::dateToString()
+ */
+ virtual KDE_DEPRECATED TQString dtStartDateStr( bool shortfmt = true ) const;
+ /**
+ returns an event's starting date and time as a string formatted according
+ to the users locale settings
+ @deprecated use IncidenceFormatter::dateTimeToString()
+ */
+ virtual KDE_DEPRECATED TQString dtStartStr() const;
virtual void setDuration( int seconds );
int duration() const;
diff --git a/libkcal/incidenceformatter.cpp b/libkcal/incidenceformatter.cpp
index 61861171..cebdd0b1 100644
--- a/libkcal/incidenceformatter.cpp
+++ b/libkcal/incidenceformatter.cpp
@@ -3,6 +3,7 @@
Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
Copyright (c) 2004 Reinhold Kainhofer <reinhold@kainhofer.com>
+ Copyright (c) 2009-2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
@@ -42,27 +43,28 @@
#include <kabc/stdaddressbook.h>
#include <kapplication.h>
-// #include <kdebug.h>
+#include <kemailsettings.h>
#include <klocale.h>
#include <kglobal.h>
#include <kiconloader.h>
+#include <kcalendarsystem.h>
+#include <kmimetype.h>
#include <tqbuffer.h>
#include <tqstylesheet.h>
#include <tqdatetime.h>
+#include <tqregexp.h>
#include <time.h>
-
using namespace KCal;
+/*******************
+ * General helpers
+ *******************/
-/*******************************************************************
- * Helper functions for the extensive display (event viewer)
- *******************************************************************/
-
-static TQString eventViewerAddLink( const TQString &ref, const TQString &text,
+static TQString htmlAddLink( const TQString &ref, const TQString &text,
bool newline = true )
{
TQString tmpStr( "<a href=\"" + ref + "\">" + text + "</a>" );
@@ -70,7 +72,7 @@ static TQString eventViewerAddLink( const TQString &ref, const TQString &text,
return tmpStr;
}
-static TQString eventViewerAddTag( const TQString & tag, const TQString & text )
+static TQString htmlAddTag( const TQString & tag, const TQString & text )
{
int numLineBreaks = text.contains( "\n" );
TQString str = "<" + tag + ">";
@@ -94,134 +96,299 @@ static TQString eventViewerAddTag( const TQString & tag, const TQString & text )
return tmpStr;
}
-static TQString linkPerson( const TQString& email, TQString name, TQString uid )
+static bool iamAttendee( Attendee *attendee )
+{
+ // Check if I'm this attendee
+
+ bool iam = false;
+ KEMailSettings settings;
+ TQStringList profiles = settings.profiles();
+ for( TQStringList::Iterator it=profiles.begin(); it!=profiles.end(); ++it ) {
+ settings.setProfile( *it );
+ if ( settings.getSetting( KEMailSettings::EmailAddress ) == attendee->email() ) {
+ iam = true;
+ break;
+ }
+ }
+ return iam;
+}
+
+static bool iamOrganizer( Incidence *incidence )
+{
+ // Check if I'm the organizer for this incidence
+
+ if ( !incidence ) {
+ return false;
+ }
+
+ bool iam = false;
+ KEMailSettings settings;
+ TQStringList profiles = settings.profiles();
+ for( TQStringList::Iterator it=profiles.begin(); it!=profiles.end(); ++it ) {
+ settings.setProfile( *it );
+ if ( settings.getSetting( KEMailSettings::EmailAddress ) == incidence->organizer().email() ) {
+ iam = true;
+ break;
+ }
+ }
+ return iam;
+}
+
+static bool senderIsOrganizer( Incidence *incidence, const TQString &sender )
+{
+ // Check if the specified sender is the organizer
+
+ if ( !incidence || sender.isEmpty() ) {
+ return true;
+ }
+ bool isorg = true;
+ TQString senderName, senderEmail;
+ if ( KPIM::getNameAndMail( sender, senderName, senderEmail ) ) {
+ // for this heuristic, we say the sender is the organizer if either the name or the email match.
+ if ( incidence->organizer().email() != senderEmail &&
+ incidence->organizer().name() != senderName ) {
+ isorg = false;
+ }
+ }
+ return isorg;
+}
+
+static TQString firstAttendeeName( Incidence *incidence, const TQString &defName )
+{
+ TQString name;
+ if ( !incidence ) {
+ return name;
+ }
+
+ Attendee::List attendees = incidence->attendees();
+ if( attendees.count() > 0 ) {
+ Attendee *attendee = *attendees.begin();
+ name = attendee->name();
+ if ( name.isEmpty() ) {
+ name = attendee->email();
+ }
+ if ( name.isEmpty() ) {
+ name = defName;
+ }
+ }
+ return name;
+}
+
+/*******************************************************************
+ * Helper functions for the extensive display (display viewer)
+ *******************************************************************/
+
+static TQString displayViewLinkPerson( const TQString& email, TQString name, TQString uid )
{
// Make the search, if there is an email address to search on,
// and either name or uid is missing
if ( !email.isEmpty() && ( name.isEmpty() || uid.isEmpty() ) ) {
KABC::AddressBook *add_book = KABC::StdAddressBook::self( true );
KABC::Addressee::List addressList = add_book->findByEmail( email );
- KABC::Addressee o = addressList.first();
- if ( !o.isEmpty() && addressList.size() < 2 ) {
- if ( name.isEmpty() )
- // No name set, so use the one from the addressbook
- name = o.formattedName();
- uid = o.uid();
- } else
- // Email not found in the addressbook. Don't make a link
- uid = TQString::null;
+ if ( !addressList.isEmpty() ) {
+ KABC::Addressee o = addressList.first();
+ if ( !o.isEmpty() && addressList.size() < 2 ) {
+ if ( name.isEmpty() ) {
+ // No name set, so use the one from the addressbook
+ name = o.formattedName();
+ }
+ uid = o.uid();
+ } else {
+ // Email not found in the addressbook. Don't make a link
+ uid = TQString::null;
+ }
+ }
}
- kdDebug(5850) << "formatAttendees: uid = " << uid << endl;
// Show the attendee
- TQString tmpString = "<li>";
+ TQString tmpString;
if ( !uid.isEmpty() ) {
// There is a UID, so make a link to the addressbook
- if ( name.isEmpty() )
+ if ( name.isEmpty() ) {
// Use the email address for text
- tmpString += eventViewerAddLink( "uid:" + uid, email );
- else
- tmpString += eventViewerAddLink( "uid:" + uid, name );
+ tmpString += htmlAddLink( "uid:" + uid, email );
+ } else {
+ tmpString += htmlAddLink( "uid:" + uid, name );
+ }
} else {
// No UID, just show some text
tmpString += ( name.isEmpty() ? email : name );
}
- tmpString += '\n';
// Make the mailto link
if ( !email.isEmpty() ) {
- KCal::Person person( name, email );
KURL mailto;
mailto.setProtocol( "mailto" );
- mailto.setPath( person.fullName() );
- tmpString += eventViewerAddLink( mailto.url(), TQString::null );
+ mailto.setPath( email );
+ const TQString iconPath =
+ KGlobal::iconLoader()->iconPath( "mail_new", KIcon::Small );
+ tmpString += "&nbsp;" +
+ htmlAddLink( mailto.url(),
+ "<img valign=\"top\" src=\"" + iconPath + "\">" );
}
- tmpString += "</li>\n";
return tmpString;
}
-static TQString eventViewerFormatAttendees( Incidence *event )
+static TQString displayViewFormatAttendeeRoleList( Incidence *incidence, Attendee::Role role )
{
TQString tmpStr;
- Attendee::List attendees = event->attendees();
- if ( attendees.count() ) {
-
- // Add organizer link
- tmpStr += eventViewerAddTag( "i", i18n("Organizer") );
- tmpStr += "<ul>";
- tmpStr += linkPerson( event->organizer().email(),
- event->organizer().name(), TQString::null );
- tmpStr += "</ul>";
-
- // Add attendees links
- tmpStr += eventViewerAddTag( "i", i18n("Attendees") );
- tmpStr += "<ul>";
- Attendee::List::ConstIterator it;
- for( it = attendees.begin(); it != attendees.end(); ++it ) {
- Attendee *a = *it;
- tmpStr += linkPerson( a->email(), a->name(), a->uid() );
- if ( !a->delegator().isEmpty() ) {
- tmpStr += i18n(" (delegated by %1)" ).arg( a->delegator() );
- }
- if ( !a->delegate().isEmpty() ) {
- tmpStr += i18n(" (delegated to %1)" ).arg( a->delegate() );
- }
+ Attendee::List::ConstIterator it;
+ Attendee::List attendees = incidence->attendees();
+
+ for ( it = attendees.begin(); it != attendees.end(); ++it ) {
+ Attendee *a = *it;
+ if ( a->role() != role ) {
+ // skip this role
+ continue;
+ }
+ if ( a->email() == incidence->organizer().email() ) {
+ // skip attendee that is also the organizer
+ continue;
}
- tmpStr += "</ul>";
+ tmpStr += displayViewLinkPerson( a->email(), a->name(), a->uid() );
+ if ( !a->delegator().isEmpty() ) {
+ tmpStr += i18n(" (delegated by %1)" ).arg( a->delegator() );
+ }
+ if ( !a->delegate().isEmpty() ) {
+ tmpStr += i18n(" (delegated to %1)" ).arg( a->delegate() );
+ }
+ tmpStr += "<br>";
+ }
+ if ( tmpStr.endsWith( "<br>" ) ) {
+ tmpStr.truncate( tmpStr.length() - 4 );
}
return tmpStr;
}
-static TQString eventViewerFormatAttachments( Incidence *i )
+static TQString displayViewFormatAttendees( Incidence *incidence )
+{
+ TQString tmpStr, str;
+
+ // Add organizer link
+ int attendeeCount = incidence->attendees().count();
+ if ( attendeeCount > 1 ||
+ ( attendeeCount == 1 &&
+ incidence->organizer().email() != incidence->attendees().first()->email() ) ) {
+ tmpStr += "<tr>";
+ tmpStr += "<td><b>" + i18n( "Organizer:" ) + "</b></td>";
+ tmpStr += "<td>" +
+ displayViewLinkPerson( incidence->organizer().email(),
+ incidence->organizer().name(),
+ TQString::null ) +
+ "</td>";
+ tmpStr += "</tr>";
+ }
+
+ // Add "chair"
+ str = displayViewFormatAttendeeRoleList( incidence, Attendee::Chair );
+ if ( !str.isEmpty() ) {
+ tmpStr += "<tr>";
+ tmpStr += "<td><b>" + i18n( "Chair:" ) + "</b></td>";
+ tmpStr += "<td>" + str + "</td>";
+ tmpStr += "</tr>";
+ }
+
+ // Add required participants
+ str = displayViewFormatAttendeeRoleList( incidence, Attendee::ReqParticipant );
+ if ( !str.isEmpty() ) {
+ tmpStr += "<tr>";
+ tmpStr += "<td><b>" + i18n( "Required Participants:" ) + "</b></td>";
+ tmpStr += "<td>" + str + "</td>";
+ tmpStr += "</tr>";
+ }
+
+ // Add optional participants
+ str = displayViewFormatAttendeeRoleList( incidence, Attendee::OptParticipant );
+ if ( !str.isEmpty() ) {
+ tmpStr += "<tr>";
+ tmpStr += "<td><b>" + i18n( "Optional Participants:" ) + "</b></td>";
+ tmpStr += "<td>" + str + "</td>";
+ tmpStr += "</tr>";
+ }
+
+ // Add observers
+ str = displayViewFormatAttendeeRoleList( incidence, Attendee::NonParticipant );
+ if ( !str.isEmpty() ) {
+ tmpStr += "<tr>";
+ tmpStr += "<td><b>" + i18n( "Observers:" ) + "</b></td>";
+ tmpStr += "<td>" + str + "</td>";
+ tmpStr += "</tr>";
+ }
+
+ return tmpStr;
+}
+
+static TQString displayViewFormatAttachments( Incidence *incidence )
{
TQString tmpStr;
- Attachment::List as = i->attachments();
- if ( as.count() > 0 ) {
- Attachment::List::ConstIterator it;
- for( it = as.begin(); it != as.end(); ++it ) {
- if ( (*it)->isUri() ) {
- TQString name;
- if ( (*it)->uri().startsWith( "kmail:" ) )
- name = i18n( "Show mail" );
- else
+ Attachment::List as = incidence->attachments();
+ Attachment::List::ConstIterator it;
+ uint count = 0;
+ for( it = as.begin(); it != as.end(); ++it ) {
+ count++;
+ if ( (*it)->isUri() ) {
+ TQString name;
+ if ( (*it)->uri().startsWith( "kmail:" ) ) {
+ name = i18n( "Show mail" );
+ } else {
+ if ( (*it)->label().isEmpty() ) {
name = (*it)->uri();
- tmpStr += eventViewerAddLink( (*it)->uri(), name );
- tmpStr += "<br>";
+ } else {
+ name = (*it)->label();
+ }
}
+ tmpStr += htmlAddLink( (*it)->uri(), name );
+ } else {
+ tmpStr += htmlAddLink( "ATTACH:" + incidence->uid() + ':' + (*it)->label(),
+ (*it)->label(), false );
+ }
+ if ( count < as.count() ) {
+ tmpStr += "<br>";
}
}
return tmpStr;
}
-/*
- FIXME:This function depends of kaddressbook. Is necessary a new
- type of event?
-*/
-static TQString eventViewerFormatBirthday( Event *event )
+static TQString displayViewFormatCategories( Incidence *incidence )
{
- if ( !event) return TQString::null;
- if ( event->customProperty("KABC","BIRTHDAY") != "YES" ) return TQString::null;
+ // We do not use Incidence::categoriesStr() since it does not have whitespace
+ return incidence->categories().join( ", " );
+}
+
+static TQString displayViewFormatCreationDate( Incidence *incidence )
+{
+ return i18n( "Creation date: %1" ).
+ arg( IncidenceFormatter::dateTimeToString( incidence->created(), false, true ) );
+}
+
+static TQString displayViewFormatBirthday( Event *event )
+{
+ if ( !event ) {
+ return TQString::null;
+ }
+ if ( event->customProperty("KABC","BIRTHDAY") != "YES" ) {
+ return TQString::null;
+ }
TQString uid = event->customProperty("KABC","UID-1");
TQString name = event->customProperty("KABC","NAME-1");
TQString email= event->customProperty("KABC","EMAIL-1");
- TQString tmpString = "<ul>";
- tmpString += linkPerson( email, name, uid );
+ TQString tmpStr = displayViewLinkPerson( email, name, uid );
if ( event->customProperty( "KABC", "ANNIVERSARY") == "YES" ) {
uid = event->customProperty("KABC","UID-2");
name = event->customProperty("KABC","NAME-2");
email= event->customProperty("KABC","EMAIL-2");
- tmpString += linkPerson( email, name, uid );
+ tmpStr += "<br>";
+ tmpStr += displayViewLinkPerson( email, name, uid );
}
- tmpString += "</ul>";
- return tmpString;
+ return tmpStr;
}
-static TQString eventViewerFormatHeader( Incidence *incidence )
+static TQString displayViewFormatHeader( Incidence *incidence )
{
TQString tmpStr = "<table><tr>";
@@ -230,32 +397,41 @@ static TQString eventViewerFormatHeader( Incidence *incidence )
tmpStr += "<td>";
if ( incidence->type() == "Event" ) {
- tmpStr += "<img src=\"" +
- KGlobal::iconLoader()->iconPath( "appointment", KIcon::Small ) +
- "\">";
+ TQString iconPath;
+ if ( incidence->customProperty( "KABC", "BIRTHDAY" ) == "YES" ) {
+ if ( incidence->customProperty( "KABC", "ANNIVERSARY" ) == "YES" ) {
+ iconPath =
+ KGlobal::iconLoader()->iconPath( "calendaranniversary", KIcon::Small );
+ } else {
+ iconPath = KGlobal::iconLoader()->iconPath( "calendarbirthday", KIcon::Small );
+ }
+ } else {
+ iconPath = KGlobal::iconLoader()->iconPath( "appointment", KIcon::Small );
+ }
+ tmpStr += "<img valign=\"top\" src=\"" + iconPath + "\">";
}
if ( incidence->type() == "Todo" ) {
- tmpStr += "<img src=\"" +
+ tmpStr += "<img valign=\"top\" src=\"" +
KGlobal::iconLoader()->iconPath( "todo", KIcon::Small ) +
"\">";
}
if ( incidence->type() == "Journal" ) {
- tmpStr += "<img src=\"" +
+ tmpStr += "<img valign=\"top\" src=\"" +
KGlobal::iconLoader()->iconPath( "journal", KIcon::Small ) +
"\">";
}
if ( incidence->isAlarmEnabled() ) {
- tmpStr += "<img src=\"" +
+ tmpStr += "<img valign=\"top\" src=\"" +
KGlobal::iconLoader()->iconPath( "bell", KIcon::Small ) +
"\">";
}
if ( incidence->doesRecur() ) {
- tmpStr += "<img src=\"" +
+ tmpStr += "<img valign=\"top\" src=\"" +
KGlobal::iconLoader()->iconPath( "recur", KIcon::Small ) +
"\">";
}
if ( incidence->isReadOnly() ) {
- tmpStr += "<img src=\"" +
+ tmpStr += "<img valign=\"top\" src=\"" +
KGlobal::iconLoader()->iconPath( "readonlyevent", KIcon::Small ) +
"\">";
}
@@ -263,245 +439,409 @@ static TQString eventViewerFormatHeader( Incidence *incidence )
tmpStr += "</td>";
}
- tmpStr += "<td>"
- + eventViewerAddTag( "u",
- eventViewerAddTag( "b", incidence->summary() ) )
- + "</td>";
- tmpStr += "</tr></table><br>";
+ tmpStr += "<td>";
+ tmpStr += "<b><u>" + incidence->summary() + "</u></b>";
+ tmpStr += "</td>";
+
+ tmpStr += "</tr></table>";
return tmpStr;
}
-static TQString eventViewerFormatEvent( Event *event )
+static TQString displayViewFormatEvent( Calendar *calendar, Event *event,
+ const TQDate &date )
{
- if ( !event ) return TQString::null;
- TQString tmpStr = eventViewerFormatHeader( event );
+ if ( !event ) {
+ return TQString::null;
+ }
+
+ TQString tmpStr = displayViewFormatHeader( event );
tmpStr += "<table>";
+ tmpStr += "<col width=\"25%\"/>";
+ tmpStr += "<col width=\"75%\"/>";
+
+ if ( calendar ) {
+ TQString calStr = IncidenceFormatter::resourceString( calendar, event );
+ if ( !calStr.isEmpty() ) {
+ tmpStr += "<tr>";
+ tmpStr += "<td><b>" + i18n( "Calendar:" ) + "</b></td>";
+ tmpStr += "<td>" + calStr + "</td>";
+ tmpStr += "</tr>";
+ }
+ }
+
+ if ( !event->location().isEmpty() ) {
+ tmpStr += "<tr>";
+ tmpStr += "<td><b>" + i18n( "Location:" ) + "</b></td>";
+ tmpStr += "<td>" + event->location() + "</td>";
+ tmpStr += "</tr>";
+ }
+
+ TQDateTime startDt = event->dtStart();
+ TQDateTime endDt = event->dtEnd();
+ if ( event->doesRecur() ) {
+ if ( date.isValid() ) {
+ TQDateTime dt( date, TQTime( 0, 0, 0 ) );
+ int diffDays = startDt.daysTo( dt );
+ dt = dt.addSecs( -1 );
+ startDt.setDate( event->recurrence()->getNextDateTime( dt ).date() );
+ if ( event->hasEndDate() ) {
+ endDt = endDt.addDays( diffDays );
+ if ( startDt > endDt ) {
+ startDt.setDate( event->recurrence()->getPreviousDateTime( dt ).date() );
+ endDt = startDt.addDays( event->dtStart().daysTo( event->dtEnd() ) );
+ }
+ }
+ }
+ }
tmpStr += "<tr>";
if ( event->doesFloat() ) {
if ( event->isMultiDay() ) {
- tmpStr += "<td align=\"right\"><b>" + i18n( "Time" ) + "</b></td>";
- tmpStr += "<td>" + i18n("<beginTime> - <endTime>","%1 - %2")
- .arg( event->dtStartDateStr() )
- .arg( event->dtEndDateStr() ) + "</td>";
+ tmpStr += "<td><b>" + i18n( "Date:" ) + "</b></td>";
+ tmpStr += "<td>" +
+ i18n("<beginDate> - <endDate>","%1 - %2").
+ arg( IncidenceFormatter::dateToString( startDt, false ) ).
+ arg( IncidenceFormatter::dateToString( endDt, false ) ) +
+ "</td>";
} else {
- tmpStr += "<td align=\"right\"><b>" + i18n( "Date" ) + "</b></td>";
- tmpStr += "<td>" + i18n("date as string","%1").arg( event->dtStartDateStr() ) + "</td>";
+ tmpStr += "<td><b>" + i18n( "Date:" ) + "</b></td>";
+ tmpStr += "<td>" +
+ i18n("date as string","%1").
+ arg( IncidenceFormatter::dateToString( startDt, false ) ) +
+ "</td>";
}
} else {
if ( event->isMultiDay() ) {
- tmpStr += "<td align=\"right\"><b>" + i18n( "Time" ) + "</b></td>";
- tmpStr += "<td>" + i18n("<beginTime> - <endTime>","%1 - %2")
- .arg( event->dtStartStr() )
- .arg( event->dtEndStr() ) + "</td>";
+ tmpStr += "<td><b>" + i18n( "Date:" ) + "</b></td>";
+ tmpStr += "<td>" +
+ i18n("<beginDate> - <endDate>","%1 - %2").
+ arg( IncidenceFormatter::dateToString( startDt, false ) ).
+ arg( IncidenceFormatter::dateToString( endDt, false ) ) +
+ "</td>";
} else {
- tmpStr += "<td align=\"right\"><b>" + i18n( "Time" ) + "</b></td>";
- if ( event->hasEndDate() && event->dtStart() != event->dtEnd()) {
- tmpStr += "<td>" + i18n("<beginTime> - <endTime>","%1 - %2")
- .arg( event->dtStartTimeStr() )
- .arg( event->dtEndTimeStr() ) + "</td>";
+ tmpStr += "<td><b>" + i18n( "Date:" ) + "</b></td>";
+ tmpStr += "<td>" +
+ i18n("date as string","%1").
+ arg( IncidenceFormatter::dateToString( startDt, false ) ) +
+ "</td>";
+
+ tmpStr += "</tr><tr>";
+ tmpStr += "<td><b>" + i18n( "Time:" ) + "</b></td>";
+ if ( event->hasEndDate() && startDt != endDt ) {
+ tmpStr += "<td>" +
+ i18n("<beginTime> - <endTime>","%1 - %2").
+ arg( IncidenceFormatter::timeToString( startDt, true ) ).
+ arg( IncidenceFormatter::timeToString( endDt, true ) ) +
+ "</td>";
} else {
- tmpStr += "<td>" + event->dtStartTimeStr() + "</td>";
+ tmpStr += "<td>" +
+ IncidenceFormatter::timeToString( startDt, true ) +
+ "</td>";
}
- tmpStr += "</tr><tr>";
- tmpStr += "<td align=\"right\"><b>" + i18n( "Date" ) + "</b></td>";
- tmpStr += "<td>" + i18n("date as string","%1")
- .arg( event->dtStartDateStr() ) + "</td>";
}
}
tmpStr += "</tr>";
- if ( event->customProperty("KABC","BIRTHDAY")== "YES" ) {
+ TQString durStr = IncidenceFormatter::durationString( event );
+ if ( !durStr.isEmpty() ) {
tmpStr += "<tr>";
- tmpStr += "<td align=\"right\"><b>" + i18n( "Birthday" ) + "</b></td>";
- tmpStr += "<td>" + eventViewerFormatBirthday( event ) + "</td>";
+ tmpStr += "<td><b>" + i18n( "Duration:" ) + "</b></td>";
+ tmpStr += "<td>" + durStr + "</td>";
tmpStr += "</tr>";
- tmpStr += "</table>";
- return tmpStr;
}
- if ( !event->description().isEmpty() ) {
+ if ( event->doesRecur() ) {
tmpStr += "<tr>";
- tmpStr += "<td align=\"right\"><b>" + i18n( "Description" ) + "</b></td>";
- tmpStr += "<td>" + eventViewerAddTag( "p", event->description() ) + "</td>";
+ tmpStr += "<td><b>" + i18n( "Recurrence:" ) + "</b></td>";
+ tmpStr += "<td>" +
+ IncidenceFormatter::recurrenceString( event ) +
+ "</td>";
tmpStr += "</tr>";
}
- if ( !event->location().isEmpty() ) {
+ if ( event->customProperty("KABC","BIRTHDAY")== "YES" ) {
tmpStr += "<tr>";
- tmpStr += "<td align=\"right\"><b>" + i18n( "Location" ) + "</b></td>";
- tmpStr += "<td>" + event->location() + "</td>";
+ if ( event->customProperty( "KABC", "ANNIVERSARY" ) == "YES" ) {
+ tmpStr += "<td><b>" + i18n( "Anniversary:" ) + "</b></td>";
+ } else {
+ tmpStr += "<td><b>" + i18n( "Birthday:" ) + "</b></td>";
+ }
+ tmpStr += "<td>" + displayViewFormatBirthday( event ) + "</td>";
tmpStr += "</tr>";
+ tmpStr += "</table>";
+ return tmpStr;
}
- if ( event->categories().count() > 0 ) {
+ if ( !event->description().isEmpty() ) {
tmpStr += "<tr>";
- tmpStr += "<td align=\"right\"><b>" + i18n( "1 Category", "%n Categories", event->categories().count() )+ "</b></td>";
- tmpStr += "<td>" + event->categoriesStr() + "</td>";
+ tmpStr += "<td><b>" + i18n( "Description:" ) + "</b></td>";
+ tmpStr += "<td>" + event->description() + "</td>";
tmpStr += "</tr>";
}
- if ( event->doesRecur() ) {
- TQDateTime dt =
- event->recurrence()->getNextDateTime( TQDateTime::currentDateTime() );
+ // TODO: print comments?
+
+ int reminderCount = event->alarms().count();
+ if ( reminderCount > 0 && event->isAlarmEnabled() ) {
tmpStr += "<tr>";
- tmpStr += "<td align=\"right\"><b>" + i18n( "Next on" ) + "</b></td>";
- if ( !event->doesFloat() ) {
- tmpStr += "<td>" +
- KGlobal::locale()->formatDateTime( dt, true ) + "</td>";
- } else {
- tmpStr += "<td>" +
- KGlobal::locale()->formatDate( dt.date(), true ) + "</td>";
- }
+ tmpStr += "<td><b>" +
+ i18n( "Reminder:", "%n Reminders:", reminderCount ) +
+ "</b></td>";
+ tmpStr += "<td>" + IncidenceFormatter::reminderStringList( event ).join( "<br>" ) + "</td>";
tmpStr += "</tr>";
}
- int attendeeCount = event->attendees().count();
- if ( attendeeCount > 0 ) {
- tmpStr += "<tr><td colspan=\"2\">";
- tmpStr += eventViewerFormatAttendees( event );
- tmpStr += "</td></tr>";
+ tmpStr += displayViewFormatAttendees( event );
+
+ int categoryCount = event->categories().count();
+ if ( categoryCount > 0 ) {
+ tmpStr += "<tr>";
+ tmpStr += "<td><b>" +
+ i18n( "Category:", "%n Categories:", categoryCount ) +
+ "</b></td>";
+ tmpStr += "<td>" + displayViewFormatCategories( event ) + "</td>";
+ tmpStr += "</tr>";
}
int attachmentCount = event->attachments().count();
if ( attachmentCount > 0 ) {
tmpStr += "<tr>";
- tmpStr += "<td align=\"right\"><b>" + i18n( "1 attachment", "%n attachments", attachmentCount )+ "</b></td>";
- tmpStr += "<td>" + eventViewerFormatAttachments( event ) + "</td>";
+ tmpStr += "<td><b>" +
+ i18n( "Attachment:", "%n Attachments:", attachmentCount ) +
+ "</b></td>";
+ tmpStr += "<td>" + displayViewFormatAttachments( event ) + "</td>";
tmpStr += "</tr>";
}
-
tmpStr += "</table>";
- tmpStr += "<em>" + i18n( "Creation date: %1.").arg(
- KGlobal::locale()->formatDateTime( event->created() , true ) ) + "</em>";
+
+ tmpStr += "<em>" + displayViewFormatCreationDate( event ) + "</em>";
+
return tmpStr;
}
-static TQString eventViewerFormatTodo( Todo *todo )
+static TQString displayViewFormatTodo( Calendar *calendar, Todo *todo,
+ const TQDate &date )
{
- if ( !todo ) return TQString::null;
- TQString tmpStr = eventViewerFormatHeader( todo );
+ if ( !todo ) {
+ return TQString::null;
+ }
+
+ TQString tmpStr = displayViewFormatHeader( todo );
tmpStr += "<table>";
+ tmpStr += "<col width=\"25%\"/>";
+ tmpStr += "<col width=\"75%\"/>";
+
+ if ( calendar ) {
+ TQString calStr = IncidenceFormatter::resourceString( calendar, todo );
+ if ( !calStr.isEmpty() ) {
+ tmpStr += "<tr>";
+ tmpStr += "<td><b>" + i18n( "Calendar:" ) + "</b></td>";
+ tmpStr += "<td>" + calStr + "</td>";
+ tmpStr += "</tr>";
+ }
+ }
- if ( todo->hasDueDate() ) {
+ if ( !todo->location().isEmpty() ) {
tmpStr += "<tr>";
- tmpStr += "<td align=\"right\"><b>" + i18n( "Due on" ) + "</b></td>";
- if ( !todo->doesFloat() ) {
- tmpStr += "<td>" +
- KGlobal::locale()->formatDateTime( todo->dtDue(), true ) +
- "</td>";
- } else {
- tmpStr += "<td>" +
- KGlobal::locale()->formatDate( todo->dtDue().date(), true ) +
- "</td>";
+ tmpStr += "<td><b>" + i18n( "Location:" ) + "</b></td>";
+ tmpStr += "<td>" + todo->location() + "</td>";
+ tmpStr += "</tr>";
+ }
+
+ if ( todo->hasStartDate() && todo->dtStart().isValid() ) {
+ TQDateTime startDt = todo->dtStart();
+ if ( todo->doesRecur() ) {
+ if ( date.isValid() ) {
+ startDt.setDate( date );
+ }
}
+ tmpStr += "<tr>";
+ tmpStr += "<td><b>" + i18n( "Start:" ) + "</b></td>";
+ tmpStr += "<td>" +
+ IncidenceFormatter::dateTimeToString( startDt,
+ todo->doesFloat(), false ) +
+ "</td>";
tmpStr += "</tr>";
}
- if ( !todo->description().isEmpty() ) {
+ if ( todo->hasDueDate() && todo->dtDue().isValid() ) {
+ TQDateTime dueDt = todo->dtDue();
+ if ( todo->doesRecur() ) {
+ if ( date.isValid() ) {
+ TQDateTime dt( date, TQTime( 0, 0, 0 ) );
+ dt = dt.addSecs( -1 );
+ dueDt.setDate( todo->recurrence()->getNextDateTime( dt ).date() );
+ }
+ }
tmpStr += "<tr>";
- tmpStr += "<td align=\"right\"><b>" + i18n( "Description" ) + "</b></td>";
- tmpStr += "<td>" + todo->description() + "</td>";
+ tmpStr += "<td><b>" + i18n( "Due:" ) + "</b></td>";
+ tmpStr += "<td>" +
+ IncidenceFormatter::dateTimeToString( dueDt,
+ todo->doesFloat(), false ) +
+ "</td>";
tmpStr += "</tr>";
}
- if ( !todo->location().isEmpty() ) {
+ TQString durStr = IncidenceFormatter::durationString( todo );
+ if ( !durStr.isEmpty() ) {
tmpStr += "<tr>";
- tmpStr += "<td align=\"right\"><b>" + i18n( "Location" ) + "</b></td>";
- tmpStr += "<td>" + todo->location() + "</td>";
+ tmpStr += "<td><b>" + i18n( "Duration:" ) + "</b></td>";
+ tmpStr += "<td>" + durStr + "</td>";
tmpStr += "</tr>";
}
- if ( todo->categories().count() > 0 ) {
+ if ( todo->doesRecur() ) {
tmpStr += "<tr>";
- tmpStr += "<td align=\"right\"><b>" + i18n( "1 Category", "%n Categories", todo->categories().count() )+ "</b></td>";
- tmpStr += "<td>" + todo->categoriesStr() + "</td>";
+ tmpStr += "<td><b>" + i18n( "Recurrence:" ) + "</b></td>";
+ tmpStr += "<td>" +
+ IncidenceFormatter::recurrenceString( todo ) +
+ "</td>";
tmpStr += "</tr>";
}
- tmpStr += "<tr>";
- tmpStr += "<td align=\"right\"><b>" + i18n( "Priority" ) + "</b></td>";
- if ( todo->priority() > 0 ) {
- tmpStr += "<td>" + TQString::number( todo->priority() ) + "</td>";
- } else {
- tmpStr += "<td>" + i18n( "Unspecified" ) + "</td>";
+ if ( !todo->description().isEmpty() ) {
+ tmpStr += "<tr>";
+ tmpStr += "<td><b>" + i18n( "Description:" ) + "</b></td>";
+ tmpStr += "<td>" + todo->description() + "</td>";
+ tmpStr += "</tr>";
}
- tmpStr += "</tr>";
- tmpStr += "<tr>";
- tmpStr += "<td align=\"right\"><b>" + i18n( "Completed" ) + "</b></td>";
- tmpStr += "<td>" + i18n( "%1 %" ).arg( todo->percentComplete() ) + "</td>";
- tmpStr += "</tr>";
+ // TODO: print comments?
- if ( todo->doesRecur() ) {
- TQDateTime dt =
- todo->recurrence()->getNextDateTime( TQDateTime::currentDateTime() );
+ int reminderCount = todo->alarms().count();
+ if ( reminderCount > 0 && todo->isAlarmEnabled() ) {
tmpStr += "<tr>";
- tmpStr += "<td align=\"right\"><b>" + i18n( "Next on" ) + "</b></td>";
- if ( !todo->doesFloat() ) {
- tmpStr += "<td>" +
- KGlobal::locale()->formatDateTime( dt, true ) + "</td>";
- } else {
- tmpStr += "<td>" +
- KGlobal::locale()->formatDate( dt.date(), true ) + "</td>";
- }
+ tmpStr += "<td><b>" +
+ i18n( "Reminder:", "%n Reminders:", reminderCount ) +
+ "</b></td>";
+ tmpStr += "<td>" + IncidenceFormatter::reminderStringList( todo ).join( "<br>" ) + "</td>";
+ tmpStr += "</tr>";
+ }
+
+ tmpStr += displayViewFormatAttendees( todo );
+
+ int categoryCount = todo->categories().count();
+ if ( categoryCount > 0 ) {
+ tmpStr += "<tr>";
+ tmpStr += "<td><b>" +
+ i18n( "Category:", "%n Categories:", categoryCount ) +
+ "</b></td>";
+ tmpStr += "<td>" + displayViewFormatCategories( todo ) + "</td>";
+ tmpStr += "</tr>";
+ }
+
+ if ( todo->priority() > 0 ) {
+ tmpStr += "<tr>";
+ tmpStr += "<td><b>" + i18n( "Priority:" ) + "</b></td>";
+ tmpStr += "<td>";
+ tmpStr += TQString::number( todo->priority() );
+ tmpStr += "</td>";
tmpStr += "</tr>";
}
- int attendeeCount = todo->attendees().count();
- if ( attendeeCount > 0 ) {
- tmpStr += "<tr><td colspan=\"2\">";
- tmpStr += eventViewerFormatAttendees( todo );
- tmpStr += "</td></tr>";
+ tmpStr += "<tr>";
+ if ( todo->isCompleted() ) {
+ tmpStr += "<td><b>" + i18n( "Completed:" ) + "</b></td>";
+ tmpStr += "<td>";
+ tmpStr += todo->completedStr();
+ } else {
+ tmpStr += "<td><b>" + i18n( "Percent Done:" ) + "</b></td>";
+ tmpStr += "<td>";
+ tmpStr += i18n( "%1%" ).arg( todo->percentComplete() );
}
+ tmpStr += "</td>";
+ tmpStr += "</tr>";
int attachmentCount = todo->attachments().count();
if ( attachmentCount > 0 ) {
tmpStr += "<tr>";
- tmpStr += "<td align=\"right\"><b>" + i18n( "1 attachment", "%n attachments", attachmentCount )+ "</b></td>";
- tmpStr += "<td>" + eventViewerFormatAttachments( todo ) + "</td>";
+ tmpStr += "<td><b>" +
+ i18n( "Attachment:", "Attachments:", attachmentCount ) +
+ "</b></td>";
+ tmpStr += "<td>" + displayViewFormatAttachments( todo ) + "</td>";
tmpStr += "</tr>";
}
tmpStr += "</table>";
- tmpStr += "<em>" + i18n( "Creation date: %1.").arg(
- KGlobal::locale()->formatDateTime( todo->created(), true ) ) + "</em>";
+
+ tmpStr += "<em>" + displayViewFormatCreationDate( todo ) + "</em>";
+
return tmpStr;
}
-static TQString eventViewerFormatJournal( Journal *journal )
+static TQString displayViewFormatJournal( Calendar *calendar, Journal *journal )
{
- if ( !journal ) return TQString::null;
+ if ( !journal ) {
+ return TQString::null;
+ }
- TQString tmpStr;
- if ( !journal->summary().isEmpty() ) {
- tmpStr += eventViewerAddTag( "u",
- eventViewerAddTag( "b", journal->summary() ) );
+ TQString tmpStr = displayViewFormatHeader( journal );
+
+ tmpStr += "<table>";
+ tmpStr += "<col width=\"25%\"/>";
+ tmpStr += "<col width=\"75%\"/>";
+
+ if ( calendar ) {
+ TQString calStr = IncidenceFormatter::resourceString( calendar, journal );
+ if ( !calStr.isEmpty() ) {
+ tmpStr += "<tr>";
+ tmpStr += "<td><b>" + i18n( "Calendar:" ) + "</b></td>";
+ tmpStr += "<td>" + calStr + "</td>";
+ tmpStr += "</tr>";
+ }
}
- tmpStr += eventViewerAddTag( "b", i18n("Journal for %1").arg( journal->dtStartDateStr( false ) ) );
- if ( !journal->description().isEmpty() )
- tmpStr += eventViewerAddTag( "p", journal->description() );
+
+ tmpStr += "<tr>";
+ tmpStr += "<td><b>" + i18n( "Date:" ) + "</b></td>";
+ tmpStr += "<td>" +
+ IncidenceFormatter::dateToString( journal->dtStart(), false ) +
+ "</td>";
+ tmpStr += "</tr>";
+
+ if ( !journal->description().isEmpty() ) {
+ tmpStr += "<tr>";
+ tmpStr += "<td><b>" + i18n( "Description:" ) + "</b></td>";
+ tmpStr += "<td>" + journal->description() + "</td>";
+ tmpStr += "</tr>";
+ }
+
+ int categoryCount = journal->categories().count();
+ if ( categoryCount > 0 ) {
+ tmpStr += "<tr>";
+ tmpStr += "<td><b>" +
+ i18n( "Category:", "%n Categories:", categoryCount ) +
+ "</b></td>";
+ tmpStr += "<td>" + displayViewFormatCategories( journal ) + "</td>";
+ tmpStr += "</tr>";
+ }
+ tmpStr += "</table>";
+
+ tmpStr += "<em>" + displayViewFormatCreationDate( journal ) + "</em>";
+
return tmpStr;
}
-static TQString eventViewerFormatFreeBusy( FreeBusy *fb )
+static TQString displayViewFormatFreeBusy( Calendar * /*calendar*/, FreeBusy *fb )
{
- if ( !fb ) return TQString::null;
+ if ( !fb ) {
+ return TQString::null;
+ }
+
+ TQString tmpStr = htmlAddTag( "h2",
+ htmlAddTag( "b",
+ i18n("Free/Busy information for %1").
+ arg( fb->organizer().fullName() ) ) );
- TQString tmpStr =
- eventViewerAddTag( "u",
- eventViewerAddTag( "b", i18n("Free/Busy information for %1")
- .arg( fb->organizer().fullName() ) ) );
- tmpStr += eventViewerAddTag( "i", i18n("Busy times in date range %1 - %2:")
- .arg( KGlobal::locale()->formatDate( fb->dtStart().date(), true ) )
- .arg( KGlobal::locale()->formatDate( fb->dtEnd().date(), true ) ) );
+ tmpStr += htmlAddTag( "h4", i18n("Busy times in date range %1 - %2:").
+ arg( IncidenceFormatter::dateToString( fb->dtStart(), true ) ).
+ arg( IncidenceFormatter::dateToString( fb->dtEnd(), true ) ) );
TQValueList<Period> periods = fb->busyPeriods();
- TQString text = eventViewerAddTag( "em", eventViewerAddTag( "b", i18n("Busy:") ) );
+ TQString text = htmlAddTag( "em", htmlAddTag( "b", i18n("Busy:") ) );
TQValueList<Period>::iterator it;
for ( it = periods.begin(); it != periods.end(); ++it ) {
Period per = *it;
@@ -519,91 +859,121 @@ static TQString eventViewerFormatFreeBusy( FreeBusy *fb )
if ( dur > 0 ) {
cont += i18n("1 second", "%n seconds", dur);
}
- text += i18n("startDate for duration", "%1 for %2")
- .arg( KGlobal::locale()->formatDateTime( per.start(), false ) )
- .arg( cont );
+ text += i18n("startDate for duration", "%1 for %2").
+ arg( IncidenceFormatter::dateTimeToString( per.start(), false, true ) ).
+ arg( cont );
text += "<br>";
} else {
if ( per.start().date() == per.end().date() ) {
- text += i18n("date, fromTime - toTime ", "%1, %2 - %3")
- .arg( KGlobal::locale()->formatDate( per.start().date() ) )
- .arg( KGlobal::locale()->formatTime( per.start().time() ) )
- .arg( KGlobal::locale()->formatTime( per.end().time() ) );
+ text += i18n("date, fromTime - toTime ", "%1, %2 - %3").
+ arg( IncidenceFormatter::dateToString( per.start().date(), true ) ).
+ arg( IncidenceFormatter::timeToString( per.start(), true ) ).
+ arg( IncidenceFormatter::timeToString( per.end(), true ) );
} else {
- text += i18n("fromDateTime - toDateTime", "%1 - %2")
- .arg( KGlobal::locale()->formatDateTime( per.start(), false ) )
- .arg( KGlobal::locale()->formatDateTime( per.end(), false ) );
+ text += i18n("fromDateTime - toDateTime", "%1 - %2").
+ arg( IncidenceFormatter::dateTimeToString( per.start(), false, true ) ).
+ arg( IncidenceFormatter::dateTimeToString( per.end(), false, true ) );
}
text += "<br>";
}
}
- tmpStr += eventViewerAddTag( "p", text );
+ tmpStr += htmlAddTag( "p", text );
return tmpStr;
}
class IncidenceFormatter::EventViewerVisitor : public IncidenceBase::Visitor
{
public:
- EventViewerVisitor() { mResult = ""; }
- bool act( IncidenceBase *incidence ) { return incidence->accept( *this ); }
+ EventViewerVisitor()
+ : mCalendar( 0 ), mResult( "" ) {}
+
+ bool act( Calendar *calendar, IncidenceBase *incidence, const TQDate &date )
+ {
+ mCalendar = calendar;
+ mDate = date;
+ mResult = "";
+ return incidence->accept( *this );
+ }
TQString result() const { return mResult; }
+
protected:
bool visit( Event *event )
{
- mResult = eventViewerFormatEvent( event );
+ mResult = displayViewFormatEvent( mCalendar, event, mDate );
return !mResult.isEmpty();
}
bool visit( Todo *todo )
{
- mResult = eventViewerFormatTodo( todo );
+ mResult = displayViewFormatTodo( mCalendar, todo, mDate );
return !mResult.isEmpty();
}
bool visit( Journal *journal )
{
- mResult = eventViewerFormatJournal( journal );
+ mResult = displayViewFormatJournal( mCalendar, journal );
return !mResult.isEmpty();
}
bool visit( FreeBusy *fb )
{
- mResult = eventViewerFormatFreeBusy( fb );
+ mResult = displayViewFormatFreeBusy( mCalendar, fb );
return !mResult.isEmpty();
}
protected:
+ Calendar *mCalendar;
+ TQDate mDate;
TQString mResult;
};
TQString IncidenceFormatter::extensiveDisplayString( IncidenceBase *incidence )
{
- if ( !incidence ) return TQString::null;
+ return extensiveDisplayStr( 0, incidence, TQDate() );
+}
+
+TQString IncidenceFormatter::extensiveDisplayStr( Calendar *calendar,
+ IncidenceBase *incidence,
+ const TQDate &date )
+{
+ if ( !incidence ) {
+ return TQString::null;
+ }
+
EventViewerVisitor v;
- if ( v.act( incidence ) ) {
+ if ( v.act( calendar, incidence, date ) ) {
return v.result();
- } else
+ } else {
return TQString::null;
+ }
}
-
-
-
-/*******************************************************************
- * Helper functions for the body part formatter of kmail
- *******************************************************************/
+/***********************************************************************
+ * Helper functions for the body part formatter of kmail (Invitations)
+ ***********************************************************************/
static TQString string2HTML( const TQString& str )
{
return TQStyleSheet::convertFromPlainText(str, TQStyleSheetItem::WhiteSpaceNormal);
}
+static TQString cleanHtml( const TQString &html )
+{
+ TQRegExp rx( "<body[^>]*>(.*)</body>" );
+ rx.setCaseSensitive( false );
+ rx.search( html );
+ TQString body = rx.cap( 1 );
+
+ return TQStyleSheet::escape( body.remove( TQRegExp( "<[^>]*>" ) ).stripWhiteSpace() );
+}
+
static TQString eventStartTimeStr( Event *event )
{
TQString tmp;
- if ( ! event->doesFloat() ) {
- tmp = i18n("%1: Start Date, %2: Start Time", "%1 %2")
- .arg( event->dtStartDateStr(), event->dtStartTimeStr() );
+ if ( !event->doesFloat() ) {
+ tmp = i18n( "%1: Start Date, %2: Start Time", "%1 %2" ).
+ arg( IncidenceFormatter::dateToString( event->dtStart(), true ),
+ IncidenceFormatter::timeToString( event->dtStart(), true ) );
} else {
- tmp = i18n("%1: Start Date", "%1 (time unspecified)")
- .arg( event->dtStartDateStr() );
+ tmp = i18n( "%1: Start Date", "%1 (all day)" ).
+ arg( IncidenceFormatter::dateToString( event->dtStart(), true ) );
}
return tmp;
}
@@ -611,16 +981,15 @@ static TQString eventStartTimeStr( Event *event )
static TQString eventEndTimeStr( Event *event )
{
TQString tmp;
- if ( event->hasEndDate() ) {
- if ( ! event->doesFloat() ) {
- tmp = i18n("%1: End Date, %2: End Time", "%1 %2")
- .arg( event->dtEndDateStr(), event->dtEndTimeStr() );
+ if ( event->hasEndDate() && event->dtEnd().isValid() ) {
+ if ( !event->doesFloat() ) {
+ tmp = i18n( "%1: End Date, %2: End Time", "%1 %2" ).
+ arg( IncidenceFormatter::dateToString( event->dtEnd(), true ),
+ IncidenceFormatter::timeToString( event->dtEnd(), true ) );
} else {
- tmp = i18n("%1: End Date", "%1 (time unspecified)")
- .arg( event->dtEndDateStr() );
+ tmp = i18n( "%1: End Date", "%1 (all day)" ).
+ arg( IncidenceFormatter::dateToString( event->dtEnd(), true ) );
}
- } else {
- tmp = i18n( "Unspecified" );
}
return tmp;
}
@@ -630,198 +999,495 @@ static TQString invitationRow( const TQString &cell1, const TQString &cell2 )
return "<tr><td>" + cell1 + "</td><td>" + cell2 + "</td></tr>\n";
}
-static TQString invitationsDetailsIncidence( Incidence *incidence )
+static Attendee *findDelegatedFromMyAttendee( Incidence *incidence )
+{
+ // Return the first attendee that was delegated-from me
+
+ Attendee *attendee = 0;
+ if ( !incidence ) {
+ return attendee;
+ }
+
+ KEMailSettings settings;
+ TQStringList profiles = settings.profiles();
+ for( TQStringList::Iterator it=profiles.begin(); it!=profiles.end(); ++it ) {
+ settings.setProfile( *it );
+
+ TQString delegatorName, delegatorEmail;
+ Attendee::List attendees = incidence->attendees();
+ Attendee::List::ConstIterator it2;
+ for ( it2 = attendees.begin(); it2 != attendees.end(); ++it2 ) {
+ Attendee *a = *it2;
+ KPIM::getNameAndMail( a->delegator(), delegatorName, delegatorEmail );
+ if ( settings.getSetting( KEMailSettings::EmailAddress ) == delegatorEmail ) {
+ attendee = a;
+ break;
+ }
+ }
+ }
+ return attendee;
+}
+
+static Attendee *findMyAttendee( Incidence *incidence )
+{
+ // Return the attendee for the incidence that is probably me
+
+ Attendee *attendee = 0;
+ if ( !incidence ) {
+ return attendee;
+ }
+
+ KEMailSettings settings;
+ TQStringList profiles = settings.profiles();
+ for( TQStringList::Iterator it=profiles.begin(); it!=profiles.end(); ++it ) {
+ settings.setProfile( *it );
+
+ Attendee::List attendees = incidence->attendees();
+ Attendee::List::ConstIterator it2;
+ for ( it2 = attendees.begin(); it2 != attendees.end(); ++it2 ) {
+ Attendee *a = *it2;
+ if ( settings.getSetting( KEMailSettings::EmailAddress ) == a->email() ) {
+ attendee = a;
+ break;
+ }
+ }
+ }
+ return attendee;
+}
+
+static Attendee *findAttendee( Incidence *incidence, const TQString &email )
+{
+ // Search for an attendee by email address
+
+ Attendee *attendee = 0;
+ if ( !incidence ) {
+ return attendee;
+ }
+
+ Attendee::List attendees = incidence->attendees();
+ Attendee::List::ConstIterator it;
+ for ( it = attendees.begin(); it != attendees.end(); ++it ) {
+ Attendee *a = *it;
+ if ( email == a->email() ) {
+ attendee = a;
+ break;
+ }
+ }
+ return attendee;
+}
+
+static bool rsvpRequested( Incidence *incidence )
+{
+ if ( !incidence ) {
+ return false;
+ }
+
+ //use a heuristic to determine if a response is requested.
+
+ bool rsvp = true; // better send superfluously than not at all
+ Attendee::List attendees = incidence->attendees();
+ Attendee::List::ConstIterator it;
+ for ( it = attendees.begin(); it != attendees.end(); ++it ) {
+ if ( it == attendees.begin() ) {
+ rsvp = (*it)->RSVP(); // use what the first one has
+ } else {
+ if ( (*it)->RSVP() != rsvp ) {
+ rsvp = true; // they differ, default
+ break;
+ }
+ }
+ }
+ return rsvp;
+}
+
+static TQString rsvpRequestedStr( bool rsvpRequested, const TQString &role )
+{
+ if ( rsvpRequested ) {
+ if ( role.isEmpty() ) {
+ return i18n( "Your response is requested" );
+ } else {
+ return i18n( "Your response as <b>%1</b> is requested" ).arg( role );
+ }
+ } else {
+ if ( role.isEmpty() ) {
+ return i18n( "No response is necessary" );
+ } else {
+ return i18n( "No response as <b>%1</b> is necessary" ).arg( role );
+ }
+ }
+}
+
+static TQString myStatusStr( Incidence *incidence )
+{
+ TQString ret;
+ Attendee *a = findMyAttendee( incidence );
+ if ( a &&
+ a->status() != Attendee::NeedsAction && a->status() != Attendee::Delegated ) {
+ ret = i18n( "(<b>Note</b>: the Organizer preset your response to <b>%1</b>)" ).
+ arg( Attendee::statusName( a->status() ) );
+ }
+ return ret;
+}
+
+static TQString invitationPerson( const TQString& email, TQString name, TQString uid )
{
+ // Make the search, if there is an email address to search on,
+ // and either name or uid is missing
+ if ( !email.isEmpty() && ( name.isEmpty() || uid.isEmpty() ) ) {
+ KABC::AddressBook *add_book = KABC::StdAddressBook::self( true );
+ KABC::Addressee::List addressList = add_book->findByEmail( email );
+ if ( !addressList.isEmpty() ) {
+ KABC::Addressee o = addressList.first();
+ if ( !o.isEmpty() && addressList.size() < 2 ) {
+ if ( name.isEmpty() ) {
+ // No name set, so use the one from the addressbook
+ name = o.formattedName();
+ }
+ uid = o.uid();
+ } else {
+ // Email not found in the addressbook. Don't make a link
+ uid = TQString::null;
+ }
+ }
+ }
+
+ // Show the attendee
+ TQString tmpString;
+ if ( !uid.isEmpty() ) {
+ // There is a UID, so make a link to the addressbook
+ if ( name.isEmpty() ) {
+ // Use the email address for text
+ tmpString += htmlAddLink( "uid:" + uid, email );
+ } else {
+ tmpString += htmlAddLink( "uid:" + uid, name );
+ }
+ } else {
+ // No UID, just show some text
+ tmpString += ( name.isEmpty() ? email : name );
+ }
+ tmpString += '\n';
+
+ // Make the mailto link
+ if ( !email.isEmpty() ) {
+ KCal::Person person( name, email );
+ KURL mailto;
+ mailto.setProtocol( "mailto" );
+ mailto.setPath( person.fullName() );
+ const TQString iconPath =
+ KGlobal::iconLoader()->iconPath( "mail_new", KIcon::Small );
+ tmpString += "&nbsp;" +
+ htmlAddLink( mailto.url(), "<img src=\"" + iconPath + "\">" )
+;
+ }
+ tmpString += "\n";
+
+ return tmpString;
+}
+
+static TQString invitationsDetailsIncidence( Incidence *incidence, bool noHtmlMode )
+{
+ // if description and comment -> use both
+ // if description, but no comment -> use the desc as the comment (and no desc)
+ // if comment, but no description -> use the comment and no description
+
TQString html;
- TQString descr = incidence->description();
+ TQString descr;
+ TQStringList comments;
+
+ if ( incidence->comments().isEmpty() ) {
+ if ( !incidence->description().isEmpty() ) {
+ // use description as comments
+ if ( !TQStyleSheet::mightBeRichText( incidence->description() ) ) {
+ comments << string2HTML( incidence->description() );
+ } else {
+ comments << incidence->description();
+ if ( noHtmlMode ) {
+ comments[0] = cleanHtml( comments[0] );
+ }
+ comments[0] = htmlAddTag( "p", comments[0] );
+ }
+ }
+ //else desc and comments are empty
+ } else {
+ // non-empty comments
+ TQStringList cl = incidence->comments();
+ uint i = 0;
+ for( TQStringList::Iterator it=cl.begin(); it!=cl.end(); ++it ) {
+ if ( !TQStyleSheet::mightBeRichText( *it ) ) {
+ comments.append( string2HTML( *it ) );
+ } else {
+ if ( noHtmlMode ) {
+ comments.append( cleanHtml( "<body>" + (*it) + "</body>" ) );
+ } else {
+ comments.append( *it );
+ }
+ }
+ i++;
+ }
+ if ( !incidence->description().isEmpty() ) {
+ // use description too
+ if ( !TQStyleSheet::mightBeRichText( incidence->description() ) ) {
+ descr = string2HTML( incidence->description() );
+ } else {
+ descr = incidence->description();
+ if ( noHtmlMode ) {
+ descr = cleanHtml( descr );
+ }
+ descr = htmlAddTag( "p", descr );
+ }
+ }
+ }
+
if( !descr.isEmpty() ) {
- html += "<br/><u>" + i18n("Description:")
- + "</u><table border=\"0\"><tr><td>&nbsp;</td><td>";
- html += string2HTML(descr) + "</td></tr></table>";
+ html += "<p>";
+ html += "<table border=\"0\" style=\"margin-top:4px;\">";
+ html += "<tr><td><center>" +
+ htmlAddTag( "u", i18n( "Description:" ) ) +
+ "</center></td></tr>";
+ html += "<tr><td>" + descr + "</td></tr>";
+ html += "</table>";
}
- TQStringList comments = incidence->comments();
+
if ( !comments.isEmpty() ) {
- html += "<br><u>" + i18n("Comments:")
- + "</u><table border=\"0\"><tr><td>&nbsp;</td><td><ul>";
- for ( uint i = 0; i < comments.count(); ++i )
- html += "<li>" + string2HTML( comments[i] ) + "</li>";
- html += "</ul></td></tr></table>";
+ html += "<p>";
+ html += "<table border=\"0\" style=\"margin-top:4px;\">";
+ html += "<tr><td><center>" +
+ htmlAddTag( "u", i18n( "Comments:" ) ) +
+ "</center></td></tr>";
+ html += "<tr><td>";
+ if ( comments.count() > 1 ) {
+ html += "<ul>";
+ for ( uint i=0; i < comments.count(); ++i ) {
+ html += "<li>" + comments[i] + "</li>";
+ }
+ html += "</ul>";
+ } else {
+ html += comments[0];
+ }
+ html += "</td></tr>";
+ html += "</table>";
}
return html;
}
-static TQString invitationDetailsEvent( Event* event )
+static TQString invitationDetailsEvent( Event* event, bool noHtmlMode )
{
- // Meeting details are formatted into an HTML table
- if ( !event )
+ // Invitation details are formatted into an HTML table
+ if ( !event ) {
return TQString::null;
-
- TQString html;
- TQString tmp;
+ }
TQString sSummary = i18n( "Summary unspecified" );
- if ( ! event->summary().isEmpty() ) {
- sSummary = string2HTML( event->summary() );
+ if ( !event->summary().isEmpty() ) {
+ if ( !TQStyleSheet::mightBeRichText( event->summary() ) ) {
+ sSummary = TQStyleSheet::escape( event->summary() );
+ } else {
+ sSummary = event->summary();
+ if ( noHtmlMode ) {
+ sSummary = cleanHtml( sSummary );
+ }
+ }
}
TQString sLocation = i18n( "Location unspecified" );
- if ( ! event->location().isEmpty() ) {
- sLocation = string2HTML( event->location() );
+ if ( !event->location().isEmpty() ) {
+ if ( !TQStyleSheet::mightBeRichText( event->location() ) ) {
+ sLocation = TQStyleSheet::escape( event->location() );
+ } else {
+ sLocation = event->location();
+ if ( noHtmlMode ) {
+ sLocation = cleanHtml( sLocation );
+ }
+ }
}
TQString dir = ( TQApplication::reverseLayout() ? "rtl" : "ltr" );
- html = TQString("<div dir=\"%1\">\n").arg(dir);
+ TQString html = TQString("<div dir=\"%1\">\n").arg(dir);
html += "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n";
- // Meeting summary & location rows
+ // Invitation summary & location rows
html += invitationRow( i18n( "What:" ), sSummary );
html += invitationRow( i18n( "Where:" ), sLocation );
- // Meeting Start Time Row
if (event->doesRecur() == true) {
html += invitationRow( i18n( "First Start Time:" ), eventStartTimeStr( event ) );
- }
- else {
- html += invitationRow( i18n( "Start Time:" ), eventStartTimeStr( event ) );
- }
-
- // Meeting End Time Row
- if (event->doesRecur() == true) {
html += invitationRow( i18n( "First End Time:" ), eventEndTimeStr( event ) );
}
- else {
- html += invitationRow( i18n( "End Time:" ), eventEndTimeStr( event ) );
- }
-
- // Meeting Duration Row
- if ( !event->doesFloat() && event->hasEndDate() ) {
- tmp = TQString::null;
- TQTime sDuration(0,0,0), t;
- int secs = event->dtStart().secsTo( event->dtEnd() );
- t = sDuration.addSecs( secs );
- if ( t.hour() > 0 ) {
- tmp += i18n( "1 hour ", "%n hours ", t.hour() );
- }
- if ( t.minute() > 0 ) {
- tmp += i18n( "1 minute ", "%n minutes ", t.minute() );
+// else {
+ // If a 1 day event
+ if ( event->dtStart().date() == event->dtEnd().date() ) {
+ html += invitationRow( i18n( "Date:" ),
+ IncidenceFormatter::dateToString( event->dtStart(), false ) );
+ if ( !event->doesFloat() ) {
+ html += invitationRow( i18n( "Time:" ),
+ IncidenceFormatter::timeToString( event->dtStart(), true ) +
+ " - " +
+ IncidenceFormatter::timeToString( event->dtEnd(), true ) );
+ }
+ } else {
+ html += invitationRow( i18n( "Starting date of an event", "From:" ),
+ IncidenceFormatter::dateToString( event->dtStart(), false ) );
+ if ( !event->doesFloat() ) {
+ html += invitationRow( i18n( "Starting time of an event", "At:" ),
+ IncidenceFormatter::timeToString( event->dtStart(), true ) );
+ }
+ if ( event->hasEndDate() ) {
+ html += invitationRow( i18n( "Ending date of an event", "To:" ),
+ IncidenceFormatter::dateToString( event->dtEnd(), false ) );
+ if ( !event->doesFloat() ) {
+ html += invitationRow( i18n( "Starting time of an event", "At:" ),
+ IncidenceFormatter::timeToString( event->dtEnd(), true ) );
+ }
+ } else {
+ html += invitationRow( i18n( "Ending date of an event", "To:" ),
+ i18n( "no end date specified" ) );
+ }
}
+// }
- html += invitationRow( i18n( "Duration:" ), tmp );
-
- if ( event->doesRecur() ) {
- TQString recurrence[]= {i18n("no recurrence", "None"),
- i18n("Minutely"), i18n("Hourly"), i18n("Daily"),
- i18n("Weekly"), i18n("Monthly Same Day"), i18n("Monthly Same Position"),
- i18n("Yearly"), i18n("Yearly"), i18n("Yearly")};
+ // Invitation Duration Row
+ QString durStr = IncidenceFormatter::durationString( event );
+ if ( !durStr.isEmpty() ) {
+ html += invitationRow( i18n( "Duration:" ), durStr );
+ }
- Recurrence *recur = event->recurrence();
- if (event->doesRecur() == true) {
- html += invitationRow( " ", " " );
- html += invitationRow( i18n( "Recurs:" ), recurrence[ recur->recurrenceType() ] );
- html += invitationRow( i18n("Frequency:"), i18n("%1").arg(event->recurrence()->frequency()) );
+ // Recurrence Information Rows
+ if ( event->doesRecur() ) {
+ Recurrence *recur = event->recurrence();
+ html += invitationRow( i18n( "Recurrence:" ), IncidenceFormatter::recurrenceString( event ) );
- if ( recur->duration() > 0 ) {
- if ( recur->duration() == 1 )
- html += invitationRow( i18n("Repeats:"), i18n("Once") );
- else
- html += invitationRow( i18n("Repeats:"), i18n("%1 times").arg(recur->duration()));
- } else {
- if ( recur->duration() != -1 ) {
- TQString endstr;
- if ( event->doesFloat() ) {
- endstr = KGlobal::locale()->formatDate( recur->endDate() );
- } else {
- endstr = KGlobal::locale()->formatDateTime( recur->endDateTime() );
- }
- html += invitationRow( i18n("Repeats until:"), endstr );
- } else {
- html += invitationRow( i18n("Repeats:"), i18n("Forever") );
- }
+ DateList exceptions = recur->exDates();
+ if (exceptions.isEmpty() == false) {
+ bool isFirstExRow;
+ isFirstExRow = true;
+ DateList::ConstIterator ex_iter;
+ for ( ex_iter = exceptions.begin(); ex_iter != exceptions.end(); ++ex_iter ) {
+ if (isFirstExRow == true) {
+ isFirstExRow = false;
+ html += invitationRow( i18n("Cancelled on:"), KGlobal::locale()->formatDate(* ex_iter ) );
}
-
- DateList exceptions = recur->exDates();
- if (exceptions.isEmpty() == false) {
- bool isFirstExRow;
- isFirstExRow = true;
- DateList::ConstIterator ex_iter;
- for ( ex_iter = exceptions.begin(); ex_iter != exceptions.end(); ++ex_iter ) {
- if (isFirstExRow == true) {
- isFirstExRow = false;
- html += invitationRow( i18n("Cancelled on:"), KGlobal::locale()->formatDate(* ex_iter ) );
- }
- else {
- html += invitationRow(" ", KGlobal::locale()->formatDate(* ex_iter ) );
- }
- }
+ else {
+ html += invitationRow(" ", KGlobal::locale()->formatDate(* ex_iter ) );
}
}
}
}
html += "</table>\n";
- html += invitationsDetailsIncidence( event );
+ html += invitationsDetailsIncidence( event, noHtmlMode );
html += "</div>\n";
return html;
}
-static TQString invitationDetailsTodo( Todo *todo )
+static TQString invitationDetailsTodo( Todo *todo, bool noHtmlMode )
{
// Task details are formatted into an HTML table
- if ( !todo )
+ if ( !todo ) {
return TQString::null;
+ }
TQString sSummary = i18n( "Summary unspecified" );
- TQString sDescr = i18n( "Description unspecified" );
- if ( ! todo->summary().isEmpty() ) {
- sSummary = todo->summary();
+ if ( !todo->summary().isEmpty() ) {
+ if ( !TQStyleSheet::mightBeRichText( todo->summary() ) ) {
+ sSummary = TQStyleSheet::escape( todo->summary() );
+ } else {
+ sSummary = todo->summary();
+ if ( noHtmlMode ) {
+ sSummary = cleanHtml( sSummary );
+ }
+ }
+ }
+
+ TQString sLocation = i18n( "Location unspecified" );
+ if ( !todo->location().isEmpty() ) {
+ if ( !TQStyleSheet::mightBeRichText( todo->location() ) ) {
+ sLocation = TQStyleSheet::escape( todo->location() );
+ } else {
+ sLocation = todo->location();
+ if ( noHtmlMode ) {
+ sLocation = cleanHtml( sLocation );
+ }
+ }
}
- if ( ! todo->description().isEmpty() ) {
- sDescr = todo->description();
+
+ TQString dir = ( TQApplication::reverseLayout() ? "rtl" : "ltr" );
+ TQString html = TQString("<div dir=\"%1\">\n").arg(dir);
+ html += "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n";
+
+ // Invitation summary & location rows
+ html += invitationRow( i18n( "What:" ), sSummary );
+ html += invitationRow( i18n( "Where:" ), sLocation );
+
+ if ( todo->hasStartDate() && todo->dtStart().isValid() ) {
+ html += invitationRow( i18n( "Start Date:" ),
+ IncidenceFormatter::dateToString( todo->dtStart(), false ) );
+ if ( !todo->doesFloat() ) {
+ html += invitationRow( i18n( "Start Time:" ),
+ IncidenceFormatter::timeToString( todo->dtStart(), false ) );
+ }
+ }
+ if ( todo->hasDueDate() && todo->dtDue().isValid() ) {
+ html += invitationRow( i18n( "Due Date:" ),
+ IncidenceFormatter::dateToString( todo->dtDue(), false ) );
+ if ( !todo->doesFloat() ) {
+ html += invitationRow( i18n( "Due Time:" ),
+ IncidenceFormatter::timeToString( todo->dtDue(), false ) );
+ }
+
+ } else {
+ html += invitationRow( i18n( "Due Date:" ), i18n( "Due Date: None", "None" ) );
}
- TQString html( "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n" );
- html += invitationRow( i18n( "Summary:" ), sSummary );
- html += invitationRow( i18n( "Description:" ), sDescr );
- html += "</table>\n";
- html += invitationsDetailsIncidence( todo );
+
+ html += "</table></div>\n";
+ html += invitationsDetailsIncidence( todo, noHtmlMode );
return html;
}
-static TQString invitationDetailsJournal( Journal *journal )
+static TQString invitationDetailsJournal( Journal *journal, bool noHtmlMode )
{
- if ( !journal )
+ if ( !journal ) {
return TQString::null;
+ }
TQString sSummary = i18n( "Summary unspecified" );
TQString sDescr = i18n( "Description unspecified" );
if ( ! journal->summary().isEmpty() ) {
sSummary = journal->summary();
+ if ( noHtmlMode ) {
+ sSummary = cleanHtml( sSummary );
+ }
}
if ( ! journal->description().isEmpty() ) {
sDescr = journal->description();
+ if ( noHtmlMode ) {
+ sDescr = cleanHtml( sDescr );
+ }
}
TQString html( "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n" );
html += invitationRow( i18n( "Summary:" ), sSummary );
- html += invitationRow( i18n( "Date:" ), journal->dtStartDateStr( false ) );
+ html += invitationRow( i18n( "Date:" ),
+ IncidenceFormatter::dateToString( journal->dtStart(), false ) );
html += invitationRow( i18n( "Description:" ), sDescr );
html += "</table>\n";
- html += invitationsDetailsIncidence( journal );
+ html += invitationsDetailsIncidence( journal, noHtmlMode );
return html;
}
-static TQString invitationDetailsFreeBusy( FreeBusy *fb )
+static TQString invitationDetailsFreeBusy( FreeBusy *fb, bool /*noHtmlMode*/ )
{
if ( !fb )
return TQString::null;
TQString html( "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n" );
html += invitationRow( i18n("Person:"), fb->organizer().fullName() );
- html += invitationRow( i18n("Start date:"), fb->dtStartDateStr() );
+ html += invitationRow( i18n("Start date:"),
+ IncidenceFormatter::dateToString( fb->dtStart(), true ) );
html += invitationRow( i18n("End date:"),
- KGlobal::locale()->formatDate( fb->dtEnd().date(), true ) );
+ KGlobal::locale()->formatDate( fb->dtEnd().date(), true ) );
html += "<tr><td colspan=2><hr></td></tr>\n";
html += "<tr><td colspan=2>Busy periods given in this free/busy object:</td></tr>\n";
@@ -868,259 +1534,524 @@ static TQString invitationDetailsFreeBusy( FreeBusy *fb )
return html;
}
-static TQString invitationHeaderEvent( Event *event, ScheduleMessage *msg )
+static bool replyMeansCounter( Incidence */*incidence*/ )
+{
+ return false;
+/**
+ see kolab/issue 3665 for an example of when we might use this for something
+
+ bool status = false;
+ if ( incidence ) {
+ // put code here that looks at the incidence and determines that
+ // the reply is meant to be a counter proposal. We think this happens
+ // with Outlook counter proposals, but we aren't sure how yet.
+ if ( condition ) {
+ status = true;
+ }
+ }
+ return status;
+*/
+}
+
+static TQString invitationHeaderEvent( Event *event, Incidence *existingIncidence,
+ ScheduleMessage *msg, const TQString &sender )
{
if ( !msg || !event )
return TQString::null;
+
switch ( msg->method() ) {
- case Scheduler::Publish:
- return i18n("This event has been published");
- case Scheduler::Request:
- if ( event->revision() > 0 )
- return i18n( "This meeting has been updated" );
- return i18n( "You have been invited to this meeting" );
- case Scheduler::Refresh:
- return i18n( "This invitation was refreshed" );
- case Scheduler::Cancel:
- return i18n( "This meeting has been canceled" );
- case Scheduler::Add:
- return i18n( "Addition to the meeting invitation" );
- case Scheduler::Reply: {
- Attendee::List attendees = event->attendees();
- if( attendees.count() == 0 ) {
- kdDebug(5850) << "No attendees in the iCal reply!\n";
- return TQString::null;
+ case Scheduler::Publish:
+ return i18n( "This invitation has been published" );
+ case Scheduler::Request:
+ if ( existingIncidence && event->revision() > 0 ) {
+ return i18n( "This invitation has been updated by the organizer %1" ).
+ arg( event->organizer().fullName() );
+ }
+ if ( iamOrganizer( event ) ) {
+ return i18n( "I created this invitation" );
+ } else {
+ TQString orgStr;
+ if ( !event->organizer().fullName().isEmpty() ) {
+ orgStr = event->organizer().fullName();
+ } else if ( !event->organizer().email().isEmpty() ) {
+ orgStr = event->organizer().email();
+ }
+ if ( senderIsOrganizer( event, sender ) ) {
+ if ( !orgStr.isEmpty() ) {
+ return i18n( "You received an invitation from %1" ).arg( orgStr );
+ } else {
+ return i18n( "You received an invitation" );
}
- if( attendees.count() != 1 )
- kdDebug(5850) << "Warning: attendeecount in the reply should be 1 "
- << "but is " << attendees.count() << endl;
- Attendee* attendee = *attendees.begin();
- TQString attendeeName = attendee->name();
- if ( attendeeName.isEmpty() )
- attendeeName = attendee->email();
- if ( attendeeName.isEmpty() )
- attendeeName = i18n( "Sender" );
-
- TQString delegatorName, dummy;
- KPIM::getNameAndMail( attendee->delegator(), delegatorName, dummy );
- if ( delegatorName.isEmpty() )
- delegatorName = attendee->delegator();
-
- switch( attendee->status() ) {
- case Attendee::NeedsAction:
- return i18n( "%1 indicates this invitation still needs some action" ).arg( attendeeName );
- case Attendee::Accepted:
- if ( delegatorName.isEmpty() )
- return i18n( "%1 accepts this meeting invitation" ).arg( attendeeName );
- return i18n( "%1 accepts this meeting invitation on behalf of %2" )
- .arg( attendeeName ).arg( delegatorName );
- case Attendee::Tentative:
- if ( delegatorName.isEmpty() )
- return i18n( "%1 tentatively accepts this meeting invitation" ).arg( attendeeName );
- return i18n( "%1 tentatively accepts this meeting invitation on behalf of %2" )
- .arg( attendeeName ).arg( delegatorName );
- case Attendee::Declined:
- if ( delegatorName.isEmpty() )
- return i18n( "%1 declines this meeting invitation" ).arg( attendeeName );
- return i18n( "%1 declines this meeting invitation on behalf of %2" )
- .arg( attendeeName ).arg( delegatorName );
- case Attendee::Delegated: {
- TQString delegate, dummy;
- KPIM::getNameAndMail( attendee->delegate(), delegate, dummy );
- if ( delegate.isEmpty() )
- delegate = attendee->delegate();
- if ( !delegate.isEmpty() )
- return i18n( "%1 has delegated this meeting invitation to %2" )
- .arg( attendeeName ) .arg( delegate );
- return i18n( "%1 has delegated this meeting invitation" ).arg( attendeeName );
- }
- case Attendee::Completed:
- return i18n( "This meeting invitation is now completed" );
- case Attendee::InProcess:
- return i18n( "%1 is still processing the invitation" ).arg( attendeeName );
- default:
- return i18n( "Unknown response to this meeting invitation" );
+ } else {
+ if ( !orgStr.isEmpty() ) {
+ return i18n( "You received an invitation from %1 as a representative of %2" ).
+ arg( sender, orgStr );
+ } else {
+ return i18n( "You received an invitation from %1 as the organizer's representative" ).
+ arg( sender );
}
- break; }
- case Scheduler::Counter:
- return i18n( "Sender makes this counter proposal" );
- case Scheduler::Declinecounter:
- return i18n( "Sender declines the counter proposal" );
- case Scheduler::NoMethod:
- return i18n("Error: iMIP message with unknown method: '%1'")
- .arg( msg->method() );
+ }
+ }
+ case Scheduler::Refresh:
+ return i18n( "This invitation was refreshed" );
+ case Scheduler::Cancel:
+ return i18n( "This invitation has been canceled" );
+ case Scheduler::Add:
+ return i18n( "Addition to the invitation" );
+ case Scheduler::Reply:
+ {
+ if ( replyMeansCounter( event ) ) {
+ return i18n( "%1 makes this counter proposal" ).
+ arg( firstAttendeeName( event, i18n( "Sender" ) ) );
+ }
+
+ Attendee::List attendees = event->attendees();
+ if( attendees.count() == 0 ) {
+ kdDebug(5850) << "No attendees in the iCal reply!" << endl;
+ return TQString::null;
+ }
+ if( attendees.count() != 1 ) {
+ kdDebug(5850) << "Warning: attendeecount in the reply should be 1 "
+ << "but is " << attendees.count() << endl;
+ }
+ TQString attendeeName = firstAttendeeName( event, i18n( "Sender" ) );
+
+ TQString delegatorName, dummy;
+ Attendee* attendee = *attendees.begin();
+ KPIM::getNameAndMail( attendee->delegator(), delegatorName, dummy );
+ if ( delegatorName.isEmpty() ) {
+ delegatorName = attendee->delegator();
+ }
+
+ switch( attendee->status() ) {
+ case Attendee::NeedsAction:
+ return i18n( "%1 indicates this invitation still needs some action" ).arg( attendeeName );
+ case Attendee::Accepted:
+ if ( event->revision() > 0 ) {
+ if ( !sender.isEmpty() ) {
+ return i18n( "This invitation has been updated by attendee %1" ).arg( sender );
+ } else {
+ return i18n( "This invitation has been updated by an attendee" );
+ }
+ } else {
+ if ( delegatorName.isEmpty() ) {
+ return i18n( "%1 accepts this invitation" ).arg( attendeeName );
+ } else {
+ return i18n( "%1 accepts this invitation on behalf of %2" ).
+ arg( attendeeName ).arg( delegatorName );
+ }
+ }
+ case Attendee::Tentative:
+ if ( delegatorName.isEmpty() ) {
+ return i18n( "%1 tentatively accepts this invitation" ).
+ arg( attendeeName );
+ } else {
+ return i18n( "%1 tentatively accepts this invitation on behalf of %2" ).
+ arg( attendeeName ).arg( delegatorName );
+ }
+ case Attendee::Declined:
+ if ( delegatorName.isEmpty() ) {
+ return i18n( "%1 declines this invitation" ).arg( attendeeName );
+ } else {
+ return i18n( "%1 declines this invitation on behalf of %2" ).
+ arg( attendeeName ).arg( delegatorName );
+ }
+ case Attendee::Delegated: {
+ TQString delegate, dummy;
+ KPIM::getNameAndMail( attendee->delegate(), delegate, dummy );
+ if ( delegate.isEmpty() ) {
+ delegate = attendee->delegate();
+ }
+ if ( !delegate.isEmpty() ) {
+ return i18n( "%1 has delegated this invitation to %2" ).
+ arg( attendeeName ) .arg( delegate );
+ } else {
+ return i18n( "%1 has delegated this invitation" ).arg( attendeeName );
+ }
+ }
+ case Attendee::Completed:
+ return i18n( "This invitation is now completed" );
+ case Attendee::InProcess:
+ return i18n( "%1 is still processing the invitation" ).
+ arg( attendeeName );
+ default:
+ return i18n( "Unknown response to this invitation" );
+ }
+ break;
+ }
+
+ case Scheduler::Counter:
+ return i18n( "%1 makes this counter proposal" ).
+ arg( firstAttendeeName( event, i18n( "Sender" ) ) );
+
+ case Scheduler::Declinecounter:
+ return i18n( "%1 declines the counter proposal" ).
+ arg( firstAttendeeName( event, i18n( "Sender" ) ) );
+
+ case Scheduler::NoMethod:
+ return i18n("Error: iMIP message with unknown method: '%1'").
+ arg( msg->method() );
}
return TQString::null;
}
-static TQString invitationHeaderTodo( Todo *todo, ScheduleMessage *msg )
+static TQString invitationHeaderTodo( Todo *todo, Incidence *existingIncidence,
+ ScheduleMessage *msg, const TQString &sender )
{
- if ( !msg || !todo )
+ if ( !msg || !todo ) {
return TQString::null;
+ }
+
switch ( msg->method() ) {
- case Scheduler::Publish:
- return i18n("This task has been published");
- case Scheduler::Request:
- if ( todo->revision() > 0 )
- return i18n( "This task has been updated" );
- return i18n( "You have been assigned this task" );
- case Scheduler::Refresh:
- return i18n( "This task was refreshed" );
- case Scheduler::Cancel:
- return i18n( "This task was canceled" );
- case Scheduler::Add:
- return i18n( "Addition to the task" );
- case Scheduler::Reply: {
- Attendee::List attendees = todo->attendees();
- if( attendees.count() == 0 ) {
- kdDebug(5850) << "No attendees in the iCal reply!\n";
- return TQString::null;
+ case Scheduler::Publish:
+ return i18n("This task has been published");
+ case Scheduler::Request:
+ if ( existingIncidence && todo->revision() > 0 ) {
+ return i18n( "This task has been updated by the organizer %1" ).
+ arg( todo->organizer().fullName() );
+ } else {
+ if ( iamOrganizer( todo ) ) {
+ return i18n( "I created this task" );
+ } else {
+ TQString orgStr;
+ if ( !todo->organizer().fullName().isEmpty() ) {
+ orgStr = todo->organizer().fullName();
+ } else if ( !todo->organizer().email().isEmpty() ) {
+ orgStr = todo->organizer().email();
}
- if( attendees.count() != 1 )
- kdDebug(5850) << "Warning: attendeecount in the reply should be 1 "
- << "but is " << attendees.count() << endl;
- Attendee* attendee = *attendees.begin();
-
- switch( attendee->status() ) {
- case Attendee::NeedsAction:
- return i18n( "Sender indicates this task assignment still needs some action" );
- case Attendee::Accepted:
- return i18n( "Sender accepts this task" );
- case Attendee::Tentative:
- return i18n( "Sender tentatively accepts this task" );
- case Attendee::Declined:
- return i18n( "Sender declines this task" );
- case Attendee::Delegated: {
- TQString delegate, dummy;
- KPIM::getNameAndMail( attendee->delegate(), delegate, dummy );
- if ( delegate.isEmpty() )
- delegate = attendee->delegate();
- if ( !delegate.isEmpty() )
- return i18n( "Sender has delegated this request for the task to %1" ).arg( delegate );
- return i18n( "Sender has delegated this request for the task " );
+ if ( senderIsOrganizer( todo, sender ) ) {
+ if ( !orgStr.isEmpty() ) {
+ return i18n( "You have been assigned this task by %1" ).arg( orgStr );
+ } else {
+ return i18n( "You have been assigned this task" );
}
- case Attendee::Completed:
- return i18n( "The request for this task is now completed" );
- case Attendee::InProcess:
- return i18n( "Sender is still processing the invitation" );
- default:
- return i18n( "Unknown response to this task" );
+ } else {
+ if ( !orgStr.isEmpty() ) {
+ return i18n( "You have been assigned this task by %1 as a representative of %2" ).
+ arg( sender, orgStr );
+ } else {
+ return i18n( "You have been assigned this task by %1 as the organizer's representative" ).
+ arg( sender );
}
- break; }
- case Scheduler::Counter:
- return i18n( "Sender makes this counter proposal" );
- case Scheduler::Declinecounter:
- return i18n( "Sender declines the counter proposal" );
- case Scheduler::NoMethod:
- return i18n("Error: iMIP message with unknown method: '%1'")
- .arg( msg->method() );
+ }
+ }
+ }
+ case Scheduler::Refresh:
+ return i18n( "This task was refreshed" );
+ case Scheduler::Cancel:
+ return i18n( "This task was canceled" );
+ case Scheduler::Add:
+ return i18n( "Addition to the task" );
+ case Scheduler::Reply:
+ {
+ if ( replyMeansCounter( todo ) ) {
+ return i18n( "%1 makes this counter proposal" ).
+ arg( firstAttendeeName( todo, i18n( "Sender" ) ) );
+ }
+
+ Attendee::List attendees = todo->attendees();
+ if( attendees.count() == 0 ) {
+ kdDebug(5850) << "No attendees in the iCal reply!" << endl;
+ return TQString::null;
+ }
+ if( attendees.count() != 1 ) {
+ kdDebug(5850) << "Warning: attendeecount in the reply should be 1 "
+ << "but is " << attendees.count() << endl;
+ }
+ TQString attendeeName = firstAttendeeName( todo, i18n( "Sender" ) );
+
+ TQString delegatorName, dummy;
+ Attendee* attendee = *attendees.begin();
+ KPIM::getNameAndMail( attendee->delegator(), delegatorName, dummy );
+ if ( delegatorName.isEmpty() ) {
+ delegatorName = attendee->delegator();
+ }
+
+ switch( attendee->status() ) {
+ case Attendee::NeedsAction:
+ return i18n( "%1 indicates this task assignment still needs some action" ).arg( attendeeName );
+ case Attendee::Accepted:
+ if ( todo->revision() > 0 ) {
+ if ( !sender.isEmpty() ) {
+ if ( todo->isCompleted() ) {
+ return i18n( "This task has been completed by assignee %1" ).arg( sender );
+ } else {
+ return i18n( "This task has been updated by assignee %1" ).arg( sender );
+ }
+ } else {
+ if ( todo->isCompleted() ) {
+ return i18n( "This task has been completed by an assignee" );
+ } else {
+ return i18n( "This task has been updated by an assignee" );
+ }
+ }
+ } else {
+ if ( delegatorName.isEmpty() ) {
+ return i18n( "%1 accepts this task" ).arg( attendeeName );
+ } else {
+ return i18n( "%1 accepts this task on behalf of %2" ).
+ arg( attendeeName ).arg( delegatorName );
+ }
+ }
+ case Attendee::Tentative:
+ if ( delegatorName.isEmpty() ) {
+ return i18n( "%1 tentatively accepts this task" ).
+ arg( attendeeName );
+ } else {
+ return i18n( "%1 tentatively accepts this task on behalf of %2" ).
+ arg( attendeeName ).arg( delegatorName );
+ }
+ case Attendee::Declined:
+ if ( delegatorName.isEmpty() ) {
+ return i18n( "%1 declines this task" ).arg( attendeeName );
+ } else {
+ return i18n( "%1 declines this task on behalf of %2" ).
+ arg( attendeeName ).arg( delegatorName );
+ }
+ case Attendee::Delegated: {
+ TQString delegate, dummy;
+ KPIM::getNameAndMail( attendee->delegate(), delegate, dummy );
+ if ( delegate.isEmpty() ) {
+ delegate = attendee->delegate();
+ }
+ if ( !delegate.isEmpty() ) {
+ return i18n( "%1 has delegated this request for the task to %2" ).
+ arg( attendeeName ).arg( delegate );
+ } else {
+ return i18n( "%1 has delegated this request for the task" ).
+ arg( attendeeName );
+ }
+ }
+ case Attendee::Completed:
+ return i18n( "The request for this task is now completed" );
+ case Attendee::InProcess:
+ return i18n( "%1 is still processing the task" ).
+ arg( attendeeName );
+ default:
+ return i18n( "Unknown response to this task" );
+ }
+ break;
+ }
+
+ case Scheduler::Counter:
+ return i18n( "%1 makes this counter proposal" ).
+ arg( firstAttendeeName( todo, i18n( "Sender" ) ) );
+
+ case Scheduler::Declinecounter:
+ return i18n( "%1 declines the counter proposal" ).
+ arg( firstAttendeeName( todo, i18n( "Sender" ) ) );
+
+ case Scheduler::NoMethod:
+ return i18n( "Error: iMIP message with unknown method: '%1'" ).
+ arg( msg->method() );
}
return TQString::null;
}
static TQString invitationHeaderJournal( Journal *journal, ScheduleMessage *msg )
{
- // TODO: Several of the methods are not allowed for journals, so remove them.
- if ( !msg || !journal )
+ if ( !msg || !journal ) {
return TQString::null;
+ }
+
switch ( msg->method() ) {
- case Scheduler::Publish:
- return i18n("This journal has been published");
- case Scheduler::Request:
- return i18n( "You have been assigned this journal" );
- case Scheduler::Refresh:
- return i18n( "This journal was refreshed" );
- case Scheduler::Cancel:
- return i18n( "This journal was canceled" );
- case Scheduler::Add:
- return i18n( "Addition to the journal" );
- case Scheduler::Reply: {
- Attendee::List attendees = journal->attendees();
- if( attendees.count() == 0 ) {
- kdDebug(5850) << "No attendees in the iCal reply!\n";
- return TQString::null;
- }
- if( attendees.count() != 1 )
- kdDebug(5850) << "Warning: attendeecount in the reply should be 1 "
- << "but is " << attendees.count() << endl;
- Attendee* attendee = *attendees.begin();
-
- switch( attendee->status() ) {
- case Attendee::NeedsAction:
- return i18n( "Sender indicates this journal assignment still needs some action" );
- case Attendee::Accepted:
- return i18n( "Sender accepts this journal" );
- case Attendee::Tentative:
- return i18n( "Sender tentatively accepts this journal" );
- case Attendee::Declined:
- return i18n( "Sender declines this journal" );
- case Attendee::Delegated:
- return i18n( "Sender has delegated this request for the journal" );
- case Attendee::Completed:
- return i18n( "The request for this journal is now completed" );
- case Attendee::InProcess:
- return i18n( "Sender is still processing the invitation" );
- default:
- return i18n( "Unknown response to this journal" );
- }
- break; }
- case Scheduler::Counter:
- return i18n( "Sender makes this counter proposal" );
- case Scheduler::Declinecounter:
- return i18n( "Sender declines the counter proposal" );
- case Scheduler::NoMethod:
- return i18n("Error: iMIP message with unknown method: '%1'")
- .arg( msg->method() );
+ case Scheduler::Publish:
+ return i18n("This journal has been published");
+ case Scheduler::Request:
+ return i18n( "You have been assigned this journal" );
+ case Scheduler::Refresh:
+ return i18n( "This journal was refreshed" );
+ case Scheduler::Cancel:
+ return i18n( "This journal was canceled" );
+ case Scheduler::Add:
+ return i18n( "Addition to the journal" );
+ case Scheduler::Reply:
+ {
+ if ( replyMeansCounter( journal ) ) {
+ return i18n( "Sender makes this counter proposal" );
+ }
+
+ Attendee::List attendees = journal->attendees();
+ if( attendees.count() == 0 ) {
+ kdDebug(5850) << "No attendees in the iCal reply!" << endl;
+ return TQString::null;
+ }
+ if( attendees.count() != 1 ) {
+ kdDebug(5850) << "Warning: attendeecount in the reply should be 1 "
+ << "but is " << attendees.count() << endl;
+ }
+ Attendee* attendee = *attendees.begin();
+
+ switch( attendee->status() ) {
+ case Attendee::NeedsAction:
+ return i18n( "Sender indicates this journal assignment still needs some action" );
+ case Attendee::Accepted:
+ return i18n( "Sender accepts this journal" );
+ case Attendee::Tentative:
+ return i18n( "Sender tentatively accepts this journal" );
+ case Attendee::Declined:
+ return i18n( "Sender declines this journal" );
+ case Attendee::Delegated:
+ return i18n( "Sender has delegated this request for the journal" );
+ case Attendee::Completed:
+ return i18n( "The request for this journal is now completed" );
+ case Attendee::InProcess:
+ return i18n( "Sender is still processing the invitation" );
+ default:
+ return i18n( "Unknown response to this journal" );
+ }
+ break;
+ }
+ case Scheduler::Counter:
+ return i18n( "Sender makes this counter proposal" );
+ case Scheduler::Declinecounter:
+ return i18n( "Sender declines the counter proposal" );
+ case Scheduler::NoMethod:
+ return i18n("Error: iMIP message with unknown method: '%1'").
+ arg( msg->method() );
}
return TQString::null;
}
static TQString invitationHeaderFreeBusy( FreeBusy *fb, ScheduleMessage *msg )
{
- if ( !msg || !fb )
+ if ( !msg || !fb ) {
return TQString::null;
+ }
+
switch ( msg->method() ) {
- case Scheduler::Publish:
- return i18n("This free/busy list has been published");
- case Scheduler::Request:
- return i18n( "The free/busy list has been requested" );
- case Scheduler::Refresh:
- return i18n( "This free/busy list was refreshed" );
- case Scheduler::Cancel:
- return i18n( "This free/busy list was canceled" );
- case Scheduler::Add:
- return i18n( "Addition to the free/busy list" );
- case Scheduler::NoMethod:
- default:
- return i18n("Error: Free/Busy iMIP message with unknown method: '%1'")
- .arg( msg->method() );
+ case Scheduler::Publish:
+ return i18n("This free/busy list has been published");
+ case Scheduler::Request:
+ return i18n( "The free/busy list has been requested" );
+ case Scheduler::Refresh:
+ return i18n( "This free/busy list was refreshed" );
+ case Scheduler::Cancel:
+ return i18n( "This free/busy list was canceled" );
+ case Scheduler::Add:
+ return i18n( "Addition to the free/busy list" );
+ case Scheduler::NoMethod:
+ default:
+ return i18n("Error: Free/Busy iMIP message with unknown method: '%1'").
+ arg( msg->method() );
+ }
+}
+
+static TQString invitationAttendees( Incidence *incidence )
+{
+ TQString tmpStr;
+ if ( !incidence ) {
+ return tmpStr;
+ }
+
+ if ( incidence->type() == "Todo" ) {
+ tmpStr += htmlAddTag( "u", i18n( "Assignees" ) );
+ } else {
+ tmpStr += htmlAddTag( "u", i18n( "Attendees" ) );
+ }
+ tmpStr += "<br/>";
+
+ int count=0;
+ Attendee::List attendees = incidence->attendees();
+ if ( !attendees.isEmpty() ) {
+
+ Attendee::List::ConstIterator it;
+ for( it = attendees.begin(); it != attendees.end(); ++it ) {
+ Attendee *a = *it;
+ if ( !iamAttendee( a ) ) {
+ count++;
+ if ( count == 1 ) {
+ tmpStr += "<table border=\"1\" cellpadding=\"1\" cellspacing=\"0\" columns=\"2\">";
+ }
+ tmpStr += "<tr>";
+ tmpStr += "<td>";
+ tmpStr += invitationPerson( a->email(), a->name(), TQString::null );
+ if ( !a->delegator().isEmpty() ) {
+ tmpStr += i18n(" (delegated by %1)" ).arg( a->delegator() );
+ }
+ if ( !a->delegate().isEmpty() ) {
+ tmpStr += i18n(" (delegated to %1)" ).arg( a->delegate() );
+ }
+ tmpStr += "</td>";
+ tmpStr += "<td>" + a->statusStr() + "</td>";
+ tmpStr += "</tr>";
+ }
+ }
+ }
+ if ( count ) {
+ tmpStr += "</table>";
+ } else {
+ tmpStr += "<i>" + i18n( "No attendee", "None" ) + "</i>";
+ }
+
+ return tmpStr;
+}
+
+static TQString invitationAttachments( InvitationFormatterHelper *helper, Incidence *incidence )
+{
+ TQString tmpStr;
+ if ( !incidence ) {
+ return tmpStr;
+ }
+
+ Attachment::List attachments = incidence->attachments();
+ if ( !attachments.isEmpty() ) {
+ tmpStr += i18n( "Attached Documents:" ) + "<ol>";
+
+ Attachment::List::ConstIterator it;
+ for( it = attachments.begin(); it != attachments.end(); ++it ) {
+ Attachment *a = *it;
+ tmpStr += "<li>";
+ // Attachment icon
+ KMimeType::Ptr mimeType = KMimeType::mimeType( a->mimeType() );
+ const TQString iconStr = mimeType ? mimeType->icon( a->uri(), false ) : TQString( "application-octet-stream" );
+ const TQString iconPath = KGlobal::iconLoader()->iconPath( iconStr, KIcon::Small );
+ if ( !iconPath.isEmpty() ) {
+ tmpStr += "<img valign=\"top\" src=\"" + iconPath + "\">";
+ }
+ tmpStr += helper->makeLink( "ATTACH:" + a->label(), a->label() );
+ tmpStr += "</li>";
+ }
+ tmpStr += "</ol>";
}
+
+ return tmpStr;
}
-class IncidenceFormatter::ScheduleMessageVisitor : public IncidenceBase::Visitor
+class IncidenceFormatter::ScheduleMessageVisitor
+ : public IncidenceBase::Visitor
{
public:
- ScheduleMessageVisitor() : mMessage(0) { mResult = ""; }
- bool act( IncidenceBase *incidence, ScheduleMessage *msg ) { mMessage = msg; return incidence->accept( *this ); }
+ ScheduleMessageVisitor() : mExistingIncidence( 0 ), mMessage( 0 ) { mResult = ""; }
+ bool act( IncidenceBase *incidence, Incidence *existingIncidence, ScheduleMessage *msg,
+ const TQString &sender )
+ {
+ mExistingIncidence = existingIncidence;
+ mMessage = msg;
+ mSender = sender;
+ return incidence->accept( *this );
+ }
TQString result() const { return mResult; }
protected:
TQString mResult;
+ Incidence *mExistingIncidence;
ScheduleMessage *mMessage;
+ TQString mSender;
};
-class IncidenceFormatter::InvitationHeaderVisitor :
- public IncidenceFormatter::ScheduleMessageVisitor
+class IncidenceFormatter::InvitationHeaderVisitor
+ : public IncidenceFormatter::ScheduleMessageVisitor
{
protected:
bool visit( Event *event )
{
- mResult = invitationHeaderEvent( event, mMessage );
+ mResult = invitationHeaderEvent( event, mExistingIncidence, mMessage, mSender );
return !mResult.isEmpty();
}
bool visit( Todo *todo )
{
- mResult = invitationHeaderTodo( todo, mMessage );
+ mResult = invitationHeaderTodo( todo, mExistingIncidence, mMessage, mSender );
return !mResult.isEmpty();
}
bool visit( Journal *journal )
@@ -1135,47 +2066,59 @@ class IncidenceFormatter::InvitationHeaderVisitor :
}
};
-class IncidenceFormatter::InvitationBodyVisitor :
- public IncidenceFormatter::ScheduleMessageVisitor
+class IncidenceFormatter::InvitationBodyVisitor
+ : public IncidenceFormatter::ScheduleMessageVisitor
{
+ public:
+ InvitationBodyVisitor( bool noHtmlMode )
+ : ScheduleMessageVisitor(), mNoHtmlMode( noHtmlMode ) {}
+
protected:
bool visit( Event *event )
{
- mResult = invitationDetailsEvent( event );
+ mResult = invitationDetailsEvent( event, mNoHtmlMode );
return !mResult.isEmpty();
}
bool visit( Todo *todo )
{
- mResult = invitationDetailsTodo( todo );
+ mResult = invitationDetailsTodo( todo, mNoHtmlMode );
return !mResult.isEmpty();
}
bool visit( Journal *journal )
{
- mResult = invitationDetailsJournal( journal );
+ mResult = invitationDetailsJournal( journal, mNoHtmlMode );
return !mResult.isEmpty();
}
bool visit( FreeBusy *fb )
{
- mResult = invitationDetailsFreeBusy( fb );
+ mResult = invitationDetailsFreeBusy( fb, mNoHtmlMode );
return !mResult.isEmpty();
}
+
+ private:
+ bool mNoHtmlMode;
};
-class IncidenceFormatter::IncidenceCompareVisitor :
- public IncidenceBase::Visitor
+class IncidenceFormatter::IncidenceCompareVisitor
+ : public IncidenceBase::Visitor
{
public:
IncidenceCompareVisitor() : mExistingIncidence(0) {}
- bool act( IncidenceBase *incidence, Incidence* existingIncidence )
+ bool act( IncidenceBase *incidence, Incidence *existingIncidence, int method )
{
+ Incidence *inc = dynamic_cast<Incidence*>( incidence );
+ if ( !inc || !existingIncidence || inc->revision() <= existingIncidence->revision() )
+ return false;
mExistingIncidence = existingIncidence;
+ mMethod = method;
return incidence->accept( *this );
}
TQString result() const
{
- if ( mChanges.isEmpty() )
- return TQString();
+ if ( mChanges.isEmpty() ) {
+ return TQString::null;
+ }
TQString html = "<div align=\"left\"><ul><li>";
html += mChanges.join( "</li><li>" );
html += "</li><ul></div>";
@@ -1186,17 +2129,18 @@ class IncidenceFormatter::IncidenceCompareVisitor :
bool visit( Event *event )
{
compareEvents( event, dynamic_cast<Event*>( mExistingIncidence ) );
- compareIncidences( event, mExistingIncidence );
+ compareIncidences( event, mExistingIncidence, mMethod );
return !mChanges.isEmpty();
}
bool visit( Todo *todo )
{
- compareIncidences( todo, mExistingIncidence );
+ compareTodos( todo, dynamic_cast<Todo*>( mExistingIncidence ) );
+ compareIncidences( todo, mExistingIncidence, mMethod );
return !mChanges.isEmpty();
}
bool visit( Journal *journal )
{
- compareIncidences( journal, mExistingIncidence );
+ compareIncidences( journal, mExistingIncidence, mMethod );
return !mChanges.isEmpty();
}
bool visit( FreeBusy *fb )
@@ -1211,60 +2155,60 @@ class IncidenceFormatter::IncidenceCompareVisitor :
if ( !oldEvent || !newEvent )
return;
if ( oldEvent->dtStart() != newEvent->dtStart() || oldEvent->doesFloat() != newEvent->doesFloat() )
- mChanges += i18n( "The begin of the meeting has been changed from %1 to %2" )
- .arg( eventStartTimeStr( oldEvent ) ).arg( eventStartTimeStr( newEvent ) );
+ mChanges += i18n( "The invitation starting time has been changed from %1 to %2" )
+ .arg( eventStartTimeStr( oldEvent ) ).arg( eventStartTimeStr( newEvent ) );
if ( oldEvent->dtEnd() != newEvent->dtEnd() || oldEvent->doesFloat() != newEvent->doesFloat() )
- mChanges += i18n( "The end of the meeting has been changed from %1 to %2" )
- .arg( eventEndTimeStr( oldEvent ) ).arg( eventEndTimeStr( newEvent ) );
- if ( newEvent->doesRecur() ) {
- TQString recurrence[]= {i18n("no recurrence", "None"),
- i18n("Minutely"), i18n("Hourly"), i18n("Daily"),
- i18n("Weekly"), i18n("Monthly Same Day"), i18n("Monthly Same Position"),
- i18n("Yearly"), i18n("Yearly"), i18n("Yearly")};
-
- Recurrence *recur = newEvent->recurrence();
- if (oldEvent->doesRecur() == false) {
- mChanges += i18n( "The meeting now recurs %1" ).arg( recurrence[ recur->recurrenceType() ] );
- DateList exceptions = recur->exDates();
- if (exceptions.isEmpty() == false) {
- mChanges += i18n("This recurring meeting has been cancelled on the following days:<br>");
- DateList::ConstIterator ex_iter;
- for ( ex_iter = exceptions.begin(); ex_iter != exceptions.end(); ++ex_iter ) {
- mChanges += i18n("&nbsp&nbsp%1<br>").arg( KGlobal::locale()->formatDate(* ex_iter ) );
- }
- }
- }
- else {
- Recurrence *oldRecur = oldEvent->recurrence();
- DateList exceptions = recur->exDates();
- DateList oldExceptions = oldRecur->exDates();
- bool existsInOldEvent;
- bool atLeastOneModified;
- if (exceptions.isEmpty() == false) {
- atLeastOneModified = false;
- DateList::ConstIterator ex_iter;
- DateList::ConstIterator ex_iter_old;
- for ( ex_iter = exceptions.begin(); ex_iter != exceptions.end(); ++ex_iter ) {
- existsInOldEvent = false;
- for ( ex_iter_old = oldExceptions.begin(); ex_iter_old != oldExceptions.end(); ++ex_iter_old ) {
- if ( KGlobal::locale()->formatDate(* ex_iter ) == KGlobal::locale()->formatDate(* ex_iter_old ) ) {
- existsInOldEvent = true;
- if (atLeastOneModified == false) {
- mChanges += i18n("This recurring meeting has been cancelled on the following days:<br>");
- }
- atLeastOneModified = true;
- }
- }
- if (existsInOldEvent == false ) {
- mChanges += i18n("&nbsp&nbsp%1<br>").arg( KGlobal::locale()->formatDate(* ex_iter ) );
- }
- }
- }
- }
+ mChanges += i18n( "The invitation ending time has been changed from %1 to %2" )
+ .arg( eventEndTimeStr( oldEvent ) ).arg( eventEndTimeStr( newEvent ) );
+ }
+
+ void compareTodos( Todo *newTodo, Todo *oldTodo )
+ {
+ if ( !oldTodo || !newTodo ) {
+ return;
+ }
+
+ if ( !oldTodo->isCompleted() && newTodo->isCompleted() ) {
+ mChanges += i18n( "The task has been completed" );
+ }
+ if ( oldTodo->isCompleted() && !newTodo->isCompleted() ) {
+ mChanges += i18n( "The task is no longer completed" );
+ }
+ if ( oldTodo->percentComplete() != newTodo->percentComplete() ) {
+ const TQString oldPer = i18n( "%1%" ).arg( oldTodo->percentComplete() );
+ const TQString newPer = i18n( "%1%" ).arg( newTodo->percentComplete() );
+ mChanges += i18n( "The task completed percentage has changed from %1 to %2" ).
+ arg( oldPer, newPer );
+ }
+
+ if ( !oldTodo->hasStartDate() && newTodo->hasStartDate() ) {
+ mChanges += i18n( "A task starting time has been added" );
+ }
+ if ( oldTodo->hasStartDate() && !newTodo->hasStartDate() ) {
+ mChanges += i18n( "The task starting time has been removed" );
+ }
+ if ( oldTodo->hasStartDate() && newTodo->hasStartDate() &&
+ oldTodo->dtStart() != newTodo->dtStart() ) {
+ mChanges += i18n( "The task starting time has been changed from %1 to %2" ).
+ arg( dateTimeToString( oldTodo->dtStart(), oldTodo->doesFloat(), false ),
+ dateTimeToString( newTodo->dtStart(), newTodo->doesFloat(), false ) );
+ }
+
+ if ( !oldTodo->hasDueDate() && newTodo->hasDueDate() ) {
+ mChanges += i18n( "A task due time has been added" );
+ }
+ if ( oldTodo->hasDueDate() && !newTodo->hasDueDate() ) {
+ mChanges += i18n( "The task due time has been removed" );
+ }
+ if ( oldTodo->hasDueDate() && newTodo->hasDueDate() &&
+ oldTodo->dtDue() != newTodo->dtDue() ) {
+ mChanges += i18n( "The task due time has been changed from %1 to %2" ).
+ arg( dateTimeToString( oldTodo->dtDue(), oldTodo->doesFloat(), false ),
+ dateTimeToString( newTodo->dtDue(), newTodo->doesFloat(), false ) );
}
}
- void compareIncidences( Incidence *newInc, Incidence *oldInc )
+ void compareIncidences( Incidence *newInc, Incidence *oldInc, int method )
{
if ( !oldInc || !newInc )
return;
@@ -1276,56 +2220,176 @@ class IncidenceFormatter::IncidenceCompareVisitor :
mChanges += i18n( "The description has been changed to: \"%1\"" ).arg( newInc->description() );
Attendee::List oldAttendees = oldInc->attendees();
Attendee::List newAttendees = newInc->attendees();
- for ( Attendee::List::ConstIterator it = newAttendees.constBegin(); it != newAttendees.constEnd(); ++it ) {
+ for ( Attendee::List::ConstIterator it = newAttendees.constBegin();
+ it != newAttendees.constEnd(); ++it ) {
Attendee *oldAtt = oldInc->attendeeByMail( (*it)->email() );
if ( !oldAtt ) {
mChanges += i18n( "Attendee %1 has been added" ).arg( (*it)->fullName() );
} else {
if ( oldAtt->status() != (*it)->status() )
- mChanges += i18n( "The status of attendee %1 has been changed to: %2" ).arg( (*it)->fullName() )
- .arg( (*it)->statusStr() );
+ mChanges += i18n( "The status of attendee %1 has been changed to: %2" ).
+ arg( (*it)->fullName() ).arg( (*it)->statusStr() );
}
}
- for ( Attendee::List::ConstIterator it = oldAttendees.constBegin(); it != oldAttendees.constEnd(); ++it ) {
- Attendee *newAtt = newInc->attendeeByMail( (*it)->email() );
- if ( !newAtt )
- mChanges += i18n( "Attendee %1 has been removed" ).arg( (*it)->fullName() );
+ if ( method == Scheduler::Request ) {
+ for ( Attendee::List::ConstIterator it = oldAttendees.constBegin();
+ it != oldAttendees.constEnd(); ++it ) {
+ if ( (*it)->email() != oldInc->organizer().email() ) {
+ Attendee *newAtt = newInc->attendeeByMail( (*it)->email() );
+ if ( !newAtt ) {
+ mChanges += i18n( "Attendee %1 has been removed" ).arg( (*it)->fullName() );
+ }
+ }
+ }
}
}
private:
- Incidence* mExistingIncidence;
+ Incidence *mExistingIncidence;
+ int mMethod;
TQStringList mChanges;
};
TQString InvitationFormatterHelper::makeLink( const TQString &id, const TQString &text )
{
- TQString res( "<a href=\"%1\"><b>%2</b></a>" );
- return res.arg( generateLinkURL( id ) ).arg( text );
- return res;
+ if ( !id.startsWith( "ATTACH:" ) ) {
+ TQString res = TQString( "<a href=\"%1\"><b>%2</b></a>" ).
+ arg( generateLinkURL( id ), text );
+ return res;
+ } else {
+ // draw the attachment links in non-bold face
+ TQString res = TQString( "<a href=\"%1\">%2</a>" ).
+ arg( generateLinkURL( id ), text );
+ return res;
+ }
}
// Check if the given incidence is likely one that we own instead one from
// a shared calendar (Kolab-specific)
-static bool incidenceOwnedByMe( Calendar* calendar, Incidence *incidence )
+static bool incidenceOwnedByMe( Calendar *calendar, Incidence *incidence )
{
- CalendarResources* cal = dynamic_cast<CalendarResources*>( calendar );
- if ( !cal || !incidence )
+ CalendarResources *cal = dynamic_cast<CalendarResources*>( calendar );
+ if ( !cal || !incidence ) {
return true;
- ResourceCalendar* res = cal->resource( incidence );
- if ( !res )
+ }
+ ResourceCalendar *res = cal->resource( incidence );
+ if ( !res ) {
return true;
+ }
const TQString subRes = res->subresourceIdentifier( incidence );
- if ( !subRes.contains( "/.INBOX.directory/" ) )
+ if ( !subRes.contains( "/.INBOX.directory/" ) ) {
return false;
+ }
return true;
}
-TQString IncidenceFormatter::formatICalInvitation( TQString invitation, Calendar *mCalendar,
- InvitationFormatterHelper *helper )
+// The spacer for the invitation buttons
+static TQString spacer = "<td> &nbsp; </td>";
+// The open & close table cell tags for the invitation buttons
+static TQString tdOpen = "<td>";
+static TQString tdClose = "</td>" + spacer;
+
+static TQString responseButtons( Incidence *inc, bool rsvpReq, bool rsvpRec,
+ InvitationFormatterHelper *helper )
+{
+ TQString html;
+ if ( !helper ) {
+ return html;
+ }
+
+ if ( !rsvpReq && ( inc && inc->revision() == 0 ) ) {
+ // Record only
+ html += tdOpen;
+ html += helper->makeLink( "record", i18n( "[Record]" ) );
+ html += tdClose;
+
+ // Move to trash
+ html += tdOpen;
+ html += helper->makeLink( "delete", i18n( "[Move to Trash]" ) );
+ html += tdClose;
+
+ } else {
+
+ // Accept
+ html += tdOpen;
+ html += helper->makeLink( "accept", i18n( "[Accept]" ) );
+ html += tdClose;
+
+ // Tentative
+ html += tdOpen;
+ html += helper->makeLink( "accept_conditionally",
+ i18n( "Accept conditionally", "[Accept cond.]" ) );
+ html += tdClose;
+
+ // Counter proposal
+ html += tdOpen;
+ html += helper->makeLink( "counter", i18n( "[Counter proposal]" ) );
+ html += tdClose;
+
+ // Decline
+ html += tdOpen;
+ html += helper->makeLink( "decline", i18n( "[Decline]" ) );
+ html += tdClose;
+ }
+
+ if ( !rsvpRec || ( inc && inc->revision() > 0 ) ) {
+ // Delegate
+ html += tdOpen;
+ html += helper->makeLink( "delegate", i18n( "[Delegate]" ) );
+ html += tdClose;
+
+ // Forward
+ html += tdOpen;
+ html += helper->makeLink( "forward", i18n( "[Forward]" ) );
+ html += tdClose;
+
+ // Check calendar
+ if ( inc && inc->type() == "Event" ) {
+ html += tdOpen;
+ html += helper->makeLink( "check_calendar", i18n("[Check my calendar]" ) );
+ html += tdClose;
+ }
+ }
+ return html;
+}
+
+static TQString counterButtons( Incidence *incidence,
+ InvitationFormatterHelper *helper )
+{
+ TQString html;
+ if ( !helper ) {
+ return html;
+ }
+
+ // Accept proposal
+ html += tdOpen;
+ html += helper->makeLink( "accept_counter", i18n("[Accept]") );
+ html += tdClose;
+
+ // Decline proposal
+ html += tdOpen;
+ html += helper->makeLink( "decline_counter", i18n("[Decline]") );
+ html += tdClose;
+
+ // Check calendar
+ if ( incidence && incidence->type() == "Event" ) {
+ html += tdOpen;
+ html += helper->makeLink( "check_calendar", i18n("[Check my calendar]" ) );
+ html += tdClose;
+ }
+ return html;
+}
+
+TQString IncidenceFormatter::formatICalInvitationHelper( TQString invitation,
+ Calendar *mCalendar,
+ InvitationFormatterHelper *helper,
+ bool noHtmlMode,
+ const TQString &sender )
{
- if ( invitation.isEmpty() ) return TQString::null;
+ if ( invitation.isEmpty() ) {
+ return TQString::null;
+ }
ICalFormat format;
// parseScheduleMessage takes the tz from the calendar, no need to set it manually here for the format!
@@ -1340,15 +2404,18 @@ TQString IncidenceFormatter::formatICalInvitation( TQString invitation, Calendar
IncidenceBase *incBase = msg->event();
- Incidence* existingIncidence = 0;
- if ( helper->calendar() ) {
+ // Determine if this incidence is in my calendar (and owned by me)
+ Incidence *existingIncidence = 0;
+ if ( incBase && helper->calendar() ) {
existingIncidence = helper->calendar()->incidence( incBase->uid() );
- if ( !incidenceOwnedByMe( helper->calendar(), existingIncidence ) )
+ if ( !incidenceOwnedByMe( helper->calendar(), existingIncidence ) ) {
existingIncidence = 0;
+ }
if ( !existingIncidence ) {
const Incidence::List list = helper->calendar()->incidences();
for ( Incidence::List::ConstIterator it = list.begin(), end = list.end(); it != end; ++it ) {
- if ( (*it)->schedulingID() == incBase->uid() && incidenceOwnedByMe( helper->calendar(), *it ) ) {
+ if ( (*it)->schedulingID() == incBase->uid() &&
+ incidenceOwnedByMe( helper->calendar(), *it ) ) {
existingIncidence = *it;
break;
}
@@ -1369,115 +2436,262 @@ TQString IncidenceFormatter::formatICalInvitation( TQString invitation, Calendar
html += tableHead;
InvitationHeaderVisitor headerVisitor;
// The InvitationHeaderVisitor returns false if the incidence is somehow invalid, or not handled
- if ( !headerVisitor.act( incBase, msg ) )
+ if ( !headerVisitor.act( incBase, existingIncidence, msg, sender ) )
return TQString::null;
html += "<b>" + headerVisitor.result() + "</b>";
- InvitationBodyVisitor bodyVisitor;
- if ( !bodyVisitor.act( incBase, msg ) )
+ InvitationBodyVisitor bodyVisitor( noHtmlMode );
+ if ( !bodyVisitor.act( incBase, existingIncidence, msg, sender ) )
return TQString::null;
html += bodyVisitor.result();
- if ( msg->method() == Scheduler::Request ) { // ### Scheduler::Publish/Refresh/Add as well?
+ if ( msg->method() == Scheduler::Request ) {
+ IncidenceCompareVisitor compareVisitor;
+ if ( compareVisitor.act( incBase, existingIncidence, msg->method() ) ) {
+ html += "<p align=\"left\">";
+ html += i18n( "The following changes have been made by the organizer:" );
+ html += "</p>";
+ html += compareVisitor.result();
+ }
+ }
+ if ( msg->method() == Scheduler::Reply ) {
IncidenceCompareVisitor compareVisitor;
- if ( compareVisitor.act( incBase, existingIncidence ) ) {
- html += i18n("<p align=\"left\">The following changes have been made by the organizer:</p>");
+ if ( compareVisitor.act( incBase, existingIncidence, msg->method() ) ) {
+ html += "<p align=\"left\">";
+ if ( !sender.isEmpty() ) {
+ html += i18n( "The following changes have been made by %1:" ).arg( sender );
+ } else {
+ html += i18n( "The following changes have been made by an attendee:" );
+ }
+ html += "</p>";
html += compareVisitor.result();
}
}
- html += "<br/>";
- html += "<table border=\"0\" cellspacing=\"0\"><tr><td>&nbsp;</td></tr><tr>";
+ Incidence *inc = dynamic_cast<Incidence*>( incBase );
+
+ // determine if I am the organizer for this invitation
+ bool myInc = iamOrganizer( inc );
-#if 0
- html += helper->makeLinkURL( "accept", i18n("[Enter this into my calendar]") );
- html += "</td><td> &nbsp; </td><td>";
-#endif
+ // determine if the invitation response has already been recorded
+ bool rsvpRec = false;
+ Attendee *ea = 0;
+ if ( !myInc ) {
+ Incidence *rsvpIncidence = existingIncidence;
+ if ( !rsvpIncidence && inc && inc->revision() > 0 ) {
+ rsvpIncidence = inc;
+ }
+ if ( rsvpIncidence ) {
+ ea = findMyAttendee( rsvpIncidence );
+ }
+ if ( ea &&
+ ( ea->status() == Attendee::Accepted ||
+ ea->status() == Attendee::Declined ||
+ ea->status() == Attendee::Tentative ) ) {
+ rsvpRec = true;
+ }
+ }
+
+ // determine invitation role
+ TQString role;
+ bool isDelegated = false;
+ Attendee *a = findMyAttendee( inc );
+ if ( !a && inc ) {
+ if ( !inc->attendees().isEmpty() ) {
+ a = inc->attendees().first();
+ }
+ }
+ if ( a ) {
+ isDelegated = ( a->status() == Attendee::Delegated );
+ role = Attendee::roleName( a->role() );
+ }
+
+ // determine if RSVP needed, not-needed, or response already recorded
+ bool rsvpReq = rsvpRequested( inc );
+ if ( !myInc && a ) {
+ html += "<br/>";
+ html += "<i><u>";
+ if ( rsvpRec && inc ) {
+ if ( inc->revision() == 0 ) {
+ html += i18n( "Your <b>%1</b> response has already been recorded" ).
+ arg( ea->statusStr() );
+ } else {
+ html += i18n( "Your status for this invitation is <b>%1</b>" ).
+ arg( ea->statusStr() );
+ }
+ rsvpReq = false;
+ } else if ( msg->method() == Scheduler::Cancel ) {
+ html += i18n( "This invitation was declined" );
+ } else if ( msg->method() == Scheduler::Add ) {
+ html += i18n( "This invitation was accepted" );
+ } else {
+ if ( !isDelegated ) {
+ html += rsvpRequestedStr( rsvpReq, role );
+ } else {
+ html += i18n( "Awaiting delegation response" );
+ }
+ }
+ html += "</u></i>";
+ }
+
+ // Print if the organizer gave you a preset status
+ if ( !myInc ) {
+ if ( inc && inc->revision() == 0 ) {
+ TQString statStr = myStatusStr( inc );
+ if ( !statStr.isEmpty() ) {
+ html += "<br/>";
+ html += "<i>";
+ html += statStr;
+ html += "</i>";
+ }
+ }
+ }
// Add groupware links
+ html += "<br><table border=\"0\" cellspacing=\"0\"><tr><td>&nbsp;</td></tr>";
+
switch ( msg->method() ) {
case Scheduler::Publish:
case Scheduler::Request:
case Scheduler::Refresh:
case Scheduler::Add:
{
- Incidence *inc = dynamic_cast<Incidence*>( incBase );
- if ( inc && inc->revision() > 0 && (existingIncidence || !helper->calendar()) ) {
- if ( incBase->type() == "Todo" ) {
- html += "<td colspan=\"9\">";
- html += helper->makeLink( "reply", i18n( "[Enter this into my task list]" ) );
- } else {
- html += "<td colspan=\"13\">";
- html += helper->makeLink( "reply", i18n( "[Enter this into my calendar]" ) );
- }
- html += "</td></tr><tr>";
- }
- html += "<td>";
-
- if ( !existingIncidence ) {
- // Accept
- html += helper->makeLink( "accept", i18n( "[Accept]" ) );
- html += "</td><td> &nbsp; </td><td>";
- html += helper->makeLink( "accept_conditionally",
- i18n( "Accept conditionally", "[Accept cond.]" ) );
- html += "</td><td> &nbsp; </td><td>";
- // counter proposal
- html += helper->makeLink( "counter", i18n( "[Counter proposal]" ) );
- html += "</td><td> &nbsp; </td><td>";
- // Decline
- html += helper->makeLink( "decline", i18n( "[Decline]" ) );
- html += "</td><td> &nbsp; </td><td>";
-
- // Delegate
- html += helper->makeLink( "delegate", i18n( "[Delegate]" ) );
- html += "</td><td> &nbsp; </td><td>";
-
- // Forward
- html += helper->makeLink( "forward", i18n( "[Forward]" ) );
-
- if ( incBase->type() == "Event" ) {
- html += "</b></a></td><td> &nbsp; </td><td>";
- html += helper->makeLink( "check_calendar", i18n("[Check my calendar]" ) );
- }
+ if ( inc && inc->revision() > 0 && ( existingIncidence || !helper->calendar() ) ) {
+ html += "<tr>";
+ if ( inc->type() == "Todo" ) {
+ html += "<td colspan=\"9\">";
+ html += helper->makeLink( "reply", i18n( "[Record invitation in my task list]" ) );
+ } else {
+ html += "<td colspan=\"13\">";
+ html += helper->makeLink( "reply", i18n( "[Record invitation in my calendar]" ) );
}
- break;
+ html += "</td></tr>";
+ }
+
+ if ( !myInc && a ) {
+ html += "<tr>" + responseButtons( inc, rsvpReq, rsvpRec, helper ) + "</tr>";
+ }
+ break;
}
case Scheduler::Cancel:
- // Cancel event from my calendar
- html += helper->makeLink( "cancel", i18n( "[Remove this from my calendar]" ) );
- break;
+ // Remove invitation
+ if ( inc ) {
+ html += "<tr>";
+ if ( inc->type() == "Todo" ) {
+ html += "<td colspan=\"9\">";
+ html += helper->makeLink( "cancel", i18n( "[Remove invitation from my task list]" ) );
+ } else {
+ html += "<td colspan=\"13\">";
+ html += helper->makeLink( "cancel", i18n( "[Remove invitation from my calendar]" ) );
+ }
+ html += "</td></tr>";
+ }
+ break;
case Scheduler::Reply:
- // Enter this into my calendar
- if ( incBase->type() == "Todo" ) {
- html += helper->makeLink( "reply", i18n( "[Enter this into my task list]" ) );
- } else {
- html += helper->makeLink( "reply", i18n( "[Enter this into my calendar]" ) );
+ {
+ // Record invitation response
+ Attendee *a = 0;
+ Attendee *ea = 0;
+ if ( inc ) {
+ // First, determine if this reply is really a counter in disguise.
+ if ( replyMeansCounter( inc ) ) {
+ html += "<tr>" + counterButtons( inc, helper ) + "</tr>";
+ break;
}
- break;
+
+ // Next, maybe this is a declined reply that was delegated from me?
+ // find first attendee who is delegated-from me
+ // look a their PARTSTAT response, if the response is declined,
+ // then we need to start over which means putting all the action
+ // buttons and NOT putting on the [Record response..] button
+ a = findDelegatedFromMyAttendee( inc );
+ if ( a ) {
+ if ( a->status() != Attendee::Accepted ||
+ a->status() != Attendee::Tentative ) {
+ html += "<tr>" + responseButtons( inc, rsvpReq, rsvpRec, helper ) + "</tr>";
+ break;
+ }
+ }
+
+ // Finally, simply allow a Record of the reply
+ if ( !inc->attendees().isEmpty() ) {
+ a = inc->attendees().first();
+ }
+ if ( a ) {
+ ea = findAttendee( existingIncidence, a->email() );
+ }
+ }
+ if ( ea && ( ea->status() != Attendee::NeedsAction ) && ( ea->status() == a->status() ) ) {
+ if ( inc && inc->revision() > 0 ) {
+ html += "<br><u><i>";
+ html += i18n( "The response has been recorded [%1]" ).arg( ea->statusStr() );
+ html += "</i></u>";
+ }
+ } else {
+ if ( inc ) {
+ html += "<tr><td>";
+ if ( inc->type() == "Todo" ) {
+ html += helper->makeLink( "reply", i18n( "[Record response in my task list]" ) );
+ } else {
+ html += helper->makeLink( "reply", i18n( "[Record response in my calendar]" ) );
+ }
+ html += "</td></tr>";
+ }
+ }
+ break;
+ }
case Scheduler::Counter:
- html += helper->makeLink( "accept_counter", i18n("[Accept]") );
- html += "&nbsp;";
- html += helper->makeLink( "decline_counter", i18n("[Decline]") );
- html += "&nbsp;";
- html += helper->makeLink( "check_calendar", i18n("[Check my calendar]" ) );
- break;
+ // Counter proposal
+ html += "<tr>" + counterButtons( inc, helper ) + "</tr>";
+ break;
+
case Scheduler::Declinecounter:
case Scheduler::NoMethod:
- break;
+ break;
}
+ // close the groupware table
html += "</td></tr></table>";
+ // Add the attendee list if I am the organizer
+ if ( myInc && helper->calendar() ) {
+ html += invitationAttendees( helper->calendar()->incidence( inc->uid() ) );
+ }
+
+ // close the top-level table
html += "</td></tr></table><br></div>";
+ // Add the attachment list
+ html += invitationAttachments( helper, inc );
+
return html;
}
+TQString IncidenceFormatter::formatICalInvitation( TQString invitation,
+ Calendar *mCalendar,
+ InvitationFormatterHelper *helper )
+{
+ return formatICalInvitationHelper( invitation, mCalendar, helper, false, TQString() );
+}
+
+TQString IncidenceFormatter::formatICalInvitationNoHtml( TQString invitation,
+ Calendar *mCalendar,
+ InvitationFormatterHelper *helper )
+{
+ return formatICalInvitationHelper( invitation, mCalendar, helper, true, TQString() );
+}
+TQString IncidenceFormatter::formatICalInvitationNoHtml( TQString invitation,
+ Calendar *mCalendar,
+ InvitationFormatterHelper *helper,
+ const TQString &sender )
+{
+ return formatICalInvitationHelper( invitation, mCalendar, helper, true, sender );
+}
/*******************************************************************
@@ -1944,10 +3158,14 @@ TQString IncidenceFormatter::formatTNEFInvitation( const TQByteArray& tnef,
class IncidenceFormatter::ToolTipVisitor : public IncidenceBase::Visitor
{
public:
- ToolTipVisitor() : mRichText( true ), mResult( "" ) {}
+ ToolTipVisitor()
+ : mCalendar( 0 ), mRichText( true ), mResult( "" ) {}
- bool act( IncidenceBase *incidence, bool richText=true)
+ bool act( Calendar *calendar, IncidenceBase *incidence,
+ const TQDate &date=TQDate(), bool richText=true )
{
+ mCalendar = calendar;
+ mDate = date;
mRichText = richText;
mResult = "";
return incidence ? incidence->accept( *this ) : false;
@@ -1960,43 +3178,65 @@ class IncidenceFormatter::ToolTipVisitor : public IncidenceBase::Visitor
bool visit( Journal *journal );
bool visit( FreeBusy *fb );
- TQString dateRangeText( Event*event );
- TQString dateRangeText( Todo *todo );
+ TQString dateRangeText( Event *event, const TQDate &date );
+ TQString dateRangeText( Todo *todo, const TQDate &date );
TQString dateRangeText( Journal *journal );
TQString dateRangeText( FreeBusy *fb );
TQString generateToolTip( Incidence* incidence, TQString dtRangeText );
protected:
+ Calendar *mCalendar;
+ TQDate mDate;
bool mRichText;
TQString mResult;
};
-TQString IncidenceFormatter::ToolTipVisitor::dateRangeText( Event*event )
+TQString IncidenceFormatter::ToolTipVisitor::dateRangeText( Event *event, const TQDate &date )
{
TQString ret;
TQString tmp;
+
+ TQDateTime startDt = event->dtStart();
+ TQDateTime endDt = event->dtEnd();
+ if ( event->doesRecur() ) {
+ if ( date.isValid() ) {
+ TQDateTime dt( date, TQTime( 0, 0, 0 ) );
+ int diffDays = startDt.daysTo( dt );
+ dt = dt.addSecs( -1 );
+ startDt.setDate( event->recurrence()->getNextDateTime( dt ).date() );
+ if ( event->hasEndDate() ) {
+ endDt = endDt.addDays( diffDays );
+ if ( startDt > endDt ) {
+ startDt.setDate( event->recurrence()->getPreviousDateTime( dt ).date() );
+ endDt = startDt.addDays( event->dtStart().daysTo( event->dtEnd() ) );
+ }
+ }
+ }
+ }
if ( event->isMultiDay() ) {
tmp = "<br>" + i18n("Event start", "<i>From:</i>&nbsp;%1");
if (event->doesFloat())
- ret += tmp.arg( event->dtStartDateStr().replace(" ", "&nbsp;") );
+ ret += tmp.arg( IncidenceFormatter::dateToString( startDt, false ).replace(" ", "&nbsp;") );
else
- ret += tmp.arg( event->dtStartStr().replace(" ", "&nbsp;") );
+ ret += tmp.arg( IncidenceFormatter::dateToString( startDt ).replace(" ", "&nbsp;") );
tmp = "<br>" + i18n("Event end","<i>To:</i>&nbsp;%1");
if (event->doesFloat())
- ret += tmp.arg( event->dtEndDateStr().replace(" ", "&nbsp;") );
+ ret += tmp.arg( IncidenceFormatter::dateToString( endDt, false ).replace(" ", "&nbsp;") );
else
- ret += tmp.arg( event->dtEndStr().replace(" ", "&nbsp;") );
+ ret += tmp.arg( IncidenceFormatter::dateToString( endDt ).replace(" ", "&nbsp;") );
} else {
ret += "<br>"+i18n("<i>Date:</i>&nbsp;%1").
- arg( event->dtStartDateStr().replace(" ", "&nbsp;") );
+ arg( IncidenceFormatter::dateToString( startDt, false ).replace(" ", "&nbsp;") );
if ( !event->doesFloat() ) {
- const TQString dtStartTime = event->dtStartTimeStr().replace( " ", "&nbsp;" );
- const TQString dtEndTime = event->dtEndTimeStr().replace( " ", "&nbsp;" );
+ const TQString dtStartTime =
+ IncidenceFormatter::timeToString( startDt, true ).replace( " ", "&nbsp;" );
+ const TQString dtEndTime =
+ IncidenceFormatter::timeToString( endDt, true ).replace( " ", "&nbsp;" );
if ( dtStartTime == dtEndTime ) { // to prevent 'Time: 17:00 - 17:00'
tmp = "<br>" + i18n("time for event, &nbsp; to prevent ugly line breaks",
"<i>Time:</i>&nbsp;%1").
@@ -2013,27 +3253,55 @@ TQString IncidenceFormatter::ToolTipVisitor::dateRangeText( Event*event )
return ret;
}
-TQString IncidenceFormatter::ToolTipVisitor::dateRangeText( Todo*todo )
+TQString IncidenceFormatter::ToolTipVisitor::dateRangeText( Todo *todo, const TQDate &date )
{
TQString ret;
bool floats( todo->doesFloat() );
- if (todo->hasStartDate())
- // No need to add <i> here. This is separated issue and each line
- // is very visible on its own. On the other hand... Yes, I like it
- // italics here :)
- ret += "<br>" + i18n("<i>Start:</i>&nbsp;%1").arg(
- (floats)
- ?(todo->dtStartDateStr().replace(" ", "&nbsp;"))
- :(todo->dtStartStr().replace(" ", "&nbsp;")) ) ;
- if (todo->hasDueDate())
- ret += "<br>" + i18n("<i>Due:</i>&nbsp;%1").arg(
- (floats)
- ?(todo->dtDueDateStr().replace(" ", "&nbsp;"))
- :(todo->dtDueStr().replace(" ", "&nbsp;")) );
- if (todo->isCompleted())
- ret += "<br>" + i18n("<i>Completed:</i>&nbsp;%1").arg( todo->completedStr().replace(" ", "&nbsp;") );
- else
- ret += "<br>" + i18n("%1 % completed").arg(todo->percentComplete());
+
+ if ( todo->hasStartDate() && todo->dtStart().isValid() ) {
+ TQDateTime startDt = todo->dtStart();
+ if ( todo->doesRecur() ) {
+ if ( date.isValid() ) {
+ startDt.setDate( date );
+ }
+ }
+ ret += "<br>" +
+ i18n("<i>Start:</i>&nbsp;%1").
+ arg( IncidenceFormatter::dateTimeToString( startDt, floats, false ).
+ replace( " ", "&nbsp;" ) );
+ }
+
+ if ( todo->hasDueDate() && todo->dtDue().isValid() ) {
+ TQDateTime dueDt = todo->dtDue();
+ if ( todo->doesRecur() ) {
+ if ( date.isValid() ) {
+ TQDateTime dt( date, TQTime( 0, 0, 0 ) );
+ dt = dt.addSecs( -1 );
+ dueDt.setDate( todo->recurrence()->getNextDateTime( dt ).date() );
+ }
+ }
+ ret += "<br>" +
+ i18n("<i>Due:</i>&nbsp;%1").
+ arg( IncidenceFormatter::dateTimeToString( dueDt, floats, false ).
+ replace( " ", "&nbsp;" ) );
+ }
+
+ // Print priority and completed info here, for lack of a better place
+
+ if ( todo->priority() > 0 ) {
+ ret += "<br>";
+ ret += "<i>" + i18n( "Priority:" ) + "</i>" + "&nbsp;";
+ ret += TQString::number( todo->priority() );
+ }
+
+ ret += "<br>";
+ if ( todo->isCompleted() ) {
+ ret += "<i>" + i18n( "Completed:" ) + "</i>" + "&nbsp;";
+ ret += todo->completedStr().replace( " ", "&nbsp;" );
+ } else {
+ ret += "<i>" + i18n( "Percent Done:" ) + "</i>" + "&nbsp;";
+ ret += i18n( "%1%" ).arg( todo->percentComplete() );
+ }
return ret;
}
@@ -2042,7 +3310,9 @@ TQString IncidenceFormatter::ToolTipVisitor::dateRangeText( Journal*journal )
{
TQString ret;
if (journal->dtStart().isValid() ) {
- ret += "<br>" + i18n("<i>Date:</i>&nbsp;%1").arg( journal->dtStartDateStr( false ) );
+ ret += "<br>" +
+ i18n("<i>Date:</i>&nbsp;%1").
+ arg( IncidenceFormatter::dateToString( journal->dtStart(), false ) );
}
return ret;
}
@@ -2060,13 +3330,13 @@ TQString IncidenceFormatter::ToolTipVisitor::dateRangeText( FreeBusy *fb )
bool IncidenceFormatter::ToolTipVisitor::visit( Event *event )
{
- mResult = generateToolTip( event, dateRangeText( event ) );
+ mResult = generateToolTip( event, dateRangeText( event, mDate ) );
return !mResult.isEmpty();
}
bool IncidenceFormatter::ToolTipVisitor::visit( Todo *todo )
{
- mResult = generateToolTip( todo, dateRangeText( todo ) );
+ mResult = generateToolTip( todo, dateRangeText( todo, mDate ) );
return !mResult.isEmpty();
}
@@ -2085,43 +3355,209 @@ bool IncidenceFormatter::ToolTipVisitor::visit( FreeBusy *fb )
return !mResult.isEmpty();
}
+static TQString tooltipPerson( const TQString& email, TQString name )
+{
+ // Make the search, if there is an email address to search on,
+ // and name is missing
+ if ( name.isEmpty() && !email.isEmpty() ) {
+ KABC::AddressBook *add_book = KABC::StdAddressBook::self( true );
+ KABC::Addressee::List addressList = add_book->findByEmail( email );
+ if ( !addressList.isEmpty() ) {
+ KABC::Addressee o = addressList.first();
+ if ( !o.isEmpty() && addressList.size() < 2 ) {
+ // use the name from the addressbook
+ name = o.formattedName();
+ }
+ }
+ }
+
+ // Show the attendee
+ TQString tmpString = ( name.isEmpty() ? email : name );
+
+ return tmpString;
+}
+
+static TQString etc = i18n( "elipsis", "..." );
+static TQString tooltipFormatAttendeeRoleList( Incidence *incidence, Attendee::Role role )
+{
+ int maxNumAtts = 8; // maximum number of people to print per attendee role
+ TQString sep = i18n( "separator for lists of people names", ", " );
+ int sepLen = sep.length();
+
+ int i = 0;
+ TQString tmpStr;
+ Attendee::List::ConstIterator it;
+ Attendee::List attendees = incidence->attendees();
+
+ for( it = attendees.begin(); it != attendees.end(); ++it ) {
+ Attendee *a = *it;
+ if ( a->role() != role ) {
+ // skip not this role
+ continue;
+ }
+ if ( a->email() == incidence->organizer().email() ) {
+ // skip attendee that is also the organizer
+ continue;
+ }
+ if ( i == maxNumAtts ) {
+ tmpStr += etc;
+ break;
+ }
+ tmpStr += tooltipPerson( a->email(), a->name() );
+ if ( !a->delegator().isEmpty() ) {
+ tmpStr += i18n(" (delegated by %1)" ).arg( a->delegator() );
+ }
+ if ( !a->delegate().isEmpty() ) {
+ tmpStr += i18n(" (delegated to %1)" ).arg( a->delegate() );
+ }
+ tmpStr += sep;
+ i++;
+ }
+ if ( tmpStr.endsWith( sep ) ) {
+ tmpStr.truncate( tmpStr.length() - sepLen );
+ }
+ return tmpStr;
+}
+
+static TQString tooltipFormatAttendees( Incidence *incidence )
+{
+ TQString tmpStr, str;
+
+ // Add organizer link
+ int attendeeCount = incidence->attendees().count();
+ if ( attendeeCount > 1 ||
+ ( attendeeCount == 1 &&
+ incidence->organizer().email() != incidence->attendees().first()->email() ) ) {
+ tmpStr += "<i>" + i18n( "Organizer:" ) + "</i>" + "&nbsp;";
+ tmpStr += tooltipPerson( incidence->organizer().email(),
+ incidence->organizer().name() );
+ }
+
+ // Add "chair"
+ str = tooltipFormatAttendeeRoleList( incidence, Attendee::Chair );
+ if ( !str.isEmpty() ) {
+ tmpStr += "<br><i>" + i18n( "Chair:" ) + "</i>" + "&nbsp;";
+ tmpStr += str;
+ }
+
+ // Add required participants
+ str = tooltipFormatAttendeeRoleList( incidence, Attendee::ReqParticipant );
+ if ( !str.isEmpty() ) {
+ tmpStr += "<br><i>" + i18n( "Required Participants:" ) + "</i>" + "&nbsp;";
+ tmpStr += str;
+ }
+
+ // Add optional participants
+ str = tooltipFormatAttendeeRoleList( incidence, Attendee::OptParticipant );
+ if ( !str.isEmpty() ) {
+ tmpStr += "<br><i>" + i18n( "Optional Participants:" ) + "</i>" + "&nbsp;";
+ tmpStr += str;
+ }
+
+ // Add observers
+ str = tooltipFormatAttendeeRoleList( incidence, Attendee::NonParticipant );
+ if ( !str.isEmpty() ) {
+ tmpStr += "<br><i>" + i18n( "Observers:" ) + "</i>" + "&nbsp;";
+ tmpStr += str;
+ }
+
+ return tmpStr;
+}
+
TQString IncidenceFormatter::ToolTipVisitor::generateToolTip( Incidence* incidence, TQString dtRangeText )
{
- if ( !incidence )
+ uint maxDescLen = 120; // maximum description chars to print (before elipsis)
+
+ if ( !incidence ) {
return TQString::null;
+ }
- TQString tmp = "<qt><b>"+ incidence->summary().replace("\n", "<br>")+"</b>";
+ TQString tmp = "<qt>";
+
+ // header
+ tmp += "<b>" + incidence->summary().replace( "\n", "<br>" ) + "</b>";
+ //NOTE: using <hr> seems to confuse TQt3 tooltips in some cases so use "-----"
+ tmp += "<br>----------<br>";
+
+ if ( mCalendar ) {
+ TQString calStr = IncidenceFormatter::resourceString( mCalendar, incidence );
+ if ( !calStr.isEmpty() ) {
+ tmp += "<i>" + i18n( "Calendar:" ) + "</i>" + "&nbsp;";
+ tmp += calStr;
+ }
+ }
tmp += dtRangeText;
- if (!incidence->location().isEmpty()) {
- // Put Location: in italics
- tmp += "<br>"+i18n("<i>Location:</i>&nbsp;%1").
- arg( incidence->location().replace("\n", "<br>") );
+ if ( !incidence->location().isEmpty() ) {
+ tmp += "<br>";
+ tmp += "<i>" + i18n( "Location:" ) + "</i>" + "&nbsp;";
+ tmp += incidence->location().replace( "\n", "<br>" );
+ }
+
+ TQString durStr = IncidenceFormatter::durationString( incidence );
+ if ( !durStr.isEmpty() ) {
+ tmp += "<br>";
+ tmp += "<i>" + i18n( "Duration:" ) + "</i>" + "&nbsp;";
+ tmp += durStr;
+ }
+
+ if ( incidence->doesRecur() ) {
+ tmp += "<br>";
+ tmp += "<i>" + i18n( "Recurrence:" ) + "</i>" + "&nbsp;";
+ tmp += IncidenceFormatter::recurrenceString( incidence );
}
- if (!incidence->description().isEmpty()) {
- TQString desc(incidence->description());
- if (desc.length()>120) {
- desc = desc.left(120) + "...";
+
+ if ( !incidence->description().isEmpty() ) {
+ TQString desc( incidence->description() );
+ if ( desc.length() > maxDescLen ) {
+ desc = desc.left( maxDescLen ) + etc;
}
- tmp += "<br>----------<br>" + i18n("<i>Description:</i><br>") + desc.replace("\n", "<br>");
+ tmp += "<br>----------<br>";
+ tmp += "<i>" + i18n( "Description:" ) + "</i>" + "<br>";
+ tmp += desc.replace( "\n", "<br>" );
+ tmp += "<br>----------";
+ }
+
+ int reminderCount = incidence->alarms().count();
+ if ( reminderCount > 0 && incidence->isAlarmEnabled() ) {
+ tmp += "<br>";
+ tmp += "<i>" + i18n( "Reminder:", "%n Reminders:", reminderCount ) + "</i>" + "&nbsp;";
+ tmp += IncidenceFormatter::reminderStringList( incidence ).join( ", " );
}
+
+ tmp += "<br>";
+ tmp += tooltipFormatAttendees( incidence );
+
+ int categoryCount = incidence->categories().count();
+ if ( categoryCount > 0 ) {
+ tmp += "<br>";
+ tmp += "<i>" + i18n( "Category:", "%n Categories:", categoryCount ) + "</i>" + "&nbsp;";
+ tmp += incidence->categories().join( ", " );
+ }
+
tmp += "</qt>";
return tmp;
}
TQString IncidenceFormatter::toolTipString( IncidenceBase *incidence, bool richText )
{
+ return toolTipStr( 0, incidence, TQDate(), richText );
+}
+
+TQString IncidenceFormatter::toolTipStr( Calendar *calendar,
+ IncidenceBase *incidence,
+ const TQDate &date,
+ bool richText )
+{
ToolTipVisitor v;
- if ( v.act( incidence, richText ) ) {
+ if ( v.act( calendar, incidence, date, richText ) ) {
return v.result();
- } else
+ } else {
return TQString::null;
+ }
}
-
-
-
/*******************************************************************
* Helper functions for the Incidence tooltips
*******************************************************************/
@@ -2171,15 +3607,19 @@ bool IncidenceFormatter::MailBodyVisitor::visit( Event *event )
i18n("Yearly"), i18n("Yearly"), i18n("Yearly")};
mResult = mailBodyIncidence( event );
- mResult += i18n("Start Date: %1\n").arg( event->dtStartDateStr() );
+ mResult += i18n("Start Date: %1\n").
+ arg( IncidenceFormatter::dateToString( event->dtStart(), true ) );
if ( !event->doesFloat() ) {
- mResult += i18n("Start Time: %1\n").arg( event->dtStartTimeStr() );
+ mResult += i18n("Start Time: %1\n").
+ arg( IncidenceFormatter::timeToString( event->dtStart(), true ) );
}
if ( event->dtStart() != event->dtEnd() ) {
- mResult += i18n("End Date: %1\n").arg( event->dtEndDateStr() );
+ mResult += i18n("End Date: %1\n").
+ arg( IncidenceFormatter::dateToString( event->dtEnd(), true ) );
}
if ( !event->doesFloat() ) {
- mResult += i18n("End Time: %1\n").arg( event->dtEndTimeStr() );
+ mResult += i18n("End Time: %1\n").
+ arg( IncidenceFormatter::timeToString( event->dtEnd(), true ) );
}
if ( event->doesRecur() ) {
Recurrence *recur = event->recurrence();
@@ -2228,15 +3668,19 @@ bool IncidenceFormatter::MailBodyVisitor::visit( Todo *todo )
mResult = mailBodyIncidence( todo );
if ( todo->hasStartDate() ) {
- mResult += i18n("Start Date: %1\n").arg( todo->dtStartDateStr() );
+ mResult += i18n("Start Date: %1\n").
+ arg( IncidenceFormatter::dateToString( todo->dtStart( false ), true ) );
if ( !todo->doesFloat() ) {
- mResult += i18n("Start Time: %1\n").arg( todo->dtStartTimeStr() );
+ mResult += i18n("Start Time: %1\n").
+ arg( IncidenceFormatter::timeToString( todo->dtStart( false ),true ) );
}
}
if ( todo->hasDueDate() ) {
- mResult += i18n("Due Date: %1\n").arg( todo->dtDueDateStr() );
+ mResult += i18n("Due Date: %1\n").
+ arg( IncidenceFormatter::dateToString( todo->dtDue(), true ) );
if ( !todo->doesFloat() ) {
- mResult += i18n("Due Time: %1\n").arg( todo->dtDueTimeStr() );
+ mResult += i18n("Due Time: %1\n").
+ arg( IncidenceFormatter::timeToString( todo->dtDue(), true ) );
}
}
TQString details = todo->description();
@@ -2249,9 +3693,11 @@ bool IncidenceFormatter::MailBodyVisitor::visit( Todo *todo )
bool IncidenceFormatter::MailBodyVisitor::visit( Journal *journal )
{
mResult = mailBodyIncidence( journal );
- mResult += i18n("Date: %1\n").arg( journal->dtStartDateStr() );
+ mResult += i18n("Date: %1\n").
+ arg( IncidenceFormatter::dateToString( journal->dtStart(), true ) );
if ( !journal->doesFloat() ) {
- mResult += i18n("Time: %1\n").arg( journal->dtStartTimeStr() );
+ mResult += i18n("Time: %1\n").
+ arg( IncidenceFormatter::timeToString( journal->dtStart(), true ) );
}
if ( !journal->description().isEmpty() )
mResult += i18n("Text of the journal:\n%1\n").arg( journal->description() );
@@ -2283,47 +3729,479 @@ static TQString recurEnd( Incidence *incidence )
return endstr;
}
-TQString IncidenceFormatter::recurrenceString(Incidence * incidence)
+/************************************
+ * More static formatting functions
+ ************************************/
+TQString IncidenceFormatter::recurrenceString( Incidence *incidence )
{
- if ( !incidence->doesRecur() )
+ if ( !incidence->doesRecur() ) {
return i18n( "No recurrence" );
-
+ }
+ TQStringList dayList;
+ dayList.append( i18n( "31st Last" ) );
+ dayList.append( i18n( "30th Last" ) );
+ dayList.append( i18n( "29th Last" ) );
+ dayList.append( i18n( "28th Last" ) );
+ dayList.append( i18n( "27th Last" ) );
+ dayList.append( i18n( "26th Last" ) );
+ dayList.append( i18n( "25th Last" ) );
+ dayList.append( i18n( "24th Last" ) );
+ dayList.append( i18n( "23rd Last" ) );
+ dayList.append( i18n( "22nd Last" ) );
+ dayList.append( i18n( "21st Last" ) );
+ dayList.append( i18n( "20th Last" ) );
+ dayList.append( i18n( "19th Last" ) );
+ dayList.append( i18n( "18th Last" ) );
+ dayList.append( i18n( "17th Last" ) );
+ dayList.append( i18n( "16th Last" ) );
+ dayList.append( i18n( "15th Last" ) );
+ dayList.append( i18n( "14th Last" ) );
+ dayList.append( i18n( "13th Last" ) );
+ dayList.append( i18n( "12th Last" ) );
+ dayList.append( i18n( "11th Last" ) );
+ dayList.append( i18n( "10th Last" ) );
+ dayList.append( i18n( "9th Last" ) );
+ dayList.append( i18n( "8th Last" ) );
+ dayList.append( i18n( "7th Last" ) );
+ dayList.append( i18n( "6th Last" ) );
+ dayList.append( i18n( "5th Last" ) );
+ dayList.append( i18n( "4th Last" ) );
+ dayList.append( i18n( "3rd Last" ) );
+ dayList.append( i18n( "2nd Last" ) );
+ dayList.append( i18n( "last day of the month", "Last" ) );
+ dayList.append( i18n( "unknown day of the month", "unknown" ) ); //#31 - zero offset from UI
+ dayList.append( i18n( "1st" ) );
+ dayList.append( i18n( "2nd" ) );
+ dayList.append( i18n( "3rd" ) );
+ dayList.append( i18n( "4th" ) );
+ dayList.append( i18n( "5th" ) );
+ dayList.append( i18n( "6th" ) );
+ dayList.append( i18n( "7th" ) );
+ dayList.append( i18n( "8th" ) );
+ dayList.append( i18n( "9th" ) );
+ dayList.append( i18n( "10th" ) );
+ dayList.append( i18n( "11th" ) );
+ dayList.append( i18n( "12th" ) );
+ dayList.append( i18n( "13th" ) );
+ dayList.append( i18n( "14th" ) );
+ dayList.append( i18n( "15th" ) );
+ dayList.append( i18n( "16th" ) );
+ dayList.append( i18n( "17th" ) );
+ dayList.append( i18n( "18th" ) );
+ dayList.append( i18n( "19th" ) );
+ dayList.append( i18n( "20th" ) );
+ dayList.append( i18n( "21st" ) );
+ dayList.append( i18n( "22nd" ) );
+ dayList.append( i18n( "23rd" ) );
+ dayList.append( i18n( "24th" ) );
+ dayList.append( i18n( "25th" ) );
+ dayList.append( i18n( "26th" ) );
+ dayList.append( i18n( "27th" ) );
+ dayList.append( i18n( "28th" ) );
+ dayList.append( i18n( "29th" ) );
+ dayList.append( i18n( "30th" ) );
+ dayList.append( i18n( "31st" ) );
+ int weekStart = KGlobal::locale()->weekStartDay();
+ TQString dayNames;
+ TQString recurStr, txt;
+ const KCalendarSystem *calSys = KGlobal::locale()->calendar();
Recurrence *recur = incidence->recurrence();
switch ( recur->recurrenceType() ) {
- case Recurrence::rNone:
- return i18n( "No recurrence" );
- case Recurrence::rMinutely:
- if ( recur->duration() != -1 )
- return i18n( "Recurs every minute until %1", "Recurs every %n minutes until %1", recur->frequency() )
- .arg( recurEnd( incidence ) );
- return i18n( "Recurs every minute", "Recurs every %n minutes", recur->frequency() );
- case Recurrence::rHourly:
- if ( recur->duration() != -1 )
- return i18n( "Recurs hourly until %1", "Recurs every %n hours until %1", recur->frequency() )
- .arg( recurEnd( incidence ) );
- return i18n( "Recurs hourly", "Recurs every %n hours", recur->frequency() );
- case Recurrence::rDaily:
- if ( recur->duration() != -1 )
- return i18n( "Recurs daily until %1", "Recurs every %n days until %1", recur->frequency() )
- .arg( recurEnd( incidence ) );
- return i18n( "Recurs daily", "Recurs every %n days", recur->frequency() );
- case Recurrence::rWeekly:
- if ( recur->duration() != -1 )
- return i18n( "Recurs weekly until %1", "Recurs every %n weeks until %1", recur->frequency() )
- .arg( recurEnd( incidence ) );
- return i18n( "Recurs weekly", "Recurs every %n weeks", recur->frequency() );
- case Recurrence::rMonthlyPos:
- case Recurrence::rMonthlyDay:
- if ( recur->duration() != -1 )
- return i18n( "Recurs monthly until %1" ).arg( recurEnd( incidence ) );
- return i18n( "Recurs monthly" );
- case Recurrence::rYearlyMonth:
- case Recurrence::rYearlyDay:
- case Recurrence::rYearlyPos:
- if ( recur->duration() != -1 )
- return i18n( "Recurs yearly until %1" ).arg( recurEnd( incidence ) );
- return i18n( "Recurs yearly" );
- default:
- return i18n( "Incidence recurs" );
+ case Recurrence::rNone:
+ return i18n( "No recurrence" );
+
+ case Recurrence::rMinutely:
+ recurStr = i18n( "Recurs every minute", "Recurs every %n minutes", recur->frequency() );
+ if ( recur->duration() != -1 ) {
+ txt = i18n( "%1 until %2" ).arg( recurStr ).arg( recurEnd( incidence ) );
+ if ( recur->duration() > 0 ) {
+ txt += i18n( " (%1 occurrences)" ).arg( recur->duration() );
+ }
+ return txt;
+ }
+ return recurStr;
+
+ case Recurrence::rHourly:
+ recurStr = i18n( "Recurs hourly", "Recurs every %n hours", recur->frequency() );
+ if ( recur->duration() != -1 ) {
+ txt = i18n( "%1 until %2" ).arg( recurStr ).arg( recurEnd( incidence ) );
+ if ( recur->duration() > 0 ) {
+ txt += i18n( " (%1 occurrences)" ).arg( recur->duration() );
+ }
+ return txt;
+ }
+ return recurStr;
+
+ case Recurrence::rDaily:
+ recurStr = i18n( "Recurs daily", "Recurs every %n days", recur->frequency() );
+ if ( recur->duration() != -1 ) {
+
+ txt = i18n( "%1 until %2" ).arg( recurStr ).arg( recurEnd( incidence ) );
+ if ( recur->duration() > 0 ) {
+ txt += i18n( " (%1 occurrences)" ).arg( recur->duration() );
+ }
+ return txt;
+ }
+ return recurStr;
+
+ case Recurrence::rWeekly:
+ {
+ recurStr = i18n( "Recurs weekly", "Recurs every %n weeks", recur->frequency() );
+
+ bool addSpace = false;
+ for ( int i = 0; i < 7; ++i ) {
+ if ( recur->days().testBit( ( i + weekStart + 6 ) % 7 ) ) {
+ if ( addSpace ) {
+ dayNames.append( i18n( "separator for list of days", ", " ) );
+ }
+ dayNames.append( calSys->weekDayName( ( ( i + weekStart + 6 ) % 7 ) + 1, true ) );
+ addSpace = true;
+ }
+ }
+ if ( dayNames.isEmpty() ) {
+ dayNames = i18n( "Recurs weekly on no days", "no days" );
+ }
+ if ( recur->duration() != -1 ) {
+ txt = i18n( "%1 on %2 until %3" ).
+ arg( recurStr ).arg( dayNames ).arg( recurEnd( incidence ) );
+ if ( recur->duration() > 0 ) {
+ txt += i18n( " (%1 occurrences)" ).arg( recur->duration() );
+ }
+ return txt;
+ }
+ txt = i18n( "%1 on %2" ).arg( recurStr ).arg( dayNames );
+ return txt;
+ }
+ case Recurrence::rMonthlyPos:
+ {
+ recurStr = i18n( "Recurs monthly", "Recurs every %n months", recur->frequency() );
+
+ if ( !recur->monthPositions().isEmpty() ) {
+ KCal::RecurrenceRule::WDayPos rule = recur->monthPositions()[0];
+ if ( recur->duration() != -1 ) {
+ txt = i18n( "%1 on the %2 %3 until %4" ).
+ arg( recurStr ).
+ arg( dayList[rule.pos() + 31] ).
+ arg( calSys->weekDayName( rule.day(), false ) ).
+ arg( recurEnd( incidence ) );
+ if ( recur->duration() > 0 ) {
+ txt += i18n( " (%1 occurrences)" ).arg( recur->duration() );
+ }
+ return txt;
+ }
+ txt = i18n( "%1 on the %2 %3" ).
+ arg( recurStr ).
+ arg( dayList[rule.pos() + 31] ).
+ arg( calSys->weekDayName( rule.day(), false ) );
+ return txt;
+ } else {
+ return recurStr;
+ }
+ break;
+ }
+ case Recurrence::rMonthlyDay:
+ {
+ recurStr = i18n( "Recurs monthly", "Recurs every %n months", recur->frequency() );
+
+ if ( !recur->monthDays().isEmpty() ) {
+ int days = recur->monthDays()[0];
+ if ( recur->duration() != -1 ) {
+ txt = i18n( "%1 on the %2 day until %3" ).
+ arg( recurStr ).
+ arg( dayList[days + 31] ).
+ arg( recurEnd( incidence ) );
+ if ( recur->duration() > 0 ) {
+ txt += i18n( " (%1 occurrences)" ).arg( recur->duration() );
+ }
+ return txt;
+ }
+ txt = i18n( "%1 on the %2 day" ).arg( recurStr ).arg( dayList[days + 31] );
+ return txt;
+ } else {
+ return recurStr;
+ }
+ break;
+ }
+ case Recurrence::rYearlyMonth:
+ {
+ recurStr = i18n( "Recurs yearly", "Recurs every %n years", recur->frequency() );
+
+ if ( recur->duration() != -1 ) {
+ if ( !recur->yearDates().isEmpty() ) {
+ txt = i18n( "%1 on %2 %3 until %4" ).
+ arg( recurStr ).
+ arg( calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ) ).
+ arg( dayList[ recur->yearDates()[0] + 31 ] ).
+ arg( recurEnd( incidence ) );
+ if ( recur->duration() > 0 ) {
+ txt += i18n( " (%1 occurrences)" ).arg( recur->duration() );
+ }
+ return txt;
+ }
+ }
+ if ( !recur->yearDates().isEmpty() ) {
+ txt = i18n( "%1 on %2 %3" ).
+ arg( recurStr ).
+ arg( calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ) ).
+ arg( dayList[ recur->yearDates()[0] + 31 ] );
+ return txt;
+ } else {
+ if ( !recur->yearMonths().isEmpty() ) {
+ txt = i18n( "Recurs yearly on %1 %2" ).
+ arg( calSys->monthName( recur->yearMonths()[0],
+ recur->startDate().year() ) ).
+ arg( dayList[ recur->startDate().day() + 31 ] );
+ } else {
+ txt = i18n( "Recurs yearly on %1 %2" ).
+ arg( calSys->monthName( recur->startDate().month(),
+ recur->startDate().year() ) ).
+ arg( dayList[ recur->startDate().day() + 31 ] );
+ }
+ return txt;
+ }
+ break;
+ }
+ case Recurrence::rYearlyDay:
+ {
+ recurStr = i18n( "Recurs yearly", "Recurs every %n years", recur->frequency() );
+ if ( !recur->yearDays().isEmpty() ) {
+ if ( recur->duration() != -1 ) {
+ txt = i18n( "%1 on day %2 until %3" ).
+ arg( recurStr ).
+ arg( recur->yearDays()[0] ).
+ arg( recurEnd( incidence ) );
+ if ( recur->duration() > 0 ) {
+ txt += i18n( " (%1 occurrences)" ).arg( recur->duration() );
+ }
+ return txt;
+ }
+ txt = i18n( "%1 on day %2" ).arg( recurStr ).arg( recur->yearDays()[0] );
+ return txt;
+ } else {
+ return recurStr;
+ }
+ break;
+ }
+ case Recurrence::rYearlyPos:
+ {
+ recurStr = i18n( "Every year", "Every %n years", recur->frequency() );
+ if ( !recur->yearPositions().isEmpty() && !recur->yearMonths().isEmpty() ) {
+ KCal::RecurrenceRule::WDayPos rule = recur->yearPositions()[0];
+ if ( recur->duration() != -1 ) {
+ txt = i18n( "%1 on the %2 %3 of %4 until %5" ).
+ arg( recurStr ).
+ arg( dayList[rule.pos() + 31] ).
+ arg( calSys->weekDayName( rule.day(), false ) ).
+ arg( calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ) ).
+ arg( recurEnd( incidence ) );
+ if ( recur->duration() > 0 ) {
+ txt += i18n( " (%1 occurrences)" ).arg( recur->duration() );
+ }
+ return txt;
+ }
+ txt = i18n( "%1 on the %2 %3 of %4" ).
+ arg( recurStr ).
+ arg( dayList[rule.pos() + 31] ).
+ arg( calSys->weekDayName( rule.day(), false ) ).
+ arg( calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ) );
+ return txt;
+ } else {
+ return recurStr;
+ }
+ break;
+ }
+ }
+
+ return i18n( "Incidence recurs" );
+}
+
+TQString IncidenceFormatter::timeToString( const TQDateTime &date, bool shortfmt )
+{
+ return KGlobal::locale()->formatTime( date.time(), !shortfmt );
+}
+
+TQString IncidenceFormatter::dateToString( const TQDateTime &date, bool shortfmt )
+{
+ return
+ KGlobal::locale()->formatDate( date.date(), shortfmt );
+}
+
+TQString IncidenceFormatter::dateTimeToString( const TQDateTime &date,
+ bool allDay, bool shortfmt )
+{
+ if ( allDay ) {
+ return dateToString( date, shortfmt );
}
+
+ return KGlobal::locale()->formatDateTime( date, shortfmt );
+}
+
+TQString IncidenceFormatter::resourceString( Calendar *calendar, Incidence *incidence )
+{
+ if ( !calendar || !incidence ) {
+ return TQString::null;
+ }
+
+ CalendarResources *calendarResource = dynamic_cast<CalendarResources*>( calendar );
+ if ( !calendarResource ) {
+ return TQString::null;
+ }
+
+ ResourceCalendar *resourceCalendar = calendarResource->resource( incidence );
+ if ( resourceCalendar ) {
+ if ( !resourceCalendar->subresources().isEmpty() ) {
+ TQString subRes = resourceCalendar->subresourceIdentifier( incidence );
+ if ( subRes.isEmpty() ) {
+ return resourceCalendar->resourceName();
+ } else {
+ return resourceCalendar->labelForSubresource( subRes );
+ }
+ }
+ return resourceCalendar->resourceName();
+ }
+
+ return TQString::null;
+}
+
+static TQString secs2Duration( int secs )
+{
+ TQString tmp;
+ int days = secs / 86400;
+ if ( days > 0 ) {
+ tmp += i18n( "1 day", "%n days", days );
+ tmp += ' ';
+ secs -= ( days * 86400 );
+ }
+ int hours = secs / 3600;
+ if ( hours > 0 ) {
+ tmp += i18n( "1 hour", "%n hours", hours );
+ tmp += ' ';
+ secs -= ( hours * 3600 );
+ }
+ int mins = secs / 60;
+ if ( mins > 0 ) {
+ tmp += i18n( "1 minute", "%n minutes", mins );
+ }
+ return tmp;
+}
+
+TQString IncidenceFormatter::durationString( Incidence *incidence )
+{
+ TQString tmp;
+ if ( incidence->type() == "Event" ) {
+ Event *event = static_cast<Event *>( incidence );
+ if ( event->hasEndDate() ) {
+ if ( !event->doesFloat() ) {
+ tmp = secs2Duration( event->dtStart().secsTo( event->dtEnd() ) );
+ } else {
+ tmp = i18n( "1 day", "%n days",
+ event->dtStart().date().daysTo( event->dtEnd().date() ) + 1 );
+ }
+ } else {
+ tmp = i18n( "forever" );
+ }
+ } else if ( incidence->type() == "Todo" ) {
+ Todo *todo = static_cast<Todo *>( incidence );
+ if ( todo->hasDueDate() ) {
+ if ( todo->hasStartDate() ) {
+ if ( !todo->doesFloat() ) {
+ tmp = secs2Duration( todo->dtStart().secsTo( todo->dtDue() ) );
+ } else {
+ tmp = i18n( "1 day", "%n days",
+ todo->dtStart().date().daysTo( todo->dtDue().date() ) + 1 );
+ }
+ }
+ }
+ }
+ return tmp;
+}
+
+TQStringList IncidenceFormatter::reminderStringList( Incidence *incidence, bool shortfmt )
+{
+ //TODO: implement shortfmt=false
+ Q_UNUSED( shortfmt );
+
+ TQStringList reminderStringList;
+
+ if ( incidence ) {
+ Alarm::List alarms = incidence->alarms();
+ Alarm::List::ConstIterator it;
+ for ( it = alarms.begin(); it != alarms.end(); ++it ) {
+ Alarm *alarm = *it;
+ int offset = 0;
+ TQString remStr, atStr, offsetStr;
+ if ( alarm->hasTime() ) {
+ offset = 0;
+ if ( alarm->time().isValid() ) {
+ atStr = KGlobal::locale()->formatDateTime( alarm->time() );
+ }
+ } else if ( alarm->hasStartOffset() ) {
+ offset = alarm->startOffset().asSeconds();
+ if ( offset < 0 ) {
+ offset = -offset;
+ offsetStr = i18n( "N days/hours/minutes before the start datetime",
+ "%1 before the start" );
+ } else if ( offset > 0 ) {
+ offsetStr = i18n( "N days/hours/minutes after the start datetime",
+ "%1 after the start" );
+ } else { //offset is 0
+ if ( incidence->dtStart().isValid() ) {
+ atStr = KGlobal::locale()->formatDateTime( incidence->dtStart() );
+ }
+ }
+ } else if ( alarm->hasEndOffset() ) {
+ offset = alarm->endOffset().asSeconds();
+ if ( offset < 0 ) {
+ offset = -offset;
+ if ( incidence->type() == "Todo" ) {
+ offsetStr = i18n( "N days/hours/minutes before the due datetime",
+ "%1 before the to-do is due" );
+ } else {
+ offsetStr = i18n( "N days/hours/minutes before the end datetime",
+ "%1 before the end" );
+ }
+ } else if ( offset > 0 ) {
+ if ( incidence->type() == "Todo" ) {
+ offsetStr = i18n( "N days/hours/minutes after the due datetime",
+ "%1 after the to-do is due" );
+ } else {
+ offsetStr = i18n( "N days/hours/minutes after the end datetime",
+ "%1 after the end" );
+ }
+ } else { //offset is 0
+ if ( incidence->type() == "Todo" ) {
+ Todo *t = static_cast<Todo *>( incidence );
+ if ( t->dtDue().isValid() ) {
+ atStr = KGlobal::locale()->formatDateTime( t->dtDue() );
+ }
+ } else {
+ Event *e = static_cast<Event *>( incidence );
+ if ( e->dtEnd().isValid() ) {
+ atStr = KGlobal::locale()->formatDateTime( e->dtEnd() );
+ }
+ }
+ }
+ }
+ if ( offset == 0 ) {
+ if ( !atStr.isEmpty() ) {
+ remStr = i18n( "reminder occurs at datetime", "at %1" ).arg( atStr );
+ }
+ } else {
+ remStr = offsetStr.arg( secs2Duration( offset ) );
+ }
+
+ if ( alarm->repeatCount() > 0 ) {
+ TQString countStr = i18n( "repeats once", "repeats %n times", alarm->repeatCount() );
+ TQString intervalStr = i18n( "interval is N days/hours/minutes", "interval is %1" ).
+ arg( secs2Duration( alarm->snoozeTime().asSeconds() ) );
+ TQString repeatStr = i18n( "(repeat string, interval string)", "(%1, %2)" ).
+ arg( countStr, intervalStr );
+ remStr = remStr + ' ' + repeatStr;
+
+ }
+ reminderStringList << remStr;
+ }
+ }
+
+ return reminderStringList;
}
diff --git a/libkcal/incidenceformatter.h b/libkcal/incidenceformatter.h
index ce37b016..b2dcd432 100644
--- a/libkcal/incidenceformatter.h
+++ b/libkcal/incidenceformatter.h
@@ -3,6 +3,7 @@
Copyright (c) 2001-2003 Cornelius Schumacher <schumacher@kde.org>
Copyright (c) 2004 Reinhold Kainhofer <reinhold@kainhofer.com>
+ Copyright (c) 2009 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
@@ -22,10 +23,12 @@
#ifndef KCAL_INCIDENCEFORMATTER_H
#define KCAL_INCIDENCEFORMATTER_H
-#include <tqstring.h>
-
#include "libkcal_export.h"
+#include <tqdatetime.h>
+#include <tqstring.h>
+#include <tqstringlist.h>
+
namespace KCal {
class Calendar;
class Incidence;
@@ -36,7 +39,7 @@ class LIBKCAL_EXPORT InvitationFormatterHelper
public:
virtual TQString generateLinkURL( const TQString &id ) { return id; }
virtual TQString makeLink( const TQString &id, const TQString &text );
- virtual Calendar* calendar() const { return 0; }
+ virtual Calendar *calendar() const { return 0; }
};
/**
@@ -49,12 +52,27 @@ class LIBKCAL_EXPORT InvitationFormatterHelper
class LIBKCAL_EXPORT IncidenceFormatter
{
public:
- static TQString toolTipString( IncidenceBase *incidence, bool richText = true );
+ static TQString KDE_DEPRECATED toolTipString( IncidenceBase *incidence, bool richText = true );
+ static TQString toolTipStr( Calendar *calendar,
+ IncidenceBase *incidence,
+ const TQDate &date=TQDate(),
+ bool richText = true );
static TQString mailBodyString( IncidenceBase *incidencebase );
- static TQString extensiveDisplayString( IncidenceBase *incidence );
+ static TQString KDE_DEPRECATED extensiveDisplayString( IncidenceBase *incidence );
+ static TQString extensiveDisplayStr( Calendar *calendar,
+ IncidenceBase *incidence,
+ const TQDate &date=TQDate() );
static TQString formatICalInvitation( TQString invitation, Calendar *mCalendar,
InvitationFormatterHelper *helper );
+ static TQString KDE_DEPRECATED formatICalInvitationNoHtml( TQString invitation,
+ Calendar *mCalendar,
+ InvitationFormatterHelper *helper );
+ static TQString formatICalInvitationNoHtml( TQString invitation,
+ Calendar *mCalendar,
+ InvitationFormatterHelper *helper,
+ const TQString &sender );
+
// Format a TNEF attachment to an HTML mail
static TQString formatTNEFInvitation( const TQByteArray& tnef,
Calendar *mCalendar,
@@ -63,7 +81,44 @@ class LIBKCAL_EXPORT IncidenceFormatter
static TQString msTNEFToVPart( const TQByteArray& tnef );
static TQString recurrenceString( Incidence *incidence );
+
+ /*
+ Returns a reminder string computed for the specified Incidence.
+ Each item of the returning TQStringList corresponds to a string
+ representation of an reminder belonging to this incidence.
+ @param incidence is a pointer to the Incidence.
+ @param shortfmt if false, a short version of each reminder is printed;
+ else a longer version of each reminder is printed.
+ */
+ static TQStringList reminderStringList( Incidence *incidence, bool shortfmt = true );
+
+ static TQString timeToString( const TQDateTime &date, bool shortfmt = true );
+
+ static TQString dateToString( const TQDateTime &date, bool shortfmt = true );
+
+ static TQString dateTimeToString( const TQDateTime &date,
+ bool dateOnly = false,
+ bool shortfmt = true );
+ /**
+ Returns a Calendar Resource label name for the specified Incidence.
+ @param calendar is a pointer to the Calendar.
+ @param incidence is a pointer to the Incidence.
+ */
+ static TQString resourceString( Calendar *calendar, Incidence *incidence );
+
+ /**
+ Returns a duration string computed for the specified Incidence.
+ Only makes sense for Events and Todos.
+ @param incidence is a pointer to the Incidence.
+ */
+ static TQString durationString( Incidence *incidence );
+
private:
+ static TQString formatICalInvitationHelper( TQString invitation,
+ Calendar *mCalendar,
+ InvitationFormatterHelper *helper,
+ bool noHtmlMode,
+ const TQString &sender );
class EventViewerVisitor;
class ScheduleMessageVisitor;
class InvitationHeaderVisitor;
diff --git a/libkcal/kcal_manager.desktop b/libkcal/kcal_manager.desktop
index e606a9d6..a0a72e28 100644
--- a/libkcal/kcal_manager.desktop
+++ b/libkcal/kcal_manager.desktop
@@ -29,7 +29,6 @@ Name[hu]=Naptár
Name[is]=Dagatal
Name[it]=Calendario
Name[ja]=カレンダー
-Name[ka]=კალენდარი
Name[kk]=Күнтізбе
Name[km]=ប្រតិទិន
Name[lt]=Kalendorius
@@ -56,8 +55,7 @@ Name[tg]=Тақвим
Name[th]=บันทึกประจำวัน
Name[tr]=Takvim
Name[uk]=Календар
-Name[uz]=Kalendar
-Name[uz@cyrillic]=Календар
+Name[uz]=Календар
Name[zh_CN]=日历
Name[zh_TW]=行事曆
Type=Service
diff --git a/libkcal/libical/configure.in.in b/libkcal/libical/configure.in.in
new file mode 100644
index 00000000..79aafbc3
--- /dev/null
+++ b/libkcal/libical/configure.in.in
@@ -0,0 +1,20 @@
+dnl Checks for programs.
+AC_PROG_YACC
+AM_PROG_LEX
+
+AC_CHECK_PROGS(PERL, perl5 perl)
+
+AC_DEFINE(ICAL_SAFESAVES,1, [safe saves])
+AC_DEFINE(ICAL_UNIX_NEWLINE,1, [unix newline])
+
+AC_CHECK_HEADERS(time.h sys/types.h assert.h)
+
+dnl Checks for typedefs, structures, and compiler characteristics.
+AC_C_CONST
+AC_TYPE_SIZE_T
+AC_STRUCT_TM
+AM_PROG_LEX
+
+dnl Checks for library functions.
+AC_CHECK_FUNCS(strdup)
+
diff --git a/libkcal/libical/src/libical/icalattach.c b/libkcal/libical/src/libical/icalattach.c
new file mode 100644
index 00000000..106096bf
--- /dev/null
+++ b/libkcal/libical/src/libical/icalattach.c
@@ -0,0 +1,151 @@
+/* -*- Mode: C -*-
+ ======================================================================
+ FILE: icalattach.c
+ CREATOR: acampi 28 May 02
+
+ $Id: icalattach.c 1024886 2009-09-17 13:28:13Z winterz $
+ $Locker: $
+
+
+ (C) COPYRIGHT 2000, Andrea Campi
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of either:
+
+ The LGPL as published by the Free Software Foundation, version
+ 2.1, available at: http://www.fsf.org/copyleft/lesser.html
+
+ Or:
+
+ The Mozilla Public License Version 1.0. You may obtain a copy of
+ the License at http://www.mozilla.org/MPL/
+
+ The original code is icaltypes.c
+
+ ======================================================================*/
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "icaltypes.h"
+#include "icalerror.h"
+#include "icalmemory.h"
+#include "icalattachimpl.h"
+#include <stdlib.h> /* for malloc and abs() */
+#include <errno.h> /* for errno */
+#include <string.h> /* for icalmemory_strdup */
+#include <assert.h>
+
+icalattach *
+icalattach_new_from_url (const char *url)
+{
+ icalattach *attach;
+ char *url_copy;
+
+ icalerror_check_arg_rz ((url != NULL), "url");
+
+ if ((attach = malloc (sizeof (icalattach))) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ if ((url_copy = strdup (url)) == NULL) {
+ free (attach);
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ attach->refcount = 1;
+ attach->is_url = 1;
+ attach->u.url.url = url_copy;
+
+ return attach;
+}
+
+icalattach *
+icalattach_new_from_data (unsigned char *data, icalattach_free_fn_t free_fn,
+ void *free_fn_data)
+{
+ icalattach *attach;
+ char *data_copy;
+
+ icalerror_check_arg_rz ((data != NULL), "data");
+
+ if ((attach = malloc (sizeof (icalattach))) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ if ((data_copy = strdup (data)) == NULL) {
+ free (attach);
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ attach->refcount = 1;
+ attach->is_url = 0;
+ attach->u.data.data = data_copy;
+ attach->u.data.free_fn = free_fn;
+ attach->u.data.free_fn_data = free_fn_data;
+
+ return attach;
+}
+
+void
+icalattach_ref (icalattach *attach)
+{
+ icalerror_check_arg_rv ((attach != NULL), "attach");
+ icalerror_check_arg_rv ((attach->refcount > 0), "attach->refcount > 0");
+
+ attach->refcount++;
+}
+
+void
+icalattach_unref (icalattach *attach)
+{
+ icalerror_check_arg_rv ((attach != NULL), "attach");
+ icalerror_check_arg_rv ((attach->refcount > 0), "attach->refcount > 0");
+
+ attach->refcount--;
+
+ if (attach->refcount != 0)
+ return;
+
+ if (attach->is_url) {
+ free (attach->u.url.url);
+ } else {
+ free (attach->u.data.data);
+/* unused for now
+ if (attach->u.data.free_fn)
+ (* attach->u.data.free_fn) (attach->u.data.data, attach->u.data.free_fn_data);
+*/
+ }
+
+ free (attach);
+}
+
+int
+icalattach_get_is_url (icalattach *attach)
+{
+ icalerror_check_arg_rz ((attach != NULL), "attach");
+
+ return attach->is_url ? 1 : 0;
+}
+
+const char *
+icalattach_get_url (icalattach *attach)
+{
+ icalerror_check_arg_rz ((attach != NULL), "attach");
+ icalerror_check_arg_rz ((attach->is_url), "attach->is_url");
+
+ return attach->u.url.url;
+}
+
+unsigned char *
+icalattach_get_data (icalattach *attach)
+{
+ icalerror_check_arg_rz ((attach != NULL), "attach");
+ icalerror_check_arg_rz ((!attach->is_url), "!attach->is_url");
+
+ return attach->u.data.data;
+}
diff --git a/libkcal/local.desktop b/libkcal/local.desktop
index 66956592..9cf91413 100644
--- a/libkcal/local.desktop
+++ b/libkcal/local.desktop
@@ -26,7 +26,6 @@ Name[hu]=Helyi fájlban tárolt naptár
Name[is]=Dagatal í staðbundinni skrá
Name[it]=Calendario in file locale
Name[ja]=ローカルファイルのカレンダー
-Name[ka]=კალენდარი ლოკალურ ფაილში
Name[kk]=Жергілікті файлдағы күнтізбе
Name[km]=ប្រតិទិន​នៅ​ក្នុង​ឯកសារ​មូលដ្ឋាន
Name[lt]=Kalendorius vietinėje byloje
diff --git a/libkcal/localdir.desktop b/libkcal/localdir.desktop
index f65fe745..d7e4b413 100644
--- a/libkcal/localdir.desktop
+++ b/libkcal/localdir.desktop
@@ -26,7 +26,6 @@ Name[hu]=Helyi könyvtárban tárolt naptár
Name[is]=Dagatal í staðbundinni möppu
Name[it]=Calendario nella directory locale
Name[ja]=ローカルディレクトリのカレンダー
-Name[ka]=კალენდარი ლოკალურ დირექტორიაში
Name[kk]=Жергілікті каталогтағы күнтізбе
Name[km]=ប្រតិទិន​នៅ​ក្នុង​ថត​មូលដ្ឋាន
Name[lt]=Kalendorius vietiniame aplanke
diff --git a/libkcal/period.cpp b/libkcal/period.cpp
index be07d7d3..ff944ee9 100644
--- a/libkcal/period.cpp
+++ b/libkcal/period.cpp
@@ -51,6 +51,14 @@ bool Period::operator<( const Period& other )
return start() < other.start();
}
+bool Period::operator==( const Period &other ) const
+{
+ return
+ mStart == other.mStart &&
+ mEnd == other.mEnd &&
+ mHasDuration == other.mHasDuration;
+}
+
TQDateTime Period::start() const
{
return mStart;
diff --git a/libkcal/period.h b/libkcal/period.h
index 64dd24d9..b0ca32a3 100644
--- a/libkcal/period.h
+++ b/libkcal/period.h
@@ -42,6 +42,24 @@ class KDE_EXPORT Period
/** Returns true if this element is smaller than the @param other one */
bool operator<( const Period& other );
+ /**
+ Returns true if this period is equal to the @p other one.
+ Even if their start and end times are the same, two periods are
+ considered not equal if one is defined in terms of a duration and the
+ other in terms of a start and end time.
+
+ @param other the other period to compare
+ */
+ bool operator==( const Period &other ) const;
+
+ /**
+ Returns true if this period is not equal to the @p other one.
+
+ @param other the other period to compare
+ @see operator==()
+ */
+ bool operator!=( const Period &other ) const { return !operator==( other ); }
+
TQDateTime start() const;
TQDateTime end() const;
Duration duration();
diff --git a/libkcal/recurrence.cpp b/libkcal/recurrence.cpp
index c2d7897a..ae47db32 100644
--- a/libkcal/recurrence.cpp
+++ b/libkcal/recurrence.cpp
@@ -34,7 +34,6 @@
using namespace KCal;
-
Recurrence::Recurrence()
: mFloating( false ),
mRecurReadOnly(false),
@@ -282,9 +281,8 @@ bool Recurrence::recursOn(const TQDate &qd) const
if ( mRDates.contains( qd ) ) return true;
- // Check if it might recur today at all.
bool recurs = false;
- if ( startDate() == qd ) recurs = true;
+
for ( RecurrenceRule::List::ConstIterator rr = mRRules.begin(); rr != mRRules.end(); ++rr ) {
recurs = recurs || (*rr)->recursOn( qd );
}
@@ -770,10 +768,74 @@ TimeList Recurrence::recurTimesOn( const TQDate &date ) const
return times;
}
+DateTimeList Recurrence::timesInInterval( const TQDateTime &start, const TQDateTime &end ) const
+{
+ int i, count;
+ DateTimeList times;
+ for ( i = 0, count = mRRules.count(); i < count; ++i ) {
+ times += mRRules[i]->timesInInterval( start, end );
+ }
+
+ // add rdatetimes that fit in the interval
+ for ( i = 0, count = mRDateTimes.count(); i < count; ++i ) {
+ if ( mRDateTimes[i] >= start && mRDateTimes[i] <= end ) {
+ times += mRDateTimes[i];
+ }
+ }
+
+ // add rdates that fit in the interval
+ TQDateTime qdt( mStartDateTime );
+ for ( i = 0, count = mRDates.count(); i < count; ++i ) {
+ qdt.setDate( mRDates[i] );
+ if ( qdt >= start && qdt <= end ) {
+ times += qdt;
+ }
+ }
+
+ // Recurrence::timesInInterval(...) doesn't explicitly add mStartDateTime to the list
+ // of times to be returned. It calls mRRules[i]->timesInInterval(...) which include
+ // mStartDateTime.
+ // So, If we have rdates/rdatetimes but don't have any rrule we must explicitly
+ // add mStartDateTime to the list, otherwise we won't see the first occurrence.
+ if ( ( !mRDates.isEmpty() || !mRDateTimes.isEmpty() ) &&
+ mRRules.isEmpty() &&
+ start <= mStartDateTime &&
+ end >= mStartDateTime ) {
+ times += mStartDateTime;
+ }
+
+ qSortUnique( times );
+
+ // Remove excluded times
+ int idt = 0;
+ int enddt = times.count();
+ for ( i = 0, count = mExDates.count(); i < count && idt < enddt; ++i ) {
+ while ( idt < enddt && times[idt].date() < mExDates[i] ) ++idt;
+ while ( idt < enddt && times[idt].date() == mExDates[i] ) {
+ times.remove( times.at( idt ) );
+ --enddt;
+ }
+ }
+ DateTimeList extimes;
+ for ( i = 0, count = mExRules.count(); i < count; ++i ) {
+ extimes += mExRules[i]->timesInInterval( start, end );
+ }
+ extimes += mExDateTimes;
+ qSortUnique( extimes );
+
+ int st = 0;
+ for ( i = 0, count = extimes.count(); i < count; ++i ) {
+ int j = removeSorted( times, extimes[i], st );
+ if ( j >= 0 ) {
+ st = j;
+ }
+ }
+
+ return times;
+}
TQDateTime Recurrence::getNextDateTime( const TQDateTime &preDateTime ) const
{
-//kdDebug(5800) << " Recurrence::getNextDateTime after " << preDateTime << endl;
TQDateTime nextDT = preDateTime;
// prevent infinite loops, e.g. when an exrule extinguishes an rrule (e.g.
// the exrule is identical to the rrule). If an occurrence is found, break
@@ -795,40 +857,51 @@ TQDateTime Recurrence::getNextDateTime( const TQDateTime &preDateTime ) const
++loop;
// First, get the next recurrence from the RDate lists
DateTimeList dates;
- if ( nextDT < startDateTime() ) dates << startDateTime();
- DateTimeList::ConstIterator it = mRDateTimes.begin();
+ if ( nextDT < startDateTime() ) {
+ dates << startDateTime();
+ }
+
+ int end;
// Assume that the rdatetime list is sorted
- while ( it != mRDateTimes.end() && (*it) <= nextDT ) ++it;
- if ( it != mRDateTimes.end() ) dates << (*it);
+ int i = findGT( mRDateTimes, nextDT, 0 );
+ if ( i >= 0 ) {
+ dates << mRDateTimes[i];
+ }
-/*kdDebug(5800) << " nextDT: " << nextDT << ", startDT: " << startDateTime() << endl;
-kdDebug(5800) << " getNextDateTime: found " << dates.count() << " RDATES and DTSTART in loop " << loop << endl;*/
- DateList::ConstIterator dit = mRDates.begin();
- while ( dit != mRDates.end() && TQDateTime( (*dit), startDateTime().time() ) <= nextDT ) ++dit;
- if ( dit != mRDates.end() ) dates << TQDateTime( (*dit), startDateTime().time() );
+ TQDateTime qdt( startDateTime() );
+ for ( i = 0, end = mRDates.count(); i < end; ++i ) {
+ qdt.setDate( mRDates[i] );
+ if ( qdt > nextDT ) {
+ dates << qdt;
+ break;
+ }
+ }
// Add the next occurrences from all RRULEs.
- for ( RecurrenceRule::List::ConstIterator rr = mRRules.begin(); rr != mRRules.end(); ++rr ) {
- TQDateTime dt = (*rr)->getNextDate( nextDT );
- if ( dt.isValid() ) dates << dt;
+ for ( i = 0, end = mRRules.count(); i < end; ++i ) {
+ TQDateTime dt = mRRules[i]->getNextDate( nextDT );
+ if ( dt.isValid() ) {
+ dates << dt;
+ }
}
// Take the first of these (all others can't be used later on)
qSortUnique( dates );
-// kdDebug(5800) << " getNextDateTime: found " << dates.count() << " dates in loop " << loop << endl;
-
- if ( dates.isEmpty() ) return TQDateTime();
+ if ( dates.isEmpty() ) {
+ return TQDateTime();
+ }
nextDT = dates.first();
// Check if that date/time is excluded explicitly or by an exrule:
- if ( !mExDates.contains( nextDT.date() ) && !mExDateTimes.contains( nextDT ) ) {
-// kdDebug(5800) << " NextDT" << nextDT << " not excluded by EXDATE " << endl;
+ if ( !containsSorted( mExDates, nextDT.date() ) &&
+ !containsSorted( mExDateTimes, nextDT ) ) {
bool allowed = true;
- for ( RecurrenceRule::List::ConstIterator rr = mExRules.begin(); rr != mExRules.end(); ++rr ) {
- allowed = allowed && !( (*rr)->recursAt( nextDT ) );
+ for ( i = 0, end = mExRules.count(); i < end; ++i ) {
+ allowed = allowed && !( mExRules[i]->recursAt( nextDT ) );
+ }
+ if ( allowed ) {
+ return nextDT;
}
-// kdDebug(5800) << " NextDT " << nextDT << ", allowed=" << allowed << endl;
- if ( allowed ) return nextDT;
}
}
@@ -856,44 +929,50 @@ TQDateTime Recurrence::getPreviousDateTime( const TQDateTime &afterDateTime ) co
++loop;
// First, get the next recurrence from the RDate lists
DateTimeList dates;
- if ( prevDT > startDateTime() ) dates << startDateTime();
-
- DateTimeList::ConstIterator dtit = mRDateTimes.end();
- if ( dtit != mRDateTimes.begin() ) {
- do {
- --dtit;
- } while ( dtit != mRDateTimes.begin() && (*dtit) >= prevDT );
- if ( (*dtit) < prevDT ) dates << (*dtit);
+ if ( prevDT > startDateTime() ) {
+ dates << startDateTime();
+ }
+
+ int i = findLT( mRDateTimes, prevDT, 0 );
+ if ( i >= 0 ) {
+ dates << mRDateTimes[i];
}
- DateList::ConstIterator dit = mRDates.end();
- if ( dit != mRDates.begin() ) {
- do {
- --dit;
- } while ( dit != mRDates.begin() && TQDateTime((*dit), startDateTime().time()) >= prevDT );
- if ( TQDateTime((*dit), startDateTime().time()) < prevDT )
- dates << TQDateTime( (*dit), startDateTime().time() );
+ TQDateTime qdt( startDateTime() );
+ for ( i = mRDates.count(); --i >= 0; ) {
+ qdt.setDate( mRDates[i] );
+ if ( qdt < prevDT ) {
+ dates << qdt;
+ break;
+ }
}
// Add the previous occurrences from all RRULEs.
- for ( RecurrenceRule::List::ConstIterator rr = mRRules.begin(); rr != mRRules.end(); ++rr ) {
- TQDateTime dt = (*rr)->getPreviousDate( prevDT );
- if ( dt.isValid() ) dates << dt;
+ int end;
+ for ( i = 0, end = mRRules.count(); i < end; ++i ) {
+ TQDateTime dt = mRRules[i]->getPreviousDate( prevDT );
+ if ( dt.isValid() ) {
+ dates << dt;
+ }
}
-//kdDebug(5800) << " getPreviousDateTime: found " << dates.count() << " dates in loop " << loop << endl;
// Take the last of these (all others can't be used later on)
qSortUnique( dates );
- if ( dates.isEmpty() ) return TQDateTime();
+ if ( dates.isEmpty() ) {
+ return TQDateTime();
+ }
prevDT = dates.last();
// Check if that date/time is excluded explicitly or by an exrule:
- if ( !mExDates.contains( prevDT.date() ) && !mExDateTimes.contains( prevDT ) ) {
+ if ( !containsSorted( mExDates, prevDT.date() ) &&
+ !containsSorted( mExDateTimes, prevDT ) ) {
bool allowed = true;
- for ( RecurrenceRule::List::ConstIterator rr = mExRules.begin(); rr != mExRules.end(); ++rr ) {
- allowed = allowed && !( (*rr)->recursAt( prevDT ) );
+ for ( i = 0, end = mExRules.count(); i < end; ++i ) {
+ allowed = allowed && !( mExRules[i]->recursAt( prevDT ) );
+ }
+ if ( allowed ) {
+ return prevDT;
}
- if ( allowed ) return prevDT;
}
}
diff --git a/libkcal/recurrence.h b/libkcal/recurrence.h
index ec696597..0a594a6f 100644
--- a/libkcal/recurrence.h
+++ b/libkcal/recurrence.h
@@ -166,6 +166,21 @@ class LIBKCAL_EXPORT Recurrence : public RecurrenceRule::Observer
*/
TQValueList<TQTime> recurTimesOn(const TQDate &date) const;
+ /** Returns a list of all the times at which the recurrence will occur
+ * between two specified times.
+ *
+ * There is a (large) maximum limit to the number of times returned. If due to
+ * this limit the list is incomplete, this is indicated by the last entry being
+ * set to an invalid TQDateTime value. If you need further values, call the
+ * method again with a start time set to just after the last valid time returned.
+ *
+ * @param start inclusive start of interval
+ * @param end inclusive end of interval
+ * @return list of date/time values
+ */
+ DateTimeList timesInInterval( const TQDateTime &start, const TQDateTime &end ) const;
+
+
/** Returns the date and time of the next recurrence, after the specified date/time.
* If the recurrence has no time, the next date after the specified date is returned.
* @param preDateTime the date/time after which to find the recurrence.
diff --git a/libkcal/recurrencerule.cpp b/libkcal/recurrencerule.cpp
index ce64aadc..17292ec5 100644
--- a/libkcal/recurrencerule.cpp
+++ b/libkcal/recurrencerule.cpp
@@ -32,6 +32,8 @@
using namespace KCal;
+// Maximum number of intervals to process
+const int LOOP_LIMIT = 10000;
// FIXME: If Qt is ever changed so that TQDateTime:::addSecs takes into account
// DST shifts, we need to use our own addSecs method, too, since we
@@ -774,6 +776,8 @@ void RecurrenceRule::setWeekStart( short weekStart )
void RecurrenceRule::buildConstraints()
{
+ mTimedRepetition = 0;
+ mNoByRules = mBySetPos.isEmpty();
mConstraints.clear();
Constraint con;
if ( mWeekStart > 0 ) con.weekstart = mWeekStart;
@@ -785,6 +789,7 @@ void RecurrenceRule::buildConstraints()
#define intConstraint( list, element ) \
if ( !list.isEmpty() ) { \
+ mNoByRules = false; \
for ( it = mConstraints.constBegin(); it != mConstraints.constEnd(); ++it ) { \
for ( intit = list.constBegin(); intit != list.constEnd(); ++intit ) { \
con = (*it); \
@@ -806,6 +811,7 @@ void RecurrenceRule::buildConstraints()
#undef intConstraint
if ( !mByDays.isEmpty() ) {
+ mNoByRules = false;
for ( it = mConstraints.constBegin(); it != mConstraints.constEnd(); ++it ) {
TQValueList<WDayPos>::const_iterator dayit;
for ( dayit = mByDays.constBegin(); dayit != mByDays.constEnd(); ++dayit ) {
@@ -866,12 +872,28 @@ void RecurrenceRule::buildConstraints()
}
#undef fixConstraint
- Constraint::List::Iterator conit = mConstraints.begin();
- while ( conit != mConstraints.end() ) {
- if ( (*conit).isConsistent( mPeriod ) ) {
- ++conit;
- } else {
- conit = mConstraints.remove( conit );
+ if ( mNoByRules ) {
+ switch ( mPeriod ) {
+ case rHourly:
+ mTimedRepetition = mFrequency * 3600;
+ break;
+ case rMinutely:
+ mTimedRepetition = mFrequency * 60;
+ break;
+ case rSecondly:
+ mTimedRepetition = mFrequency;
+ break;
+ default:
+ break;
+ }
+ } else {
+ Constraint::List::Iterator conit = mConstraints.begin();
+ while ( conit != mConstraints.end() ) {
+ if ( (*conit).isConsistent( mPeriod ) ) {
+ ++conit;
+ } else {
+ conit = mConstraints.remove( conit );
+ }
}
}
}
@@ -941,81 +963,179 @@ bool RecurrenceRule::dateMatchesRules( const TQDateTime &qdt ) const
bool RecurrenceRule::recursOn( const TQDate &qd ) const
{
-// kdDebug(5800) << " RecurrenceRule::recursOn: " << qd << endl;
- if ( qd < startDt().date() ) return false;
+ int i, iend;
+ if ( doesFloat() ) {
+ // It's a date-only rule, so it has no time specification.
+ if ( qd < mDateStart.date() ) {
+ return false;
+ }
+ // Start date is only included if it really matches
+ TQDate endDate;
+ if ( mDuration >= 0 ) {
+ endDate = endDt().date();
+ if ( qd > endDate ) {
+ return false;
+ }
+ }
+
+ // The date must be in an appropriate interval (getNextValidDateInterval),
+ // Plus it must match at least one of the constraints
+ bool match = false;
+ for ( i = 0, iend = mConstraints.count(); i < iend && !match; ++i ) {
+ match = mConstraints[i].matches( qd, recurrenceType() );
+ }
+ if ( !match ) {
+ return false;
+ }
+
+ TQDateTime start( qd, TQTime( 0, 0, 0 ) );
+ Constraint interval( getNextValidDateInterval( start, recurrenceType() ) );
+ // Constraint::matches is quite efficient, so first check if it can occur at
+ // all before we calculate all actual dates.
+ if ( !interval.matches( qd, recurrenceType() ) ) {
+ return false;
+ }
+ // We really need to obtain the list of dates in this interval, since
+ // otherwise BYSETPOS will not work (i.e. the date will match the interval,
+ // but BYSETPOS selects only one of these matching dates!
+ TQDateTime end = start.addDays(1);
+ do {
+ DateTimeList dts = datesForInterval( interval, recurrenceType() );
+ for ( i = 0, iend = dts.count(); i < iend; ++i ) {
+ if ( dts[i].date() >= qd ) {
+ return dts[i].date() == qd;
+ }
+ }
+ interval.increase( recurrenceType(), frequency() );
+ } while ( interval.intervalDateTime( recurrenceType() ) < end );
+ return false;
+ }
+
+ // It's a date-time rule, so we need to take the time specification into account.
+ TQDateTime start( qd, TQTime( 0, 0, 0 ) );
+ TQDateTime end = start.addDays( 1 );
+ if ( end < mDateStart ) {
+ return false;
+ }
+ if ( start < mDateStart ) {
+ start = mDateStart;
+ }
+
// Start date is only included if it really matches
-// if ( qd == startDt().date() ) return true;
- if ( mDuration >= 0 && qd > endDt().date() ) return false;
+ if ( mDuration >= 0 ) {
+ TQDateTime endRecur = endDt();
+ if ( endRecur.isValid() ) {
+ if ( start > endRecur ) {
+ return false;
+ }
+ if ( end > endRecur ) {
+ end = endRecur; // limit end-of-day time to end of recurrence rule
+ }
+ }
+ }
+
+ if ( mTimedRepetition ) {
+ // It's a simple sub-daily recurrence with no constraints
+ int n = static_cast<int>( ( mDateStart.secsTo( start ) - 1 ) % mTimedRepetition );
+ return start.addSecs( mTimedRepetition - n ) < end;
+ }
+
+ // Find the start and end dates in the time spec for the rule
+ TQDate startDay = start.date();
+ TQDate endDay = end.addSecs( -1 ).date();
+ int dayCount = startDay.daysTo( endDay ) + 1;
// The date must be in an appropriate interval (getNextValidDateInterval),
// Plus it must match at least one of the constraints
bool match = false;
- for ( Constraint::List::ConstIterator it = mConstraints.begin();
- it!=mConstraints.end(); ++it ) {
- match = match || ( (*it).matches( qd, recurrenceType() ) );
+ for ( i = 0, iend = mConstraints.count(); i < iend && !match; ++i ) {
+ match = mConstraints[i].matches( startDay, recurrenceType() );
+ for ( int day = 1; day < dayCount && !match; ++day ) {
+ match = mConstraints[i].matches( startDay.addDays( day ), recurrenceType() );
+ }
+ }
+ if ( !match ) {
+ return false;
}
- if ( !match ) return false;
- TQDateTime tmp( qd, TQTime( 0, 0, 0 ) );
- Constraint interval( getNextValidDateInterval( tmp, recurrenceType() ) );
+
+ Constraint interval( getNextValidDateInterval( start, recurrenceType() ) );
// Constraint::matches is quite efficient, so first check if it can occur at
// all before we calculate all actual dates.
- if ( !interval.matches( qd, recurrenceType() ) ) return false;
+ match = false;
+ Constraint intervalm = interval;
+ do {
+ match = intervalm.matches( startDay, recurrenceType() );
+ for ( int day = 1; day < dayCount && !match; ++day ) {
+ match = intervalm.matches( startDay.addDays( day ), recurrenceType() );
+ }
+ if ( match ) {
+ break;
+ }
+ intervalm.increase( recurrenceType(), frequency() );
+ } while ( intervalm.intervalDateTime( recurrenceType() ) < end );
+ if ( !match ) {
+ return false;
+ }
+
// We really need to obtain the list of dates in this interval, since
// otherwise BYSETPOS will not work (i.e. the date will match the interval,
// but BYSETPOS selects only one of these matching dates!
- DateTimeList times = datesForInterval( interval, recurrenceType() );
- DateTimeList::ConstIterator it = times.begin();
- while ( ( it != times.end() ) && ( (*it).date() < qd ) )
- ++it;
- if ( it != times.end() ) {
- // If we are beyond the end...
- if ( mDuration >= 0 && (*it) > endDt() )
- return false;
- if ( (*it).date() == qd )
- return true;
- }
+ do {
+ DateTimeList dts = datesForInterval( interval, recurrenceType() );
+ int i = findGE( dts, start, 0 );
+ if ( i >= 0 ) {
+ return dts[i] < end;
+ }
+ interval.increase( recurrenceType(), frequency() );
+ } while ( interval.intervalDateTime( recurrenceType() ) < end );
+
return false;
}
-
-bool RecurrenceRule::recursAt( const TQDateTime &qd ) const
+bool RecurrenceRule::recursAt( const TQDateTime &dt ) const
{
-// kdDebug(5800) << " RecurrenceRule::recursAt: " << qd << endl;
- if ( doesFloat() ) return recursOn( qd.date() );
- if ( qd < startDt() ) return false;
+ if ( doesFloat() ) {
+ return recursOn( dt.date() );
+ }
+ if ( dt < mDateStart ) {
+ return false;
+ }
// Start date is only included if it really matches
-// if ( qd == startDt() ) return true;
- if ( mDuration >= 0 && qd > endDt() ) return false;
+ if ( mDuration >= 0 && dt > endDt() ) {
+ return false;
+ }
+
+ if ( mTimedRepetition ) {
+ // It's a simple sub-daily recurrence with no constraints
+ return !( mDateStart.secsTo( dt ) % mTimedRepetition );
+ }
// The date must be in an appropriate interval (getNextValidDateInterval),
// Plus it must match at least one of the constraints
- bool match = dateMatchesRules( qd );
- if ( !match ) return false;
+ if ( !dateMatchesRules( dt ) ) {
+ return false;
+ }
// if it recurs every interval, speed things up...
-// if ( mFrequency == 1 && mBySetPos.isEmpty() && mByDays.isEmpty() ) return true;
- Constraint interval( getNextValidDateInterval( qd, recurrenceType() ) );
+// if ( d->mFrequency == 1 && d->mBySetPos.isEmpty() && d->mByDays.isEmpty() ) return true;
+ Constraint interval( getNextValidDateInterval( dt, recurrenceType() ) );
// TODO_Recurrence: Does this work with BySetPos???
- if ( interval.matches( qd, recurrenceType() ) ) return true;
-
+ if ( interval.matches( dt, recurrenceType() ) ) {
+ return true;
+ }
return false;
}
-
TimeList RecurrenceRule::recurTimesOn( const TQDate &date ) const
{
-// kdDebug(5800) << " RecurrenceRule::recurTimesOn: " << date << endl;
TimeList lst;
- if ( !recursOn( date ) ) return lst;
-
- if ( doesFloat() ) return lst;
-
- TQDateTime dt( date, TQTime( 0, 0, 0 ) );
- bool valid = dt.isValid() && ( dt.date() == date );
- while ( valid ) {
- // TODO: Add a flag so that the date is never increased!
- dt = getNextDate( dt );
- valid = dt.isValid() && ( dt.date() == date );
- if ( valid ) lst.append( dt.time() );
+ if ( doesFloat() ) {
+ return lst;
+ }
+ TQDateTime start( date, TQTime( 0, 0, 0 ) );
+ TQDateTime end = start.addDays( 1 ).addSecs( -1 );
+ DateTimeList dts = timesInInterval( start, end ); // returns between start and end inclusive
+ for ( int i = 0, iend = dts.count(); i < iend; ++i ) {
+ lst += dts[i].time();
}
return lst;
}
@@ -1150,6 +1270,104 @@ TQDateTime RecurrenceRule::getNextDate( const TQDateTime &preDate ) const
return TQDateTime();
}
+DateTimeList RecurrenceRule::timesInInterval( const TQDateTime &dtStart,
+ const TQDateTime &dtEnd ) const
+{
+ TQDateTime start = dtStart;
+ TQDateTime end = dtEnd;
+ DateTimeList result;
+ if ( end < mDateStart ) {
+ return result; // before start of recurrence
+ }
+ TQDateTime enddt = end;
+ if ( mDuration >= 0 ) {
+ TQDateTime endRecur = endDt();
+ if ( endRecur.isValid() ) {
+ if ( start > endRecur ) {
+ return result; // beyond end of recurrence
+ }
+ if ( end > endRecur ) {
+ enddt = endRecur; // limit end time to end of recurrence rule
+ }
+ }
+ }
+
+ if ( mTimedRepetition ) {
+ // It's a simple sub-daily recurrence with no constraints
+ int n = static_cast<int>( ( mDateStart.secsTo( start ) - 1 ) % mTimedRepetition );
+ TQDateTime dt = start.addSecs( mTimedRepetition - n );
+ if ( dt < enddt ) {
+ n = static_cast<int>( ( dt.secsTo( enddt ) - 1 ) / mTimedRepetition ) + 1;
+ // limit n by a sane value else we can "explode".
+ n = QMIN( n, LOOP_LIMIT );
+ for ( int i = 0; i < n; dt = dt.addSecs( mTimedRepetition ), ++i ) {
+ result += dt;
+ }
+ }
+ return result;
+ }
+
+ TQDateTime st = start;
+ bool done = false;
+ if ( mDuration > 0 ) {
+ if ( !mCached ) {
+ buildCache();
+ }
+ if ( mCachedDateEnd.isValid() && start > mCachedDateEnd ) {
+ return result; // beyond end of recurrence
+ }
+ int i = findGE( mCachedDates, start, 0 );
+ if ( i >= 0 ) {
+ int iend = findGT( mCachedDates, enddt, i );
+ if ( iend < 0 ) {
+ iend = mCachedDates.count();
+ } else {
+ done = true;
+ }
+ while ( i < iend ) {
+ result += mCachedDates[i++];
+ }
+ }
+ if ( mCachedDateEnd.isValid() ) {
+ done = true;
+ } else if ( !result.isEmpty() ) {
+ result += TQDateTime(); // indicate that the returned list is incomplete
+ done = true;
+ }
+ if ( done ) {
+ return result;
+ }
+ // We don't have any result yet, but we reached the end of the incomplete cache
+ st = mCachedLastDate.addSecs( 1 );
+ }
+
+ Constraint interval( getNextValidDateInterval( st, recurrenceType() ) );
+ int loop = 0;
+ do {
+ DateTimeList dts = datesForInterval( interval, recurrenceType() );
+ int i = 0;
+ int iend = dts.count();
+ if ( loop == 0 ) {
+ i = findGE( dts, st, 0 );
+ if ( i < 0 ) {
+ i = iend;
+ }
+ }
+ int j = findGT( dts, enddt, i );
+ if ( j >= 0 ) {
+ iend = j;
+ loop = LOOP_LIMIT;
+ }
+ while ( i < iend ) {
+ result += dts[i++];
+ }
+ // Increase the interval.
+ interval.increase( recurrenceType(), frequency() );
+ } while ( ++loop < LOOP_LIMIT &&
+ interval.intervalDateTime( recurrenceType() ) < end );
+ return result;
+}
+
RecurrenceRule::Constraint RecurrenceRule::getPreviousValidDateInterval( const TQDateTime &preDate, PeriodType type ) const
{
// kdDebug(5800) << " (o) getPreviousValidDateInterval after " << preDate << ", type=" << type << endl;
diff --git a/libkcal/recurrencerule.h b/libkcal/recurrencerule.h
index 049d9c52..86b8ca8e 100644
--- a/libkcal/recurrencerule.h
+++ b/libkcal/recurrencerule.h
@@ -50,6 +50,109 @@ Q_INLINE_TEMPLATES void qSortUnique( TQValueList<T> &lst )
}
}
+template <class T>
+Q_INLINE_TEMPLATES int findGE( const TQValueList<T> &lst, const T &value, int start )
+{
+ // Do a binary search to find the first item >= value
+ int st = start - 1;
+ int end = lst.count();
+ while ( end - st > 1 ) {
+ int i = ( st + end ) / 2;
+ if ( value <= lst[i] ) {
+ end = i;
+ } else {
+ st = i;
+ }
+ }
+ ++st;
+ return ( st == int( lst.count() ) ) ? -1 : st;
+}
+
+template <class T>
+Q_INLINE_TEMPLATES int findGT( const TQValueList<T> &lst, const T &value, int start )
+{
+ // Do a binary search to find the first item > value
+ int st = start - 1;
+ int end = lst.count();
+ while ( end - st > 1 ) {
+ int i = ( st + end ) / 2;
+ if ( value < lst[i] ) {
+ end = i;
+ } else {
+ st = i;
+ }
+ }
+ ++st;
+ return ( st == int( lst.count() ) ) ? -1 : st;
+}
+
+template <class T>
+Q_INLINE_TEMPLATES int findLE( const TQValueList<T> &lst, const T &value, int start )
+{
+ // Do a binary search to find the last item <= value
+ int st = start - 1;
+ int end = lst.count();
+ while ( end - st > 1 ) {
+ int i = ( st + end ) / 2;
+ if ( value < lst[i] ) {
+ end = i;
+ } else {
+ st = i;
+ }
+ }
+ return ( end > start ) ? st : -1;
+}
+
+template <class T>
+Q_INLINE_TEMPLATES int findLT( const TQValueList<T> &lst, const T &value, int start )
+{
+ // Do a binary search to find the last item < value
+ int st = start - 1;
+ int end = lst.count();
+ while ( end - st > 1 ) {
+ int i = ( st + end ) / 2;
+ if ( value <= lst[i] ) {
+ end = i;
+ } else {
+ st = i;
+ }
+ }
+ return ( end > start ) ? st : -1;
+}
+
+template <class T>
+Q_INLINE_TEMPLATES int findSorted( const TQValueList<T> &lst, const T &value, int start )
+{
+ // Do a binary search to find the item == value
+ int st = start - 1;
+ int end = lst.count();
+ while ( end - st > 1 ) {
+ int i = ( st + end ) / 2;
+ if ( value < lst[i] ) {
+ end = i;
+ } else {
+ st = i;
+ }
+ }
+ return ( end > start && value == lst[st] ) ? st : -1;
+}
+
+template <class T>
+Q_INLINE_TEMPLATES int removeSorted( TQValueList<T> &lst, const T &value, int start )
+{
+ int i = findSorted( lst, value, start );
+ if ( i >= 0 ) {
+ lst.remove( lst.at( i ) );
+ }
+ return i;
+}
+
+template <class T>
+Q_INLINE_TEMPLATES bool containsSorted( const TQValueList<T> &lst, const T &value )
+{
+ return findSorted( lst, value, 0 ) >= 0;
+}
+
namespace KCal {
@@ -188,6 +291,18 @@ class LIBKCAL_EXPORT RecurrenceRule
*/
TimeList recurTimesOn( const TQDate &date ) const;
+ /** Returns a list of all the times at which the recurrence will occur
+ * between two specified times.
+ *
+ * There is a (large) maximum limit to the number of times returned. If due to
+ * this limit the list is incomplete, this is indicated by the last entry being
+ * set to an invalid KDateTime value. If you need further values, call the
+ * method again with a start time set to just after the last valid time returned.
+ * @param start inclusive start of interval
+ * @param end inclusive end of interval
+ * @return list of date/time values
+ */
+ DateTimeList timesInInterval( const TQDateTime &start, const TQDateTime &end ) const;
/** Returns the date and time of the next recurrence, after the specified date/time.
* If the recurrence has no time, the next date after the specified date is returned.
@@ -331,8 +446,12 @@ class LIBKCAL_EXPORT RecurrenceRule
// Cache for duration
mutable DateTimeList mCachedDates;
- mutable bool mCached;
mutable TQDateTime mCachedDateEnd;
+ mutable TQDateTime mCachedLastDate; // when mCachedDateEnd invalid, last date checked
+ mutable bool mCached;
+
+ bool mNoByRules; // no BySeconds, ByMinutes, ... rules exist
+ uint mTimedRepetition; // repeats at a regular number of seconds interval, or 0
class Private;
Private *d;
diff --git a/libkcal/resourcecached.cpp b/libkcal/resourcecached.cpp
index 1a4e8b19..dacab7e9 100644
--- a/libkcal/resourcecached.cpp
+++ b/libkcal/resourcecached.cpp
@@ -50,7 +50,7 @@ static bool m_editoropen = false;
ResourceCached::ResourceCached( const KConfig* config )
: ResourceCalendar( config ), mCalendar( TQString::fromLatin1( "UTC" ) ),
- mReloadPolicy( ReloadNever ), mReloadInterval( 10 ),
+ mReloadPolicy( ReloadNever ), mReloadInterval( 10 ),
mReloadTimer( 0, "mReloadTimer" ), mReloaded( false ),
mSavePolicy( SaveNever ), mSaveInterval( 10 ),
mSaveTimer( 0, "mSaveTimer" ), mIdMapper( "kcal/uidmaps/" )
@@ -161,6 +161,12 @@ bool ResourceCached::addEvent(Event *event)
return mCalendar.addEvent( event );
}
+bool ResourceCached::addEvent(Event *event, const TQString &subresource )
+{
+ Q_UNUSED( subresource ); // CalendarLocal does not support subresources
+ return mCalendar.addEvent( event );
+}
+
// probably not really efficient, but...it works for now.
bool ResourceCached::deleteEvent( Event *event )
{
@@ -205,6 +211,12 @@ bool ResourceCached::addTodo( Todo *todo )
return mCalendar.addTodo( todo );
}
+bool ResourceCached::addTodo( Todo *todo, const TQString &subresource )
+{
+ Q_UNUSED( subresource ); // CalendarLocal does not support subresources
+ return mCalendar.addTodo( todo );
+}
+
bool ResourceCached::deleteTodo( Todo *todo )
{
return mCalendar.deleteTodo( todo );
@@ -231,11 +243,14 @@ Todo::List ResourceCached::rawTodosForDate( const TQDate &date )
return mCalendar.rawTodosForDate( date );
}
-
bool ResourceCached::addJournal( Journal *journal )
{
- kdDebug(5800) << "Adding Journal on " << journal->dtStart().toString() << endl;
+ return mCalendar.addJournal( journal );
+}
+bool ResourceCached::addJournal( Journal *journal, const TQString &subresource )
+{
+ Q_UNUSED( subresource ); // CalendarLocal does not support subresources
return mCalendar.addJournal( journal );
}
diff --git a/libkcal/resourcecached.h b/libkcal/resourcecached.h
index 60976698..74fcf5d6 100644
--- a/libkcal/resourcecached.h
+++ b/libkcal/resourcecached.h
@@ -136,7 +136,9 @@ class KDE_EXPORT ResourceCached : public ResourceCalendar,
/**
Add event to calendar.
*/
- bool addEvent(Event *anEvent);
+ KDE_DEPRECATED bool addEvent( Event *event );
+ bool addEvent( Event *event, const TQString &subresource );
+
/**
Deletes an event from this calendar.
*/
@@ -174,7 +176,9 @@ class KDE_EXPORT ResourceCached : public ResourceCalendar,
/**
Add a todo to the todolist.
*/
- bool addTodo( Todo *todo );
+ KDE_DEPRECATED bool addTodo( Todo *todo );
+ bool addTodo( Todo *todo, const TQString &subresource );
+
/**
Remove a todo from the todolist.
*/
@@ -195,15 +199,17 @@ class KDE_EXPORT ResourceCached : public ResourceCalendar,
/**
Add a Journal entry to calendar
*/
- virtual bool addJournal( Journal * );
+ KDE_DEPRECATED bool addJournal( Journal *journal );
+ bool addJournal( Journal *journal, const TQString &subresource );
+
/**
Remove a Journal from the calendar
*/
- virtual bool deleteJournal( Journal * );
+ bool deleteJournal( Journal * );
/**
Return Journal with given unique id.
*/
- virtual Journal *journal( const TQString &uid );
+ Journal *journal( const TQString &uid );
/**
Return list of all journals.
*/
diff --git a/libkcal/resourcecalendar.cpp b/libkcal/resourcecalendar.cpp
index c008f030..9f611e75 100644
--- a/libkcal/resourcecalendar.cpp
+++ b/libkcal/resourcecalendar.cpp
@@ -33,18 +33,38 @@
using namespace KCal;
ResourceCalendar::ResourceCalendar( const KConfig *config )
- : KRES::Resource( config ),mResolveConflict( false )
+ : KRES::Resource( config ), mResolveConflict( false )
{
+ mException = 0;
}
ResourceCalendar::~ResourceCalendar()
{
+ delete mException;
+}
+
+void ResourceCalendar::clearException()
+{
+ delete mException;
+ mException = 0;
+}
+
+void ResourceCalendar::setException( ErrorFormat *exception )
+{
+ delete mException;
+ mException = exception;
+}
+
+ErrorFormat *ResourceCalendar::exception()
+{
+ return mException;
}
void ResourceCalendar::setResolveConflict( bool b)
{
mResolveConflict = b;
}
+
TQString ResourceCalendar::infoText() const
{
TQString txt;
@@ -84,6 +104,12 @@ bool ResourceCalendar::addIncidence( Incidence *incidence )
return incidence->accept( v );
}
+bool ResourceCalendar::addIncidence( Incidence *incidence, const TQString &subresource )
+{
+ Incidence::AddSubResourceVisitor<ResourceCalendar> v( this, subresource );
+ return incidence->accept( v );
+}
+
bool ResourceCalendar::deleteIncidence( Incidence *incidence )
{
Incidence::DeleteVisitor<ResourceCalendar> v( this );
@@ -192,6 +218,8 @@ void ResourceCalendar::saveError( const TQString &err )
bool ResourceCalendar::setValue( const TQString &key, const TQString &value )
{
+ Q_UNUSED( key );
+ Q_UNUSED( value );
return false;
}
@@ -201,5 +229,21 @@ TQString ResourceCalendar::subresourceType( const TQString &resource )
return TQString();
}
+bool ResourceCalendar::subresourceWritable( const TQString &resource ) const
+{
+ if ( resource.isEmpty() ) {
+ return !readOnly();
+ } else {
+ return false;
+ }
+}
+
+void ResourceCalendar::beginAddingIncidences()
+{
+}
+
+void ResourceCalendar::endAddingIncidences()
+{
+}
#include "resourcecalendar.moc"
diff --git a/libkcal/resourcecalendar.h b/libkcal/resourcecalendar.h
index 3fea84a1..91f3d1ae 100644
--- a/libkcal/resourcecalendar.h
+++ b/libkcal/resourcecalendar.h
@@ -36,6 +36,7 @@
#include "event.h"
#include "journal.h"
#include "calendar.h"
+#include "exceptions.h"
#include <kresources/resource.h>
#include <kresources/manager.h>
@@ -55,11 +56,28 @@ class CalFormat;
*/
class LIBKCAL_EXPORT ResourceCalendar : public KRES::Resource
{
- Q_OBJECT
+ Q_OBJECT
public:
ResourceCalendar( const KConfig * );
virtual ~ResourceCalendar();
+ /**
+ Clears the exception status.
+ */
+ void clearException();
+
+ /**
+ Set exception for this object. This is used by the functions of this
+ class to report errors.
+ */
+ void setException( ErrorFormat *error );
+
+ /**
+ Returns an exception, if there is any, containing information about the
+ last error that occurred.
+ */
+ ErrorFormat *exception();
+
void setResolveConflict( bool b);
virtual void writeConfig( KConfig* config );
@@ -123,8 +141,14 @@ class LIBKCAL_EXPORT ResourceCalendar : public KRES::Resource
/**
Add incidence to resource.
+ @deprecated use addIncidence(Incidence *,const TQString &) instead.
+ */
+ virtual KDE_DEPRECATED bool addIncidence( Incidence * );
+
+ /**
+ Add incidence to resource and subresource.
*/
- virtual bool addIncidence( Incidence * );
+ virtual bool addIncidence( Incidence *, const TQString &subresource );
/**
Delete incidence from resource.
@@ -139,8 +163,10 @@ class LIBKCAL_EXPORT ResourceCalendar : public KRES::Resource
/**
Add event to resource.
+ @deprecated use addEvent(Event *,const TQString&) instead.
*/
- virtual bool addEvent( Event *event ) = 0;
+ virtual KDE_DEPRECATED bool addEvent( Event *event ) = 0;
+ virtual bool addEvent( Event *event, const TQString &subresource ) = 0;
/**
Delete event from this resource.
@@ -241,8 +267,11 @@ class LIBKCAL_EXPORT ResourceCalendar : public KRES::Resource
public:
/**
Add a todo to the todolist.
+ @deprecated use addTodo(Todo *,const TQString &) instead.
*/
- virtual bool addTodo( Todo *todo ) = 0;
+ virtual KDE_DEPRECATED bool addTodo( Todo *todo ) = 0;
+ virtual bool addTodo( Todo *todo, const TQString &subresource ) = 0;
+
/**
Remove a todo from the todolist.
*/
@@ -265,8 +294,10 @@ class LIBKCAL_EXPORT ResourceCalendar : public KRES::Resource
/**
Add a Journal entry to the resource.
+ @deprecated use addJournal(Journal *,const TQString &) instead.
*/
- virtual bool addJournal( Journal * ) = 0;
+ virtual KDE_DEPRECATED bool addJournal( Journal * ) = 0;
+ virtual bool addJournal( Journal *journal, const TQString &subresource ) = 0;
/**
Remove a Journal entry from calendar.
@@ -324,6 +355,11 @@ class LIBKCAL_EXPORT ResourceCalendar : public KRES::Resource
virtual bool subresourceActive( const TQString& ) const { return true; }
/**
+ Is this subresource writable or not?
+ */
+ virtual bool subresourceWritable( const TQString& ) const;
+
+ /**
What is the label for this subresource?
*/
virtual const TQString labelForSubresource( const TQString& resource ) const
@@ -360,6 +396,18 @@ class LIBKCAL_EXPORT ResourceCalendar : public KRES::Resource
*/
virtual TQString subresourceType( const TQString &resource );
+ /**
+ * Called when we starting adding a batch of incidences.
+ * So we don't show the same warnings for each incidence.
+ */
+ virtual void beginAddingIncidences();
+
+ /**
+ * Called when we finish adding a batch of incidences.
+ * @see beginAddingIncidences()
+ */
+ virtual void endAddingIncidences();
+
public slots:
/**
(De-)activate a subresource.
@@ -402,6 +450,8 @@ class LIBKCAL_EXPORT ResourceCalendar : public KRES::Resource
bool mReceivedLoadError;
bool mReceivedSaveError;
+ ErrorFormat *mException;
+
class Private;
Private *d;
};
diff --git a/libkcal/resourcelocaldir.cpp b/libkcal/resourcelocaldir.cpp
index 1d5c9caa..f1e00f32 100644
--- a/libkcal/resourcelocaldir.cpp
+++ b/libkcal/resourcelocaldir.cpp
@@ -128,7 +128,7 @@ bool ResourceLocalDir::doLoad()
TQString dirName = mURL.path();
if ( !( KStandardDirs::exists( dirName ) || KStandardDirs::exists( dirName + "/") ) ) {
- kdDebug(5800) << "ResourceLocalDir::load(): Directory '" << dirName
+ kdDebug(5800) << "ResourceLocalDir::load(): Directory '" << dirName
<< "' doesn't exist yet. Creating it..." << endl;
// Create the directory. Use 0775 to allow group-writable if the umask
// allows it (permissions will be 0775 & ~umask). This is desired e.g. for
@@ -139,7 +139,7 @@ bool ResourceLocalDir::doLoad()
// The directory exists. Now try to open (the files in) it.
kdDebug(5800) << "ResourceLocalDir::load(): '" << dirName << "'" << endl;
TQFileInfo dirInfo( dirName );
- if ( !( dirInfo.isDir() && dirInfo.isReadable() &&
+ if ( !( dirInfo.isDir() && dirInfo.isReadable() &&
( dirInfo.isWritable() || readOnly() ) ) )
return false;
@@ -193,6 +193,11 @@ bool ResourceLocalDir::doSave()
bool ResourceLocalDir::doSave( Incidence *incidence )
{
+ if ( mDeletedIncidences.contains( incidence ) ) {
+ mDeletedIncidences.remove( incidence );
+ return true;
+ }
+
mDirWatch.stopScan(); // do prohibit the dirty() signal and a following reload()
TQString fileName = mURL.path() + "/" + incidence->uid();
@@ -231,28 +236,46 @@ void ResourceLocalDir::reload( const TQString &file )
bool ResourceLocalDir::deleteEvent(Event *event)
{
kdDebug(5800) << "ResourceLocalDir::deleteEvent" << endl;
- if ( deleteIncidenceFile(event) )
- return( mCalendar.deleteEvent( event ) );
- else
- return( false );
+ if ( deleteIncidenceFile(event) ) {
+ if ( mCalendar.deleteEvent( event ) ) {
+ mDeletedIncidences.append( event );
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
}
bool ResourceLocalDir::deleteTodo(Todo *todo)
{
- if ( deleteIncidenceFile(todo) )
- return( mCalendar.deleteTodo( todo ) );
- else
- return( false );
+ if ( deleteIncidenceFile(todo) ) {
+ if ( mCalendar.deleteTodo( todo ) ) {
+ mDeletedIncidences.append( todo );
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
}
bool ResourceLocalDir::deleteJournal( Journal *journal )
{
- if ( deleteIncidenceFile( journal ) )
- return( mCalendar.deleteJournal( journal ) );
- else
- return( false );
+ if ( deleteIncidenceFile( journal ) ) {
+ if ( mCalendar.deleteJournal( journal ) ) {
+ mDeletedIncidences.append( journal );
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
}
diff --git a/libkcal/resourcelocaldir.h b/libkcal/resourcelocaldir.h
index e6c5526a..e1316f21 100644
--- a/libkcal/resourcelocaldir.h
+++ b/libkcal/resourcelocaldir.h
@@ -94,6 +94,7 @@ class LIBKCAL_EXPORT ResourceLocalDir : public ResourceCached
KABC::Lock *mLock;
+ TQPtrList<Incidence>mDeletedIncidences;
class Private;
Private *d;
};
diff --git a/libkcal/resourcelocaldirconfig.cpp b/libkcal/resourcelocaldirconfig.cpp
index 2c8d8983..df2412b3 100644
--- a/libkcal/resourcelocaldirconfig.cpp
+++ b/libkcal/resourcelocaldirconfig.cpp
@@ -26,6 +26,7 @@
#include <klocale.h>
#include <kdebug.h>
+#include <kmessagebox.h>
#include <kstandarddirs.h>
#include "vcaldrag.h"
@@ -41,7 +42,7 @@ using namespace KCal;
ResourceLocalDirConfig::ResourceLocalDirConfig( TQWidget* parent, const char* name )
: KRES::ConfigWidget( parent, name )
{
- resize( 245, 115 );
+ resize( 245, 115 );
TQGridLayout *mainLayout = new TQGridLayout( this, 2, 2 );
TQLabel *label = new TQLabel( i18n( "Location:" ), this );
@@ -65,6 +66,14 @@ void ResourceLocalDirConfig::saveSettings( KRES::Resource *resource )
ResourceLocalDir* res = static_cast<ResourceLocalDir*>( resource );
if (res) {
res->mURL = mURL->url();
+ if ( mURL->url().isEmpty() && !resource->readOnly() ) {
+ KMessageBox::information(
+ this,
+ i18n( "No location specified. The calendar will be read-only." ),
+ TQString(),
+ "ResourceLocalDirUrl" );
+ resource->setReadOnly( true );
+ }
} else
kdDebug(5700) << "ERROR: ResourceLocalDirConfig::saveSettings(): no ResourceLocalDir, cast failed" << endl;
}
diff --git a/libkcal/scheduler.cpp b/libkcal/scheduler.cpp
index 802c977f..6e3cf385 100644
--- a/libkcal/scheduler.cpp
+++ b/libkcal/scheduler.cpp
@@ -25,12 +25,15 @@
#include <kmessagebox.h>
#include <kstandarddirs.h>
+#include "calhelper.h"
#include "event.h"
#include "todo.h"
#include "freebusy.h"
#include "icalformat.h"
#include "calendar.h"
+#include "calendarresources.h"
#include "freebusycache.h"
+#include "assignmentvisitor.h"
#include "scheduler.h"
@@ -94,7 +97,10 @@ FreeBusyCache *Scheduler::freeBusyCache() const
return d->mFreeBusyCache;
}
-bool Scheduler::acceptTransaction(IncidenceBase *incidence,Method method,ScheduleMessage::Status status)
+bool Scheduler::acceptTransaction( IncidenceBase *incidence,
+ Method method,
+ ScheduleMessage::Status status,
+ const TQString &attendee )
{
kdDebug(5800) << "Scheduler::acceptTransaction, method="
<< methodName( method ) << endl;
@@ -103,11 +109,11 @@ bool Scheduler::acceptTransaction(IncidenceBase *incidence,Method method,Schedul
case Publish:
return acceptPublish(incidence, status, method);
case Request:
- return acceptRequest(incidence, status);
+ return acceptRequest( incidence, status, attendee );
case Add:
return acceptAdd(incidence, status);
case Cancel:
- return acceptCancel(incidence, status);
+ return acceptCancel(incidence, status, attendee );
case Declinecounter:
return acceptDeclineCounter(incidence, status);
case Reply:
@@ -185,24 +191,28 @@ bool Scheduler::acceptPublish( IncidenceBase *newIncBase,
bool res = false;
kdDebug(5800) << "Scheduler::acceptPublish, status="
- << ScheduleMessage::statusName( status ) << endl;
+ << ScheduleMessage::statusName( status ) << endl;
Incidence *newInc = static_cast<Incidence *>( newIncBase );
Incidence *calInc = mCalendar->incidence( newIncBase->uid() );
switch ( status ) {
case ScheduleMessage::Unknown:
case ScheduleMessage::PublishNew:
case ScheduleMessage::PublishUpdate:
- res = true;
- if ( calInc ) {
+ if ( calInc && newInc ) {
if ( (newInc->revision() > calInc->revision()) ||
(newInc->revision() == calInc->revision() &&
newInc->lastModified() > calInc->lastModified() ) ) {
- mCalendar->deleteIncidence( calInc );
- } else
- res = false;
+ AssignmentVisitor visitor;
+ const TQString oldUid = calInc->uid();
+ if ( !visitor.assign( calInc, newInc ) ) {
+ kdError(5800) << "assigning different incidence types" << endl;
+ } else {
+ calInc->setUid( oldUid );
+ calInc->setSchedulingID( newInc->uid() );
+ res = true;
+ }
+ }
}
- if ( res )
- mCalendar->addIncidence( newInc );
break;
case ScheduleMessage::Obsolete:
res = true;
@@ -214,36 +224,169 @@ bool Scheduler::acceptPublish( IncidenceBase *newIncBase,
return res;
}
-bool Scheduler::acceptRequest(IncidenceBase *newIncBase, ScheduleMessage::Status /* status */)
+bool Scheduler::acceptRequest( IncidenceBase *incidence,
+ ScheduleMessage::Status status,
+ const TQString &attendee )
{
- if (newIncBase->type()=="FreeBusy") {
+ Incidence *inc = static_cast<Incidence *>(incidence);
+ if ( !inc )
+ return false;
+ if (inc->type()=="FreeBusy") {
// reply to this request is handled in korganizer's incomingdialog
return true;
}
- Incidence *newInc = dynamic_cast<Incidence *>( newIncBase );
- if ( newInc ) {
- bool res = true;
- Incidence *exInc = mCalendar->incidenceFromSchedulingID( newIncBase->uid() );
- if ( exInc ) {
- res = false;
- if ( (newInc->revision() > exInc->revision()) ||
- (newInc->revision() == exInc->revision() &&
- newInc->lastModified()>exInc->lastModified()) ) {
- mCalendar->deleteIncidence( exInc );
- res = true;
+
+ const Incidence::List existingIncidences = mCalendar->incidencesFromSchedulingID( inc->uid() );
+ kdDebug(5800) << "Scheduler::acceptRequest status=" << ScheduleMessage::statusName( status ) << ": found " << existingIncidences.count() << " incidences with schedulingID " << inc->schedulingID() << endl;
+ Incidence::List::ConstIterator incit = existingIncidences.begin();
+ for ( ; incit != existingIncidences.end() ; ++incit ) {
+ Incidence* const i = *incit;
+ kdDebug(5800) << "Considering this found event ("
+ << ( i->isReadOnly() ? "readonly" : "readwrite" )
+ << ") :" << mFormat->toString( i ) << endl;
+ // If it's readonly, we can't possible update it.
+ if ( i->isReadOnly() )
+ continue;
+ if ( i->revision() <= inc->revision() ) {
+ // The new incidence might be an update for the found one
+ bool isUpdate = true;
+ // Code for new invitations:
+ // If you think we could check the value of "status" to be RequestNew: we can't.
+ // It comes from a similar check inside libical, where the event is compared to
+ // other events in the calendar. But if we have another version of the event around
+ // (e.g. shared folder for a group), the status could be RequestNew, Obsolete or Updated.
+ kdDebug(5800) << "looking in " << i->uid() << "'s attendees" << endl;
+ // This is supposed to be a new request, not an update - however we want to update
+ // the existing one to handle the "clicking more than once on the invitation" case.
+ // So check the attendee status of the attendee.
+ const KCal::Attendee::List attendees = i->attendees();
+ KCal::Attendee::List::ConstIterator ait;
+ for ( ait = attendees.begin(); ait != attendees.end(); ++ait ) {
+ if( (*ait)->email() == attendee && (*ait)->status() == Attendee::NeedsAction ) {
+ // This incidence wasn't created by me - it's probably in a shared folder
+ // and meant for someone else, ignore it.
+ kdDebug(5800) << "ignoring " << i->uid() << " since I'm still NeedsAction there" << endl;
+ isUpdate = false;
+ break;
+ }
+ }
+ if ( isUpdate ) {
+ if ( i->revision() == inc->revision() &&
+ i->lastModified() > inc->lastModified() ) {
+ // This isn't an update - the found incidence was modified more recently
+ kdDebug(5800) << "This isn't an update - the found incidence was modified more recently" << endl;
+ deleteTransaction(incidence);
+ return false;
+ }
+ kdDebug(5800) << "replacing existing incidence " << i->uid() << endl;
+ bool res = true;
+ AssignmentVisitor visitor;
+ const TQString oldUid = i->uid();
+ if ( !visitor.assign( i, inc ) ) {
+ kdError(5800) << "assigning different incidence types" << endl;
+ res = false;
+ } else {
+ i->setUid( oldUid );
+ i->setSchedulingID( inc->uid() );
+ }
+ deleteTransaction( incidence );
+ return res;
}
+ } else {
+ // This isn't an update - the found incidence has a bigger revision number
+ kdDebug(5800) << "This isn't an update - the found incidence has a bigger revision number" << endl;
+ deleteTransaction(incidence);
+ return false;
+ }
+ }
+
+ // Move the uid to be the schedulingID and make a unique UID
+ inc->setSchedulingID( inc->uid() );
+ inc->setUid( CalFormat::createUniqueId() );
+ // notify the user in case this is an update and we didn't find the to-be-updated incidence
+ if ( existingIncidences.count() == 0 && inc->revision() > 0 ) {
+ KMessageBox::information(
+ 0,
+ i18n( "<qt>"
+ "You accepted an invitation update, but an earlier version of the "
+ "item could not be found in your calendar.<p>"
+ "This may have occurred because:<ul>"
+ "<li>the organizer did not include you in the original invitation</li>"
+ "<li>you did not accept the original invitation yet</li>"
+ "<li>you deleted the original invitation from your calendar</li>"
+ "<li>you no longer have access to the calendar containing the invitation</li>"
+ "</ul>"
+ "This is not a problem, but we thought you should know.</qt>" ),
+ i18n( "Cannot find invitation to be updated" ), "AcceptCantFindIncidence" );
+ }
+ kdDebug(5800) << "Storing new incidence with scheduling uid=" << inc->schedulingID()
+ << " and uid=" << inc->uid() << endl;
+
+ CalendarResources *stdcal = dynamic_cast<CalendarResources *>( mCalendar );
+ if( stdcal && !stdcal->hasCalendarResources() ) {
+ KMessageBox::sorry(
+ 0,
+ i18n( "No calendars found, unable to save the invitation." ) );
+ return false;
+ }
+
+ // FIXME: This is a nasty hack, since we need to set a parent for the
+ // resource selection dialog. However, we don't have any UI methods
+ // in the calendar, only in the CalendarResources::DestinationPolicy
+ // So we need to type-cast it and extract it from the CalendarResources
+ TQWidget *tmpparent = 0;
+ if ( stdcal ) {
+ tmpparent = stdcal->dialogParentWidget();
+ stdcal->setDialogParentWidget( 0 );
+ }
+
+TryAgain:
+ bool success = false;
+ if ( stdcal ) {
+ success = stdcal->addIncidence( inc );
+ } else {
+ success = mCalendar->addIncidence( inc );
+ }
+
+ if ( !success ) {
+ ErrorFormat *e = stdcal ? stdcal->exception() : 0;
+
+ if ( e && e->errorCode() == KCal::ErrorFormat::UserCancel &&
+ KMessageBox::warningYesNo(
+ 0,
+ i18n( "You canceled the save operation. Therefore, the appointment will not be "
+ "stored in your calendar even though you accepted the invitation. "
+ "Are you certain you want to discard this invitation? " ),
+ i18n( "Discard this invitation?" ),
+ i18n( "Discard" ), i18n( "Go Back to Folder Selection" ) ) == KMessageBox::Yes ) {
+ KMessageBox::information(
+ 0,
+ i18n( "The invitation \"%1\" was not saved to your calendar "
+ "but you are still listed as an attendee for that appointment.\n"
+ "If you mistakenly accepted the invitation or do not plan to attend, please notify "
+ "the organizer %2 and ask them to remove you from the attendee list.").
+ arg( inc->summary(), inc->organizer().fullName() ) );
+ deleteTransaction( incidence );
+ return true;
+ } else {
+ goto TryAgain;
}
- if ( res ) {
- // Move the uid to be the schedulingID and make a unique UID
- newInc->setSchedulingID( newInc->uid() );
- newInc->setUid( CalFormat::createUniqueId() );
- mCalendar->addIncidence(newInc);
+ // We can have a failure if the user pressed [cancel] in the resource
+ // selectdialog, so check the exception.
+ if ( !e ||
+ ( e && ( e->errorCode() != KCal::ErrorFormat::UserCancel &&
+ e->errorCode() != KCal::ErrorFormat::NoWritableFound ) ) ) {
+ TQString errMessage = i18n( "Unable to save %1 \"%2\"." ).
+ arg( i18n( inc->type() ) ).
+ arg( inc->summary() );
+ KMessageBox::sorry( 0, errMessage );
}
- deleteTransaction( newIncBase );
- return res;
+ return false;
}
- return false;
+
+ deleteTransaction( incidence );
+ return true;
}
bool Scheduler::acceptAdd(IncidenceBase *incidence,ScheduleMessage::Status /* status */)
@@ -252,22 +395,131 @@ bool Scheduler::acceptAdd(IncidenceBase *incidence,ScheduleMessage::Status /* st
return false;
}
-bool Scheduler::acceptCancel(IncidenceBase *incidence,ScheduleMessage::Status /* status */)
+bool Scheduler::acceptCancel( IncidenceBase *incidence,
+ ScheduleMessage::Status status,
+ const TQString &attendee )
{
+ Incidence *inc = static_cast<Incidence *>( incidence );
+ if ( !inc ) {
+ return false;
+ }
+
+ if ( inc->type() == "FreeBusy" ) {
+ // reply to this request is handled in korganizer's incomingdialog
+ return true;
+ }
+
+ const Incidence::List existingIncidences = mCalendar->incidencesFromSchedulingID( inc->uid() );
+ kdDebug(5800) << "Scheduler::acceptCancel="
+ << ScheduleMessage::statusName( status )
+ << ": found " << existingIncidences.count()
+ << " incidences with schedulingID " << inc->schedulingID()
+ << endl;
+
+ // Remove existing incidences that aren't stored in my calendar as we
+ // will never attempt to remove those -- even if we have write-access.
+ Incidence::List myExistingIncidences;
+ Incidence::List::ConstIterator incit = existingIncidences.begin();
+ for ( ; incit != existingIncidences.end() ; ++incit ) {
+ Incidence *i = *incit;
+ if ( CalHelper::isMyCalendarIncidence( mCalendar, i ) ) {
+ myExistingIncidences.append( i );
+ }
+ }
+
bool ret = false;
+ incit = myExistingIncidences.begin();
+ for ( ; incit != myExistingIncidences.end() ; ++incit ) {
+ Incidence *i = *incit;
+ kdDebug(5800) << "Considering this found event ("
+ << ( i->isReadOnly() ? "readonly" : "readwrite" )
+ << ") :" << mFormat->toString( i ) << endl;
+
+ // If it's readonly, we can't possible remove it.
+ if ( i->isReadOnly() ) {
+ continue;
+ }
+
+ // Code for new invitations:
+ // We cannot check the value of "status" to be RequestNew because
+ // "status" comes from a similar check inside libical, where the event
+ // is compared to other events in the calendar. But if we have another
+ // version of the event around (e.g. shared folder for a group), the
+ // status could be RequestNew, Obsolete or Updated.
+ kdDebug(5800) << "looking in " << i->uid() << "'s attendees" << endl;
+
+ // This is supposed to be a new request, not an update - however we want
+ // to update the existing one to handle the "clicking more than once
+ // on the invitation" case. So check the attendee status of the attendee.
+ bool isMine = true;
+ const KCal::Attendee::List attendees = i->attendees();
+ KCal::Attendee::List::ConstIterator ait;
+ for ( ait = attendees.begin(); ait != attendees.end(); ++ait ) {
+ if ( (*ait)->email() == attendee &&
+ (*ait)->status() == Attendee::NeedsAction ) {
+ // This incidence wasn't created by me - it's probably in a shared
+ // folder and meant for someone else, ignore it.
+ kdDebug(5800) << "ignoring " << i->uid()
+ << " since I'm still NeedsAction there" << endl;
+ isMine = false;
+ break;
+ }
+ }
+
+ if ( isMine ) {
+ kdDebug(5800) << "removing existing incidence " << i->uid() << endl;
+ if ( i->type() == "Event" ) {
+ Event *event = mCalendar->event( i->uid() );
+ ret = ( event && mCalendar->deleteEvent( event ) );
+ } else if ( i->type() == "Todo" ) {
+ Todo *todo = mCalendar->todo( i->uid() );
+ ret = ( todo && mCalendar->deleteTodo( todo ) );
+ }
+ deleteTransaction( incidence );
+ return ret;
+ }
+ }
+
+ // in case we didn't find the to-be-removed incidence
+ if ( myExistingIncidences.count() > 0 && inc->revision() > 0 ) {
+ KMessageBox::information(
+ 0,
+ i18n( "The event or task could not be removed from your calendar. "
+ "Maybe it has already been deleted or is not owned by you. "
+ "Or it might belong to a read-only or disabled calendar." ) );
+ }
+ deleteTransaction( incidence );
+ return ret;
+}
+
+bool Scheduler::acceptCancel(IncidenceBase *incidence,ScheduleMessage::Status /* status */)
+{
const IncidenceBase *toDelete = mCalendar->incidenceFromSchedulingID( incidence->uid() );
+
+ bool ret = true;
if ( toDelete ) {
- Event *even = mCalendar->event(toDelete->uid());
- if (even) {
- mCalendar->deleteEvent(even);
- ret = true;
- } else {
- Todo *todo = mCalendar->todo(toDelete->uid());
- if (todo) {
- mCalendar->deleteTodo(todo);
- ret = true;
- }
+ if ( toDelete->type() == "Event" ) {
+ Event *event = mCalendar->event( toDelete->uid() );
+ ret = ( event && mCalendar->deleteEvent( event ) );
+ } else if ( toDelete->type() == "Todo" ) {
+ Todo *todo = mCalendar->todo( toDelete->uid() );
+ ret = ( todo && mCalendar->deleteTodo( todo ) );
}
+ } else {
+ // only complain if we failed to determine the toDelete incidence
+ // on non-initial request.
+ Incidence *inc = static_cast<Incidence *>( incidence );
+ if ( inc->revision() > 0 ) {
+ ret = false;
+ }
+ }
+
+ if ( !ret ) {
+ KMessageBox::information(
+ 0,
+ i18n( "The event or task to be canceled could not be removed from your calendar. "
+ "Maybe it has already been deleted or is not owned by you. "
+ "Or it might belong to a read-only or disabled calendar." ) );
}
deleteTransaction(incidence);
return ret;
@@ -370,13 +622,25 @@ bool Scheduler::acceptReply(IncidenceBase *incidence,ScheduleMessage::Status /*
// send update about new participants
if ( attendeeAdded ) {
+ bool sendMail = false;
+ if ( ev || to ) {
+ if ( KMessageBox::questionYesNo( 0, i18n( "An attendee was added to the incidence. "
+ "Do you want to email the attendees an update message?" ),
+ i18n( "Attendee Added" ), i18n( "Send Messages" ),
+ i18n( "Do Not Send" ) ) == KMessageBox::Yes ) {
+ sendMail = true;
+ }
+ }
+
if ( ev ) {
ev->setRevision( ev->revision() + 1 );
- performTransaction( ev, Scheduler::Request );
+ if ( sendMail )
+ performTransaction( ev, Scheduler::Request );
}
if ( to ) {
- to->setRevision( ev->revision() + 1 );
- performTransaction( to, Scheduler::Request );
+ to->setRevision( to->revision() + 1 );
+ if ( sendMail )
+ performTransaction( to, Scheduler::Request );
}
}
diff --git a/libkcal/scheduler.h b/libkcal/scheduler.h
index 17e19ec4..bf22c9c8 100644
--- a/libkcal/scheduler.h
+++ b/libkcal/scheduler.h
@@ -49,14 +49,14 @@ class ScheduleMessage
*/
enum Status { PublishNew, PublishUpdate, 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.
*/
@@ -102,13 +102,13 @@ class LIBKCAL_EXPORT Scheduler
*/
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
*/
@@ -121,8 +121,8 @@ class LIBKCAL_EXPORT Scheduler
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
+ 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,
@@ -136,10 +136,12 @@ class LIBKCAL_EXPORT Scheduler
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.
+ taken for this incidence. The attendee is the email address of the person
+ on who's behalf this transaction is to be performed.
*/
bool acceptTransaction( IncidenceBase *, Method method,
- ScheduleMessage::Status status );
+ ScheduleMessage::Status status,
+ const TQString& attendee = TQString::null );
/**
Return a machine-readable name for a iTIP method.
@@ -151,7 +153,7 @@ class LIBKCAL_EXPORT Scheduler
static TQString translatedMethodName( Method );
virtual bool deleteTransaction( IncidenceBase *incidence );
-
+
/**
Returns the directory where the free-busy information is stored.
*/
@@ -169,9 +171,12 @@ class LIBKCAL_EXPORT Scheduler
protected:
bool acceptPublish( IncidenceBase *, ScheduleMessage::Status status,
Method method );
- bool acceptRequest( IncidenceBase *, ScheduleMessage::Status status );
+ bool acceptRequest( IncidenceBase *, ScheduleMessage::Status status,
+ const TQString & attendee );
bool acceptAdd( IncidenceBase *, ScheduleMessage::Status status );
- bool acceptCancel( IncidenceBase *, ScheduleMessage::Status status );
+ KDE_DEPRECATED bool acceptCancel( IncidenceBase *, ScheduleMessage::Status status );
+ bool acceptCancel( IncidenceBase *, ScheduleMessage::Status status,
+ const TQString & attendee );
bool acceptDeclineCounter( IncidenceBase *,
ScheduleMessage::Status status );
bool acceptReply( IncidenceBase *, ScheduleMessage::Status status,
diff --git a/libkcal/tests/Makefile.am b/libkcal/tests/Makefile.am
index 6e7096a1..1a09143e 100644
--- a/libkcal/tests/Makefile.am
+++ b/libkcal/tests/Makefile.am
@@ -18,7 +18,8 @@ check_PROGRAMS = testtostring \
testrecurson \
testrecurrencetype \
testvcalexport \
- testfb
+ testfb \
+ testcalselectdialog
METASOURCES = AUTO
@@ -76,6 +77,10 @@ testfb_SOURCES = testfb.cpp
testfb_LDFLAGS = $(all_libraries) $(KDE_RPATH)
testfb_LDADD = ../libkcal.la
+testcalselectdialog_SOURCES = testcalselectdialog.cpp
+testcalselectdialog_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+testcalselectdialog_LDADD = ../libkcal.la
+
TESTFILES = test1.ics test2.ics test3.ics test4.ics test5.ics test_Mozilla.ics
check-local: readandwrite testrecurrence testrecurprevious testrecurson testvcalexport
diff --git a/libkcal/tests/data/RecurrenceRule/ConnectDaily/ConnectDaily11.ics.recurson.ref b/libkcal/tests/data/RecurrenceRule/ConnectDaily/ConnectDaily11.ics.recurson.ref
index 1698d7d2..783c984a 100644
--- a/libkcal/tests/data/RecurrenceRule/ConnectDaily/ConnectDaily11.ics.recurson.ref
+++ b/libkcal/tests/data/RecurrenceRule/ConnectDaily/ConnectDaily11.ics.recurson.ref
@@ -1,4 +1,3 @@
-2005-04-01
2005-05-03
2005-05-05
2005-06-07
diff --git a/libkcal/tests/data/RecurrenceRule/KAlarm_3.4/KAlarm_TestCase06.ics.recurson.ref b/libkcal/tests/data/RecurrenceRule/KAlarm_3.4/KAlarm_TestCase06.ics.recurson.ref
index 2562eae7..8b137891 100644
--- a/libkcal/tests/data/RecurrenceRule/KAlarm_3.4/KAlarm_TestCase06.ics.recurson.ref
+++ b/libkcal/tests/data/RecurrenceRule/KAlarm_3.4/KAlarm_TestCase06.ics.recurson.ref
@@ -1,2 +1 @@
-2005-05-31
diff --git a/libkcal/tests/data/RecurrenceRule/LibICal/LibICal_TestCase02.ics.recurson.ref b/libkcal/tests/data/RecurrenceRule/LibICal/LibICal_TestCase02.ics.recurson.ref
index eb9f8158..a17df247 100644
--- a/libkcal/tests/data/RecurrenceRule/LibICal/LibICal_TestCase02.ics.recurson.ref
+++ b/libkcal/tests/data/RecurrenceRule/LibICal/LibICal_TestCase02.ics.recurson.ref
@@ -1,4 +1,3 @@
-2002-04-02
2002-04-04
2002-04-11
2002-04-18
diff --git a/libkcal/tests/data/RecurrenceRule/LibICal/LibICal_TestCase24.ics.recurson.ref b/libkcal/tests/data/RecurrenceRule/LibICal/LibICal_TestCase24.ics.recurson.ref
index 7abf1e5a..13edfa43 100644
--- a/libkcal/tests/data/RecurrenceRule/LibICal/LibICal_TestCase24.ics.recurson.ref
+++ b/libkcal/tests/data/RecurrenceRule/LibICal/LibICal_TestCase24.ics.recurson.ref
@@ -1,4 +1,3 @@
-1997-09-02
1997-09-03
1997-09-05
1997-09-15
diff --git a/libkcal/tests/data/RecurrenceRule/LibICal/LibICal_TestCase42.ics.recurson.ref b/libkcal/tests/data/RecurrenceRule/LibICal/LibICal_TestCase42.ics.recurson.ref
index 887df0de..e1d367ad 100644
--- a/libkcal/tests/data/RecurrenceRule/LibICal/LibICal_TestCase42.ics.recurson.ref
+++ b/libkcal/tests/data/RecurrenceRule/LibICal/LibICal_TestCase42.ics.recurson.ref
@@ -1,4 +1,3 @@
-1997-09-02
1998-02-13
1998-03-13
1998-11-13
diff --git a/libkcal/tests/data/RecurrenceRule/RFC2445/RFC2445_RRULETestCase12.ics.recurson.ref b/libkcal/tests/data/RecurrenceRule/RFC2445/RFC2445_RRULETestCase12.ics.recurson.ref
index 7abf1e5a..24ad0551 100644
--- a/libkcal/tests/data/RecurrenceRule/RFC2445/RFC2445_RRULETestCase12.ics.recurson.ref
+++ b/libkcal/tests/data/RecurrenceRule/RFC2445/RFC2445_RRULETestCase12.ics.recurson.ref
@@ -1,4 +1,3 @@
-1997-09-02
1997-09-03
1997-09-05
1997-09-15
@@ -23,4 +22,3 @@
1997-12-10
1997-12-12
1997-12-22
-
diff --git a/libkcal/tests/data/RecurrenceRule/UntilInUTC/Until_TestCase02.ics.recurson.ref b/libkcal/tests/data/RecurrenceRule/UntilInUTC/Until_TestCase02.ics.recurson.ref
index 2f3a7e16..e69de29b 100644
--- a/libkcal/tests/data/RecurrenceRule/UntilInUTC/Until_TestCase02.ics.recurson.ref
+++ b/libkcal/tests/data/RecurrenceRule/UntilInUTC/Until_TestCase02.ics.recurson.ref
@@ -1 +0,0 @@
-1997-12-15
diff --git a/libkcal/tests/data/RecurrenceRule/UntilInUTC/Until_TestCase04.ics.recurson.ref b/libkcal/tests/data/RecurrenceRule/UntilInUTC/Until_TestCase04.ics.recurson.ref
index 6fca0d2f..f57f89d0 100644
--- a/libkcal/tests/data/RecurrenceRule/UntilInUTC/Until_TestCase04.ics.recurson.ref
+++ b/libkcal/tests/data/RecurrenceRule/UntilInUTC/Until_TestCase04.ics.recurson.ref
@@ -1,2 +1 @@
1997-09-02
-
diff --git a/libkcal/tests/data/RecurrenceRule/unsorted/lastworkday.ics.recurson.ref b/libkcal/tests/data/RecurrenceRule/unsorted/lastworkday.ics.recurson.ref
index 05df6b37..c55b5583 100644
--- a/libkcal/tests/data/RecurrenceRule/unsorted/lastworkday.ics.recurson.ref
+++ b/libkcal/tests/data/RecurrenceRule/unsorted/lastworkday.ics.recurson.ref
@@ -1,4 +1,3 @@
-2005-05-12
2005-05-31
2005-06-30
2005-07-29
diff --git a/libkcal/tests/data/RecurrenceRule/unsorted/monthly.ics.recurson.ref b/libkcal/tests/data/RecurrenceRule/unsorted/monthly.ics.recurson.ref
index bbf1358c..4c2c61aa 100644
--- a/libkcal/tests/data/RecurrenceRule/unsorted/monthly.ics.recurson.ref
+++ b/libkcal/tests/data/RecurrenceRule/unsorted/monthly.ics.recurson.ref
@@ -1,4 +1,3 @@
-2005-05-12
2005-05-18
2005-05-24
2005-06-15
diff --git a/libkcal/tests/data/RecurrenceRule/unsorted/rdate.ics.recurson.ref b/libkcal/tests/data/RecurrenceRule/unsorted/rdate.ics.recurson.ref
index 4082c9c2..70712a5e 100644
--- a/libkcal/tests/data/RecurrenceRule/unsorted/rdate.ics.recurson.ref
+++ b/libkcal/tests/data/RecurrenceRule/unsorted/rdate.ics.recurson.ref
@@ -1,4 +1,3 @@
-2005-05-12
2005-05-14
2005-05-16
2005-05-17
diff --git a/libkcal/tests/data/RecurrenceRule/unsorted/test1.ics.recurson.ref b/libkcal/tests/data/RecurrenceRule/unsorted/test1.ics.recurson.ref
index 71cfa3b6..5bd295fc 100644
--- a/libkcal/tests/data/RecurrenceRule/unsorted/test1.ics.recurson.ref
+++ b/libkcal/tests/data/RecurrenceRule/unsorted/test1.ics.recurson.ref
@@ -1,4 +1,3 @@
-2005-05-12
2007-01-07
2007-01-14
2007-01-21
diff --git a/libkcal/tests/data/RecurrenceRule/unsorted/weekly.ics.recurson.ref b/libkcal/tests/data/RecurrenceRule/unsorted/weekly.ics.recurson.ref
index 59d81cdc..1b1e0191 100644
--- a/libkcal/tests/data/RecurrenceRule/unsorted/weekly.ics.recurson.ref
+++ b/libkcal/tests/data/RecurrenceRule/unsorted/weekly.ics.recurson.ref
@@ -1,4 +1,3 @@
-2005-05-12
2005-05-23
2005-05-25
2005-06-06
@@ -293,3 +292,210 @@
2010-12-15
2010-12-27
2010-12-29
+2011-01-10
+2011-01-12
+2011-01-24
+2011-01-26
+2011-02-07
+2011-02-09
+2011-02-21
+2011-02-23
+2011-03-07
+2011-03-09
+2011-03-21
+2011-03-23
+2011-04-04
+2011-04-06
+2011-04-18
+2011-04-20
+2011-05-02
+2011-05-04
+2011-05-16
+2011-05-18
+2011-05-30
+2011-06-01
+2011-06-13
+2011-06-15
+2011-06-27
+2011-06-29
+2011-07-11
+2011-07-13
+2011-07-25
+2011-07-27
+2011-08-08
+2011-08-10
+2011-08-22
+2011-08-24
+2011-09-05
+2011-09-07
+2011-09-19
+2011-09-21
+2011-10-03
+2011-10-05
+2011-10-17
+2011-10-19
+2011-10-31
+2011-11-02
+2011-11-14
+2011-11-16
+2011-11-28
+2011-11-30
+2011-12-12
+2011-12-14
+2011-12-26
+2011-12-28
+2012-01-09
+2012-01-11
+2012-01-23
+2012-01-25
+2012-02-06
+2012-02-08
+2012-02-20
+2012-02-22
+2012-03-05
+2012-03-07
+2012-03-19
+2012-03-21
+2012-04-02
+2012-04-04
+2012-04-16
+2012-04-18
+2012-04-30
+2012-05-02
+2012-05-14
+2012-05-16
+2012-05-28
+2012-05-30
+2012-06-11
+2012-06-13
+2012-06-25
+2012-06-27
+2012-07-09
+2012-07-11
+2012-07-23
+2012-07-25
+2012-08-06
+2012-08-08
+2012-08-20
+2012-08-22
+2012-09-03
+2012-09-05
+2012-09-17
+2012-09-19
+2012-10-01
+2012-10-03
+2012-10-15
+2012-10-17
+2012-10-29
+2012-10-31
+2012-11-12
+2012-11-14
+2012-11-26
+2012-11-28
+2012-12-10
+2012-12-12
+2012-12-24
+2012-12-26
+2013-01-07
+2013-01-09
+2013-01-21
+2013-01-23
+2013-02-04
+2013-02-06
+2013-02-18
+2013-02-20
+2013-03-04
+2013-03-06
+2013-03-18
+2013-03-20
+2013-04-01
+2013-04-03
+2013-04-15
+2013-04-17
+2013-04-29
+2013-05-01
+2013-05-13
+2013-05-15
+2013-05-27
+2013-05-29
+2013-06-10
+2013-06-12
+2013-06-24
+2013-06-26
+2013-07-08
+2013-07-10
+2013-07-22
+2013-07-24
+2013-08-05
+2013-08-07
+2013-08-19
+2013-08-21
+2013-09-02
+2013-09-04
+2013-09-16
+2013-09-18
+2013-09-30
+2013-10-02
+2013-10-14
+2013-10-16
+2013-10-28
+2013-10-30
+2013-11-11
+2013-11-13
+2013-11-25
+2013-11-27
+2013-12-09
+2013-12-11
+2013-12-23
+2013-12-25
+2014-01-06
+2014-01-08
+2014-01-20
+2014-01-22
+2014-02-03
+2014-02-05
+2014-02-17
+2014-02-19
+2014-03-03
+2014-03-05
+2014-03-17
+2014-03-19
+2014-03-31
+2014-04-02
+2014-04-14
+2014-04-16
+2014-04-28
+2014-04-30
+2014-05-12
+2014-05-14
+2014-05-26
+2014-05-28
+2014-06-09
+2014-06-11
+2014-06-23
+2014-06-25
+2014-07-07
+2014-07-09
+2014-07-21
+2014-07-23
+2014-08-04
+2014-08-06
+2014-08-18
+2014-08-20
+2014-09-01
+2014-09-03
+2014-09-15
+2014-09-17
+2014-09-29
+2014-10-01
+2014-10-13
+2014-10-15
+2014-10-27
+2014-10-29
+2014-11-10
+2014-11-12
+2014-11-24
+2014-11-26
+2014-12-08
+2014-12-10
+2014-12-22
diff --git a/libkcal/tests/runtestcase.pl b/libkcal/tests/runtestcase.pl
index 52e6ead1..4cad27cc 100755
--- a/libkcal/tests/runtestcase.pl
+++ b/libkcal/tests/runtestcase.pl
@@ -78,7 +78,7 @@ sub checkfile()
exit 1;
}
while( <REF> ) {
- push @ref, $_;
+ push @ref, $_ if($_ !~ m/^\s*$/); #skip blank lines in the ref
}
close REF;
@@ -92,10 +92,13 @@ sub checkfile()
$line = 0;
my $errorlines = 0;
while( <READ> ) {
+ next if ($_ =~ m/^\s*$/); #skip blank lines in the output
$out = $_;
$ref = @ref[$i++];
$line++;
+ $out =~ s/\s*$//; #remove trailing whitespace
+ $ref =~ s/\s*$//; #remove trailing whitespace
# DTSTAMP, LAST-MODIFIED and CREATED might be different to the reference...
if ( $out =~ /^DTSTAMP:[0-9ZT]+\r?$/ && $ref =~ /^DTSTAMP:[0-9ZT]+\r?$/ ) {
next;
@@ -122,7 +125,6 @@ sub checkfile()
print " <Remaining error suppressed>\n";
}
}
-
}
close READ;
@@ -150,7 +152,7 @@ sub checkfile()
} else {
print "\n FAILED: $error errors found.\n";
if ( $error > 5 ) {
- system( "diff -u $file.$id.ref $outfile" );
+ system( "diff -u $file.$id.ref $outfile" );
}
system( "touch FAILED" );
exit 1;
diff --git a/libkcal/tests/testcalselectdialog.cpp b/libkcal/tests/testcalselectdialog.cpp
new file mode 100644
index 00000000..5c513045
--- /dev/null
+++ b/libkcal/tests/testcalselectdialog.cpp
@@ -0,0 +1,45 @@
+/*
+ Copyright (c) 2010 Klar�lvdalens Datakonsult AB, a KDAB Group company <info@kdab.net>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "calselectdialog.h"
+using namespace KCal;
+
+#include <kapplication.h>
+#include <kcmdlineargs.h>
+#include <klocale.h>
+#include <kdebug.h>
+
+int main( int argc, char **argv )
+{
+ KCmdLineArgs::init( argc, argv, "testcalselectdialog", 0,
+ "KCalSelectDialogTest", "1.0",
+ "kcalselectedialog test app" );
+ KApplication app;
+ TQStringList cals;
+ cals << "standard" << "shared" << "mine" << "yours";
+ TQString cal = CalSelectDialog::getItem( i18n( "Calendar Selection" ),
+ i18n( "Please select a calendar" ),
+ cals );
+
+ if ( !cal.isEmpty() ) {
+ kdDebug() << "Selected calendar " << cal << endl;
+ } else {
+ kdDebug() << "nothing selected. user cancel" << endl;
+ }
+}
diff --git a/libkcal/todo.cpp b/libkcal/todo.cpp
index 9787ff2a..b3fe65d9 100644
--- a/libkcal/todo.cpp
+++ b/libkcal/todo.cpp
@@ -116,10 +116,13 @@ void Todo::setDtDue(const TQDateTime &dtDue, bool first )
TQDateTime Todo::dtDue( bool first ) const
{
- if ( doesRecur() && !first && mDtRecurrence.isValid() )
+ if ( doesRecur() && !first && mDtRecurrence.isValid() ) {
return mDtRecurrence;
-
- return mDtDue;
+ } else if ( hasDueDate() ) {
+ return mDtDue;
+ } else {
+ return TQDateTime();
+ }
}
TQString Todo::dtDueTimeStr() const
@@ -173,10 +176,17 @@ void Todo::setHasStartDate(bool f)
TQDateTime Todo::dtStart( bool first ) const
{
- if ( doesRecur() && !first )
- return mDtRecurrence.addDays( dtDue( first ).daysTo( IncidenceBase::dtStart() ) );
- else
+ if ( doesRecur() && !first ) {
+ TQDateTime dt = mDtRecurrence.addDays( dtDue( true ).daysTo( IncidenceBase::dtStart() ) );
+
+ // We want the dtStart's time, not dtDue's
+ dt.setTime( IncidenceBase::dtStart().time() );
+ return dt;
+ } else if ( hasStartDate() ) {
return IncidenceBase::dtStart();
+ } else {
+ return TQDateTime();
+ }
}
void Todo::setDtStart( const TQDateTime &dtStart )
@@ -255,10 +265,14 @@ int Todo::percentComplete() const
return mPercentComplete;
}
-void Todo::setPercentComplete(int v)
+void Todo::setPercentComplete( int v )
{
mPercentComplete = v;
- if ( v != 100 ) mHasCompletedDate = false;
+ if ( v != 100 ) {
+ mHasCompletedDate = false;
+ mCompleted = TQDateTime();
+ }
+
updated();
}
@@ -292,7 +306,8 @@ bool Todo::recurTodo()
while ( !recursAt( nextDate ) || nextDate <= TQDateTime::currentDateTime() ) {
- if ( !nextDate.isValid() || nextDate > endDateTime ) {
+ if ( !nextDate.isValid() ||
+ ( nextDate > endDateTime && r->duration() != -1 ) ) {
return false;
}
diff --git a/libkcal/todo.h b/libkcal/todo.h
index 1a7b050d..74e1b39d 100644
--- a/libkcal/todo.h
+++ b/libkcal/todo.h
@@ -66,21 +66,26 @@ class LIBKCAL_EXPORT Todo : public Incidence
/**
Returns due time as string formatted according to the users locale
settings.
+ @deprecated use IncidenceFormatter::timeToString()
*/
- TQString dtDueTimeStr() const;
+ KDE_DEPRECATED TQString dtDueTimeStr() const;
+
/**
Returns due date as string formatted according to the users locale
settings.
@param shortfmt If set to true, use short date format, if set to false use
long format.
+ @deprecated use IncidenceFormatter::dateToString()
*/
- TQString dtDueDateStr( bool shortfmt = true ) const;
+ KDE_DEPRECATED TQString dtDueDateStr( bool shortfmt = true ) const;
+
/**
Returns due date and time as string formatted according to the users locale
settings.
+ @deprecated use IncidenceFormatter::dateTimeToString()
*/
- TQString dtDueStr() const;
+ KDE_DEPRECATED TQString dtDueStr() const;
/**
Returns true if the todo has a due date, otherwise return false.
@@ -218,7 +223,14 @@ class LIBKCAL_EXPORT Todo : public Incidence
private:
bool accept(Visitor &v) { return v.visit( this ); }
- /** Returns true if the todo got a new date, else false will be returned. */
+
+ /**
+ * If the todo recurs, mDtRecurrence is set to the next occurrence
+ * that's after today, mPercentComplete is set to 0 and true is returned.
+ *
+ * If the todo doesn't recur or if there aren't anymore occurrences
+ * it just returns false.
+ */
bool recurTodo();
TQDateTime mDtDue; // due date of todo