]> err.no Git - sope/blob - sope-ical/NGiCal/iCalEvent.m
39749a77134139269ba720fcb8e9ce39ad80c487
[sope] / sope-ical / NGiCal / iCalEvent.m
1 /*
2   Copyright (C) 2000-2005 SKYRIX Software AG
3
4   This file is part of SOPE.
5
6   SOPE is free software; you can redistribute it and/or modify it under
7   the terms of the GNU Lesser General Public License as published by the
8   Free Software Foundation; either version 2, or (at your option) any
9   later version.
10
11   SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
12   WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14   License for more details.
15
16   You should have received a copy of the GNU Lesser General Public
17   License along with SOPE; see the file COPYING.  If not, write to the
18   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19   02111-1307, USA.
20 */
21
22 #include "iCalEvent.h"
23 #include "iCalPerson.h"
24 #include "iCalEventChanges.h"
25 #include "iCalRecurrenceRule.h"
26 #include "iCalRenderer.h"
27 #include <NGExtensions/NGCalendarDateRange.h>
28 #include "common.h"
29
30 @interface NSString(DurationTimeInterval)
31 - (NSTimeInterval)durationAsTimeInterval;
32 @end
33
34 @implementation iCalEvent
35
36 + (int)version {
37   return [super version] + 0 /* v0 */;
38 }
39 + (void)initialize {
40   NSAssert2([super version] == 0,
41             @"invalid superclass (%@) version %i !",
42             NSStringFromClass([self superclass]), [super version]);
43 }
44
45 - (void)dealloc {
46   [self->endDate      release];
47   [self->duration     release];
48   [self->transparency release];
49   [super dealloc];
50 }
51
52 /* NSCopying */
53
54 - (id)copyWithZone:(NSZone *)_zone {
55   iCalEvent *new;
56   
57   new = [super copyWithZone:_zone];
58   
59   new->endDate      = [self->endDate      copyWithZone:_zone];
60   new->duration     = [self->duration     copyWithZone:_zone];
61   new->transparency = [self->transparency copyWithZone:_zone];
62
63   return new;
64 }
65
66 /* accessors */
67
68 - (void)setEndDate:(NSCalendarDate *)_date {
69   id tmp;
70   if (self->endDate == _date) return;
71   tmp = self->endDate;
72   self->endDate = [_date retain];
73   [tmp release];
74 }
75 - (NSCalendarDate *)endDate {
76   if ([self hasEndDate])
77     return self->endDate;
78   
79   if ([self hasDuration] && (self->startDate != nil)) {
80     return [[self startDate] dateByAddingYears:0 months:0 days:0
81                              hours:0 minutes:0 
82                              seconds:[self durationAsTimeInterval]];
83   }
84   return nil;
85 }
86 - (BOOL)hasEndDate {
87   return self->endDate ? YES : NO;
88 }
89
90 - (void)setDuration:(NSString *)_value {
91   ASSIGNCOPY(self->duration, _value);
92 }
93 - (NSString *)duration {
94   // eg: "DURATION:PT1H"
95   if ([self hasDuration])
96     return self->duration;
97   
98   // TODO: calculate
99   return nil;
100 }
101 - (BOOL)hasDuration {
102   return self->duration ? YES : NO;
103 }
104 - (NSTimeInterval)durationAsTimeInterval {
105   /*
106     eg: DURATION:PT1H
107     P      - "period"
108     P2H30M - "2 hours 30 minutes"
109
110      dur-value  = (["+"] / "-") "P" (dur-date / dur-time / dur-week)
111
112      dur-date   = dur-day [dur-time]
113      dur-time   = "T" (dur-hour / dur-minute / dur-second)
114      dur-week   = 1*DIGIT "W"
115      dur-hour   = 1*DIGIT "H" [dur-minute]
116      dur-minute = 1*DIGIT "M" [dur-second]
117      dur-second = 1*DIGIT "S"
118      dur-day    = 1*DIGIT "D"
119   */
120   
121   if (self->duration)
122     return [self->duration durationAsTimeInterval];
123   
124   if (self->endDate != nil && self->startDate != nil)
125     /* calculate duration using enddate */
126     return [[self endDate] timeIntervalSinceDate:[self startDate]];
127   
128   return 0.0;
129 }
130
131 - (void)setTransparency:(NSString *)_transparency {
132   ASSIGNCOPY(self->transparency, _transparency);
133 }
134 - (NSString *)transparency {
135   return self->transparency;
136 }
137
138 /* convenience */
139
140 - (BOOL)isOpaque {
141   NSString *s;
142   
143   s = [self transparency];
144   if (s && [[s uppercaseString] isEqualToString:@"TRANSPARENT"])
145     return NO;
146   return YES; /* default is OPAQUE, see RFC2445, Section 4.8.2.7 */
147 }
148
149 /* TODO: FIX THIS!
150    The problem is, that startDate/endDate are inappropriately modelled here.
151    We'd need to have a special iCalDate in order to fix all the mess.
152    For the time being, we chose allday to mean 00:00 - 23:59 in startDate's
153    timezone.
154 */
155 - (BOOL)isAllDay {
156   NSCalendarDate *ed;
157
158   if (![self hasEndDate])
159     return NO;
160   
161   ed = [[[self endDate] copy] autorelease];
162   [ed setTimeZone:[self->startDate timeZone]];
163   if (([self->startDate hourOfDay]    ==  0) &&
164       ([self->startDate minuteOfHour] ==  0) &&
165       ([ed hourOfDay]                 == 23) &&
166       ([ed minuteOfHour]              == 59))
167       return YES;
168   return NO;
169 }
170
171 - (BOOL)isWithinCalendarDateRange:(NGCalendarDateRange *)_range {
172   if (![self isRecurrent]) {
173     if (self->startDate && self->endDate) {
174       NGCalendarDateRange *r;
175       
176       r = [NGCalendarDateRange calendarDateRangeWithStartDate:self->startDate
177                                endDate:self->endDate];
178       return [_range containsDateRange:r];
179     }
180     else {
181       return [_range containsDate:self->startDate];
182     }
183   }
184   else {
185     NGCalendarDateRange *fir;
186
187     fir = [NGCalendarDateRange calendarDateRangeWithStartDate:self->startDate
188                                endDate:self->endDate];
189     
190     return [self isWithinCalendarDateRange:_range
191                  firstInstanceCalendarDateRange:fir];
192   }
193   return NO;
194 }
195
196 - (NSArray *)recurrenceRangesWithinCalendarDateRange:(NGCalendarDateRange *)_r {
197   NGCalendarDateRange *fir;
198   
199   if (![self isRecurrent])
200     return nil;
201   
202   fir = [NGCalendarDateRange calendarDateRangeWithStartDate:self->startDate
203                              endDate:self->endDate];
204   return [self recurrenceRangesWithinCalendarDateRange:_r
205                firstInstanceCalendarDateRange:fir];
206 }
207
208 - (NSCalendarDate *)lastPossibleRecurrenceStartDate {
209   NGCalendarDateRange *fir;
210
211   if (![self isRecurrent])
212     return nil;
213
214   fir = [NGCalendarDateRange calendarDateRangeWithStartDate:self->startDate
215                              endDate:self->endDate];
216   return [self lastPossibleRecurrenceStartDateUsingFirstInstanceCalendarDateRange:fir];
217 }
218
219 /* ical typing */
220
221 - (NSString *)entityName {
222   return @"vevent";
223 }
224
225 /* descriptions */
226
227 - (NSString *)description {
228   NSMutableString *ms;
229
230   ms = [NSMutableString stringWithCapacity:128];
231   [ms appendFormat:@"<0x%08X[%@]:", self, NSStringFromClass([self class])];
232
233   if (self->uid)       [ms appendFormat:@" uid=%@", self->uid];
234   if (self->startDate) [ms appendFormat:@" from=%@", self->startDate];
235   if (self->endDate)   [ms appendFormat:@" to=%@", self->endDate];
236   if (self->summary)   [ms appendFormat:@" summary=%@", self->summary];
237   
238   if (self->organizer)
239     [ms appendFormat:@" organizer=%@", self->organizer];
240   if (self->attendees)
241     [ms appendFormat:@" attendees=%@", self->attendees];
242   
243   if ([self hasAlarms])
244     [ms appendFormat:@" alarms=%@", self->alarms];
245   
246   [ms appendString:@">"];
247   return ms;
248 }
249
250 /* changes */
251
252 - (iCalEventChanges *)getChangesRelativeToEvent:(iCalEvent *)_event {
253   return [iCalEventChanges changesFromEvent:_event
254                            toEvent:self];
255 }
256
257 /* generating iCal content */
258
259 - (NSString *)vEventString {
260   return [[iCalRenderer sharedICalendarRenderer] vEventStringForEvent:self];
261 }
262
263 @end /* iCalEvent */
264
265 @implementation NSString(DurationTimeInterval)
266
267 - (NSTimeInterval)durationAsTimeInterval {
268   /*
269     eg: DURATION:PT1H
270     P      - "period"
271     P2H30M - "2 hours 30 minutes"
272
273      dur-value  = (["+"] / "-") "P" (dur-date / dur-time / dur-week)
274
275      dur-date   = dur-day [dur-time]
276      dur-time   = "T" (dur-hour / dur-minute / dur-second)
277      dur-week   = 1*DIGIT "W"
278      dur-hour   = 1*DIGIT "H" [dur-minute]
279      dur-minute = 1*DIGIT "M" [dur-second]
280      dur-second = 1*DIGIT "S"
281      dur-day    = 1*DIGIT "D"
282   */
283   unsigned       i, len;
284   NSTimeInterval ti;
285   BOOL           isTime;
286   int            val;
287     
288   if (![self hasPrefix:@"P"]) {
289     NSLog(@"Cannot parse iCal duration value: '%@'", self);
290     return 0.0;
291   }
292     
293   ti  = 0.0;
294   val = 0;
295   for (i = 1, len = [self length], isTime = NO; i < len; i++) {
296     unichar c;
297       
298     c = [self characterAtIndex:i];
299     if (c == 't' || c == 'T') {
300       isTime = YES;
301       val = 0;
302       continue;
303     }
304       
305     if (isdigit(c)) {
306       val = (val * 10) + (c - 48);
307       continue;
308     }
309       
310     switch (c) {
311     case 'W': /* week */
312       ti += (val * 7 * 24 * 60 * 60);
313       break;
314     case 'D': /* day  */
315       ti += (val * 24 * 60 * 60);
316       break;
317     case 'H': /* hour */
318       ti += (val * 60 * 60);
319       break;
320     case 'M': /* min  */
321       ti += (val * 60);
322       break;
323     case 'S': /* sec  */
324       ti += val;
325       break;
326     default:
327       [self logWithFormat:@"cannot process duration unit: '%c'", c];
328       break;
329     }
330     val = 0;
331   }
332   return ti;
333 }
334
335 @end /* NSString(DurationTimeInterval) */