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

Last change on this file since 53 was 53, checked in by Nicholas Riley, 17 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.