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

Last change on this file since 53 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.