Changeset 53


Ignore:
Timestamp:
01/02/03 05:30:03 (17 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:
21 added
1 deleted
56 edited

Legend:

Unmodified
Added
Removed
  • trunk/Cocoa/Pester/Source

    • Property svn:ignore
      •  

        old new  
        11build
         2.gdb_history
  • trunk/Cocoa/Pester/Source/BDAlias.h

    r39 r53  
    6363- (BOOL)isEqual:(id)object;
    6464
     65- (NSString *)displayNameWithKindString:(NSString **)outKindString;
     66
    6567- (NSString *)fullPath;
    6668- (NSString *)fullPathRelativeToPath:(NSString *)relPath;
  • trunk/Cocoa/Pester/Source/BDAlias.m

    r39 r53  
    245245}
    246246
     247- (NSString *)displayNameWithKindString:(NSString **)outKindString;
     248{
     249    AliasHandle alias = [self alias];
     250    FSRef ref;
     251    Boolean wasChanged;
     252    CFStringRef name;
     253
     254    if (alias == NULL) return nil;
     255    if (FSResolveAlias(NULL, alias, &ref, &wasChanged) != noErr) return nil;
     256
     257    if (LSCopyDisplayNameForRef(&ref, &name) != noErr) return nil;
     258    [(NSString *)name autorelease];
     259
     260    if (outKindString != NULL) {
     261        if (LSCopyKindStringForRef(&ref, (CFStringRef *)outKindString) != noErr) return nil;
     262        [*outKindString autorelease];
     263    }
     264
     265    return (NSString *)name;
     266}
     267
    247268- (NSString *)fullPath
    248269{
  • trunk/Cocoa/Pester/Source/English.lproj/MainMenu.nib/classes.nib

    r41 r53  
    1717        {CLASS = NJRIntegerFilter; LANGUAGE = ObjC; SUPERCLASS = NSNumberFormatter; },
    1818        {
     19            CLASS = NJRIntervalField;
     20            LANGUAGE = ObjC;
     21            OUTLETS = {intervalUnits = NSPopUpButton; };
     22            SUPERCLASS = NSTextField;
     23        },
     24        {
    1925            ACTIONS = {stopSoundPreview = id; };
    2026            CLASS = NJRQTMediaPopUpButton;
     
    2329            SUPERCLASS = NSPopUpButton;
    2430        },
     31        {CLASS = NJRUnfocusableMovieView; LANGUAGE = ObjC; SUPERCLASS = NSMovieView; },
    2532        {
    2633            ACTIONS = {stopVoicePreview = id; };
     
    2936            SUPERCLASS = NSPopUpButton;
    3037        },
     38        {CLASS = NSDisclosureButtonCell; LANGUAGE = ObjC; SUPERCLASS = NSButtonCell; },
    3139        {CLASS = NSObject; LANGUAGE = ObjC; },
    3240        {
     
    3543                doScriptChanged = id;
    3644                doSpeakChanged = id;
     45                editAlertChanged = id;
    3746                inAtChanged = id;
    3847                playSoundChanged = id;
     
    4554            LANGUAGE = ObjC;
    4655            OUTLETS = {
     56                alertTabs = NSTabView;
     57                alertView = NSTextField;
    4758                bounceDockIcon = NSButton;
     59                cancelButton = NSButton;
    4860                displayMessage = NSButtonCell;
    4961                doScript = NSButtonCell;
    5062                doSpeak = NSButtonCell;
     63                editAlert = NSButtonCell;
    5164                inAtMatrix = NSMatrix;
    5265                messageField = NSTextField;
     
    6780                timeSummary = NSTextField;
    6881                voice = NJRVoicePopUpButton;
     82                wakeUp = NSButtonCell;
    6983            };
    7084            SUPERCLASS = NSWindowController;
  • trunk/Cocoa/Pester/Source/English.lproj/MainMenu.nib/info.nib

    r45 r53  
    44<dict>
    55        <key>IBDocumentLocation</key>
    6         <string>73 38 356 240 0 0 1600 1178 </string>
     6        <string>63 2 356 240 0 0 1280 832 </string>
    77        <key>IBEditorPositions</key>
    88        <dict>
    99                <key>29</key>
    10                 <string>94 343 273 44 0 0 1600 1178 </string>
     10                <string>130 241 273 44 0 0 1280 832 </string>
    1111        </dict>
    1212        <key>IBFramework Version</key>
    13         <string>286.0</string>
     13        <string>291.0</string>
    1414        <key>IBOpenObjects</key>
    1515        <array>
     
    1818        </array>
    1919        <key>IBSystem Version</key>
    20         <string>6D52</string>
     20        <string>6G30</string>
    2121        <key>IBUserGuides</key>
    2222        <dict>
     
    2525                        <key>guideLocations</key>
    2626                        <array>
    27                                 <string>Horizontal:92.000000</string>
    28                                 <string>Horizontal:148.000000</string>
    29                                 <string>Horizontal:116.000000</string>
    30                                 <string>Horizontal:64.000000</string>
     27                                <string>Horizontal:122.000000</string>
     28                                <string>Horizontal:178.000000</string>
     29                                <string>Horizontal:192.000000</string>
     30                                <string>Vertical:36.000000</string>
     31                                <string>Vertical:16.000000</string>
     32                                <string>Vertical:85.000000</string>
     33                                <string>Vertical:422.000000</string>
     34                                <string>Horizontal:151.000000</string>
    3135                        </array>
    3236                        <key>guidesLocked</key>
  • trunk/Cocoa/Pester/Source/English.lproj/Movie alert.nib/classes.nib

    r33 r53  
    99            SUPERCLASS = NSWindowController;
    1010        },
    11         {CLASS = java.lang.Object; LANGUAGE = Java; }
     11        {CLASS = "java.lang.Object"; LANGUAGE = Java; }
    1212    );
    1313    IBVersion = 1;
  • trunk/Cocoa/Pester/Source/English.lproj/Movie alert.nib/info.nib

    r33 r53  
    11<?xml version="1.0" encoding="UTF-8"?>
    2 <!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
    3 <plist version="0.9">
     2<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
     3<plist version="1.0">
    44<dict>
    55        <key>IBFramework Version</key>
    6         <string>263.2</string>
     6        <string>291.0</string>
    77        <key>IBOpenObjects</key>
    88        <array>
     
    1010        </array>
    1111        <key>IBSystem Version</key>
    12         <string>5S66</string>
     12        <string>6G30</string>
    1313</dict>
    1414</plist>
  • trunk/Cocoa/Pester/Source/English.lproj/Notifier.nib/classes.nib

    r21 r53  
    22    IBClasses = (
    33        {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; },
     4        {CLASS = NJRIntegerFilter; LANGUAGE = ObjC; SUPERCLASS = NSNumberFormatter; },
    45        {
    5             ACTIONS = {close = id; };
     6            CLASS = NJRIntervalField;
     7            LANGUAGE = ObjC;
     8            OUTLETS = {intervalUnits = NSPopUpButton; };
     9            SUPERCLASS = NSTextField;
     10        },
     11        {
     12            ACTIONS = {close = id; snooze = id; stopRepeating = id; };
    613            CLASS = PSAlarmNotifierController;
    714            LANGUAGE = ObjC;
    8             OUTLETS = {dateField = NSTextField; messageField = NSTextField; };
     15            OUTLETS = {
     16                dateField = NSTextField;
     17                intervalField = NSTextField;
     18                messageField = NSTextField;
     19                nextDateField = NSTextField;
     20                okButton = NSButton;
     21                snoozeButton = NSButton;
     22                snoozeIntervalField = NJRIntervalField;
     23            };
    924            SUPERCLASS = NSWindowController;
    1025        }
  • trunk/Cocoa/Pester/Source/English.lproj/Notifier.nib/info.nib

    r43 r53  
    11<?xml version="1.0" encoding="UTF-8"?>
    2 <!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
    3 <plist version="0.9">
     2<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
     3<plist version="1.0">
    44<dict>
    55        <key>IBDocumentLocation</key>
    6         <string>424 83 356 240 0 0 1280 832 </string>
     6        <string>122 13 356 240 0 0 1280 832 </string>
    77        <key>IBFramework Version</key>
    8         <string>263.2</string>
     8        <string>291.0</string>
    99        <key>IBOpenObjects</key>
    1010        <array>
     
    1212        </array>
    1313        <key>IBSystem Version</key>
    14         <string>5S66</string>
     14        <string>6G30</string>
     15        <key>IBUserGuides</key>
     16        <dict>
     17                <key>5</key>
     18                <dict>
     19                        <key>guideLocations</key>
     20                        <array>
     21                                <string>Horizontal:87.000000</string>
     22                                <string>Horizontal:108.000000</string>
     23                                <string>Horizontal:66.000000</string>
     24                                <string>Vertical:92.000000</string>
     25                                <string>Horizontal:133.000000</string>
     26                        </array>
     27                        <key>guidesLocked</key>
     28                        <false/>
     29                </dict>
     30        </dict>
    1531</dict>
    1632</plist>
  • trunk/Cocoa/Pester/Source/NJRFSObjectSelector.m

    r43 r53  
    11#import "NJRFSObjectSelector.h"
    22#import "NSImage-NJRExtensions.h"
     3#import "NSString-NJRExtensions.h"
    34#include <Carbon/Carbon.h>
    45
     6static NSImage *PopupTriangleImage = nil;
     7static NSSize PopupTriangleSize;
     8
    59@implementation NJRFSObjectSelector
    610
    711- (void)_initSelector;
    812{
     13    if (PopupTriangleImage == nil) {
     14        PopupTriangleImage = [[NSImage imageNamed: @"Popup triangle"] retain];
     15        PopupTriangleSize = [PopupTriangleImage size];
     16    }
    917    canChooseFiles = YES; canChooseDirectories = NO;
    1018    [self setAlias: nil];
     
    4755        [NSBezierPath strokeRect: NSInsetRect(boundsRect, 2, 2)];
    4856    } else if (selectedAlias != nil && [self isEnabled]) {
    49         static NSImage *popupTriangle = nil;
    50         static NSSize imageSize;
    51         if (popupTriangle == nil) {
    52             popupTriangle = [[NSImage imageNamed: @"Popup triangle"] retain];
    53             imageSize = [popupTriangle size];
    54         }
    5557        // equivalent to popup triangle location for large bezel in Carbon
    56         [popupTriangle compositeToPoint: NSMakePoint(NSMaxX(boundsRect) - imageSize.width - 5, NSMaxY(boundsRect) - 5) operation: NSCompositeSourceOver];
     58        [PopupTriangleImage compositeToPoint: NSMakePoint(NSMaxX(boundsRect) - PopupTriangleSize.width - 5, NSMaxY(boundsRect) - 5) operation: NSCompositeSourceOver];
    5759    }
    5860}
     
    130132        NSString *path = [alias fullPath];
    131133        NSString *revealPath = nil;
    132         NSString *targetName = [path lastPathComponent];
    133134        NSMenu *menu = [[NSMenu alloc] initWithTitle: @""];
    134135        NSFileManager *fmgr = [NSFileManager defaultManager];
     
    136137        if (path != nil) { // can resolve alias
    137138            [self setImage: [[NSWorkspace sharedWorkspace] iconForFile: path]];
    138             [self setTitle: targetName];
     139            {   // set image first so titleRectForBounds: returns the correct value
     140                NSMutableString *title = [[fmgr displayNameAtPath: path] mutableCopy];
     141                NSDictionary *fontAttributes = [[self attributedTitle] fontAttributesInRange: NSMakeRange(0, 0)];
     142                [title truncateToWidth: [[self cell] titleRectForBounds: [self bounds]].size.width - PopupTriangleSize.width by: NSLineBreakByTruncatingMiddle withAttributes: fontAttributes];
     143                [self setTitle: title];
     144                [title release];
     145            }
    139146            do {
    140147                item = [menu addItemWithTitle: [fmgr displayNameAtPath: path]
  • trunk/Cocoa/Pester/Source/NJRHistoryTrackingComboBox.m

    r39 r53  
    6060    NSString *newValue = [self stringValue];
    6161    int oldIndex = [self indexOfItemWithObjectValue: newValue];
     62    if ([newValue length] == 0) return YES; // donÕt save empty entries
    6263    [self removeItemWithObjectValue: newValue];
    6364    [self insertItemWithObjectValue: newValue atIndex: 0];
  • trunk/Cocoa/Pester/Source/NJRIntegerFilter.h

    r21 r53  
    77//
    88
    9 #import <AppKit/AppKit.h>
     9#import <Foundation/Foundation.h>
    1010
    1111
  • trunk/Cocoa/Pester/Source/NJRIntegerFilter.m

    r21 r53  
    77//
    88
    9 // based on MyIntegerFilter
    10 // Copyright © 2001 Bill Cheeseman. All rights reserved.
     9// based on MyIntegerFilter, later VRIntegerNumberFilter
     10// Copyright © 2001-2002 Bill Cheeseman. All rights reserved.
    1111
    1212#import "NJRIntegerFilter.h"
     
    1414@implementation NJRIntegerFilter
    1515
    16 // Input filter
     16// Input validation
    1717
    18 - (BOOL)isPartialStringValid:(NSString **)partialStringPtr proposedSelectedRange:(NSRangePointer)proposedSelRangePtr originalString:(NSString *)origString originalSelectedRange:(NSRange)origSelRange errorDescription:(NSString **)error {
    19 
    20     // Override method to enable NSControl delegate method control:didFailToValidatePartialString:errorDescription: to
    21     // reject invalid keypresses. Filters out keyboard input characters other than "1" .. "9".
     18- (BOOL)isPartialStringValid:(NSString **)partialStringPtr proposedSelectedRange:(NSRangePointer)proposedSelRangePtr originalString:(NSString *)origString originalSelectedRange:(NSRange)origSelRange errorDescription:(NSString **)error;
     19{
     20    // Override method to enable NSControl delegate method control:didFailToValidatePartialString:errorDescription: to reject invalid keypresses. Filters out keyboard input characters other than 0..9.
    2221    if ([[*partialStringPtr substringWithRange:NSMakeRange(origSelRange.location, (*proposedSelRangePtr).location - origSelRange.location)] rangeOfCharacterFromSet:[[NSCharacterSet decimalDigitCharacterSet] invertedSet] options:NSLiteralSearch].location != NSNotFound) {
    23         *error = NSLocalizedString(@"Input is not an integer", @"Presented when user value not a numeric digit");
    24         return NO; // Reject *partialStringPtr as typed, invoke delegate method
     22        *error = NSLocalizedString(@"Input is not an integer.", @"Presented when user value not a numeric digit");
     23        return NO; // Reject *partialStringPtr as typed, invoke delegate method for error handling
    2524    }
    26 
    27     if ([*partialStringPtr length] == 0) {
    28         // Work around NSFormatter issue in Mac OS X 10.0.
    29         [[[NSApp keyWindow] fieldEditor:NO forObject:nil] setSelectedRange:*proposedSelRangePtr];
    30     }
    31 
     25    *error = nil;
    3226    return YES; // Accept *partialStringPtr as typed
    3327}
  • trunk/Cocoa/Pester/Source/NJRQTMediaPopUpButton.h

    r41 r53  
    2222
    2323- (BDAlias *)selectedAlias;
     24- (void)setAlias:(BDAlias *)alias;
     25
    2426- (BOOL)canRepeat;
     27
    2528- (IBAction)stopSoundPreview:(id)sender;
    2629
  • trunk/Cocoa/Pester/Source/NJRQTMediaPopUpButton.m

    r47 r53  
    1111#import "NSMovie-NJRExtensions.h"
    1212#import "NSImage-NJRExtensions.h"
     13
     14// XXX workaround for SoundFileManager log message in 10.2.3 and earlier
     15#include <stdio.h>
     16#include <unistd.h>
     17#include <fcntl.h>
     18// XXX end workaround
    1319
    1420static const int NJRQTMediaPopUpButtonMaxRecentItems = 10;
     
    103109#pragma mark initialize-release
    104110
    105 - (void)awakeFromNib;
     111- (void)_setUp;
    106112{
    107113    NSMenu *menu;
     
    145151}
    146152
     153- (id)initWithFrame:(NSRect)frame;
     154{
     155    if ( (self = [super initWithFrame: frame]) != nil) {
     156        [self _setUp];
     157    }
     158    return self;
     159}
     160
     161- (id)initWithCoder:(NSCoder *)coder;
     162{
     163    if ( (self = [super initWithCoder: coder]) != nil) {
     164        [self _setUp];
     165    }
     166    return self;
     167}
     168
    147169- (void)dealloc;
    148170{
     
    171193}
    172194
     195- (void)setAlias:(BDAlias *)alias;
     196{
     197    [self _setAlias: alias];
     198    if ([self _validateWithPreview: NO]) {
     199        [self selectItem: [self _itemForAlias: selectedAlias]];
     200    }
     201}
     202
    173203- (void)_setPath:(NSString *)path;
    174204{
     
    186216    // [self _validateRecentMedia];
    187217    path = [alias fullPath];
    188     sf = [[SoundFileManager sharedSoundFileManager] soundFileFromPath: path];
     218    {   // XXX suppress log message from Apple's code:
     219        // 2002-12-14 14:09:58.740 Pester[26529] Could not find sound type for directory /Users/nicholas/Desktop
     220        int errfd = dup(STDERR_FILENO), nullfd = open("/dev/null", O_WRONLY, 0);
     221        // need to have something open in STDERR_FILENO because if it isn't,
     222        // NSLog will log to /dev/console
     223        dup2(nullfd, STDERR_FILENO);
     224        close(nullfd);
     225        sf = [[SoundFileManager sharedSoundFileManager] soundFileFromPath: path];
     226        dup2(errfd, STDERR_FILENO);
     227        close(errfd);
     228    }
    189229    // NSLog(@"_itemForAlias: %@", path);
    190230
     
    250290            [preview setMovie: nil];
    251291            if (movie == nil) {
    252                 NSBeginAlertSheet(@"Format not recognized", @"OK", nil, nil, [self window], nil, nil, nil, nil, @"The item you selected isnÕt a sound or movie recognized by QuickTime.  Please select a different item.");
     292                NSBeginAlertSheet(@"Format not recognized", nil, nil, nil, [self window], nil, nil, nil, nil, @"The item you selected isnÕt a sound or movie recognized by QuickTime.  Please select a different item.");
    253293                [self _invalidateSelection];
    254294                return NO;
    255295            }
    256296            if (![movie hasAudio] && ![movie hasVideo]) {
    257                 NSBeginAlertSheet(@"No video or audio", @"OK", nil, nil, [self window], nil, nil, nil, nil, @"Ò%@Ó contains neither audio nor video content playable by QuickTime.  Please select a different item.", [[NSFileManager defaultManager] displayNameAtPath: [selectedAlias fullPath]]);
     297                NSBeginAlertSheet(@"No video or audio", nil, nil, nil, [self window], nil, nil, nil, nil, @"Ò%@Ó contains neither audio nor video content playable by QuickTime.  Please select a different item.", [[NSFileManager defaultManager] displayNameAtPath: [selectedAlias fullPath]]);
    258298                [self _invalidateSelection];
    259299                [movie release];
     
    262302        }
    263303        [movie release];
    264         [preview start: self];
     304        if (doPreview) [preview start: self];
    265305    }
    266306    [[NSNotificationCenter defaultCenter] postNotificationName: NJRQTMediaPopUpButtonMovieChangedNotification object: self];
  • trunk/Cocoa/Pester/Source/NJRTableDelegate.m

    r51 r53  
    179179            NSRect columnRect = [superview convertRect: [tableView rectOfColumn: [tableView columnWithIdentifier: sortingKey]] fromView: tableView];
    180180            // XXX support horizontal scroll bar/clipping (not for Pester, but eventually)
    181             NSRect tableScrollFrame = [[tableView enclosingScrollView] frame];
     181            // NSRect tableScrollFrame = [[tableView enclosingScrollView] frame];
    182182            NSRect selectFrame = [typeSelectControl frame];
    183183            [superview setNeedsDisplayInRect: selectFrame]; // fix artifacts caused by moving view
  • trunk/Cocoa/Pester/Source/NJRTableView.h

    r51 r53  
    1111
    1212@interface NJRTableView : NSTableView {
     13    NSMutableDictionary *toolTipRegionList;
    1314    NSMutableString *typed;
    1415    IBOutlet id typeSelectDisplay;
     
    2930
    3031- (void)removeSelectedRowsFromTableView:(NSTableView *)tableView;
     32- (NSString *)tableView:(NSTableView *)aTableView toolTipForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex;
    3133
    3234@end
  • trunk/Cocoa/Pester/Source/NJRTableView.m

    r51 r53  
    1717
    1818@implementation NJRTableView
     19
     20- (id)initWithCoder:(NSCoder *)aDecoder;
     21{
     22    if ( (self = [super initWithCoder: aDecoder]) != nil) {
     23        toolTipRegionList = [[NSMutableDictionary alloc] initWithCapacity: 20];
     24    }
     25    return self;
     26}
     27
     28- (void)dealloc;
     29{
     30    [toolTipRegionList release];
     31    [super dealloc];
     32}
     33
     34#pragma mark tool tips
     35
     36- (void)reloadData;
     37{
     38    [toolTipRegionList removeAllObjects];
     39    [self removeAllToolTips];
     40    [super reloadData];
     41}
     42
     43- (void)noteNumberOfRowsChanged;
     44{
     45    [toolTipRegionList removeAllObjects];
     46    [self removeAllToolTips];
     47    [super noteNumberOfRowsChanged];
     48}
     49
     50- (NSString *)_keyForColumn:(int)columnIndex row:(int)rowIndex;
     51{
     52    return [NSString stringWithFormat:@"%d,%d", rowIndex, columnIndex];
     53}
     54
     55- (NSString *)view:(NSView *)view stringForToolTip:(NSToolTipTag)tag point:(NSPoint)point userData:(void *)data;
     56{
     57    // ask our data source for the tool tip
     58    if ([[self dataSource] respondsToSelector: @selector(tableView:toolTipForTableColumn:row:)]) {
     59        if ([self rowAtPoint: point] >= 0) return [[self dataSource] tableView: self toolTipForTableColumn: [[self tableColumns] objectAtIndex: [self columnAtPoint: point]] row: [self rowAtPoint: point]];
     60    }
     61    return nil;
     62}
     63
     64- (NSRect)frameOfCellAtColumn:(int)columnIndex row:(int)rowIndex;
     65{
     66    // this cell is apparently displayed, so we need to add a region for it
     67    NSNumber *toolTipTag;
     68    NSRect result = [super frameOfCellAtColumn: columnIndex row: rowIndex];
     69    // check if cell is already in the list
     70    NSString *cellKey = [self _keyForColumn: columnIndex row: rowIndex];
     71    // remove old region
     72    if (toolTipTag = [toolTipRegionList objectForKey: cellKey])
     73        [self removeToolTip: [toolTipTag intValue]];
     74    // add new region
     75    [toolTipRegionList setObject: [NSNumber numberWithInt: [self addToolTipRect: result owner: self userData: cellKey]] forKey: cellKey];
     76    return [super frameOfCellAtColumn: columnIndex row: rowIndex];
     77}
     78
     79#pragma mark type selection
    1980
    2081- (id)typeSelectDisplay;
  • trunk/Cocoa/Pester/Source/NJRVoicePopUpButton.h

    r42 r53  
    1515}
    1616
     17- (void)setVoice:(NSString *)voice;
     18
     19- (id)delegate;
    1720- (void)setDelegate:(id)delegate;
    18 - (id)delegate;
    1921
    2022- (IBAction)stopVoicePreview:(id)sender;
  • trunk/Cocoa/Pester/Source/NJRVoicePopUpButton.m

    r43 r53  
    6868}
    6969
     70- (void)setVoice:(NSString *)voice;
     71{
     72    int voiceIdx = [self indexOfItemWithTitle: voice];
     73    if (voiceIdx == -1) {
     74        [self _invalidateVoiceSelection];
     75    } else {
     76        [self selectItemAtIndex: voiceIdx];
     77    }
     78}
     79
    7080- (void)_previewVoice;
    7181{
     
    8090
    8191    if ( (err = GetIndVoice(voiceIndex, &voice)) != noErr) {
    82         NSBeginAlertSheet(@"Voice not available", @"OK", nil, nil, [self window], nil, nil, nil, nil, @"The voice Ò%@Ó you selected could not be used.  An error of type %ld occurred while attempting to retrieve voice information.", voiceName, err);
     92        NSBeginAlertSheet(@"Voice not available", nil, nil, nil, [self window], nil, nil, nil, nil, @"The voice Ò%@Ó you selected could not be used.  An error of type %ld occurred while attempting to retrieve voice information.", voiceName, err);
    8393        [self _invalidateVoiceSelection];
    8494        return;
  • trunk/Cocoa/Pester/Source/PSAlarm.h

    r51 r53  
    88
    99#import <Foundation/Foundation.h>
     10#import "PSPropertyListSerialization.h"
    1011
    1112typedef enum {
    12     PSAlarmInvalid, PSAlarmInterval, PSAlarmDate, PSAlarmSet
     13    PSAlarmInvalid, // incorrectly specified
     14    PSAlarmInterval, // interval specified (possibly repeating)
     15    PSAlarmDate, // date specified
     16    PSAlarmSet, // pending, timer set
     17    PSAlarmSnooze, // expired alarm with snooze interval set (possibly repeating)
     18    PSAlarmExpired // expired alarm (possibly repeating)
    1319} PSAlarmType;
    1420
    1521extern NSString * const PSAlarmTimerSetNotification;
    1622extern NSString * const PSAlarmTimerExpiredNotification;
     23extern NSString * const PSAlarmDiedNotification;
    1724
    18 // XXX figure out how to support reading old alarms
     25@class PSAlert, PSAlerts;
    1926
    20 @class PSAlert;
    21 
    22 @interface PSAlarm : NSObject <NSCoding> {
    23     PSAlarmType alarmType;
     27@interface PSAlarm : NSObject <NSCoding, PSPropertyListSerialization> {
     28    PSAlarmType alarmType; // changes during lifetime of alarm; more like a state
    2429    NSCalendarDate *alarmDate;
    2530    NSTimeInterval alarmInterval;
     31    NSTimeInterval snoozeInterval;
     32    NSTimeInterval timeRemaining;
    2633    NSString *alarmMessage;
    2734    NSString *invalidMessage;
    2835    NSTimer *timer;
    29     NSMutableArray *alerts;
     36    PSAlerts *alerts;
     37    BOOL repeating;
    3038}
    3139
     
    3442- (void)setForDate:(NSDate *)date atTime:(NSDate *)time;
    3543- (void)setMessage:(NSString *)aMessage;
    36 - (void)addAlert:(PSAlert *)alert;
    37 - (void)removeAlerts;
     44- (void)setAlerts:(PSAlerts *)theAlerts;
     45- (void)setRepeating:(BOOL)isRepeating;
     46- (void)setSnoozeInterval:(NSTimeInterval)anInterval;
    3847
    3948- (NSCalendarDate *)date;
    4049- (NSCalendarDate *)time;
    4150- (NSTimeInterval)interval;
     51- (NSTimeInterval)timeRemaining;
    4252- (NSString *)message;
    43 - (NSArray *)alerts;
     53- (PSAlerts *)alerts;
     54- (BOOL)isRepeating;
     55- (NSTimeInterval)snoozeInterval; // most recent interval (nonzero return does not indicate alarm is snoozing or set to snooze)
    4456
    4557- (NSString *)dateString;
    4658- (NSString *)shortDateString;
    4759- (NSString *)timeString;
     60- (NSString *)dateTimeString; // current or next alarm time
     61- (NSString *)nextDateTimeString; // next alarm time
     62- (NSString *)intervalString;
    4863- (NSString *)timeRemainingString;
    4964
     
    5166- (NSString *)invalidMessage;
    5267
     68- (NSAttributedString *)prettyDescription;
     69
    5370- (NSComparisonResult)compareDate:(PSAlarm *)otherAlarm;
    5471- (NSComparisonResult)compareMessage:(PSAlarm *)otherAlarm;
    5572
    56 - (BOOL)setTimer;
     73- (BOOL)setTimer; // or die, if expired and no snooze/repeat
    5774- (void)cancelTimer;
    5875
     76// 1.1 only, going away when we move to keyed archiving
     77- (NSDictionary *)propertyListRepresentation;
     78- (id)initWithPropertyList:(NSDictionary *)dict;
     79
    5980@end
  • 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) {
  • trunk/Cocoa/Pester/Source/PSAlarmAlertController.h

    r34 r53  
    1313
    1414@interface PSAlarmAlertController : NSObject {
    15 
     15    NSMutableSet *pendingAlerts;
     16    ProcessSerialNumber frontmostApp;
     17    BOOL appWasHidden;
    1618}
    1719
  • trunk/Cocoa/Pester/Source/PSAlarmAlertController.m

    r40 r53  
    88
    99#import "PSAlarmAlertController.h"
     10#import "PSAlert.h"
     11#import "PSAlerts.h"
    1012
    1113NSString * const PSAlarmAlertStopNotification = @"PSAlarmAlertStopNotification";
     
    2325}
    2426
     27- (void)_resumeAlarm:(PSAlarm *)alarm;
     28{
     29    [[NSNotificationCenter defaultCenter] removeObserver: self];
     30    [alarm setTimer]; // if snooze not set and not repeating, alarm will die
     31    if (frontmostApp.highLongOfPSN != 0 || frontmostApp.lowLongOfPSN != 0) {
     32        SetFrontProcess(&frontmostApp);
     33        if (appWasHidden)
     34            [NSApp hide: self];
     35    }
     36}
     37
     38- (void)_alertCompleted:(NSNotification *)notification;
     39{
     40    PSAlert *alert = [[notification userInfo] objectForKey: @"alert"];
     41    unsigned count = [pendingAlerts count];
     42    [pendingAlerts removeObject: alert];
     43    NSLog(@"removed: %@; still pending: %@", alert, [pendingAlerts description]);
     44    NSLog(@"alarm: %@ retainCount %d", [notification object], [[notification object] retainCount]);
     45    NSAssert2([pendingAlerts count] == count - 1, @"alert not in set: %@\n%@", alert, notification);
     46    if ([pendingAlerts count] == 0) {
     47        [self _resumeAlarm: [notification object]];
     48        [self release];
     49    }
     50}
     51
    2552- (id)initWithAlarm:(PSAlarm *)alarm;
    2653{
    2754    if ( (self = [super init]) != nil) {
    28         [[alarm alerts] makeObjectsPerformSelector: @selector(triggerForAlarm:)
    29                                         withObject: alarm];
    30         [NSApp activateIgnoringOtherApps: YES];
     55        PSAlerts *alerts = [alarm alerts];
     56        NSArray *allAlerts = [alerts allAlerts];
     57        if ([allAlerts count] == 0) {
     58            [self _resumeAlarm: alarm];
     59        } else {
     60            pendingAlerts = [[NSMutableSet alloc] init];
     61            [pendingAlerts addObjectsFromArray: allAlerts];
     62            [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(_alertCompleted:)
     63                                                         name: PSAlarmAlertCompletedNotification object: alarm];
     64            [self retain]; // release in _alertCompleted:
     65        }
     66        [alerts triggerForAlarm: alarm];
     67        if ([alerts requirePesterFrontmost] && ![NSApp isActive]) { // restore frontmost process afterward
     68            NSDictionary *activeProcessInfo = [[NSWorkspace sharedWorkspace] activeApplication];
     69            frontmostApp.highLongOfPSN = [[activeProcessInfo objectForKey: @"NSApplicationProcessSerialNumberHigh"] longValue];
     70            frontmostApp.lowLongOfPSN = [[activeProcessInfo objectForKey: @"NSApplicationProcessSerialNumberLow"] longValue];
     71            appWasHidden = [NSApp isHidden];
     72            [NSApp activateIgnoringOtherApps: YES];
     73        }
    3174    }
    3275    return self;
    3376}
    3477
     78- (void)dealloc;
     79{
     80    NSLog(@"%@ dealloc", self);
     81    [pendingAlerts release];
     82    [super dealloc];
     83}
     84
    3585@end
  • trunk/Cocoa/Pester/Source/PSAlarmNotifierController.h

    r34 r53  
    99#import <AppKit/AppKit.h>
    1010
     11@class NJRIntervalField;
    1112@class PSAlarm;
    1213
     
    1415    IBOutlet NSTextField *messageField;
    1516    IBOutlet NSTextField *dateField;
     17    IBOutlet NSTextField *intervalField;
     18    IBOutlet NSTextField *nextDateField;
     19    IBOutlet NJRIntervalField *snoozeIntervalField;
     20    IBOutlet NSButton *okButton;
     21    IBOutlet NSButton *snoozeButton;
     22    NSTimer *updateTimer;
     23    PSAlarm *alarm;
     24    BOOL canSnooze;
    1625}
    1726
    18 - (id)initWithAlarm:(PSAlarm *)alarm;
     27- (id)initWithAlarm:(PSAlarm *)anAlarm;
    1928
    20 - (IBAction)close:(NSButton *)sender;
     29- (IBAction)close:(id)sender;
     30- (IBAction)snooze:(NSButton *)sender;
     31- (IBAction)stopRepeating:(NSButton *)sender;
    2132
    2233@end
  • trunk/Cocoa/Pester/Source/PSAlarmNotifierController.m

    r43 r53  
    1010#import "PSAlarmAlertController.h"
    1111#import "PSAlarm.h"
     12#import "PSNotifierAlert.h"
     13#import "NJRIntervalField.h"
     14
     15static NSString * const PSAlarmSnoozeInterval = @"Pester alarm snooze interval"; // NSUserDefaults key
     16
     17@interface PSAlarmNotifierController (Private)
     18
     19- (void)update:(id)sender;
     20- (void)updateNextDateDisplay:(id)sender;
     21
     22@end
    1223
    1324@implementation PSAlarmNotifierController
     
    1526// XXX should use NSNonactivatingPanelMask on 10.2
    1627
    17 - (id)initWithAlarm:(PSAlarm *)alarm;
     28- (id)initWithAlarm:(PSAlarm *)anAlarm;
    1829{
    1930    if ([self initWithWindowNibName: @"Notifier"]) {
    20         [[self window] center];
     31        NSWindow *window = [self window];
     32        NSRect frameRect = [window frame];
     33        alarm = [anAlarm retain];
    2134        [messageField setStringValue: [alarm message]];
    22         [dateField setStringValue:
    23             [NSString stringWithFormat: @"%@ at %@", [alarm dateString], [alarm timeString]]];
    24         [[self window] makeKeyAndOrderFront: nil];
    25         [[self window] orderFrontRegardless];
     35        [dateField setStringValue: [alarm dateTimeString]];
     36        if (![snoozeIntervalField setInterval: [alarm snoozeInterval]] &&
     37            ![snoozeIntervalField setInterval: [[[NSUserDefaults standardUserDefaults] objectForKey: PSAlarmSnoozeInterval] doubleValue]])
     38            [snoozeIntervalField setInterval: 15 * 60]; // 15 minutes
     39        if ([alarm isRepeating]) {
     40            [intervalField setStringValue:
     41                [NSString stringWithFormat: @"every %@", [[alarm intervalString] lowercaseString]]];
     42            [self updateNextDateDisplay: nil];
     43            updateTimer = [NSTimer scheduledTimerWithTimeInterval: 1 target: self selector: @selector(updateNextDateDisplay:) userInfo: nil repeats: YES];
     44            frameRect.size = [window maxSize];
     45        } else {
     46            frameRect.size = [window minSize];
     47        }
     48        [window setFrame: frameRect display: NO];
     49        [window center];
     50        [window makeKeyAndOrderFront: nil];
     51        [window orderFrontRegardless];
    2652    }
    2753    return self;
     54}
     55
     56- (void)dealloc;
     57{
     58    [alarm release];
     59    [updateTimer invalidate]; updateTimer = nil;
     60    [super dealloc];
     61}
     62
     63- (void)updateNextDateDisplay:(id)sender;
     64{
     65    if (!canSnooze) {
     66        NSString *nextDateTimeString = [alarm nextDateTimeString];
     67        if (nextDateTimeString == nil) { // no longer repeating
     68            [updateTimer invalidate]; updateTimer = nil;
     69        } else {
     70            [nextDateField setStringValue: nextDateTimeString];
     71        }
     72    }
     73}
     74
     75- (void)update:(id)sender;
     76{
     77    canSnooze = [snoozeIntervalField interval] != 0;
     78    if (canSnooze) [nextDateField setStringValue: @"after snooze"];
     79    [snoozeButton setEnabled: canSnooze];
     80    [canSnooze ? snoozeButton : okButton setKeyEquivalent: @"\r"];
     81    [canSnooze ? okButton : snoozeButton setKeyEquivalent: @""];
    2882}
    2983
     
    3286    [PSAlarmAlertController stopAlerts: sender];
    3387    [self close];
     88    [[PSNotifierAlert alert] completedForAlarm: alarm];
     89}
     90
     91- (IBAction)snooze:(NSButton *)sender;
     92{
     93    NSTimeInterval snoozeInterval = [snoozeIntervalField interval];
     94    [alarm setSnoozeInterval: snoozeInterval];
     95    [[NSUserDefaults standardUserDefaults] setObject: [NSNumber numberWithDouble: snoozeInterval] forKey: PSAlarmSnoozeInterval];
     96    [self close: sender];
     97}
     98
     99- (IBAction)stopRepeating:(NSButton *)sender;
     100{
     101    NSWindow *window = [self window];
     102    NSRect frameRect = [window frame];
     103    NSSize newSize = [window minSize];
     104   
     105    [alarm setRepeating: NO];
     106    [sender setEnabled: NO];
     107    frameRect.origin.y += frameRect.size.height - newSize.height;
     108    frameRect.size = newSize;
     109    [window setFrame: frameRect display: YES animate: YES];
    34110}
    35111
    36112@end
     113
     114@implementation PSAlarmNotifierController (NSControlSubclassDelegate)
     115
     116- (BOOL)control:(NSControl *)control didFailToFormatString:(NSString *)string errorDescription:(NSString *)error;
     117{
     118    if (control == snoozeIntervalField)
     119        [snoozeIntervalField handleDidFailToFormatString: string errorDescription: error label: @"snooze interval"];
     120    return NO;
     121}
     122
     123- (void)control:(NSControl *)control didFailToValidatePartialString:(NSString *)string errorDescription:(NSString *)error;
     124{
     125    // NSLog(@"UPDATING FROM validation");
     126    [self update: control]; // switch to snooze if someone types something weird...
     127}
     128
     129- (BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)commandSelector;
     130{
     131    // NSLog(@"UPDATING from textView: %@", NSStringFromSelector(commandSelector));
     132    if (commandSelector == @selector(cancel:)) {
     133        // if someone just wants the stupid thing to go away and presses escape, donÕt hinder them
     134        [self close: control];
     135        return YES;
     136    }
     137    // if someone invokes the default button or switches fields, donÕt override it
     138    if (commandSelector == @selector(insertNewline:) ||
     139        commandSelector == @selector(insertTab:) ||
     140        commandSelector == @selector(insertBacktab:)) return NO;
     141    [self update: control]; // ...or if they type a navigation key...
     142    return NO; // we donÕt handle it
     143}
     144
     145@end
     146
     147@implementation PSAlarmNotifierController (NSControlSubclassNotifications)
     148
     149- (void)controlTextDidChange:(NSNotification *)notification;
     150{
     151    // NSLog(@"UPDATING FROM controlTextDidChange: %@", [notification object]);
     152    [self update: [notification object]]; // ...or if they modify the snooze interval
     153}
     154
     155@end
     156
     157@implementation PSAlarmNotifierController (NSWindowNotifications)
     158
     159- (void)windowWillClose:(NSNotification *)notification;
     160{
     161    // canÕt rely on dealloc to invalidate the timer, because it retains this object
     162    [updateTimer invalidate]; updateTimer = nil;
     163    [self release]; // in non-document-based apps, this is needed; see docs
     164}
     165
     166@end
  • trunk/Cocoa/Pester/Source/PSAlarmSetController.h

    r41 r53  
    1111
    1212@class NJRFSObjectSelector;
     13@class NJRIntervalField;
    1314@class NJRQTMediaPopUpButton;
    1415@class NJRVoicePopUpButton;
     
    1718    IBOutlet NSTextField *messageField;
    1819    IBOutlet NSMatrix *inAtMatrix;
    19     IBOutlet NSTextField *timeInterval;
     20    IBOutlet NJRIntervalField *timeInterval;
    2021    IBOutlet NSPopUpButton *timeIntervalUnits;
    2122    IBOutlet NSButton *timeIntervalRepeats;
     
    2324    IBOutlet NSTextField *timeDate;
    2425    IBOutlet NSPopUpButton *timeDateCompletions; // XXX should go away when bug preventing both formatters and popup menus from existing is fixed
     26    IBOutlet NSButtonCell *editAlert;
     27    IBOutlet NSTextField *alertView;
     28    IBOutlet NSTabView *alertTabs;
    2529    IBOutlet NSButtonCell *displayMessage;
    2630    IBOutlet NSButton *bounceDockIcon;
     
    3539    IBOutlet NJRVoicePopUpButton *voice;
    3640    IBOutlet NSButton *scriptSelectButton;
     41    IBOutlet NSButtonCell *wakeUp;
    3742    IBOutlet NSTextField *timeSummary;
     43    IBOutlet NSButton *cancelButton;
    3844    IBOutlet NSButton *setButton;
    3945    NSString *status;
     
    4652- (IBAction)dateCompleted:(NSPopUpButton *)sender;
    4753- (IBAction)inAtChanged:(id)sender;
     54- (IBAction)editAlertChanged:(id)sender;
    4855- (IBAction)playSoundChanged:(id)sender;
    4956- (IBAction)doScriptChanged:(id)sender;
  • 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
  • trunk/Cocoa/Pester/Source/PSAlarms.h

    r51 r53  
    88
    99#import <Cocoa/Cocoa.h>
     10#import "PSPropertyListSerialization.h"
    1011
    1112extern NSString * const PSAlarmsDidChangeNotification;
     
    1415@class PSAlarm;
    1516
    16 @interface PSAlarms : NSObject {
     17@interface PSAlarms : NSObject <PSPropertyListSerialization> {
    1718    NSMutableArray *alarms;
     19    NSMutableSet *expiredAlarms;
    1820    PSAlarm *nextAlarm;
    1921}
     
    2224+ (PSAlarms *)allAlarms;
    2325
     26- (unsigned)countOfVersion1Alarms;
     27- (void)importVersion1Alarms;
     28- (void)discardVersion1Alarms;
     29
    2430- (NSArray *)alarms;
     31
    2532- (PSAlarm *)nextAlarm;
    2633- (int)alarmCount;
  • trunk/Cocoa/Pester/Source/PSAlarms.m

    r51 r53  
    99#import "PSAlarms.h"
    1010#import "PSAlarm.h"
     11#import "NSDictionary-NJRExtensions.h"
     12
     13NSString * const PSAlarmImportException = @"PSAlarmImportException";
    1114
    1215NSString * const PSAlarmsDidChangeNotification = @"PSAlarmsDidChangeNotification";
    1316NSString * const PSAlarmsNextAlarmDidChangeNotification = @"PSAlarmsNextAlarmDidChangeNotification";
    1417
    15 static NSString * const PSPendingAlarms = @"Pester pending alarms"; // NSUserDefaults key
     18// NSUserDefaults key
     19static NSString * const PSPendingAlarms = @"Pester pending alarms"; // 1.0 Ð 1.1a3
     20static NSString * const PSAllAlarms = @"Pester alarms"; // 1.1a4 Ð
     21
     22// property list keys
     23static NSString * const PLAlarmsPending = @"pending";
     24static NSString * const PLAlarmsExpired = @"expired";
    1625
    1726static PSAlarms *PSAlarmsAllAlarms = nil;
     
    2837{
    2938    if (PSAlarmsAllAlarms == nil) {
    30         PSAlarmsAllAlarms = [[self alloc] init];
     39        NSDictionary *plAlarms = [[NSUserDefaults standardUserDefaults] objectForKey: PSAllAlarms];
     40        if (plAlarms == nil) {
     41            PSAlarmsAllAlarms = [[self alloc] init];
     42        } else {
     43            PSAlarmsAllAlarms = [[self alloc] initWithPropertyList: plAlarms];
     44        }
    3145        [PSAlarmsAllAlarms _updateNextAlarm]; // only generate notifications after singleton established
    3246    }
     
    3751    NSAssert(PSAlarmsAllAlarms != nil, @"Attempt to use +[PSAlarms allAlarms] before setup complete");
    3852    return PSAlarmsAllAlarms;
     53}
     54
     55#pragma mark private
     56
     57- (void)_changed;
     58{
     59    NSMutableArray *alarmsData = [[NSMutableArray alloc] init];
     60    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
     61    [self _updateNextAlarm];
     62    // NSLog(@"PSAlarms _changed:\n%@", alarms);
     63    [defaults setObject: [self propertyListRepresentation] forKey: PSAllAlarms];
     64    [defaults synchronize];
     65    [alarmsData release];
     66    [[NSNotificationCenter defaultCenter] postNotificationName: PSAlarmsDidChangeNotification object: self];
     67}
     68
     69- (void)_alarmTimerExpired:(NSNotification *)notification;
     70{
     71    PSAlarm *alarm = [notification object];
     72    NSLog(@"timer expired: %@ retainCount %d", alarm, [alarm retainCount]);
     73    [expiredAlarms addObject: alarm];
     74    NSLog(@"expired alarms: %@", [expiredAlarms description]);
     75    [alarms removeObject: alarm];
     76    [self _changed];
     77}
     78
     79- (void)_alarmTimerSet:(NSNotification *)notification;
     80{
     81    PSAlarm *alarm = [notification object];
     82    NSLog(@"timer set: %@ retainCount %d", alarm, [alarm retainCount]);
     83    [alarms addObject: alarm];
     84    [expiredAlarms removeObject: alarm];
     85    [self _changed];
     86}
     87
     88- (void)_alarmDied:(NSNotification *)notification;
     89{
     90    PSAlarm *alarm = [notification object];
     91    NSLog(@"alarm died: %@ retainCount %d", alarm, [alarm retainCount]);
     92    [alarms removeObject: alarm];
     93    [expiredAlarms removeObject: alarm];
     94    [self _changed];
    3995}
    4096
     
    59115}
    60116
     117- (void)_setUpNotifications;
     118{
     119    [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(_alarmTimerSet:) name: PSAlarmTimerSetNotification object: nil];
     120    [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(_alarmTimerExpired:) name: PSAlarmTimerExpiredNotification object: nil];
     121    [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(_alarmDied:) name: PSAlarmDiedNotification object: nil];
     122}
     123
     124#pragma mark initialize-release
     125
    61126- (id)init;
    62127{
    63128    if ( (self = [super init]) != nil) {
    64129        alarms = [[NSMutableArray alloc] init];
    65         NS_DURING
    66             NSArray *alarmsData = [[NSUserDefaults standardUserDefaults] arrayForKey: PSPendingAlarms];
    67             NSEnumerator *e = [alarmsData objectEnumerator];
    68             NSData *alarmData;
    69             PSAlarm *alarm;
    70             while ( (alarmData = [e nextObject]) != nil) {
    71                 alarm = [NSUnarchiver unarchiveObjectWithData: alarmData];
    72                 if (alarm != nil)
    73                     [alarms addObject: alarm];
    74             }
    75         NS_HANDLER
    76             // XXX need better error handling here, don't stomp on data
    77             NSLog(@"An error occurred while attempting to restore the alarm list: %@", localException);
    78         NS_ENDHANDLER
    79         [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(_alarmTimerSet:) name: PSAlarmTimerSetNotification object: nil];
    80         [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(_alarmTimerExpired:) name: PSAlarmTimerExpiredNotification object: nil];
     130        expiredAlarms = [[NSMutableSet alloc] init];
     131        [self _setUpNotifications];
    81132    }
    82133    return self;
     
    86137{
    87138    [alarms release];
     139    [expiredAlarms release];
    88140    [[NSNotificationCenter defaultCenter] removeObserver: self];
    89141    [super dealloc];
    90142}
    91143
    92 - (void)_changed;
    93 {
    94     NSMutableArray *alarmsData = [[NSMutableArray alloc] init];
    95     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    96     NSEnumerator *e;
    97     PSAlarm *alarm;
    98     [self _updateNextAlarm];
    99     // NSLog(@"PSAlarms _changed:\n%@", alarms);
    100     // archive
    101     e = [alarms objectEnumerator];
    102     while ( (alarm = [e nextObject]) != nil) {
    103         [alarmsData addObject: [NSArchiver archivedDataWithRootObject: alarm]];
    104     }
    105     [defaults setObject: alarmsData forKey: PSPendingAlarms];
    106     [defaults synchronize];
    107     [alarmsData release];
    108     [[NSNotificationCenter defaultCenter] postNotificationName: PSAlarmsDidChangeNotification object: self];
    109 }
    110 
    111 - (void)_alarmTimerExpired:(NSNotification *)notification;
    112 {
    113     [alarms removeObject: [notification object]];
    114     [self _changed];
    115 }
    116 
    117 - (void)_alarmTimerSet:(NSNotification *)notification;
    118 {
    119     [alarms addObject: [notification object]];
    120     [self _changed];
     144#pragma mark accessing
     145
     146- (NSArray *)alarms;
     147{
     148    return alarms;
    121149}
    122150
     
    124152{
    125153    return nextAlarm;
    126 }
    127 
    128 - (NSArray *)alarms;
    129 {
    130     return alarms;
    131154}
    132155
     
    185208}
    186209
     210#pragma mark property list serialization (Pester 1.1)
     211
     212- (NSDictionary *)propertyListRepresentation;
     213{
     214    NSMutableArray *plPendingAlarms = [[NSMutableArray alloc] initWithCapacity: [alarms count]];
     215    NSMutableArray *plExpiredAlarms = [[NSMutableArray alloc] initWithCapacity: [expiredAlarms count]];
     216    NSDictionary *plAllAlarms, *plAlarm;
     217    NSEnumerator *e;
     218    PSAlarm *alarm;
     219
     220    e = [alarms objectEnumerator];
     221    while ( (alarm = [e nextObject]) != nil) {
     222        plAlarm = [alarm propertyListRepresentation];
     223        if (plAlarm != nil)
     224            [plPendingAlarms addObject: plAlarm];
     225    }
     226
     227    e = [expiredAlarms objectEnumerator];
     228    while ( (alarm = [e nextObject]) != nil) {
     229        plAlarm = [alarm propertyListRepresentation];
     230        if (plAlarm != nil)
     231            [plExpiredAlarms addObject: plAlarm];
     232    }
     233   
     234    plAllAlarms = [NSDictionary dictionaryWithObjectsAndKeys:
     235        plPendingAlarms, PLAlarmsPending, plExpiredAlarms, PLAlarmsExpired, nil];
     236    [plPendingAlarms release];
     237    [plExpiredAlarms release];
     238   
     239    return plAllAlarms;
     240}
     241
     242- (id)initWithPropertyList:(NSDictionary *)dict;
     243{
     244    if ( (self = [super init]) != nil) {
     245        NSArray *plPendingAlarms = [dict objectForRequiredKey: PLAlarmsPending];
     246        NSArray *plExpiredAlarms = [dict objectForRequiredKey: PLAlarmsExpired];
     247        NSEnumerator *e;
     248        NSDictionary *plAlarm;
     249        PSAlarm *alarm;
     250
     251        alarms = [[NSMutableArray alloc] initWithCapacity: [plPendingAlarms count]];
     252        e = [plPendingAlarms objectEnumerator];
     253        while ( (plAlarm = [e nextObject]) != nil) {
     254            [alarms addObject: [[PSAlarm alloc] initWithPropertyList: plAlarm]];
     255        }
     256
     257        e = [plExpiredAlarms objectEnumerator];
     258        while ( (plAlarm = [e nextObject]) != nil) {
     259            // expired alarms may be just that, or they may have outstanding repeats - if the latter, PSAlarm will reschedule the alarm.
     260            if ( (alarm = [[PSAlarm alloc] initWithPropertyList: plAlarm]) != nil)
     261                [alarms addObject: alarm];
     262        }
     263        expiredAlarms = [[NSMutableSet alloc] init];
     264       
     265        [self _setUpNotifications];
     266    }
     267    return self;
     268}
     269
     270#pragma mark archiving (Pester 1.0)
     271
     272- (unsigned)countOfVersion1Alarms;
     273{
     274    return [[[NSUserDefaults standardUserDefaults] objectForKey: PSPendingAlarms] count];
     275}
     276
     277- (void)discardVersion1Alarms;
     278{
     279    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
     280    [defaults removeObjectForKey: PSPendingAlarms];
     281    [defaults synchronize];
     282}
     283
     284- (void)importVersion1Alarms;
     285{
     286    NSArray *alarmsData = [[NSUserDefaults standardUserDefaults] arrayForKey: PSPendingAlarms];
     287    NSEnumerator *e = [alarmsData objectEnumerator];
     288    NSData *alarmData;
     289    PSAlarm *alarm;
     290    while ( (alarmData = [e nextObject]) != nil) {
     291        NS_DURING
     292            alarm = [NSUnarchiver unarchiveObjectWithData: alarmData];
     293        NS_HANDLER
     294            alarm = nil;
     295            // XXX
     296        NS_ENDHANDLER
     297        if (alarm != nil)
     298            [alarms addObject: alarm];
     299    }
     300}
     301
    187302@end
  • trunk/Cocoa/Pester/Source/PSAlarmsController.m

    r51 r53  
    99#import "PSAlarmsController.h"
    1010#import "PSAlarm.h"
     11#import "PSAlerts.h"
    1112#import "NSTableView-NJRExtensions.h"
    1213#import "NJRTableView.h"
     
    8283}
    8384
     85- (NSString *)tableView:(NSTableView *)aTableView toolTipForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex;
     86{
     87    PSAlarm *alarm = [reorderedAlarms objectAtIndex: rowIndex];
     88
     89    return [[alarm prettyDescription] string];
     90}
     91
    8492@end
    8593
  • trunk/Cocoa/Pester/Source/PSAlert.h

    r34 r53  
    99#import <Foundation/Foundation.h>
    1010#import "PSAlarm.h"
     11#import "PSPropertyListSerialization.h"
     12#import "NSString-NJRExtensions.h"
    1113
    12 @interface PSAlert : NSObject {
     14extern NSString * const PSAlertCreationException;
    1315
     16extern NSString * const PSAlarmAlertCompletedNotification; // userInfo key: "alert" -> PSAlert
     17
     18@interface PSAlert : NSObject <PSPropertyListSerialization> {
     19   
    1420}
    15 
    16 // XXX need archiving support
    1721
    1822// subclasses should implement these methods
    1923+ (PSAlert *)alert;
    2024- (void)triggerForAlarm:(PSAlarm *)alarm;
     25- (BOOL)requiresPesterFrontmost;
     26
     27- (NSAttributedString *)actionDescription;
     28
     29// after alert completes, invoke method of superclass
     30- (void)completedForAlarm:(PSAlarm *)alarm;
    2131
    2232@end
  • trunk/Cocoa/Pester/Source/PSAlert.m

    r34 r53  
    88
    99#import "PSAlert.h"
     10#import "NSDictionary-NJRExtensions.h"
    1011
     12NSString * const PSAlertCreationException = @"PSAlertCreationException";
     13
     14NSString * const PSAlarmAlertCompletedNotification = @"PSAlarmAlertCompletedNotification";
     15
     16// property list keys
     17static NSString * const PLAlertClass = @"class"; // NSString
    1118
    1219@implementation PSAlert
     
    2330}
    2431
     32- (BOOL)requiresPesterFrontmost;
     33{
     34    return NO;
     35}
     36
     37- (void)completedForAlarm:(PSAlarm *)alarm;
     38{
     39    [[NSNotificationCenter defaultCenter] postNotificationName: PSAlarmAlertCompletedNotification object: alarm userInfo: [NSDictionary dictionaryWithObject: self forKey: @"alert"]];
     40}
     41
     42- (NSAttributedString *)actionDescription;
     43{
     44    NSAssert(NO, @"Class is abstract");
     45    return nil;
     46}
     47
     48#pragma mark property list serialization (Pester 1.1)
     49
     50- (NSDictionary *)propertyListRepresentation;
     51{
     52    return [NSDictionary dictionaryWithObject: NSStringFromClass([self class]) forKey: PLAlertClass];
     53}
     54
     55- (id)initWithPropertyList:(NSDictionary *)dict;
     56{
     57    if ( (self = [self init]) != nil) {
     58        IMP myImp = [self methodForSelector: _cmd];
     59        NSString *clsString = [dict objectForRequiredKey: PLAlertClass];
     60        Class cls = NSClassFromString(clsString);
     61        NSAssert1(cls != nil, @"Alert class %@ is not available", clsString);
     62        [super release];
     63        self = [cls alloc];
     64        if (self != nil) {
     65            IMP subImp = [self methodForSelector: @selector(initWithPropertyList:)];
     66            NSAssert1(subImp != myImp, @"No implementation of initWithPropertyList: for alert class %@", clsString);
     67            self = [self initWithPropertyList: dict];
     68        }
     69    }
     70    return self;
     71}
     72
    2573@end
  • trunk/Cocoa/Pester/Source/PSApplication.m

    r34 r53  
    2121    [[NSNotificationCenter defaultCenter] addObserver: [PSAlarmAlertController class] selector: @selector(controllerWithTimerExpiredNotification:) name: PSAlarmTimerExpiredNotification object: nil];
    2222    [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(nextAlarmDidChange:) name: PSAlarmsNextAlarmDidChangeNotification object: nil];
     23    // XXX exception handling
    2324    [PSAlarms setUp];
    2425    [self setDelegate: self];
     
    7172{
    7273    PSAlarm *alarm = [timer userInfo];
    73     NSTimeInterval alarmInterval;
     74    NSTimeInterval timeRemaining;
    7475    NSString *tileString;
    7576    if (timer == nil) alarm = [[PSAlarms allAlarms] nextAlarm];
    7677    if (alarm == nil) return;
    7778    tileString = [alarm timeRemainingString];
     79    timeRemaining = [alarm timeRemaining]; // want to err on the side of timeRemaining being smaller, otherwise ÇexpiredÈ can appear
    7880    {
    7981        NSMutableDictionary *atts = [NSMutableDictionary dictionary];
     
    117119        [tile release];
    118120    }
    119     alarmInterval = [alarm interval];
    120     // NSLog(@"_updateDockTile > time remaining %@ (%.0lf), last time interval %.0lf", tileString, alarmInterval, dockUpdateInterval);
    121     if (alarmInterval > 61) {
    122         NSTimeInterval nextUpdate = ((unsigned long long)alarmInterval) % 60;
     121    // NSLog(@"_updateDockTile > time remaining %@ (%.6lf), last time interval %.6lf", tileString, timeRemaining, dockUpdateInterval);
     122    if (timeRemaining > 61) {
     123        NSTimeInterval nextUpdate = ((unsigned long long)timeRemaining) % 60;
    123124        if (nextUpdate <= 1) nextUpdate = 60;
    124125        [self _resetUpdateTimer];
     
    126127        // NSLog(@"_updateDockTile > set timer for %.0lf seconds", nextUpdate);
    127128    } else if (timer == nil || dockUpdateInterval > 1) {
    128         [self _resetUpdateTimer];
     129        [self _resetUpdateTimer]; 
    129130        [self _setUpdateTimerForInterval: 1 alarm: alarm repeats: YES];
    130131        // NSLog(@"_updateDockTile > set timer for 1 second");
    131     } else if (alarmInterval < 2) {
     132    } else if (timeRemaining <= 1) {
    132133        [self _resetUpdateTimer];
    133134    }
     
    182183@implementation PSApplication (NSApplicationNotifications)
    183184
     185- (void)applicationDidFinishLaunching:(NSNotification *)notification;
     186{
     187    // XXX import panel will not be frontmost window if you switch to another app while Pester is launching; Mac OS X bug?
     188    PSAlarms *allAlarms = [PSAlarms allAlarms];
     189    unsigned version1AlarmCount = [allAlarms countOfVersion1Alarms];
     190    if (version1AlarmCount > 0) {
     191        int answer = NSRunAlertPanel(@"Import alarms from older Pester version?", @"Pester found %u alarm%@ created with an older version. These alarms must be converted for use with this version of Pester, and will be unavailable in previous versions after conversion. New alarms created with this version of Pester will not appear in Pester version 1.1a3 or earlier.",
     192                                     @"Import", @"Discard", @"DonÕt Import",
     193                                     version1AlarmCount, version1AlarmCount == 1 ? @"" : @"s");
     194        switch (answer) {
     195            case NSAlertDefaultReturn:
     196                NS_DURING
     197                    [allAlarms importVersion1Alarms];
     198                NS_HANDLER
     199                    NSRunAlertPanel(@"Error occurred importing alarms", @"Pester was unable to convert some alarms created with an older version. Those alarms which could be read have been converted. The previous-format alarms have been retained; try using an older version of Pester to read them.\n\n%@", nil, nil, nil, [localException reason]);
     200                    NS_VOIDRETURN;
     201                NS_ENDHANDLER
     202            case NSAlertAlternateReturn:
     203                NSLog(@"discard");
     204                // [allAlarms discardVersion1Alarms];
     205                break;
     206            case NSAlertOtherReturn:
     207                NSLog(@"donÕt import");
     208                break;
     209        }
     210    }
     211}
     212
    184213- (void)applicationWillTerminate:(NSNotification *)notification;
    185214{
  • trunk/Cocoa/Pester/Source/PSBeepAlert.h

    r34 r53  
    99#import "PSAlert.h"
    1010
    11 
    1211@interface PSBeepAlert : PSAlert {
     12    PSAlarm *alarm;
    1313    unsigned short repetitions;
    14     unsigned short repetitionsRemaining;   
     14    unsigned short repetitionsRemaining;
    1515}
    1616
     
    1818- (id)initWithRepetitions:(unsigned short)numReps;
    1919
     20- (unsigned short)repetitions;
     21
    2022@end
  • trunk/Cocoa/Pester/Source/PSBeepAlert.m

    r34 r53  
    1010#import "PSBeepAlert.h"
    1111#import "PSAlarmAlertController.h"
     12#import "NSDictionary-NJRExtensions.h"
     13
     14// property list keys
     15static NSString * const PLAlertRepetitions = @"times"; // NSNumber
    1216
    1317@implementation PSBeepAlert
     
    3236}
    3337
     38- (unsigned short)repetitions;
     39{
     40    return repetitions;
     41}
     42
    3443- (void)beep;
    3544{
     
    3746    repetitionsRemaining--;
    3847    if (repetitionsRemaining == 0) {
     48        [self completedForAlarm: alarm];
    3949        [self release];
    4050        return;
     
    4858}
    4959
    50 - (void)triggerForAlarm:(PSAlarm *)alarm;
     60- (void)triggerForAlarm:(PSAlarm *)anAlarm;
    5161{
     62    alarm = anAlarm;
    5263    repetitionsRemaining = repetitions;
    5364    [self retain];
     
    5667}
    5768
     69- (NSAttributedString *)actionDescription;
     70{
     71    return [[@"Play the system alert sound" stringByAppendingString:
     72                                            repetitions == 1 ? @"" : [NSString stringWithFormat: @" %hu times", repetitions]] small];
     73}
     74
     75#pragma mark property list serialization (Pester 1.1)
     76
     77- (NSDictionary *)propertyListRepresentation;
     78{
     79    NSMutableDictionary *plAlert = [[super propertyListRepresentation] mutableCopy];
     80    [plAlert setObject: [NSNumber numberWithUnsignedShort: repetitions] forKey: PLAlertRepetitions];
     81    return [plAlert autorelease];
     82}
     83
     84- (id)initWithPropertyList:(NSDictionary *)dict;
     85{
     86    return [self initWithRepetitions: [[dict objectForRequiredKey: PLAlertRepetitions] unsignedShortValue]];
     87}
     88
    5889@end
  • trunk/Cocoa/Pester/Source/PSDockBounceAlert.m

    r40 r53  
    3030    [NSApp requestUserAttention: NSInformationalRequest];
    3131    [[self class] performSelector: @selector(stopBouncing) withObject: nil afterDelay: 1 inModes: [NSArray arrayWithObject: NSDefaultRunLoopMode]];
     32    [self completedForAlarm: alarm];
    3233}
    3334
     35- (NSAttributedString *)actionDescription;
     36{
     37    return [@"Bounce dock icon" small];
     38}
     39
     40#pragma mark property list serialization (Pester 1.1)
     41
     42- (id)initWithPropertyList:(NSDictionary *)dict;
     43{
     44    [self release];
     45    return [[PSDockBounceAlert alert] retain];
     46}
     47       
    3448@end
  • trunk/Cocoa/Pester/Source/PSMovieAlert.h

    r34 r53  
    99#import <AppKit/AppKit.h>
    1010#import "PSAlert.h"
    11 #import "BDAlias.h"
    1211
     12@class BDAlias;
    1313
    1414@interface PSMovieAlert : PSAlert {
     
    2222+ (PSMovieAlert *)alertWithMovieFileAlias:(BDAlias *)anAlias repetitions:(unsigned short)numReps;
    2323
    24 - (id)initWithAlias:(BDAlias *)anAlias repetitions:(unsigned int) numReps;
     24- (id)initWithMovieFileAlias:(BDAlias *)anAlias repetitions:(unsigned int) numReps;
    2525
    2626- (BOOL)hasVideo;
    2727- (NSMovie *)movie;
     28- (BDAlias *)movieFileAlias;
    2829- (unsigned short)repetitions;
    2930
  • trunk/Cocoa/Pester/Source/PSMovieAlert.m

    r45 r53  
    1010#import "PSMovieAlert.h"
    1111#import "PSMovieAlertController.h"
     12#import "NSDictionary-NJRExtensions.h"
    1213#import "NSMovie-NJRExtensions.h"
     14#import "BDAlias.h"
     15
     16// property list keys
     17static NSString * const PLAlertRepetitions = @"times"; // NSString
     18static NSString * const PLAlertAlias = @"alias"; // NSData
    1319
    1420@implementation PSMovieAlert
     
    1622+ (PSMovieAlert *)alertWithMovieFileAlias:(BDAlias *)anAlias repetitions:(unsigned short)numReps;
    1723{
    18     return [[[self alloc] initWithAlias: anAlias repetitions: numReps] autorelease];
     24    return [[[self alloc] initWithMovieFileAlias: anAlias repetitions: numReps] autorelease];
    1925}
    2026
    21 - (id)initWithAlias:(BDAlias *)anAlias repetitions:(unsigned int) numReps;
     27- (id)initWithMovieFileAlias:(BDAlias *)anAlias repetitions:(unsigned int) numReps;
    2228{
    2329    if ( (self = [super init]) != nil) {
     30        NSString *path = [anAlias fullPath];
     31        if (path == nil) {
     32            [self release];
     33            [NSException raise: PSAlertCreationException format: @"CanÕt locate media to play as alert."];
     34        }
    2435        alias = [anAlias retain];
    2536        repetitions = numReps;
    26         movie = [[NSMovie alloc] initWithURL: [NSURL fileURLWithPath: [anAlias fullPath]] byReference: YES];
     37        movie = [[NSMovie alloc] initWithURL: [NSURL fileURLWithPath: path] byReference: YES];
    2738        if (movie == nil) {
    2839            [self release];
     
    4657}
    4758
     59- (BOOL)requiresPesterFrontmost;
     60{
     61    return hasVideo;
     62}
     63
    4864- (NSMovie *)movie;
    4965{
    5066    return movie;
     67}
     68
     69- (BDAlias *)movieFileAlias;
     70{
     71    return alias;
    5172}
    5273
     
    7394}
    7495
     96- (NSAttributedString *)actionDescription;
     97{
     98    BOOL isStatic = [movie isStatic];
     99    NSMutableAttributedString *string = [[(isStatic ? @"Show " : @"Play ") small] mutableCopy];
     100    NSString *kindString = nil, *name = [alias displayNameWithKindString: &kindString];
     101    if (name == nil) name = @"ÇcanÕt locate media fileÈ";
     102    else [string appendAttributedString: [[NSString stringWithFormat: @"%@ ", kindString] small]];
     103    [string appendAttributedString: [name underlined]];
     104    if (repetitions > 1 && !isStatic) {
     105        [string appendAttributedString: [[NSString stringWithFormat: @" %hu times", repetitions] small]];
     106    }
     107    return [string autorelease];
     108}
     109
     110#pragma mark property list serialization (Pester 1.1)
     111
     112- (NSDictionary *)propertyListRepresentation;
     113{
     114    NSMutableDictionary *plAlert = [[super propertyListRepresentation] mutableCopy];
     115    [plAlert setObject: [NSNumber numberWithUnsignedShort: repetitions] forKey: PLAlertRepetitions];
     116    [plAlert setObject: [alias aliasData] forKey: PLAlertAlias];
     117    return [plAlert autorelease];
     118}
     119
     120- (id)initWithPropertyList:(NSDictionary *)dict;
     121{
     122    return [self initWithMovieFileAlias: [BDAlias aliasWithData: [dict objectForRequiredKey: PLAlertAlias]]
     123                            repetitions: [[dict objectForRequiredKey: PLAlertRepetitions] unsignedShortValue]];
     124}
     125
    75126@end
  • trunk/Cocoa/Pester/Source/PSMovieAlertController.h

    r34 r53  
    1313
    1414@interface PSMovieAlertController : NSWindowController {
     15    PSAlarm *alarm;
     16    PSMovieAlert *alert;
    1517    IBOutlet NSMovieView *movieView;
    1618    void *theMovie; /* Movie */
     
    1921}
    2022
    21 + (PSMovieAlertController *)controllerWithAlarm:(PSAlarm *)alarm movieAlert:(PSMovieAlert *)alert;
     23+ (PSMovieAlertController *)controllerWithAlarm:(PSAlarm *)anAlarm movieAlert:(PSMovieAlert *)anAlert;
    2224
    23 - (id)initWithAlarm:(PSAlarm *)alarm movieAlert:(PSMovieAlert *)alert;
     25- (id)initWithAlarm:(PSAlarm *)anAlarm movieAlert:(PSMovieAlert *)anAlert;
    2426
    2527@end
  • trunk/Cocoa/Pester/Source/PSMovieAlertController.m

    r45 r53  
    1717@implementation PSMovieAlertController
    1818
    19 + (PSMovieAlertController *)controllerWithAlarm:(PSAlarm *)alarm movieAlert:(PSMovieAlert *)alert;
     19+ (PSMovieAlertController *)controllerWithAlarm:(PSAlarm *)anAlarm movieAlert:(PSMovieAlert *)anAlert;
    2020{
    21     return [[self alloc] initWithAlarm: alarm movieAlert: alert];
     21    return [[self alloc] initWithAlarm: anAlarm movieAlert: anAlert];
    2222}
    2323
     
    3939}
    4040
    41 - (id)initWithAlarm:(PSAlarm *)alarm movieAlert:(PSMovieAlert *)alert;
     41- (id)initWithAlarm:(PSAlarm *)anAlarm movieAlert:(PSMovieAlert *)anAlert;
    4242{
    4343    if ([self initWithWindowNibName: @"Movie alert"]) {
    44         NSMovie *movie = [alert movie];
     44        NSMovie *movie = [anAlert movie];
    4545        NSWindow *window = [self window]; // connect outlets
     46        alarm = anAlarm;
     47        alert = anAlert;
    4648        [movieView setMovie: movie];
    4749        theMovie = [movie QTMovie];
     
    6769            [window setFrame: frame display: NO];
    6870            [window setTitle: [alarm message]];
     71            {   // XXX workaround for (IMO) ugly appearance of Cocoa utility windows
     72                NSView *miniButton = [window standardWindowButton: NSWindowMiniaturizeButton],
     73                *zoomButton = [window standardWindowButton: NSWindowZoomButton];
     74                // NOTE: this will not work if the window is resizable: when the frame is reset, the standard buttons reappear
     75                [miniButton setFrameOrigin: NSMakePoint(-100, -100)];
     76                [zoomButton setFrameOrigin: NSMakePoint(-100, -100)];
     77                [[miniButton superview] setNeedsDisplay: YES];
     78                [[zoomButton superview] setNeedsDisplay: YES];
     79            }
    6980            [[self window] orderFrontRegardless];
    7081        }
     
    91102    repetitions = 0;
    92103    [movieView stop: self];
    93     [self release];
     104    [alert completedForAlarm: alarm];
     105    [self autorelease];
    94106    // note: there may still be a retained copy of this object until the runloop timer has let go of us at the end of the current movie playback cycle; donÕt worry about it.
    95107}
  • trunk/Cocoa/Pester/Source/PSNotifierAlert.m

    r34 r53  
    2626}
    2727
     28- (BOOL)requiresPesterFrontmost;
     29{
     30    return YES;
     31}
     32
     33- (NSAttributedString *)actionDescription;
     34{
     35    return [@"Display message and time" small];
     36}
     37
     38#pragma mark property list serialization (Pester 1.1)
     39
     40- (id)initWithPropertyList:(NSDictionary *)dict;
     41{
     42    [self release];
     43    return [[PSNotifierAlert alert] retain];
     44}
     45
    2846@end
  • trunk/Cocoa/Pester/Source/PSScriptAlert.h

    r34 r53  
    1818- (id)initWithScriptFileAlias:(BDAlias *)anAlias;
    1919
     20- (BDAlias *)scriptFileAlias;
     21
    2022@end
  • trunk/Cocoa/Pester/Source/PSScriptAlert.m

    r34 r53  
    99#import "PSScriptAlert.h"
    1010#import "BDAlias.h"
    11 #import "NDAppleScriptObject.h"
     11// XXX remove #import "NDAppleScriptObject.h"
     12#import "NSDictionary-NJRExtensions.h"
     13
     14// property list keys
     15static NSString * const PLAlertAlias = @"alias"; // NSData
    1216
    1317@implementation PSScriptAlert
     
    2630}
    2731
     32- (BDAlias *)scriptFileAlias;
     33{
     34    return alias;
     35}
     36
    2837- (void)triggerForAlarm:(PSAlarm *)alarm;
    2938{
    30     NDAppleScriptObject *as = [NDAppleScriptObject appleScriptObjectWithContentsOfFile: [alias fullPath]];
     39    NSString *scriptPath = [alias fullPath];
    3140
    32     if (as != nil) {
    33         [as execute];
     41    if (scriptPath == nil) {
     42        NSRunAlertPanel(@"CanÕt find script", @"Pester couldnÕt find the script for the alarm Ò%@Ó.",
     43                        nil, nil, nil, [alarm message]);
     44    } else {
     45        NSDictionary *errorInfo;
     46        NSAppleScript *script = [[NSAppleScript alloc] initWithContentsOfURL: [NSURL fileURLWithPath: scriptPath] error: &errorInfo];
     47        if (script == nil) {
     48            NSString *errorMessage = [errorInfo objectForKey: NSAppleScriptErrorMessage];
     49            NSNumber *errorNumber = [errorInfo objectForKey: NSAppleScriptErrorNumber];
     50            NSString *appName = [errorInfo objectForKey: NSAppleScriptErrorAppName];
     51            if (errorMessage == nil) errorMessage = [errorInfo objectForKey: NSAppleScriptErrorBriefMessage];
     52            NSRunAlertPanel(@"Script loading error",
     53                            @"Pester encountered an error while attempting to load Ò%@Ó%@ %@",
     54                            nil, nil, nil,
     55                            [[NSFileManager defaultManager] displayNameAtPath: scriptPath],
     56                            errorMessage == nil ? @"" : [NSString stringWithFormat: @":\n\n%@%@", appName == nil ? @"" : @"Ò%@Ó reported an error: ", errorMessage],
     57                            errorNumber == nil ? @"" : [NSString stringWithFormat: @"(%@)", errorNumber]);
     58        } else {
     59            NSAppleEventDescriptor *scriptResult = [script executeAndReturnError: &errorInfo];
     60            if (scriptResult == nil) {
     61                NSString *errorMessage = [errorInfo objectForKey: NSAppleScriptErrorMessage];
     62                NSNumber *errorNumber = [errorInfo objectForKey: NSAppleScriptErrorNumber];
     63                NSString *appName = [errorInfo objectForKey: NSAppleScriptErrorAppName];
     64                if (errorMessage == nil) errorMessage = [errorInfo objectForKey: NSAppleScriptErrorBriefMessage];
     65                NSRunAlertPanel(@"Script execution error",
     66                                @"Pester encountered an error while attempting to execute the script Ò%@Ó%@ %@",
     67                                nil, nil, nil,
     68                                [[NSFileManager defaultManager] displayNameAtPath: scriptPath],
     69                                errorMessage == nil ? @"" : [NSString stringWithFormat: @":\n\n%@%@", appName == nil ? @"" : @"Ò%@Ó reported an error: ", errorMessage],
     70                                errorNumber == nil ? @"" : [NSString stringWithFormat: @"(%@)", errorNumber]);
     71            }
     72        }
    3473    }
     74    [self completedForAlarm: alarm];
    3575}
     76
     77- (NSAttributedString *)actionDescription;
     78{
     79    NSMutableAttributedString *string = [[@"Do script " small] mutableCopy];
     80    NSString *scriptName = [alias displayNameWithKindString: NULL];
     81    if (scriptName == nil) scriptName = @"ÇcanÕt locate scriptÈ";
     82    [string appendAttributedString: [scriptName underlined]];
     83    return [string autorelease];
     84}
     85
     86#pragma mark property list serialization (Pester 1.1)
     87
     88- (NSDictionary *)propertyListRepresentation;
     89{
     90    NSMutableDictionary *plAlert = [[super propertyListRepresentation] mutableCopy];
     91    [plAlert setObject: [alias aliasData] forKey: PLAlertAlias];
     92    return [plAlert autorelease];
     93}
     94
     95- (id)initWithPropertyList:(NSDictionary *)dict;
     96{
     97    return [self initWithScriptFileAlias: [BDAlias aliasWithData: [dict objectForRequiredKey: PLAlertAlias]]];
     98}
     99
    36100@end
  • trunk/Cocoa/Pester/Source/PSSpeechAlert.h

    r34 r53  
    88
    99#import "PSAlert.h"
    10 #import "SUSpeaker.h"
     10
     11@class SUSpeaker;
    1112
    1213@interface PSSpeechAlert : PSAlert {
    1314    SUSpeaker *speaker;
    1415    NSString *voice;
     16    PSAlarm *alarm;
    1517}
    1618
     
    1921- (id)initWithVoice:(NSString *)aVoice;
    2022
     23- (NSString *)voice;
     24
    2125@end
  • trunk/Cocoa/Pester/Source/PSSpeechAlert.m

    r34 r53  
    44//
    55//  Created by Nicholas Riley on Sat Oct 26 2002.
    6 //  Copyright (c) 2002 __MyCompanyName__. All rights reserved.
     6//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
    77//
    88
    99#import "PSSpeechAlert.h"
    1010#import "PSAlarmAlertController.h"
     11#import "NSDictionary-NJRExtensions.h"
     12#import "SUSpeaker.h"
     13
     14// property list keys
     15static NSString * const PLAlertVoice = @"voice"; // NSString
    1116
    1217@implementation PSSpeechAlert
     
    2025{
    2126    if ( (self = [super init]) != nil) {
    22         voice = aVoice;
     27        voice = [aVoice retain];
    2328    }
    2429    return self;
     
    3338}
    3439
     40- (NSString *)voice;
     41{
     42    return voice;
     43}
     44
    3545- (void)_stopSpeaking:(NSNotification *)notification;
    3646{
    37     [speaker stopSpeaking];
    38     // don't release here, we'll still get the didFinishSpeaking: message as a delegate
     47    [speaker stopSpeaking]; // triggers didFinishSpeaking:
    3948}
    4049
    41 - (void)triggerForAlarm:(PSAlarm *)alarm;
     50- (void)triggerForAlarm:(PSAlarm *)anAlarm;
    4251{
    4352    [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(_stopSpeaking:) name: PSAlarmAlertStopNotification object: nil];
    4453   
    4554    if ( (speaker = [[SUSpeaker alloc] init]) == nil) return;
     55    alarm = anAlarm;
    4656    [speaker setDelegate: self];
    4757    [speaker setVoice: [[SUSpeaker voiceNames] indexOfObject: voice] + 1];
    4858    [speaker speakText: [alarm message]];
    49    
    50     [self retain];
     59}
     60
     61- (NSAttributedString *)actionDescription;
     62{
     63    NSMutableAttributedString *string = [[@"Speak message with voice " small] mutableCopy];
     64    [string appendAttributedString: [voice underlined]];
     65    return [string autorelease];
     66}
     67
     68#pragma mark property list serialization (Pester 1.1)
     69
     70- (NSDictionary *)propertyListRepresentation;
     71{
     72    NSMutableDictionary *plAlert = [[super propertyListRepresentation] mutableCopy];
     73    [plAlert setObject: voice forKey: PLAlertVoice];
     74    return [plAlert autorelease];
     75}
     76
     77- (id)initWithPropertyList:(NSDictionary *)dict;
     78{
     79    if ( (self = [self init]) != nil) {
     80        voice = [dict objectForRequiredKey: PLAlertVoice];
     81    }
     82    return self;
    5183}
    5284
     
    5587@implementation PSSpeechAlert (SUSpeakerDelegate)
    5688
    57 - (void)didFinishSpeaking:(SUSpeaker*)speaker;
     89- (void)didFinishSpeaking:(SUSpeaker *)aSpeaker;
    5890{
    59     [self release];
     91    [self completedForAlarm: alarm];
     92    [speaker release]; speaker = nil;
    6093}
    6194
  • trunk/Cocoa/Pester/Source/Pester.pbproj/nicholas.pbxuser

    r51 r53  
    22{
    33        29B97313FDCFA39411CA2CEA = {
    4                 activeBuildStyle = 4A9504CDFFE6A4B311CA0CBA;
     4                activeBuildStyle = 4A9504CCFFE6A4B311CA0CBA;
    55                activeExecutable = F59B967103538BE201154278;
    66                activeTarget = 29B97326FDCFA39411CA2CEA;
     
    1212                        F500E5370359719B01AEEDB1,
    1313                        F5B15F74037E42EB01AEEDB1,
     14                        E1A9DEE703BF3C110089E8B2,
     15                        E17AED7003C0F4D600B89ACF,
     16                        E1156BFA03C148E2006717DC,
     17                        E1156C2C03C394D6006717DC,
     18                        E117A4AB03C3E2FD00E10BA2,
    1419                );
    1520                executables = (
     
    1722                );
    1823                perUserDictionary = {
    19                         PBXPerProjectTemplateStateSaveDate = 59271904;
    20                         "PBXTemplateGeometry-F5314676015831810DCA290F" = {
    21                                 ContentSize = "{618, 257}";
     24                        PBXPerProjectTemplateStateSaveDate = 63172038;
     25                        "PBXTemplateGeometry-F5CA7ECB015C094F0DCA290F" = {
     26                                ContentSize = "{668, 621}";
    2227                                LeftSlideOut = {
    2328                                        Collapsed = NO;
    24                                         Frame = "{{0, 23}, {618, 234}}";
     29                                        Frame = "{{0, 0}, {668, 621}}";
    2530                                        Split0 = {
    26                                                 ActiveTab = 2;
    27                                                 Collapsed = YES;
    28                                                 Frame = "{{0, 0}, {618, 234}}";
     31                                                Collapsed = NO;
     32                                                Frame = "{{0, 0}, {668, 621}}";
    2933                                                Split0 = {
    30                                                         Frame = "{{1e+06, 1e+06}, {618, 0}}";
     34                                                        Frame = "{{0, 0}, {668, 621}}";
    3135                                                };
    3236                                                SplitCount = 1;
    3337                                                Tab0 = {
     38                                                        Frame = "{{0, 0}, {484, 208}}";
     39                                                };
     40                                                Tab1 = {
    3441                                                        Debugger = {
    3542                                                                Collapsed = NO;
    36                                                                 Frame = "{{0, 0}, {952, 321}}";
     43                                                                Frame = "{{0, 0}, {664, 208}}";
    3744                                                                Split0 = {
    38                                                                         Frame = "{{0, 24}, {952, 297}}";
     45                                                                        Frame = "{{0, 24}, {664, 184}}";
    3946                                                                        Split0 = {
    40                                                                                 Frame = "{{0, 0}, {468, 297}}";
     47                                                                                Frame = "{{0, 0}, {325, 184}}";
    4148                                                                        };
    4249                                                                        Split1 = {
    4350                                                                                DebugVariablesTableConfiguration = (
    4451                                                                                        Name,
    45                                                                                         126.803,
     52                                                                                        123,
    4653                                                                                        Value,
    47                                                                                         150.074,
     54                                                                                        85,
    4855                                                                                        Summary,
    49                                                                                         172.123,
     56                                                                                        96.123,
    5057                                                                                );
    51                                                                                 Frame = "{{477, 0}, {475, 297}}";
     58                                                                                Frame = "{{334, 0}, {330, 184}}";
    5259                                                                        };
    5360                                                                        SplitCount = 2;
     
    6370                                                                TabsVisible = YES;
    6471                                                        };
    65                                                         Frame = "{{0, 0}, {952, 321}}";
     72                                                        Frame = "{{0, 0}, {664, 208}}";
    6673                                                        LauncherConfigVersion = 7;
    6774                                                };
    68                                                 Tab1 = {
    69                                                         Frame = "{{0, 0}, {781, 452}}";
     75                                                Tab2 = {
     76                                                        Frame = "{{0, 0}, {664, 50}}";
    7077                                                        LauncherConfigVersion = 3;
    7178                                                        Runner = {
    72                                                                 Frame = "{{0, 0}, {781, 452}}";
     79                                                                Frame = "{{0, 0}, {664, 50}}";
    7380                                                        };
    7481                                                };
    75                                                 Tab2 = {
    76                                                         BuildMessageFrame = "{{0, 0}, {620, 123}}";
    77                                                         BuildTranscriptFrame = "{{0, 132}, {620, 98}}";
    78                                                         Frame = "{{0, 0}, {618, 228}}";
    79                                                 };
    8082                                                Tab3 = {
     83                                                        BuildMessageFrame = "{{0, 0}, {614, 262}}";
     84                                                        BuildTranscriptFrame = "{{0, 271}, {614, 2}}";
     85                                                        BuildTranscriptFrameExpanded = YES;
    8186                                                        Frame = "{{0, 0}, {612, 295}}";
    8287                                                };
    83                                                 TabCount = 4;
    84                                                 TabsVisible = NO;
    85                                         };
    86                                         SplitCount = 1;
    87                                         Tab0 = {
    88                                                 Frame = "{{0, 0}, {300, 533}}";
    89                                                 GroupTreeTableConfiguration = (
    90                                                         TargetStatusColumn,
    91                                                         18,
    92                                                         MainColumn,
    93                                                         267,
    94                                                 );
    95                                         };
    96                                         Tab1 = {
    97                                                 ClassesFrame = "{{0, 0}, {280, 398}}";
    98                                                 ClassesTreeTableConfiguration = (
    99                                                         PBXBookColumnIdentifier,
    100                                                         20,
    101                                                         PBXClassColumnIdentifier,
    102                                                         237,
    103                                                 );
    104                                                 Frame = "{{0, 0}, {278, 659}}";
    105                                                 MembersFrame = "{{0, 407}, {280, 252}}";
    106                                                 MembersTreeTableConfiguration = (
    107                                                         PBXBookColumnIdentifier,
    108                                                         20,
    109                                                         PBXMethodColumnIdentifier,
    110                                                         236,
    111                                                 );
    112                                         };
    113                                         Tab2 = {
    114                                                 Frame = "{{0, 0}, {200, 100}}";
    115                                         };
    116                                         Tab3 = {
    117                                                 Frame = "{{0, 0}, {200, 100}}";
    118                                                 Split0 = {
    119                                                         Frame = "{{0, 0}, {200, 45}}";
    120                                                         TargetTreeTableConfiguration = (
    121                                                                 ActiveTarget,
    122                                                                 16,
    123                                                                 TargetName,
    124                                                                 168,
    125                                                         );
     88                                                Tab4 = {
     89                                                        Frame = "{{0, 0}, {612, 295}}";
    12690                                                };
    127                                                 Split1 = {
    128                                                         BuildStyleTreeTableConfiguration = (
    129                                                                 IsActive,
    130                                                                 16,
    131                                                                 Name,
    132                                                                 169,
    133                                                         );
    134                                                         Frame = "{{0, 54}, {200, 46}}";
    135                                                 };
    136                                                 SplitCount = 2;
    137                                         };
    138                                         Tab4 = {
    139                                                 ExecutableTreeTableConfiguration = (
    140                                                         ActiveExecutable,
    141                                                         16,
    142                                                         ExecutableName,
    143                                                         217,
    144                                                 );
    145                                                 Frame = "{{0, 0}, {250, 100}}";
    146                                         };
    147                                         Tab5 = {
    148                                                 BreakpointsTreeTableConfiguration = (
    149                                                         breakpointColumn,
    150                                                         197,
    151                                                         enabledColumn,
    152                                                         31,
    153                                                 );
    154                                                 Frame = "{{0, 0}, {250, 100}}";
    155                                         };
    156                                         TabCount = 6;
    157                                         TabsVisible = NO;
    158                                 };
    159                                 StatusViewVisible = YES;
    160                                 Template = F5314676015831810DCA290F;
    161                                 ToolbarVisible = YES;
    162                                 WindowLocation = "{460, 270}";
    163                         };
    164                         "PBXTemplateGeometry-F5CA7EC9015C08ED0DCA290F" = {
    165                                 ContentSize = "{665, 594}";
    166                                 LeftSlideOut = {
    167                                         Collapsed = NO;
    168                                         Frame = "{{0, 0}, {665, 594}}";
    169                                         Split0 = {
    170                                                 ActiveTab = 3;
    171                                                 Collapsed = NO;
    172                                                 Frame = "{{0, 0}, {665, 594}}";
    173                                                 Split0 = {
    174                                                         Frame = "{{0, 304}, {665, 290}}";
    175                                                 };
    176                                                 SplitCount = 1;
    177                                                 Tab0 = {
    178                                                         Debugger = {
    179                                                                 Collapsed = NO;
    180                                                                 Frame = "{{0, 0}, {484, 208}}";
    181                                                                 Split0 = {
    182                                                                         Frame = "{{0, 24}, {484, 184}}";
    183                                                                         Split0 = {
    184                                                                                 Frame = "{{0, 0}, {236, 184}}";
    185                                                                         };
    186                                                                         Split1 = {
    187                                                                                 DebugVariablesTableConfiguration = (
    188                                                                                         Name,
    189                                                                                         48.80299,
    190                                                                                         Value,
    191                                                                                         71.07401,
    192                                                                                         Summary,
    193                                                                                         93.123,
    194                                                                                 );
    195                                                                                 Frame = "{{245, 0}, {239, 184}}";
    196                                                                         };
    197                                                                         SplitCount = 2;
    198                                                                 };
    199                                                                 SplitCount = 1;
    200                                                                 Tab0 = {
    201                                                                         Frame = "{{0, 0}, {100, 50}}";
    202                                                                 };
    203                                                                 Tab1 = {
    204                                                                         Frame = "{{0, 0}, {100, 50}}";
    205                                                                 };
    206                                                                 TabCount = 2;
    207                                                                 TabsVisible = YES;
    208                                                         };
    209                                                         Frame = "{{0, 0}, {484, 208}}";
    210                                                         LauncherConfigVersion = 7;
    211                                                 };
    212                                                 Tab1 = {
    213                                                         Frame = "{{0, 0}, {664, 208}}";
    214                                                         LauncherConfigVersion = 3;
    215                                                         Runner = {
    216                                                                 Frame = "{{0, 0}, {664, 208}}";
    217                                                         };
    218                                                 };
    219                                                 Tab2 = {
    220                                                         BuildMessageFrame = "{{0, 0}, {666, 43}}";
    221                                                         BuildTranscriptFrame = "{{0, 52}, {666, 0}}";
    222                                                         Frame = "{{0, 0}, {664, 50}}";
    223                                                 };
    224                                                 Tab3 = {
    225                                                         Frame = "{{0, 0}, {665, 298}}";
    226                                                 };
    227                                                 TabCount = 4;
     91                                                TabCount = 5;
    22892                                                TabsVisible = NO;
    22993                                        };
     
    260124                                        Tab3 = {
    261125                                                Frame = "{{0, 0}, {200, 557}}";
    262                                                 Split0 = {
    263                                                         Frame = "{{0, 0}, {200, 270}}";
    264                                                         TargetTreeTableConfiguration = (
    265                                                                 ActiveTarget,
    266                                                                 16,
    267                                                                 TargetName,
    268                                                                 168,
    269                                                         );
    270                                                 };
    271                                                 Split1 = {
    272                                                         BuildStyleTreeTableConfiguration = (
    273                                                                 IsActive,
    274                                                                 16,
    275                                                                 Name,
    276                                                                 169,
    277                                                         );
    278                                                         Frame = "{{0, 279}, {200, 278}}";
    279                                                 };
    280                                                 SplitCount = 2;
     126                                                TargetTableConfiguration = (
     127                                                        ActiveObject,
     128                                                        16,
     129                                                        ObjectNames,
     130                                                        202.296,
     131                                                );
    281132                                        };
    282133                                        Tab4 = {
    283                                                 ExecutableTreeTableConfiguration = (
    284                                                         ActiveExecutable,
    285                                                         16,
    286                                                         ExecutableName,
    287                                                         217,
    288                                                 );
    289                                                 Frame = "{{0, 0}, {250, 100}}";
    290                                         };
    291                                         Tab5 = {
    292134                                                BreakpointsTreeTableConfiguration = (
    293135                                                        breakpointColumn,
     
    298140                                                Frame = "{{0, 0}, {250, 100}}";
    299141                                        };
    300                                         TabCount = 6;
     142                                        TabCount = 5;
    301143                                        TabsVisible = NO;
    302144                                };
    303                                 StatusViewVisible = NO;
    304                                 Template = F5CA7EC9015C08ED0DCA290F;
    305                                 ToolbarVisible = NO;
    306                                 WindowLocation = "{69, 216}";
    307                         };
    308                         "PBXTemplateGeometry-F5CA7ECB015C094F0DCA290F" = {
    309                                 ContentSize = "{668, 621}";
    310                                 LeftSlideOut = {
    311                                         Collapsed = NO;
    312                                         Frame = "{{0, 0}, {668, 621}}";
    313                                         Split0 = {
    314                                                 Collapsed = NO;
    315                                                 Frame = "{{0, 0}, {668, 621}}";
    316                                                 Split0 = {
    317                                                         Frame = "{{0, 0}, {668, 621}}";
    318                                                 };
    319                                                 SplitCount = 1;
    320                                                 Tab0 = {
    321                                                         Debugger = {
    322                                                                 Collapsed = NO;
    323                                                                 Frame = "{{0, 0}, {484, 208}}";
    324                                                                 Split0 = {
    325                                                                         Frame = "{{0, 24}, {484, 184}}";
    326                                                                         Split0 = {
    327                                                                                 Frame = "{{0, 0}, {236, 184}}";
    328                                                                         };
    329                                                                         Split1 = {
    330                                                                                 DebugVariablesTableConfiguration = (
    331                                                                                         Name,
    332                                                                                         123,
    333                                                                                         Value,
    334                                                                                         85,
    335                                                                                         Summary,
    336                                                                                         62.123,
    337                                                                                 );
    338                                                                                 Frame = "{{245, 0}, {239, 184}}";
    339                                                                         };
    340                                                                         SplitCount = 2;
    341                                                                 };
    342                                                                 SplitCount = 1;
    343                                                                 Tab0 = {
    344                                                                         Frame = "{{0, 0}, {100, 50}}";
    345                                                                 };
    346                                                                 Tab1 = {
    347                                                                         Frame = "{{0, 0}, {100, 50}}";
    348                                                                 };
    349                                                                 TabCount = 2;
    350                                                                 TabsVisible = YES;
    351                                                         };
    352                                                         Frame = "{{0, 0}, {484, 208}}";
    353                                                         LauncherConfigVersion = 7;
    354                                                 };
    355                                                 Tab1 = {
    356                                                         Frame = "{{0, 0}, {664, 208}}";
    357                                                         LauncherConfigVersion = 3;
    358                                                         Runner = {
    359                                                                 Frame = "{{0, 0}, {664, 208}}";
    360                                                         };
    361                                                 };
    362                                                 Tab2 = {
    363                                                         BuildMessageFrame = "{{0, 0}, {666, 43}}";
    364                                                         BuildTranscriptFrame = "{{0, 52}, {666, 0}}";
    365                                                         Frame = "{{0, 0}, {664, 50}}";
    366                                                 };
    367                                                 Tab3 = {
    368                                                         Frame = "{{0, 0}, {612, 295}}";
    369                                                 };
    370                                                 TabCount = 4;
    371                                                 TabsVisible = NO;
    372                                         };
    373                                         SplitCount = 1;
    374                                         Tab0 = {
    375                                                 Frame = "{{0, 0}, {313, 531}}";
    376                                                 GroupTreeTableConfiguration = (
    377                                                         TargetStatusColumn,
    378                                                         18,
    379                                                         MainColumn,
    380                                                         280,
    381                                                 );
    382                                         };
    383                                         Tab1 = {
    384                                                 ClassesFrame = "{{0, 0}, {280, 398}}";
    385                                                 ClassesTreeTableConfiguration = (
    386                                                         PBXBookColumnIdentifier,
    387                                                         20,
    388                                                         PBXClassColumnIdentifier,
    389                                                         237,
    390                                                 );
    391                                                 Frame = "{{0, 0}, {278, 659}}";
    392                                                 MembersFrame = "{{0, 407}, {280, 252}}";
    393                                                 MembersTreeTableConfiguration = (
    394                                                         PBXBookColumnIdentifier,
    395                                                         20,
    396                                                         PBXMethodColumnIdentifier,
    397                                                         236,
    398                                                 );
    399                                         };
    400                                         Tab2 = {
    401                                                 Frame = "{{0, 0}, {200, 100}}";
    402                                         };
    403                                         Tab3 = {
    404                                                 Frame = "{{0, 0}, {200, 557}}";
    405                                                 Split0 = {
    406                                                         Frame = "{{0, 0}, {200, 270}}";
    407                                                         TargetTreeTableConfiguration = (
    408                                                                 ActiveTarget,
    409                                                                 16,
    410                                                                 TargetName,
    411                                                                 168,
    412                                                         );
    413                                                 };
    414                                                 Split1 = {
    415                                                         BuildStyleTreeTableConfiguration = (
    416                                                                 IsActive,
    417                                                                 16,
    418                                                                 Name,
    419                                                                 169,
    420                                                         );
    421                                                         Frame = "{{0, 279}, {200, 278}}";
    422                                                 };
    423                                                 SplitCount = 2;
    424                                         };
    425                                         Tab4 = {
    426                                                 ExecutableTreeTableConfiguration = (
    427                                                         ActiveExecutable,
    428                                                         16,
    429                                                         ExecutableName,
    430                                                         217,
    431                                                 );
    432                                                 Frame = "{{0, 0}, {250, 100}}";
    433                                         };
    434                                         Tab5 = {
    435                                                 BreakpointsTreeTableConfiguration = (
    436                                                         breakpointColumn,
    437                                                         197,
    438                                                         enabledColumn,
    439                                                         31,
    440                                                 );
    441                                                 Frame = "{{0, 0}, {250, 100}}";
    442                                         };
    443                                         TabCount = 6;
    444                                         TabsVisible = NO;
    445                                 };
     145                                NavBarShownByDefault = YES;
    446146                                StatusViewVisible = NO;
    447147                                Template = F5CA7ECB015C094F0DCA290F;
    448148                                ToolbarVisible = NO;
    449                                 WindowLocation = "{361, 189}";
    450                         };
    451                         "PBXTemplateGeometry-F5CA7ECC015C09990DCA290F" = {
    452                                 ContentSize = "{773, 558}";
    453                                 LeftSlideOut = {
    454                                         Collapsed = NO;
    455                                         Frame = "{{0, 23}, {773, 535}}";
    456                                         Split0 = {
    457                                                 ActiveTab = 0;
    458                                                 Collapsed = NO;
    459                                                 Frame = "{{24, 0}, {749, 535}}";
    460                                                 Split0 = {
    461                                                         Frame = "{{0, 337}, {749, 198}}";
    462                                                 };
    463                                                 SplitCount = 1;
    464                                                 Tab0 = {
    465                                                         Debugger = {
    466                                                                 ActiveTab = 1;
    467                                                                 Collapsed = NO;
    468                                                                 Frame = "{{0, 0}, {749, 331}}";
    469                                                                 Split0 = {
    470                                                                         Frame = "{{0, 127}, {749, 204}}";
    471                                                                         Split0 = {
    472                                                                                 Frame = "{{0, 0}, {366, 204}}";
    473                                                                         };
    474                                                                         Split1 = {
    475                                                                                 DebugVariablesTableConfiguration = (
    476                                                                                         Name,
    477                                                                                         120.803,
    478                                                                                         Value,
    479                                                                                         116.074,
    480                                                                                         Summary,
    481                                                                                         111.123,
    482                                                                                 );
    483                                                                                 Frame = "{{375, 0}, {374, 204}}";
    484                                                                         };
    485                                                                         SplitCount = 2;
    486                                                                 };
    487                                                                 SplitCount = 1;
    488                                                                 Tab0 = {
    489                                                                         Frame = "{{0, 0}, {100, 50}}";
    490                                                                 };
    491                                                                 Tab1 = {
    492                                                                         Frame = "{{0, 0}, {749, 103}}";
    493                                                                 };
    494                                                                 TabCount = 2;
    495                                                                 TabsVisible = YES;
    496                                                         };
    497                                                         Frame = "{{0, 0}, {749, 331}}";
    498                                                         LauncherConfigVersion = 7;
    499                                                 };
    500                                                 Tab1 = {
    501                                                         Frame = "{{0, 0}, {664, 208}}";
    502                                                         LauncherConfigVersion = 3;
    503                                                         Runner = {
    504                                                                 Frame = "{{0, 0}, {664, 208}}";
    505                                                         };
    506                                                 };
    507                                                 Tab2 = {
    508                                                         BuildMessageFrame = "{{0, 0}, {666, 43}}";
    509                                                         BuildTranscriptFrame = "{{0, 52}, {666, 0}}";
    510                                                         Frame = "{{0, 0}, {664, 50}}";
    511                                                 };
    512                                                 Tab3 = {
    513                                                         Frame = "{{0, 0}, {612, 295}}";
    514                                                 };
    515                                                 TabCount = 4;
    516                                                 TabsVisible = NO;
    517                                         };
    518                                         SplitCount = 1;
    519                                         Tab0 = {
    520                                                 Frame = "{{0, 0}, {313, 531}}";
    521                                                 GroupTreeTableConfiguration = (
    522                                                         TargetStatusColumn,
    523                                                         18,
    524                                                         MainColumn,
    525                                                         280,
    526                                                 );
    527                                         };
    528                                         Tab1 = {
    529                                                 ClassesFrame = "{{0, 0}, {280, 398}}";
    530                                                 ClassesTreeTableConfiguration = (
    531                                                         PBXBookColumnIdentifier,
    532                                                         20,
    533                                                         PBXClassColumnIdentifier,
    534                                                         237,
    535                                                 );
    536                                                 Frame = "{{0, 0}, {278, 659}}";
    537                                                 MembersFrame = "{{0, 407}, {280, 252}}";
    538                                                 MembersTreeTableConfiguration = (
    539                                                         PBXBookColumnIdentifier,
    540                                                         20,
    541                                                         PBXMethodColumnIdentifier,
    542                                                         236,
    543                                                 );
    544                                         };
    545                                         Tab2 = {
    546                                                 Frame = "{{0, 0}, {200, 100}}";
    547                                         };
    548                                         Tab3 = {
    549                                                 Frame = "{{0, 0}, {200, 557}}";
    550                                                 Split0 = {
    551                                                         Frame = "{{0, 0}, {200, 270}}";
    552                                                         TargetTreeTableConfiguration = (
    553                                                                 ActiveTarget,
    554                                                                 16,
    555                                                                 TargetName,
    556                                                                 168,
    557                                                         );
    558                                                 };
    559                                                 Split1 = {
    560                                                         BuildStyleTreeTableConfiguration = (
    561                                                                 IsActive,
    562                                                                 16,
    563                                                                 Name,
    564                                                                 169,
    565                                                         );
    566                                                         Frame = "{{0, 279}, {200, 278}}";
    567                                                 };
    568                                                 SplitCount = 2;
    569                                         };
    570                                         Tab4 = {
    571                                                 ExecutableTreeTableConfiguration = (
    572                                                         ActiveExecutable,
    573                                                         16,
    574                                                         ExecutableName,
    575                                                         217,
    576                                                 );
    577                                                 Frame = "{{0, 0}, {250, 100}}";
    578                                         };
    579                                         Tab5 = {
    580                                                 BreakpointsTreeTableConfiguration = (
    581                                                         breakpointColumn,
    582                                                         197,
    583                                                         enabledColumn,
    584                                                         31,
    585                                                 );
    586                                                 Frame = "{{0, 0}, {250, 100}}";
    587                                         };
    588                                         TabCount = 6;
    589                                         TabsVisible = YES;
    590                                 };
    591                                 StatusViewVisible = YES;
    592                                 Template = F5CA7ECC015C09990DCA290F;
    593                                 ToolbarVisible = YES;
    594                                 WindowLocation = "{13, 212}";
    595                         };
    596                         "PBXTemplateGeometry-F5E465990156DFB90DCA290F" = {
    597                                 ContentSize = "{352, 553}";
    598                                 LeftSlideOut = {
    599                                         ActiveTab = 0;
    600                                         Collapsed = YES;
    601                                         Frame = "{{0, 23}, {352, 530}}";
    602                                         Split0 = {
    603                                                 Collapsed = NO;
    604                                                 Frame = "{{1e+06, 1e+06}, {0, 530}}";
    605                                                 Split0 = {
    606                                                         Frame = "{{0, 0}, {0, 530}}";
    607                                                 };
    608                                                 SplitCount = 1;
    609                                                 Tab0 = {
    610                                                         Debugger = {
    611                                                                 Collapsed = NO;
    612                                                                 Frame = "{{0, 0}, {484, 208}}";
    613                                                                 Split0 = {
    614                                                                         Frame = "{{0, 24}, {484, 184}}";
    615                                                                         Split0 = {
    616                                                                                 Frame = "{{0, 0}, {236, 184}}";
    617                                                                         };
    618                                                                         Split1 = {
    619                                                                                 DebugVariablesTableConfiguration = (
    620                                                                                         Name,
    621                                                                                         123,
    622                                                                                         Value,
    623                                                                                         85,
    624                                                                                         Summary,
    625                                                                                         62.123,
    626                                                                                 );
    627                                                                                 Frame = "{{245, 0}, {239, 184}}";
    628                                                                         };
    629                                                                         SplitCount = 2;
    630                                                                 };
    631                                                                 SplitCount = 1;
    632                                                                 Tab0 = {
    633                                                                         Frame = "{{0, 0}, {100, 50}}";
    634                                                                 };
    635                                                                 Tab1 = {
    636                                                                         Frame = "{{0, 0}, {100, 50}}";
    637                                                                 };
    638                                                                 TabCount = 2;
    639                                                                 TabsVisible = YES;
    640                                                         };
    641                                                         Frame = "{{0, 0}, {484, 208}}";
    642                                                         LauncherConfigVersion = 7;
    643                                                 };
    644                                                 Tab1 = {
    645                                                         Frame = "{{0, 0}, {664, 208}}";
    646                                                         LauncherConfigVersion = 3;
    647                                                         Runner = {
    648                                                                 Frame = "{{0, 0}, {664, 208}}";
    649                                                         };
    650                                                 };
    651                                                 Tab2 = {
    652                                                         BuildMessageFrame = "{{0, 0}, {666, 43}}";
    653                                                         BuildTranscriptFrame = "{{0, 52}, {666, 0}}";
    654                                                         Frame = "{{0, 0}, {664, 50}}";
    655                                                 };
    656                                                 Tab3 = {
    657                                                         Frame = "{{0, 0}, {612, 295}}";
    658                                                 };
    659                                                 TabCount = 4;
    660                                                 TabsVisible = NO;
    661                                         };
    662                                         SplitCount = 1;
    663                                         Tab0 = {
    664                                                 Frame = "{{0, 0}, {328, 530}}";
    665                                                 GroupTreeTableConfiguration = (
    666                                                         TargetStatusColumn,
    667                                                         18,
    668                                                         MainColumn,
    669                                                         295,
    670                                                 );
    671                                         };
    672                                         Tab1 = {
    673                                                 ClassesFrame = "{{0, 0}, {343, 348}}";
    674                                                 ClassesTreeTableConfiguration = (
    675                                                         PBXBookColumnIdentifier,
    676                                                         20,
    677                                                         PBXClassColumnIdentifier,
    678                                                         300,
    679                                                 );
    680                                                 Frame = "{{0, 0}, {341, 578}}";
    681                                                 MembersFrame = "{{0, 357}, {343, 221}}";
    682                                                 MembersTreeTableConfiguration = (
    683                                                         PBXBookColumnIdentifier,
    684                                                         20,
    685                                                         PBXMethodColumnIdentifier,
    686                                                         299,
    687                                                 );
    688                                         };
    689                                         Tab2 = {
    690                                                 Frame = "{{0, 0}, {319, 576}}";
    691                                         };
    692                                         Tab3 = {
    693                                                 Frame = "{{0, 0}, {341, 578}}";
    694                                                 Split0 = {
    695                                                         Frame = "{{0, 0}, {341, 281}}";
    696                                                         TargetTreeTableConfiguration = (
    697                                                                 ActiveTarget,
    698                                                                 16,
    699                                                                 TargetName,
    700                                                                 309,
    701                                                         );
    702                                                 };
    703                                                 Split1 = {
    704                                                         BuildStyleTreeTableConfiguration = (
    705                                                                 IsActive,
    706                                                                 16,
    707                                                                 Name,
    708                                                                 310,
    709                                                         );
    710                                                         Frame = "{{0, 290}, {341, 288}}";
    711                                                 };
    712                                                 SplitCount = 2;
    713                                         };
    714                                         Tab4 = {
    715                                                 ExecutableTreeTableConfiguration = (
    716                                                         ActiveExecutable,
    717                                                         16,
    718                                                         ExecutableName,
    719                                                         286,
    720                                                 );
    721                                                 Frame = "{{0, 0}, {319, 576}}";
    722                                         };
    723                                         Tab5 = {
    724                                                 BreakpointsTreeTableConfiguration = (
    725                                                         breakpointColumn,
    726                                                         197,
    727                                                         enabledColumn,
    728                                                         31,
    729                                                 );
    730                                                 Frame = "{{0, 0}, {250, 100}}";
    731                                         };
    732                                         TabCount = 6;
    733                                         TabsVisible = YES;
    734                                 };
    735                                 StatusViewVisible = YES;
    736                                 Template = F5E465990156DFB90DCA290F;
    737                                 ToolbarVisible = YES;
    738                                 WindowLocation = "{2, 217}";
     149                                WindowLocation = "{420, 75}";
    739150                        };
    740151                        PBXWorkspaceContents = (
     
    743154                                                Split0 = {
    744155                                                        Split0 = {
     156                                                                NavContent0 = {
     157                                                                        bookmark = E1096EC203C3F504009B92C1;
     158                                                                        history = (
     159                                                                                E1D2937503BD418200F9204E,
     160                                                                                E1D2937B03BD418200F9204E,
     161                                                                                E1D2937C03BD418200F9204E,
     162                                                                                E1D2937E03BD418200F9204E,
     163                                                                                E1D2938303BD418200F9204E,
     164                                                                                E1D2938403BD418200F9204E,
     165                                                                                E1D2938503BD418200F9204E,
     166                                                                                E1D2938603BD418200F9204E,
     167                                                                                E1D2938903BD418200F9204E,
     168                                                                                E195A26303BE251F00186F9B,
     169                                                                                E195A26403BE251F00186F9B,
     170                                                                                E195A26503BE251F00186F9B,
     171                                                                                E195A26603BE251F00186F9B,
     172                                                                                E195A26703BE251F00186F9B,
     173                                                                                E195A26803BE251F00186F9B,
     174                                                                                E195A26C03BE251F00186F9B,
     175                                                                                E195A26D03BE251F00186F9B,
     176                                                                                E195A26E03BE251F00186F9B,
     177                                                                                E195A26F03BE251F00186F9B,
     178                                                                                E195A27603BE251F00186F9B,
     179                                                                                E195A27A03BE251F00186F9B,
     180                                                                                E195A27B03BE251F00186F9B,
     181                                                                                E195A27D03BE251F00186F9B,
     182                                                                                E195A27E03BE251F00186F9B,
     183                                                                                E195A28003BE251F00186F9B,
     184                                                                                E195A28F03BE251F00186F9B,
     185                                                                                E19DCB6C03BE4A0A00B934BF,
     186                                                                                E19DCB7803BE4A0A00B934BF,
     187                                                                                E19DCB8203BE4A0A00B934BF,
     188                                                                                E19DCB8303BE4A0A00B934BF,
     189                                                                                E19DCC8703BE900300B934BF,
     190                                                                                E19DCC8803BE900300B934BF,
     191                                                                                E19DCC8903BE900300B934BF,
     192                                                                                E19DCC8A03BE900300B934BF,
     193                                                                                E19DCC8B03BE900300B934BF,
     194                                                                                E19DCC8C03BE900300B934BF,
     195                                                                                E19DCC8E03BE900300B934BF,
     196                                                                                E19DCC8F03BE900300B934BF,
     197                                                                                E19DCC9203BE900300B934BF,
     198                                                                                E19DCC9603BE900300B934BF,
     199                                                                                E19DCC9703BE900300B934BF,
     200                                                                                E19DCC9C03BE900300B934BF,
     201                                                                                E19DCC9E03BE900300B934BF,
     202                                                                                E1A9DEEB03BF480F0089E8B2,
     203                                                                                E1A9DEEE03BF480F0089E8B2,
     204                                                                                E1A9DEF003BF480F0089E8B2,
     205                                                                                E1A9DEF103BF480F0089E8B2,
     206                                                                                E1A9DEF203BF480F0089E8B2,
     207                                                                                E11CD2CA03BF9B3300C49BEF,
     208                                                                                E11CD2D003BF9B3300C49BEF,
     209                                                                                E11CD2D103BF9B3300C49BEF,
     210                                                                                E11CD2D203BF9B3300C49BEF,
     211                                                                                E11CD2D303BF9B3300C49BEF,
     212                                                                                E11CD2D403BF9B3300C49BEF,
     213                                                                                E11CD2D503BF9B3300C49BEF,
     214                                                                                E11CD2D603BF9B3300C49BEF,
     215                                                                                E11CD2D703BF9B3300C49BEF,
     216                                                                                E11CD2D803BF9B3300C49BEF,
     217                                                                                E11CD2E003BF9B3300C49BEF,
     218                                                                                E17AED7203C0F5D300B89ACF,
     219                                                                                E17AED7303C0F5D300B89ACF,
     220                                                                                E17AED7403C0F5D300B89ACF,
     221                                                                                E17AED7503C0F5D300B89ACF,
     222                                                                                E17AED7603C0F5D300B89ACF,
     223                                                                                E17AED7703C0F5D300B89ACF,
     224                                                                                E17AED7803C0F5D300B89ACF,
     225                                                                                E17AED7903C0F5D300B89ACF,
     226                                                                                E17AED7A03C0F5D300B89ACF,
     227                                                                                E17AED7B03C0F5D300B89ACF,
     228                                                                                E17AED7C03C0F5D300B89ACF,
     229                                                                                E17AED7D03C0F5D300B89ACF,
     230                                                                                E17AED7E03C0F5D300B89ACF,
     231                                                                                E17AED7F03C0F5D300B89ACF,
     232                                                                                E17AED8003C0F5D300B89ACF,
     233                                                                                E17AED8103C0F5D300B89ACF,
     234                                                                                E17AED8203C0F5D300B89ACF,
     235                                                                                E17AED8303C0F5D300B89ACF,
     236                                                                                E17AED8403C0F5D300B89ACF,
     237                                                                                E17AED8503C0F5D300B89ACF,
     238                                                                                E17AED8603C0F5D300B89ACF,
     239                                                                                E17AED8703C0F5D300B89ACF,
     240                                                                                E17AED8803C0F5D300B89ACF,
     241                                                                                E17AED8903C0F5D300B89ACF,
     242                                                                                E117A4AC03C3E9C600E10BA2,
     243                                                                                E117A4AD03C3E9C600E10BA2,
     244                                                                                E117A4AE03C3E9C600E10BA2,
     245                                                                                E117A4AF03C3E9C600E10BA2,
     246                                                                                E117A4B003C3E9C600E10BA2,
     247                                                                                E1096EB603C3EDB0009B92C1,
     248                                                                                E1096EB903C3EDB0009B92C1,
     249                                                                        );
     250                                                                        prevStack = (
     251                                                                                E1D2939103BD418200F9204E,
     252                                                                                E1D2939203BD418200F9204E,
     253                                                                                E1D2939303BD418200F9204E,
     254                                                                                E1D2939403BD418200F9204E,
     255                                                                                E1D2939503BD418200F9204E,
     256                                                                                E1D2939603BD418200F9204E,
     257                                                                                E1D2939703BD418200F9204E,
     258                                                                                E1D2939803BD418200F9204E,
     259                                                                                E1D2939903BD418200F9204E,
     260                                                                                E1D2939A03BD418200F9204E,
     261                                                                                E1D2939B03BD418200F9204E,
     262                                                                                E1D2939C03BD418200F9204E,
     263                                                                                E1D2939D03BD418200F9204E,
     264                                                                                E1D2939E03BD418200F9204E,
     265                                                                                E1D2939F03BD418200F9204E,
     266                                                                                E1D293A003BD418200F9204E,
     267                                                                                E1D293A103BD418200F9204E,
     268                                                                                E1D293A203BD418200F9204E,
     269                                                                                E1D293A303BD418200F9204E,
     270                                                                                E1D293A403BD418200F9204E,
     271                                                                                E1D293A503BD418200F9204E,
     272                                                                                E1D293A603BD418200F9204E,
     273                                                                                E1D293A703BD418200F9204E,
     274                                                                                E1D293A803BD418200F9204E,
     275                                                                                E1D293A903BD418200F9204E,
     276                                                                                E1D293AA03BD418200F9204E,
     277                                                                                E1D293AB03BD418200F9204E,
     278                                                                                E1D293AC03BD418200F9204E,
     279                                                                                E1D293AD03BD418200F9204E,
     280                                                                                E1D293AE03BD418200F9204E,
     281                                                                                E1D293AF03BD418200F9204E,
     282                                                                                E1D293B003BD418200F9204E,
     283                                                                                E1D293B203BD418200F9204E,
     284                                                                                E1D293B403BD418200F9204E,
     285                                                                                E1D293B503BD418200F9204E,
     286                                                                                E1D293B703BD418200F9204E,
     287                                                                                E1D293B903BD418200F9204E,
     288                                                                                E1D293BA03BD418200F9204E,
     289                                                                                E1D293BB03BD418200F9204E,
     290                                                                                E1D293BC03BD418200F9204E,
     291                                                                                E1D293BD03BD418200F9204E,
     292                                                                                E1D293BE03BD418200F9204E,
     293                                                                                E1D293BF03BD418200F9204E,
     294                                                                                E1D293C003BD418200F9204E,
     295                                                                                E1D293C103BD418200F9204E,
     296                                                                                E1D293C203BD418200F9204E,
     297                                                                                E1D293C403BD418200F9204E,
     298                                                                                E1D293C503BD418200F9204E,
     299                                                                                E1D293C603BD418200F9204E,
     300                                                                                E1D293C703BD418200F9204E,
     301                                                                                E1D293C803BD418200F9204E,
     302                                                                                E1D293C903BD418200F9204E,
     303                                                                                E1D293CA03BD418200F9204E,
     304                                                                                E1D293CB03BD418200F9204E,
     305                                                                                E1D293CC03BD418200F9204E,
     306                                                                                E1D293CD03BD418200F9204E,
     307                                                                                E1D293CE03BD418200F9204E,
     308                                                                                E1D293CF03BD418200F9204E,
     309                                                                                E1D293D003BD418200F9204E,
     310                                                                                E1D293D103BD418200F9204E,
     311                                                                                E1D293D203BD418200F9204E,
     312                                                                                E1D293D303BD418200F9204E,
     313                                                                                E1D293D403BD418200F9204E,
     314                                                                                E1D293D503BD418200F9204E,
     315                                                                                E1D293D603BD418200F9204E,
     316                                                                                E1D293D703BD418200F9204E,
     317                                                                                E1D293D803BD418200F9204E,
     318                                                                                E1D293D903BD418200F9204E,
     319                                                                                E1D293DA03BD418200F9204E,
     320                                                                                E1D293DB03BD418200F9204E,
     321                                                                                E1D293DC03BD418200F9204E,
     322                                                                                E1D293DD03BD418200F9204E,
     323                                                                                E1D293DE03BD418200F9204E,
     324                                                                                E1D293DF03BD418200F9204E,
     325                                                                                E1D293E003BD418200F9204E,
     326                                                                                E1D293E103BD418200F9204E,
     327                                                                                E1D293E203BD418200F9204E,
     328                                                                                E1D293E303BD418200F9204E,
     329                                                                                E1D293E403BD418200F9204E,
     330                                                                                E1D293E503BD418200F9204E,
     331                                                                                E1D293E603BD418200F9204E,
     332                                                                                E1D293E703BD418200F9204E,
     333                                                                                E1D293E803BD418200F9204E,
     334                                                                                E1D293E903BD418200F9204E,
     335                                                                                E1D293EA03BD418200F9204E,
     336                                                                                E1D293EB03BD418200F9204E,
     337                                                                                E1D293EC03BD418200F9204E,
     338                                                                                E1D293EE03BD418200F9204E,
     339                                                                                E1D293F003BD418200F9204E,
     340                                                                                E1D293F103BD418200F9204E,
     341                                                                                E1D293F203BD418200F9204E,
     342                                                                                E1D293F303BD418200F9204E,
     343                                                                                E1D293F403BD418200F9204E,
     344                                                                                E1D293F503BD418200F9204E,
     345                                                                                E1D293F703BD418200F9204E,
     346                                                                                E1D293F803BD418200F9204E,
     347                                                                                E1D293F903BD418200F9204E,
     348                                                                                E1D293FB03BD418200F9204E,
     349                                                                                E1D293FC03BD418200F9204E,
     350                                                                                E1D293FD03BD418200F9204E,
     351                                                                                E1D293FE03BD418200F9204E,
     352                                                                                E1D293FF03BD418200F9204E,
     353                                                                                E1D2940003BD418200F9204E,
     354                                                                                E1D2940103BD418200F9204E,
     355                                                                                E1D2940203BD418200F9204E,
     356                                                                                E1D2940303BD418200F9204E,
     357                                                                                E1D2940403BD418200F9204E,
     358                                                                                E1D2940503BD418200F9204E,
     359                                                                                E1D2940603BD418200F9204E,
     360                                                                                E1D2940703BD418200F9204E,
     361                                                                                E1D2940803BD418200F9204E,
     362                                                                                E1D2940903BD418200F9204E,
     363                                                                                E1D2940A03BD418200F9204E,
     364                                                                                E1D2940B03BD418200F9204E,
     365                                                                                E1D2940C03BD418200F9204E,
     366                                                                                E1D2940D03BD418200F9204E,
     367                                                                                E1D2940E03BD418200F9204E,
     368                                                                                E1D2940F03BD418200F9204E,
     369                                                                                E1D2941003BD418200F9204E,
     370                                                                                E1D2941103BD418200F9204E,
     371                                                                                E1D2941203BD418200F9204E,
     372                                                                                E1D2941303BD418200F9204E,
     373                                                                                E1D2941403BD418200F9204E,
     374                                                                                E1D2941503BD418200F9204E,
     375                                                                                E1D2941603BD418200F9204E,
     376                                                                                E1D2941703BD418200F9204E,
     377                                                                                E1D2941803BD418200F9204E,
     378                                                                                E1D2941903BD418200F9204E,
     379                                                                                E1D2941A03BD418200F9204E,
     380                                                                                E1D2941B03BD418200F9204E,
     381                                                                                E1D2941C03BD418200F9204E,
     382                                                                                E1D2941D03BD418200F9204E,
     383                                                                                E1D2941E03BD418200F9204E,
     384                                                                                E1D2941F03BD418200F9204E,
     385                                                                                E1D2942003BD418200F9204E,
     386                                                                                E1D2942103BD418200F9204E,
     387                                                                                E1D2942203BD418200F9204E,
     388                                                                                E1D2942303BD418200F9204E,
     389                                                                                E1D2942403BD418200F9204E,
     390                                                                                E1D2942503BD418200F9204E,
     391                                                                                E1D2942603BD418200F9204E,
     392                                                                                E1D2942703BD418200F9204E,
     393                                                                                E1D2942803BD418200F9204E,
     394                                                                                E1D2942903BD418200F9204E,
     395                                                                                E1D2942A03BD418200F9204E,
     396                                                                                E1D2942B03BD418200F9204E,
     397                                                                                E1D2942C03BD418200F9204E,
     398                                                                                E1D2942D03BD418200F9204E,
     399                                                                                E1D2942E03BD418200F9204E,
     400                                                                                E1D2942F03BD418200F9204E,
     401                                                                                E1D2943003BD418200F9204E,
     402                                                                                E1D2943103BD418200F9204E,
     403                                                                                E1D2943203BD418200F9204E,
     404                                                                                E1D2943303BD418200F9204E,
     405                                                                                E1D2943403BD418200F9204E,
     406                                                                                E1D2943503BD418200F9204E,
     407                                                                                E1D2943603BD418200F9204E,
     408                                                                                E1D2943703BD418200F9204E,
     409                                                                                E1D2943803BD418200F9204E,
     410                                                                                E1D2943903BD418200F9204E,
     411                                                                                E195A29103BE251F00186F9B,
     412                                                                                E195A29203BE251F00186F9B,
     413                                                                                E195A29503BE251F00186F9B,
     414                                                                                E195A29603BE251F00186F9B,
     415                                                                                E195A29703BE251F00186F9B,
     416                                                                                E195A29803BE251F00186F9B,
     417                                                                                E195A29903BE251F00186F9B,
     418                                                                                E195A29A03BE251F00186F9B,
     419                                                                                E195A29D03BE251F00186F9B,
     420                                                                                E195A29E03BE251F00186F9B,
     421                                                                                E195A29F03BE251F00186F9B,
     422                                                                                E195A2A003BE251F00186F9B,
     423                                                                                E195A2A103BE251F00186F9B,
     424                                                                                E195A2A203BE251F00186F9B,
     425                                                                                E195A2A303BE251F00186F9B,
     426                                                                                E195A2A503BE251F00186F9B,
     427                                                                                E195A2A603BE251F00186F9B,
     428                                                                                E195A2A703BE251F00186F9B,
     429                                                                                E195A2A803BE251F00186F9B,
     430                                                                                E195A2A903BE251F00186F9B,
     431                                                                                E195A2AA03BE251F00186F9B,
     432                                                                                E195A2AC03BE251F00186F9B,
     433                                                                                E195A2AD03BE251F00186F9B,
     434                                                                                E195A2AE03BE251F00186F9B,
     435                                                                                E195A2AF03BE251F00186F9B,
     436                                                                                E195A2B003BE251F00186F9B,
     437                                                                                E195A2B103BE251F00186F9B,
     438                                                                                E195A2B203BE251F00186F9B,
     439                                                                                E195A2B303BE251F00186F9B,
     440                                                                                E195A2B403BE251F00186F9B,
     441                                                                                E195A2B503BE251F00186F9B,
     442                                                                                E195A2B603BE251F00186F9B,
     443                                                                                E195A2B703BE251F00186F9B,
     444                                                                                E195A2B803BE251F00186F9B,
     445                                                                                E195A2B903BE251F00186F9B,
     446                                                                                E195A2BA03BE251F00186F9B,
     447                                                                                E195A2BB03BE251F00186F9B,
     448                                                                                E195A2BC03BE251F00186F9B,
     449                                                                                E195A2BD03BE251F00186F9B,
     450                                                                                E195A2BE03BE251F00186F9B,
     451                                                                                E195A2C103BE251F00186F9B,
     452                                                                                E195A2C203BE251F00186F9B,
     453                                                                                E195A2C303BE251F00186F9B,
     454                                                                                E195A2C403BE251F00186F9B,
     455                                                                                E195A2C503BE251F00186F9B,
     456                                                                                E195A2C603BE251F00186F9B,
     457                                                                                E195A2C703BE251F00186F9B,
     458                                                                                E195A2C803BE251F00186F9B,
     459                                                                                E195A2C903BE251F00186F9B,
     460                                                                                E195A2CA03BE251F00186F9B,
     461                                                                                E195A2CB03BE251F00186F9B,
     462                                                                                E195A2CD03BE251F00186F9B,
     463                                                                                E195A2CF03BE251F00186F9B,
     464                                                                                E195A2D103BE251F00186F9B,
     465                                                                                E195A2D303BE251F00186F9B,
     466                                                                                E195A2D503BE251F00186F9B,
     467                                                                                E195A2D703BE251F00186F9B,
     468                                                                                E195A2D903BE251F00186F9B,
     469                                                                                E195A2DB03BE251F00186F9B,
     470                                                                                E195A2DD03BE251F00186F9B,
     471                                                                                E195A2DF03BE251F00186F9B,
     472                                                                                E195A2E003BE251F00186F9B,
     473                                                                                E195A2E103BE251F00186F9B,
     474                                                                                E195A2E203BE251F00186F9B,
     475                                                                                E195A2E303BE251F00186F9B,
     476                                                                                E195A2E403BE251F00186F9B,
     477                                                                                E195A2E503BE251F00186F9B,
     478                                                                                E195A2E603BE251F00186F9B,
     479                                                                                E195A2E803BE251F00186F9B,
     480                                                                                E195A2E903BE251F00186F9B,
     481                                                                                E195A2EA03BE251F00186F9B,
     482                                                                                E195A2EB03BE251F00186F9B,
     483                                                                                E195A2EC03BE251F00186F9B,
     484                                                                                E195A2ED03BE251F00186F9B,
     485                                                                                E195A2EE03BE251F00186F9B,
     486                                                                                E195A2F003BE251F00186F9B,
     487                                                                                E195A2F103BE251F00186F9B,
     488                                                                                E195A2F303BE251F00186F9B,
     489                                                                                E195A2F403BE251F00186F9B,
     490                                                                                E195A2F503BE251F00186F9B,
     491                                                                                E195A2F603BE251F00186F9B,
     492                                                                                E195A2F703BE251F00186F9B,
     493                                                                                E195A2F803BE251F00186F9B,
     494                                                                                E195A2F903BE251F00186F9B,
     495                                                                                E195A2FA03BE251F00186F9B,
     496                                                                                E195A2FC03BE251F00186F9B,
     497                                                                                E195A2FE03BE251F00186F9B,
     498                                                                                E19DCB8503BE4A0A00B934BF,
     499                                                                                E19DCB8603BE4A0A00B934BF,
     500                                                                                E19DCB8703BE4A0A00B934BF,
     501                                                                                E19DCB8903BE4A0A00B934BF,
     502                                                                                E19DCB8A03BE4A0A00B934BF,
     503                                                                                E19DCB8B03BE4A0A00B934BF,
     504                                                                                E19DCB8C03BE4A0A00B934BF,
     505                                                                                E19DCB8E03BE4A0A00B934BF,
     506                                                                                E19DCB9003BE4A0A00B934BF,
     507                                                                                E19DCB9103BE4A0A00B934BF,
     508                                                                                E19DCB9303BE4A0A00B934BF,
     509                                                                                E19DCB9403BE4A0A00B934BF,
     510                                                                                E19DCB9503BE4A0A00B934BF,
     511                                                                                E19DCB9603BE4A0A00B934BF,
     512                                                                                E19DCB9703BE4A0A00B934BF,
     513                                                                                E19DCB9803BE4A0A00B934BF,
     514                                                                                E19DCB9903BE4A0A00B934BF,
     515                                                                                E19DCB9A03BE4A0A00B934BF,
     516                                                                                E19DCB9B03BE4A0A00B934BF,
     517                                                                                E19DCB9C03BE4A0A00B934BF,
     518                                                                                E19DCB9D03BE4A0A00B934BF,
     519                                                                                E19DCB9E03BE4A0A00B934BF,
     520                                                                                E19DCB9F03BE4A0A00B934BF,
     521                                                                                E19DCBA003BE4A0A00B934BF,
     522                                                                                E19DCBA103BE4A0A00B934BF,
     523                                                                                E19DCBA203BE4A0A00B934BF,
     524                                                                                E19DCBA303BE4A0A00B934BF,
     525                                                                                E19DCBA403BE4A0A00B934BF,
     526                                                                                E19DCBA503BE4A0A00B934BF,
     527                                                                                E19DCBA603BE4A0A00B934BF,
     528                                                                                E19DCBA703BE4A0A00B934BF,
     529                                                                                E19DCBA803BE4A0A00B934BF,
     530                                                                                E19DCBA903BE4A0A00B934BF,
     531                                                                                E19DCBAA03BE4A0A00B934BF,
     532                                                                                E19DCBAB03BE4A0A00B934BF,
     533                                                                                E19DCBAC03BE4A0A00B934BF,
     534                                                                                E19DCBAD03BE4A0A00B934BF,
     535                                                                                E19DCBAE03BE4A0A00B934BF,
     536                                                                                E19DCBAF03BE4A0A00B934BF,
     537                                                                                E19DCBB003BE4A0A00B934BF,
     538                                                                                E19DCBB103BE4A0A00B934BF,
     539                                                                                E19DCBB203BE4A0A00B934BF,
     540                                                                                E19DCBB603BE4A0A00B934BF,
     541                                                                                E19DCBB703BE4A0A00B934BF,
     542                                                                                E19DCBB803BE4A0A00B934BF,
     543                                                                                E19DCBB903BE4A0A00B934BF,
     544                                                                                E19DCBBA03BE4A0A00B934BF,
     545                                                                                E19DCBBB03BE4A0A00B934BF,
     546                                                                                E19DCBBC03BE4A0A00B934BF,
     547                                                                                E19DCBBD03BE4A0A00B934BF,
     548                                                                                E19DCCA203BE900300B934BF,
     549                                                                                E19DCCA303BE900300B934BF,
     550                                                                                E19DCCA403BE900300B934BF,
     551                                                                                E19DCCA503BE900300B934BF,
     552                                                                                E19DCCA603BE900300B934BF,
     553                                                                                E19DCCA703BE900300B934BF,
     554                                                                                E19DCCA803BE900300B934BF,
     555                                                                                E19DCCA903BE900300B934BF,
     556                                                                                E19DCCAB03BE900300B934BF,
     557                                                                                E19DCCAC03BE900300B934BF,
     558                                                                                E19DCCAE03BE900300B934BF,
     559                                                                                E19DCCAF03BE900300B934BF,
     560                                                                                E19DCCB003BE900300B934BF,
     561                                                                                E19DCCB103BE900300B934BF,
     562                                                                                E19DCCB203BE900300B934BF,
     563                                                                                E19DCCB303BE900300B934BF,
     564                                                                                E19DCCB403BE900300B934BF,
     565                                                                                E19DCCB503BE900300B934BF,
     566                                                                                E19DCCB603BE900300B934BF,
     567                                                                                E19DCCB703BE900300B934BF,
     568                                                                                E19DCCB903BE900300B934BF,
     569                                                                                E19DCCBA03BE900300B934BF,
     570                                                                                E19DCCBB03BE900300B934BF,
     571                                                                                E19DCCBC03BE900300B934BF,
     572                                                                                E19DCCBD03BE900300B934BF,
     573                                                                                E19DCCBE03BE900300B934BF,
     574                                                                                E19DCCBF03BE900300B934BF,
     575                                                                                E19DCCC003BE900300B934BF,
     576                                                                                E19DCCC103BE900300B934BF,
     577                                                                                E19DCCC203BE900300B934BF,
     578                                                                                E19DCCC303BE900300B934BF,
     579                                                                                E19DCCC403BE900300B934BF,
     580                                                                                E19DCCC503BE900300B934BF,
     581                                                                                E19DCCC603BE900300B934BF,
     582                                                                                E19DCCC703BE900300B934BF,
     583                                                                                E19DCCC803BE900300B934BF,
     584                                                                                E19DCCC903BE900300B934BF,
     585                                                                                E19DCCCA03BE900300B934BF,
     586                                                                                E19DCCCB03BE900300B934BF,
     587                                                                                E19DCCCD03BE900300B934BF,
     588                                                                                E19DCCCE03BE900300B934BF,
     589                                                                                E19DCCCF03BE900300B934BF,
     590                                                                                E19DCCD103BE900300B934BF,
     591                                                                                E19DCCD203BE900300B934BF,
     592                                                                                E19DCCD403BE900300B934BF,
     593                                                                                E19DCCD503BE900300B934BF,
     594                                                                                E19DCCD603BE900300B934BF,
     595                                                                                E19DCCD703BE900300B934BF,
     596                                                                                E19DCCD803BE900300B934BF,
     597                                                                                E19DCCD903BE900300B934BF,
     598                                                                                E19DCCDA03BE900300B934BF,
     599                                                                                E19DCCDB03BE900300B934BF,
     600                                                                                E1A9DEF703BF480F0089E8B2,
     601                                                                                E1A9DEF903BF480F0089E8B2,
     602                                                                                E1A9DEFB03BF480F0089E8B2,
     603                                                                                E1A9DEFC03BF480F0089E8B2,
     604                                                                                E1A9DEFD03BF480F0089E8B2,
     605                                                                                E1A9DEFE03BF480F0089E8B2,
     606                                                                                E1A9DF0003BF480F0089E8B2,
     607                                                                                E1A9DF0203BF480F0089E8B2,
     608                                                                                E1A9DF0303BF480F0089E8B2,
     609                                                                                E1A9DF0403BF480F0089E8B2,
     610                                                                                E1A9DF0503BF480F0089E8B2,
     611                                                                                E1A9DF0603BF480F0089E8B2,
     612                                                                                E1A9DF0703BF480F0089E8B2,
     613                                                                                E1A9DF0803BF480F0089E8B2,
     614                                                                                E1A9DF0A03BF480F0089E8B2,
     615                                                                                E11CD2E303BF9B3300C49BEF,
     616                                                                                E11CD2E503BF9B3300C49BEF,
     617                                                                                E11CD2E603BF9B3300C49BEF,
     618                                                                                E11CD2E703BF9B3300C49BEF,
     619                                                                                E11CD2E803BF9B3300C49BEF,
     620                                                                                E11CD2E903BF9B3300C49BEF,
     621                                                                                E11CD2EA03BF9B3300C49BEF,
     622                                                                                E11CD2EB03BF9B3300C49BEF,
     623                                                                                E11CD2EC03BF9B3300C49BEF,
     624                                                                                E11CD2ED03BF9B3300C49BEF,
     625                                                                                E11CD2EE03BF9B3300C49BEF,
     626                                                                                E11CD2EF03BF9B3300C49BEF,
     627                                                                                E11CD2F603BF9B3300C49BEF,
     628                                                                                E11CD2F803BF9B3300C49BEF,
     629                                                                                E11CD2FB03BF9B3300C49BEF,
     630                                                                                E17AED8B03C0F5D300B89ACF,
     631                                                                                E17AED8C03C0F5D300B89ACF,
     632                                                                                E17AED8D03C0F5D300B89ACF,
     633                                                                                E17AED8E03C0F5D300B89ACF,
     634                                                                                E17AED8F03C0F5D300B89ACF,
     635                                                                                E17AED9003C0F5D300B89ACF,
     636                                                                                E17AED9103C0F5D300B89ACF,
     637                                                                                E17AED9203C0F5D300B89ACF,
     638                                                                                E17AED9303C0F5D300B89ACF,
     639                                                                                E17AED9403C0F5D300B89ACF,
     640                                                                                E17AED9503C0F5D300B89ACF,
     641                                                                                E17AED9603C0F5D300B89ACF,
     642                                                                                E17AED9703C0F5D300B89ACF,
     643                                                                                E17AED9803C0F5D300B89ACF,
     644                                                                                E17AED9903C0F5D300B89ACF,
     645                                                                                E17AED9A03C0F5D300B89ACF,
     646                                                                                E17AED9B03C0F5D300B89ACF,
     647                                                                                E17AED9C03C0F5D300B89ACF,
     648                                                                                E17AED9D03C0F5D300B89ACF,
     649                                                                                E17AED9E03C0F5D300B89ACF,
     650                                                                                E17AED9F03C0F5D300B89ACF,
     651                                                                                E17AEDA003C0F5D300B89ACF,
     652                                                                                E17AEDA103C0F5D300B89ACF,
     653                                                                                E17AEDA203C0F5D300B89ACF,
     654                                                                                E17AEDA303C0F5D300B89ACF,
     655                                                                                E17AEDA403C0F5D300B89ACF,
     656                                                                                E17AEDA503C0F5D300B89ACF,
     657                                                                                E17AEDA603C0F5D300B89ACF,
     658                                                                                E17AEDA703C0F5D300B89ACF,
     659                                                                                E17AEDA803C0F5D300B89ACF,
     660                                                                                E17AEDA903C0F5D300B89ACF,
     661                                                                                E17AEDAA03C0F5D300B89ACF,
     662                                                                                E17AEDAB03C0F5D300B89ACF,
     663                                                                                E17AEDAC03C0F5D300B89ACF,
     664                                                                                E17AEDAD03C0F5D300B89ACF,
     665                                                                                E17AEDAE03C0F5D300B89ACF,
     666                                                                                E17AEDAF03C0F5D300B89ACF,
     667                                                                                E17AEDB003C0F5D300B89ACF,
     668                                                                                E17AEDB103C0F5D300B89ACF,
     669                                                                                E17AEDB203C0F5D300B89ACF,
     670                                                                                E17AEDB303C0F5D300B89ACF,
     671                                                                                E17AEDB403C0F5D300B89ACF,
     672                                                                                E17AEDB503C0F5D300B89ACF,
     673                                                                                E17AEDB603C0F5D300B89ACF,
     674                                                                                E17AEDB703C0F5D300B89ACF,
     675                                                                                E17AEDB803C0F5D300B89ACF,
     676                                                                                E17AEDB903C0F5D300B89ACF,
     677                                                                                E17AEDBA03C0F5D300B89ACF,
     678                                                                                E17AEDBB03C0F5D300B89ACF,
     679                                                                                E17AEDBC03C0F5D300B89ACF,
     680                                                                                E17AEDBD03C0F5D300B89ACF,
     681                                                                                E17AEDBE03C0F5D300B89ACF,
     682                                                                                E17AEDBF03C0F5D300B89ACF,
     683                                                                                E17AEDC003C0F5D300B89ACF,
     684                                                                                E17AEDC103C0F5D300B89ACF,
     685                                                                                E17AEDC203C0F5D300B89ACF,
     686                                                                                E17AEDC303C0F5D300B89ACF,
     687                                                                                E17AEDC403C0F5D300B89ACF,
     688                                                                                E17AEDC503C0F5D300B89ACF,
     689                                                                                E17AEDC603C0F5D300B89ACF,
     690                                                                                E17AEDC703C0F5D300B89ACF,
     691                                                                                E17AEDC803C0F5D300B89ACF,
     692                                                                                E17AEDC903C0F5D300B89ACF,
     693                                                                                E117A4B203C3E9C600E10BA2,
     694                                                                                E117A4B303C3E9C600E10BA2,
     695                                                                                E117A4B403C3E9C600E10BA2,
     696                                                                                E1096EB803C3EDB0009B92C1,
     697                                                                        );
     698                                                                };
    745699                                                                NavCount = 1;
    746700                                                                NavGeometry0 = {
    747                                                                         Frame = "{{0, 0}, {150, 530}}";
     701                                                                        Frame = "{{0, 0}, {689, 635}}";
    748702                                                                        NavBarVisible = YES;
    749703                                                                };
     704                                                                NavSplitVertical = NO;
    750705                                                        };
    751706                                                        SplitCount = 1;
    752                                                         Tab0 = {
     707                                                        Tab1 = {
    753708                                                                Debugger = {
    754709                                                                        Split0 = {
     
    760715                                                                LauncherConfigVersion = 7;
    761716                                                        };
    762                                                         Tab1 = {
     717                                                        Tab2 = {
    763718                                                                LauncherConfigVersion = 3;
    764719                                                                Runner = {
    765720                                                                };
    766721                                                        };
    767                                                         TabCount = 4;
     722                                                        TabCount = 5;
     723                                                };
     724                                                SplitCount = 1;
     725                                                Tab1 = {
     726                                                        OptionsSetName = "Flat, project classes";
     727                                                };
     728                                                TabCount = 5;
     729                                        };
     730                                },
     731                                {
     732                                        LeftSlideOut = {
     733                                                Split0 = {
     734                                                        Split0 = {
     735                                                                NavCount = 1;
     736                                                                NavGeometry0 = {
     737                                                                        Frame = "{{0, 0}, {945, 150}}";
     738                                                                        NavBarVisible = YES;
     739                                                                };
     740                                                                NavSplitVertical = NO;