]> err.no Git - sope/blob - sope-ical/NGiCal/iCalEvent.m
NSCopying support added
[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 - (void)dealloc {
37   [self->endDate      release];
38   [self->duration     release];
39   [self->transparency release];
40   [super dealloc];
41 }
42
43 /* NSCopying */
44
45 - (id)copyWithZone:(NSZone *)_zone {
46   iCalEvent *new;
47   
48   new = [super copyWithZone:_zone];
49   
50   ASSIGNCOPY(new->endDate,      self->endDate);
51   ASSIGNCOPY(new->duration,     self->duration);
52   ASSIGNCOPY(new->transparency, self->transparency);
53
54   return new;
55 }
56
57 /* accessors */
58
59 - (void)setEndDate:(NSCalendarDate *)_date {
60   id tmp;
61   if (self->endDate == _date) return;
62   tmp = self->endDate;
63   self->endDate = [_date retain];
64   [tmp release];
65 }
66 - (NSCalendarDate *)endDate {
67   if ([self hasEndDate])
68     return self->endDate;
69   
70   if ([self hasDuration] && (self->startDate != nil)) {
71     return [[self startDate] dateByAddingYears:0 months:0 days:0
72                              hours:0 minutes:0 
73                              seconds:[self durationAsTimeInterval]];
74   }
75   return nil;
76 }
77 - (BOOL)hasEndDate {
78   return self->endDate ? YES : NO;
79 }
80
81 - (void)setDuration:(NSString *)_value {
82   ASSIGNCOPY(self->duration, _value);
83 }
84 - (NSString *)duration {
85   // eg: "DURATION:PT1H"
86   if ([self hasDuration])
87     return self->duration;
88   
89   // TODO: calculate
90   return nil;
91 }
92 - (BOOL)hasDuration {
93   return self->duration ? YES : NO;
94 }
95 - (NSTimeInterval)durationAsTimeInterval {
96   /*
97     eg: DURATION:PT1H
98     P      - "period"
99     P2H30M - "2 hours 30 minutes"
100
101      dur-value  = (["+"] / "-") "P" (dur-date / dur-time / dur-week)
102
103      dur-date   = dur-day [dur-time]
104      dur-time   = "T" (dur-hour / dur-minute / dur-second)
105      dur-week   = 1*DIGIT "W"
106      dur-hour   = 1*DIGIT "H" [dur-minute]
107      dur-minute = 1*DIGIT "M" [dur-second]
108      dur-second = 1*DIGIT "S"
109      dur-day    = 1*DIGIT "D"
110   */
111   
112   if (self->duration)
113     return [self->duration durationAsTimeInterval];
114   
115   if (self->endDate != nil && self->startDate != nil)
116     /* calculate duration using enddate */
117     return [[self endDate] timeIntervalSinceDate:[self startDate]];
118   
119   return 0.0;
120 }
121
122 - (void)setTransparency:(NSString *)_transparency {
123   ASSIGNCOPY(self->transparency, _transparency);
124 }
125 - (NSString *)transparency {
126   return self->transparency;
127 }
128
129 /* convenience */
130
131 - (BOOL)isOpaque {
132   NSString *s;
133   
134   s = [self transparency];
135   if (s && [[s uppercaseString] isEqualToString:@"TRANSPARENT"])
136     return NO;
137   return YES; /* default is OPAQUE, see RFC2445, Section 4.8.2.7 */
138 }
139
140 /* TODO: FIX THIS!
141    The problem is, that startDate/endDate are inappropriately modelled here.
142    We'd need to have a special iCalDate in order to fix all the mess.
143    For the time being, we chose allday to mean 00:00 - 23:59 in startDate's
144    timezone.
145 */
146 - (BOOL)isAllDay {
147   NSCalendarDate *ed;
148
149   if (![self hasEndDate])
150     return NO;
151   
152   ed = [[[self endDate] copy] autorelease];
153   [ed setTimeZone:[self->startDate timeZone]];
154   if (([self->startDate hourOfDay]    ==  0) &&
155       ([self->startDate minuteOfHour] ==  0) &&
156       ([ed hourOfDay]                 == 23) &&
157       ([ed minuteOfHour]              == 59))
158       return YES;
159   return NO;
160 }
161
162 - (BOOL)isWithinCalendarDateRange:(NGCalendarDateRange *)_range {
163   if (![self isRecurrent]) {
164     if (self->startDate && self->endDate) {
165       NGCalendarDateRange *r;
166       
167       r = [NGCalendarDateRange calendarDateRangeWithStartDate:self->startDate
168                                endDate:self->endDate];
169       return [_range containsDateRange:r];
170     }
171     else {
172       return [_range containsDate:self->startDate];
173     }
174   }
175   else {
176     NGCalendarDateRange *fir;
177
178     fir = [NGCalendarDateRange calendarDateRangeWithStartDate:self->startDate
179                                endDate:self->endDate];
180     
181     return [self isWithinCalendarDateRange:_range
182                  firstInstanceCalendarDateRange:fir];
183   }
184   return NO;
185 }
186
187 - (NSArray *)recurrenceRangesWithinCalendarDateRange:(NGCalendarDateRange *)_r {
188   NGCalendarDateRange *fir;
189   
190   if (![self isRecurrent])
191     return nil;
192   
193   fir = [NGCalendarDateRange calendarDateRangeWithStartDate:self->startDate
194                              endDate:self->endDate];
195   return [self recurrenceRangesWithinCalendarDateRange:_r
196                firstInstanceCalendarDateRange:fir];
197 }
198
199 - (NSCalendarDate *)lastPossibleRecurrenceStartDate {
200   NGCalendarDateRange *fir;
201
202   if (![self isRecurrent])
203     return nil;
204
205   fir = [NGCalendarDateRange calendarDateRangeWithStartDate:self->startDate
206                              endDate:self->endDate];
207   return [self lastPossibleRecurrenceStartDateUsingFirstInstanceCalendarDateRange:fir];
208 }
209
210 /* ical typing */
211
212 - (NSString *)entityName {
213   return @"vevent";
214 }
215
216 /* descriptions */
217
218 - (NSString *)description {
219   NSMutableString *ms;
220
221   ms = [NSMutableString stringWithCapacity:128];
222   [ms appendFormat:@"<0x%08X[%@]:", self, NSStringFromClass([self class])];
223
224   if (self->uid)       [ms appendFormat:@" uid=%@", self->uid];
225   if (self->startDate) [ms appendFormat:@" from=%@", self->startDate];
226   if (self->endDate)   [ms appendFormat:@" to=%@", self->endDate];
227   if (self->summary)   [ms appendFormat:@" summary=%@", self->summary];
228   
229   if (self->organizer)
230     [ms appendFormat:@" organizer=%@", self->organizer];
231   if (self->attendees)
232     [ms appendFormat:@" attendees=%@", self->attendees];
233   
234   if ([self hasAlarms])
235     [ms appendFormat:@" alarms=%@", self->alarms];
236   
237   [ms appendString:@">"];
238   return ms;
239 }
240
241 /* changes */
242
243 - (iCalEventChanges *)getChangesRelativeToEvent:(iCalEvent *)_event {
244   return [iCalEventChanges changesFromEvent:_event
245                            toEvent:self];
246 }
247
248 /* generating iCal content */
249
250 - (NSString *)vEventString {
251   return [[iCalRenderer sharedICalendarRenderer] vEventStringForEvent:self];
252 }
253
254 @end /* iCalEvent */
255
256 @implementation NSString(DurationTimeInterval)
257
258 - (NSTimeInterval)durationAsTimeInterval {
259   /*
260     eg: DURATION:PT1H
261     P      - "period"
262     P2H30M - "2 hours 30 minutes"
263
264      dur-value  = (["+"] / "-") "P" (dur-date / dur-time / dur-week)
265
266      dur-date   = dur-day [dur-time]
267      dur-time   = "T" (dur-hour / dur-minute / dur-second)
268      dur-week   = 1*DIGIT "W"
269      dur-hour   = 1*DIGIT "H" [dur-minute]
270      dur-minute = 1*DIGIT "M" [dur-second]
271      dur-second = 1*DIGIT "S"
272      dur-day    = 1*DIGIT "D"
273   */
274   unsigned       i, len;
275   NSTimeInterval ti;
276   BOOL           isTime;
277   int            val;
278     
279   if (![self hasPrefix:@"P"]) {
280     NSLog(@"Cannot parse iCal duration value: '%@'", self);
281     return 0.0;
282   }
283     
284   ti  = 0.0;
285   val = 0;
286   for (i = 1, len = [self length], isTime = NO; i < len; i++) {
287     unichar c;
288       
289     c = [self characterAtIndex:i];
290     if (c == 't' || c == 'T') {
291       isTime = YES;
292       val = 0;
293       continue;
294     }
295       
296     if (isdigit(c)) {
297       val = (val * 10) + (c - 48);
298       continue;
299     }
300       
301     switch (c) {
302     case 'W': /* week */
303       ti += (val * 7 * 24 * 60 * 60);
304       break;
305     case 'D': /* day  */
306       ti += (val * 24 * 60 * 60);
307       break;
308     case 'H': /* hour */
309       ti += (val * 60 * 60);
310       break;
311     case 'M': /* min  */
312       ti += (val * 60);
313       break;
314     case 'S': /* sec  */
315       ti += val;
316       break;
317     default:
318       [self logWithFormat:@"cannot process duration unit: '%c'", c];
319       break;
320     }
321     val = 0;
322   }
323   return ti;
324 }
325
326 @end /* NSString(DurationTimeInterval) */