Changeset 53 for trunk/Cocoa/Pester/Source/PSAlarm.m
- Timestamp:
- 01/02/03 05:30:03 (21 years ago)
- Location:
- trunk/Cocoa/Pester/Source
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Cocoa/Pester/Source
- Property svn:ignore
-
old new 1 1 build 2 .gdb_history
-
- Property svn:ignore
-
trunk/Cocoa/Pester/Source/PSAlarm.m
r51 r53 9 9 #import "PSAlarm.h" 10 10 #import "PSAlert.h" 11 #import "PSAlerts.h" 11 12 #import "NJRDateFormatter.h" 13 #import "NSCalendarDate-NJRExtensions.h" 14 #import "NSDictionary-NJRExtensions.h" 15 #import "NSString-NJRExtensions.h" 12 16 13 17 NSString * const PSAlarmTimerSetNotification = @"PSAlarmTimerSetNotification"; 14 18 NSString * const PSAlarmTimerExpiredNotification = @"PSAlarmTimerExpiredNotification"; 19 NSString * const PSAlarmDiedNotification = @"PSAlarmDiedNotification"; 20 21 // property list keys 22 static NSString * const PLAlarmType = @"type"; // NSString 23 static NSString * const PLAlarmDate = @"date"; // NSNumber 24 static NSString * const PLAlarmInterval = @"interval"; // NSNumber 25 static NSString * const PLAlarmSnoozeInterval = @"snooze interval"; // NSNumber 26 static NSString * const PLAlarmMessage = @"message"; // NSString 27 static NSString * const PLAlarmAlerts = @"alerts"; // NSDictionary 28 static NSString * const PLAlarmRepeating = @"repeating"; // NSNumber 15 29 16 30 static NSString *dateFormat, *shortDateFormat, *timeFormat; … … 72 86 invalidMessage = nil; 73 87 alarmType = type; 88 if (type != PSAlarmInterval) [self setRepeating: NO]; 74 89 } 75 90 76 91 - (void)_setDateFromInterval; 77 92 { 78 [alarmDate release]; alarmDate = nil; 79 alarmDate = [NSCalendarDate dateWithTimeIntervalSinceNow: alarmInterval]; 80 [alarmDate retain]; 93 [self _setAlarmDate: [NSCalendarDate dateWithTimeIntervalSinceNow: alarmInterval]]; 81 94 [self _beValidWithType: PSAlarmInterval]; 82 95 } … … 84 97 - (void)_setIntervalFromDate; 85 98 { 86 alarmInterval = [alarmDate timeIntervalSinceNow] + 1;99 alarmInterval = [alarmDate timeIntervalSinceNow]; 87 100 if (alarmInterval <= 0) { 88 101 [self _beInvalid: @"Please specify an alarm time in the future."]; … … 90 103 } 91 104 [self _beValidWithType: PSAlarmDate]; 105 } 106 107 - (PSAlarmType)_alarmTypeForString:(NSString *)string; 108 { 109 if ([string isEqualToString: @"PSAlarmDate"]) return PSAlarmDate; 110 if ([string isEqualToString: @"PSAlarmInterval"]) return PSAlarmInterval; 111 if ([string isEqualToString: @"PSAlarmSet"]) return PSAlarmSet; 112 if ([string isEqualToString: @"PSAlarmInvalid"]) return PSAlarmInvalid; 113 if ([string isEqualToString: @"PSAlarmSnooze"]) return PSAlarmSnooze; 114 if ([string isEqualToString: @"PSAlarmExpired"]) return PSAlarmExpired; 115 NSLog(@"unknown alarm type string: %@", string); 116 return nil; 92 117 } 93 118 … … 99 124 case PSAlarmSet: return @"PSAlarmSet"; 100 125 case PSAlarmInvalid: return @"PSAlarmInvalid"; 126 case PSAlarmSnooze: return @"PSAlarmSnooze"; 127 case PSAlarmExpired: return @"PSAlarmExpired"; 101 128 default: return [NSString stringWithFormat: @"<unknown: %u>", alarmType]; 102 129 } 103 130 } 104 131 132 - (NSString *)_stringForInterval:(unsigned long long)interval; 133 { 134 const unsigned long long minute = 60, hour = minute * 60, day = hour * 24, year = day * 365.26; 135 // +[NSString stringWithFormat:] in 10.1 does not support long longs: work around it by converting to unsigned ints or longs for display 136 if (interval == 0) return nil; 137 if (interval < minute) return [NSString stringWithFormat: @"%us", (unsigned)interval]; 138 if (interval < day) return [NSString stringWithFormat: @"%uh %um", (unsigned)(interval / hour), (unsigned)((interval % hour) / minute)]; 139 if (interval < 2 * day) return @"One day"; 140 if (interval < year) return [NSString stringWithFormat: @"%u days", (unsigned)(interval / day)]; 141 if (interval < 2 * year) return @"One year"; 142 return [NSString stringWithFormat: @"%lu years", (unsigned long)(interval / year)]; 143 } 144 105 145 - (void)_timerExpired:(NSTimer *)aTimer; 106 146 { 147 NSLog(@"expired: %@; now %@", [[aTimer fireDate] description], [[NSDate date] description]); 148 alarmType = PSAlarmExpired; 107 149 [[NSNotificationCenter defaultCenter] postNotificationName: PSAlarmTimerExpiredNotification object: self]; 108 150 [timer release]; timer = nil; … … 128 170 - (void)setForDate:(NSDate *)date atTime:(NSDate *)time; 129 171 { 130 NSCalendarDate * calTime, *calDate;172 NSCalendarDate *dateTime; 131 173 if (time == nil && date == nil) { 132 174 [self _beInvalid: @"Please specify an alarm date and time."]; return; … … 139 181 } 140 182 // XXX if calTime's date is different from the default date, complain 141 calTime = [NSCalendarDate dateWithTimeIntervalSinceReferenceDate: [time timeIntervalSinceReferenceDate]]; 142 calDate = [NSCalendarDate dateWithTimeIntervalSinceReferenceDate: [date timeIntervalSinceReferenceDate]]; 143 if (calTime == nil || calDate == nil) { 183 dateTime = [NSCalendarDate dateWithDate: date atTime: time]; 184 if (dateTime == nil) { 144 185 [self _beInvalid: @"Please specify a reasonable date and time."]; 145 186 } 146 [self setForDateAtTime: 147 [[[NSCalendarDate alloc] initWithYear: [calDate yearOfCommonEra] 148 month: [calDate monthOfYear] 149 day: [calDate dayOfMonth] 150 hour: [calTime hourOfDay] 151 minute: [calTime minuteOfHour] 152 second: [calTime secondOfMinute] 153 timeZone: nil] autorelease]]; 187 [self setForDateAtTime: dateTime]; 188 } 189 190 - (void)setRepeating:(BOOL)isRepeating; 191 { 192 repeating = isRepeating; 193 } 194 195 - (void)setSnoozeInterval:(NSTimeInterval)anInterval; 196 { 197 snoozeInterval = anInterval; 198 NSAssert(alarmType == PSAlarmExpired, @"CanÕt snooze an alarm that hasnÕt expired"); 199 alarmType = PSAlarmSnooze; 154 200 } 155 201 … … 175 221 { 176 222 if (alarmType == PSAlarmDate) [self _setIntervalFromDate]; 177 return (alarmType != PSAlarmInvalid); 223 if (alarmType == PSAlarmInvalid || 224 (alarmType == PSAlarmExpired && ![self isRepeating])) return NO; 225 return YES; 178 226 } 179 227 … … 204 252 - (NSTimeInterval)interval; 205 253 { 206 if (alarmType == PSAlarm Set || alarmType == PSAlarmDate) [self _setIntervalFromDate];254 if (alarmType == PSAlarmDate) [self _setIntervalFromDate]; 207 255 return alarmInterval; 208 256 } 209 257 210 - (void)addAlert:(PSAlert *)alert; 211 { 212 if (alerts == nil) alerts = [[NSMutableArray alloc] initWithCapacity: 4]; 213 [alerts addObject: alert]; 214 } 215 216 - (void)removeAlerts; 217 { 218 [alerts removeAllObjects]; 219 } 220 221 - (NSArray *)alerts; 222 { 223 return [[alerts copy] autorelease]; 258 - (NSTimeInterval)snoozeInterval; 259 { 260 return snoozeInterval; 261 } 262 263 - (NSTimeInterval)timeRemaining; 264 { 265 NSAssert1(alarmType == PSAlarmSet, @"CanÕt get time remaining on alarm with no timer set: %@", self); 266 return -[[NSDate date] timeIntervalSinceDate: alarmDate]; 267 } 268 269 - (void)setAlerts:(PSAlerts *)theAlerts; 270 { 271 [alerts release]; alerts = nil; 272 alerts = [theAlerts retain]; 273 } 274 275 - (PSAlerts *)alerts; 276 { 277 if (alerts == nil) alerts = [[PSAlerts alloc] init]; 278 return alerts; 279 } 280 281 - (BOOL)isRepeating; 282 { 283 return repeating; 224 284 } 225 285 … … 239 299 } 240 300 301 - (NSString *)dateTimeString; 302 { 303 return [NSString stringWithFormat: @"%@ at %@", [self dateString], [self timeString]]; 304 } 305 306 - (NSString *)nextDateTimeString; 307 { 308 if (![self isRepeating]) { 309 return nil; 310 } else { 311 NSCalendarDate *date = [[NSCalendarDate alloc] initWithTimeIntervalSinceNow: [self interval]]; 312 NSString *nextDateTimeString = [NSString stringWithFormat: @"%@ at %@", 313 [date descriptionWithCalendarFormat: dateFormat locale: locale], 314 [date descriptionWithCalendarFormat: timeFormat locale: locale]]; 315 [date release]; 316 return nextDateTimeString; 317 } 318 } 319 320 - (NSString *)intervalString; 321 { 322 const unsigned long long mval = 99, minute = 60, hour = minute * 60; 323 unsigned long long interval = [self interval]; 324 if (interval == 0) return nil; 325 if (interval == 1) return @"One second"; 326 if (interval == minute) return @"One minute"; 327 if (interval % minute == 0) return [NSString stringWithFormat: @"%u minutes", (unsigned)(interval / minute)]; 328 if (interval <= mval) return [NSString stringWithFormat: @"%u seconds", (unsigned)interval]; 329 if (interval == hour) return @"One hour"; 330 if (interval % hour == 0) return [NSString stringWithFormat: @"%u hours", (unsigned)(interval / hour)]; 331 if (interval <= mval * minute) return [NSString stringWithFormat: @"%u minutes", (unsigned)(interval / minute)]; 332 if (interval <= mval * hour) return [NSString stringWithFormat: @"%u hours", (unsigned)(interval / hour)]; 333 return [self _stringForInterval: interval]; 334 } 335 241 336 - (NSString *)timeRemainingString; 242 337 { 243 static const unsigned long long minute = 60, hour = minute * 60, day = hour * 24, year = day * 365.26; 244 unsigned long long interval = [self interval]; 245 // +[NSString stringWithFormat:] in 10.1 does not support long longs: work around it by converting to unsigned ints or longs for display 246 if (interval == 0) return @"ÇexpiredÈ"; 247 if (interval < minute) return [NSString stringWithFormat: @"%us", (unsigned)interval]; 248 if (interval < day) return [NSString stringWithFormat: @"%uh %um", (unsigned)(interval / hour), (unsigned)((interval % hour) / minute)]; 249 if (interval < year) return [NSString stringWithFormat: @"%u days", (unsigned)(interval / day)]; 250 if (interval < 2 * year) return @"One year"; 251 return [NSString stringWithFormat: @"%lu years", (unsigned long)(interval / year)]; 338 NSString *timeRemainingString = [self _stringForInterval: llround([self timeRemaining])]; 339 340 if (timeRemainingString == nil) return @"ÇexpiredÈ"; 341 return timeRemainingString; 342 } 343 344 - (NSAttributedString *)prettyDescription; 345 { 346 NSMutableAttributedString *string = [[NSMutableAttributedString alloc] init]; 347 NSAttributedString *alertList = [alerts prettyList]; 348 349 [string appendAttributedString: 350 [[NSString stringWithFormat: @"At alarm time for Ò%@Ó:\n", [self message]] small]]; 351 if (alertList != nil) { 352 [string appendAttributedString: alertList]; 353 } else { 354 [string appendAttributedString: [@"Do nothing." small]]; 355 } 356 if ([self isRepeating]) { 357 [string appendAttributedString: 358 [[NSString stringWithFormat: @"\nAlarm repeats every %@.", [[self intervalString] lowercaseString]] small]]; 359 } 360 return [string autorelease]; 252 361 } 253 362 … … 256 365 - (BOOL)setTimer; 257 366 { 258 switch (alarmType) { 259 case PSAlarmDate: if (![self isValid]) return NO; 260 case PSAlarmInterval: 261 timer = [NSTimer scheduledTimerWithTimeInterval: alarmInterval target: self selector: @selector(_timerExpired:) userInfo: nil repeats: NO]; 262 if (timer != nil) { 263 [timer retain]; 264 alarmType = PSAlarmSet; 265 [[NSNotificationCenter defaultCenter] postNotificationName: PSAlarmTimerSetNotification object: self]; 266 return YES; 267 } 268 default: 367 if (alarmType == PSAlarmExpired) { 368 if ([self isRepeating]) { 369 [self _setDateFromInterval]; 370 } else { 371 [[NSNotificationCenter defaultCenter] postNotificationName: PSAlarmDiedNotification object: self]; 269 372 return NO; 270 } 373 } 374 } else if (alarmType == PSAlarmDate) { 375 if (![self isValid]) return NO; 376 } else if (alarmType == PSAlarmSnooze) { 377 [self _setAlarmDate: [NSCalendarDate dateWithTimeIntervalSinceNow: snoozeInterval]]; 378 } else if (alarmType != PSAlarmInterval) { 379 return NO; 380 } 381 timer = [NSTimer scheduledTimerWithTimeInterval: (alarmType == PSAlarmSnooze ? snoozeInterval : alarmInterval) target: self selector: @selector(_timerExpired:) userInfo: nil repeats: NO]; 382 if (timer == nil) return NO; 383 [timer retain]; 384 alarmType = PSAlarmSet; 385 [[NSNotificationCenter defaultCenter] postNotificationName: PSAlarmTimerSetNotification object: self]; 386 // NSLog(@"set: %@; now %@; remaining %@", [[timer fireDate] description], [[NSDate date] description], [self timeRemainingString]); 387 return YES; 271 388 } 272 389 … … 274 391 { 275 392 [timer invalidate]; [timer release]; timer = nil; 393 [self setRepeating: NO]; 276 394 } 277 395 … … 300 418 } 301 419 302 #pragma mark archiving 420 #pragma mark property list serialization (Pester 1.1) 421 422 - (NSDictionary *)propertyListRepresentation; 423 { 424 NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity: 5]; 425 if (![self isValid]) return nil; 426 [dict setObject: [self _alarmTypeString] forKey: PLAlarmType]; 427 switch (alarmType) { 428 case PSAlarmDate: 429 case PSAlarmSet: 430 [dict setObject: [NSNumber numberWithDouble: [alarmDate timeIntervalSinceReferenceDate]] forKey: PLAlarmDate]; 431 break; 432 case PSAlarmSnooze: 433 case PSAlarmInterval: 434 case PSAlarmExpired: 435 [dict setObject: [NSNumber numberWithDouble: alarmInterval] forKey: PLAlarmInterval]; 436 [dict setObject: [NSNumber numberWithBool: repeating] forKey: PLAlarmRepeating]; 437 break; 438 default: 439 NSAssert1(NO, @"CanÕt save alarm type %@", [self _alarmTypeString]); 440 break; 441 } 442 if (snoozeInterval != 0) 443 [dict setObject: [NSNumber numberWithDouble: snoozeInterval] forKey: PLAlarmSnoozeInterval]; 444 [dict setObject: alarmMessage forKey: PLAlarmMessage]; 445 if (alerts != nil) { 446 [dict setObject: [alerts propertyListRepresentation] forKey: PLAlarmAlerts]; 447 } 448 return dict; 449 } 450 451 - (id)initWithPropertyList:(NSDictionary *)dict; 452 { 453 if ( (self = [self init]) != nil) { 454 PSAlerts *alarmAlerts; 455 alarmType = [self _alarmTypeForString: [dict objectForRequiredKey: PLAlarmType]]; 456 switch (alarmType) { 457 case PSAlarmDate: 458 case PSAlarmSet: 459 { NSCalendarDate *date = [[NSCalendarDate alloc] initWithTimeIntervalSinceReferenceDate: [[dict objectForRequiredKey: PLAlarmDate] doubleValue]]; 460 [self _setAlarmDate: date]; 461 [date release]; 462 } 463 break; 464 case PSAlarmSnooze: // snooze interval set but not confirmed; ignore 465 alarmType = PSAlarmExpired; 466 case PSAlarmInterval: 467 case PSAlarmExpired: 468 alarmInterval = [[dict objectForRequiredKey: PLAlarmInterval] doubleValue]; 469 repeating = [[dict objectForRequiredKey: PLAlarmRepeating] boolValue]; 470 break; 471 default: 472 NSAssert1(NO, @"CanÕt load alarm type %@", [self _alarmTypeString]); 473 break; 474 } 475 snoozeInterval = [[dict objectForKey: PLAlarmSnoozeInterval] doubleValue]; 476 [self setMessage: [dict objectForRequiredKey: PLAlarmMessage]]; 477 alarmAlerts = [[PSAlerts alloc] initWithPropertyList: [dict objectForRequiredKey: PLAlarmAlerts]]; 478 [self setAlerts: alarmAlerts]; 479 [alarmAlerts release]; 480 if (alarmType == PSAlarmSet) { 481 alarmType = PSAlarmDate; 482 [self setTimer]; 483 } 484 if (alarmType == PSAlarmExpired) { 485 [self setTimer]; 486 if (alarmType == PSAlarmExpired) { // failed to restart 487 [self release]; 488 self = nil; 489 } 490 } 491 } 492 return self; 493 } 494 495 #pragma mark archiving (Pester 1.0) 303 496 304 497 - (void)encodeWithCoder:(NSCoder *)coder; … … 324 517 - (id)initWithCoder:(NSCoder *)coder; 325 518 { 326 if ( (self = [super init]) != nil) { 519 if ( (self = [self init]) != nil) { 520 PSAlerts *legacyAlerts = [[PSAlerts alloc] initWithPesterVersion1Alerts]; 521 [self setAlerts: legacyAlerts]; 522 [legacyAlerts release]; 327 523 [coder decodeValueOfObjCType: @encode(PSAlarmType) at: &alarmType]; 328 524 switch (alarmType) {
Note:
See TracChangeset
for help on using the changeset viewer.