source: releases/Pester/1.1a5/Source/PSAlarms.m

Last change on this file was 53, checked in by Nicholas Riley, 21 years ago

Updated for Pester 1.1a5 (very limited release).

Pester 1.1a4 was never released.

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