source: trunk/Cocoa/Pester/Source/PSTimer.m @ 600

Last change on this file since 600 was 600, checked in by Nicholas Riley, 10 years ago

Prototypes to pacify GCC.

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