source: releases/Pester/1.1a3/Source/PSAlarm.m@ 114

Last change on this file since 114 was 43, checked in by Nicholas Riley, 22 years ago

Pester 1.1a1.

English.lproj/InfoPlist.strings: Updated for 1.1a1.

English.lproj/MainMenu.nib: Placeholder for day names in popup menu, fixed up by code (this means you can still edit it from IB though). Added command-shift-T to both in/at cells (required, code removes one or the other as appropriate). Fixed up sizes of fields. Default to today (this will need fixing when we localized the word "today", but it's fine for now...).

English.lproj/Notifier.nib: Remove date formatter because we set a string directly now instead (could set formatter from code, but we don't).

NJRDateFormatter: many workarounds for Cocoa bugs: missing AM/PM, incorrect results with space before AM/PM, etc. Added class methods to do format manipulation and return localized formats which work for output (though not always for input; this class has an internal workaround for the AM/PM problem).

NJRFSObjectSelector: properly handle enabled attribute, store internally and report externally as appropriate. Previously, the button would become enabled if you dropped something on it even if it was supposed to be disabled.

NJRQTMediaPopUpButton: stop sound preview when button disabled.

NJRVoicePopUpButton: stop voice preview when button disabled.

PSAlarm: new method -dateString returns long date string. Maintain local copy of long date, short date and time formats, and locale, using NJRDateFormatter.

PSAlarmNotifierController: update to use -[PSAlarm dateString], -[PSAlarm timeString] for localization instead of using broken formatter.

PSAlarmSetController: update documentation for some more Cocoa bugs I need to file. Set time of day and date formatters with localized date formats from NJRDateFormatter (retain/release issue here?) Localize weekday popup for predefined dates. Localize static date display with NJRDateFormatter. Note a solution (thanks to Douglas Davidson) for figuring out which control is editing. Added command-shift-T key equivalent to toggle in/at. Properly work around bugs witih soundRepetitionCount flashing, except where it's impossible to do anything else.

Read Me.rtfd: Updated for 1.1a1.

VERSION: Updated for 1.1a1.

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