From de1bd56f2c33784009afc91b688809df420046c2 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Wed, 26 Mar 2008 20:30:53 +0000 Subject: [PATCH] Monotone-Parent: 7e4ce901b4c13b7bc91a9cf02ff8ff0946502aaf Monotone-Revision: e70b402803fd5be6062e9c2357b0a52a293a3440 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2008-03-26T20:30:53 Monotone-Branch: ca.inverse.sogo --- NEWS | 7 +- SOPE/NGCards/ChangeLog | 10 + SOPE/NGCards/NGCardsSaxHandler.m | 1 + .../NGCards/iCalMonthlyRecurrenceCalculator.m | 27 ++- SOPE/NGCards/iCalRecurrenceCalculator.m | 216 ++++++++++-------- SOPE/NGCards/iCalRecurrenceRule.m | 13 +- SOPE/NGCards/iCalWeeklyRecurrenceCalculator.m | 14 +- .../Appointments/SOGoAppointmentFolder.m | 78 +++---- 8 files changed, 204 insertions(+), 162 deletions(-) diff --git a/NEWS b/NEWS index 509dbe8b3..a2ed8f1da 100644 --- a/NEWS +++ b/NEWS @@ -5,9 +5,12 @@ - pressing enter in the contact edition dialog will perform the creation/update operation - implemented more of the CalDAV specification for compatibility with Lightning 0.8 - added Italian translation, thanks to Marco Lertora -- improved restoration of drag hanldes state +- improved restoration of drag handles state - improved contextual menu handling of Address Book module -- fixed various bugs with Safari 3.1 +- fixed various bugs occuring with Safari 3.1 +- monthly events would not be returned properly +- bi-weekly events would appear every week instead +- weekly events with specified days of week would not appear on the correct days 0.9.0-20080208 (1.0 rc5) ------------------------ diff --git a/SOPE/NGCards/ChangeLog b/SOPE/NGCards/ChangeLog index 7abe74abe..434420f92 100644 --- a/SOPE/NGCards/ChangeLog +++ b/SOPE/NGCards/ChangeLog @@ -1,3 +1,13 @@ +2008-03-26 Wolfgang Sourdeau + + * iCalRecurrenceRule.m ([iCalRecurrenceRule -byMonthDay]): check + whether the "bymonthday" value is empty and returns an array only if + not, otherwise returns nil. + + * iCalRecurrenceCalculator.m ([iCalRecurrenceCalculator + +recurrenceRangesWithinCalendarDateRange:_rfirstInstanceCalendarDateRange:_firrecurrenceRules:_rRulesexceptionRules:_exRulesexceptionDates:_exDates]): + split method in many submethods for clarity. + 2008-03-10 Wolfgang Sourdeau * iCalTimeZonePeriod.m ([iCalTimeZonePeriod diff --git a/SOPE/NGCards/NGCardsSaxHandler.m b/SOPE/NGCards/NGCardsSaxHandler.m index f01097394..22aedebc3 100644 --- a/SOPE/NGCards/NGCardsSaxHandler.m +++ b/SOPE/NGCards/NGCardsSaxHandler.m @@ -23,6 +23,7 @@ #import #import +#import "NSString+NGCards.h" #import "NGCardsSaxHandler.h" #import "CardGroup.h" diff --git a/SOPE/NGCards/iCalMonthlyRecurrenceCalculator.m b/SOPE/NGCards/iCalMonthlyRecurrenceCalculator.m index e8a236312..913fbf6da 100644 --- a/SOPE/NGCards/iCalMonthlyRecurrenceCalculator.m +++ b/SOPE/NGCards/iCalMonthlyRecurrenceCalculator.m @@ -43,18 +43,21 @@ typedef BOOL NGMonthSet[12]; typedef BOOL NGMonthDaySet[32]; // 0 is unused -static void NGMonthDaySet_clear(NGMonthDaySet *daySet) { - register unsigned i; - - for (i = 1; i <= 31; i++) - (*daySet)[i] = NO; -} - -static void NGMonthDaySet_copyOrUnion(NGMonthDaySet *base, NGMonthDaySet *new, - BOOL doCopy) +static void +NGMonthDaySet_clear(NGMonthDaySet *daySet) { register unsigned i; + for (i = 0; i <= 31; i++) + (*daySet)[i] = NO; +} + +static void +NGMonthDaySet_copyOrUnion(NGMonthDaySet *base, NGMonthDaySet *new, + BOOL doCopy) +{ + register unsigned i; + if (doCopy) memcpy(base, new, sizeof(NGMonthDaySet)); else { @@ -265,7 +268,7 @@ static void NGMonthDaySet_fillWithByDayX(NGMonthDaySet *daySet, /* check whether the range to be processed is beyond the 'until' date */ - if (until != nil) { + if (until) { if ([until compare:rStart] == NSOrderedAscending) /* until before start */ return nil; if ([until compare:rEnd] == NSOrderedDescending) /* end before until */ @@ -275,7 +278,7 @@ static void NGMonthDaySet_fillWithByDayX(NGMonthDaySet *daySet, /* precalculate month days (same for all instances) */ - if (byMonthDay != nil) { + if (byMonthDay) { #if HEAVY_DEBUG NSLog(@"byMonthDay: %@", byMonthDay); #endif @@ -343,7 +346,7 @@ static void NGMonthDaySet_fillWithByDayX(NGMonthDaySet *daySet, didByFill = NO; - if (byMonthDay != nil) { /* list of days in the month */ + if (byMonthDay) { /* list of days in the month */ NGMonthDaySet_copyOrUnion(&monthDays, &byMonthDaySet, !didByFill); didByFill = YES; } diff --git a/SOPE/NGCards/iCalRecurrenceCalculator.m b/SOPE/NGCards/iCalRecurrenceCalculator.m index b5ef74410..c3e9b8ed3 100644 --- a/SOPE/NGCards/iCalRecurrenceCalculator.m +++ b/SOPE/NGCards/iCalRecurrenceCalculator.m @@ -48,19 +48,22 @@ @implementation iCalRecurrenceCalculator static Class NSCalendarDateClass = Nil; +static Class NSStringClass = Nil; static Class iCalRecurrenceRuleClass = Nil; static Class dailyCalcClass = Nil; static Class weeklyCalcClass = Nil; static Class monthlyCalcClass = Nil; static Class yearlyCalcClass = Nil; -+ (void)initialize { ++ (void) initialize +{ static BOOL didInit = NO; if (didInit) return; didInit = YES; NSCalendarDateClass = [NSCalendarDate class]; + NSStringClass = [NSString class]; iCalRecurrenceRuleClass = [iCalRecurrenceRule class]; dailyCalcClass = NSClassFromString(@"iCalDailyRecurrenceCalculator"); @@ -107,98 +110,122 @@ static Class yearlyCalcClass = Nil; /* complex calculation convenience */ -+ (NSArray *)recurrenceRangesWithinCalendarDateRange:(NGCalendarDateRange *)_r - firstInstanceCalendarDateRange:(NGCalendarDateRange *)_fir - recurrenceRules:(NSArray *)_rRules - exceptionRules:(NSArray *)_exRules - exceptionDates:(NSArray *)_exDates ++ (void) _fillRanges: (NSMutableArray *) ranges + fromRules: (NSArray *) rrules + withinRange: (NGCalendarDateRange *) limits + startingWithDate: (NGCalendarDateRange *) first { - id rule; + NSEnumerator *rules; + iCalRecurrenceRule *currentRule; iCalRecurrenceCalculator *calc; - NSMutableArray *ranges; - NSMutableArray *exDates; - unsigned i, count, rCount; - - ranges = [NSMutableArray arrayWithCapacity:64]; - - for (i = 0, count = [_rRules count]; i < count; i++) { - NSArray *rs; - rule = [_rRules objectAtIndex:i]; - if (![rule isKindOfClass:iCalRecurrenceRuleClass]) - rule = [iCalRecurrenceRule recurrenceRuleWithICalRepresentation:rule]; - - calc = [self recurrenceCalculatorForRecurrenceRule:rule - withFirstInstanceCalendarDateRange:_fir]; + rules = [rrules objectEnumerator]; + while ((currentRule = [rules nextObject])) + { + if ([currentRule isKindOfClass: NSStringClass]) + currentRule = + [iCalRecurrenceRule + recurrenceRuleWithICalRepresentation: (NSString *) currentRule]; - rs = [calc recurrenceRangesWithinCalendarDateRange:_r]; - [ranges addObjectsFromArray:rs]; - } - - if ([ranges count] == 0) - return nil; - - /* test if any exceptions do match */ - - for (i = 0, count = [_exRules count]; i < count; i++) { - NSArray *rs; - - rule = [_exRules objectAtIndex:i]; - if (![rule isKindOfClass:iCalRecurrenceRuleClass]) - rule = [iCalRecurrenceRule recurrenceRuleWithICalRepresentation:rule]; - - calc = [self recurrenceCalculatorForRecurrenceRule:rule - withFirstInstanceCalendarDateRange:_fir]; - rs = [calc recurrenceRangesWithinCalendarDateRange:_r]; - [ranges removeObjectsInArray:rs]; - } - - if (![ranges isNotEmpty]) - return nil; - - /* exception dates */ - - if ((count = [_exDates count]) == 0) - return ranges; - - /* sort out exDates not within range */ - - exDates = [NSMutableArray arrayWithCapacity:count]; - for (i = 0; i < count; i++) { - id exDate; - - exDate = [_exDates objectAtIndex:i]; - if (![exDate isKindOfClass:NSCalendarDateClass]) - exDate = [NSCalendarDate calendarDateWithICalRepresentation:exDate]; - - if ([_r containsDate:exDate]) - [exDates addObject:exDate]; - } - - /* remove matching exDates from ranges */ - - if ((count = [exDates count]) == 0) - return ranges; - - for (i = 0, rCount = [ranges count]; i < count; i++) { - NSCalendarDate *exDate; - NGCalendarDateRange *r; - unsigned k; - - exDate = [exDates objectAtIndex:i]; - - for (k = 0; k < rCount; k++) { - unsigned rIdx; - - rIdx = (rCount - k) - 1; - r = [ranges objectAtIndex:rIdx]; - if ([r containsDate:exDate]) { - [ranges removeObjectAtIndex:rIdx]; - rCount--; - break; /* this is safe because we know that ranges don't overlap */ - } + calc = [self recurrenceCalculatorForRecurrenceRule: currentRule + withFirstInstanceCalendarDateRange: first]; + [ranges addObjectsFromArray: + [calc recurrenceRangesWithinCalendarDateRange: limits]]; } - } +} + ++ (void) _removeExceptionsFromRanges: (NSMutableArray *) ranges + withRules: (NSArray *) exrules + withinRange: (NGCalendarDateRange *) limits + startingWithDate: (NGCalendarDateRange *) first +{ + NSEnumerator *rules; + iCalRecurrenceRule *currentRule; + iCalRecurrenceCalculator *calc; + + rules = [exrules objectEnumerator]; + while ((currentRule = [rules nextObject])) + { + if ([currentRule isKindOfClass: NSStringClass]) + currentRule = + [iCalRecurrenceRule + recurrenceRuleWithICalRepresentation: (NSString *) currentRule]; + + calc = [self recurrenceCalculatorForRecurrenceRule: currentRule + withFirstInstanceCalendarDateRange: first]; + [ranges removeObjectsInArray: + [calc recurrenceRangesWithinCalendarDateRange: limits]]; + } +} + ++ (NSArray *) _dates: (NSArray *) dateList + withinRange: (NGCalendarDateRange *) limits +{ + NSMutableArray *newDates; + NSEnumerator *dates; + NSCalendarDate *currentDate; + + newDates = [NSMutableArray array]; + + dates = [dateList objectEnumerator]; + while ((currentDate = [dates nextObject])) + { + if ([currentDate isKindOfClass: NSStringClass]) + currentDate + = [NSCalendarDate + calendarDateWithICalRepresentation: (NSString *) currentDate]; + if ([limits containsDate: currentDate]) + [newDates addObject: currentDate]; + } + + return newDates; +} + ++ (void) _removeExceptionDatesFromRanges: (NSMutableArray *) ranges + withDates: (NSArray *) exdates + withinRange: (NGCalendarDateRange *) limits + startingWithDate: (NGCalendarDateRange *) first +{ + NSEnumerator *dates; + NSCalendarDate *currentDate; + NGCalendarDateRange *currentRange; + int count, maxRanges; + + maxRanges = [ranges count]; + dates = [[self _dates: exdates withinRange: limits] objectEnumerator]; + while ((currentDate = [dates nextObject])) + for (count = (maxRanges - 1); count > -1; count++) + { + currentRange = [ranges objectAtIndex: count]; + if ([currentRange containsDate: currentDate]) + { + [ranges removeObjectAtIndex: count]; + maxRanges--; + } + } +} + ++ (NSArray *) + recurrenceRangesWithinCalendarDateRange: (NGCalendarDateRange *) _r + firstInstanceCalendarDateRange: (NGCalendarDateRange *) _fir + recurrenceRules: (NSArray *) _rRules + exceptionRules: (NSArray *) _exRules + exceptionDates: (NSArray *) _exDates +{ + NSMutableArray *ranges; + + ranges = [NSMutableArray arrayWithCapacity: 64]; + + if ([_rRules count] > 0) + { + [self _fillRanges: ranges fromRules: _rRules + withinRange: _r startingWithDate: _fir]; + [self _removeExceptionsFromRanges: ranges withRules: _exRules + withinRange: _r startingWithDate: _fir]; + [self _removeExceptionDatesFromRanges: ranges withDates: _exDates + withinRange: _r startingWithDate: _fir]; + } + return ranges; } @@ -265,23 +292,12 @@ static Class yearlyCalcClass = Nil; - (iCalWeekDay) weekDayForJulianNumber: (long)_jn { - unsigned int day; - iCalWeekDay weekDay; iCalWeekDay weekDays[] = {iCalWeekDaySunday, iCalWeekDayMonday, iCalWeekDayTuesday, iCalWeekDayWednesday, iCalWeekDayThursday, iCalWeekDayFriday, iCalWeekDaySaturday}; - if (day < 7) - weekDay = weekDays[day]; - else - { - [self errorWithFormat: - @"got unexpected weekday: %d (falling back on sunday)", day]; - weekDay = iCalWeekDaySunday; - } - - return weekDay; + return weekDays[[self offsetFromSundayForJulianNumber: _jn]]; } /* calculation */ diff --git a/SOPE/NGCards/iCalRecurrenceRule.m b/SOPE/NGCards/iCalRecurrenceRule.m index 3ce38f1a6..53f9939ab 100644 --- a/SOPE/NGCards/iCalRecurrenceRule.m +++ b/SOPE/NGCards/iCalRecurrenceRule.m @@ -325,7 +325,16 @@ - (NSArray *) byMonthDay { - return [[self namedValue: @"bymonthday"] componentsSeparatedByString: @","]; + NSArray *byMonthDay; + NSString *byMonthDayStr; + + byMonthDayStr = [self namedValue: @"bymonthday"]; + if ([byMonthDayStr length]) + byMonthDay = [byMonthDayStr componentsSeparatedByString: @","]; + else + byMonthDay = nil; + + return byMonthDay; } - (BOOL) isInfinite @@ -363,11 +372,13 @@ foundDay = iCalWeekDayTuesday; else if (chars[1] == 'H') foundDay = iCalWeekDayThursday; + break; case 'S': if (chars[1] == 'A') foundDay = iCalWeekDaySaturday; else if (chars[1] == 'U') foundDay = iCalWeekDaySunday; + break; } } diff --git a/SOPE/NGCards/iCalWeeklyRecurrenceCalculator.m b/SOPE/NGCards/iCalWeeklyRecurrenceCalculator.m index eade01969..4c2dd0b3e 100644 --- a/SOPE/NGCards/iCalWeeklyRecurrenceCalculator.m +++ b/SOPE/NGCards/iCalWeeklyRecurrenceCalculator.m @@ -32,13 +32,13 @@ @interface iCalRecurrenceCalculator (PrivateAPI) -- (NSCalendarDate *)lastInstanceStartDate; +- (NSCalendarDate *) lastInstanceStartDate; -- (unsigned)offsetFromSundayForJulianNumber:(long)_jn; -- (unsigned)offsetFromSundayForWeekDay:(iCalWeekDay)_weekDay; -- (unsigned)offsetFromSundayForCurrentWeekStart; +- (unsigned) offsetFromSundayForJulianNumber:(long)_jn; +- (unsigned) offsetFromSundayForWeekDay:(iCalWeekDay)_weekDay; +- (unsigned) offsetFromSundayForCurrentWeekStart; -- (iCalWeekDay)weekDayForJulianNumber:(long)_jn; +- (iCalWeekDay) weekDayForJulianNumber:(long)_jn; @end @@ -48,7 +48,9 @@ */ @implementation iCalWeeklyRecurrenceCalculator -- (NSArray *)recurrenceRangesWithinCalendarDateRange:(NGCalendarDateRange *)_r { +- (NSArray *) + recurrenceRangesWithinCalendarDateRange: (NGCalendarDateRange *) _r +{ NSMutableArray *ranges; NSCalendarDate *firStart; long i, jnFirst, jnStart, jnEnd, startEndCount; diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.m b/SoObjects/Appointments/SOGoAppointmentFolder.m index 27a2eb3bf..173b9c0f9 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.m +++ b/SoObjects/Appointments/SOGoAppointmentFolder.m @@ -907,10 +907,10 @@ static NSNumber *sharedYes = nil; forRange: (NGCalendarDateRange *) _r intoArray: (NSMutableArray *) _ma { - NSMutableDictionary *row; + NSMutableDictionary *row, *fixedRow; NSDictionary *cycleinfo; NSCalendarDate *startDate, *endDate; - NGCalendarDateRange *fir; + NGCalendarDateRange *fir, *rRange; NSArray *rules, *exRules, *exDates, *ranges; unsigned i, count; NSString *content; @@ -918,47 +918,44 @@ static NSNumber *sharedYes = nil; content = [_row objectForKey: @"c_cycleinfo"]; if (![content isNotNull]) { - [self errorWithFormat:@"cyclic record doesn't have cycleinfo -> %@", _row]; + [self errorWithFormat:@"cyclic record doesn't have cycleinfo -> %@", + _row]; return; } cycleinfo = [content propertyList]; if (!cycleinfo) { - [self errorWithFormat:@"cyclic record doesn't have cycleinfo -> %@", _row]; + [self errorWithFormat:@"cyclic record doesn't have cycleinfo -> %@", + _row]; return; } row = [self fixupRecord:_row fetchRange: _r]; [row removeObjectForKey: @"c_cycleinfo"]; - [row setObject: sharedYes forKey:@"isRecurrentEvent"]; + [row setObject: sharedYes forKey: @"isRecurrentEvent"]; - startDate = [row objectForKey:@"startDate"]; - endDate = [row objectForKey:@"endDate"]; - fir = [NGCalendarDateRange calendarDateRangeWithStartDate:startDate - endDate:endDate]; - rules = [cycleinfo objectForKey:@"rules"]; - exRules = [cycleinfo objectForKey:@"exRules"]; - exDates = [cycleinfo objectForKey:@"exDates"]; + startDate = [row objectForKey: @"startDate"]; + endDate = [row objectForKey: @"endDate"]; + fir = [NGCalendarDateRange calendarDateRangeWithStartDate: startDate + endDate: endDate]; + rules = [cycleinfo objectForKey: @"rules"]; + exRules = [cycleinfo objectForKey: @"exRules"]; + exDates = [cycleinfo objectForKey: @"exDates"]; - ranges = [iCalRecurrenceCalculator recurrenceRangesWithinCalendarDateRange:_r - firstInstanceCalendarDateRange:fir - recurrenceRules:rules - exceptionRules:exRules - exceptionDates:exDates]; + ranges = [iCalRecurrenceCalculator recurrenceRangesWithinCalendarDateRange: _r + firstInstanceCalendarDateRange: fir + recurrenceRules: rules + exceptionRules: exRules + exceptionDates: exDates]; count = [ranges count]; - - for (i = 0; i < count; i++) { - NGCalendarDateRange *rRange; - id fixedRow; - - rRange = [ranges objectAtIndex:i]; - fixedRow = [self fixupCycleRecord:row cycleRange:rRange]; - if (fixedRow != nil) - { + for (i = 0; i < count; i++) + { + rRange = [ranges objectAtIndex:i]; + fixedRow = [self fixupCycleRecord: row cycleRange: rRange]; + if (fixedRow) [_ma addObject:fixedRow]; - } - } + } } - (NSArray *) fixupCyclicRecords: (NSArray *) _records @@ -966,19 +963,18 @@ static NSNumber *sharedYes = nil; { // TODO: is the result supposed to be sorted by date? NSMutableArray *ma; - unsigned i, count; - - if (_records == nil) return nil; - if ((count = [_records count]) == 0) - return _records; - - ma = [NSMutableArray arrayWithCapacity:count]; - for (i = 0; i < count; i++) { - id row; // TODO: what is the type of the record? - - row = [_records objectAtIndex:i]; - [self _flattenCycleRecord:row forRange:_r intoArray:ma]; - } + NSDictionary *row; + unsigned int i, count; + + count = [_records count]; + ma = [NSMutableArray arrayWithCapacity: count]; + if (count > 0) + for (i = 0; i < count; i++) + { + row = [_records objectAtIndex: i]; + [self _flattenCycleRecord: row forRange: _r intoArray: ma]; + } + return ma; }