source: trunk/Cocoa/Pester/Source/PSAlarms.m@ 622

Last change on this file since 622 was 602, checked in by Nicholas Riley, 14 years ago

Rename variables which shadow stdlib functions (and maxSize).

File size: 9.8 KB
RevLine 
[26]1//
2// PSAlarms.m
3// Pester
4//
5// Created by Nicholas Riley on Fri Oct 11 2002.
6// Copyright (c) 2002 Nicholas Riley. All rights reserved.
7//
8
9#import "PSAlarms.h"
10#import "PSAlarm.h"
[61]11#import "PSTimer.h"
[53]12#import "NSDictionary-NJRExtensions.h"
[26]13
[53]14NSString * const PSAlarmImportException = @"PSAlarmImportException";
15
[26]16NSString * const PSAlarmsDidChangeNotification = @"PSAlarmsDidChangeNotification";
[28]17NSString * const PSAlarmsNextAlarmDidChangeNotification = @"PSAlarmsNextAlarmDidChangeNotification";
[26]18
[53]19// NSUserDefaults key
20static NSString * const PSPendingAlarms = @"Pester pending alarms"; // 1.0 Ð 1.1a3
21static NSString * const PSAllAlarms = @"Pester alarms"; // 1.1a4 Ð
[26]22
[53]23// property list keys
24static NSString * const PLAlarmsPending = @"pending";
25static NSString * const PLAlarmsExpired = @"expired";
26
[26]27static PSAlarms *PSAlarmsAllAlarms = nil;
28
[28]29@interface PSAlarms (Private)
30
31- (void)_updateNextAlarm;
32
33@end
34
[26]35@implementation PSAlarms
36
[28]37+ (void)setUp;
[26]38{
[64]39 [PSTimer setUp];
[26]40 if (PSAlarmsAllAlarms == nil) {
[53]41 NSDictionary *plAlarms = [[NSUserDefaults standardUserDefaults] objectForKey: PSAllAlarms];
42 if (plAlarms == nil) {
43 PSAlarmsAllAlarms = [[self alloc] init];
44 } else {
45 PSAlarmsAllAlarms = [[self alloc] initWithPropertyList: plAlarms];
46 }
[28]47 [PSAlarmsAllAlarms _updateNextAlarm]; // only generate notifications after singleton established
[26]48 }
49}
50
51+ (PSAlarms *)allAlarms;
52{
[28]53 NSAssert(PSAlarmsAllAlarms != nil, @"Attempt to use +[PSAlarms allAlarms] before setup complete");
[26]54 return PSAlarmsAllAlarms;
55}
56
[53]57#pragma mark private
58
59- (void)_changed;
60{
61 NSMutableArray *alarmsData = [[NSMutableArray alloc] init];
62 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
63 [self _updateNextAlarm];
64 // NSLog(@"PSAlarms _changed:\n%@", alarms);
65 [defaults setObject: [self propertyListRepresentation] forKey: PSAllAlarms];
66 [defaults synchronize];
67 [alarmsData release];
68 [[NSNotificationCenter defaultCenter] postNotificationName: PSAlarmsDidChangeNotification object: self];
69}
70
71- (void)_alarmTimerExpired:(NSNotification *)notification;
72{
73 PSAlarm *alarm = [notification object];
[113]74 NSLog(@"timer expired: %@ retainCount %d", alarm, [alarm retainCount]);
[53]75 [expiredAlarms addObject: alarm];
[113]76 NSLog(@"expired alarms: %@", [expiredAlarms description]);
[53]77 [alarms removeObject: alarm];
78 [self _changed];
79}
80
81- (void)_alarmTimerSet:(NSNotification *)notification;
82{
83 PSAlarm *alarm = [notification object];
[113]84 NSLog(@"timer set: %@ retainCount %d", alarm, [alarm retainCount]);
[53]85 [alarms addObject: alarm];
86 [expiredAlarms removeObject: alarm];
87 [self _changed];
88}
89
90- (void)_alarmDied:(NSNotification *)notification;
91{
92 PSAlarm *alarm = [notification object];
[61]93 // NSLog(@"alarm died: %@ retainCount %d", alarm, [alarm retainCount]);
[53]94 [alarms removeObject: alarm];
95 [expiredAlarms removeObject: alarm];
96 [self _changed];
97}
98
[28]99- (void)_updateNextAlarm;
100{
101 NSEnumerator *e;
102 PSAlarm *alarm, *oldNextAlarm = nextAlarm;
103 [nextAlarm release];
104 nextAlarm = nil;
105 // sort alarms so earliest is first
[51]106 [alarms sortUsingSelector: @selector(compareDate:)];
[28]107 // find first un-expired alarm
108 e = [alarms objectEnumerator];
109 while ( (alarm = [e nextObject]) != nil) {
110 if ([alarm isValid]) {
111 nextAlarm = [alarm retain];
112 break;
113 }
114 }
115 if (oldNextAlarm != nextAlarm)
116 [[NSNotificationCenter defaultCenter] postNotificationName: PSAlarmsNextAlarmDidChangeNotification object: nextAlarm];
117}
118
[53]119- (void)_setUpNotifications;
120{
121 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(_alarmTimerSet:) name: PSAlarmTimerSetNotification object: nil];
122 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(_alarmTimerExpired:) name: PSAlarmTimerExpiredNotification object: nil];
123 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(_alarmDied:) name: PSAlarmDiedNotification object: nil];
124}
125
126#pragma mark initialize-release
127
[26]128- (id)init;
129{
130 if ( (self = [super init]) != nil) {
[28]131 alarms = [[NSMutableArray alloc] init];
[53]132 expiredAlarms = [[NSMutableSet alloc] init];
133 [self _setUpNotifications];
[26]134 }
135 return self;
136}
137
138- (void)dealloc;
139{
[28]140 [alarms release];
[53]141 [expiredAlarms release];
[26]142 [[NSNotificationCenter defaultCenter] removeObserver: self];
143 [super dealloc];
144}
145
[53]146#pragma mark accessing
[26]147
[53]148- (NSArray *)alarms;
[26]149{
[53]150 return alarms;
[26]151}
152
[28]153- (PSAlarm *)nextAlarm;
154{
155 return nextAlarm;
156}
157
[26]158- (int)alarmCount;
159{
160 return [alarms count];
161}
162
[602]163- (PSAlarm *)alarmAtIndex:(int)alarmIndex;
[26]164{
[602]165 return [alarms objectAtIndex: alarmIndex];
[26]166}
167
[602]168- (void)removeAlarmAtIndex:(int)alarmIndex;
[26]169{
[602]170 [(PSAlarm *)[alarms objectAtIndex: alarmIndex] cancelTimer];
171 [alarms removeObjectAtIndex: alarmIndex];
[26]172}
173
174- (void)removeAlarmsAtIndices:(NSArray *)indices;
175{
176 NSEnumerator *e = [indices objectEnumerator];
177 NSNumber *n;
[355]178 unsigned indexCount = [indices count], i = 0, alarmIndex;
179 unsigned *indexArray = (unsigned *)malloc(indexCount * sizeof(unsigned));
[364]180 @try {
[26]181 while ( (n = [e nextObject]) != nil) {
[28]182 alarmIndex = [n intValue];
[51]183 [(PSAlarm *)[alarms objectAtIndex: alarmIndex] cancelTimer];
[28]184 indexArray[i] = alarmIndex;
[26]185 i++;
186 }
187 [alarms removeObjectsFromIndices: indexArray numIndices: indexCount];
[364]188 } @finally {
[51]189 free(indexArray); indexArray = NULL;
[26]190 [self _changed];
[364]191 }
[26]192}
193
[51]194- (void)removeAlarms:(NSSet *)alarmsToRemove;
195{
196 NSEnumerator *e = [alarms objectEnumerator];
197 PSAlarm *alarm;
198 NSMutableArray *indices = [NSMutableArray arrayWithCapacity: [alarmsToRemove count]];
199 int alarmIndex = 0;
200
201 while ( (alarm = [e nextObject]) != nil) {
202 if ([alarmsToRemove containsObject: alarm])
203 [indices addObject: [NSNumber numberWithInt: alarmIndex]];
204 alarmIndex++;
205 }
206 [self removeAlarmsAtIndices: indices];
207}
208
[113]209- (void)restoreAlarms:(NSSet *)alarmsToRestore;
210{
211 [alarmsToRestore makeObjectsPerformSelector: @selector(resetTimer)];
212}
213
[60]214- (BOOL)alarmsExpiring;
215{
216 return [expiredAlarms count] != 0;
217}
218
[105]219#pragma mark printing
220
221- (NSString *)description;
222{
223 return [NSString stringWithFormat: @"%@ pending %@\n%@\n",
224 [super description], alarms,
225 [expiredAlarms count] > 0 ? [NSString stringWithFormat: @"expired %@\n", expiredAlarms]
226 : @""];
227}
228
[53]229#pragma mark property list serialization (Pester 1.1)
230
231- (NSDictionary *)propertyListRepresentation;
232{
233 NSMutableArray *plPendingAlarms = [[NSMutableArray alloc] initWithCapacity: [alarms count]];
234 NSMutableArray *plExpiredAlarms = [[NSMutableArray alloc] initWithCapacity: [expiredAlarms count]];
235 NSDictionary *plAllAlarms, *plAlarm;
236 NSEnumerator *e;
237 PSAlarm *alarm;
238
239 e = [alarms objectEnumerator];
240 while ( (alarm = [e nextObject]) != nil) {
241 plAlarm = [alarm propertyListRepresentation];
242 if (plAlarm != nil)
243 [plPendingAlarms addObject: plAlarm];
244 }
245
246 e = [expiredAlarms objectEnumerator];
247 while ( (alarm = [e nextObject]) != nil) {
248 plAlarm = [alarm propertyListRepresentation];
249 if (plAlarm != nil)
250 [plExpiredAlarms addObject: plAlarm];
251 }
252
253 plAllAlarms = [NSDictionary dictionaryWithObjectsAndKeys:
254 plPendingAlarms, PLAlarmsPending, plExpiredAlarms, PLAlarmsExpired, nil];
255 [plPendingAlarms release];
256 [plExpiredAlarms release];
[105]257
[53]258 return plAllAlarms;
259}
260
261- (id)initWithPropertyList:(NSDictionary *)dict;
262{
263 if ( (self = [super init]) != nil) {
264 NSArray *plPendingAlarms = [dict objectForRequiredKey: PLAlarmsPending];
265 NSArray *plExpiredAlarms = [dict objectForRequiredKey: PLAlarmsExpired];
266 NSEnumerator *e;
267 NSDictionary *plAlarm;
268 PSAlarm *alarm;
269
270 alarms = [[NSMutableArray alloc] initWithCapacity: [plPendingAlarms count]];
271 e = [plPendingAlarms objectEnumerator];
272 while ( (plAlarm = [e nextObject]) != nil) {
[357]273 alarm = [[PSAlarm alloc] initWithPropertyList: plAlarm];
274 [alarms addObject: alarm];
275 [alarm release];
[53]276 }
277
278 e = [plExpiredAlarms objectEnumerator];
279 while ( (plAlarm = [e nextObject]) != nil) {
[113]280 // expired alarms may be ready for deletion, or may repeat - if the latter, PSAlarm will reschedule the alarm so the repeat interval begins at restoration time.
[357]281 if ( (alarm = [[PSAlarm alloc] initWithPropertyList: plAlarm]) != nil) {
[53]282 [alarms addObject: alarm];
[357]283 [alarm release];
284 }
[53]285 }
286 expiredAlarms = [[NSMutableSet alloc] init];
287
288 [self _setUpNotifications];
289 }
290 return self;
291}
292
293#pragma mark archiving (Pester 1.0)
294
295- (unsigned)countOfVersion1Alarms;
296{
297 return [[[NSUserDefaults standardUserDefaults] objectForKey: PSPendingAlarms] count];
298}
299
300- (void)discardVersion1Alarms;
301{
302 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
303 [defaults removeObjectForKey: PSPendingAlarms];
304 [defaults synchronize];
305}
306
307- (void)importVersion1Alarms;
308{
309 NSArray *alarmsData = [[NSUserDefaults standardUserDefaults] arrayForKey: PSPendingAlarms];
310 NSEnumerator *e = [alarmsData objectEnumerator];
311 NSData *alarmData;
312 PSAlarm *alarm;
[364]313 NSMutableArray *importedAlarms = [[NSMutableArray alloc] initWithCapacity: [alarmsData count]];
314 @try {
315 while ( (alarmData = [e nextObject]) != nil) {
316 alarm = [NSUnarchiver unarchiveObjectWithData: alarmData];
317 if (alarm == nil)
318 @throw [NSException exceptionWithName: NSInternalInconsistencyException reason: @"Failed to decode Pester 1.0 alarm." userInfo: nil];
319 [importedAlarms addObject: alarm];
320 if (![alarm setTimer]) // expired
321 [alarms addObject: alarm];
322 }
323 } @catch (NSException *exception) {
324 [self removeAlarms: [NSSet setWithArray: importedAlarms]];
325 @throw;
326 } @finally {
327 [importedAlarms release];
[53]328 }
329}
330
[26]331@end
Note: See TracBrowser for help on using the repository browser.