source: trunk/Cocoa/Pester/Source/PSAlarm.m @ 34

Last change on this file since 34 was 34, checked in by Nicholas Riley, 20 years ago

Changes for Pester 1.1d1.

File size: 9.0 KB
Line 
1//
2//  PSAlarm.m
3//  Pester
4//
5//  Created by Nicholas Riley on Wed Oct 09 2002.
6//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
7//
8
9#import "PSAlarm.h"
10#import "PSAlert.h"
11
12NSString * const PSAlarmTimerSetNotification = @"PSAlarmTimerSetNotification";
13NSString * const PSAlarmTimerExpiredNotification = @"PSAlarmTimerExpiredNotification";
14
15// XXX need to reset pending alarms after sleep, they "freeze" and never expire.
16
17@implementation PSAlarm
18
19- (void)dealloc;
20{
21    // NSLog(@"DEALLOC %@", self);
22    alarmType = PSAlarmInvalid;
23    [alarmDate release]; alarmDate = nil;
24    [alarmMessage release]; alarmMessage = nil;
25    [invalidMessage release]; invalidMessage = nil;
26    [timer invalidate]; [timer release]; timer = nil;
27    [alerts release]; alerts = nil;
28    [super dealloc];
29}
30
31- (void)_setAlarmDate:(NSCalendarDate *)aDate;
32{
33    if (alarmDate != aDate) {
34        [alarmDate release];
35        alarmDate = nil;
36        alarmDate = [aDate retain];
37    }
38}
39
40- (void)_invalidate:(NSString *)aMessage;
41{
42    alarmType = PSAlarmInvalid;
43    if (aMessage != invalidMessage) {
44        [invalidMessage release];
45        invalidMessage = nil;
46        [self _setAlarmDate: nil];
47        alarmInterval = 0;
48        invalidMessage = [aMessage retain];
49    }
50}
51
52- (void)_validateForType:(PSAlarmType)type;
53{
54    if (alarmType == PSAlarmSet) return; // already valid
55    [invalidMessage release];
56    invalidMessage = nil;
57    alarmType = type;
58}
59
60- (void)_setDateFromInterval;
61{
62    [alarmDate release]; alarmDate = nil;
63    alarmDate = [NSCalendarDate dateWithTimeIntervalSinceNow: alarmInterval];
64    [alarmDate retain];
65    [self _validateForType: PSAlarmInterval];
66}
67
68- (void)setInterval:(NSTimeInterval)anInterval;
69{
70    alarmInterval = anInterval;
71    if (alarmInterval <= 0) {
72        [self _invalidate: @"Please specify an alarm interval."]; return;
73    }
74    [self _setDateFromInterval];
75}
76
77- (void)_setIntervalFromDate;
78{
79    alarmInterval = [alarmDate timeIntervalSinceNow] + 1;
80    if (alarmInterval <= 0) {
81        [self _invalidate: @"Please specify an alarm time in the future."];
82        return;
83    }
84    [self _validateForType: PSAlarmDate];
85}
86
87- (void)setForDateAtTime:(NSCalendarDate *)dateTime;
88{
89    [self _setAlarmDate: dateTime];
90    [self _setIntervalFromDate];
91}
92
93- (void)setForDate:(NSDate *)date atTime:(NSDate *)time;
94{
95    NSCalendarDate *calTime, *calDate;
96    if (time == nil && date == nil) {
97        [self _invalidate: @"Please specify an alarm date and time."]; return;
98    }
99    if (time == nil) {
100        [self _invalidate: @"Please specify an alarm time."]; return;
101    }
102    if (date == nil) {
103        [self _invalidate: @"Please specify an alarm date."]; return;
104    }
105    // XXX if calTime's date is different from the default date, complain
106    calTime = [NSCalendarDate dateWithTimeIntervalSinceReferenceDate: [time timeIntervalSinceReferenceDate]];
107    calDate = [NSCalendarDate dateWithTimeIntervalSinceReferenceDate: [date timeIntervalSinceReferenceDate]];
108    if (calTime == nil || calDate == nil) {
109        [self _invalidate: @"Please specify a reasonable date and time."];
110    }
111    [self setForDateAtTime:
112        [[[NSCalendarDate alloc] initWithYear: [calDate yearOfCommonEra]
113                                        month: [calDate monthOfYear]
114                                          day: [calDate dayOfMonth]
115                                         hour: [calTime hourOfDay]
116                                       minute: [calTime minuteOfHour]
117                                       second: [calTime secondOfMinute]
118                                     timeZone: nil] autorelease]];
119}
120
121- (BOOL)isValid;
122{
123    if (alarmType == PSAlarmDate) [self _setIntervalFromDate];
124    return (alarmType != PSAlarmInvalid);
125}
126
127- (void)setMessage:(NSString *)aMessage;
128{
129    if (aMessage != alarmMessage) {
130        [alarmMessage release];
131        alarmMessage = nil;
132        alarmMessage = [aMessage retain];
133    }
134}
135
136- (NSString *)message;
137{
138    if (alarmMessage == nil || [alarmMessage isEqualToString: @""])
139        return @"Alarm!";
140    return alarmMessage;   
141}
142
143- (NSString *)invalidMessage;
144{
145    if (invalidMessage == nil) return @"";
146    return invalidMessage;
147}
148
149- (NSCalendarDate *)date;
150{
151    if (alarmType == PSAlarmInterval) [self _setDateFromInterval];
152    return alarmDate;
153}
154
155- (NSString *)shortDateString;
156{
157    return [[self date] descriptionWithCalendarFormat: [[NSUserDefaults standardUserDefaults] stringForKey: NSShortDateFormatString]];
158}
159
160- (NSString *)timeString;
161{
162    return [[self date] descriptionWithCalendarFormat: @"%1I:%M:%S %p"]; // XXX regular format doesn't work
163}
164
165- (NSString *)timeRemainingString;
166{
167    static const unsigned long long minute = 60, hour = minute * 60, day = hour * 24, year = day * 365.26;
168    unsigned long long interval = [self interval];
169    // +[NSString stringWithFormat:] in 10.1 does not support long longs: work around it by converting to unsigned ints or longs for display
170    if (interval == 0) return @"ÇexpiredÈ";
171    if (interval < minute) return [NSString stringWithFormat: @"%us", (unsigned)interval];
172    if (interval < day) return [NSString stringWithFormat: @"%uh %um", (unsigned)(interval / hour), (unsigned)((interval % hour) / minute)];
173    if (interval < year) return [NSString stringWithFormat: @"%u days", (unsigned)(interval / day)];
174    if (interval < 2 * year) return @"One year";
175    return [NSString stringWithFormat: @"%lu years", (unsigned long)(interval / year)];
176}
177
178- (NSTimeInterval)interval;
179{
180    if (alarmType == PSAlarmSet || alarmType == PSAlarmDate) [self _setIntervalFromDate];
181    return alarmInterval;
182}
183
184- (BOOL)setTimer;
185{
186    switch (alarmType) {
187        case PSAlarmDate: if (![self isValid]) return NO;
188        case PSAlarmInterval:
189            timer = [NSTimer scheduledTimerWithTimeInterval: alarmInterval
190                                                     target: self
191                                                   selector: @selector(_timerExpired:)
192                                                   userInfo: nil
193                                                    repeats: NO];
194            if (timer != nil) {
195                [timer retain];
196                alarmType = PSAlarmSet;
197                [[NSNotificationCenter defaultCenter] postNotificationName: PSAlarmTimerSetNotification object: self];
198                return YES;
199            }
200        default:
201            return NO;
202    }
203}
204
205- (void)cancel;
206{
207    [timer invalidate]; [timer release]; timer = nil;
208}
209
210- (void)_timerExpired:(NSTimer *)aTimer;
211{
212    [[NSNotificationCenter defaultCenter] postNotificationName: PSAlarmTimerExpiredNotification object: self];
213    [timer release]; timer = nil;
214}
215
216- (NSString *)_alarmTypeString;
217{
218    switch (alarmType) {
219        case PSAlarmDate: return @"PSAlarmDate";
220        case PSAlarmInterval: return @"PSAlarmInterval";
221        case PSAlarmSet: return @"PSAlarmSet";
222        case PSAlarmInvalid: return @"PSAlarmInvalid";
223        default: return [NSString stringWithFormat: @"<unknown: %u>", alarmType];
224    }
225}
226
227- (NSComparisonResult)compare:(PSAlarm *)otherAlarm;
228{
229    return [[self date] compare: [otherAlarm date]];
230}
231
232- (void)addAlert:(PSAlert *)alert;
233{
234    if (alerts == nil) alerts = [[NSMutableArray alloc] initWithCapacity: 4];
235    [alerts addObject: alert];
236}
237
238- (void)removeAlerts;
239{
240    [alerts removeAllObjects];
241}
242
243- (NSArray *)alerts;
244{
245    return [[alerts copy] autorelease];
246}
247
248- (NSString *)description;
249{
250    return [NSString stringWithFormat: @"%@: type %@ date %@ interval %.1f%@",
251        [super description], [self _alarmTypeString], alarmDate, alarmInterval,
252        (alarmType == PSAlarmInvalid ?
253         [NSString stringWithFormat: @"\ninvalid message: %@", invalidMessage]
254        : (alarmType == PSAlarmSet ?
255           [NSString stringWithFormat: @"\ntimer: %@", timer] : @""))];
256}
257
258- (void)encodeWithCoder:(NSCoder *)coder;
259{
260    if (![self isValid]) return;
261    [coder encodeValueOfObjCType: @encode(PSAlarmType) at: &alarmType];
262    switch (alarmType) {
263        case PSAlarmDate:
264        case PSAlarmSet:
265            [coder encodeObject: alarmDate];
266            break;
267        case PSAlarmInterval:
268            [coder encodeValueOfObjCType: @encode(NSTimeInterval) at: &alarmInterval];
269            break;
270        default:
271            break;
272    }
273    [coder encodeObject: alarmMessage];
274    // NSLog(@"encoded: %@", self); // XXX happening twice, gdb refuses to show proper backtrace, grr
275    return;
276}
277
278- (id)initWithCoder:(NSCoder *)coder;
279{
280    if ( (self = [super init]) != nil) {
281        [coder decodeValueOfObjCType: @encode(PSAlarmType) at: &alarmType];
282        switch (alarmType) {
283            case PSAlarmDate:
284            case PSAlarmSet:
285                [self _setAlarmDate: [coder decodeObject]];
286                break;
287            case PSAlarmInterval:
288                [coder decodeValueOfObjCType: @encode(NSTimeInterval) at: &alarmInterval];
289                break;
290            default:
291                break;
292        }
293        [self setMessage: [coder decodeObject]];
294        if (alarmType == PSAlarmSet) {
295            alarmType = PSAlarmDate;
296            [self setTimer];
297        }
298    }
299    return self;
300}
301
302@end
Note: See TracBrowser for help on using the repository browser.