source: trunk/Cocoa/Pester/Source/PSTimer.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: 7.3 KB
Line 
1//
2//  PSTimer.m
3//  Pester
4//
5//  Created by Nicholas Riley on Sun Jan 05 2003.
6//  Copyright (c) 2003 Nicholas Riley. All rights reserved.
7//
8
9#import "PSTimer.h"
10#import "PSAlarm.h"
11#import "PSPowerManager.h"
12
13NSTimer *PSTimerCurrent = nil;
14PSTimer *PSTimerOnWake = nil;
15NSMutableArray *PSTimerAllTimers = nil;
16
17@interface PSTimer (Private)
18+ (void)_schedule;
19@end
20
21@implementation PSTimer
22
23+ (void)setUp;
24{
25    static PSPowerManager *powerManager;
26
27    if (powerManager == nil) {
28        powerManager = [[PSPowerManager alloc] initWithDelegate: self];
29        PSTimerAllTimers = [[NSMutableArray alloc] init];
30    }
31}
32
33+ (void)_schedule;
34{
35    NSDate *aboutNow = [NSDate dateWithTimeIntervalSinceNow: 0.1];
36    [PSTimerCurrent invalidate]; [PSTimerCurrent release]; PSTimerCurrent = nil;
37    PSTimerOnWake = nil;
38    if ([PSTimerAllTimers count] > 0) {
39        PSTimer *timer = nil;
40        NSEnumerator *e;
41        [PSTimerAllTimers sortUsingSelector: @selector(compare:)];
42        // NSLog(@"_schedule: timers %@", [PSTimerAllTimers description]);
43        e = [PSTimerAllTimers objectEnumerator];
44        while ( (timer = [e nextObject]) != nil) {
45            if ([timer isWakeUp]) {
46                PSTimerOnWake = timer;
47                // NSLog(@"scheduling wake timer %@", timer);
48                break;
49            }
50        }
51        e = [PSTimerAllTimers objectEnumerator];
52        while ( (timer = [e nextObject]) != nil) {
53            if ([[timer fireDate] compare: aboutNow] != NSOrderedDescending) {
54                [timer performSelector: @selector(_timerExpired) withObject: nil afterDelay: 0];
55                return;
56            } else {
57                NSTimeInterval ti = [[timer fireDate] timeIntervalSinceNow];
58                if (ti > 0.1) {
59                    PSTimerCurrent = [[NSTimer scheduledTimerWithTimeInterval: ti target: timer selector: @selector(_timerExpired) userInfo: nil repeats: NO] retain];
60                    // NSLog(@"setting timer: %@", PSTimerCurrent);
61                } else {
62                    // NSLog(@"timer would have been too fast, setting: %@", timer);
63                    [timer performSelector: @selector(_timerExpired) withObject: nil afterDelay: 0];
64                }
65                return;
66            }
67        }
68        NSAssert(NO, @"shouldn't get here");
69    } else {
70        // NSLog(@"_schedule: no timers");
71    }
72}
73
74+ (void)_timerAdded:(PSTimer *)timer;
75{
76    NSAssert1([PSTimerAllTimers indexOfObject: timer] == NSNotFound, @"PSTimerAllTimers already contains %@", timer);
77    [PSTimerAllTimers addObject: timer];
78    [self _schedule];
79}
80
81+ (void)_timerDeleted:(PSTimer *)timer;
82{
83    NSAssert1([PSTimerAllTimers indexOfObject: timer] != NSNotFound, @"PSTimerAllTimers does not contain %@", timer);
84    [PSTimerAllTimers removeObject: timer];
85    [self _schedule];
86}
87
88#pragma mark private
89
90- (void)_setFireDate:(NSDate *)date;
91{
92    if (fireDate != date) {
93        [fireDate release];
94        fireDate = [date retain];
95        if (fireDate != nil) {
96            isValid = YES;
97        }
98    }
99}
100
101- (void)_setFireDateFromInterval;
102{
103    [self _setFireDate: [NSDate dateWithTimeIntervalSinceNow: timeInterval]];
104}
105
106- (void)_invalidate;
107{
108    if (isValid) {
109        isValid = NO;
110        [[self class] _timerDeleted: self];
111    }
112}
113
114- (void)_timerExpired;
115{
116    if (!isValid) return; // in case the timer went off after we were invalidated
117    [self retain]; // make sure we’re still accessible during the invocation
118    // NSLog(@"timer expired: %@", self);
119    if (repeats) {
120        [invocation invoke];
121        if (isValid) {
122            [self _setFireDateFromInterval];
123            [[self class] _schedule];
124            // NSLog(@"timer repeats: %@", self);
125        }
126    } else {
127        [self _invalidate];
128        [invocation invoke];
129    }
130    [self release];
131}
132
133#pragma mark initialize-release
134
135- (id)initWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)anObject repeats:(BOOL)yesOrNo;
136{
137    if ( (self = [self init]) != nil) {
138        invocation = [[NSInvocation invocationWithMethodSignature:
139            [aTarget methodSignatureForSelector: aSelector]] retain];
140        [invocation setSelector: aSelector];
141        [invocation setTarget: aTarget];
142        [invocation setArgument: &self atIndex: 2];
143        userInfo = [anObject retain];
144        repeats = yesOrNo;
145        timeInterval = ti;
146        // don't do this or we leak: [invocation retainArguments];
147        [aTarget retain]; // mimics retain behavior
148        [self _setFireDateFromInterval];
149        [[self class] _timerAdded: self]; // mimics runloop retention behavior
150    }
151    return self;
152}
153
154+ (PSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)anObject repeats:(BOOL)yesOrNo;
155{
156    PSTimer *timer = [[self alloc] initWithTimeInterval: ti target: aTarget selector: aSelector userInfo: anObject repeats: yesOrNo];
157    [timer release];
158    return timer;
159}
160
161- (void)dealloc;
162{
163    // NSLog(@"DEALLOC %@", self);
164    isValid = NO;
165    [fireDate release]; fireDate = nil;
166    [[invocation target] release];
167    [invocation release]; invocation = nil;
168    [userInfo release]; userInfo = nil;
169    [super dealloc];
170}
171
172#pragma mark accessing
173
174- (NSDate *)fireDate;
175{
176    return fireDate;
177}
178
179- (void)invalidate;
180{
181    repeats = NO;
182    [self _invalidate];
183}
184
185- (BOOL)isValid;
186{
187    return isValid;
188}
189
190- (id)userInfo;
191{
192    return userInfo;
193}
194
195- (BOOL)isWakeUp;
196{
197    return isWakeUp;
198}
199
200- (void)setWakeUp:(BOOL)doWake;
201{
202    isWakeUp = doWake;
203}
204
205- (NSComparisonResult)compare:(PSTimer *)other;
206{
207    return [fireDate compare: [other fireDate]];
208}
209
210- (NSString *)description;
211{
212    return [NSString stringWithFormat: @"%@: at %@ do %@ on %@", [super description], [self fireDate], NSStringFromSelector([invocation selector]), [[invocation target] class]];
213}
214
215@end
216
217@implementation PSTimer (PSPowerManagerDelegate)
218
219+ (void)_runScheduledWakeErrorPanel:(NSString *)error;
220{
221    NSRunAlertPanel(NSLocalizedString(@"Can't schedule wake from sleep", "Wake timer set failure panel title"), NSLocalizedString(@"Pester is unable to set this computer to wake up at a later date (%@)", "Wake timer set failure panel message"), NSLocalizedString(@"Sleep", "Wake timer set failure panel button"), nil, nil, error);
222}
223
224+ (BOOL)powerManagerShouldIdleSleep:(PSPowerManager *)powerManager;
225{
226    [PSTimerCurrent invalidate];
227    if (PSTimerOnWake != nil) {
228        NSDate *date = [PSTimerOnWake fireDate];
229        // NSLog(@"%lf sec remain until alarm", [date timeIntervalSinceNow]);
230        if ([date timeIntervalSinceNow] > 30) {
231            // NSLog(@"going to sleep, setting timer %@", PSTimerOnWake);
232            NS_DURING
233                [PSPowerManager setWakeTime: [[PSTimerOnWake fireDate] addTimeInterval: -15]];
234            NS_HANDLER
235                [self performSelectorOnMainThread: @selector(_runScheduledWakeErrorPanel:) withObject: [localException description] waitUntilDone: YES];
236            NS_ENDHANDLER
237            return YES;
238        } else {
239            // NSLog(@"not setting timer, will attempt to prevent idle sleep");
240            return NO;
241        }
242    }
243    return YES;
244}
245
246+ (void)powerManagerWillDemandSleep:(PSPowerManager *)powerManager;
247{
248    [self powerManagerShouldIdleSleep: powerManager];
249}
250
251+ (void)powerManagerDidWake:(PSPowerManager *)powerManager;
252{
253    if (PSTimerCurrent != nil) {
254        [self _schedule];
255    }
256}
257
258@end
Note: See TracBrowser for help on using the repository browser.