Ignore:
Timestamp:
01/02/03 05:30:03 (20 years ago)
Author:
Nicholas Riley
Message:

Updated for Pester 1.1a5 (very limited release).

Pester 1.1a4 was never released.

Location:
trunk/Cocoa/Pester/Source
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/Cocoa/Pester/Source

    • Property svn:ignore
      •  

        old new  
        11build
         2.gdb_history
  • trunk/Cocoa/Pester/Source/PSAlarm.m

    r51 r53  
    99#import "PSAlarm.h"
    1010#import "PSAlert.h"
     11#import "PSAlerts.h"
    1112#import "NJRDateFormatter.h"
     13#import "NSCalendarDate-NJRExtensions.h"
     14#import "NSDictionary-NJRExtensions.h"
     15#import "NSString-NJRExtensions.h"
    1216
    1317NSString * const PSAlarmTimerSetNotification = @"PSAlarmTimerSetNotification";
    1418NSString * const PSAlarmTimerExpiredNotification = @"PSAlarmTimerExpiredNotification";
     19NSString * const PSAlarmDiedNotification = @"PSAlarmDiedNotification";
     20
     21// property list keys
     22static NSString * const PLAlarmType = @"type"; // NSString
     23static NSString * const PLAlarmDate = @"date"; // NSNumber
     24static NSString * const PLAlarmInterval = @"interval"; // NSNumber
     25static NSString * const PLAlarmSnoozeInterval = @"snooze interval"; // NSNumber
     26static NSString * const PLAlarmMessage = @"message"; // NSString
     27static NSString * const PLAlarmAlerts = @"alerts"; // NSDictionary
     28static NSString * const PLAlarmRepeating = @"repeating"; // NSNumber
    1529
    1630static NSString *dateFormat, *shortDateFormat, *timeFormat;
     
    7286    invalidMessage = nil;
    7387    alarmType = type;
     88    if (type != PSAlarmInterval) [self setRepeating: NO];
    7489}
    7590
    7691- (void)_setDateFromInterval;
    7792{
    78     [alarmDate release]; alarmDate = nil;
    79     alarmDate = [NSCalendarDate dateWithTimeIntervalSinceNow: alarmInterval];
    80     [alarmDate retain];
     93    [self _setAlarmDate: [NSCalendarDate dateWithTimeIntervalSinceNow: alarmInterval]];
    8194    [self _beValidWithType: PSAlarmInterval];
    8295}
     
    8497- (void)_setIntervalFromDate;
    8598{
    86     alarmInterval = [alarmDate timeIntervalSinceNow] + 1;
     99    alarmInterval = [alarmDate timeIntervalSinceNow];
    87100    if (alarmInterval <= 0) {
    88101        [self _beInvalid: @"Please specify an alarm time in the future."];
     
    90103    }
    91104    [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;
    92117}
    93118
     
    99124        case PSAlarmSet: return @"PSAlarmSet";
    100125        case PSAlarmInvalid: return @"PSAlarmInvalid";
     126        case PSAlarmSnooze: return @"PSAlarmSnooze";
     127        case PSAlarmExpired: return @"PSAlarmExpired";
    101128        default: return [NSString stringWithFormat: @"<unknown: %u>", alarmType];
    102129    }
    103130}
    104131
     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
    105145- (void)_timerExpired:(NSTimer *)aTimer;
    106146{
     147    NSLog(@"expired: %@; now %@", [[aTimer fireDate] description], [[NSDate date] description]);
     148    alarmType = PSAlarmExpired;
    107149    [[NSNotificationCenter defaultCenter] postNotificationName: PSAlarmTimerExpiredNotification object: self];
    108150    [timer release]; timer = nil;
     
    128170- (void)setForDate:(NSDate *)date atTime:(NSDate *)time;
    129171{
    130     NSCalendarDate *calTime, *calDate;
     172    NSCalendarDate *dateTime;
    131173    if (time == nil && date == nil) {
    132174        [self _beInvalid: @"Please specify an alarm date and time."]; return;
     
    139181    }
    140182    // 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) {
    144185        [self _beInvalid: @"Please specify a reasonable date and time."];
    145186    }
    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;
    154200}
    155201
     
    175221{
    176222    if (alarmType == PSAlarmDate) [self _setIntervalFromDate];
    177     return (alarmType != PSAlarmInvalid);
     223    if (alarmType == PSAlarmInvalid ||
     224        (alarmType == PSAlarmExpired && ![self isRepeating])) return NO;
     225    return YES;
    178226}
    179227
     
    204252- (NSTimeInterval)interval;
    205253{
    206     if (alarmType == PSAlarmSet || alarmType == PSAlarmDate) [self _setIntervalFromDate];
     254    if (alarmType == PSAlarmDate) [self _setIntervalFromDate];
    207255    return alarmInterval;
    208256}
    209257
    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;
    224284}
    225285
     
    239299}
    240300
     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
    241336- (NSString *)timeRemainingString;
    242337{
    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];
    252361}
    253362
     
    256365- (BOOL)setTimer;
    257366{
    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];
    269372            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;
    271388}
    272389
     
    274391{
    275392    [timer invalidate]; [timer release]; timer = nil;
     393    [self setRepeating: NO];
    276394}
    277395
     
    300418}
    301419
    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)
    303496
    304497- (void)encodeWithCoder:(NSCoder *)coder;
     
    324517- (id)initWithCoder:(NSCoder *)coder;
    325518{
    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];
    327523        [coder decodeValueOfObjCType: @encode(PSAlarmType) at: &alarmType];
    328524        switch (alarmType) {
Note: See TracChangeset for help on using the changeset viewer.