Ignore:
Timestamp:
01/02/03 05:30:03 (21 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/PSAlarmSetController.m

    r51 r53  
    99#import "PSAlarmSetController.h"
    1010#import "PSAlarmAlertController.h"
     11#import "PSPowerManager.h"
    1112#import "NJRDateFormatter.h"
    1213#import "NJRFSObjectSelector.h"
     14#import "NJRIntervalField.h"
    1315#import "NJRQTMediaPopUpButton.h"
    1416#import "NJRVoicePopUpButton.h"
     17#import "NSString-NJRExtensions.h"
     18#import "NSAttributedString-NJRExtensions.h"
     19#import "NSCalendarDate-NJRExtensions.h"
    1520#import <Carbon/Carbon.h>
    1621
     22#import "PSAlerts.h"
    1723#import "PSDockBounceAlert.h"
    1824#import "PSScriptAlert.h"
     
    3844¥ If you feed NSCalendarDate dateWithNaturalLanguageString: an " AM"/" PM" locale, it doesn't accept that date format.
    3945¥ descriptions for %X and %x are reversed (time zone is in %X, not %x)
     46¥ NSComboBox data source issues, canÕt have it appear as ÒtodayÓ because the formatter doesnÕt like that.  Should be able to enter text into the data source and have the formatter process it without altering it.
    4047¥ too hard to implement date-only or time-only formatters
    4148¥ should be able to specify that natural language favors date or time (10 = 10th of month, not 10am)
     
    4451*/
    4552
     53static NSString * const PSAlertsSelected = @"Pester alerts selected"; // NSUserDefaults key
     54static NSString * const PSAlertsEditing = @"Pester alerts editing"; // NSUserDefaults key
     55
    4656@interface PSAlarmSetController (Private)
    4757
     58- (void)_readAlerts:(PSAlerts *)alerts;
     59- (BOOL)_setAlerts;
    4860- (void)_stopUpdateTimer;
    4961
     
    5466- (void)awakeFromNib;
    5567{
     68    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    5669    alarm = [[PSAlarm alloc] init];
    5770    [[self window] center];
     
    6073    [timeDate setFormatter: [[NJRDateFormatter alloc] initWithDateFormat: [NJRDateFormatter localizedDateFormatIncludingWeekday: NO] allowNaturalLanguage: YES]];
    6174    {
    62         NSArray *dayNames = [[NSUserDefaults standardUserDefaults] arrayForKey:
     75        NSArray *dayNames = [defaults arrayForKey:
    6376            NSWeekDayNameArray];
    6477        NSArray *completions = [timeDateCompletions itemTitles];
     
    8396        }
    8497    }
     98    [editAlert setIntValue: [defaults boolForKey: PSAlertsEditing]];
     99    {
     100        NSDictionary *plAlerts = [defaults dictionaryForKey: PSAlertsSelected];
     101        PSAlerts *alerts;
     102        if (plAlerts == nil) {
     103            alerts = [[PSAlerts alloc] initWithPesterVersion1Alerts];
     104        } else {
     105            NS_DURING
     106                alerts = [[PSAlerts alloc] initWithPropertyList: plAlerts];
     107            NS_HANDLER
     108                NSRunAlertPanel(@"Unable to restore alerts", @"Pester could not restore recent alert information for one or more alerts in the Set Alarm window.  The default set of alerts will be used instead.\n\n%@", nil, nil, nil, [localException reason]);
     109                alerts = [[PSAlerts alloc] initWithPesterVersion1Alerts];
     110            NS_ENDHANDLER
     111        }
     112        [self _readAlerts: alerts];
     113    }
    85114    [timeDate setObjectValue: [NSDate date]];
    86     [self inAtChanged: nil];
     115    [self inAtChanged: nil]; // by convention, if sender is nil, we're initializing
    87116    [self playSoundChanged: nil];
    88117    [self doScriptChanged: nil];
    89118    [self doSpeakChanged: nil];
     119    [self editAlertChanged: nil];
    90120    [script setFileTypes: [NSArray arrayWithObjects: @"applescript", @"script", NSFileTypeForHFSTypeCode(kOSAFileType), NSFileTypeForHFSTypeCode('TEXT'), nil]];
    91121    [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(silence:) name: PSAlarmAlertStopNotification object: nil];
    92122    [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(playSoundChanged:) name: NJRQTMediaPopUpButtonMovieChangedNotification object: sound];
    93     [voice setDelegate: self];
    94     // XXX still broken under 10.2, check 10.1 behavior and see if subclassing NSComboBox will help
    95     // if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_1) {
    96         // XXX workaround for 10.1.x bug which sets the first responder to the wrong field, but it works if I set the initial first responder to nil... go figure.
    97         [[self window] setInitialFirstResponder: nil];
    98     // }
     123    [voice setDelegate: self]; // XXX why don't we do this in IB?  It should use the accessor...
     124    [wakeUp setEnabled: [PSPowerManager autoWakeSupported]];
     125    // XXX workaround for 10.1.x and 10.2.x bug which sets the first responder to the wrong field alternately, but it works if I set the initial first responder to nil... go figure.
     126    [[self window] setInitialFirstResponder: nil];
    99127    [[self window] makeKeyAndOrderFront: nil];
    100128}
     
    110138}
    111139
     140// XXX with -[NSControl currentEditor] don't need to compare?  Also check -[NSControl validateEditing]
    112141- (id)objectValueForTextField:(NSTextField *)field whileEditing:(id)sender;
    113142{
     
    124153}
    125154
     155#pragma mark date/interval setting
     156
    126157- (void)setAlarmDateAndInterval:(id)sender;
    127158{
    128159    if (isInterval) {
    129         [alarm setInterval:
    130             [[self objectValueForTextField: timeInterval whileEditing: sender] intValue] *
    131                 [timeIntervalUnits selectedTag]];
     160        [alarm setInterval: [timeInterval interval]];
    132161    } else {
    133162        [alarm setForDate: [self objectValueForTextField: timeDate whileEditing: sender]
     
    141170}
    142171
    143 // XXX use OACalendar?
     172// XXX use OACalendar in popup like Palm Desktop?
    144173
    145174- (IBAction)updateDateDisplay:(id)sender;
     
    193222    [timeDateCompletions setEnabled: !isInterval];
    194223    if (sender != nil)
    195         [[self window] makeFirstResponder: isInterval ? timeInterval : timeOfDay];
     224        [[self window] makeFirstResponder: isInterval ? (NSTextField *)timeInterval : timeOfDay];
    196225    // NSLog(@"UPDATING FROM inAtChanged");
    197226    [self update: nil];
    198227}
     228
     229- (IBAction)dateCompleted:(NSPopUpButton *)sender;
     230{
     231    [timeDate setStringValue: [sender titleOfSelectedItem]];
     232    [self update: sender];
     233}
     234
     235#pragma mark alert editing
     236
     237- (IBAction)editAlertChanged:(id)sender;
     238{
     239    BOOL editAlertSelected = [editAlert intValue];
     240    NSView *editAlertControl = [editAlert controlView];
     241    NSWindow *window = [self window];
     242    NSRect frame = [window frame];
     243    if (editAlertSelected) {
     244        NSSize editWinSize = [window maxSize];
     245        [editAlertControl setNextKeyView: [displayMessage controlView]];
     246        frame.origin.y += frame.size.height - editWinSize.height;
     247        frame.size = editWinSize;
     248        [window setFrame: frame display: (sender != nil) animate: (sender != nil)];
     249        [self updateDateDisplay: sender];
     250        [alertTabs selectTabViewItemWithIdentifier: @"edit"];
     251    } else {
     252        NSSize viewWinSize = [window minSize];
     253        NSRect textFrame = [alertView frame];
     254        float textHeight;
     255        if (![self _setAlerts]) {
     256            [alertView setStringValue: [NSString stringWithFormat: @"CouldnÕt process alert information.\n%@", status]];
     257        } else {
     258            NSAttributedString *string = [[alarm alerts] prettyList];
     259            if (string == nil) {
     260                [alertView setStringValue: @"Do nothing. Click the button labeled ÒEditÓ to add an alert."];
     261            } else {
     262                [alertView setAttributedStringValue: string];
     263                [self updateDateDisplay: sender];
     264            }
     265        }
     266        if (sender != nil) { // nil == we're initializing, don't mess with focus
     267            NSResponder *oldResponder = [window firstResponder];
     268            // make sure focus doesn't get stuck in the edit tab: it is confusing and leaves behind artifacts
     269            if (oldResponder == editAlertControl || [oldResponder isKindOfClass: [NSView class]] && [(NSView *)oldResponder isDescendantOf: alertTabs])
     270                [window makeFirstResponder: messageField]; // would use editAlertControl, but can't get it to display anomaly-free.
     271            [self silence: sender];
     272        }
     273        // allow height to expand, though not arbitrarily (should still fit on an 800x600 screen)
     274        textHeight = [[alertView cell] cellSizeForBounds: NSMakeRect(0, 0, textFrame.size.width, 400)].height;
     275        textFrame.origin.y += textFrame.size.height - textHeight;
     276        textFrame.size.height = textHeight;
     277        [alertView setFrame: textFrame];
     278        viewWinSize.height += textHeight;
     279        [alertTabs selectTabViewItemWithIdentifier: @"view"];
     280        frame.origin.y += frame.size.height - viewWinSize.height;
     281        frame.size = viewWinSize;
     282        [window setFrame: frame display: (sender != nil) animate: (sender != nil)];
     283        [editAlertControl setNextKeyView: cancelButton];
     284    }
     285    if (sender != nil) {
     286        [[NSUserDefaults standardUserDefaults] setBool: editAlertSelected forKey: PSAlertsEditing];
     287    }
     288}
     289
    199290
    200291- (IBAction)playSoundChanged:(id)sender;
     
    206297    [soundRepetitionStepper setEnabled: canRepeat];
    207298    [soundRepetitionsLabel setTextColor: canRepeat ? [NSColor controlTextColor] : [NSColor disabledControlTextColor]];
    208     if (playSoundSelected && sender != nil)
     299    if (playSoundSelected && sender == playSound) {
    209300        [[self window] makeFirstResponder: sound];
     301    }
    210302}
    211303
     
    234326    [script setEnabled: doScriptSelected];
    235327    [scriptSelectButton setEnabled: doScriptSelected];
    236     if (doScriptSelected && sender != nil)
     328    if (doScriptSelected && sender != nil) {
    237329        [[self window] makeFirstResponder: scriptSelectButton];
     330        if ([script alias] == nil) [scriptSelectButton performClick: sender];
     331    }
    238332}
    239333
    240334- (IBAction)doSpeakChanged:(id)sender;
    241335{
    242     BOOL doSpeakSelected = [doSpeak intValue];
     336    BOOL doSpeakSelected = [doSpeak state] == NSOnState;
    243337    [voice setEnabled: doSpeakSelected];
    244338    if (doSpeakSelected && sender != nil)
     
    246340}
    247341
    248 - (IBAction)dateCompleted:(NSPopUpButton *)sender;
    249 {
    250     [timeDate setStringValue: [sender titleOfSelectedItem]];
    251     [self update: sender];
    252 }
     342- (void)_readAlerts:(PSAlerts *)alerts;
     343{
     344    NSEnumerator *e = [alerts alertEnumerator];
     345    PSAlert *alert;
     346   
     347    [alarm setAlerts: alerts];
     348
     349    // turn off all alerts
     350    [bounceDockIcon setState: NSOffState];
     351    [doScript setIntValue: NO];
     352    [displayMessage setIntValue: NO];
     353    [playSound setIntValue: NO];
     354    [doSpeak setIntValue: NO];
     355
     356    while ( (alert = [e nextObject]) != nil) {
     357        if ([alert isKindOfClass: [PSDockBounceAlert class]]) {
     358            [bounceDockIcon setState: NSOnState];
     359        } else if ([alert isKindOfClass: [PSScriptAlert class]]) {
     360            [doScript setIntValue: YES];
     361            [script setAlias: [(PSScriptAlert *)alert scriptFileAlias]];
     362        } else if ([alert isKindOfClass: [PSNotifierAlert class]]) {
     363            [displayMessage setIntValue: YES];
     364        } else if ([alert isKindOfClass: [PSBeepAlert class]]) {
     365            unsigned int repetitions = [(PSBeepAlert *)alert repetitions];
     366            [playSound setIntValue: YES];
     367            [sound setAlias: nil];
     368            [soundRepetitions setIntValue: repetitions];
     369            [soundRepetitionStepper setIntValue: repetitions];
     370        } else if ([alert isKindOfClass: [PSMovieAlert class]]) {
     371            unsigned int repetitions = [(PSMovieAlert *)alert repetitions];
     372            [playSound setIntValue: YES];
     373            [sound setAlias: [(PSMovieAlert *)alert movieFileAlias]];
     374            [soundRepetitions setIntValue: repetitions];
     375            [soundRepetitionStepper setIntValue: repetitions];
     376        } else if ([alert isKindOfClass: [PSSpeechAlert class]]) {
     377            [doSpeak setIntValue: YES];
     378            [voice setVoice: [(PSSpeechAlert *)alert voice]];
     379        }
     380    }
     381}
     382
     383- (BOOL)_setAlerts;
     384{
     385    PSAlerts *alerts = [alarm alerts];
     386   
     387    [alerts removeAlerts];
     388    NS_DURING
     389        // dock bounce alert
     390        if ([bounceDockIcon state] == NSOnState)
     391            [alerts addAlert: [PSDockBounceAlert alert]];
     392        // script alert
     393        if ([doScript intValue]) {
     394            BDAlias *scriptFileAlias = [script alias];
     395            if (scriptFileAlias == nil) {
     396                [self setStatus: @"Unable to set script alert (no script specified?)"];
     397                return NO;
     398            }
     399            [alerts addAlert: [PSScriptAlert alertWithScriptFileAlias: scriptFileAlias]];
     400        }
     401        // notifier alert
     402        if ([displayMessage intValue])
     403            [alerts addAlert: [PSNotifierAlert alert]];
     404        // sound alerts
     405        if ([playSound intValue]) {
     406            BDAlias *soundAlias = [sound selectedAlias];
     407            unsigned short numReps = [soundRepetitions intValue];
     408            if (soundAlias == nil) // beep alert
     409                [alerts addAlert: [PSBeepAlert alertWithRepetitions: numReps]];
     410            else // movie alert
     411                [alerts addAlert: [PSMovieAlert alertWithMovieFileAlias: soundAlias repetitions: numReps]];
     412        }
     413        // speech alert
     414        if ([doSpeak intValue])
     415            [alerts addAlert: [PSSpeechAlert alertWithVoice: [voice titleOfSelectedItem]]];
     416        [[NSUserDefaults standardUserDefaults] setObject: [alerts propertyListRepresentation] forKey: PSAlertsSelected];
     417    NS_HANDLER
     418        [self setStatus: [localException reason]];
     419        NS_VALUERETURN(NO, BOOL);
     420    NS_ENDHANDLER
     421    return YES;
     422}
     423
     424#pragma mark actions
    253425
    254426// to ensure proper updating of interval, this should be the only method by which the window is shown (e.g. from the Alarm menu)
     
    256428{
    257429    if (![[self window] isVisible]) {
     430        NSDate *today = [NSCalendarDate dateForDay: [NSDate date]];
     431        if ([(NSDate *)[timeDate objectValue] compare: today] == NSOrderedAscending) {
     432            [timeDate setObjectValue: today];
     433        }
    258434        [self update: self];
    259         // XXX otherwise, first responder appears to alternate every time the window is shown?!  And if you set the initial first responder, you can't tab in the window. :(
     435        // XXX bug workaround - otherwise, first responder appears to alternate every time the window is shown.  And if you set the initial first responder, you can't tab in the window. :(
    260436        [[self window] makeFirstResponder: [[self window] initialFirstResponder]];
    261437    }
     
    266442{
    267443    // set alerts before setting alarm...
    268     [alarm removeAlerts];
    269     // dock bounce alert
    270     if ([bounceDockIcon state] == NSOnState)
    271         [alarm addAlert: [PSDockBounceAlert alert]];
    272     // script alert
    273     if ([doScript intValue]) {
    274         BDAlias *scriptFileAlias = [script alias];
    275         if (scriptFileAlias == nil) {
    276             [self setStatus: @"Unable to set script alert (no script specified?)"];
    277             return;
    278         }
    279         [alarm addAlert: [PSScriptAlert alertWithScriptFileAlias: scriptFileAlias]];
    280     }
    281     // notifier alert
    282     if ([displayMessage intValue])
    283         [alarm addAlert: [PSNotifierAlert alert]];
    284     // sound alerts
    285     if ([playSound intValue]) {
    286         BDAlias *soundAlias = [sound selectedAlias];
    287         unsigned short numReps = [soundRepetitions intValue];
    288         if (soundAlias == nil) // beep alert
    289             [alarm addAlert: [PSBeepAlert alertWithRepetitions: numReps]];
    290         else // movie alert
    291             [alarm addAlert: [PSMovieAlert alertWithMovieFileAlias: soundAlias repetitions: numReps]];
    292     }
    293     // speech alert
    294     if ([doSpeak intValue])
    295         [alarm addAlert: [PSSpeechAlert alertWithVoice: [voice titleOfSelectedItem]]];
     444    if (![self _setAlerts]) return;
    296445
    297446    // set alarm
    298447    [self setAlarmDateAndInterval: sender];
     448    [alarm setRepeating: [timeIntervalRepeats state] == NSOnState];
    299449    [alarm setMessage: [messageField stringValue]];
    300450    if (![alarm setTimer]) {
     
    319469@implementation PSAlarmSetController (NSControlSubclassDelegate)
    320470
     471- (BOOL)control:(NSControl *)control didFailToFormatString:(NSString *)string errorDescription:(NSString *)error;
     472{
     473    if (control == timeInterval)
     474        [timeInterval handleDidFailToFormatString: string errorDescription: error label: @"alarm interval"];
     475    return NO;
     476}
     477
    321478- (void)control:(NSControl *)control didFailToValidatePartialString:(NSString *)string errorDescription:(NSString *)error;
    322479{
    323     unichar c;
    324     int tag;
    325     unsigned length = [string length];
    326     if (control != timeInterval || length == 0) return;
    327     c = [string characterAtIndex: length - 1];
    328     switch (c) {
    329         case 's': case 'S': tag = 1; break;
    330         case 'm': case 'M': tag = 60; break;
    331         case 'h': case 'H': tag = 60 * 60; break;
    332         default: return;
    333     }
    334     [timeIntervalUnits selectItemAtIndex:
    335         [timeIntervalUnits indexOfItemWithTag: tag]];
    336480    // NSLog(@"UPDATING FROM validation");
    337     [self update: timeInterval]; // make sure we still examine the field editor, otherwise if the existing numeric string is invalid, it'll be cleared
     481    if (control == timeInterval) [self update: timeInterval]; // make sure we still examine the field editor, otherwise if the existing numeric string is invalid, it'll be cleared
    338482}
    339483
     
    347491    [self silence: nil];
    348492    [self _stopUpdateTimer];
     493    [self _setAlerts];
    349494}
    350495
     
    367512- (NSString *)voicePopUpButton:(NJRVoicePopUpButton *)sender previewStringForVoice:(NSString *)voice;
    368513{
    369     return [messageField stringValue];
     514    NSString *message = [messageField stringValue];
     515    if (message == nil || [message length] == 0)
     516        message = [alarm message];
     517    return message;
    370518}
    371519
Note: See TracChangeset for help on using the changeset viewer.