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

Last change on this file since 355 was 355, checked in by Nicholas Riley, 12 years ago

English.lproj/MainMenu.nib: Modernize menu and alarm set dialog
layout. Use keyed archiving (10.2+) nib format.

Info-Pester.plist: Moved from old PBX project.

NJRFSObjectSelector.m: Bug fixes from code sent to Joey: remove
incorrect usage of tryToPerform:with:; fix logic error in menu
construction. Work around Cocoa's deciding that the menu font size
needs adjustment when it doesn't - so the menu font size now matches
the button font size, though the position is still off. Don't pop up
a menu if we're disabled. Use IconRefs? for menu icons, though not
(yet) for the button icon.

NJRHistoryTrackingComboBox.m: Remove item height adjustment
workaround; it now makes the items too tall.

NJRHotKey.m: Add a missing [super dealloc] caught by current GCC.

NJRHotKeyField.m: Add a missing [super dealloc] caught by current GCC.

NJRHotKeyManager.m: Add a missing [super dealloc] caught by current
GCC.

NJRIntervalField.m: Fix some type errors.

NJRQTMediaPopUpButton.m: Replace SoundFileManager? SPI usage, which
doesn't work in Leopard anyway, with manual enumeration of system
sounds. Start migration to QTKit. Use IconRefs? for menu icons.

NJRReadMeController.m: Change source encoding to UTF-8.

NJRSoundManager.m: Fix a type error.

NJRVoicePopUpButton.m: Change source encoding to UTF-8.

NSMenuItem-NJRExtensions.[hm]: Code from ICeCoffEE to use IconRefs? for
menu item icons.

PSAlarm.m: Change source encoding to UTF-8.

PSAlarms.m: Fix a signedness mismatch.

PSAlarmsController.m: Change source encoding to UTF-8.

PSAlarmSetController.m: Set keyboard focus after unchecking "Do
script:" and "Play" checkboxes.

PSAlerts.m: Add a missing [super dealloc] caught by current GCC. Fix
a memory leak in property list serialization.

PSPowerManager.[hm]: There's now API for scheduling wakeups; use it
(the old code asserted on startup). To fix: removing scheduled
wakeup. Fix a small type-checking error.

PSPreferencesController.m: Add a missing [super dealloc] caught by
current GCC.

PSScriptAlert.m: Change source encoding to UTF-8.

PSTimeDateEditor.m: Fix a tiny, and one-time, memory leak.

PSTimer.m: Update for new PSPowerManager API.

Pester.pbproj: Deleted; now supporting OS X 10.4+ (up from 10.1,
aiee.)

Pester.xcodeproj: Xcode 2.4+ project, upgraded targets, etc.

SoundFileManager?.h: Deleted; this SPI no longer exists in Leopard and
possibly earlier.

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    unsigned indexCount = [indices count], i = 0, alarmIndex;
179    unsigned *indexArray = (unsigned *)malloc(indexCount * sizeof(unsigned));
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.