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

Last change on this file since 324 was 113, checked in by Nicholas Riley, 22 years ago

PSTimer.m: Fix bug 15, leaking PSTimer from NSInvocation argument retention.

Read Me.rtfd: Updated release notes.

PSAlarm.[hm]: Removed obsolete comment. Changed 1m-59m times to
display without `0h'. Moved code from deserialization to -resetTimer
to be used for alarm deletion undo - fix bug 14.

PSAlarmsController.m: Fix bug 14 - implement _restoreAlarms:,
_removeAlarms and _undoManager. Need to add localized string.

PSAlarmAlertController.m: Fix bug 17, move alert triggering from
constructor to -performAlertsForAlarm: to avoid nested notifications
delivering in wrong order.

PSAlarms.[hm]: Fix bug 14 - implement -restoreAlarms, invokes [PSAlarm
resetTimer]. Clarified a comment. Uncommented some debug logging.

package-Pester.sh: Implemented partition map-less disk image, saves a
few K on distribution. Removed obsolete comments. Added agvtool
bump.

File size: 9.5 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 "PSTimer.h"
12#import "NSDictionary-NJRExtensions.h"
13
14NSString * const PSAlarmImportException = @"PSAlarmImportException";
15
16NSString * const PSAlarmsDidChangeNotification = @"PSAlarmsDidChangeNotification";
17NSString * const PSAlarmsNextAlarmDidChangeNotification = @"PSAlarmsNextAlarmDidChangeNotification";
18
19// NSUserDefaults key
20static NSString * const PSPendingAlarms = @"Pester pending alarms"; // 1.0 Ð 1.1a3
21static NSString * const PSAllAlarms = @"Pester alarms"; // 1.1a4 Ð
22
23// property list keys
24static NSString * const PLAlarmsPending = @"pending";
25static NSString * const PLAlarmsExpired = @"expired";
26
27static PSAlarms *PSAlarmsAllAlarms = nil;
28
29@interface PSAlarms (Private)
30
31- (void)_updateNextAlarm;
32
33@end
34
35@implementation PSAlarms
36
37+ (void)setUp;
38{
39 [PSTimer setUp];
40 if (PSAlarmsAllAlarms == nil) {
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 }
47 [PSAlarmsAllAlarms _updateNextAlarm]; // only generate notifications after singleton established
48 }
49}
50
51+ (PSAlarms *)allAlarms;
52{
53 NSAssert(PSAlarmsAllAlarms != nil, @"Attempt to use +[PSAlarms allAlarms] before setup complete");
54 return PSAlarmsAllAlarms;
55}
56
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];
74 NSLog(@"timer expired: %@ retainCount %d", alarm, [alarm retainCount]);
75 [expiredAlarms addObject: alarm];
76 NSLog(@"expired alarms: %@", [expiredAlarms description]);
77 [alarms removeObject: alarm];
78 [self _changed];
79}
80
81- (void)_alarmTimerSet:(NSNotification *)notification;
82{
83 PSAlarm *alarm = [notification object];
84 NSLog(@"timer set: %@ retainCount %d", alarm, [alarm retainCount]);
85 [alarms addObject: alarm];
86 [expiredAlarms removeObject: alarm];
87 [self _changed];
88}
89
90- (void)_alarmDied:(NSNotification *)notification;
91{
92 PSAlarm *alarm = [notification object];
93 // NSLog(@"alarm died: %@ retainCount %d", alarm, [alarm retainCount]);
94 [alarms removeObject: alarm];
95 [expiredAlarms removeObject: alarm];
96 [self _changed];
97}
98
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
106 [alarms sortUsingSelector: @selector(compareDate:)];
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
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
128- (id)init;
129{
130 if ( (self = [super init]) != nil) {
131 alarms = [[NSMutableArray alloc] init];
132 expiredAlarms = [[NSMutableSet alloc] init];
133 [self _setUpNotifications];
134 }
135 return self;
136}
137
138- (void)dealloc;
139{
140 [alarms release];
141 [expiredAlarms release];
142 [[NSNotificationCenter defaultCenter] removeObserver: self];
143 [super dealloc];
144}
145
146#pragma mark accessing
147
148- (NSArray *)alarms;
149{
150 return alarms;
151}
152
153- (PSAlarm *)nextAlarm;
154{
155 return nextAlarm;
156}
157
158- (int)alarmCount;
159{
160 return [alarms count];
161}
162
163- (PSAlarm *)alarmAtIndex:(int)index;
164{
165 return [alarms objectAtIndex: index];
166}
167
168- (void)removeAlarmAtIndex:(int)index;
169{
170 [(PSAlarm *)[alarms objectAtIndex: index] cancelTimer];
171 [alarms removeObjectAtIndex: index];
172}
173
174- (void)removeAlarmsAtIndices:(NSArray *)indices;
175{
176 NSEnumerator *e = [indices objectEnumerator];
177 NSNumber *n;
178 int indexCount = [indices count], i = 0, alarmIndex;
179 int *indexArray = (int *)malloc(indexCount * sizeof(int));
180 NS_DURING
181 while ( (n = [e nextObject]) != nil) {
182 alarmIndex = [n intValue];
183 [(PSAlarm *)[alarms objectAtIndex: alarmIndex] cancelTimer];
184 indexArray[i] = alarmIndex;
185 i++;
186 }
187 [alarms removeObjectsFromIndices: indexArray numIndices: indexCount];
188 free(indexArray); indexArray = NULL;
189 [self _changed];
190 NS_HANDLER
191 free(indexArray);
192 [self _changed];
193 [localException raise];
194 NS_ENDHANDLER
195}
196
197- (void)removeAlarms:(NSSet *)alarmsToRemove;
198{
199 NSEnumerator *e = [alarms objectEnumerator];
200 PSAlarm *alarm;
201 NSMutableArray *indices = [NSMutableArray arrayWithCapacity: [alarmsToRemove count]];
202 int alarmIndex = 0;
203
204 while ( (alarm = [e nextObject]) != nil) {
205 if ([alarmsToRemove containsObject: alarm])
206 [indices addObject: [NSNumber numberWithInt: alarmIndex]];
207 alarmIndex++;
208 }
209 [self removeAlarmsAtIndices: indices];
210}
211
212- (void)restoreAlarms:(NSSet *)alarmsToRestore;
213{
214 [alarmsToRestore makeObjectsPerformSelector: @selector(resetTimer)];
215}
216
217- (BOOL)alarmsExpiring;
218{
219 return [expiredAlarms count] != 0;
220}
221
222#pragma mark printing
223
224- (NSString *)description;
225{
226 return [NSString stringWithFormat: @"%@ pending %@\n%@\n",
227 [super description], alarms,
228 [expiredAlarms count] > 0 ? [NSString stringWithFormat: @"expired %@\n", expiredAlarms]
229 : @""];
230}
231
232#pragma mark property list serialization (Pester 1.1)
233
234- (NSDictionary *)propertyListRepresentation;
235{
236 NSMutableArray *plPendingAlarms = [[NSMutableArray alloc] initWithCapacity: [alarms count]];
237 NSMutableArray *plExpiredAlarms = [[NSMutableArray alloc] initWithCapacity: [expiredAlarms count]];
238 NSDictionary *plAllAlarms, *plAlarm;
239 NSEnumerator *e;
240 PSAlarm *alarm;
241
242 e = [alarms objectEnumerator];
243 while ( (alarm = [e nextObject]) != nil) {
244 plAlarm = [alarm propertyListRepresentation];
245 if (plAlarm != nil)
246 [plPendingAlarms addObject: plAlarm];
247 }
248
249 e = [expiredAlarms objectEnumerator];
250 while ( (alarm = [e nextObject]) != nil) {
251 plAlarm = [alarm propertyListRepresentation];
252 if (plAlarm != nil)
253 [plExpiredAlarms addObject: plAlarm];
254 }
255
256 plAllAlarms = [NSDictionary dictionaryWithObjectsAndKeys:
257 plPendingAlarms, PLAlarmsPending, plExpiredAlarms, PLAlarmsExpired, nil];
258 [plPendingAlarms release];
259 [plExpiredAlarms release];
260
261 return plAllAlarms;
262}
263
264- (id)initWithPropertyList:(NSDictionary *)dict;
265{
266 if ( (self = [super init]) != nil) {
267 NSArray *plPendingAlarms = [dict objectForRequiredKey: PLAlarmsPending];
268 NSArray *plExpiredAlarms = [dict objectForRequiredKey: PLAlarmsExpired];
269 NSEnumerator *e;
270 NSDictionary *plAlarm;
271 PSAlarm *alarm;
272
273 alarms = [[NSMutableArray alloc] initWithCapacity: [plPendingAlarms count]];
274 e = [plPendingAlarms objectEnumerator];
275 while ( (plAlarm = [e nextObject]) != nil) {
276 [alarms addObject: [[PSAlarm alloc] initWithPropertyList: plAlarm]];
277 }
278
279 e = [plExpiredAlarms objectEnumerator];
280 while ( (plAlarm = [e nextObject]) != nil) {
281 // 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.
282 if ( (alarm = [[PSAlarm alloc] initWithPropertyList: plAlarm]) != nil)
283 [alarms addObject: alarm];
284 }
285 expiredAlarms = [[NSMutableSet alloc] init];
286
287 [self _setUpNotifications];
288 }
289 return self;
290}
291
292#pragma mark archiving (Pester 1.0)
293
294- (unsigned)countOfVersion1Alarms;
295{
296 return [[[NSUserDefaults standardUserDefaults] objectForKey: PSPendingAlarms] count];
297}
298
299- (void)discardVersion1Alarms;
300{
301 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
302 [defaults removeObjectForKey: PSPendingAlarms];
303 [defaults synchronize];
304}
305
306- (void)importVersion1Alarms;
307{
308 NSArray *alarmsData = [[NSUserDefaults standardUserDefaults] arrayForKey: PSPendingAlarms];
309 NSEnumerator *e = [alarmsData objectEnumerator];
310 NSData *alarmData;
311 PSAlarm *alarm;
312 while ( (alarmData = [e nextObject]) != nil) {
313 NS_DURING
314 alarm = [NSUnarchiver unarchiveObjectWithData: alarmData];
315 NS_HANDLER
316 alarm = nil;
317 // XXX
318 NS_ENDHANDLER
319 if (alarm != nil)
320 [alarms addObject: alarm];
321 }
322}
323
324@end
Note: See TracBrowser for help on using the repository browser.