Index: /releases/Pester/1.1a1/Source/BDAlias.h
===================================================================
--- /releases/Pester/1.1a1/Source/BDAlias.h	(revision 44)
+++ /releases/Pester/1.1a1/Source/BDAlias.h	(revision 44)
@@ -0,0 +1,75 @@
+/*
+    Copyright (c) 2001, bDistributed.com, Inc.
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or
+    without modification, are permitted provided that the following
+    conditions are met:
+
+    *   Redistributions of source code must retain the above
+        copyright notice, this list of conditions and the following
+        disclaimer.
+    
+    *   Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials
+        provided with the distribution.
+    
+    *   Neither the name of bDistributed.com, Inc. nor the names of
+        its contributors may be used to endorse or promote products
+        derived from this software without specific prior written
+        permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+    CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+    INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+    MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+    OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+    OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+    TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+    OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+    OF SUCH DAMAGE.
+*/
+
+#import <Foundation/Foundation.h>
+#import <CoreServices/CoreServices.h>
+
+@interface BDAlias : NSObject
+{
+	AliasHandle _alias;
+}
+
+- (id)initWithAliasHandle:(AliasHandle)alias; // designated initializer
+- (id)initWithData:(NSData *)data;
+- (id)initWithPath:(NSString *)fullPath;
+- (id)initWithPath:(NSString *)path relativeToPath:(NSString *)relPath;
+- (id)initWithFSRef:(FSRef *)ref;
+- (id)initWithFSRef:(FSRef *)ref relativeToFSRef:(FSRef *)relRef;
+
+- (void)dealloc;
+
+- (AliasHandle)alias;
+- (void)setAlias:(AliasHandle)newAlias;
+
+- (NSData *)aliasData;
+- (void)setAliasData:(NSData *)newAliasData;
+
+- (BOOL)aliasIsEqual:(AliasHandle)otherAlias;
+- (BOOL)aliasDataIsEqual:(NSData *)data;
+- (BOOL)isEqual:(id)object;
+
+- (NSString *)fullPath;
+- (NSString *)fullPathRelativeToPath:(NSString *)relPath;
+
++ (BDAlias *)aliasWithAliasHandle:(AliasHandle)alias;
++ (BDAlias *)aliasWithData:(NSData *)data;
++ (BDAlias *)aliasWithPath:(NSString *)fullPath;
++ (BDAlias *)aliasWithPath:(NSString *)path relativeToPath:(NSString *)relPath;
++ (BDAlias *)aliasWithFSRef:(FSRef *)ref;
++ (BDAlias *)aliasWithFSRef:(FSRef *)ref relativeToFSRef:(FSRef *)relRef;
+
+@end
Index: /releases/Pester/1.1a1/Source/BDAlias.m
===================================================================
--- /releases/Pester/1.1a1/Source/BDAlias.m	(revision 44)
+++ /releases/Pester/1.1a1/Source/BDAlias.m	(revision 44)
@@ -0,0 +1,347 @@
+/*
+    Copyright (c) 2001, bDistributed.com, Inc.
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or
+    without modification, are permitted provided that the following
+    conditions are met:
+
+    *   Redistributions of source code must retain the above
+        copyright notice, this list of conditions and the following
+        disclaimer.
+    
+    *   Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials
+        provided with the distribution.
+    
+    *   Neither the name of bDistributed.com, Inc. nor the names of
+        its contributors may be used to endorse or promote products
+        derived from this software without specific prior written
+        permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+    CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+    INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+    MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+    OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+    OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+    TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+    OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+    OF SUCH DAMAGE.
+*/
+
+#include <assert.h>
+
+#import "BDAlias.h"
+
+
+static Handle DataToHandle(CFDataRef inData);
+static CFDataRef HandleToData(Handle inHandle);
+
+static OSStatus PathToFSRef(CFStringRef inPath, FSRef *outRef);
+static CFStringRef FSRefToPathCopy(const FSRef *inRef);
+
+
+static Handle DataToHandle(CFDataRef inData)
+{
+    CFIndex	len;
+    Handle	handle = NULL;
+    
+    if (inData == NULL) {
+        return NULL;
+    }
+    
+    len = CFDataGetLength(inData);
+    
+    handle = NewHandle(len);
+    
+    if ((handle != NULL) && (len > 0)) {
+        HLock(handle);
+        BlockMoveData(CFDataGetBytePtr(inData), *handle, len);
+        HUnlock(handle);
+    }
+    
+    return handle;
+}
+
+static CFDataRef HandleToData(Handle inHandle)
+{
+    CFDataRef	data = NULL;
+    CFIndex	len;
+    SInt8	handleState;
+    
+    if (inHandle == NULL) {
+        return NULL;
+    }
+    
+    len = GetHandleSize(inHandle);
+    
+    handleState = HGetState(inHandle);
+    
+    HLock(inHandle);
+    
+    data = CFDataCreate(kCFAllocatorDefault, (const UInt8 *) *inHandle, len);
+    
+    HSetState(inHandle, handleState);
+    
+    return data;
+}
+
+static OSStatus PathToFSRef(CFStringRef inPath, FSRef *outRef)
+{
+    CFURLRef	tempURL = NULL;
+    Boolean	gotRef = false;
+    
+    tempURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, inPath,
+                                            kCFURLPOSIXPathStyle, false);
+    
+    if (tempURL == NULL) {
+        return fnfErr;
+    }
+    
+    gotRef = CFURLGetFSRef(tempURL, outRef);
+    
+    if (gotRef == false) {
+        return fnfErr;
+    }
+    
+    return noErr;
+}
+
+static CFStringRef FSRefToPathCopy(const FSRef *inRef)
+{
+    CFURLRef	tempURL = NULL;
+    CFStringRef	result = NULL;
+    
+    if (inRef != NULL) {
+        tempURL = CFURLCreateFromFSRef(kCFAllocatorDefault, inRef);
+        
+        if (tempURL == NULL) {
+            return NULL;
+        }
+        
+        result = CFURLCopyFileSystemPath(tempURL, kCFURLPOSIXPathStyle);
+        
+        CFRelease(tempURL);
+    }
+    
+    return result;
+}
+
+
+@implementation BDAlias
+
+- (id)initWithAliasHandle:(AliasHandle)alias
+{
+    id ret = [super init];
+    
+    if (ret != nil) {
+        _alias = alias;
+    }
+    
+    return ret;
+}
+
+- (id)initWithData:(NSData *)data
+{
+    return [self initWithAliasHandle:(AliasHandle)DataToHandle((CFDataRef) data)];
+}
+
+- (id)initWithPath:(NSString *)fullPath
+{
+    OSStatus	anErr = noErr;
+    FSRef		ref;
+    
+    anErr = PathToFSRef((CFStringRef) fullPath, &ref);
+    
+    if (anErr != noErr) {
+        return nil;
+    }
+    
+    return [self initWithFSRef:&ref];;
+}
+
+- (id)initWithPath:(NSString *)path relativeToPath:(NSString *)relPath
+{
+    OSStatus	anErr = noErr;
+    FSRef		ref, relRef;
+    
+    anErr = PathToFSRef((CFStringRef) [relPath stringByAppendingPathComponent:path],
+                        &ref);
+    
+    if (anErr != noErr) {
+        return nil;
+    }
+    
+    anErr = PathToFSRef((CFStringRef) relPath, &relRef);
+    
+    if (anErr != noErr) {
+        return nil;
+    }
+    
+    return [self initWithFSRef:&ref relativeToFSRef:&relRef];
+}
+
+- (id)initWithFSRef:(FSRef *)ref
+{
+    return [self initWithFSRef:ref relativeToFSRef:NULL];
+}
+
+- (id)initWithFSRef:(FSRef *)ref relativeToFSRef:(FSRef *)relRef
+{
+    OSStatus	anErr = noErr;
+    AliasHandle	alias = NULL;
+    
+    anErr = FSNewAlias(relRef, ref, &alias);
+    
+    if (anErr != noErr) {
+        return nil;
+    }
+    
+    return [self initWithAliasHandle:alias];
+}
+
+- (void)dealloc
+{
+    if (_alias != NULL) {
+        DisposeHandle((Handle) _alias);
+        _alias = NULL;
+    }
+    
+    [super dealloc];
+}
+
+- (AliasHandle)alias
+{
+    return _alias;
+}
+
+- (void)setAlias:(AliasHandle)newAlias
+{
+    if (_alias != NULL) {
+        DisposeHandle((Handle) _alias);
+    }
+    
+    _alias = newAlias;
+}
+
+- (NSData *)aliasData
+{
+    NSData *result;
+    
+    result = (NSData *)HandleToData((Handle) _alias);
+    
+    return [result autorelease];
+}
+
+- (void)setAliasData:(NSData *)newAliasData
+{
+    [self setAlias:(AliasHandle) DataToHandle((CFDataRef) newAliasData)];
+}
+
+- (NSString *)fullPath
+{
+    return [self fullPathRelativeToPath:nil];
+}
+
+- (NSString *)fullPathRelativeToPath:(NSString *)relPath
+{
+    OSStatus	anErr = noErr;
+    FSRef	relPathRef;
+    FSRef	tempRef;
+    NSString	*result = nil;
+    Boolean	wasChanged;
+    
+    if (_alias != NULL) {
+        if (relPath != nil) {
+            anErr = PathToFSRef((CFStringRef)relPath, &relPathRef);
+            
+            if (anErr != noErr) {
+                return NULL;
+            }
+            
+            anErr = FSResolveAlias(&relPathRef, _alias, &tempRef, &wasChanged);
+        } else {
+            anErr = FSResolveAlias(NULL, _alias, &tempRef, &wasChanged);
+        }
+        
+        if (anErr != noErr) {
+            return NULL;
+        }
+        
+        result = (NSString *)FSRefToPathCopy(&tempRef);
+    }
+    
+    return [result autorelease];
+}
+
+- (BOOL)aliasIsEqual:(AliasHandle)otherAlias;
+{
+    AliasHandle alias = [self alias];
+    FSRef ref, otherRef;
+    Boolean wasChanged;
+
+    if (alias == otherAlias) return YES;
+    if (alias == NULL || otherAlias == NULL) return NO;
+    if (FSResolveAlias(NULL, alias, &ref, &wasChanged) != noErr) return NO;
+    if (FSResolveAlias(NULL, otherAlias, &otherRef, &wasChanged) != noErr) return NO;
+    return (FSCompareFSRefs(&ref, &otherRef) == noErr);
+}
+
+- (BOOL)aliasDataIsEqual:(NSData *)data;
+{
+    AliasHandle otherAlias;
+    const UInt8 *aliasPtr = CFDataGetBytePtr((CFDataRef)data);
+    BOOL result;
+    if (aliasPtr == NULL) {
+        otherAlias = (AliasHandle)DataToHandle((CFDataRef)data);
+    } else {
+        otherAlias = (AliasHandle)&aliasPtr;
+    }
+    result = [self aliasIsEqual: otherAlias];
+    if (aliasPtr == NULL) DisposeHandle((Handle)otherAlias);
+    return result;
+}
+
+- (BOOL)isEqual:(id)object;
+{
+    if (![object isKindOfClass: [BDAlias class]]) return NO;
+    return [self aliasIsEqual: [object alias]];
+}
+
++ (BDAlias *)aliasWithAliasHandle:(AliasHandle)alias
+{
+    return [[[BDAlias alloc] initWithAliasHandle:alias] autorelease];
+}
+
++ (BDAlias *)aliasWithData:(NSData *)data
+{
+    return [[[BDAlias alloc] initWithData:data] autorelease];
+}
+
++ (BDAlias *)aliasWithPath:(NSString *)fullPath
+{
+    return [[[BDAlias alloc] initWithPath:fullPath] autorelease];
+}
+
++ (BDAlias *)aliasWithPath:(NSString *)path relativeToPath:(NSString *)relPath
+{
+    return [[[BDAlias alloc] initWithPath:path relativeToPath:relPath] autorelease];
+}
+
++ (BDAlias *)aliasWithFSRef:(FSRef *)ref
+{
+    return [[[BDAlias alloc] initWithFSRef:ref] autorelease];
+}
+
++ (BDAlias *)aliasWithFSRef:(FSRef *)ref relativeToFSRef:(FSRef *)relRef
+{
+    return [[[BDAlias alloc] initWithFSRef:ref relativeToFSRef:relRef] autorelease];
+}
+
+@end
Index: /releases/Pester/1.1a1/Source/English.lproj/Alarms.nib/classes.nib
===================================================================
--- /releases/Pester/1.1a1/Source/English.lproj/Alarms.nib/classes.nib	(revision 44)
+++ /releases/Pester/1.1a1/Source/English.lproj/Alarms.nib/classes.nib	(revision 44)
@@ -0,0 +1,14 @@
+{
+    IBClasses = (
+        {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, 
+        {CLASS = NJRScrollView; LANGUAGE = ObjC; SUPERCLASS = NSScrollView; }, 
+        {
+            ACTIONS = {remove = id; }; 
+            CLASS = PSAlarmsController; 
+            LANGUAGE = ObjC; 
+            OUTLETS = {removeButton = NSButton; tableView = NSTableView; }; 
+            SUPERCLASS = NSWindowController; 
+        }
+    ); 
+    IBVersion = 1; 
+}
Index: /releases/Pester/1.1a1/Source/English.lproj/Alarms.nib/info.nib
===================================================================
--- /releases/Pester/1.1a1/Source/English.lproj/Alarms.nib/info.nib	(revision 44)
+++ /releases/Pester/1.1a1/Source/English.lproj/Alarms.nib/info.nib	(revision 44)
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>IBDocumentLocation</key>
+	<string>579 27 356 240 0 0 1600 1178 </string>
+	<key>IBFramework Version</key>
+	<string>286.0</string>
+	<key>IBOpenObjects</key>
+	<array>
+		<integer>8</integer>
+	</array>
+	<key>IBSystem Version</key>
+	<string>6D52</string>
+</dict>
+</plist>
Index: /releases/Pester/1.1a1/Source/English.lproj/Credits.html
===================================================================
--- /releases/Pester/1.1a1/Source/English.lproj/Credits.html	(revision 44)
+++ /releases/Pester/1.1a1/Source/English.lproj/Credits.html	(revision 44)
@@ -0,0 +1,2 @@
+<font face="Lucida Grande" size="-1"><b>Icon by</b> <a href="mailto:excellence@global.co.za">Diederik Vemer</a></font>.<br>
+<font face="Lucida Grande" size="-1"><b>Thanks to</b> <a href="mailto:ryanwilcox@mac.com">Ryan Wilcox</a></font>.
Index: /releases/Pester/1.1a1/Source/English.lproj/MainMenu.nib/classes.nib
===================================================================
--- /releases/Pester/1.1a1/Source/English.lproj/MainMenu.nib/classes.nib	(revision 44)
+++ /releases/Pester/1.1a1/Source/English.lproj/MainMenu.nib/classes.nib	(revision 44)
@@ -0,0 +1,87 @@
+{
+    IBClasses = (
+        {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, 
+        {CLASS = NJRDateFormatter; LANGUAGE = ObjC; SUPERCLASS = NSDateFormatter; }, 
+        {
+            ACTIONS = {select = id; }; 
+            CLASS = NJRFSObjectSelector; 
+            LANGUAGE = ObjC; 
+            SUPERCLASS = NSButton; 
+        }, 
+        {
+            ACTIONS = {clearAllEntries = id; removeEntry = id; }; 
+            CLASS = NJRHistoryTrackingComboBox; 
+            LANGUAGE = ObjC; 
+            SUPERCLASS = NSComboBox; 
+        }, 
+        {CLASS = NJRIntegerFilter; LANGUAGE = ObjC; SUPERCLASS = NSNumberFormatter; }, 
+        {
+            ACTIONS = {stopSoundPreview = id; }; 
+            CLASS = NJRQTMediaPopUpButton; 
+            LANGUAGE = ObjC; 
+            OUTLETS = {preview = NSMovieView; }; 
+            SUPERCLASS = NSPopUpButton; 
+        }, 
+        {
+            ACTIONS = {stopVoicePreview = id; }; 
+            CLASS = NJRVoicePopUpButton; 
+            LANGUAGE = ObjC; 
+            SUPERCLASS = NSPopUpButton; 
+        }, 
+        {CLASS = NSObject; LANGUAGE = ObjC; }, 
+        {
+            ACTIONS = {
+                dateCompleted = id; 
+                doScriptChanged = id; 
+                doSpeakChanged = id; 
+                inAtChanged = id; 
+                playSoundChanged = id; 
+                setAlarm = id; 
+                setSoundRepetitionCount = id; 
+                silence = id; 
+                update = id; 
+            }; 
+            CLASS = PSAlarmSetController; 
+            LANGUAGE = ObjC; 
+            OUTLETS = {
+                bounceDockIcon = NSButton; 
+                displayMessage = NSButtonCell; 
+                doScript = NSButtonCell; 
+                doSpeak = NSButtonCell; 
+                inAtMatrix = NSMatrix; 
+                messageField = NSTextField; 
+                playSound = NSButtonCell; 
+                script = NJRFSObjectSelector; 
+                scriptSelectButton = NSButton; 
+                setButton = NSButton; 
+                sound = NJRQTMediaPopUpButton; 
+                soundRepetitionStepper = NSStepper; 
+                soundRepetitions = NSTextField; 
+                soundRepetitionsLabel = NSTextField; 
+                timeDate = NSTextField; 
+                timeDateCompletions = NSPopUpButton; 
+                timeInterval = NSTextField; 
+                timeIntervalRepeats = NSButton; 
+                timeIntervalUnits = NSPopUpButton; 
+                timeOfDay = NSTextField; 
+                timeSummary = NSTextField; 
+                voice = NJRVoicePopUpButton; 
+            }; 
+            SUPERCLASS = NSWindowController; 
+        }, 
+        {
+            CLASS = PSAlarmsController; 
+            LANGUAGE = ObjC; 
+            OUTLETS = {tableView = NSTableView; }; 
+            SUPERCLASS = NSWindowController; 
+        }, 
+        {
+            ACTIONS = {orderFrontAlarmsPanel = id; orderFrontSetAlarmPanel = id; stopAlerts = id; }; 
+            CLASS = PSApplication; 
+            LANGUAGE = ObjC; 
+            OUTLETS = {alarmSetController = PSAlarmSetController; }; 
+            SUPERCLASS = NSApplication; 
+        }
+    ); 
+    IBVersion = 1; 
+}
Index: /releases/Pester/1.1a1/Source/English.lproj/MainMenu.nib/info.nib
===================================================================
--- /releases/Pester/1.1a1/Source/English.lproj/MainMenu.nib/info.nib	(revision 44)
+++ /releases/Pester/1.1a1/Source/English.lproj/MainMenu.nib/info.nib	(revision 44)
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
+<plist version="0.9">
+<dict>
+	<key>IBDocumentLocation</key>
+	<string>54 24 356 240 0 0 1280 832 </string>
+	<key>IBEditorPositions</key>
+	<dict>
+		<key>29</key>
+		<string>71 238 273 44 0 0 1280 832 </string>
+	</dict>
+	<key>IBFramework Version</key>
+	<string>263.2</string>
+	<key>IBOpenObjects</key>
+	<array>
+		<integer>21</integer>
+		<integer>29</integer>
+	</array>
+	<key>IBSystem Version</key>
+	<string>5S66</string>
+	<key>IBUserGuides</key>
+	<dict>
+		<key>21</key>
+		<dict>
+			<key>guideLocations</key>
+			<array>
+				<string>Horizontal:92.000000</string>
+				<string>Horizontal:148.000000</string>
+				<string>Horizontal:116.000000</string>
+				<string>Horizontal:64.000000</string>
+			</array>
+			<key>guidesLocked</key>
+			<false/>
+		</dict>
+	</dict>
+</dict>
+</plist>
Index: /releases/Pester/1.1a1/Source/English.lproj/Movie alert.nib/classes.nib
===================================================================
--- /releases/Pester/1.1a1/Source/English.lproj/Movie alert.nib/classes.nib	(revision 44)
+++ /releases/Pester/1.1a1/Source/English.lproj/Movie alert.nib/classes.nib	(revision 44)
@@ -0,0 +1,14 @@
+{
+    IBClasses = (
+        {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, 
+        {
+            ACTIONS = {close = id; }; 
+            CLASS = PSMovieAlertController; 
+            LANGUAGE = ObjC; 
+            OUTLETS = {movieView = NSMovieView; }; 
+            SUPERCLASS = NSWindowController; 
+        }, 
+        {CLASS = java.lang.Object; LANGUAGE = Java; }
+    ); 
+    IBVersion = 1; 
+}
Index: /releases/Pester/1.1a1/Source/English.lproj/Movie alert.nib/info.nib
===================================================================
--- /releases/Pester/1.1a1/Source/English.lproj/Movie alert.nib/info.nib	(revision 44)
+++ /releases/Pester/1.1a1/Source/English.lproj/Movie alert.nib/info.nib	(revision 44)
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
+<plist version="0.9">
+<dict>
+	<key>IBFramework Version</key>
+	<string>263.2</string>
+	<key>IBOpenObjects</key>
+	<array>
+		<integer>10</integer>
+	</array>
+	<key>IBSystem Version</key>
+	<string>5S66</string>
+</dict>
+</plist>
Index: /releases/Pester/1.1a1/Source/English.lproj/Notifier.nib/classes.nib
===================================================================
--- /releases/Pester/1.1a1/Source/English.lproj/Notifier.nib/classes.nib	(revision 44)
+++ /releases/Pester/1.1a1/Source/English.lproj/Notifier.nib/classes.nib	(revision 44)
@@ -0,0 +1,13 @@
+{
+    IBClasses = (
+        {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, 
+        {
+            ACTIONS = {close = id; }; 
+            CLASS = PSAlarmNotifierController; 
+            LANGUAGE = ObjC; 
+            OUTLETS = {dateField = NSTextField; messageField = NSTextField; }; 
+            SUPERCLASS = NSWindowController; 
+        }
+    ); 
+    IBVersion = 1; 
+}
Index: /releases/Pester/1.1a1/Source/English.lproj/Notifier.nib/info.nib
===================================================================
--- /releases/Pester/1.1a1/Source/English.lproj/Notifier.nib/info.nib	(revision 44)
+++ /releases/Pester/1.1a1/Source/English.lproj/Notifier.nib/info.nib	(revision 44)
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
+<plist version="0.9">
+<dict>
+	<key>IBDocumentLocation</key>
+	<string>424 83 356 240 0 0 1280 832 </string>
+	<key>IBFramework Version</key>
+	<string>263.2</string>
+	<key>IBOpenObjects</key>
+	<array>
+		<integer>5</integer>
+	</array>
+	<key>IBSystem Version</key>
+	<string>5S66</string>
+</dict>
+</plist>
Index: /releases/Pester/1.1a1/Source/NDAppleScript/NDAppleScriptObject.h
===================================================================
--- /releases/Pester/1.1a1/Source/NDAppleScript/NDAppleScriptObject.h	(revision 44)
+++ /releases/Pester/1.1a1/Source/NDAppleScript/NDAppleScriptObject.h	(revision 44)
@@ -0,0 +1,77 @@
+/*
+ *  NDAppleScriptObject.h
+ *  NDAppleScriptObjectProject
+ *
+ *  Created by nathan on Thu Nov 29 2001.
+ *  Copyright (c) 2001 Nathan Day. All rights reserved.
+ */
+
+#import <Foundation/Foundation.h>
+#import <Carbon/Carbon.h>
+#import "NDAppleScriptObject_Protocols.h"
+
+@interface NDAppleScriptObject : NSObject <NDAppleScriptObjectSendEvent, NDAppleScriptObjectActive>
+{
+    @private
+    OSAID compiledScriptID, resultingValueID;
+    NDAppleScriptObject	* contextAppleScriptObject;
+    id<NDAppleScriptObjectSendEvent>	sendAppleEventTarget;
+    id<NDAppleScriptObjectActive>	activeTarget;
+    ComponentInstance			osaComponent;
+
+    long										executionModeFlags;
+}
+
++ (id)compileExecuteString:(NSString *) aString;
++ (Component)findNextComponent;
+
++ (id)appleScriptObjectWithString:(NSString *) aString;
++ (id)appleScriptObjectWithData:(NSData *) aData;
++ (id)appleScriptObjectWithContentsOfFile:(NSString *) aPath;
++ (id)appleScriptObjectWithContentsOfURL:(NSURL *) aURL;
+
+- (id)initWithString:(NSString *)aString modeFlags:(long)aModeFlags;
+- (id)initWithContentsOfFile:(NSString *)aPath;
+- (id)initWithContentsOfFile:(NSString *)aPath component:(Component)aComponent;
+- (id)initWithContentsOfURL:(NSURL *)anURL;
+- (id)initWithContentsOfURL:(NSURL *)aURL component:(Component)aComponent;
+- (id)initWithData:(NSData *)aDesc;
+
+- (id)initWithString:(NSString *)aString modeFlags:(long)aModeFlags component:(Component)aComponent;
+- (id)initWithData:(NSData *)aData component:(Component)aComponent;
+
+- (NSData *)data;
+
+- (BOOL)execute;
+- (BOOL)executeOpen:(NSArray *)aParameters;
+- (BOOL)executeEvent:(NSAppleEventDescriptor *)anEvent;
+
+- (NSArray *)arrayOfEventIdentifier;
+- (BOOL)respondsToEventClass:(AEEventClass)aEventClass eventID:(AEEventID)aEventID;
+
+- (NSAppleEventDescriptor *)resultAppleEventDescriptor;
+- (id)resultObject;
+- (id)resultData;
+- (NSString *)resultAsString;
+
+    //- (void)setContextAppleScriptObject:(NDAppleScriptObject *)aAppleScriptObject;		// NOT FUNCTIONING YET
+- (long)executionModeFlags;
+- (void)setExecutionModeFlags:(long)aModeFlags;
+
+- (void)setDefaultTarget:(NSAppleEventDescriptor *)aDefaultTarget;
+- (void)setDefaultTargetAsCreator:(OSType)aCreator;
+- (void)setFinderAsDefaultTarget;
+
+- (void)setAppleEventSendTarget:(id)aTarget;
+- (id)appleEventSendTarget;
+- (void)setActiveTarget:(id)aTarget;
+- (id)activeTarget;
+
+- (NSAppleEventDescriptor *)targetNoProcess;
+
+- (BOOL)writeToURL:(NSURL *)aURL;
+- (BOOL)writeToURL:(NSURL *)aURL Id:(short)anID;
+- (BOOL)writeToFile:(NSString *)aPath;
+- (BOOL)writeToFile:(NSString *)aPath Id:(short)anID;
+
+@end
Index: /releases/Pester/1.1a1/Source/NDAppleScript/NDAppleScriptObject.m
===================================================================
--- /releases/Pester/1.1a1/Source/NDAppleScript/NDAppleScriptObject.m	(revision 44)
+++ /releases/Pester/1.1a1/Source/NDAppleScript/NDAppleScriptObject.m	(revision 44)
@@ -0,0 +1,1273 @@
+/*
+ *  NDAppleScriptObject.m
+ *  NDAppleScriptObjectProject
+ *
+ *  Created by nathan on Thu Nov 29 2001.
+ *  Copyright (c) 2001 Nathan Day. All rights reserved.
+ */
+
+#import "NDAppleScriptObject.h"
+#import "NSURL+NDCarbonUtilities.h"
+#import "NDResourceFork.h"
+#import "NSAppleEventDescriptor+NDAppleScriptObject.h"
+
+const short	kScriptResourceID = 128;
+const OSType	kFinderCreatorCode = 'MACS';
+const OSType	kScriptEditorCreatorCode = 'ToyS';
+const OSType	kCompiledAppleScriptTypeCode = 'osas';
+
+static OSASendUPP				defaultSendProcPtr = NULL;
+static long						defaultSendProcRefCon = 0;
+static OSAActiveProcPtr		defaultActiveProcPtr = NULL;
+static long						defaultActiveProcRefCon = 0;
+
+void setUpToRecieveFirstEvent( NSAppleEventDescriptor * anAppleEventDescriptor );
+
+@interface NDAppleScriptObject (private)
++ (ComponentInstance)OSAComponent;
++ (id)objectForAEDesc:(const AEDesc *)aDesc;
+- (OSAID)compileString:(NSString *)aString modeFlags:(long)aModeFlags;
+- (ComponentInstance)OSAComponent;
+- (OSAID)loadCompiledScriptData:(NSData *)aData;
+
+- (OSAID)compiledScriptID;
+- (OSAID)scriptContextID;
+
+- (void)setAppleScriptProcedures;
++ (void)setAppleScriptSendProcWith:(id)anObject;
++ (void)setAppleScriptActiveProcWith:(id)anObject;
+
+@end
+
+@interface NSString (NDAEDescCreation)
++ (id)nd_stringWithAEDesc:(const AEDesc *)aDesc;
+@end
+
+@interface NSArray (NDAEDescCreation)
++ (id)nd_arrayWithAEDesc:(const AEDesc *)aDesc;
+@end
+
+@interface NSDictionary (NDAEDescCreation)
++ (id)nd_dictionaryWithAEDesc:(const AEDesc *)aDesc;
+@end
+
+@interface NSData (NDAEDescCreation)
++ (id)nd_dataWithAEDesc:(const AEDesc *)aDesc;
+@end
+
+@interface NSNumber (NDAEDescCreation)
++ (id)nd_numberWithAEDesc:(const AEDesc *)aDesc;
+@end
+
+@interface NSURL (NDAEDescCreation)
++ (id)nd_URLWithAEDesc:(const AEDesc *)aDesc;
+@end
+
+@interface NSAppleEventDescriptor (NDAEDescCreation)
++ (id)nd_appleEventDescriptorWithAEDesc:(const AEDesc *)aDesc;
+- (BOOL)nd_getAEDesc:(AEDesc *)aDescPtr;
+@end
+
+@implementation NDAppleScriptObject
+
+static ComponentInstance		defaultOSAComponent = NULL;
+
+/*
+ * + compileExecuteString:
+ */
++ (id)compileExecuteString:(NSString *) aString
+{
+    OSAID			theResultID;
+    AEDesc		theResultDesc = { typeNull, NULL },
+        theScriptDesc = { typeNull, NULL };
+    id				theResultObject = nil;
+
+    [self setAppleScriptSendProcWith:nil];		// we have no instance
+    [self setAppleScriptActiveProcWith:nil];
+
+    if( (AECreateDesc( typeChar, [aString cString], [aString cStringLength], &theScriptDesc) ==  noErr) && (OSACompileExecute( [self OSAComponent], &theScriptDesc, kOSANullScript, kOSAModeNull, &theResultID) ==  noErr ) )
+       {
+        if( OSACoerceToDesc( [self OSAComponent], theResultID, typeWildCard, kOSAModeNull, &theResultDesc ) == noErr )
+           {
+            if( theResultDesc.descriptorType != typeNull )
+               {
+                theResultObject = [self objectForAEDesc:&theResultDesc];
+                AEDisposeDesc( &theResultDesc );
+               }
+           }
+        AEDisposeDesc( &theScriptDesc );
+        if( theResultID != kOSANullScript )
+            OSADispose( [self OSAComponent], theResultID );
+       }
+
+    return theResultObject;
+}
+
+/*
+ * findNextComponent
+ */
++ (Component)findNextComponent
+{
+    ComponentDescription		theReturnCompDesc;
+    static Component			theLastComponent = NULL;
+    ComponentDescription		theComponentDesc;
+
+    theComponentDesc.componentType = kOSAComponentType;
+    theComponentDesc.componentSubType = kOSAGenericScriptingComponentSubtype;
+    theComponentDesc.componentManufacturer = 0;
+    theComponentDesc.componentFlags =  kOSASupportsCompiling | kOSASupportsGetSource
+        | kOSASupportsAECoercion | kOSASupportsAESending
+        | kOSASupportsConvenience | kOSASupportsDialects
+        | kOSASupportsEventHandling;
+
+    theComponentDesc.componentFlagsMask = theComponentDesc.componentFlags;
+
+    do
+       {
+           theLastComponent = FindNextComponent( theLastComponent, &theComponentDesc );
+       }
+    while( GetComponentInfo( theLastComponent, &theReturnCompDesc, NULL, NULL, NULL ) == noErr && theComponentDesc.componentSubType == kOSAGenericScriptingComponentSubtype );
+
+    return theLastComponent;
+}
+
+/*
+ * closeDefaultComponent
+ */
++ (void)closeDefaultComponent
+{
+    if( defaultOSAComponent != NULL )
+        CloseComponent( defaultOSAComponent );
+}
+
+/*
+ * + appleScriptObjectWithString:
+ */
++ (id)appleScriptObjectWithString:(NSString *) aString
+{
+    return [[[self alloc] initWithString:aString modeFlags:kOSAModeCompileIntoContext] autorelease];
+}
+
+/*
+ * + appleScriptObjectWithData:
+ */
++ (id)appleScriptObjectWithData:(NSData *) aData
+{
+    return [[[self alloc] initWithData:aData] autorelease];
+}
+
+/*
+ * + appleScriptObjectWithPath:
+ */
++ (id)appleScriptObjectWithContentsOfFile:(NSString *) aPath
+{
+    return [[[self alloc] initWithContentsOfFile:aPath] autorelease];
+}
+
+/*
+ * + appleScriptObjectWithURL:
+ */
++ (id)appleScriptObjectWithContentsOfURL:(NSURL *) aURL
+{
+    return [[[self alloc] initWithContentsOfURL:aURL] autorelease];
+}
+
+
+/*
+ * - initWithString:modeFlags:
+ */
+- (id)initWithString:(NSString *)aString modeFlags:(long)aModeFlags
+{
+    return [self initWithString:aString modeFlags:aModeFlags component:NULL];
+}
+
+/*
+ * - initWithContentsOfFile:
+ */
+- (id)initWithContentsOfFile:(NSString *)aPath
+{
+    return [self initWithContentsOfFile:aPath component:NULL];
+}
+
+/*
+ * - initWithContentsOfFile:component:
+ */
+- (id)initWithContentsOfFile:(NSString *)aPath component:(Component)aComponent
+{
+    NSData *theData;
+
+    theData = [[NDResourceFork resourceForkForReadingAtPath:aPath] dataForType: kOSAScriptResourceType Id: kScriptResourceID];
+
+    if (theData != nil) {
+        self = [self initWithData: theData component: aComponent];
+    } else {
+        NSString *scriptString = [NSString stringWithContentsOfFile: aPath];
+        self = [self initWithString: scriptString modeFlags: kOSAModeCompileIntoContext component: aComponent];
+    }
+
+    return self;
+}
+
+/*
+ * initWithContentsOfURL:
+ */
+- (id)initWithContentsOfURL:(NSURL *)aURL
+{
+    return [self initWithContentsOfURL:aURL component:NULL];
+}
+
+/*
+ * - initWithContentsOfURL:
+ */
+- (id)initWithContentsOfURL:(NSURL *)aURL component:(Component)aComponent
+{
+    NSData		* theData;
+
+    theData = [[NDResourceFork resourceForkForReadingAtURL:aURL] dataForType:kOSAScriptResourceType Id:kScriptResourceID];
+
+    if( theData != nil )
+       {
+        self = [self initWithData:theData component:aComponent];
+       }
+    else
+       {
+        [self release];
+        self = nil;
+       }
+
+    return self;
+}
+
+/*
+ * - initWithAppleEventDescriptor:
+ */
+- (id)initWithAppleEventDescriptor:(NSAppleEventDescriptor *)aDescriptor
+{
+    if( [aDescriptor descriptorType] == cScript )
+       {
+        self = [self initWithData:[aDescriptor data]];
+       }
+    else
+       {
+        [self release];
+        self = nil;
+       }
+
+    return self;
+}
+
+/*
+ * - initWithData:
+ */
+- (id)initWithData:(NSData *)aData
+{
+    return [self initWithData:aData component:NULL];
+}
+
+/*
+ * - initWithString:modeFlags:component:
+ */
+- (id)initWithString:(NSString *)aString modeFlags:(long)aModeFlags component:(Component)aComponent
+{
+    if( ( self = [super init] )  )
+       {
+        if( aComponent != NULL )
+            osaComponent = OpenComponent( aComponent );
+        else
+            osaComponent = NULL;
+
+        compiledScriptID = [self compileString:aString modeFlags: aModeFlags];
+        resultingValueID = kOSANullScript;
+        executionModeFlags = kOSAModeNull;
+        osaComponent = NULL;
+
+        if( compiledScriptID == kOSANullScript )
+           {
+            [self release];
+            self = nil;
+           }
+       }
+
+    return self;
+}
+
+/*
+ * - initWithData:componet:
+ */
+- (id)initWithData:(NSData *)aData component:(Component)aComponent
+{
+    if( (self = [self init]) != nil )
+       {
+        if( aComponent != NULL )
+            osaComponent = OpenComponent( aComponent );
+        else
+            osaComponent = NULL;
+
+        compiledScriptID = [self loadCompiledScriptData:aData];
+        resultingValueID = kOSANullScript;
+        executionModeFlags = kOSAModeNull;
+        osaComponent = NULL;
+
+        if( compiledScriptID == kOSANullScript )
+           {
+            [self release];
+            self = nil;
+           }
+       }
+
+    return self;
+}
+
+/*
+ * - dealloc
+ */
+-(void)dealloc
+{
+    if( compiledScriptID != kOSANullScript )
+        OSADispose( [self OSAComponent], compiledScriptID );
+    if( resultingValueID != kOSANullScript )
+        OSADispose( [self OSAComponent], resultingValueID );
+
+    [contextAppleScriptObject release];
+    [sendAppleEventTarget release];
+    [activeTarget release];
+
+    if( osaComponent != NULL )
+       {
+        CloseComponent( osaComponent );
+       }
+    else	// clean up the default components send and active procedure
+       {
+        OSASetSendProc( [NDAppleScriptObject OSAComponent], defaultSendProcPtr, defaultSendProcRefCon );
+        OSASetActiveProc( [NDAppleScriptObject OSAComponent], defaultActiveProcPtr , defaultActiveProcRefCon );
+       }
+
+    [super dealloc];
+}
+
+/*
+ * - data
+ */
+- (NSData *)data
+{
+    NSData				* theData;
+    OSStatus				theError;
+    AEDesc				theDesc = { typeNull, NULL };
+
+    theError = (OSErr)OSAStore( [self OSAComponent], compiledScriptID, typeOSAGenericStorage, kOSAModeNull, &theDesc );
+    if( noErr == theError ) {
+        theData = [NSData nd_dataWithAEDesc: &theDesc];
+        AEDisposeDesc( &theDesc );
+        return theData;
+    }
+    
+    return nil;
+}
+
+/*
+ * - execute
+ */
+- (BOOL)execute
+{
+    OSStatus		theError;
+
+    [self setAppleScriptProcedures];
+    theError = OSAExecute([self OSAComponent], compiledScriptID, [self scriptContextID], [self executionModeFlags], &resultingValueID);
+
+    return (theError == noErr);
+}
+
+/*
+ * - executeOpen:
+ */
+- (BOOL)executeOpen:(NSArray *)aParameters
+{
+    NSAppleEventDescriptor	* theEvent = nil,
+    * theEventList = nil,
+    * theTarget = nil;
+
+    theTarget = [self targetNoProcess];
+    theEventList = [NSAppleEventDescriptor aliasListDescriptorWithArray:aParameters];
+
+    if( theTarget != nil && theEventList != nil )
+       {
+        theEvent = [NSAppleEventDescriptor appleEventWithEventClass:kCoreEventClass eventID:kAEOpenDocuments targetDescriptor:theTarget returnID:kAutoGenerateReturnID transactionID:kAnyTransactionID];
+
+        [theEvent setParamDescriptor:theEventList forKeyword:keyDirectObject];
+       }
+
+    return (theEvent != nil ) ? [self executeEvent:theEvent] : NO;
+}
+
+/*
+ * - executeEvent:
+ */
+- (BOOL)executeEvent:(NSAppleEventDescriptor *)anEvent
+{
+    AEDesc				theEventDesc;
+    BOOL					theSuccess = NO;
+
+    if( [anEvent nd_getAEDesc:&theEventDesc] )
+       {
+        [self setAppleScriptProcedures];
+        theSuccess = OSAExecuteEvent([self OSAComponent], &theEventDesc, compiledScriptID, [self executionModeFlags], &resultingValueID) == noErr;
+        AEDisposeDesc( &theEventDesc );
+       }
+
+    return theSuccess;
+}
+
+/*
+ * - arrayOfEventIdentifier
+ */
+- (NSArray *)arrayOfEventIdentifier
+{
+    NSArray			* theNamesArray = nil;
+    AEDescList		theNamesDescList;
+    if( OSAGetHandlerNames ( [self OSAComponent], kOSAModeNull, compiledScriptID, &theNamesDescList ) == noErr )
+       {
+        theNamesArray = [NDAppleScriptObject objectForAEDesc: &theNamesDescList];
+        AEDisposeDesc( &theNamesDescList );
+       }
+
+    return theNamesArray;
+}
+
+/*
+ * - respondsToEventClass:eventID:
+ */
+- (BOOL)respondsToEventClass:(AEEventClass)aEventClass eventID:(AEEventID)aEventID
+{
+    NSString		* theEventClass,
+    * theEventID;
+    theEventClass = [NSString stringWithCString:(char*)&aEventClass length:sizeof(aEventClass)];
+    theEventID = [NSString stringWithCString:(char*)&aEventID length:sizeof(aEventID)];
+
+    return [[self arrayOfEventIdentifier] containsObject:[theEventClass stringByAppendingString:theEventID]];
+}
+
+/*
+ * resultDescriptor
+ */
+- (NSAppleEventDescriptor *)resultAppleEventDescriptor
+{
+    AEDesc									theResultDesc = { typeNull, NULL };
+    NSAppleEventDescriptor				* theResult = nil;
+
+    if( OSACoerceToDesc( [self OSAComponent], resultingValueID, typeWildCard, kOSAModeNull, &theResultDesc ) == noErr )
+       {
+        theResult = [NSAppleEventDescriptor descriptorWithDescriptorType:theResultDesc.descriptorType data:[NSData nd_dataWithAEDesc:&theResultDesc]];
+        AEDisposeDesc(&theResultDesc);
+       }
+
+    return theResult;
+}
+
+/*
+ * resultObject
+ */
+- (id)resultObject
+{
+    id				theResult = nil;
+
+    if( resultingValueID != kOSANullScript )
+       {
+        AEDesc		theResultDesc = { typeNull, NULL };
+
+        if( OSACoerceToDesc( [self OSAComponent], resultingValueID, typeWildCard, kOSAModeNull, &theResultDesc ) == noErr )
+           {
+            theResult = [NDAppleScriptObject objectForAEDesc: &theResultDesc];
+            AEDisposeDesc(&theResultDesc);
+           }
+       }
+
+    return theResult;
+}
+
+/*
+ * resultData
+ */
+- (id)resultData
+{
+    id				theResult = nil;
+
+    if( resultingValueID != kOSANullScript )
+       {
+        AEDesc		theResultDesc = { typeNull, NULL };
+
+        if( OSACoerceToDesc( [self OSAComponent], resultingValueID, typeWildCard, kOSAModeNull, &theResultDesc ) == noErr )
+           {
+            theResult = [NSData nd_dataWithAEDesc: &theResultDesc];
+            AEDisposeDesc(&theResultDesc);
+           }
+       }
+
+    return theResult;
+}
+
+/*
+ * - resultAsString
+ */
+- (NSString *)resultAsString
+{
+    AEDesc					theResultDesc = { typeNull, NULL };
+    NSString					* theResult = nil;
+
+    if( OSADisplay( [self OSAComponent], resultingValueID, typeChar, kOSAModeNull, &theResultDesc ) == noErr )
+       {
+        theResult = [NSString nd_stringWithAEDesc:&theResultDesc];
+        AEDisposeDesc(&theResultDesc);
+       }
+
+    return theResult;
+}
+
+/*
+ * - setContextAppleScriptObject:
+ */
+- (void)setContextAppleScriptObject:(NDAppleScriptObject *)anAppleScriptObject
+{
+    [contextAppleScriptObject release];
+    contextAppleScriptObject = [anAppleScriptObject retain];
+}
+
+/*
+ * - executionModeFlags
+ */
+- (long)executionModeFlags
+{
+    return executionModeFlags;
+}
+
+/*
+ * - setExecutionModeFlags:
+ */
+- (void)setExecutionModeFlags:(long)aModeFlags
+{
+    executionModeFlags = aModeFlags;
+}
+
+/*
+ * - setDefaultTarget:
+ */
+- (void)setDefaultTarget:(NSAppleEventDescriptor *)aDefaultTarget
+{
+    AEAddressDesc		theTargetDesc;
+    if( [aDefaultTarget nd_getAEDesc:(AEDesc*)&theTargetDesc] )
+       {
+        OSASetDefaultTarget( [self OSAComponent], &theTargetDesc );
+       }
+    else
+       {
+        NSLog( @"Could not set default target" );
+       }
+}
+
+/*
+ * - setDefaultTargetAsCreator:
+ */
+- (void)setDefaultTargetAsCreator:(OSType)aCreator
+{
+    NSAppleEventDescriptor	* theAppleEventDescriptor;
+
+    theAppleEventDescriptor = [NSAppleEventDescriptor descriptorWithDescriptorType:typeApplSignature data:[NSData dataWithBytes:&aCreator length:sizeof(aCreator)]];
+    [self setDefaultTarget:theAppleEventDescriptor];
+}
+
+/*
+ * - setFinderAsDefaultTarget
+ */
+- (void)setFinderAsDefaultTarget
+{
+    [self setDefaultTargetAsCreator:kFinderCreatorCode];
+}
+
+/*
+ * setAppleEventSendTarget:
+ */
+- (void)setAppleEventSendTarget:(id)aTarget
+{
+    [sendAppleEventTarget release];
+    sendAppleEventTarget = [aTarget retain];
+}
+
+/*
+ * appleEventSendTarget
+ */
+- (id)appleEventSendTarget
+{
+    return sendAppleEventTarget;
+}
+
+/*
+ * setActiveTarget:
+ */
+- (void)setActiveTarget:(id)aTarget
+{
+    [activeTarget release];
+    activeTarget = [aTarget retain];
+}
+
+/*
+ * activeTarget
+ */
+- (id)activeTarget
+{
+    return activeTarget;
+}
+
+/*
+ * sendAppleEvent:sendMode:sendPriority:timeOutInTicks:idleProc:filterProc:
+ */
+- (NSAppleEventDescriptor *)sendAppleEvent:(NSAppleEventDescriptor *)theAppleEventDescriptor sendMode:(AESendMode)aSendMode sendPriority:(AESendPriority)aSendPriority timeOutInTicks:(long)aTimeOutInTicks idleProc:(AEIdleUPP)anIdleProc filterProc:(AEFilterUPP)aFilterProc
+{
+    AppleEvent						theAppleEvent;
+    NSAppleEventDescriptor		* theReplyAppleEventDesc = nil;
+
+    if( [theAppleEventDescriptor nd_getAEDesc:&theAppleEvent] )
+       {
+        AppleEvent					theReplyAppleEvent;
+
+        if( defaultSendProcPtr != NULL )
+           {
+            setUpToRecieveFirstEvent( theAppleEventDescriptor );
+
+            if( defaultSendProcPtr( &theAppleEvent, &theReplyAppleEvent, aSendMode, aSendPriority, aTimeOutInTicks, anIdleProc, aFilterProc, defaultSendProcRefCon ) == noErr )
+               {
+                theReplyAppleEventDesc = [NSAppleEventDescriptor nd_appleEventDescriptorWithAEDesc:&theReplyAppleEvent];
+               }
+           }
+        else
+           {
+            NSLog(@"No default send procedure");
+           }
+       }
+
+    return theReplyAppleEventDesc;
+}
+
+/*
+ * appleScriptActive
+ */
+- (BOOL)appleScriptActive
+{
+    if( defaultActiveProcPtr != NULL )
+       {
+        return defaultActiveProcPtr( defaultActiveProcRefCon ) == noErr;
+       }
+    else
+       {
+        NSLog(@"No default active procedure");
+        return NO;
+       }
+}
+
+/*
+ * targetNoProcess
+ */
+- (NSAppleEventDescriptor *)targetNoProcess
+{
+    unsigned int				theCurrentProcess = kNoProcess; //kCurrentProcess;
+
+    return [NSAppleEventDescriptor descriptorWithDescriptorType:typeProcessSerialNumber data:[NSData dataWithBytes:&theCurrentProcess length:sizeof(theCurrentProcess)]];
+}
+
+/*
+ * description
+ */
+- (NSString *)description
+{
+    AEDesc		theDesc = { typeNull, NULL };
+    NSString		* theResult = nil;
+
+    if( OSAGetSource( [self OSAComponent], compiledScriptID, typeChar, &theDesc) == noErr )
+       {
+        theResult = [NSString nd_stringWithAEDesc: &theDesc];
+        AEDisposeDesc( &theDesc );
+       }
+
+    return theResult;
+}
+
+/*
+ * writeToURL:
+ */
+- (BOOL)writeToURL:(NSURL *)aURL
+{
+    return [self writeToURL:aURL Id:kScriptResourceID];
+}
+
+/*
+ * writeToURL:Id:
+ */
+- (BOOL)writeToURL:(NSURL *)aURL Id:(short)anID
+{
+    NSData				* theData;
+    NDResourceFork		* theResourceFork;
+    BOOL					theResult = NO,
+        theCanNotWriteTo = NO;
+
+    if( (theData = [self data]) )
+       {
+        if( ![[NSFileManager defaultManager] fileExistsAtPath:[aURL path] isDirectory:&theCanNotWriteTo] )
+           {
+            theCanNotWriteTo = ![[NSFileManager defaultManager] createFileAtPath:[aURL path] contents:nil attributes:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedLong:kScriptEditorCreatorCode], NSFileHFSCreatorCode, [NSNumber numberWithUnsignedLong:kCompiledAppleScriptTypeCode], NSFileHFSTypeCode, nil]];
+           }
+
+        if( !theCanNotWriteTo && (theResourceFork = [NDResourceFork resourceForkForWritingAtURL:aURL]) )
+            theResult = [theResourceFork addData:theData type:kOSAScriptResourceType Id:anID name:@"script"];
+       }
+
+    return theResult;
+}
+
+/*
+ * writeToFile:
+ */
+- (BOOL)writeToFile:(NSString *)aPath
+{
+    return [self writeToURL:[NSURL fileURLWithPath:aPath] Id:kScriptResourceID];
+}
+
+/*
+ * writeToFile:Id:
+ */
+- (BOOL)writeToFile:(NSString *)aPath Id:(short)anID
+{
+    return [self writeToURL:[NSURL fileURLWithPath:aPath] Id:anID];
+}
+
+@end
+
+@implementation NDAppleScriptObject (private)
+
+/*
+ * OSAComponent
+ */
++ (ComponentInstance)OSAComponent
+{
+    if( defaultOSAComponent == NULL )
+       {
+        defaultOSAComponent = OpenDefaultComponent( kOSAComponentType, kAppleScriptSubtype );
+       }
+    return defaultOSAComponent;
+}
+
+/*
+ * + appleScriptObjectWithAEDesc:
+ */
++ (id)appleScriptObjectWithAEDesc:(const AEDesc *)aDesc
+{
+    NSData		* theData;
+
+    theData = [NSData nd_dataWithAEDesc: aDesc];
+
+    return ( theData == nil ) ? nil : [[[self alloc] initWithData:theData] autorelease];
+}
+
+/*
+ * + objectForAEDesc:
+ */
++ (id)objectForAEDesc:(const AEDesc *)aDesc
+{
+    id			theResult;
+
+#if 0
+    char		*theType = (char*)&aDesc->descriptorType;
+    NSLog(@"objectForAEDesc: recieved type '%c%c%c%c'\n",theType[0],theType[1],theType[2],theType[3]);
+#endif
+
+    switch(aDesc->descriptorType)
+       {
+        case typeBoolean:						//	1-byte Boolean value
+        case typeShortInteger:				//	16-bit integer
+                                  //		case typeSMInt:							//	16-bit integer
+        case typeLongInteger:				//	32-bit integer
+                                 //		case typeInteger:							//	32-bit integer
+        case typeShortFloat:					//	SANE single
+                                 //		case typeSMFloat:							//	SANE single
+        case typeFloat:						//	SANE double
+                             //		case typeLongFloat:						//	SANE double
+                             //		case typeExtended:						//	SANE extended
+                             //		case typeComp:							//	SANE comp
+        case typeMagnitude:					//	unsigned 32-bit integer
+        case typeTrue:							//	TRUE Boolean value
+        case typeFalse:						//	FALSE Boolean value
+            theResult = [NSNumber nd_numberWithAEDesc:aDesc];
+            break;
+        case typeChar:							//	unterminated string
+            theResult = [NSString nd_stringWithAEDesc:aDesc];
+            break;
+        case typeAEList:						//	list of descriptor records
+            theResult = [NSArray nd_arrayWithAEDesc:aDesc];
+            break;
+        case typeAERecord:					//	list of keyword-specified
+            theResult = [NSDictionary nd_dictionaryWithAEDesc:aDesc];
+            break;
+        case typeAppleEvent:						//	Apple event record
+            theResult = [NSAppleEventDescriptor nd_appleEventDescriptorWithAEDesc:aDesc];
+            break;
+        case typeAlias:							//	alias record
+        case typeFileURL:
+            theResult = [NSURL nd_URLWithAEDesc:aDesc];
+            break;
+            //		case typeEnumerated:					//	enumerated data
+            //			break;
+        case cScript:							// script data
+            theResult = [NDAppleScriptObject appleScriptObjectWithAEDesc:aDesc];
+            break;
+        case cEventIdentifier:
+            theResult = [NSString nd_stringWithAEDesc: aDesc];
+            break;
+        default:
+            theResult = [NSAppleEventDescriptor nd_appleEventDescriptorWithAEDesc:aDesc];
+            //			theResult = [NSData nd_dataWithAEDesc: aDesc];
+            break;
+       }
+
+    return theResult;
+}
+
+/*
+ * - compileString:
+ */
+- (OSAID)compileString:(NSString *)aString modeFlags:(long)aModeFlags
+{
+    OSAID				theCompiledScript = kOSANullScript;
+    AEDesc			theScriptDesc = { typeNull, NULL };
+
+    if ( AECreateDesc( typeChar, [aString cString], [aString cStringLength], &theScriptDesc) == noErr )
+       {
+        OSACompile([self OSAComponent], &theScriptDesc, aModeFlags, &theCompiledScript);
+        AEDisposeDesc( &theScriptDesc );
+       }
+
+    return theCompiledScript;
+}
+
+/*
+ * OSAComponent
+ */
+- (ComponentInstance)OSAComponent
+{
+    if( osaComponent == NULL )
+       {
+        return [NDAppleScriptObject OSAComponent];
+       }
+    else
+        return osaComponent;
+}
+
+/*
+ * - loadCompiledScriptData:
+ */
+- (OSAID)loadCompiledScriptData:(NSData *)aData
+{
+    AEDesc		theScriptDesc = { typeNull, NULL };
+    OSAID			theCompiledScript = kOSANullScript;
+
+    if( AECreateDesc( typeOSAGenericStorage, [aData bytes], [aData length], &theScriptDesc ) == noErr )
+       {
+        OSALoad([self OSAComponent], &theScriptDesc, kOSAModeCompileIntoContext, &theCompiledScript);
+        AEDisposeDesc( &theScriptDesc );
+       }
+
+    return theCompiledScript;
+}
+
+/*
+ * - compiledScriptID
+ */
+- (OSAID)compiledScriptID
+{
+    return compiledScriptID;
+}
+
+/*
+ * - scriptContextID
+ */
+- (OSAID)scriptContextID
+{
+    return ( contextAppleScriptObject ) ? [contextAppleScriptObject compiledScriptID] : kOSANullScript;
+}
+
+/*
+ * setAppleScriptProcedures
+ */
+- (void)setAppleScriptProcedures
+{
+    [NDAppleScriptObject setAppleScriptSendProcWith:self];
+    [NDAppleScriptObject setAppleScriptActiveProcWith:self];
+}
+
+/*
+ * + setAppleScriptSendProcWith:
+ */
++ (void)setAppleScriptSendProcWith:(id)anObject
+{
+    OSErr		AppleEventSendProc( const AppleEvent *theAppleEvent, AppleEvent *reply, AESendMode sendMode, AESendPriority sendPriority, long timeOutInTicks, AEIdleUPP idleProc, AEFilterUPP filterProc, long refCon );
+
+    ComponentInstance		theComponent;
+
+    if( sizeof(long) != sizeof(id) )
+        NSAssert( sizeof(long) == sizeof(id), @"This method works by assuming type long is the same size as type id" );
+
+    // use anObjects component else use default component
+    theComponent = (anObject) ? [anObject OSAComponent] : [self OSAComponent];
+
+    /*
+     * need to save the default send proceedure as we will call it in our send proceedure
+     */
+    if( defaultSendProcPtr == NULL )
+       {
+        if( OSAGetSendProc( theComponent, &defaultSendProcPtr, &defaultSendProcRefCon) != noErr )
+           {
+            defaultSendProcPtr = NULL;
+            NSLog(@"Could not get default AppleScript send procedure");
+           }
+       }
+
+    if( OSASetSendProc( theComponent, AppleEventSendProc, (long)anObject ) != noErr )
+       {
+        NSLog(@"Could not set AppleScript send procedure");
+       }
+}
+/*
+ * function AppleEventSendProc
+ */
+OSErr AppleEventSendProc( const AppleEvent *anAppleEvent, AppleEvent *aReply, AESendMode aSendMode, AESendPriority aSendPriority, long aTimeOutInTicks, AEIdleUPP anIdleProc, AEFilterUPP aFilterProc, long aRefCon )
+{
+    NSAppleEventDescriptor		* theAppleEventDescriptor = nil,
+    * theAppleEventDescReply;
+    OSErr								theError = errOSASystemError;
+    id									theSendTarget;
+
+    /*
+     * if we have an instance, it has a target and we can create a NSAppleEventDescriptor
+     */
+    if( aRefCon != nil && (theSendTarget = [(id)aRefCon appleEventSendTarget]) != nil && (theAppleEventDescriptor = [NSAppleEventDescriptor nd_appleEventDescriptorWithAEDesc:anAppleEvent]) != nil )
+       {
+        theAppleEventDescReply = [theSendTarget sendAppleEvent:theAppleEventDescriptor sendMode:aSendMode sendPriority:aSendPriority timeOutInTicks:aTimeOutInTicks idleProc:anIdleProc filterProc:aFilterProc];
+
+        if( [theAppleEventDescReply nd_getAEDesc:(AEDesc*)aReply] )
+            theError = noErr;			// NO ERROR
+       }
+    else if( defaultSendProcPtr != NULL )
+       {
+        setUpToRecieveFirstEvent( theAppleEventDescriptor );
+
+        theError = defaultSendProcPtr( anAppleEvent, aReply, aSendMode, aSendPriority, aTimeOutInTicks, anIdleProc, aFilterProc, defaultSendProcRefCon );
+
+       }
+
+    return theError;
+}
+
+/*
+ * + setAppleScriptActiveProcWith:
+ */
++ (void)setAppleScriptActiveProcWith:(id)anObject
+{
+    OSErr						AppleScriptActiveProc( long aRefCon );
+    ComponentInstance		theComponent;
+
+    // use anObjects component else use default component
+    theComponent = (anObject) ? [anObject OSAComponent] : [self OSAComponent];
+
+    /*
+     * need to save the default active proceedure as we will call it in our active proceedure
+     */
+    if( defaultActiveProcPtr == NULL )
+       {
+        if( OSAGetActiveProc(theComponent, &defaultActiveProcPtr, &defaultActiveProcRefCon ) != noErr )
+           {
+            defaultActiveProcPtr = NULL;
+            NSLog(@"Could not get default AppleScript active procedure");
+           }
+       }
+
+    if( sizeof(long) != sizeof(id) )
+        NSAssert( sizeof(long) == sizeof(id), @"This method works by assuming type long is the same size as type id" );
+
+    if( OSASetActiveProc( theComponent, AppleScriptActiveProc , (long)anObject ) != noErr )
+        NSLog(@"Could not set AppleScript active procedure.");
+}
+/*
+	* function AppleScriptActiveProc
+	*/
+OSErr AppleScriptActiveProc( long aRefCon )
+{
+    id			theActiveTarget;
+
+    if( aRefCon != nil && (theActiveTarget = [(id)aRefCon activeTarget]) != nil )
+       {
+        return [theActiveTarget appleScriptActive] ? noErr : errOSASystemError;
+       }
+    else
+       {
+        return defaultActiveProcPtr( defaultActiveProcRefCon );
+       }
+}
+
+@end
+
+/*
+ * function setUpToRecieveFirstEvent
+ */
+void setUpToRecieveFirstEvent( NSAppleEventDescriptor * anAppleEventDescriptor )
+{
+    static BOOL						hasFirstEventBeenSent = NO;
+    /*
+     * if this is the first event to current process need to send Notification for it to work
+     */
+    if( !hasFirstEventBeenSent && [anAppleEventDescriptor isTargetCurrentProcess] )
+       {
+        hasFirstEventBeenSent = YES;
+        [[NSNotificationCenter defaultCenter] postNotificationName:NSAppleEventManagerWillProcessFirstEventNotification object:[NSAppleEventManager sharedAppleEventManager]];
+       }
+}
+
+@implementation NSString (NDAEDescCreation)
+
+/*
+ * + nd_stringWithAEDesc:
+ */
++ (id)nd_stringWithAEDesc:(const AEDesc *)aDesc
+{
+    NSData			* theTextData;
+
+    theTextData = [NSData nd_dataWithAEDesc: aDesc];
+
+    return ( theTextData == nil ) ? nil : [[[NSString alloc]initWithData:theTextData encoding:NSMacOSRomanStringEncoding] autorelease];
+}
+
+@end
+
+@implementation NSArray (NDAEDescCreation)
+
+/*
+ * + nd_arrayWithAEDesc:
+ */
++ (id)nd_arrayWithAEDesc:(const AEDesc *)aDesc
+{
+    SInt32				theNumOfItems,
+    theIndex;
+    id						theInstance = nil;
+
+    AECountItems( aDesc, &theNumOfItems );
+    theInstance = [NSMutableArray arrayWithCapacity:theNumOfItems];
+
+    for( theIndex = 1; theIndex <= theNumOfItems; theIndex++)
+       {
+        AEDesc		theDesc = { typeNull, NULL };
+        AEKeyword	theAEKeyword;
+
+        if( AEGetNthDesc ( aDesc, theIndex, typeWildCard, &theAEKeyword, &theDesc ) == noErr )
+           {
+            [theInstance addObject: [NDAppleScriptObject objectForAEDesc: &theDesc]];
+            AEDisposeDesc( &theDesc );
+           }
+       }
+
+    return theInstance;
+}
+
+@end
+
+@implementation NSDictionary (NDAEDescCreation)
+
+/*
+ * + nd_dictionaryWithAEDesc:
+ */
++ (id)nd_dictionaryWithAEDesc:(const AEDesc *)aDesc
+{
+    id						theInstance = nil;
+    AEDesc				theListDesc = { typeNull, NULL };
+    AEKeyword			theAEKeyword;
+
+    if( AEGetNthDesc ( aDesc, 1, typeWildCard, &theAEKeyword, &theListDesc ) == noErr )
+       {
+        SInt32				theNumOfItems,
+								theIndex;
+        AECountItems( &theListDesc, &theNumOfItems );
+        theInstance = [NSMutableDictionary dictionaryWithCapacity:theNumOfItems];
+
+        for( theIndex = 1; theIndex <= theNumOfItems; theIndex += 2)
+           {
+            AEDesc		theDesc = { typeNull, NULL },
+            theKeyDesc = { typeNull, NULL };
+
+
+            if( ( AEGetNthDesc ( &theListDesc, theIndex + 1, typeWildCard, &theAEKeyword, &theDesc ) == noErr) && ( AEGetNthDesc ( &theListDesc, theIndex, typeWildCard, &theAEKeyword, &theKeyDesc ) == noErr) )
+               {
+                [theInstance setObject: [NDAppleScriptObject objectForAEDesc: &theDesc] forKey:[NSString nd_stringWithAEDesc: &theKeyDesc]];
+                AEDisposeDesc( &theDesc );
+                AEDisposeDesc( &theKeyDesc );
+               }
+            else
+               {
+                AEDisposeDesc( &theDesc );
+                theInstance = nil;
+                break;
+               }
+           }
+        AEDisposeDesc( &theListDesc );
+       }
+
+    return theInstance;
+}
+
+@end
+
+@implementation NSData (NDAEDescCreation)
+
+/*
+ * + nd_dataWithAEDesc:
+ */
++ (id)nd_dataWithAEDesc:(const AEDesc *)aDesc
+{
+    NSMutableData *			theInstance;
+
+    theInstance = [NSMutableData dataWithLength: (unsigned int)AEGetDescDataSize(aDesc)];
+
+    if( AEGetDescData(aDesc, [theInstance mutableBytes], [theInstance length]) != noErr )
+       {
+        theInstance = nil;
+       }
+
+    return theInstance;
+}
+
+@end
+
+@implementation NSNumber (NDAEDescCreation)
+
++ (id)nd_numberWithAEDesc:(const AEDesc *)aDesc
+{
+    id						theInstance = nil;
+
+    switch(aDesc->descriptorType)
+       {
+        case typeBoolean:						//	1-byte Boolean value
+           {
+               BOOL		theBoolean;
+               if( AEGetDescData(aDesc, &theBoolean, sizeof(BOOL)) == noErr )
+                   theInstance = [NSNumber numberWithBool:theBoolean];
+               break;
+           }
+        case typeShortInteger:				//	16-bit integer
+                                  //		case typeSMInt:							//	16-bit integer
+           {
+               short int		theInteger;
+               if( AEGetDescData(aDesc, &theInteger, sizeof(short int)) == noErr )
+                   theInstance = [NSNumber numberWithShort: theInteger];
+               break;
+           }
+        case typeLongInteger:				//	32-bit integer
+                                 //		case typeInteger:							//	32-bit integer
+           {
+               int		theInteger;
+               if( AEGetDescData(aDesc, &theInteger, sizeof(int)) == noErr )
+                   theInstance = [NSNumber numberWithInt: theInteger];
+               break;
+           }
+        case typeShortFloat:					//	SANE single
+                                 //		case typeSMFloat:							//	SANE single
+           {
+               float		theFloat;
+               if( AEGetDescData(aDesc, &theFloat, sizeof(float)) == noErr )
+                   theInstance = [NSNumber numberWithFloat: theFloat];
+               break;
+           }
+        case typeFloat:						//	SANE double
+                             //		case typeLongFloat:						//	SANE double
+           {
+               double theFloat;
+               if( AEGetDescData(aDesc, &theFloat, sizeof(double)) == noErr )
+                   theInstance = [NSNumber numberWithDouble: theFloat];
+               break;
+           }
+            //		case typeExtended:						//	SANE extended
+            //			break;
+            //		case typeComp:							//	SANE comp
+            //			break;
+        case typeMagnitude:					//	unsigned 32-bit integer
+           {
+               unsigned int		theInteger;
+               if( AEGetDescData(aDesc, &theInteger, sizeof(unsigned int)) == noErr )
+                   theInstance = [NSNumber numberWithUnsignedInt: theInteger];
+               break;
+           }
+        case typeTrue:							//	TRUE Boolean value
+            theInstance = [NSNumber numberWithBool:YES];
+            break;
+        case typeFalse:						//	FALSE Boolean value
+            theInstance = [NSNumber numberWithBool:NO];
+            break;
+        default:
+            theInstance = nil;
+            break;
+       }
+
+    return theInstance;
+}
+
+@end
+
+@implementation NSURL (NDAEDescCreation)
+
+/*
+ * + nd_URLWithAEDesc:
+ */
++ (id)nd_URLWithAEDesc:(const AEDesc *)aDesc
+{
+    unsigned int	theSize;
+    id					theURL = nil;
+    OSAError			theError;
+
+    theSize = (unsigned int)AEGetDescDataSize(aDesc);
+
+    switch(aDesc->descriptorType)
+       {
+        case typeAlias:							//	alias record
+           {
+               Handle			theAliasHandle;
+               FSRef				theTarget;
+               Boolean			theWasChanged;
+
+               theAliasHandle = NewHandle( theSize );
+               HLock(theAliasHandle);
+               theError = AEGetDescData(aDesc, *theAliasHandle, theSize);
+               HUnlock(theAliasHandle);
+               if( theError == noErr  && FSResolveAlias( NULL, (AliasHandle)theAliasHandle, &theTarget, &theWasChanged ) == noErr )
+                  {
+                   theURL = [NSURL URLWithFSRef:&theTarget];
+                  }
+
+               DisposeHandle(theAliasHandle);
+               break;
+           }
+        case typeFileURL:					// ???		NOT IMPLEMENTED YET
+            NSLog(@"NOT IMPLEMENTED YET: Attempt to create a NSURL from 'typeFileURL' AEDesc" );
+            break;
+       }
+
+    return theURL;
+}
+
+@end
+
+@implementation NSAppleEventDescriptor (NDAEDescCreation)
++ (id)nd_appleEventDescriptorWithAEDesc:(const AEDesc *)aDesc
+{
+    return [self descriptorWithDescriptorType:aDesc->descriptorType data:[NSData nd_dataWithAEDesc:aDesc]];
+}
+
+- (BOOL)nd_getAEDesc:(AEDesc *)aDescPtr
+{
+    NSData		* theData;
+
+    theData = [self data];
+    return AECreateDesc( [self descriptorType], [theData bytes], [theData length], aDescPtr ) == noErr;
+}
+@end
Index: /releases/Pester/1.1a1/Source/NDAppleScript/NDAppleScriptObject_Protocols.h
===================================================================
--- /releases/Pester/1.1a1/Source/NDAppleScript/NDAppleScriptObject_Protocols.h	(revision 44)
+++ /releases/Pester/1.1a1/Source/NDAppleScript/NDAppleScriptObject_Protocols.h	(revision 44)
@@ -0,0 +1,20 @@
+/*
+ *  NDAppleScriptObject_Protocols.h
+ *  NDAppleScriptObjectProject
+ *
+ *  Created by Nathan Day on Sat Feb 16 2002.
+ *  Copyright (c) 2001 Nathan Day. All rights reserved.
+ *
+ */
+
+#import <Foundation/Foundation.h>
+#import <Carbon/Carbon.h>
+
+@protocol NDAppleScriptObjectSendEvent <NSObject>
+- (NSAppleEventDescriptor *)sendAppleEvent:(NSAppleEventDescriptor *)theAppleEventDescriptor sendMode:(AESendMode)aSendMode sendPriority:(AESendPriority)aSendPriority timeOutInTicks:(long)aTimeOutInTicks idleProc:(AEIdleUPP)anIdleProc filterProc:(AEFilterUPP)aFilterProc;
+@end
+
+@protocol NDAppleScriptObjectActive <NSObject>
+- (BOOL)appleScriptActive;
+@end
+
Index: /releases/Pester/1.1a1/Source/NDAppleScript/NDResourceFork.h
===================================================================
--- /releases/Pester/1.1a1/Source/NDAppleScript/NDResourceFork.h	(revision 44)
+++ /releases/Pester/1.1a1/Source/NDAppleScript/NDResourceFork.h	(revision 44)
@@ -0,0 +1,32 @@
+/*
+ *  NDResourceFork.h
+ *  AppleScriptObjectProject
+ *
+ *  Created by nathan on Wed Dec 05 2001.
+ *  Copyright (c) 2001 Nathan Day. All rights reserved.
+ */
+
+#import <Foundation/Foundation.h>
+#import <Carbon/Carbon.h>
+
+@interface NDResourceFork : NSObject
+{
+    short		fileReference;
+}
+
++ (id)resourceForkForReadingAtURL:(NSURL *)aURL;
++ (id)resourceForkForWritingAtURL:(NSURL *)aURL;
++ (id)resourceForkForReadingAtPath:(NSString *)aPath;
++ (id)resourceForkForWritingAtPath:(NSString *)aPath;
+
+- (id)initForReadingAtURL:(NSURL *)aURL;
+- (id)initForWritingAtURL:(NSURL *)aURL;
+- (id)initForReadingAtPath:(NSString *)aPath;
+- (id)initForWritingAtPath:(NSString *)aPath;
+- (id)initForPermission:(char)aPermission AtURL:(NSURL *)aURL;
+
+- (BOOL)addData:(NSData *)aData type:(ResType)aType Id:(short)anID name:(NSString *)aName;
+- (NSData *)dataForType:(ResType)aType Id:(short)anID;
+- (BOOL)removeType:(ResType)aType Id:(short)anID;
+
+@end
Index: /releases/Pester/1.1a1/Source/NDAppleScript/NDResourceFork.m
===================================================================
--- /releases/Pester/1.1a1/Source/NDAppleScript/NDResourceFork.m	(revision 44)
+++ /releases/Pester/1.1a1/Source/NDAppleScript/NDResourceFork.m	(revision 44)
@@ -0,0 +1,222 @@
+/*
+ *  NDResourceFork.m
+ *
+ *  Created by nathan on Wed Dec 05 2001.
+ *  Copyright (c) 2001 Nathan Day. All rights reserved.
+ *
+ *	Currently ResourceFork will not add resource forks to files
+ *	or create new files with resource forks
+ *
+ */
+
+#import "NDResourceFork.h"
+#import "NSURL+NDCarbonUtilities.h"
+
+@implementation NDResourceFork
+
+/*
+ * resourceForkForReadingAtURL:
+ */
++ (id)resourceForkForReadingAtURL:(NSURL *)aURL
+{
+    return [[[self alloc] initForReadingAtURL:aURL] autorelease];
+}
+
+/*
+ * resourceForkForWritingAtURL:
+ */
++ (id)resourceForkForWritingAtURL:(NSURL *)aURL
+{
+    return [[[self alloc] initForWritingAtURL:aURL] autorelease];
+}
+
+/*
+ * resourceForkForReadingAtPath:
+ */
++ (id)resourceForkForReadingAtPath:(NSString *)aPath
+{
+    return [[[self alloc] initForReadingAtPath:aPath] autorelease];
+}
+
+/*
+ * resourceForkForWritingAtPath:
+ */
++ (id)resourceForkForWritingAtPath:(NSString *)aPath
+{
+    return [[[self alloc] initForWritingAtPath:aPath] autorelease];
+}
+
+/*
+ * initForReadingAtURL:
+ */
+- (id)initForReadingAtURL:(NSURL *)aURL
+{
+    return [self initForPermission:fsRdPerm AtURL:aURL];
+}
+
+/*
+ * initForWritingAtURL:
+ */
+- (id)initForWritingAtURL:(NSURL *)aURL
+{
+    return [self initForPermission:fsWrPerm AtURL:aURL];
+}
+
+/*
+ * initForPermission:AtURL:
+ */
+- (id)initForPermission:(char)aPermission AtURL:(NSURL *)aURL
+{
+    OSErr			theError = noErr;
+    FSRef			theFsRef;
+    FSSpec		theFSSpec;
+
+    if( ( self = [self init] ) && [aURL getFSRef:&theFsRef] )
+       {
+        if( (aPermission & 0x06) != 0 && FSGetCatalogInfo( &theFsRef, kFSCatInfoNone, NULL, NULL, &theFSSpec, NULL ) == noErr )		// if write permission and we can get a FSSpec, then create fork
+           {
+            OSType				theCreatorCode,
+            theFileTypeCode;
+            NSDictionary		* theFileAttributes;
+
+            /*
+             * we don't want to change the file type creator codes
+             */
+            theFileAttributes = [[NSFileManager defaultManager] fileSystemAttributesAtPath:[aURL path]];
+            theCreatorCode = [theFileAttributes fileHFSCreatorCode];
+            theFileTypeCode = [theFileAttributes fileHFSTypeCode];
+
+            FSpCreateResFile( &theFSSpec, theCreatorCode, theFileTypeCode, smRoman );		// doesn't replace fork if already exists
+
+            theError = ResError( );
+           }
+
+
+        if( theError == noErr || theError == dupFNErr )
+           {
+            fileReference = FSOpenResFile ( &theFsRef, aPermission );
+
+            theError = fileReference > 0 ? ResError( ) : !noErr;
+           }
+       }
+
+    if( noErr != theError )
+       {
+        [self release];
+        self = nil;
+       }
+
+    return self;
+}
+
+/*
+ * initForReadingAtPath:
+ */
+- (id)initForReadingAtPath:(NSString *)aPath
+{
+    if( [[NSFileManager defaultManager] fileExistsAtPath:aPath] )
+        return [self initForPermission:fsRdPerm AtURL:[NSURL fileURLWithPath:aPath]];
+    else
+        return nil;
+}
+
+/*
+ * initForWritingAtPath:
+ */
+- (id)initForWritingAtPath:(NSString *)aPath
+{
+    return [self initForPermission:fsWrPerm AtURL:[NSURL fileURLWithPath:aPath]];
+}
+
+/*
+ * dealloc
+ */
+- (void)dealloc
+{
+    CloseResFile( fileReference );
+}
+
+- (BOOL)addData:(NSData *)aData type:(ResType)aType Id:(short)anID name:(NSString *)aName
+{
+    Handle		theResHandle;
+
+    if( [self removeType:aType Id:anID] )
+       {
+        short			thePreviousRefNum;
+
+        thePreviousRefNum = CurResFile();	// save current resource
+        UseResFile( fileReference );    			// set this resource to be current
+
+        // copy NSData's bytes to a handle
+        if ( noErr == PtrToHand ( [aData bytes], &theResHandle, [aData length] ) )
+           {
+            Str255			thePName;
+
+            CopyCStringToPascal ( [aName lossyCString], thePName );
+
+            HLock( theResHandle );
+            AddResource( theResHandle, aType, anID, thePName );
+            HUnlock( theResHandle );
+
+            UseResFile( thePreviousRefNum );     		// reset back to resource previously set
+
+            //			DisposeHandle( theResHandle );
+            return ( ResError( ) == noErr );
+           }
+       }
+
+    return NO;
+}
+
+/*
+ * dataForType:Id:
+ */
+- (NSData *)dataForType:(ResType)aType Id:(short)anID
+{
+    NSData		* theData = nil;
+    Handle		theResHandle;
+    short			thePreviousRefNum;
+
+    thePreviousRefNum = CurResFile();	// save current resource
+
+    UseResFile( fileReference );    		// set this resource to be current
+
+    theResHandle = Get1Resource( aType, anID );
+
+    if ( noErr == ResError( ) && theResHandle != NULL )
+       {
+        HLock(theResHandle);
+        theData = [NSData dataWithBytes:*theResHandle length:GetHandleSize( theResHandle )];
+        HUnlock(theResHandle);
+       }
+
+    if ( theResHandle )
+        ReleaseResource( theResHandle );
+
+    UseResFile( thePreviousRefNum );     		// reset back to resource previously set
+
+    return theData;
+}
+
+/*
+ * removeType: Id:
+ */
+- (BOOL)removeType:(ResType)aType Id:(short)anID
+{
+    Handle		theResHandle;
+    OSErr			theError;
+
+    UseResFile( fileReference );    			// set this resource to be current
+
+    theResHandle = Get1Resource( aType, anID );
+    theError = ResError( );
+    if( theResHandle != NULL && theError == noErr )
+       {
+        RemoveResource( theResHandle );			// Disposed of in current resource file
+        theError = ResError( );
+       }
+
+    return theError == noErr;
+}
+
+@end
Index: /releases/Pester/1.1a1/Source/NDAppleScript/NSAppleEventDescriptor+NDAppleScriptObject.h
===================================================================
--- /releases/Pester/1.1a1/Source/NDAppleScript/NSAppleEventDescriptor+NDAppleScriptObject.h	(revision 44)
+++ /releases/Pester/1.1a1/Source/NDAppleScript/NSAppleEventDescriptor+NDAppleScriptObject.h	(revision 44)
@@ -0,0 +1,35 @@
+/*
+ *  NSAppleEventDescriptor+NDAppleScriptObject.h
+ *  AppleScriptObjectProject
+ *
+ *  Created by Nathan Day on Fri Dec 14 2001.
+ *  Copyright (c) 2001 Nathan Day. All rights reserved.
+ */
+
+#import <Foundation/Foundation.h>
+#import <Carbon/Carbon.h>
+
+@interface NSAppleEventDescriptor (NDAppleScriptObject)
+
++ (NSAppleEventDescriptor *)appleEventDescriptorWithString:(NSString *)aString;
+
++ (NSAppleEventDescriptor *)aliasListDescriptorWithArray:(NSArray *)aArray;
+
++ (NSAppleEventDescriptor *)appleEventDescriptorWithURL:(NSURL *)aURL;
++ (NSAppleEventDescriptor *)aliasDescriptorWithURL:(NSURL *)aURL;
+
++ (NSAppleEventDescriptor *)appleEventDescriptorWithBOOL:(BOOL)aValue;
++ (NSAppleEventDescriptor *)trueBoolDescriptor;
++ (NSAppleEventDescriptor *)falseBoolDescriptor;
++ (NSAppleEventDescriptor *)appleEventDescriptorWithShort:(short int)aValue;
++ (NSAppleEventDescriptor *)appleEventDescriptorWithLong:(long int)aValue;
++ (NSAppleEventDescriptor *)appleEventDescriptorWithInt:(int)aValue;
++ (NSAppleEventDescriptor *)appleEventDescriptorWithFloat:(float)aValue;
++ (NSAppleEventDescriptor *)appleEventDescriptorWithDouble:(double)aValue;
++ (NSAppleEventDescriptor *)appleEventDescriptorWithUnsignedInt:(unsigned int)aValue;
+
+- (ProcessSerialNumber)targetProcessSerialNumber;
+- (OSType)targetCreator;
+- (BOOL)isTargetCurrentProcess;
+
+@end
Index: /releases/Pester/1.1a1/Source/NDAppleScript/NSAppleEventDescriptor+NDAppleScriptObject.m
===================================================================
--- /releases/Pester/1.1a1/Source/NDAppleScript/NSAppleEventDescriptor+NDAppleScriptObject.m	(revision 44)
+++ /releases/Pester/1.1a1/Source/NDAppleScript/NSAppleEventDescriptor+NDAppleScriptObject.m	(revision 44)
@@ -0,0 +1,179 @@
+/*
+ *  NSAppleEventDescriptor+NDAppleScriptObject.m
+ *  AppleScriptObjectProject
+ *
+ *  Created by Nathan Day on Fri Dec 14 2001.
+ *  Copyright (c) 2001 Nathan Day. All rights reserved.
+ */
+
+#import "NSAppleEventDescriptor+NDAppleScriptObject.h"
+#import "NSURL+NDCarbonUtilities.h"
+
+@implementation NSAppleEventDescriptor (NDAppleScriptObject)
+
+/*
+ * + appleEventDescriptorWithString:
+ */
+
++ (NSAppleEventDescriptor *)appleEventDescriptorWithString:(NSString *)aString
+{
+    return [self descriptorWithDescriptorType:typeChar data:[aString dataUsingEncoding:NSMacOSRomanStringEncoding]];
+}
+
+/*
+ * + aliasListDescriptorWithArray:
+ */
++ (NSAppleEventDescriptor *)aliasListDescriptorWithArray:(NSArray *)aArray
+{
+    NSAppleEventDescriptor	* theEventList = nil;
+    unsigned int				theIndex,
+        theNumOfParam;
+
+    theNumOfParam = [aArray count];
+
+    if( theNumOfParam > 0)
+       {
+        theEventList = [self listDescriptor];
+
+        for( theIndex = 0; theIndex < theNumOfParam; theIndex++ )
+           {
+            id				theObject;
+            theObject = [aArray objectAtIndex:theIndex];
+
+            if( [theObject isKindOfClass:[NSString class]] )
+                theObject = [NSURL fileURLWithPath:theObject];
+
+            [theEventList insertDescriptor:[self aliasDescriptorWithURL:theObject] atIndex:theIndex+1];
+           }
+       }
+
+    return theEventList;
+}
+
+/*
+ * + appleEventDescriptorWithURL:
+ */
++ (NSAppleEventDescriptor *)appleEventDescriptorWithURL:(NSURL *)aURL
+{
+    return [self descriptorWithDescriptorType:typeFileURL data:[NSData dataWithBytes:(void *)aURL length:sizeof(NSURL)]];
+}
+
+/*
+ * + aliasDescriptorWithURL:
+ */
++ (NSAppleEventDescriptor *)aliasDescriptorWithURL:(NSURL *)aURL
+{
+    AliasHandle						theAliasHandle;
+    FSRef								theReference;
+    NSAppleEventDescriptor		* theAppleEventDescriptor = nil;
+
+    if( [aURL getFSRef:&theReference] == YES && FSNewAliasMinimal( &theReference, &theAliasHandle ) == noErr )
+       {
+        HLock((Handle)theAliasHandle);
+        theAppleEventDescriptor = [self descriptorWithDescriptorType:typeAlias data:[NSData dataWithBytes:*theAliasHandle length:GetHandleSize((Handle) theAliasHandle)]];
+        HUnlock((Handle)theAliasHandle);
+        DisposeHandle((Handle)theAliasHandle);
+       }
+
+    return theAppleEventDescriptor;
+}
+
+// typeBoolean
++ (NSAppleEventDescriptor *)appleEventDescriptorWithBOOL:(BOOL)aValue
+{
+    return [self descriptorWithDescriptorType:typeBoolean data:[NSData dataWithBytes:&aValue length: sizeof(aValue)]];
+}
+// typeTrue
++ (NSAppleEventDescriptor *)trueBoolDescriptor
+{
+    return [self descriptorWithDescriptorType:typeTrue data:[NSData data]];
+}
+// typeFalse
++ (NSAppleEventDescriptor *)falseBoolDescriptor
+{
+    return [self descriptorWithDescriptorType:typeFalse data:[NSData data]];
+}
+// typeShortInteger
++ (NSAppleEventDescriptor *)appleEventDescriptorWithShort:(short int)aValue
+{
+    return [self descriptorWithDescriptorType:typeShortInteger data:[NSData dataWithBytes:&aValue length: sizeof(aValue)]];
+}
+// typeLongInteger
++ (NSAppleEventDescriptor *)appleEventDescriptorWithLong:(long int)aValue
+{
+    return [self descriptorWithDescriptorType:typeLongInteger data:[NSData dataWithBytes:&aValue length: sizeof(aValue)]];
+}
+// typeInteger
++ (NSAppleEventDescriptor *)appleEventDescriptorWithInt:(int)aValue
+{
+    return [self descriptorWithDescriptorType:typeInteger data:[NSData dataWithBytes:&aValue length: sizeof(aValue)]];
+}
+// typeShortFloat
++ (NSAppleEventDescriptor *)appleEventDescriptorWithFloat:(float)aValue
+{
+    return [self descriptorWithDescriptorType:typeShortFloat data:[NSData dataWithBytes:&aValue length: sizeof(aValue)]];
+}
+// typeLongFloat
++ (NSAppleEventDescriptor *)appleEventDescriptorWithDouble:(double)aValue
+{
+    return [self descriptorWithDescriptorType:typeLongFloat data:[NSData dataWithBytes:&aValue length: sizeof(aValue)]];
+}
+// typeMagnitude
++ (NSAppleEventDescriptor *)appleEventDescriptorWithUnsignedInt:(unsigned int)aValue
+{
+    return [self descriptorWithDescriptorType:typeMagnitude data:[NSData dataWithBytes:&aValue length: sizeof(aValue)]];
+}
+
+/*
+ * targetProcessSerialNumber
+ */
+- (ProcessSerialNumber)targetProcessSerialNumber
+{
+    NSAppleEventDescriptor	* theTarget;
+    ProcessSerialNumber		theProcessSerialNumber = { 0, 0 };
+
+    theTarget = [self attributeDescriptorForKeyword:keyAddressAttr];
+
+    if( theTarget )
+       {
+        if( [theTarget descriptorType] != typeProcessSerialNumber )
+            theTarget = [theTarget coerceToDescriptorType:typeProcessSerialNumber];
+
+        [[theTarget data] getBytes:&theProcessSerialNumber length:sizeof(ProcessSerialNumber)];
+       }
+    return theProcessSerialNumber;
+}
+
+/*
+ * targetCreator
+ */
+- (OSType)targetCreator
+{
+    NSAppleEventDescriptor	* theTarget;
+    OSType						theCreator = 0;
+
+    theTarget = [self attributeDescriptorForKeyword:keyAddressAttr];
+
+    if( theTarget )
+       {
+        if( [theTarget descriptorType] != typeApplSignature )
+            theTarget = [theTarget coerceToDescriptorType:typeApplSignature];
+
+        [[theTarget data] getBytes:&theCreator length:sizeof(OSType)];
+       }
+    return theCreator;
+}
+
+/*
+ * isTargetCurrentProcess
+ */
+- (BOOL)isTargetCurrentProcess
+{
+    ProcessSerialNumber		theProcessSerialNumber;
+
+    theProcessSerialNumber = [self targetProcessSerialNumber];
+
+    return theProcessSerialNumber.highLongOfPSN == 0 && theProcessSerialNumber.lowLongOfPSN == kCurrentProcess;
+}
+
+@end
Index: /releases/Pester/1.1a1/Source/NDAppleScript/NSURL+NDCarbonUtilities.h
===================================================================
--- /releases/Pester/1.1a1/Source/NDAppleScript/NSURL+NDCarbonUtilities.h	(revision 44)
+++ /releases/Pester/1.1a1/Source/NDAppleScript/NSURL+NDCarbonUtilities.h	(revision 44)
@@ -0,0 +1,17 @@
+/*
+ *  NSURL+NDCarbonUtilities.h category
+ *  AppleScriptObjectProject
+ *
+ *  Created by nathan on Wed Dec 05 2001.
+ *  Copyright (c) 2001 __CompanyName__. All rights reserved.
+ */
+
+#import <Foundation/Foundation.h>
+#import <Carbon/Carbon.h>
+
+@interface NSURL (NDCarbonUtilities)
++ (NSURL *)URLWithFSRef:(const FSRef *)aFsRef;
+- (NSURL *)URLByDeletingLastPathComponent;
+- (BOOL)getFSRef:(FSRef *)aFsRef;
+- (NSString *)fileSystemPathHFSStyle;
+@end
Index: /releases/Pester/1.1a1/Source/NDAppleScript/NSURL+NDCarbonUtilities.m
===================================================================
--- /releases/Pester/1.1a1/Source/NDAppleScript/NSURL+NDCarbonUtilities.m	(revision 44)
+++ /releases/Pester/1.1a1/Source/NDAppleScript/NSURL+NDCarbonUtilities.m	(revision 44)
@@ -0,0 +1,33 @@
+/*
+ *  NSURL+NDCarbonUtilities.m category
+ *  AppleScriptObjectProject
+ *
+ *  Created by nathan on Wed Dec 05 2001.
+ *  Copyright (c) 2001 __CompanyName__. All rights reserved.
+ */
+
+#import "NSURL+NDCarbonUtilities.h"
+
+@implementation NSURL (NDCarbonUtilities)
+
++ (NSURL *)URLWithFSRef:(const FSRef *)aFsRef
+{
+    return [(NSURL *)CFURLCreateFromFSRef( kCFAllocatorDefault, aFsRef ) autorelease];
+}
+
+- (NSURL *)URLByDeletingLastPathComponent
+{
+    return [(NSURL *)CFURLCreateCopyDeletingLastPathComponent( kCFAllocatorDefault, (CFURLRef)self) autorelease];
+}
+
+- (BOOL)getFSRef:(FSRef *)aFsRef
+{
+    return CFURLGetFSRef( (CFURLRef)self, aFsRef );
+}
+
+- (NSString *)fileSystemPathHFSStyle
+{
+    return [(NSString *)CFURLCopyFileSystemPath((CFURLRef)self, kCFURLHFSPathStyle) autorelease];
+}
+
+@end
Index: /releases/Pester/1.1a1/Source/NJRDateFormatter.h
===================================================================
--- /releases/Pester/1.1a1/Source/NJRDateFormatter.h	(revision 44)
+++ /releases/Pester/1.1a1/Source/NJRDateFormatter.h	(revision 44)
@@ -0,0 +1,21 @@
+//
+//  NJRDateFormatter.h
+//  Pester
+//
+//  Created by Nicholas Riley on Wed Oct 09 2002.
+//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+
+@interface NJRDateFormatter : NSDateFormatter {
+    NSDictionary *alteredLocale;
+}
+
++ (NSString *)format:(NSString *)format withoutComponent:(unichar)component;
++ (NSString *)localizedDateFormatIncludingWeekday:(BOOL)weekday;
++ (NSString *)localizedShortDateFormatIncludingWeekday:(BOOL)weekday;
++ (NSString *)localizedTimeFormatIncludingSeconds:(BOOL)seconds;
+
+@end
Index: /releases/Pester/1.1a1/Source/NJRDateFormatter.m
===================================================================
--- /releases/Pester/1.1a1/Source/NJRDateFormatter.m	(revision 44)
+++ /releases/Pester/1.1a1/Source/NJRDateFormatter.m	(revision 44)
@@ -0,0 +1,175 @@
+//
+//  NJRDateFormatter.m
+//  Pester
+//
+//  Created by Nicholas Riley on Wed Oct 09 2002.
+//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
+//
+
+#import "NJRDateFormatter.h"
+
+NSUserDefaults *locale;
+
+@implementation NJRDateFormatter
+
++ (void)initialize;
+{
+    locale = [[NSUserDefaults standardUserDefaults] retain];
+}
+
++ (NSString *)format:(NSString *)format withoutComponent:(unichar)component;
+{
+    NSScanner *scanner = [NSScanner scannerWithString: format];
+    NSRange range;
+    [scanner setCharactersToBeSkipped: [NSCharacterSet characterSetWithCharactersInString: @""]];
+    while ([scanner scanUpToString: @"%" intoString: nil] || ![scanner isAtEnd]) {
+        range.location = [scanner scanLocation];
+        [scanner scanUpToCharactersFromSet: [NSCharacterSet letterCharacterSet] intoString: nil];
+        if ([format characterAtIndex: [scanner scanLocation]] == component) {
+            if ([scanner scanUpToString: @"%" intoString: nil] && ![scanner isAtEnd]) {
+                NSMutableString *mutableFormat = [format mutableCopy];
+                if (range.location != 0 && [[NSCharacterSet punctuationCharacterSet] characterIsMember: [format characterAtIndex: range.location - 1]]) {
+                    range.location--; // "%I:%M:%S%p" -> "%I:%M%p"
+                }
+                range.length = [scanner scanLocation] - range.location;
+                [mutableFormat deleteCharactersInRange: range];
+                format = [mutableFormat copy];
+                [mutableFormat release];
+                return [format autorelease];
+            } else {
+                range = [format rangeOfCharacterFromSet: [NSCharacterSet letterCharacterSet] options: NSBackwardsSearch range: NSMakeRange(0, range.location)];
+                return [format substringToIndex: NSMaxRange(range)];
+            }
+        }
+    }
+    return format;
+}
+
++ (NSString *)localizedDateFormatIncludingWeekday:(BOOL)weekday;
+{
+    NSString *format = [locale stringForKey: NSDateFormatString];
+    if (weekday) return format;
+    return [self format: format withoutComponent: (unichar)'A'];
+}
+
++ (NSString *)localizedShortDateFormatIncludingWeekday:(BOOL)weekday;
+{
+    NSString *format = [locale stringForKey: NSShortDateFormatString];
+    if (weekday) return format;
+    return [self format: format withoutComponent: (unichar)'A'];
+}
+
+NSString *stringByInsertingStringAtLocation(NSString *string, NSString *insert, int location) {
+    return [NSString stringWithFormat: @"%@%@%@", [string substringToIndex: location], insert,
+        [string substringFromIndex: location]];
+}
+
++ (NSString *)localizedTimeFormatIncludingSeconds:(BOOL)seconds;
+{
+    NSString *format = [locale stringForKey: NSTimeFormatString];
+    NSArray *ampm = [locale arrayForKey: NSAMPMDesignation];
+    NSString *am = [ampm objectAtIndex: 0], *pm = [ampm objectAtIndex: 1];
+    // work around bug with inconsistent AM/PM and time format
+    if ([am isEqualToString: @""] && [pm isEqualToString: @""])
+        format = [self format: format withoutComponent: 'p'];
+    else {
+        NSRange ampmComponentRange = [format rangeOfString: @"%p"];
+        NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
+        BOOL needSpaceInFormatString = ![whitespace characterIsMember: [am characterAtIndex: 0]];
+        if (ampmComponentRange.location == NSNotFound) // "%1I:%M:%S" -> "%1I:%M:%S%p", "%1I:%M:%S %p"
+            format = [format stringByAppendingString: (needSpaceInFormatString ? @" %p" : @"%p")];
+        else {
+            NSRange whitespaceRange = [format rangeOfCharacterFromSet: whitespace options: NSBackwardsSearch range: NSMakeRange(0, ampmComponentRange.location)];
+            if (whitespaceRange.location == NSNotFound) {
+                if (needSpaceInFormatString) // "%1I:%M:%S%p" -> "%1I:%M:%S %p"
+                    format = stringByInsertingStringAtLocation(format, @" ", ampmComponentRange.location);
+                // else "%1I:%M:%S%p" -> no change
+            } else {
+                if (NSMaxRange(whitespaceRange) == ampmComponentRange.location) { 
+                    if (!needSpaceInFormatString) // "%1I:%M:%S %p" -> "%1I:%M:%S%p"
+                        format = [[format substringToIndex: whitespaceRange.location] stringByAppendingString: [format substringFromIndex: ampmComponentRange.location]];
+                    // else "%1I:%M:%S %p" -> no change
+                } else {
+                    if (needSpaceInFormatString)
+                        format = stringByInsertingStringAtLocation(format, @" ", ampmComponentRange.location);
+                    // else "%1I %M:%S%p" -> no change
+                }
+            }
+        }
+    }
+    if (seconds) return format;
+    return [self format: format withoutComponent: 'S'];
+}
+
+// workaround for bug in Jaguar (and earlier?) NSCalendarDate dateWithNaturalLanguageString:
+NSString * stringByRemovingSurroundingWhitespace(NSString *string) {
+    static NSCharacterSet *nonWhitespace = nil;
+    NSRange firstValidCharacter, lastValidCharacter;
+
+    if (!nonWhitespace) {
+        nonWhitespace = [[[NSCharacterSet characterSetWithCharactersInString:
+            @" \t\r\n"] invertedSet] retain];
+    }
+
+    firstValidCharacter = [string rangeOfCharacterFromSet:nonWhitespace];
+    if (firstValidCharacter.length == 0)
+        return @"";
+    lastValidCharacter = [string rangeOfCharacterFromSet:nonWhitespace options: NSBackwardsSearch];
+
+    if (firstValidCharacter.location == 0 && lastValidCharacter.location == [string length] - 1)
+        return string;
+    else
+        return [string substringWithRange: NSUnionRange(firstValidCharacter, lastValidCharacter)];
+}
+
+- (id)initWithDateFormat:(NSString *)format allowNaturalLanguage:(BOOL)flag;
+{
+    if ( (self = [super initWithDateFormat: format allowNaturalLanguage: flag]) != nil) {
+        NSRange ampmRange = [format rangeOfString: @"%p"];
+        NSArray *ampm = [locale arrayForKey: NSAMPMDesignation];
+        NSString *am = [ampm objectAtIndex: 0], *pm = [ampm objectAtIndex: 1];
+        if (flag && ampmRange.location != NSNotFound &&
+            [[locale stringForKey: NSTimeFormatString] rangeOfString: @"%p"].location == NSNotFound && ![am isEqualToString: pm]) {
+            // workaround for bug in NSCalendarDate dateWithNaturalLanguageString: discarding AM/PM value when AM/PM designations have spaces in them (of which the use thereof is a workaround for NSDateFormatter discarding the AM/PM value)
+            NSMutableString *paddedFormat = [format mutableCopy];
+            [paddedFormat replaceCharactersInRange: ampmRange withString: @" %p"];
+            alteredLocale = [[locale dictionaryRepresentation] mutableCopy];
+            [(NSMutableDictionary *)alteredLocale setObject: paddedFormat forKey: NSTimeFormatString];
+            [(NSMutableDictionary *)alteredLocale setObject:
+                [NSArray arrayWithObjects: stringByRemovingSurroundingWhitespace(am),
+                    stringByRemovingSurroundingWhitespace(pm)] forKey: NSAMPMDesignation];
+            [paddedFormat release];
+        } else {
+            alteredLocale = [(NSDictionary *)locale retain];
+        }
+    }
+    return self;
+}
+
+- (void)dealloc;
+{
+    [alteredLocale release];
+    [super dealloc];
+}
+
+- (BOOL)getObjectValue:(id *)anObject forString:(NSString *)string errorDescription:(NSString **)error
+{
+    NSCalendarDate *date;
+    if (![self allowsNaturalLanguage])
+        return [super getObjectValue: anObject forString: string errorDescription: error];
+    if (string == nil) return nil;
+    NS_DURING // dateWithNaturalLanguageString: can throw an exception
+        date = [NSCalendarDate dateWithNaturalLanguageString: stringByRemovingSurroundingWhitespace(string) locale: alteredLocale];
+        // NSLog(@"%@: natural language date is %@", string, date);
+    NS_HANDLER
+        if (error != nil) *error = [localException reason];
+        NS_VALUERETURN(NO, BOOL);
+    NS_ENDHANDLER
+    // [super getObjectValue: anObject forString: string errorDescription: error];
+    // NSLog(@"%@: formatter date is %@", string, anObject == nil ? @"(null)" : *anObject);
+    if (date == nil) return [super getObjectValue: anObject forString: string errorDescription: error];
+    *anObject = date;
+    return YES;
+}
+
+@end
Index: /releases/Pester/1.1a1/Source/NJRFSObjectSelector.h
===================================================================
--- /releases/Pester/1.1a1/Source/NJRFSObjectSelector.h	(revision 44)
+++ /releases/Pester/1.1a1/Source/NJRFSObjectSelector.h	(revision 44)
@@ -0,0 +1,27 @@
+/* NJRFSObjectSelector */
+
+#import <Cocoa/Cocoa.h>
+#import "BDAlias.h"
+
+@interface NJRFSObjectSelector : NSButton
+{
+    BDAlias *selectedAlias;
+    NSArray *fileTypes;
+    BOOL canChooseDirectories;
+    BOOL canChooseFiles;
+    BOOL dragAccepted;
+    BOOL isEnabled;
+}
+- (IBAction)select:(id)sender;
+- (void)setAlias:(BDAlias *)alias;
+- (BDAlias *)alias;
+- (BOOL)acceptsPath:(NSString *)path;
+- (void)setPath:(NSString *)path; // does not validate
+- (BOOL)canChooseDirectories;
+- (BOOL)canChooseFiles;
+- (void)setCanChooseDirectories:(BOOL)flag;
+- (void)setCanChooseFiles:(BOOL)flag;
+- (NSArray *)fileTypes;
+- (void)setFileTypes:(NSArray *)types;
+
+@end
Index: /releases/Pester/1.1a1/Source/NJRFSObjectSelector.m
===================================================================
--- /releases/Pester/1.1a1/Source/NJRFSObjectSelector.m	(revision 44)
+++ /releases/Pester/1.1a1/Source/NJRFSObjectSelector.m	(revision 44)
@@ -0,0 +1,310 @@
+#import "NJRFSObjectSelector.h"
+#import "NSImage-NJRExtensions.h"
+#include <Carbon/Carbon.h>
+
+@implementation NJRFSObjectSelector
+
+- (void)_initSelector;
+{
+    canChooseFiles = YES; canChooseDirectories = NO;
+    [self setAlias: nil];
+    [[self cell] setHighlightsBy: NSChangeBackgroundCell];
+    [[self cell] setGradientType: NSGradientNone];
+    [self registerForDraggedTypes:
+        [NSArray arrayWithObjects: NSFilenamesPboardType, NSURLPboardType, nil]];
+}
+
+- (void)dealloc;
+{
+    [selectedAlias release];
+    [fileTypes release];
+    [super dealloc];
+}
+
+- (id)initWithCoder:(NSCoder *)coder;
+{
+    if ( (self = [super initWithCoder: coder]) != nil) {
+        [self _initSelector];
+    }
+    return self;
+}
+
+- (id)initWithFrame:(NSRect)frame;
+{
+    if ( (self = [super initWithFrame: frame]) != nil) {
+        [self _initSelector];
+    }
+    return self;
+}
+
+- (void)drawRect:(NSRect)rect;
+{
+    NSRect boundsRect = [self bounds];
+    [super drawRect: rect];
+    if (dragAccepted) {
+        [[NSColor selectedControlColor] set];
+        [NSBezierPath setDefaultLineWidth: 2];
+        [NSBezierPath strokeRect: NSInsetRect(boundsRect, 2, 2)];
+    } else if (selectedAlias != nil && [self isEnabled]) {
+        static NSImage *popupTriangle = nil;
+        static NSSize imageSize;
+        if (popupTriangle == nil) {
+            popupTriangle = [[NSImage imageNamed: @"Popup triangle"] retain];
+            imageSize = [popupTriangle size];
+        }
+        // equivalent to popup triangle location for large bezel in Carbon
+        [popupTriangle compositeToPoint: NSMakePoint(NSMaxX(boundsRect) - imageSize.width - 5, NSMaxY(boundsRect) - 5) operation: NSCompositeSourceOver];
+    }
+}
+
+- (BOOL)acceptsPath:(NSString *)path;
+{
+    NSFileManager *fm = [NSFileManager defaultManager];
+    BOOL isDir;
+
+    if (![fm fileExistsAtPath: path isDirectory: &isDir]) return NO;
+
+    if (isDir) return canChooseDirectories;
+    if (canChooseFiles) {
+        NSEnumerator *e = [fileTypes objectEnumerator];
+        NSString *extension = [path pathExtension];
+        NSString *hfsType = NSHFSTypeOfFile(path);
+        NSString *fileType;
+        
+        while ( (fileType = [e nextObject]) != nil) {
+            if ([fileType isEqualToString: extension] || [fileType isEqualToString: hfsType])
+                return YES;
+        }
+    }
+    return NO;
+}
+
+- (void)setImage:(NSImage *)image;
+{
+    [super setImage: [image bestFitImageForSize: [[self cell] cellSize]]];
+}
+
+- (IBAction)select:(id)sender;
+{
+    NSOpenPanel *openPanel = [NSOpenPanel openPanel];
+    NSString *path = [selectedAlias fullPath];
+    [openPanel setAllowsMultipleSelection: NO];
+    [openPanel setCanChooseDirectories: canChooseDirectories];
+    [openPanel setCanChooseFiles: canChooseFiles];
+    [openPanel beginSheetForDirectory: [path stringByDeletingLastPathComponent]
+                                 file: [path lastPathComponent]
+                                types: fileTypes
+                       modalForWindow: [self window]
+                        modalDelegate: self
+                       didEndSelector: @selector(openPanelDidEnd:returnCode:contextInfo:)
+                          contextInfo: nil];
+}
+
+- (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo;
+{
+    [sheet close];
+
+    if (returnCode == NSOKButton) {
+        NSArray *files = [sheet filenames];
+        NSAssert1([files count] == 1, @"%d items returned, only one expected", [files count]);
+        [self setPath: [files objectAtIndex: 0]];
+        [[self target] tryToPerform: [self action] with: self];
+    }
+}
+
+- (void)revealInFinder:(id<NSMenuItem>)sender;
+{
+    NSString *path = [sender representedObject];
+    if (path == nil) return;
+    [[NSWorkspace sharedWorkspace] selectFile: path inFileViewerRootedAtPath: @""];
+}
+
+- (void)setAlias:(BDAlias *)alias;
+{
+    if (selectedAlias != alias) {
+        [selectedAlias release];
+        selectedAlias = [alias retain];
+    }
+
+    if (alias != nil) { // alias is set
+        NSString *path = [alias fullPath];
+        NSString *revealPath = nil;
+        NSString *targetName = [path lastPathComponent];
+        NSMenu *menu = [[NSMenu alloc] initWithTitle: @""];
+        NSFileManager *fmgr = [NSFileManager defaultManager];
+        NSMenuItem *item;
+        if (path != nil) { // can resolve alias
+            [self setImage: [[NSWorkspace sharedWorkspace] iconForFile: path]];
+            [self setTitle: targetName];
+            do {
+                item = [menu addItemWithTitle: [fmgr displayNameAtPath: path]
+                                       action: @selector(revealInFinder:)
+                                keyEquivalent: @""];
+                [item setTarget: self];
+                [item setRepresentedObject: revealPath];
+                [item setImage:
+                    [[[NSWorkspace sharedWorkspace] iconForFile: path] bestFitImageForSize: NSMakeSize(16, 16)]];
+                revealPath = path;
+                path = [path stringByDeletingLastPathComponent];
+                NSAssert1(![path isEqualToString: revealPath], @"Stuck on path |%@|", [alias fullPath]);
+            } while (![revealPath isEqualToString: @"/"] && ![path isEqualToString: @"/Volumes"]);
+            [[self cell] setMenu: menu];
+        } else {
+            [self setImage: nil];
+            [self setTitle: @"(not available)"];
+            [[self cell] setMenu: nil];
+        }
+    } else {
+        [self setImage: nil];
+        [self setTitle: @"(none selected)"];
+        [[self cell] setMenu: nil];
+    }
+    [self setEnabled: isEnabled];
+}
+
+- (BOOL)isEnabled;
+{
+    return isEnabled;
+}
+
+- (void)setEnabled:(BOOL)enabled;
+{
+    isEnabled = enabled;
+    [super setEnabled: enabled ? selectedAlias != nil : NO];
+}
+
+- (void)rightMouseDown:(NSEvent *)theEvent;
+{
+    [self mouseDown: theEvent];
+}
+
+- (void)otherMouseDown:(NSEvent *)theEvent;
+{
+    [self mouseDown: theEvent];
+}
+
+extern MenuRef _NSGetCarbonMenu(NSMenu *menu);
+
+- (void)mouseDown:(NSEvent *)theEvent;
+{
+    NSMenu *menu = [[self cell] menu];
+    MenuRef mRef = _NSGetCarbonMenu(menu);
+
+    if (mRef == NULL) {
+        NSMenu *appMenu = [[[NSApp mainMenu] itemWithTitle: @""] submenu];
+        if (appMenu != nil) {
+            NSMenuItem *item = [appMenu addItemWithTitle: @"" action: NULL keyEquivalent: @""];
+            [appMenu setSubmenu: menu forItem: item];
+            [appMenu removeItem: item];
+        }
+        mRef = _NSGetCarbonMenu(menu);
+    }
+
+    ChangeMenuAttributes(mRef, kMenuAttrExcludesMarkColumn, 0);
+    theEvent = [NSEvent mouseEventWithType: [theEvent type]
+                                  location: [self convertPoint: NSMakePoint(-1, 1) toView: nil]
+                             modifierFlags: [theEvent modifierFlags]
+                                 timestamp: [theEvent timestamp]
+                              windowNumber: [theEvent windowNumber]
+                                   context: [theEvent context]
+                               eventNumber: [theEvent eventNumber]
+                                clickCount: [theEvent clickCount]
+                                  pressure: [theEvent pressure]];
+
+    // this undocumented API does not work any better; contextual menu items are still added:
+    // [menu _popUpMenuWithEvent: theEvent forView: self];
+    [NSMenu popUpContextMenu: menu withEvent: theEvent forView: self];
+}
+
+- (BDAlias *)alias;
+{
+    return selectedAlias;
+}
+
+- (void)setPath:(NSString *)path;
+{
+    [self setAlias: [BDAlias aliasWithPath: path]];
+}
+
+- (BOOL)canChooseDirectories;
+{
+    return canChooseDirectories;
+}
+
+- (BOOL)canChooseFiles;
+{
+    return canChooseFiles;
+}
+
+- (void)setCanChooseDirectories:(BOOL)flag;
+{
+    canChooseDirectories = flag;
+}
+
+- (void)setCanChooseFiles:(BOOL)flag;
+{
+    canChooseFiles = flag;
+}
+
+- (NSArray *)fileTypes;
+{
+    return fileTypes;
+}
+
+- (void)setFileTypes:(NSArray *)types;
+{
+    if (fileTypes == types) return;
+    
+    [fileTypes release];
+    fileTypes = [types retain];
+}
+
+@end
+
+
+@implementation NJRFSObjectSelector (NSDraggingDestination)
+
+- (BOOL)acceptsDragFrom:(id <NSDraggingInfo>)sender;
+{
+    NSURL *url = [NSURL URLFromPasteboard: [sender draggingPasteboard]];
+
+    if (url == nil || ![url isFileURL]) return NO;
+    return [self acceptsPath: [url path]];
+}
+
+- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender;
+{
+    if ([self acceptsDragFrom: sender] && [sender draggingSourceOperationMask] &
+        (NSDragOperationCopy | NSDragOperationLink)) {
+        dragAccepted = YES;
+        [self setNeedsDisplay: YES];
+        return NSDragOperationLink;
+    }
+    return NSDragOperationNone;
+}
+
+- (void)draggingExited:(id <NSDraggingInfo>)sender;
+{
+    dragAccepted = NO;
+    [self setNeedsDisplay: YES];
+}
+
+- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender;
+{
+    dragAccepted = NO;
+    [self setNeedsDisplay: YES];
+    return [self acceptsDragFrom: sender];
+}
+
+- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender;
+{
+    if ([sender draggingSource] != self) {
+        NSURL *url = [NSURL URLFromPasteboard: [sender draggingPasteboard]];
+        if (url == nil) return NO;
+        [self setPath: [url path]];
+        [[self target] tryToPerform: [self action] with: self];
+    }
+    return YES;
+}
+
+@end
Index: /releases/Pester/1.1a1/Source/NJRHistoryTrackingComboBox.h
===================================================================
--- /releases/Pester/1.1a1/Source/NJRHistoryTrackingComboBox.h	(revision 44)
+++ /releases/Pester/1.1a1/Source/NJRHistoryTrackingComboBox.h	(revision 44)
@@ -0,0 +1,19 @@
+//
+//  NJRHistoryTrackingComboBox.h
+//  DockCam
+//
+//  Created by Nicholas Riley on Fri Jun 28 2002.
+//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+
+
+@interface NJRHistoryTrackingComboBox : NSComboBox {
+
+}
+
+- (IBAction)removeEntry:(id)sender;
+- (IBAction)clearAllEntries:(id)sender;
+
+@end
Index: /releases/Pester/1.1a1/Source/NJRHistoryTrackingComboBox.m
===================================================================
--- /releases/Pester/1.1a1/Source/NJRHistoryTrackingComboBox.m	(revision 44)
+++ /releases/Pester/1.1a1/Source/NJRHistoryTrackingComboBox.m	(revision 44)
@@ -0,0 +1,74 @@
+//
+//  NJRHistoryTrackingComboBox.m
+//  DockCam
+//
+//  Created by Nicholas Riley on Fri Jun 28 2002.
+//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
+//
+
+#import "NJRHistoryTrackingComboBox.h"
+
+#define NJRHistoryTrackingComboBoxMaxItems 10
+
+@implementation NJRHistoryTrackingComboBox
+
+- (NSString *)_defaultKey;
+{
+    NSAssert([self tag] != 0, @"CanÕt track history for combo box with tag 0: please set a tag");
+    return [NSString stringWithFormat: @"NJRHistoryTrackingComboBox tag %d", [self tag]];
+}
+
+- (void)awakeFromNib;
+{
+    [[NSNotificationCenter defaultCenter] addObserver: self
+                                             selector: @selector(textDidEndEditing:)
+                                                 name: NSTextDidEndEditingNotification
+                                               object: self];
+    [self removeAllItems];
+    [self addItemsWithObjectValues: [[NSUserDefaults standardUserDefaults] stringArrayForKey: [self _defaultKey]]];
+    [self setItemHeight: [self itemHeight] + 2];
+}
+
+- (void)_writeHistory;
+{
+    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+    [defaults setObject: [self objectValues] forKey: [self _defaultKey]];
+    [defaults synchronize];
+}
+
+- (IBAction)removeEntry:(id)sender;
+{
+    int idx = [self indexOfSelectedItem];
+    if (idx == -1) {
+        [self selectItemWithObjectValue: [self stringValue]];
+        idx = [self indexOfSelectedItem];
+    }
+    if (idx != -1) [self removeItemAtIndex: idx];
+    [self setStringValue: @""];
+    [self _writeHistory];
+}
+
+- (IBAction)clearAllEntries:(id)sender;
+{
+    [self removeAllItems];
+    [self setStringValue: @""];
+    [self _writeHistory];
+}
+
+- (BOOL)textShouldEndEditing:(NSText *)textObject;
+{
+    NSString *newValue = [self stringValue];
+    int oldIndex = [self indexOfItemWithObjectValue: newValue];
+    [self removeItemWithObjectValue: newValue];
+    [self insertItemWithObjectValue: newValue atIndex: 0];
+    if (oldIndex == NSNotFound) {
+        int numItems = [self numberOfItems];
+        while (numItems-- > NJRHistoryTrackingComboBoxMaxItems) {
+            [self removeItemAtIndex: numItems - 1];
+        }
+    }
+    [self _writeHistory];
+    return YES;
+}
+
+@end
Index: /releases/Pester/1.1a1/Source/NJRIntegerFilter.h
===================================================================
--- /releases/Pester/1.1a1/Source/NJRIntegerFilter.h	(revision 44)
+++ /releases/Pester/1.1a1/Source/NJRIntegerFilter.h	(revision 44)
@@ -0,0 +1,16 @@
+//
+//  NJRIntegerFilter.h
+//  HostLauncher
+//
+//  Created by Nicholas Riley on Tue Dec 18 2001.
+//  Copyright (c) 2001 Nicholas Riley. All rights reserved.
+//
+
+#import <AppKit/AppKit.h>
+
+
+@interface NJRIntegerFilter : NSNumberFormatter {
+
+}
+
+@end
Index: /releases/Pester/1.1a1/Source/NJRIntegerFilter.m
===================================================================
--- /releases/Pester/1.1a1/Source/NJRIntegerFilter.m	(revision 44)
+++ /releases/Pester/1.1a1/Source/NJRIntegerFilter.m	(revision 44)
@@ -0,0 +1,35 @@
+//
+//  NJRIntegerFilter.m
+//  HostLauncher
+//
+//  Created by Nicholas Riley on Tue Dec 18 2001.
+//  Copyright (c) 2001 Nicholas Riley. All rights reserved.
+//
+
+// based on MyIntegerFilter
+// Copyright © 2001 Bill Cheeseman. All rights reserved.
+
+#import "NJRIntegerFilter.h"
+
+@implementation NJRIntegerFilter
+
+// Input filter
+
+- (BOOL)isPartialStringValid:(NSString **)partialStringPtr proposedSelectedRange:(NSRangePointer)proposedSelRangePtr originalString:(NSString *)origString originalSelectedRange:(NSRange)origSelRange errorDescription:(NSString **)error {
+
+    // Override method to enable NSControl delegate method control:didFailToValidatePartialString:errorDescription: to
+    // reject invalid keypresses. Filters out keyboard input characters other than "1" .. "9".
+    if ([[*partialStringPtr substringWithRange:NSMakeRange(origSelRange.location, (*proposedSelRangePtr).location - origSelRange.location)] rangeOfCharacterFromSet:[[NSCharacterSet decimalDigitCharacterSet] invertedSet] options:NSLiteralSearch].location != NSNotFound) {
+        *error = NSLocalizedString(@"Input is not an integer", @"Presented when user value not a numeric digit");
+        return NO; // Reject *partialStringPtr as typed, invoke delegate method
+    }
+
+    if ([*partialStringPtr length] == 0) {
+        // Work around NSFormatter issue in Mac OS X 10.0.
+        [[[NSApp keyWindow] fieldEditor:NO forObject:nil] setSelectedRange:*proposedSelRangePtr];
+    }
+
+    return YES; // Accept *partialStringPtr as typed
+}
+
+@end
Index: /releases/Pester/1.1a1/Source/NJRQTMediaPopUpButton.h
===================================================================
--- /releases/Pester/1.1a1/Source/NJRQTMediaPopUpButton.h	(revision 44)
+++ /releases/Pester/1.1a1/Source/NJRQTMediaPopUpButton.h	(revision 44)
@@ -0,0 +1,27 @@
+//
+//  NJRQTMediaPopUpButton.h
+//  Pester
+//
+//  Created by Nicholas Riley on Sat Oct 26 2002.
+//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
+//
+
+#import <AppKit/AppKit.h>
+#import "BDAlias.h"
+
+extern NSString * const NJRQTMediaPopUpButtonMovieChangedNotification;
+
+@interface NJRQTMediaPopUpButton : NSPopUpButton {
+    IBOutlet NSMovieView *preview;
+    BOOL movieCanRepeat;
+    NSMenuItem *otherItem;
+    BDAlias *selectedAlias, *previousAlias;
+    NSMutableArray *recentMediaAliasData;
+    BOOL dragAccepted;
+}
+
+- (BDAlias *)selectedAlias;
+- (BOOL)canRepeat;
+- (IBAction)stopSoundPreview:(id)sender;
+
+@end
Index: /releases/Pester/1.1a1/Source/NJRQTMediaPopUpButton.m
===================================================================
--- /releases/Pester/1.1a1/Source/NJRQTMediaPopUpButton.m	(revision 44)
+++ /releases/Pester/1.1a1/Source/NJRQTMediaPopUpButton.m	(revision 44)
@@ -0,0 +1,469 @@
+//
+//  NJRQTMediaPopUpButton.m
+//  Pester
+//
+//  Created by Nicholas Riley on Sat Oct 26 2002.
+//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
+//
+
+#import "NJRQTMediaPopUpButton.h"
+#import "SoundFileManager.h"
+#import "NSMovie-NJRExtensions.h"
+#import "NSImage-NJRExtensions.h"
+
+static const int NJRQTMediaPopUpButtonMaxRecentItems = 10;
+
+NSString * const NJRQTMediaPopUpButtonMovieChangedNotification = @"NJRQTMediaPopUpButtonMovieChangedNotification";
+
+@interface NJRQTMediaPopUpButton (Private)
+- (void)_setPath:(NSString *)path;
+- (NSMenuItem *)_itemForAlias:(BDAlias *)alias;
+- (BOOL)_validateWithPreview:(BOOL)doPreview;
+@end
+
+@implementation NJRQTMediaPopUpButton
+
+// XXX handle refreshing sound list on resume
+// XXX don't add icons on Puma, they look like ass
+// XXX launch preview on a separate thread (if movies take too long to load, they inhibit the interface responsiveness)
+// XXX when dropping invalid JPEG file on button, it dies
+
+// Recent media layout:
+// Most recent media are at TOP of menu (smaller item numbers, starting at [self indexOfItem: otherItem] + 1)
+// Most recent media are at END of array (larger indices)
+
+#pragma mark recently selected media tracking
+
+- (NSString *)_defaultKey;
+{
+    NSAssert([self tag] != 0, @"CanÕt track recently selected media for popup with tag 0: please set a tag");
+    return [NSString stringWithFormat: @"NJRQTMediaPopUpButtonMaxRecentItems tag %d", [self tag]];
+}
+
+- (void)_writeRecentMedia;
+{
+    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+    [defaults setObject: recentMediaAliasData forKey: [self _defaultKey]];
+    [defaults synchronize];
+}
+
+- (NSMenuItem *)_addRecentMediaAtPath:(NSString *)path withAlias:(BDAlias *)alias;
+{
+    NSString *title = [[NSFileManager defaultManager] displayNameAtPath: path];
+    NSMenu *menu = [self menu];
+    NSMenuItem *item = [menu insertItemWithTitle: title action: @selector(_aliasSelected:) keyEquivalent: @"" atIndex: [menu indexOfItem: otherItem] + 1];
+    [item setTarget: self];
+    [item setRepresentedObject: alias];
+    [item setImage: [[[NSWorkspace sharedWorkspace] iconForFile: path] bestFitImageForSize: NSMakeSize(16, 16)]];
+    [recentMediaAliasData addObject: [alias aliasData]];
+    if ([recentMediaAliasData count] > NJRQTMediaPopUpButtonMaxRecentItems) {
+        [menu removeItemAtIndex: [menu numberOfItems] - 1];
+        [recentMediaAliasData removeObjectAtIndex: 0];
+    }
+    return item;
+}
+
+- (void)_addRecentMediaFromAliasesData:(NSArray *)aliasesData;
+{
+    NSEnumerator *e = [aliasesData objectEnumerator];
+    NSData *aliasData;
+    BDAlias *alias;
+    while ( (aliasData = [e nextObject]) != nil) {
+        if ( (alias = [[BDAlias alloc] initWithData: aliasData]) != nil) {
+            [self _addRecentMediaAtPath: [alias fullPath] withAlias: alias];
+            [alias release];
+        }
+    }
+}
+
+- (void)_validateRecentMedia;
+{
+    NSEnumerator *e = [recentMediaAliasData reverseObjectEnumerator];
+    NSData *aliasData;
+    NSMenuItem *item;
+    BDAlias *itemAlias;
+    int otherIndex = [self indexOfItem: otherItem];
+    int aliasDataCount = [recentMediaAliasData count];
+    int lastItemIndex = [self numberOfItems] - 1;
+    int recentItemCount = lastItemIndex - otherIndex;
+    int recentItemIndex = otherIndex;
+    NSAssert2(recentItemCount == aliasDataCount, @"Counted %d recent menu items, %d of alias data", recentItemCount, aliasDataCount);
+    while ( (aliasData = [e nextObject]) != nil) { // go BACKWARD through array while going DOWN menu
+        recentItemIndex++;
+        item = [self itemAtIndex: recentItemIndex];
+        itemAlias = [item representedObject];
+        if ([itemAlias aliasDataIsEqual: aliasData])
+            NSLog(@"item %d %@: %@", recentItemIndex, [item title], [itemAlias fullPath]);
+        else
+            NSLog(@"ITEM %d %@: %@ != aliasData %@", recentItemIndex, [item title], [itemAlias fullPath], [[BDAlias aliasWithData: aliasData] fullPath]);
+    }
+}
+
+#pragma mark initialize-release
+
+- (void)awakeFromNib;
+{
+    NSMenu *menu;
+    NSMenuItem *item;
+    SoundFileManager *sfm = [SoundFileManager sharedSoundFileManager];
+    int soundCount = [sfm count];
+
+    [self removeAllItems];
+    menu = [self menu];
+    item = [menu addItemWithTitle: @"Alert sound" action: @selector(_beepSelected:) keyEquivalent: @""];
+    [item setTarget: self];
+    [menu addItem: [NSMenuItem separatorItem]];
+    if (soundCount == 0) {
+        item = [menu addItemWithTitle: @"CanÕt locate alert sounds" action: nil keyEquivalent: @""];
+        [item setEnabled: NO];
+    } else {
+        SoundFile *sf;
+        int i;
+        [sfm sortByName];
+        for (i = 0 ; i < soundCount ; i++) {
+            sf = [sfm soundFileAtIndex: i];
+            item = [menu addItemWithTitle: [sf name] action: @selector(_soundFileSelected:) keyEquivalent: @""];
+            [item setTarget: self];
+            [item setRepresentedObject: sf];
+            [item setImage: [[[NSWorkspace sharedWorkspace] iconForFile: [sf path]] bestFitImageForSize: NSMakeSize(16, 16)]];
+        }
+    }
+    [menu addItem: [NSMenuItem separatorItem]];
+    item = [menu addItemWithTitle: @"OtherÉ" action: @selector(select:) keyEquivalent: @""];
+    [item setTarget: self];
+    otherItem = [item retain];
+
+    [self _validateWithPreview: NO];
+
+    recentMediaAliasData = [[NSMutableArray alloc] initWithCapacity: NJRQTMediaPopUpButtonMaxRecentItems + 1];
+    [self _addRecentMediaFromAliasesData: [[NSUserDefaults standardUserDefaults] arrayForKey: [self _defaultKey]]];
+    // [self _validateRecentMedia];
+
+    [self registerForDraggedTypes:
+        [NSArray arrayWithObjects: NSFilenamesPboardType, NSURLPboardType, nil]];
+}
+
+- (void)dealloc;
+{
+    [recentMediaAliasData release]; recentMediaAliasData = nil;
+    [otherItem release];
+    [selectedAlias release]; [previousAlias release];
+    [super dealloc];
+}
+
+#pragma mark accessing
+
+- (BDAlias *)selectedAlias;
+{
+    return selectedAlias;
+}
+
+- (void)_setAlias:(BDAlias *)alias;
+{
+    BDAlias *oldAlias = [selectedAlias retain];
+    [previousAlias release];
+    previousAlias = oldAlias;
+    if (selectedAlias != alias) {
+        [selectedAlias release];
+        selectedAlias = [alias retain];
+    }
+}
+
+- (void)_setPath:(NSString *)path;
+{
+    [self _setAlias: [BDAlias aliasWithPath: path]];
+}
+
+- (NSMenuItem *)_itemForAlias:(BDAlias *)alias;
+{
+    NSString *path;
+    SoundFile *sf;
+    if (alias == nil) {
+        return [self itemAtIndex: 0];
+    }
+
+    // [self _validateRecentMedia];
+    path = [alias fullPath];
+    sf = [[SoundFileManager sharedSoundFileManager] soundFileFromPath: path];
+    // NSLog(@"_itemForAlias: %@", path);
+
+    // selected a system sound?
+    if (sf != nil) {
+        // NSLog(@"_itemForAlias: selected system sound");
+        return [self itemAtIndex: [self indexOfItemWithRepresentedObject: sf]];
+    } else {
+        NSEnumerator *e = [recentMediaAliasData reverseObjectEnumerator];
+        NSData *aliasData;
+        NSMenuItem *item;
+        int recentIndex = 1;
+
+        while ( (aliasData = [e nextObject]) != nil) {
+            // selected a recently selected, non-system sound?
+            if ([alias aliasDataIsEqual: aliasData]) {
+                int otherIndex = [self indexOfItem: otherItem];
+                int menuIndex = recentIndex + otherIndex;
+                if (menuIndex == otherIndex + 1) return [self itemAtIndex: menuIndex]; // already at top
+                // remove item, add (at top) later
+                // NSLog(@"_itemForAlias removing item: idx %d + otherItemIdx %d + 1 = %d [%@]", recentIndex, otherIndex, menuIndex, [self itemAtIndex: menuIndex]);
+                [self removeItemAtIndex: menuIndex];
+                [recentMediaAliasData removeObjectAtIndex: [recentMediaAliasData count] - recentIndex];
+                break;
+            }
+            recentIndex++;
+        }
+
+        // create the item
+        item = [self _addRecentMediaAtPath: path withAlias: alias];
+        [self _writeRecentMedia];
+        return item;
+    }
+}
+
+- (BOOL)canRepeat;
+{
+    return movieCanRepeat;
+}
+
+#pragma mark selected media validation
+
+- (void)_invalidateSelection;
+{
+    [self _setAlias: previousAlias];
+    [self selectItem: [self _itemForAlias: [self selectedAlias]]];
+    [[NSNotificationCenter defaultCenter] postNotificationName: NJRQTMediaPopUpButtonMovieChangedNotification object: self];
+}
+
+- (BOOL)_validateWithPreview:(BOOL)doPreview;
+{
+    [preview stop: self];
+    if (selectedAlias == nil) {
+        [preview setMovie: nil];
+        movieCanRepeat = YES;
+        if (doPreview) NSBeep();
+    } else {
+        NSMovie *movie = [[NSMovie alloc] initWithURL: [NSURL fileURLWithPath: [selectedAlias fullPath]] byReference: YES];
+        movieCanRepeat = ![movie isStatic];
+        if ([movie hasAudio])
+            [preview setMovie: movie];
+        else {
+            [preview setMovie: nil];
+            if (movie == nil) {
+                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.");
+                [self _invalidateSelection];
+                return NO;
+            }
+            if (![movie hasAudio] && ![movie hasVideo]) {
+                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]]);
+                [self _invalidateSelection];
+                [movie release];
+                return NO;
+            }
+        }
+        [movie release];
+        [preview start: self];
+    }
+    [[NSNotificationCenter defaultCenter] postNotificationName: NJRQTMediaPopUpButtonMovieChangedNotification object: self];
+    return YES;
+}
+
+#pragma mark actions
+
+- (IBAction)stopSoundPreview:(id)sender;
+{
+    [preview stop: self];
+}
+
+- (void)_beepSelected:(NSMenuItem *)sender;
+{
+    [self _setAlias: nil];
+    [self _validateWithPreview: YES];
+}
+
+- (void)_soundFileSelected:(NSMenuItem *)sender;
+{
+    [self _setPath: [(SoundFile *)[sender representedObject] path]];
+    if (![self _validateWithPreview: YES]) {
+        [[self menu] removeItem: sender];
+    }
+}
+
+- (void)_aliasSelected:(NSMenuItem *)sender;
+{
+    BDAlias *alias = [sender representedObject];
+    int index = [self indexOfItem: sender], otherIndex = [self indexOfItem: otherItem];
+    [self _setAlias: alias];
+    if (![self _validateWithPreview: YES]) {
+        [[self menu] removeItem: sender];
+    } else if (index > otherIndex + 1) { // move "other" item to top of list
+        int recentIndex = [recentMediaAliasData count] - index + otherIndex;
+        NSMenuItem *item = [[self itemAtIndex: index] retain];
+        NSData *data = [[recentMediaAliasData objectAtIndex: recentIndex] retain];
+        [self _validateRecentMedia];
+        [self removeItemAtIndex: index];
+        [[self menu] insertItem: item atIndex: otherIndex + 1];
+        [self selectItem: item];
+        [item release];
+        NSAssert(recentIndex >= 0, @"Recent media index invalid");
+        // NSLog(@"_aliasSelected removing item %d - %d + %d = %d of recentMediaAliasData", [recentMediaAliasData count], index, otherIndex, recentIndex);
+        [recentMediaAliasData removeObjectAtIndex: recentIndex];
+        [recentMediaAliasData addObject: data];
+        [self _validateRecentMedia];
+        [data release];
+    } // else NSLog(@"_aliasSelected ...already at top");
+}
+
+- (IBAction)select:(id)sender;
+{
+    NSOpenPanel *openPanel = [NSOpenPanel openPanel];
+    NSString *path = [selectedAlias fullPath];
+    [openPanel setAllowsMultipleSelection: NO];
+    [openPanel setCanChooseDirectories: NO];
+    [openPanel setCanChooseFiles: YES];
+    [openPanel beginSheetForDirectory: [path stringByDeletingLastPathComponent]
+                                 file: [path lastPathComponent]
+                                types: nil // XXX fix for QuickTime!
+                       modalForWindow: [self window]
+                        modalDelegate: self
+                       didEndSelector: @selector(openPanelDidEnd:returnCode:contextInfo:)
+                          contextInfo: nil];
+}
+
+- (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo;
+{
+    [sheet close];
+
+    if (returnCode == NSOKButton) {
+        NSArray *files = [sheet filenames];
+        NSAssert1([files count] == 1, @"%d items returned, only one expected", [files count]);
+        [self _setPath: [files objectAtIndex: 0]];
+        if ([self _validateWithPreview: YES]) {
+            [self selectItem: [self _itemForAlias: selectedAlias]];
+        }
+    } else {
+        // "Other..." item is still selected, revert to previously selected item
+        // XXX issue with cancelling, top item in recent menu is sometimes duplicated!?
+        [self selectItem: [self _itemForAlias: selectedAlias]];
+    }
+    // [self _validateRecentMedia];
+}
+
+- (void)setEnabled:(BOOL)flag;
+{
+    [super setEnabled: flag];
+    if (flag) ; // XXX [self startSoundPreview: self]; // need to prohibit at startup
+    else [self stopSoundPreview: self];
+}
+
+#pragma mark drag feedback
+
+- (void)drawRect:(NSRect)rect;
+{
+    if (dragAccepted) {
+        NSWindow *window = [self window];
+        NSRect boundsRect = [self bounds];
+        BOOL isFirstResponder = ([window firstResponder] == self);
+        // focus ring and drag feedback interfere with one another
+        if (isFirstResponder) [window makeFirstResponder: window];
+        [super drawRect: rect];
+        [[NSColor selectedControlColor] set];
+        NSFrameRectWithWidthUsingOperation(NSInsetRect(boundsRect, 2, 2), 3, NSCompositeSourceIn);
+        if (isFirstResponder) [window makeFirstResponder: self];
+    } else {
+        [super drawRect: rect];
+    }
+}
+
+@end
+
+@implementation NJRQTMediaPopUpButton (NSDraggingDestination)
+
+- (BOOL)acceptsDragFrom:(id <NSDraggingInfo>)sender;
+{
+    NSURL *url = [NSURL URLFromPasteboard: [sender draggingPasteboard]];
+    NSFileManager *fm = [NSFileManager defaultManager];
+    BOOL isDir;
+
+    if (url == nil || ![url isFileURL]) return NO;
+
+    if (![fm fileExistsAtPath: [url path] isDirectory: &isDir]) return NO;
+
+    if (isDir) return NO;
+    
+    return YES;
+}
+
+- (NSString *)_descriptionForDraggingInfo:(id <NSDraggingInfo>)sender;
+{
+    NSDragOperation mask = [sender draggingSourceOperationMask];
+    NSMutableString *s = [NSMutableString stringWithFormat: @"Drag seq %d source: %@",
+        [sender draggingSequenceNumber], [sender draggingSource]];
+    NSPasteboard *draggingPasteboard = [sender draggingPasteboard];
+    NSArray *types = [draggingPasteboard types];
+    NSEnumerator *e = [types objectEnumerator];
+    NSString *type;
+    [s appendString: @"\nDrag operations:"];
+    if (mask & NSDragOperationCopy) [s appendString: @" copy"];
+    if (mask & NSDragOperationLink) [s appendString: @" link"];
+    if (mask & NSDragOperationGeneric) [s appendString: @" generic"];
+    if (mask & NSDragOperationPrivate) [s appendString: @" private"];
+    if (mask & NSDragOperationMove) [s appendString: @" move"];
+    if (mask & NSDragOperationDelete) [s appendString: @" delete"];
+    if (mask & NSDragOperationEvery) [s appendString: @" every"];
+    if (mask & NSDragOperationNone) [s appendString: @" none"];
+    [s appendFormat: @"\nImage: %@ at %@", [sender draggedImage],
+        NSStringFromPoint([sender draggedImageLocation])];
+    [s appendFormat: @"\nDestination: %@ at %@", [sender draggingDestinationWindow],
+        NSStringFromPoint([sender draggingLocation])];
+    [s appendFormat: @"\nPasteboard: %@ types:", draggingPasteboard];
+    while ( (type = [e nextObject]) != nil) {
+        if ([type hasPrefix: @"CorePasteboardFlavorType 0x"]) {
+            const char *osTypeHex = [[type substringFromIndex: [type rangeOfString: @"0x" options: NSBackwardsSearch].location] lossyCString];
+            OSType osType;
+            sscanf(osTypeHex, "%lx", &osType);
+            [s appendFormat: @" '%4s'", &osType];
+        } else {
+            [s appendFormat: @" \"%@\"", type];
+        }
+    }
+    return s;
+}
+
+- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender;
+{
+    if ([self acceptsDragFrom: sender] && [sender draggingSourceOperationMask] &
+        (NSDragOperationCopy | NSDragOperationLink)) {
+        dragAccepted = YES;
+        [self setNeedsDisplay: YES];
+        // NSLog(@"draggingEntered accept:\n%@", [self _descriptionForDraggingInfo: sender]);
+        return NSDragOperationLink;
+    }
+    return NSDragOperationNone;
+}
+
+- (void)draggingExited:(id <NSDraggingInfo>)sender;
+{
+    dragAccepted = NO;
+    [self setNeedsDisplay: YES];
+}
+
+- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender;
+{
+    dragAccepted = NO;
+    [self setNeedsDisplay: YES];
+    return [self acceptsDragFrom: sender];
+}
+
+- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender;
+{
+    if ([sender draggingSource] != self) {
+        NSURL *url = [NSURL URLFromPasteboard: [sender draggingPasteboard]];
+        if (url == nil) return NO;
+        [self _setPath: [url path]];
+        if ([self _validateWithPreview: YES]) {
+            [self selectItem: [self _itemForAlias: selectedAlias]];
+        }
+    }
+    return YES;
+}
+
+@end
Index: /releases/Pester/1.1a1/Source/NJRScrollView.h
===================================================================
--- /releases/Pester/1.1a1/Source/NJRScrollView.h	(revision 44)
+++ /releases/Pester/1.1a1/Source/NJRScrollView.h	(revision 44)
@@ -0,0 +1,17 @@
+//
+//  NJRScrollView.h
+//  HostLauncher
+//
+//  Created by nicholas on Tue Oct 30 2001.
+//  Copyright (c) 2001 Nicholas Riley. All rights reserved.
+//
+
+#import <AppKit/AppKit.h>
+
+
+@interface NJRScrollView : NSScrollView {
+    BOOL shouldDrawFocusRing;
+    NSResponder *lastResp;
+}
+
+@end
Index: /releases/Pester/1.1a1/Source/NJRScrollView.m
===================================================================
--- /releases/Pester/1.1a1/Source/NJRScrollView.m	(revision 44)
+++ /releases/Pester/1.1a1/Source/NJRScrollView.m	(revision 44)
@@ -0,0 +1,38 @@
+//
+//  NJRScrollView.m
+//  HostLauncher
+//
+//  Created by nicholas on Tue Oct 30 2001.
+//  Copyright (c) 2001 Nicholas Riley. All rights reserved.
+//
+
+#import "NJRScrollView.h"
+
+
+@implementation NJRScrollView
+
+- (BOOL)needsDisplay;
+{
+    NSResponder *resp = nil;
+    if ([[self window] isKeyWindow]) {
+        resp = [[self window] firstResponder];
+        if (resp == lastResp) return [super needsDisplay];
+    } else if (lastResp == nil) {
+        return [super needsDisplay];
+    }
+    shouldDrawFocusRing = (resp != nil && [resp isKindOfClass: [NSView class]] &&
+                           [(NSView *)resp isDescendantOf: self]); // [sic]
+    lastResp = resp;
+    [self setKeyboardFocusRingNeedsDisplayInRect: [self bounds]];
+    return YES;
+}
+
+- (void)drawRect:(NSRect)rect {
+    [super drawRect: rect];
+    if (shouldDrawFocusRing) {
+        NSSetFocusRingStyle(NSFocusRingOnly);
+        NSRectFill([self bounds]);
+    }
+}
+
+@end
Index: /releases/Pester/1.1a1/Source/NJRTableDelegate.h
===================================================================
--- /releases/Pester/1.1a1/Source/NJRTableDelegate.h	(revision 44)
+++ /releases/Pester/1.1a1/Source/NJRTableDelegate.h	(revision 44)
@@ -0,0 +1,21 @@
+//
+//  NJRTableDelegate.h
+//  Pester
+//
+//  Created by Nicholas Riley on Sun Oct 27 2002.
+//  Copyright (c) 2002 __MyCompanyName__. All rights reserved.
+//
+
+#import <AppKit/AppKit.h>
+
+
+@interface NJRTableDelegate : NSObject {
+    IBOutlet NSTableView *tableView;
+    NSTableColumn *sortingColumn;
+    NSString *sortingKey;
+    BOOL sortDescending;
+
+    id oData; // XXX ???
+}
+
+@end
Index: /releases/Pester/1.1a1/Source/NJRTableDelegate.m
===================================================================
--- /releases/Pester/1.1a1/Source/NJRTableDelegate.m	(revision 44)
+++ /releases/Pester/1.1a1/Source/NJRTableDelegate.m	(revision 44)
@@ -0,0 +1,176 @@
+//
+//  NJRTableDelegate.m
+//  Pester
+//
+//  Created by Nicholas Riley on Sun Oct 27 2002.
+//  Copyright (c) 2002 __MyCompanyName__. All rights reserved.
+//
+
+#import "NJRTableDelegate.h"
+#import "NSTableView-NJRExtensions.h"
+
+#pragma mark sorting support
+
+typedef struct { NSString *key; BOOL descending; } SortContext;
+
+// Sort array of itemNums, by looking up the itemNum in the dictionary of dictionaries.
+// based on code of Ondra Cada <ocs@ocs.cz> on cocoa-dev list
+
+int ORDER_BY_CONTEXT(id left, id right, void *ctxt) {
+    SortContext *context = (SortContext *)ctxt;
+    int order = 0;
+    id key = context->key;
+    if (0 != key) {
+        id first, second;	// the actual objects to compare
+
+        if (context->descending) {
+            first  = [right objectForKey: key];
+            second = [left  objectForKey: key];
+        } else {
+            first  = [left  objectForKey: key];
+            second = [right objectForKey: key];
+        }
+
+        if ([first respondsToSelector: @selector(caseInsensitiveCompare:)]) {
+            order = [first caseInsensitiveCompare:second];
+        } else { // sort numbers or dates
+            order = [(NSNumber *)first compare:second];
+        }
+    }
+    return order;
+}
+
+@implementation NJRTableDelegate
+
+#pragma mark initialize-release
+
+- (void)dealloc
+{
+    [sortingColumn release];
+    [sortingKey release];
+    [super dealloc];
+}
+
+#pragma mark accessing
+
+- (void)setSortingColumn:(NSTableColumn *)inNewValue;
+{
+    [inNewValue retain];
+    [sortingColumn release];
+    sortingColumn = inNewValue;
+}
+
+- (void)setSortingKey:(NSString *)inNewValue;
+{
+    [inNewValue retain];
+    [sortingKey release];
+    sortingKey = inNewValue;
+}
+
+#pragma mark saving/restoring selection
+
+- (NSSet *)selectedItems;
+{
+    NSMutableSet *result = [NSMutableSet set];
+    NSEnumerator *e = [tableView selectedRowEnumerator];
+    NSNumber *rowNum;
+
+    while ( (rowNum = [e nextObject]) != nil) {
+        id item = [oData objectAtIndex: [rowNum intValue]];
+        [result addObject: item];
+    }
+    return result;
+}
+
+- (void)selectItems:(NSSet *)inSelectedItems;
+{
+    NSEnumerator *e = [inSelectedItems objectEnumerator];
+    id item;
+    int savedLastRow = 0;
+
+    [tableView deselectAll: nil];
+
+    while ( (item = [e nextObject]) != nil ) {
+        int row = [oData indexOfObjectIdenticalTo: item];
+        if (row != NSNotFound) {
+            [tableView selectRow: row byExtendingSelection: YES];
+            savedLastRow = row;
+        }
+    }
+    [tableView scrollRowToVisible: savedLastRow];
+}
+
+// ----------------------------------------------------------------------------------------
+// Sorting
+// ----------------------------------------------------------------------------------------
+
+- (void)sortData
+{
+    SortContext ctxt = { sortingKey, sortDescending };
+    NSSet *oldSelection = [self selectedItems];
+
+    // sort the NSMutableArray
+    [oData sortUsingFunction: ORDER_BY_CONTEXT context: &ctxt];
+
+    [tableView reloadData];
+    [self selectItems: oldSelection];
+}
+
+- (void)sortByColumn:(NSTableColumn *)inTableColumn;
+{
+    if (sortingColumn == inTableColumn) {
+        // User clicked same column, change sort order
+        sortDescending = !sortDescending;
+        // Possible optimization: Don't actually re-sort if you just change the sorting direction;
+        // instead, just display either the nth item or the (count-1-n)th item depending on ascending/descending.)
+    } else {
+        // User clicked new column, change old/new column headers,
+        // save new sorting column, and re-sort the array.
+        sortDescending = NO;
+        if (nil != sortingColumn) {
+            [tableView setIndicatorImage: nil inTableColumn: sortingColumn];
+        }
+        [self setSortingKey: [inTableColumn identifier]];
+        [self setSortingColumn: inTableColumn];
+        [tableView setHighlightedTableColumn: inTableColumn];
+    }
+    [tableView setIndicatorImage: (sortDescending ? [NSTableView descendingSortIndicator] : [NSTableView ascendingSortIndicator]) inTableColumn: inTableColumn];
+    // Actually sort the data
+    [self sortData];
+}
+
+//	Sort by whatever column was clicked upon
+- (void)tableView:(NSTableView*)aTableView didClickTableColumn:(NSTableColumn *)inTableColumn
+{
+    [[tableView window] makeFirstResponder: aTableView]; // help make this tableView be first responder
+    [self sortByColumn:inTableColumn];
+}
+
+// ----------------------------------------------------------------------------------------
+// Alphabetic Type Ahead
+// ----------------------------------------------------------------------------------------
+
+- (void) typeAheadString:(NSString *)inString;
+{
+    // This general sample looks for a highlighted column, presuming that is that column we are sorted by, and uses that as the lookup key.
+    NSTableColumn *col = [tableView highlightedTableColumn];
+    if (nil != col) {
+        NSString *key = [col identifier];
+        int i;
+        for ( i = 0 ; i < [oData count] ; i++ ) {
+            NSDictionary *rowDict = [oData objectAtIndex:i];
+            NSString *compareTo = [rowDict objectForKey:key];
+            NSComparisonResult order = [inString caseInsensitiveCompare:compareTo];
+            if (order != NSOrderedDescending) break;
+        }
+        // Make sure we're not overflowing the row count.
+        if (i >= [oData count]) {
+            i = [oData count] - 1;
+        }
+        // Now select row i -- either the one we found, or the last row if not found.
+        [tableView selectRow:i byExtendingSelection:NO];
+        [tableView scrollRowToVisible:i];
+    }
+}
+
+@end
Index: /releases/Pester/1.1a1/Source/NJRVoicePopUpButton.h
===================================================================
--- /releases/Pester/1.1a1/Source/NJRVoicePopUpButton.h	(revision 44)
+++ /releases/Pester/1.1a1/Source/NJRVoicePopUpButton.h	(revision 44)
@@ -0,0 +1,28 @@
+//
+//  NJRVoicePopUpButton.h
+//  Pester
+//
+//  Created by Nicholas Riley on Sat Oct 26 2002.
+//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
+//
+
+#import <AppKit/AppKit.h>
+#import "SUSpeaker.h"
+
+@interface NJRVoicePopUpButton : NSPopUpButton {
+    id _delegate;
+    SUSpeaker *_speaker;
+}
+
+- (void)setDelegate:(id)delegate;
+- (id)delegate;
+
+- (IBAction)stopVoicePreview:(id)sender;
+
+@end
+
+@interface NSObject (NJRVoicePopUpButtonDelegate)
+
+- (NSString *)voicePopUpButton:(NJRVoicePopUpButton *)sender previewStringForVoice:(NSString *)voice;
+
+@end
Index: /releases/Pester/1.1a1/Source/NJRVoicePopUpButton.m
===================================================================
--- /releases/Pester/1.1a1/Source/NJRVoicePopUpButton.m	(revision 44)
+++ /releases/Pester/1.1a1/Source/NJRVoicePopUpButton.m	(revision 44)
@@ -0,0 +1,133 @@
+//
+//  NJRVoicePopUpButton.m
+//  Pester
+//
+//  Created by Nicholas Riley on Sat Oct 26 2002.
+//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
+//
+
+#import "NJRVoicePopUpButton.h"
+
+// XXX says nothing instead of "Alarm!" any more if there's no alarm message specified
+
+@implementation NJRVoicePopUpButton
+
+- (void)_refreshVoiceList;
+{
+    NSMenu *menu;
+    NSMenuItem *item;
+    NSArray *voiceNames = [SUSpeaker voiceNames];
+
+    [self removeAllItems];
+    menu = [self menu];
+    [menu setAutoenablesItems: NO];
+    // XXX would be more elegant with surrogate support like my font popup menu
+    item = [menu addItemWithTitle: @"ÇunknownÈ" action: nil keyEquivalent: @""];
+    [item setEnabled: NO];
+    [menu addItem: [NSMenuItem separatorItem]];
+    if (voiceNames == nil || [voiceNames count] == 0) {
+        item = [menu addItemWithTitle: @"CanÕt locate voices" action: nil keyEquivalent: @""];
+        [item setEnabled: NO];
+    } else {
+        NSEnumerator *e = [voiceNames objectEnumerator];
+        NSString *voiceName;
+        while ( (voiceName = [e nextObject]) != nil) {
+            item = [menu addItemWithTitle: voiceName action: @selector(_previewVoice) keyEquivalent: @""];
+            [item setTarget: self];
+        }
+    }
+    if (_speaker == nil) [self selectItemWithTitle: [SUSpeaker defaultVoice]];
+}
+
+- (id)initWithFrame:(NSRect)frame;
+{
+    if ( (self = [super initWithFrame: frame]) != nil) {
+        [self _refreshVoiceList];
+    }
+    return self;
+}
+
+- (id)initWithCoder:(NSCoder *)coder;
+{
+    if ( (self = [super initWithCoder: coder]) != nil) {
+        [self _refreshVoiceList];
+    }
+    return self;
+}
+
+- (SUSpeaker *)_speaker;
+{
+    if (_speaker == nil) _speaker = [[SUSpeaker alloc] init];
+    return _speaker;
+}
+
+- (void)_invalidateVoiceSelection;
+{
+    [self _refreshVoiceList];
+    [self selectItemAtIndex: 0];
+}
+
+- (void)_previewVoice;
+{
+    NSString *voiceName = [self titleOfSelectedItem];
+    NSString *previewString = nil;
+    VoiceSpec voice;
+    OSStatus err = noErr;
+    VoiceDescription info;
+    short voiceIndex = [[SUSpeaker voiceNames] indexOfObject: voiceName] + 1;
+
+    [_speaker stopSpeaking];
+
+    if ( (err = GetIndVoice(voiceIndex, &voice)) != noErr) {
+        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);
+        [self _invalidateVoiceSelection];
+        return;
+    }
+
+    if (_delegate != nil && [_delegate respondsToSelector: @selector(voicePopUpButton:previewStringForVoice:)]) {
+        previewString = [_delegate voicePopUpButton: self previewStringForVoice: voiceName];
+    }
+
+    if (previewString == nil) {
+        err = GetVoiceDescription(&voice, &info, sizeof(info));
+        if (err != noErr || info.comment[0] == 0)
+            previewString = voiceName;
+        else {
+            previewString = (NSString *)CFStringCreateWithPascalString(NULL, info.comment, kCFStringEncodingMacRoman);
+            [previewString autorelease];
+        }
+    }
+
+    [[self _speaker] setVoice: voiceIndex];
+    [_speaker speakText: previewString];
+}
+
+- (void)dealloc;
+{
+    [_speaker release];
+    [super dealloc];
+}
+
+- (IBAction)stopVoicePreview:(id)sender;
+{
+    [_speaker stopSpeaking];
+}
+
+- (void)setEnabled:(BOOL)flag;
+{
+    [super setEnabled: flag];
+    if (flag) ; // XXX [self stopVoicePreview: self]; // need to prohibit at startup
+    else [self stopVoicePreview: self];
+}
+
+- (void)setDelegate:(id)delegate;
+{
+    _delegate = delegate;
+}
+
+- (id)delegate;
+{
+    return _delegate;
+}
+
+@end
Index: /releases/Pester/1.1a1/Source/NSImage-NJRExtensions.h
===================================================================
--- /releases/Pester/1.1a1/Source/NSImage-NJRExtensions.h	(revision 44)
+++ /releases/Pester/1.1a1/Source/NSImage-NJRExtensions.h	(revision 44)
@@ -0,0 +1,16 @@
+//
+//  NSImage-NJRExtensions.h
+//  Pester
+//
+//  Created by Nicholas Riley on Mon Oct 28 2002.
+//  Copyright (c) 2002 __MyCompanyName__. All rights reserved.
+//
+
+#import <AppKit/AppKit.h>
+
+
+@interface NSImage (NJRExtensions)
+
+- (NSImage *)bestFitImageForSize:(NSSize)imageSize;
+
+@end
Index: /releases/Pester/1.1a1/Source/NSImage-NJRExtensions.m
===================================================================
--- /releases/Pester/1.1a1/Source/NSImage-NJRExtensions.m	(revision 44)
+++ /releases/Pester/1.1a1/Source/NSImage-NJRExtensions.m	(revision 44)
@@ -0,0 +1,62 @@
+//
+//  NSImage-NJRExtensions.m
+//  Pester
+//
+//  Created by Nicholas Riley on Mon Oct 28 2002.
+//  Copyright (c) 2002 __MyCompanyName__. All rights reserved.
+//
+
+#import "NSImage-NJRExtensions.h"
+
+
+@implementation NSImage (NJRExtensions)
+
+- (NSImage *)bestFitImageForSize:(NSSize)imageSize;
+{
+    NSSize repSize;
+    NSArray *imageReps;
+    int i, repCount;
+    NSImageRep *preferredRep, *rep;
+    imageReps = [self representations];
+    repCount = [imageReps count];
+    preferredRep = [imageReps objectAtIndex: 0];
+    for (i = 1 ; i < repCount ; i++) {
+        rep = [imageReps objectAtIndex: i];
+        repSize = [rep size];
+        if (repSize.width == imageSize.width && repSize.height == imageSize.height) {
+            preferredRep = rep;
+            break;
+        }
+        if (repSize.width >= imageSize.width || repSize.height >= imageSize.height) {
+            // pick the smallest of the larger representations
+            if (repSize.width <= [preferredRep size].width ||
+                repSize.height <= [preferredRep size].height) preferredRep = rep;
+        } else {
+            // or the largest of the smaller representations
+            if (([preferredRep size].width > imageSize.width ||
+                    [preferredRep size].height > imageSize.height) ||
+                // (assuming that the previous preferred rep was smaller, too)
+                // XXX fix this in HostLauncher too - or is the FSA code better?
+                (repSize.width >= [preferredRep size].width ||
+                    repSize.height >= [preferredRep size].height)) preferredRep = rep;
+        }
+    }
+    // Begin workaround code for bug in OS X 10.1 (removeRepresentation: has no effect)
+    if ([preferredRep size].width > imageSize.width || [preferredRep size].height > imageSize.height) {
+        NSImage *scaledImage = [[NSImage alloc] initWithSize: imageSize];
+        NSRect rect = { NSZeroPoint, imageSize };
+        [scaledImage setFlipped: [self isFlipped]]; // XXX this works, but is correct?
+        [scaledImage lockFocus];
+        [preferredRep drawInRect: rect];
+        [scaledImage unlockFocus];
+        return scaledImage;
+    } else if (repCount > 1) {
+        NSImage *sizedImage = [[NSImage alloc] initWithSize: [preferredRep size]];
+        [sizedImage addRepresentation: preferredRep];
+        return sizedImage;
+    } else {
+        return self;
+    }
+}
+
+@end
Index: /releases/Pester/1.1a1/Source/NSMovie-NJRExtensions.h
===================================================================
--- /releases/Pester/1.1a1/Source/NSMovie-NJRExtensions.h	(revision 44)
+++ /releases/Pester/1.1a1/Source/NSMovie-NJRExtensions.h	(revision 44)
@@ -0,0 +1,18 @@
+//
+//  NSMovie-NJRExtensions.h
+//  Pester
+//
+//  Created by Nicholas Riley on Sat Oct 26 2002.
+//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
+//
+
+#import <AppKit/AppKit.h>
+
+
+@interface NSMovie (NJRExtensions)
+
+- (BOOL)hasAudio;
+- (BOOL)hasVideo;
+- (BOOL)isStatic;
+
+@end
Index: /releases/Pester/1.1a1/Source/NSMovie-NJRExtensions.m
===================================================================
--- /releases/Pester/1.1a1/Source/NSMovie-NJRExtensions.m	(revision 44)
+++ /releases/Pester/1.1a1/Source/NSMovie-NJRExtensions.m	(revision 44)
@@ -0,0 +1,41 @@
+//
+//  NSMovie-NJRExtensions.m
+//  Pester
+//
+//  Created by Nicholas Riley on Sat Oct 26 2002.
+//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
+//
+
+#import "NSMovie-NJRExtensions.h"
+#import <QuickTime/Movies.h>
+
+@implementation NSMovie (NJRExtensions)
+
+- (BOOL)hasAudio;
+{
+    return (NULL != GetMovieIndTrackType((Movie)[self QTMovie], 1, AudioMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly));
+}
+
+- (BOOL)hasVideo;
+{
+    return (NULL != GetMovieIndTrackType((Movie)[self QTMovie], 1, VisualMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly));
+}
+
+- (BOOL)isStatic;
+{
+    Movie movie = (Movie)[self QTMovie];
+    long trackCount = (long)GetMovieTrackCount(movie);
+    long trackIdx = 0;
+    Track track;
+    Media media;
+    for (trackIdx = 1 ; trackIdx <= trackCount ; trackIdx++) {
+        track = GetMovieIndTrack(movie, trackIdx);
+        if (track == nil) continue;
+        media = GetTrackMedia(track);
+        if (media == nil) continue;
+        if (GetMediaSampleCount(media) > 1) return NO;
+    }
+    return YES;
+}
+
+@end
Index: /releases/Pester/1.1a1/Source/NSTableView-NJRExtensions.h
===================================================================
--- /releases/Pester/1.1a1/Source/NSTableView-NJRExtensions.h	(revision 44)
+++ /releases/Pester/1.1a1/Source/NSTableView-NJRExtensions.h	(revision 44)
@@ -0,0 +1,21 @@
+//
+//  NSTableView-NJRExtensions.h
+//  Pester
+//
+//  Created by Nicholas Riley on Sun Oct 27 2002.
+//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
+//
+
+#import <AppKit/AppKit.h>
+
+
+@interface NSTableView (NJRExtensions)
+
++ (NSImage *)ascendingSortIndicator;
++ (NSImage *)descendingSortIndicator;
+
+- (float)cellHeight;
+
+- (NSArray *)selectedRowIndices;
+
+@end
Index: /releases/Pester/1.1a1/Source/NSTableView-NJRExtensions.m
===================================================================
--- /releases/Pester/1.1a1/Source/NSTableView-NJRExtensions.m	(revision 44)
+++ /releases/Pester/1.1a1/Source/NSTableView-NJRExtensions.m	(revision 44)
@@ -0,0 +1,59 @@
+//
+//  NSTableView-NJRExtensions.m
+//  Pester
+//
+//  Created by Nicholas Riley on Sun Oct 27 2002.
+//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
+//
+
+#import "NSTableView-NJRExtensions.h"
+
+
+@interface NSTableView (PumaPrivate)
+// Declarations of 10.1 private methods, just to make the compiler happy.
++ (id) _defaultTableHeaderReverseSortImage;
++ (id) _defaultTableHeaderSortImage;
+@end
+
+@implementation NSTableView (NJRExtensions)
+
++ (NSImage *)ascendingSortIndicator;
+{
+    NSImage *result = [NSImage imageNamed: @"NSAscendingSortIndicator"];
+    if (result == nil && [[NSTableView class] respondsToSelector: @selector(_defaultTableHeaderSortImage)])
+        result = [NSTableView _defaultTableHeaderSortImage];
+    return result;
+}
+
++ (NSImage *)descendingSortIndicator;
+{
+    NSImage *result = [NSImage imageNamed:@"NSDescendingSortIndicator"];
+    if (result == nil && [[NSTableView class] respondsToSelector:@selector(_defaultTableHeaderReverseSortImage)])
+        result = [NSTableView _defaultTableHeaderReverseSortImage];
+    return result;
+}
+
+- (NSArray *)selectedRowIndices;
+{
+    NSEnumerator *theEnum = [self selectedRowEnumerator];
+    NSNumber *rowNumber;
+    NSMutableArray *rowNumberArray = [NSMutableArray arrayWithCapacity: [self numberOfSelectedRows]];
+
+    while (nil != (rowNumber = [theEnum nextObject]) )
+        [rowNumberArray addObject: rowNumber];
+
+    return rowNumberArray;
+}
+
+- (float)cellHeight;
+{
+    return [self rowHeight] + [self intercellSpacing].height;
+}
+
+// causes NSTableView to get keyboard focus (with thanks to Pierre-Olivier Latour)
+- (BOOL)needsPanelToBecomeKey
+{
+    return YES;
+}
+
+@end
Index: /releases/Pester/1.1a1/Source/PSAlarm.h
===================================================================
--- /releases/Pester/1.1a1/Source/PSAlarm.h	(revision 44)
+++ /releases/Pester/1.1a1/Source/PSAlarm.h	(revision 44)
@@ -0,0 +1,57 @@
+//
+//  PSAlarm.h
+//  Pester
+//
+//  Created by Nicholas Riley on Wed Oct 09 2002.
+//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+typedef enum {
+    PSAlarmInvalid, PSAlarmInterval, PSAlarmDate, PSAlarmSet
+} PSAlarmType;
+
+extern NSString * const PSAlarmTimerSetNotification;
+extern NSString * const PSAlarmTimerExpiredNotification;
+
+// XXX figure out how to support reading old alarms
+
+@class PSAlert;
+
+@interface PSAlarm : NSObject <NSCoding> {
+    PSAlarmType alarmType;
+    NSCalendarDate *alarmDate;
+    NSTimeInterval alarmInterval;
+    NSString *alarmMessage;
+    NSString *invalidMessage;
+    NSTimer *timer;
+    NSMutableArray *alerts;
+}
+
+- (void)setInterval:(NSTimeInterval)anInterval;
+- (void)setForDateAtTime:(NSCalendarDate *)dateTime;
+- (void)setForDate:(NSDate *)date atTime:(NSDate *)time;
+- (void)setMessage:(NSString *)aMessage;
+- (void)addAlert:(PSAlert *)alert;
+- (void)removeAlerts;
+
+- (NSCalendarDate *)date;
+- (NSTimeInterval)interval;
+- (NSString *)message;
+- (NSArray *)alerts;
+
+- (NSString *)dateString;
+- (NSString *)shortDateString;
+- (NSString *)timeString;
+- (NSString *)timeRemainingString;
+
+- (BOOL)isValid;
+- (NSString *)invalidMessage;
+
+- (NSComparisonResult)compare:(PSAlarm *)otherAlarm;
+
+- (BOOL)setTimer;
+- (void)cancel;
+
+@end
Index: /releases/Pester/1.1a1/Source/PSAlarm.m
===================================================================
--- /releases/Pester/1.1a1/Source/PSAlarm.m	(revision 44)
+++ /releases/Pester/1.1a1/Source/PSAlarm.m	(revision 44)
@@ -0,0 +1,319 @@
+//
+//  PSAlarm.m
+//  Pester
+//
+//  Created by Nicholas Riley on Wed Oct 09 2002.
+//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
+//
+
+#import "PSAlarm.h"
+#import "PSAlert.h"
+#import "NJRDateFormatter.h"
+
+NSString * const PSAlarmTimerSetNotification = @"PSAlarmTimerSetNotification";
+NSString * const PSAlarmTimerExpiredNotification = @"PSAlarmTimerExpiredNotification";
+
+static NSString *dateFormat, *shortDateFormat, *timeFormat;
+static NSDictionary *locale;
+
+// XXX need to reset pending alarms after sleep, they "freeze" and never expire.
+
+@implementation PSAlarm
+
++ (void)initialize; // XXX change on locale modification, subscribe to NSNotifications
+{
+    dateFormat = [[NJRDateFormatter localizedDateFormatIncludingWeekday: YES] retain];
+    shortDateFormat = [[NJRDateFormatter localizedShortDateFormatIncludingWeekday: NO] retain];
+    timeFormat = [[NJRDateFormatter localizedTimeFormatIncludingSeconds: YES] retain];
+    locale = [[[NSUserDefaults standardUserDefaults] dictionaryRepresentation] retain];
+}
+
+- (void)dealloc;
+{
+    // NSLog(@"DEALLOC %@", self);
+    alarmType = PSAlarmInvalid;
+    [alarmDate release]; alarmDate = nil;
+    [alarmMessage release]; alarmMessage = nil;
+    [invalidMessage release]; invalidMessage = nil;
+    [timer invalidate]; [timer release]; timer = nil;
+    [alerts release]; alerts = nil;
+    [super dealloc];
+}
+
+- (void)_setAlarmDate:(NSCalendarDate *)aDate;
+{
+    if (alarmDate != aDate) {
+        [alarmDate release];
+        alarmDate = nil;
+        alarmDate = [aDate retain];
+    }
+}
+
+- (void)_invalidate:(NSString *)aMessage;
+{
+    alarmType = PSAlarmInvalid;
+    if (aMessage != invalidMessage) {
+        [invalidMessage release];
+        invalidMessage = nil;
+        [self _setAlarmDate: nil];
+        alarmInterval = 0;
+        invalidMessage = [aMessage retain];
+    }
+}
+
+- (void)_validateForType:(PSAlarmType)type;
+{
+    if (alarmType == PSAlarmSet) return; // already valid
+    [invalidMessage release];
+    invalidMessage = nil;
+    alarmType = type;
+}
+
+- (void)_setDateFromInterval;
+{
+    [alarmDate release]; alarmDate = nil;
+    alarmDate = [NSCalendarDate dateWithTimeIntervalSinceNow: alarmInterval];
+    [alarmDate retain];
+    [self _validateForType: PSAlarmInterval];
+}
+
+- (void)setInterval:(NSTimeInterval)anInterval;
+{
+    alarmInterval = anInterval;
+    if (alarmInterval <= 0) {
+        [self _invalidate: @"Please specify an alarm interval."]; return;
+    }
+    [self _setDateFromInterval];
+}
+
+- (void)_setIntervalFromDate;
+{
+    alarmInterval = [alarmDate timeIntervalSinceNow] + 1;
+    if (alarmInterval <= 0) {
+        [self _invalidate: @"Please specify an alarm time in the future."];
+        return;
+    }
+    [self _validateForType: PSAlarmDate];
+}
+
+- (void)setForDateAtTime:(NSCalendarDate *)dateTime;
+{
+    [self _setAlarmDate: dateTime];
+    [self _setIntervalFromDate];
+}
+
+- (void)setForDate:(NSDate *)date atTime:(NSDate *)time;
+{
+    NSCalendarDate *calTime, *calDate;
+    if (time == nil && date == nil) {
+        [self _invalidate: @"Please specify an alarm date and time."]; return;
+    }
+    if (time == nil) {
+        [self _invalidate: @"Please specify an alarm time."]; return;
+    }
+    if (date == nil) {
+        [self _invalidate: @"Please specify an alarm date."]; return;
+    }
+    // XXX if calTime's date is different from the default date, complain
+    calTime = [NSCalendarDate dateWithTimeIntervalSinceReferenceDate: [time timeIntervalSinceReferenceDate]];
+    calDate = [NSCalendarDate dateWithTimeIntervalSinceReferenceDate: [date timeIntervalSinceReferenceDate]];
+    if (calTime == nil || calDate == nil) {
+        [self _invalidate: @"Please specify a reasonable date and time."];
+    }
+    [self setForDateAtTime:
+        [[[NSCalendarDate alloc] initWithYear: [calDate yearOfCommonEra]
+                                        month: [calDate monthOfYear]
+                                          day: [calDate dayOfMonth]
+                                         hour: [calTime hourOfDay]
+                                       minute: [calTime minuteOfHour]
+                                       second: [calTime secondOfMinute]
+                                     timeZone: nil] autorelease]];
+}
+
+- (BOOL)isValid;
+{
+    if (alarmType == PSAlarmDate) [self _setIntervalFromDate];
+    return (alarmType != PSAlarmInvalid);
+}
+
+- (void)setMessage:(NSString *)aMessage;
+{
+    if (aMessage != alarmMessage) {
+        [alarmMessage release];
+        alarmMessage = nil;
+        alarmMessage = [aMessage retain];
+    }
+}
+
+- (NSString *)message;
+{
+    if (alarmMessage == nil || [alarmMessage isEqualToString: @""])
+        return @"Alarm!";
+    return alarmMessage;    
+}
+
+- (NSString *)invalidMessage;
+{
+    if (invalidMessage == nil) return @"";
+    return invalidMessage;
+}
+
+- (NSCalendarDate *)date;
+{
+    if (alarmType == PSAlarmInterval) [self _setDateFromInterval];
+    return alarmDate;
+}
+
+- (NSString *)dateString;
+{
+    return [[self date] descriptionWithCalendarFormat: dateFormat locale: locale];
+}
+
+- (NSString *)shortDateString;
+{
+    return [[self date] descriptionWithCalendarFormat: shortDateFormat locale: locale];
+}
+
+- (NSString *)timeString;
+{
+    return [[self date] descriptionWithCalendarFormat: timeFormat locale: locale];
+}
+
+- (NSString *)timeRemainingString;
+{
+    static const unsigned long long minute = 60, hour = minute * 60, day = hour * 24, year = day * 365.26;
+    unsigned long long interval = [self interval];
+    // +[NSString stringWithFormat:] in 10.1 does not support long longs: work around it by converting to unsigned ints or longs for display
+    if (interval == 0) return @"ÇexpiredÈ";
+    if (interval < minute) return [NSString stringWithFormat: @"%us", (unsigned)interval];
+    if (interval < day) return [NSString stringWithFormat: @"%uh %um", (unsigned)(interval / hour), (unsigned)((interval % hour) / minute)];
+    if (interval < year) return [NSString stringWithFormat: @"%u days", (unsigned)(interval / day)];
+    if (interval < 2 * year) return @"One year";
+    return [NSString stringWithFormat: @"%lu years", (unsigned long)(interval / year)];
+}
+
+- (NSTimeInterval)interval;
+{
+    if (alarmType == PSAlarmSet || alarmType == PSAlarmDate) [self _setIntervalFromDate];
+    return alarmInterval;
+}
+
+- (BOOL)setTimer;
+{
+    switch (alarmType) {
+        case PSAlarmDate: if (![self isValid]) return NO;
+        case PSAlarmInterval:
+            timer = [NSTimer scheduledTimerWithTimeInterval: alarmInterval
+                                                     target: self
+                                                   selector: @selector(_timerExpired:)
+                                                   userInfo: nil
+                                                    repeats: NO];
+            if (timer != nil) {
+                [timer retain];
+                alarmType = PSAlarmSet;
+                [[NSNotificationCenter defaultCenter] postNotificationName: PSAlarmTimerSetNotification object: self];
+                return YES;
+            }
+        default:
+            return NO;
+    }
+}
+
+- (void)cancel;
+{
+    [timer invalidate]; [timer release]; timer = nil;
+}
+
+- (void)_timerExpired:(NSTimer *)aTimer;
+{
+    [[NSNotificationCenter defaultCenter] postNotificationName: PSAlarmTimerExpiredNotification object: self];
+    [timer release]; timer = nil;
+}
+
+- (NSString *)_alarmTypeString;
+{
+    switch (alarmType) {
+        case PSAlarmDate: return @"PSAlarmDate";
+        case PSAlarmInterval: return @"PSAlarmInterval";
+        case PSAlarmSet: return @"PSAlarmSet";
+        case PSAlarmInvalid: return @"PSAlarmInvalid";
+        default: return [NSString stringWithFormat: @"<unknown: %u>", alarmType];
+    }
+}
+
+- (NSComparisonResult)compare:(PSAlarm *)otherAlarm;
+{
+    return [[self date] compare: [otherAlarm date]];
+}
+
+- (void)addAlert:(PSAlert *)alert;
+{
+    if (alerts == nil) alerts = [[NSMutableArray alloc] initWithCapacity: 4];
+    [alerts addObject: alert];
+}
+
+- (void)removeAlerts;
+{
+    [alerts removeAllObjects];
+}
+
+- (NSArray *)alerts;
+{
+    return [[alerts copy] autorelease];
+}
+
+- (NSString *)description;
+{
+    return [NSString stringWithFormat: @"%@: type %@ date %@ interval %.1f%@",
+        [super description], [self _alarmTypeString], alarmDate, alarmInterval,
+        (alarmType == PSAlarmInvalid ?
+         [NSString stringWithFormat: @"\ninvalid message: %@", invalidMessage]
+        : (alarmType == PSAlarmSet ?
+           [NSString stringWithFormat: @"\ntimer: %@", timer] : @""))];
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder;
+{
+    if (![self isValid]) return;
+    [coder encodeValueOfObjCType: @encode(PSAlarmType) at: &alarmType];
+    switch (alarmType) {
+        case PSAlarmDate:
+        case PSAlarmSet:
+            [coder encodeObject: alarmDate];
+            break;
+        case PSAlarmInterval:
+            [coder encodeValueOfObjCType: @encode(NSTimeInterval) at: &alarmInterval];
+            break;
+        default:
+            break;
+    }
+    [coder encodeObject: alarmMessage];
+    // NSLog(@"encoded: %@", self); // XXX happening twice, gdb refuses to show proper backtrace, grr
+    return;
+}
+
+- (id)initWithCoder:(NSCoder *)coder;
+{
+    if ( (self = [super init]) != nil) {
+        [coder decodeValueOfObjCType: @encode(PSAlarmType) at: &alarmType];
+        switch (alarmType) {
+            case PSAlarmDate:
+            case PSAlarmSet:
+                [self _setAlarmDate: [coder decodeObject]];
+                break;
+            case PSAlarmInterval:
+                [coder decodeValueOfObjCType: @encode(NSTimeInterval) at: &alarmInterval];
+                break;
+            default:
+                break;
+        }
+        [self setMessage: [coder decodeObject]];
+        if (alarmType == PSAlarmSet) {
+            alarmType = PSAlarmDate;
+            [self setTimer];
+        }
+    }
+    return self;
+}
+
+@end
Index: /releases/Pester/1.1a1/Source/PSAlarmAlertController.h
===================================================================
--- /releases/Pester/1.1a1/Source/PSAlarmAlertController.h	(revision 44)
+++ /releases/Pester/1.1a1/Source/PSAlarmAlertController.h	(revision 44)
@@ -0,0 +1,24 @@
+//
+//  PSAlarmAlertController.h
+//  Pester
+//
+//  Created by Nicholas Riley on Sat Oct 26 2002.
+//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
+//
+
+#import <AppKit/AppKit.h>
+#import "PSAlarm.h"
+
+extern NSString * const PSAlarmAlertStopNotification;
+
+@interface PSAlarmAlertController : NSObject {
+
+}
+
++ (PSAlarmAlertController *)controllerWithTimerExpiredNotification:(NSNotification *)notification;
+
++ (IBAction)stopAlerts:(id)sender;
+
+- (id)initWithAlarm:(PSAlarm *)alarm;
+
+@end
Index: /releases/Pester/1.1a1/Source/PSAlarmAlertController.m
===================================================================
--- /releases/Pester/1.1a1/Source/PSAlarmAlertController.m	(revision 44)
+++ /releases/Pester/1.1a1/Source/PSAlarmAlertController.m	(revision 44)
@@ -0,0 +1,35 @@
+//
+//  PSAlarmAlertController.m
+//  Pester
+//
+//  Created by Nicholas Riley on Sat Oct 26 2002.
+//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
+//
+
+#import "PSAlarmAlertController.h"
+
+NSString * const PSAlarmAlertStopNotification = @"PSAlarmAlertStopNotification";
+
+@implementation PSAlarmAlertController
+
++ (PSAlarmAlertController *)controllerWithTimerExpiredNotification:(NSNotification *)notification;
+{
+    return [[[self alloc] initWithAlarm: [notification object]] autorelease];
+}
+
++ (IBAction)stopAlerts:(id)sender;
+{
+    [[NSNotificationCenter defaultCenter] postNotificationName: PSAlarmAlertStopNotification object: nil];
+}
+
+- (id)initWithAlarm:(PSAlarm *)alarm;
+{
+    if ( (self = [super init]) != nil) {
+        [[alarm alerts] makeObjectsPerformSelector: @selector(triggerForAlarm:)
+                                        withObject: alarm];
+        [NSApp activateIgnoringOtherApps: YES];
+    }
+    return self;
+}
+
+@end
Index: /releases/Pester/1.1a1/Source/PSAlarmNotifierController.h
===================================================================
--- /releases/Pester/1.1a1/Source/PSAlarmNotifierController.h	(revision 44)
+++ /releases/Pester/1.1a1/Source/PSAlarmNotifierController.h	(revision 44)
@@ -0,0 +1,22 @@
+//
+//  PSAlarmNotifierController.h
+//  Pester
+//
+//  Created by Nicholas Riley on Tue Oct 08 2002.
+//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
+//
+
+#import <AppKit/AppKit.h>
+
+@class PSAlarm;
+
+@interface PSAlarmNotifierController : NSWindowController {
+    IBOutlet NSTextField *messageField;
+    IBOutlet NSTextField *dateField;
+}
+
+- (id)initWithAlarm:(PSAlarm *)alarm;
+
+- (IBAction)close:(NSButton *)sender;
+
+@end
Index: /releases/Pester/1.1a1/Source/PSAlarmNotifierController.m
===================================================================
--- /releases/Pester/1.1a1/Source/PSAlarmNotifierController.m	(revision 44)
+++ /releases/Pester/1.1a1/Source/PSAlarmNotifierController.m	(revision 44)
@@ -0,0 +1,36 @@
+//
+//  PSAlarmNotifierController.m
+//  Pester
+//
+//  Created by Nicholas Riley on Tue Oct 08 2002.
+//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
+//
+
+#import "PSAlarmNotifierController.h"
+#import "PSAlarmAlertController.h"
+#import "PSAlarm.h"
+
+@implementation PSAlarmNotifierController
+
+// XXX should use NSNonactivatingPanelMask on 10.2
+
+- (id)initWithAlarm:(PSAlarm *)alarm;
+{
+    if ([self initWithWindowNibName: @"Notifier"]) {
+        [[self window] center];
+        [messageField setStringValue: [alarm message]];
+        [dateField setStringValue:
+            [NSString stringWithFormat: @"%@ at %@", [alarm dateString], [alarm timeString]]];
+        [[self window] makeKeyAndOrderFront: nil];
+        [[self window] orderFrontRegardless];
+    }
+    return self;
+}
+
+- (IBAction)close:(id)sender;
+{
+    [PSAlarmAlertController stopAlerts: sender];
+    [self close];
+}
+
+@end
Index: /releases/Pester/1.1a1/Source/PSAlarmSetController.h
===================================================================
--- /releases/Pester/1.1a1/Source/PSAlarmSetController.h	(revision 44)
+++ /releases/Pester/1.1a1/Source/PSAlarmSetController.h	(revision 44)
@@ -0,0 +1,56 @@
+//
+//  PSAlarmSetController.h
+//  Pester
+//
+//  Created by Nicholas Riley on Tue Oct 08 2002.
+//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
+//
+
+#import <AppKit/AppKit.h>
+#import "PSAlarm.h"
+
+@class NJRFSObjectSelector;
+@class NJRQTMediaPopUpButton;
+@class NJRVoicePopUpButton;
+
+@interface PSAlarmSetController : NSWindowController {
+    IBOutlet NSTextField *messageField;
+    IBOutlet NSMatrix *inAtMatrix;
+    IBOutlet NSTextField *timeInterval;
+    IBOutlet NSPopUpButton *timeIntervalUnits;
+    IBOutlet NSButton *timeIntervalRepeats;
+    IBOutlet NSTextField *timeOfDay;
+    IBOutlet NSTextField *timeDate;
+    IBOutlet NSPopUpButton *timeDateCompletions; // XXX should go away when bug preventing both formatters and popup menus from existing is fixed
+    IBOutlet NSButtonCell *displayMessage;
+    IBOutlet NSButton *bounceDockIcon;
+    IBOutlet NSButtonCell *playSound;
+    IBOutlet NJRQTMediaPopUpButton *sound;
+    IBOutlet NSTextField *soundRepetitions;
+    IBOutlet NSStepper *soundRepetitionStepper;
+    IBOutlet NSTextField *soundRepetitionsLabel;
+    IBOutlet NSButtonCell *doScript;
+    IBOutlet NJRFSObjectSelector *script;
+    IBOutlet NSButtonCell *doSpeak;
+    IBOutlet NJRVoicePopUpButton *voice;
+    IBOutlet NSButton *scriptSelectButton;
+    IBOutlet NSTextField *timeSummary;
+    IBOutlet NSButton *setButton;
+    NSString *status;
+    NSTimer *updateTimer;
+    PSAlarm *alarm;
+    BOOL isInterval;
+}
+
+- (IBAction)update:(id)sender;
+- (IBAction)dateCompleted:(NSPopUpButton *)sender;
+- (IBAction)inAtChanged:(id)sender;
+- (IBAction)playSoundChanged:(id)sender;
+- (IBAction)doScriptChanged:(id)sender;
+- (IBAction)doSpeakChanged:(id)sender;
+- (IBAction)setAlarm:(NSButton *)sender;
+- (IBAction)setSoundRepetitionCount:(id)sender;
+
+- (IBAction)silence:(id)sender;
+
+@end
Index: /releases/Pester/1.1a1/Source/PSAlarmSetController.m
===================================================================
--- /releases/Pester/1.1a1/Source/PSAlarmSetController.m	(revision 44)
+++ /releases/Pester/1.1a1/Source/PSAlarmSetController.m	(revision 44)
@@ -0,0 +1,365 @@
+//
+//  PSAlarmSetController.m
+//  Pester
+//
+//  Created by Nicholas Riley on Tue Oct 08 2002.
+//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
+//
+
+#import "PSAlarmSetController.h"
+#import "PSAlarmAlertController.h"
+#import "NJRDateFormatter.h"
+#import "NJRFSObjectSelector.h"
+#import "NJRQTMediaPopUpButton.h"
+#import "NJRVoicePopUpButton.h"
+#import <Carbon/Carbon.h>
+
+#import "PSDockBounceAlert.h"
+#import "PSScriptAlert.h"
+#import "PSNotifierAlert.h"
+#import "PSBeepAlert.h"
+#import "PSMovieAlert.h"
+#import "PSSpeechAlert.h"
+
+/* Bugs to file:
+
+¥ any trailing spaces: -> exception for +[NSCalendarDate dateWithNaturalLanguageString]:
+ > NSCalendarDate dateWithNaturalLanguageString: '12 '
+  format error: internal error
+
+¥ NSDate natural language stuff in NSCalendarDate (why?), misspelled category name
+¥ NSCalendarDate natural language stuff behaves differently from NSDateFormatter (AM/PM has no effect, shouldn't they share code?)
+¥ descriptionWithCalendarFormat:, dateWithNaturalLanguageString: does not default to current locale, instead it defaults to US unless you tell it otherwise
+¥ NSDateFormatter doc class description gives two examples for natural language that are incorrect, no link to NSDate doc that describes exactly how natural language dates are parsed
+¥ NSTimeFormatString does not include %p when it should, meaning that AM/PM is stripped yet 12-hour time is still used
+¥ NSNextDayDesignations, NSNextNextDayDesignations are noted as 'a string' in NSUserDefaults docs, but maybe they are actually an array, or either an array or a string, given their names?
+¥ "Setting the Format for Dates" does not document how to get 1:15 AM, the answer is %1I - strftime has no exact equivalent; the closest is %l.  strftime does not permit numeric prefixes.  It also refers to "NSCalendar" when no such class exists.
+¥ none of many mentions of NSAMPMDesignation indicates that they include the leading spaces (" AM", " PM").  In "Setting the Format for Dates", needs to mention that the leading spaces are not included in %p with strftime.  But if you use the NSCalendarDate stuff, it appears %p doesn't include the space (because it doesn't use the locale dictionary).
+¥ If you feed NSCalendarDate dateWithNaturalLanguageString: an " AM"/" PM" locale, it doesn't accept that date format.
+¥ descriptions for %X and %x are reversed (time zone is in %X, not %x)
+¥ too hard to implement date-only or time-only formatters
+¥ should be able to specify that natural language favors date or time (10 = 10th of month, not 10am)
+¥ please expose the iCal controls!
+
+*/
+
+@interface PSAlarmSetController (Private)
+
+- (void)_stopUpdateTimer;
+
+@end
+
+@implementation PSAlarmSetController
+
+- (void)awakeFromNib;
+{
+    alarm = [[PSAlarm alloc] init];
+    [[self window] center];
+    // XXX excessive retention of formatters?  check later...
+    [timeOfDay setFormatter: [[NJRDateFormatter alloc] initWithDateFormat: [NJRDateFormatter localizedTimeFormatIncludingSeconds: NO] allowNaturalLanguage: YES]];
+    [timeDate setFormatter: [[NJRDateFormatter alloc] initWithDateFormat: [NJRDateFormatter localizedDateFormatIncludingWeekday: NO] allowNaturalLanguage: YES]];
+    {
+        NSArray *dayNames = [[NSUserDefaults standardUserDefaults] arrayForKey:
+            NSWeekDayNameArray];
+        NSArray *completions = [timeDateCompletions itemTitles];
+        NSEnumerator *e = [completions objectEnumerator];
+        NSString *title;
+        int itemIndex = 0;
+        NSRange matchingRange;
+        while ( (title = [e nextObject]) != nil) {
+            matchingRange = [title rangeOfString: @"ÇdayÈ"];
+            if (matchingRange.location != NSNotFound) {
+                NSMutableString *format = [title mutableCopy];
+                NSEnumerator *we = [dayNames objectEnumerator];
+                NSString *dayName;
+                [format deleteCharactersInRange: matchingRange];
+                [format insertString: @"%@" atIndex: matchingRange.location];
+                [timeDateCompletions removeItemAtIndex: itemIndex];
+                while ( (dayName = [we nextObject]) != nil) {
+                    [timeDateCompletions insertItemWithTitle: [NSString stringWithFormat: format, dayName] atIndex: itemIndex];
+                    itemIndex++;
+                }
+            } else itemIndex++;
+        }
+    }
+    [self inAtChanged: nil];
+    [self playSoundChanged: nil];
+    [self doScriptChanged: nil];
+    [self doSpeakChanged: nil];
+    [script setFileTypes: [NSArray arrayWithObjects: @"applescript", @"script", NSFileTypeForHFSTypeCode(kOSAFileType), NSFileTypeForHFSTypeCode('TEXT'), nil]];
+    [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(silence:) name: PSAlarmAlertStopNotification object: nil];
+    [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(playSoundChanged:) name: NJRQTMediaPopUpButtonMovieChangedNotification object: sound];
+    [voice setDelegate: self];
+    [[self window] makeKeyAndOrderFront: nil];
+}
+
+- (void)setStatus:(NSString *)aString;
+{
+    // NSLog(@"%@", alarm);
+    if (aString != status) {
+        [status release]; status = nil;
+        status = [aString retain];
+        [timeSummary setStringValue: status];
+    }
+}
+
+- (id)objectValueForTextField:(NSTextField *)field whileEditing:(id)sender;
+{
+    if (sender == field) {
+        NSString *stringValue = [[[self window] fieldEditor: NO forObject: field] string];
+        id obj = nil;
+        [[field formatter] getObjectValue: &obj forString: stringValue errorDescription: NULL];
+        // NSLog(@"from field editor: %@", obj);
+        return obj;
+    } else {
+        // NSLog(@"from field: %@", [field objectValue]);
+        return [field objectValue];
+    }
+}
+
+- (void)setAlarmDateAndInterval:(id)sender;
+{
+    if (isInterval) {
+        [alarm setInterval:
+            [[self objectValueForTextField: timeInterval whileEditing: sender] intValue] *
+                [timeIntervalUnits selectedTag]];
+    } else {
+        [alarm setForDate: [self objectValueForTextField: timeDate whileEditing: sender]
+                   atTime: [self objectValueForTextField: timeOfDay whileEditing: sender]];
+    }
+}
+
+- (void)_stopUpdateTimer;
+{
+    [updateTimer invalidate]; [updateTimer release]; updateTimer = nil;
+}
+
+// XXX use OACalendar?
+
+- (IBAction)updateDateDisplay:(id)sender;
+{
+    // NSLog(@"updateDateDisplay: %@", sender);
+    if ([alarm isValid]) {
+        [self setStatus: [NSString stringWithFormat: @"Alarm will be set for %@ on %@", [alarm timeString], [alarm dateString]]];
+        [setButton setEnabled: YES];
+        if (updateTimer == nil || ![updateTimer isValid]) {
+            // XXX this logic (and the timer) should really go into PSAlarm, to send notifications for status updates instead.  Timer starts when people are watching, stops when people aren't.
+            // NSLog(@"setting timer");
+            if (isInterval) {
+                updateTimer = [NSTimer scheduledTimerWithTimeInterval: 1 target: self selector: @selector(updateDateDisplay:) userInfo: nil repeats: YES];
+            } else {
+                updateTimer = [NSTimer scheduledTimerWithTimeInterval: [alarm interval] target: self selector: @selector(updateDateDisplay:) userInfo: nil repeats: NO];
+            }
+            [updateTimer retain];
+        }
+    } else {
+        [setButton setEnabled: NO];
+        [self setStatus: [alarm invalidMessage]];
+        [self _stopUpdateTimer];
+    }
+}
+
+// Be careful not to hook up any of the text fields' actions to update: because we handle them in controlTextDidChange: instead.  If we could get the active text field somehow via public API (guess we could use controlTextDidBegin/controlTextDidEndEditing) then we'd not need to overload the update sender for this purpose.  Or, I guess, we could use another method other than update.  It should not be this hard to implement what is essentially standard behavior.  Sigh.
+// Note: finding out whether a given control is editing is easier.  See: <http://cocoa.mamasam.com/COCOADEV/2002/03/2/28501.php>.
+
+- (IBAction)update:(id)sender;
+{
+    // NSLog(@"update: %@", sender);
+    [self setAlarmDateAndInterval: sender];
+    [self updateDateDisplay: sender];
+}
+
+- (IBAction)inAtChanged:(id)sender;
+{
+    NSButtonCell *new = [inAtMatrix selectedCell], *old;
+    isInterval = ([inAtMatrix selectedTag] == 0);
+    old = [inAtMatrix cellWithTag: isInterval];
+    NSAssert(new != old, @"in and at buttons should be distinct!");
+    [old setKeyEquivalent: [new keyEquivalent]];
+    [old setKeyEquivalentModifierMask: [new keyEquivalentModifierMask]];
+    [new setKeyEquivalent: @""];
+    [new setKeyEquivalentModifierMask: 0];
+    [timeInterval setEnabled: isInterval];
+    [timeIntervalUnits setEnabled: isInterval];
+    [timeIntervalRepeats setEnabled: isInterval];
+    [timeOfDay setEnabled: !isInterval];
+    [timeDate setEnabled: !isInterval];
+    [timeDateCompletions setEnabled: !isInterval];
+    if (sender != nil)
+        [[self window] makeFirstResponder: isInterval ? timeInterval : timeOfDay];
+    // NSLog(@"UPDATING FROM inAtChanged");
+    [self update: nil];
+}
+
+- (IBAction)playSoundChanged:(id)sender;
+{
+    BOOL playSoundSelected = [playSound intValue];
+    BOOL canRepeat = playSoundSelected ? [sound canRepeat] : NO;
+    [sound setEnabled: playSoundSelected];
+    [soundRepetitions setEnabled: canRepeat];
+    [soundRepetitionStepper setEnabled: canRepeat];
+    [soundRepetitionsLabel setTextColor: canRepeat ? [NSColor controlTextColor] : [NSColor disabledControlTextColor]];
+    if (playSoundSelected && sender != nil)
+        [[self window] makeFirstResponder: sound];
+}
+
+- (IBAction)setSoundRepetitionCount:(id)sender;
+{
+    NSTextView *fieldEditor = (NSTextView *)[soundRepetitions currentEditor];
+    BOOL isEditing = (fieldEditor != nil);
+    int newReps = [sender intValue], oldReps;
+    if (isEditing) {
+        // XXX work around bug where if you ask soundRepetitions for its intValue too often while it's editing, the field begins to flash
+        oldReps = [[[fieldEditor textStorage] string] intValue];
+    } else oldReps = [soundRepetitions intValue];
+    if (newReps != oldReps) {
+        [soundRepetitions setIntValue: newReps];
+        // NSLog(@"updating: new value %d, old value %d%@", newReps, oldReps, isEditing ? @", is editing" : @"");
+        // XXX work around 10.1 bug, otherwise field only displays every second value
+        if (isEditing) [soundRepetitions selectText: self];
+    }
+}
+
+// XXX should check the 'Do script:' button when someone drops a script on the button
+
+- (IBAction)doScriptChanged:(id)sender;
+{
+    BOOL doScriptSelected = [doScript intValue];
+    [script setEnabled: doScriptSelected];
+    [scriptSelectButton setEnabled: doScriptSelected];
+    if (doScriptSelected && sender != nil)
+        [[self window] makeFirstResponder: scriptSelectButton];
+}
+
+- (IBAction)doSpeakChanged:(id)sender;
+{
+    BOOL doSpeakSelected = [doSpeak intValue];
+    [voice setEnabled: doSpeakSelected];
+    if (doSpeakSelected && sender != nil)
+        [[self window] makeFirstResponder: voice];
+}
+
+- (IBAction)dateCompleted:(NSPopUpButton *)sender;
+{
+    [timeDate setStringValue: [sender titleOfSelectedItem]];
+    [self update: sender];
+}
+
+// to ensure proper updating of interval, this should be the only method by which the window is shown (e.g. from the Alarm menu)
+- (IBAction)showWindow:(id)sender;
+{
+    if (![[self window] isVisible]) {
+        [self update: self];
+        // 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. :(
+        [[self window] makeFirstResponder: [[self window] initialFirstResponder]];
+    }
+    [super showWindow: sender];
+}
+
+- (IBAction)setAlarm:(NSButton *)sender;
+{
+    // set alarm
+    [self setAlarmDateAndInterval: sender];
+    [alarm setMessage: [messageField stringValue]];
+    if (![alarm setTimer]) {
+        [self setStatus: [@"Unable to set alarm.  " stringByAppendingString: [alarm invalidMessage]]];
+        return;
+    }
+
+    [alarm removeAlerts];
+    // dock bounce alert
+    if ([bounceDockIcon state] == NSOnState)
+        [alarm addAlert: [PSDockBounceAlert alert]];
+    // script alert
+    if ([doScript intValue]) {
+        BDAlias *scriptFileAlias = [script alias];
+        if (scriptFileAlias == nil) {
+            [self setStatus: @"Unable to set script alert (no script specified?)"];
+            return;
+        }
+        [alarm addAlert: [PSScriptAlert alertWithScriptFileAlias: scriptFileAlias]];
+    }
+    // notifier alert
+    if ([displayMessage intValue])
+        [alarm addAlert: [PSNotifierAlert alert]];
+    // sound alerts
+    if ([playSound intValue]) {
+        BDAlias *soundAlias = [sound selectedAlias];
+        unsigned short numReps = [soundRepetitions intValue];
+        if (soundAlias == nil) // beep alert
+            [alarm addAlert: [PSBeepAlert alertWithRepetitions: numReps]];
+        else // movie alert
+            [alarm addAlert: [PSMovieAlert alertWithMovieFileAlias: soundAlias repetitions: numReps]];
+    }
+    // speech alert
+    if ([doSpeak intValue])
+        [alarm addAlert: [PSSpeechAlert alertWithVoice: [voice titleOfSelectedItem]]];
+    
+    [self setStatus: [[alarm date] descriptionWithCalendarFormat: @"Alarm set for %x at %X" timeZone: nil locale: nil]];
+    [[self window] close];
+    [alarm release];
+    alarm = [[PSAlarm alloc] init];
+}
+
+- (IBAction)silence:(id)sender;
+{
+    [sound stopSoundPreview: self];
+    [voice stopVoicePreview: self];
+}
+
+@end
+
+@implementation PSAlarmSetController (NSControlSubclassDelegate)
+
+- (void)control:(NSControl *)control didFailToValidatePartialString:(NSString *)string errorDescription:(NSString *)error;
+{
+    unichar c;
+    int tag;
+    unsigned length = [string length];
+    if (control != timeInterval || length == 0) return;
+    c = [string characterAtIndex: length - 1];
+    switch (c) {
+        case 's': case 'S': tag = 1; break;
+        case 'm': case 'M': tag = 60; break;
+        case 'h': case 'H': tag = 60 * 60; break;
+        default: return;
+    }
+    [timeIntervalUnits selectItemAtIndex:
+        [timeIntervalUnits indexOfItemWithTag: tag]];
+    // NSLog(@"UPDATING FROM validation");
+    [self update: timeInterval]; // make sure we still examine the field editor, otherwise if the existing numeric string is invalid, it'll be cleared
+}
+
+@end
+
+@implementation PSAlarmSetController (NSWindowNotifications)
+
+- (void)windowWillClose:(NSNotification *)notification;
+{
+    // NSLog(@"stopping update timer");
+    [self silence: nil];
+    [self _stopUpdateTimer];
+}
+
+@end
+
+@implementation PSAlarmSetController (NSControlSubclassNotifications)
+
+// called because we're the delegate
+
+- (void)controlTextDidChange:(NSNotification *)notification;
+{
+    // NSLog(@"UPDATING FROM controlTextDidChange: %@", [notification object]);
+    [self update: [notification object]];
+}
+
+@end
+
+@implementation PSAlarmSetController (NJRVoicePopUpButtonDelegate)
+
+- (NSString *)voicePopUpButton:(NJRVoicePopUpButton *)sender previewStringForVoice:(NSString *)voice;
+{
+    return [messageField stringValue];
+}
+
+@end
Index: /releases/Pester/1.1a1/Source/PSAlarms.h
===================================================================
--- /releases/Pester/1.1a1/Source/PSAlarms.h	(revision 44)
+++ /releases/Pester/1.1a1/Source/PSAlarms.h	(revision 44)
@@ -0,0 +1,30 @@
+//
+//  PSAlarms.h
+//  Pester
+//
+//  Created by Nicholas Riley on Fri Oct 11 2002.
+//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+
+extern NSString * const PSAlarmsDidChangeNotification;
+extern NSString * const PSAlarmsNextAlarmDidChangeNotification;
+
+@class PSAlarm;
+
+@interface PSAlarms : NSObject {
+    NSMutableArray *alarms;
+    PSAlarm *nextAlarm;
+}
+
++ (void)setUp;
++ (PSAlarms *)allAlarms;
+
+- (PSAlarm *)nextAlarm;
+- (int)alarmCount;
+- (PSAlarm *)alarmAtIndex:(int)index;
+- (void)removeAlarmAtIndex:(int)index;
+- (void)removeAlarmsAtIndices:(NSArray *)indices;
+
+@end
Index: /releases/Pester/1.1a1/Source/PSAlarms.m
===================================================================
--- /releases/Pester/1.1a1/Source/PSAlarms.m	(revision 44)
+++ /releases/Pester/1.1a1/Source/PSAlarms.m	(revision 44)
@@ -0,0 +1,166 @@
+//
+//  PSAlarms.m
+//  Pester
+//
+//  Created by Nicholas Riley on Fri Oct 11 2002.
+//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
+//
+
+#import "PSAlarms.h"
+#import "PSAlarm.h"
+
+NSString * const PSAlarmsDidChangeNotification = @"PSAlarmsDidChangeNotification";
+NSString * const PSAlarmsNextAlarmDidChangeNotification = @"PSAlarmsNextAlarmDidChangeNotification";
+
+static NSString * const PSPendingAlarms = @"Pester pending alarms"; // NSUserDefaults key
+
+static PSAlarms *PSAlarmsAllAlarms = nil;
+
+@interface PSAlarms (Private)
+
+- (void)_updateNextAlarm;
+
+@end
+
+@implementation PSAlarms
+
++ (void)setUp;
+{
+    if (PSAlarmsAllAlarms == nil) {
+        PSAlarmsAllAlarms = [[self alloc] init];
+        [PSAlarmsAllAlarms _updateNextAlarm]; // only generate notifications after singleton established
+    }
+}
+
++ (PSAlarms *)allAlarms;
+{
+    NSAssert(PSAlarmsAllAlarms != nil, @"Attempt to use +[PSAlarms allAlarms] before setup complete");
+    return PSAlarmsAllAlarms;
+}
+
+- (void)_updateNextAlarm;
+{
+    NSEnumerator *e;
+    PSAlarm *alarm, *oldNextAlarm = nextAlarm;
+    [nextAlarm release];
+    nextAlarm = nil;
+    // sort alarms so earliest is first
+    [alarms sortUsingSelector: @selector(compare:)];
+    // find first un-expired alarm
+    e = [alarms objectEnumerator];
+    while ( (alarm = [e nextObject]) != nil) {
+        if ([alarm isValid]) {
+            nextAlarm = [alarm retain];
+            break;
+        }
+    }
+    if (oldNextAlarm != nextAlarm)
+        [[NSNotificationCenter defaultCenter] postNotificationName: PSAlarmsNextAlarmDidChangeNotification object: nextAlarm];
+}
+
+- (id)init;
+{
+    if ( (self = [super init]) != nil) {
+        alarms = [[NSMutableArray alloc] init];
+        NS_DURING
+            NSArray *alarmsData = [[NSUserDefaults standardUserDefaults] arrayForKey: PSPendingAlarms];
+            NSEnumerator *e = [alarmsData objectEnumerator];
+            NSData *alarmData;
+            PSAlarm *alarm;
+            while ( (alarmData = [e nextObject]) != nil) {
+                alarm = [NSUnarchiver unarchiveObjectWithData: alarmData];
+                if (alarm != nil)
+                    [alarms addObject: alarm];
+            }
+        NS_HANDLER
+            // XXX need better error handling here, don't stomp on data
+            NSLog(@"An error occurred while attempting to restore the alarm list: %@", localException);
+        NS_ENDHANDLER
+        [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(_alarmTimerSet:) name: PSAlarmTimerSetNotification object: nil];
+        [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(_alarmTimerExpired:) name: PSAlarmTimerExpiredNotification object: nil];
+    }
+    return self;
+}
+
+- (void)dealloc;
+{
+    [alarms release];
+    [[NSNotificationCenter defaultCenter] removeObserver: self];
+    [super dealloc];
+}
+
+- (void)_changed;
+{
+    NSMutableArray *alarmsData = [[NSMutableArray alloc] init];
+    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+    NSEnumerator *e;
+    PSAlarm *alarm;
+    [self _updateNextAlarm];
+    // NSLog(@"PSAlarms _changed:\n%@", alarms);
+    // archive
+    e = [alarms objectEnumerator];
+    while ( (alarm = [e nextObject]) != nil) {
+        [alarmsData addObject: [NSArchiver archivedDataWithRootObject: alarm]];
+    }
+    [defaults setObject: alarmsData forKey: PSPendingAlarms];
+    [defaults synchronize];
+    [alarmsData release];
+    [[NSNotificationCenter defaultCenter] postNotificationName: PSAlarmsDidChangeNotification object: self];
+}
+
+- (void)_alarmTimerExpired:(NSNotification *)notification;
+{
+    [alarms removeObject: [notification object]];
+    [self _changed];
+}
+
+- (void)_alarmTimerSet:(NSNotification *)notification;
+{
+    [alarms addObject: [notification object]];
+    [self _changed];
+}
+
+- (PSAlarm *)nextAlarm;
+{
+    return nextAlarm;
+}
+
+- (int)alarmCount;
+{
+    return [alarms count];
+}
+
+- (PSAlarm *)alarmAtIndex:(int)index;
+{
+    return [alarms objectAtIndex: index];
+}
+
+- (void)removeAlarmAtIndex:(int)index;
+{
+    [(PSAlarm *)[alarms objectAtIndex: index] cancel];
+    [alarms removeObjectAtIndex: index];
+}
+
+- (void)removeAlarmsAtIndices:(NSArray *)indices;
+{
+    NSEnumerator *e = [indices objectEnumerator];
+    NSNumber *n;
+    int indexCount = [indices count], i = 0, alarmIndex;
+    int *indexArray = (int *)malloc(indexCount * sizeof(int));
+    NS_DURING
+        while ( (n = [e nextObject]) != nil) {
+            alarmIndex = [n intValue];
+            [(PSAlarm *)[alarms objectAtIndex: alarmIndex] cancel];
+            indexArray[i] = alarmIndex;
+            i++;
+        }
+        [alarms removeObjectsFromIndices: indexArray numIndices: indexCount];
+        [self _changed];
+    NS_HANDLER
+        free(indexArray);
+        [self _changed];
+        [localException raise];
+    NS_ENDHANDLER
+}
+
+@end
Index: /releases/Pester/1.1a1/Source/PSAlarmsController.h
===================================================================
--- /releases/Pester/1.1a1/Source/PSAlarmsController.h	(revision 44)
+++ /releases/Pester/1.1a1/Source/PSAlarmsController.h	(revision 44)
@@ -0,0 +1,20 @@
+//
+//  PSAlarmsController.h
+//  Pester
+//
+//  Created by Nicholas Riley on Fri Oct 11 2002.
+//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
+//
+
+#import <AppKit/AppKit.h>
+#import "PSAlarms.h"
+
+@interface PSAlarmsController : NSWindowController {
+    IBOutlet NSTableView *tableView;
+    IBOutlet NSButton *removeButton;
+    PSAlarms *alarms;
+}
+
+- (IBAction)remove:(id)sender;
+
+@end
Index: /releases/Pester/1.1a1/Source/PSAlarmsController.m
===================================================================
--- /releases/Pester/1.1a1/Source/PSAlarmsController.m	(revision 44)
+++ /releases/Pester/1.1a1/Source/PSAlarmsController.m	(revision 44)
@@ -0,0 +1,123 @@
+//
+//  PSAlarmsController.m
+//  Pester
+//
+//  Created by Nicholas Riley on Fri Oct 11 2002.
+//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
+//
+
+#import "PSAlarmsController.h"
+#import "PSAlarm.h"
+#import "NSTableView-NJRExtensions.h"
+
+
+@implementation PSAlarmsController
+
+- (id)init;
+{
+    if ( (self = [super initWithWindowNibName: @"Alarms"]) != nil) {
+        alarms = [PSAlarms allAlarms];
+        // XXX workaround for bug in 10.2.1, 10.1.5: autosave name set in IB doesn't show up
+        [self setWindowFrameAutosaveName: @"Pester alarm list"];
+        // Apple documents the NSUserDefaults key, so we can rely on it hopefully.
+        if (nil == [[NSUserDefaults standardUserDefaults] objectForKey:
+            [@"NSWindow Frame " stringByAppendingString: [[self window] frameAutosaveName]]])
+        {
+            [[self window] center];
+        }
+        [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(alarmsChanged) name: PSAlarmsDidChangeNotification object: alarms];
+        [tableView setAutosaveName: @"Alarm list"];
+        [tableView setAutosaveTableColumns: YES];
+        [tableView noteNumberOfRowsChanged];
+        [[self window] makeFirstResponder: tableView];
+    }
+    return self;
+}
+
+- (void)alarmsChanged; // XXX fix autoselection to be more reasonable, see whatever I did in that _Learning Cocoa_ project I think
+{
+    [tableView reloadData];
+    [tableView deselectAll: self];
+}
+
+- (IBAction)remove:(id)sender;
+{
+    [alarms removeAlarmsAtIndices: [[tableView selectedRowEnumerator] allObjects]];
+}
+
+@end
+
+@implementation PSAlarmsController (NSTableDataSource)
+
+- (int)numberOfRowsInTableView:(NSTableView *)tableView;
+{
+    return [alarms alarmCount];
+}
+
+- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(int)row;
+{
+    PSAlarm *alarm = [alarms alarmAtIndex: row];
+
+    if ([[tableColumn identifier] isEqualToString: @"message"]) return [alarm message];
+    else {
+        NSCalendarDate *date = [alarm date];
+        if ([[tableColumn identifier] isEqualToString: @"date"]) return [alarm shortDateString];
+        if ([[tableColumn identifier] isEqualToString: @"time"]) {
+            if (date == nil) return @"ÇexpiredÈ";
+            return [alarm timeString];
+        }
+    }
+    return nil;
+}
+@end
+
+@implementation PSAlarmsController (NSTableViewNotifications)
+
+- (void)tableViewSelectionDidChange:(NSNotification *)aNotification;
+{
+    [removeButton setEnabled: ([tableView numberOfSelectedRows] != 0)];
+}
+
+@end
+
+@implementation PSAlarmsController (NSWindowDelegate)
+
+// XXX workaround for bug in 10.1.5, 10.2.1 (and earlier?): no autosave on window move
+- (void)windowDidMove:(NSNotification *)aNotification
+{
+    NSString *autosaveName = [[self window] frameAutosaveName];
+    // on initial display, we get a notification inside -[NSWindow setFrameAutosaveName]!
+    if (autosaveName != nil) {
+        [[self window] saveFrameUsingName: autosaveName];
+    }
+}
+
+- (NSRect)windowWillUseStandardFrame:(NSWindow *)sender defaultFrame:(NSRect)defaultFrame;
+{
+    NSWindow *window = [tableView window];
+    NSRect frame = [window frame];
+    NSScrollView *scrollView = [tableView enclosingScrollView];
+    float displayedHeight = [[scrollView contentView] bounds].size.height;
+    float heightChange = [[scrollView documentView] bounds].size.height - displayedHeight;
+    float heightExcess;
+
+    if (heightChange >= 0 && heightChange <= 1) {
+        // either the window is already optimal size, or it's too big
+        float rowHeight = [tableView cellHeight];
+        heightChange = (rowHeight * [tableView numberOfRows]) - displayedHeight;
+    }
+
+    frame.size.height += heightChange;
+
+    if ( (heightExcess = [window minSize].height - frame.size.height) > 1 ||
+         (heightExcess = [window maxSize].height - frame.size.height) < 1) {
+        heightChange += heightExcess;
+        frame.size.height += heightExcess;
+    }
+
+    frame.origin.y -= heightChange;
+
+    return frame;
+}
+
+@end
Index: /releases/Pester/1.1a1/Source/PSAlert.h
===================================================================
--- /releases/Pester/1.1a1/Source/PSAlert.h	(revision 44)
+++ /releases/Pester/1.1a1/Source/PSAlert.h	(revision 44)
@@ -0,0 +1,22 @@
+//
+//  PSAlert.h
+//  Pester
+//
+//  Created by Nicholas Riley on Sat Oct 26 2002.
+//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "PSAlarm.h"
+
+@interface PSAlert : NSObject {
+
+}
+
+// XXX need archiving support
+
+// subclasses should implement these methods
++ (PSAlert *)alert;
+- (void)triggerForAlarm:(PSAlarm *)alarm;
+
+@end
Index: /releases/Pester/1.1a1/Source/PSAlert.m
===================================================================
--- /releases/Pester/1.1a1/Source/PSAlert.m	(revision 44)
+++ /releases/Pester/1.1a1/Source/PSAlert.m	(revision 44)
@@ -0,0 +1,25 @@
+//
+//  PSAlert.m
+//  Pester
+//
+//  Created by Nicholas Riley on Sat Oct 26 2002.
+//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
+//
+
+#import "PSAlert.h"
+
+
+@implementation PSAlert
+
++ (PSAlert *)alert;
+{
+    NSAssert(NO, @"Class is abstract");
+    return nil;
+}
+
+- (void)triggerForAlarm:(PSAlarm *)alarm;
+{
+    NSAssert(NO, @"Class is abstract");
+}
+
+@end
Index: /releases/Pester/1.1a1/Source/PSApplication.h
===================================================================
--- /releases/Pester/1.1a1/Source/PSApplication.h	(revision 44)
+++ /releases/Pester/1.1a1/Source/PSApplication.h	(revision 44)
@@ -0,0 +1,26 @@
+//
+//  PSApplication.h
+//  Pester
+//
+//  Created by Nicholas Riley on Fri Oct 11 2002.
+//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
+//
+
+#import <AppKit/AppKit.h>
+
+@class PSAlarmsController;
+@class PSAlarmSetController;
+
+@interface PSApplication : NSApplication {
+    PSAlarmsController *alarmsController;
+    IBOutlet PSAlarmSetController *alarmSetController;
+    NSTimer *dockUpdateTimer;
+    NSTimeInterval dockUpdateInterval;
+    NSImage *appIconImage;
+}
+
+- (IBAction)orderFrontSetAlarmPanel:(id)sender;
+- (IBAction)orderFrontAlarmsPanel:(id)sender;
+- (IBAction)stopAlerts:(id)sender;
+
+@end
Index: /releases/Pester/1.1a1/Source/PSApplication.m
===================================================================
--- /releases/Pester/1.1a1/Source/PSApplication.m	(revision 44)
+++ /releases/Pester/1.1a1/Source/PSApplication.m	(revision 44)
@@ -0,0 +1,189 @@
+//
+//  PSApplication.m
+//  Pester
+//
+//  Created by Nicholas Riley on Fri Oct 11 2002.
+//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
+//
+
+#import "PSApplication.h"
+#import "PSAlarmSetController.h"
+#import "PSAlarmAlertController.h"
+#import "PSAlarmsController.h"
+#import "PSAlarm.h"
+#import "PSAlarms.h"
+
+@implementation PSApplication
+
+- (void)finishLaunching;
+{
+    appIconImage = [[NSImage imageNamed: @"NSApplicationIcon"] retain];
+    [[NSNotificationCenter defaultCenter] addObserver: [PSAlarmAlertController class] selector: @selector(controllerWithTimerExpiredNotification:) name: PSAlarmTimerExpiredNotification object: nil];
+    [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(nextAlarmDidChange:) name: PSAlarmsNextAlarmDidChangeNotification object: nil];
+    [PSAlarms setUp];
+    [self setDelegate: self];
+    [super finishLaunching];
+}
+
+- (IBAction)showHelp:(id)sender;
+{
+    [[NSWorkspace sharedWorkspace] openFile: [[NSBundle mainBundle] pathForResource: @"Read Me" ofType: @"rtfd"]];
+}
+
+- (IBAction)stopAlerts:(id)sender;
+{
+    [PSAlarmAlertController stopAlerts: sender];
+}	
+
+- (IBAction)orderFrontSetAlarmPanel:(id)sender;
+{
+    [NSApp activateIgnoringOtherApps: YES];
+    [alarmSetController showWindow: self];
+}
+
+- (IBAction)orderFrontAlarmsPanel:(id)sender;
+{
+    [NSApp activateIgnoringOtherApps: YES];
+    if (alarmsController == nil) {
+        alarmsController = [[PSAlarmsController alloc] init];
+    }
+    [alarmsController showWindow: self];
+}
+
+- (void)_resetUpdateTimer;
+{
+    if (dockUpdateTimer != nil) {
+        [dockUpdateTimer invalidate];
+        [dockUpdateTimer release];
+        dockUpdateInterval = 0;
+        dockUpdateTimer = nil;
+    }
+}
+
+- (void)_setUpdateTimerForInterval:(NSTimeInterval)interval alarm:(PSAlarm *)alarm repeats:(BOOL)repeats;
+{
+    dockUpdateTimer = [NSTimer scheduledTimerWithTimeInterval: interval target: self selector: @selector(_updateDockTile:) userInfo: alarm repeats: repeats];
+    [dockUpdateTimer retain];
+    dockUpdateInterval = interval; // because [timer timeInterval] always returns 0 once set
+}
+
+- (void)_updateDockTile:(NSTimer *)timer;
+{
+    PSAlarm *alarm = [timer userInfo];
+    NSTimeInterval alarmInterval;
+    NSString *tileString;
+    if (timer == nil) alarm = [[PSAlarms allAlarms] nextAlarm];
+    if (alarm == nil) return;
+    tileString = [alarm timeRemainingString];
+    {
+        NSMutableDictionary *atts = [NSMutableDictionary dictionary];
+        NSSize imageSize = [appIconImage size];
+        NSImage *tile = [[NSImage alloc] initWithSize: imageSize];
+        NSSize textSize;
+        NSPoint textOrigin;
+        NSRect frameRect;
+        float fontSize = 37;
+        
+        do {
+            fontSize -= 1;
+            [atts setObject: [NSFont boldSystemFontOfSize: fontSize] forKey: NSFontAttributeName];
+            textSize = [tileString sizeWithAttributes: atts];
+        } while (textSize.width > imageSize.width - 8);
+
+        textOrigin = NSMakePoint(imageSize.width / 2 - textSize.width / 2,
+                                 imageSize.height / 2 - textSize.height / 2);
+        frameRect = NSInsetRect(NSMakeRect(textOrigin.x, textOrigin.y, textSize.width, textSize.height),
+                                -4, -2);
+        
+        [tile lockFocus];
+        // draw the grayed-out app icon
+        [appIconImage dissolveToPoint: NSZeroPoint fraction: 0.5];
+        // draw the frame
+        [[NSColor colorWithCalibratedWhite: 0.1 alpha: 0.5] set];
+        NSRectFill(frameRect);
+        // draw a gray two-pixel text shadow
+        [atts setObject: [NSColor grayColor] forKey: NSForegroundColorAttributeName];
+        textOrigin.x++; textOrigin.y--;
+        [tileString drawAtPoint: textOrigin withAttributes: atts];
+        textOrigin.x++; textOrigin.y--;
+        [tileString drawAtPoint: textOrigin withAttributes: atts];
+        // draw white text
+        textOrigin.x -= 2; textOrigin.y += 2;
+        [atts setObject: [NSColor whiteColor] forKey: NSForegroundColorAttributeName];
+        [tileString drawAtPoint: textOrigin withAttributes: atts];
+        
+        [tile unlockFocus];
+        [NSApp setApplicationIconImage: tile];
+        [tile release];
+    }
+    alarmInterval = [alarm interval];
+    // NSLog(@"_updateDockTile > time remaining %@ (%.0lf), last time interval %.0lf", tileString, alarmInterval, dockUpdateInterval);
+    if (alarmInterval > 61) {
+        NSTimeInterval nextUpdate = ((unsigned long long)alarmInterval) % 60;
+        if (nextUpdate <= 1) nextUpdate = 60;
+        [self _resetUpdateTimer];
+        [self _setUpdateTimerForInterval: nextUpdate alarm: alarm repeats: NO];
+        // NSLog(@"_updateDockTile > set timer for %.0lf seconds", nextUpdate);
+    } else if (timer == nil || dockUpdateInterval > 1) {
+        [self _resetUpdateTimer];
+        [self _setUpdateTimerForInterval: 1 alarm: alarm repeats: YES];
+        // NSLog(@"_updateDockTile > set timer for 1 second");
+    } else if (alarmInterval < 2) {
+        [self _resetUpdateTimer];
+    }
+}
+
+- (void)nextAlarmDidChange:(NSNotification *)notification;
+{
+    PSAlarm *nextAlarm = [notification object];
+    // NSLog(@"nextAlarmDidChange: %@", nextAlarm);
+    [self _resetUpdateTimer];
+    if (nextAlarm == nil) {
+        [NSApp setApplicationIconImage: appIconImage];
+    } else {
+        [self _updateDockTile: nil];
+    }
+}
+
+@end
+
+@implementation PSApplication (NSApplicationDelegate)
+
+- (BOOL)applicationShouldHandleReopen:(NSApplication *)sender hasVisibleWindows:(BOOL)flag;
+{
+    if (!flag) [alarmSetController showWindow: self];
+    return YES;
+}
+
+- (NSMenu *)applicationDockMenu:(NSApplication *)sender;
+{
+    NSMenu *dockMenu = [[NSMenu alloc] initWithTitle: @""];
+    PSAlarms *alarms = [PSAlarms allAlarms];
+    PSAlarm *nextAlarm = [alarms nextAlarm];
+    NSMenuItem *item;
+    if (nextAlarm == nil) {
+        [dockMenu addItemWithTitle: @"No Pending Alarms" action: nil keyEquivalent: @""];
+    } else {
+        [dockMenu addItemWithTitle: @"Next Alarm" action: nil keyEquivalent: @""];
+        [dockMenu addItemWithTitle: [NSString stringWithFormat: @"   %@", [nextAlarm message]] action: nil keyEquivalent: @""];
+        [dockMenu addItemWithTitle: [NSString stringWithFormat: @"   %@ %@", [nextAlarm shortDateString], [nextAlarm timeString]] action: nil keyEquivalent: @""];
+        [dockMenu addItemWithTitle: [NSString stringWithFormat: @"   Remaining: %@", [nextAlarm timeRemainingString]] action: nil keyEquivalent: @""];
+    }
+    [dockMenu addItem: [NSMenuItem separatorItem]];
+    item = [dockMenu addItemWithTitle: @"Set AlarmÉ" action: @selector(orderFrontSetAlarmPanel:) keyEquivalent: @""];
+    [item setTarget: self];
+    item = [dockMenu addItemWithTitle: [NSString stringWithFormat: @"All Alarms (%d)É", [alarms alarmCount]] action: @selector(orderFrontAlarmsPanel:) keyEquivalent: @""];
+    [item setTarget: self];
+    return [dockMenu autorelease];
+}
+
+@end
+
+@implementation PSApplication (NSApplicationNotifications)
+
+- (void)applicationWillTerminate:(NSNotification *)notification;
+{
+    [NSApp setApplicationIconImage: appIconImage];
+}
+
+@end
Index: /releases/Pester/1.1a1/Source/PSBeepAlert.h
===================================================================
--- /releases/Pester/1.1a1/Source/PSBeepAlert.h	(revision 44)
+++ /releases/Pester/1.1a1/Source/PSBeepAlert.h	(revision 44)
@@ -0,0 +1,20 @@
+//
+//  PSBeepAlert.h
+//  Pester
+//
+//  Created by Nicholas Riley on Sat Oct 26 2002.
+//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
+//
+
+#import "PSAlert.h"
+
+
+@interface PSBeepAlert : PSAlert {
+    unsigned short repetitions;
+    unsigned short repetitionsRemaining;    
+}
+
++ (PSBeepAlert *)alertWithRepetitions:(unsigned short)numReps;
+- (id)initWithRepetitions:(unsigned short)numReps;
+
+@end
Index: /releases/Pester/1.1a1/Source/PSBeepAlert.m
===================================================================
--- /releases/Pester/1.1a1/Source/PSBeepAlert.m	(revision 44)
+++ /releases/Pester/1.1a1/Source/PSBeepAlert.m	(revision 44)
@@ -0,0 +1,58 @@
+//
+//  PSBeepAlert.m
+//  Pester
+//
+//  Created by Nicholas Riley on Sat Oct 26 2002.
+//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
+//
+
+#import <AppKit/AppKit.h>
+#import "PSBeepAlert.h"
+#import "PSAlarmAlertController.h"
+
+@implementation PSBeepAlert
+
++ (PSBeepAlert *)alertWithRepetitions:(unsigned short)numReps;
+{
+    return [[self alloc] initWithRepetitions: numReps];
+}
+
+- (id)initWithRepetitions:(unsigned short)numReps;
+{
+    if ( (self = [super init]) != nil) {
+        repetitions = numReps;
+    }
+    return self;
+}
+
+- (void)dealloc;
+{
+    [[NSNotificationCenter defaultCenter] removeObserver: self];
+    [super dealloc];
+}
+
+- (void)beep;
+{
+    NSBeep();
+    repetitionsRemaining--;
+    if (repetitionsRemaining == 0) {
+        [self release];
+        return;
+    }
+    [self performSelector: @selector(beep) withObject: nil afterDelay: 0.15 inModes: [NSArray arrayWithObject: NSDefaultRunLoopMode]];
+}
+
+- (void)_stopBeeping:(NSNotification *)notification;
+{
+    repetitionsRemaining = 1;
+}
+
+- (void)triggerForAlarm:(PSAlarm *)alarm;
+{
+    repetitionsRemaining = repetitions;
+    [self retain];
+    [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(_stopBeeping:) name: PSAlarmAlertStopNotification object: nil];
+    [self beep];
+}
+
+@end
Index: /releases/Pester/1.1a1/Source/PSDockBounceAlert.h
===================================================================
--- /releases/Pester/1.1a1/Source/PSDockBounceAlert.h	(revision 44)
+++ /releases/Pester/1.1a1/Source/PSDockBounceAlert.h	(revision 44)
@@ -0,0 +1,16 @@
+//
+//  PSDockBounceAlert.h
+//  Pester
+//
+//  Created by Nicholas Riley on Sat Oct 26 2002.
+//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
+//
+
+#import "PSAlert.h"
+
+
+@interface PSDockBounceAlert : PSAlert {
+
+}
+
+@end
Index: /releases/Pester/1.1a1/Source/PSDockBounceAlert.m
===================================================================
--- /releases/Pester/1.1a1/Source/PSDockBounceAlert.m	(revision 44)
+++ /releases/Pester/1.1a1/Source/PSDockBounceAlert.m	(revision 44)
@@ -0,0 +1,34 @@
+//
+//  PSDockBounceAlert.m
+//  Pester
+//
+//  Created by Nicholas Riley on Sat Oct 26 2002.
+//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
+//
+
+#import <AppKit/AppKit.h>
+#import "PSDockBounceAlert.h"
+
+static PSDockBounceAlert *PSDockBounceAlertShared;
+
+@implementation PSDockBounceAlert
+
++ (PSAlert *)alert;
+{
+    if (PSDockBounceAlertShared == nil)
+        PSDockBounceAlertShared = [[PSDockBounceAlert alloc] init];
+    return PSDockBounceAlertShared;
+}
+
++ (void)stopBouncing;
+{
+    [NSApp cancelUserAttentionRequest: NSInformationalRequest];
+}
+
+- (void)triggerForAlarm:(PSAlarm *)alarm;
+{
+    [NSApp requestUserAttention: NSInformationalRequest];
+    [[self class] performSelector: @selector(stopBouncing) withObject: nil afterDelay: 1 inModes: [NSArray arrayWithObject: NSDefaultRunLoopMode]];
+}
+
+@end
Index: /releases/Pester/1.1a1/Source/PSMovieAlert.h
===================================================================
--- /releases/Pester/1.1a1/Source/PSMovieAlert.h	(revision 44)
+++ /releases/Pester/1.1a1/Source/PSMovieAlert.h	(revision 44)
@@ -0,0 +1,30 @@
+//
+//  PSMovieAlert.h
+//  Pester
+//
+//  Created by Nicholas Riley on Sat Oct 26 2002.
+//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
+//
+
+#import <AppKit/AppKit.h>
+#import "PSAlert.h"
+#import "BDAlias.h"
+
+
+@interface PSMovieAlert : PSAlert {
+    BDAlias *alias;
+    NSMovie *movie;
+    unsigned short repetitions;
+    BOOL hasAudio;
+    BOOL hasVideo;
+}
+
++ (PSMovieAlert *)alertWithMovieFileAlias:(BDAlias *)anAlias repetitions:(unsigned short)numReps;
+
+- (id)initWithAlias:(BDAlias *)anAlias repetitions:(unsigned int) numReps;
+
+- (BOOL)hasVideo;
+- (NSMovie *)movie;
+- (unsigned short)repetitions;
+
+@end
Index: /releases/Pester/1.1a1/Source/PSMovieAlert.m
===================================================================
--- /releases/Pester/1.1a1/Source/PSMovieAlert.m	(revision 44)
+++ /releases/Pester/1.1a1/Source/PSMovieAlert.m	(revision 44)
@@ -0,0 +1,76 @@
+//
+//  PSMovieAlert.m
+//  Pester
+//
+//  Created by Nicholas Riley on Sat Oct 26 2002.
+//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
+//
+
+#import <QuickTime/Movies.h>
+#import "PSMovieAlert.h"
+#import "PSMovieAlertController.h"
+#import "NSMovie-NJRExtensions.h"
+
+@implementation PSMovieAlert
+
++ (PSMovieAlert *)alertWithMovieFileAlias:(BDAlias *)anAlias repetitions:(unsigned short)numReps;
+{
+    return [[[self alloc] initWithAlias: anAlias repetitions: numReps] autorelease];
+}
+
+- (id)initWithAlias:(BDAlias *)anAlias repetitions:(unsigned int) numReps;
+{
+    if ( (self = [super init]) != nil) {
+        alias = [anAlias retain];
+        repetitions = numReps;
+        movie = [[NSMovie alloc] initWithURL: [NSURL fileURLWithPath: [anAlias fullPath]] byReference: YES];
+        if (movie == nil) {
+            [self release];
+            self = nil;
+        } else {
+            hasAudio = [movie hasAudio];
+            hasVideo = [movie hasVideo];
+            
+            if (!hasAudio && !hasVideo) {
+                [self release]; self = nil;
+            }
+        }
+    }
+    
+    return self;
+}
+
+- (BOOL)hasVideo;
+{
+    return hasVideo;
+}
+
+- (NSMovie *)movie;
+{
+    return movie;
+}
+
+- (unsigned short)repetitions;
+{
+    return repetitions;
+}
+
+- (void)dealloc;
+{
+    [alias release];
+    [movie release];
+    [super dealloc];
+}
+
+- (NSString *)description;
+{
+    return [NSString stringWithFormat: @"PSMovieAlert (%@%@): %@, repeats %hu times", hasAudio ? @"A" : @"", hasVideo ? @"V" : @"", [alias fullPath], repetitions];
+}
+
+- (void)triggerForAlarm:(PSAlarm *)alarm;
+{
+    NSLog(@"%@", self);
+    [PSMovieAlertController controllerWithAlarm: alarm movieAlert: self];
+}
+
+@end
Index: /releases/Pester/1.1a1/Source/PSMovieAlertController.h
===================================================================
--- /releases/Pester/1.1a1/Source/PSMovieAlertController.h	(revision 44)
+++ /releases/Pester/1.1a1/Source/PSMovieAlertController.h	(revision 44)
@@ -0,0 +1,25 @@
+//
+//  PSMovieAlertController.h
+//  Pester
+//
+//  Created by Nicholas Riley on Sat Oct 26 2002.
+//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
+//
+
+#import <AppKit/AppKit.h>
+
+@class PSAlarm;
+@class PSMovieAlert;
+
+@interface PSMovieAlertController : NSWindowController {
+    IBOutlet NSMovieView *movieView;
+    void *theMovie; /* Movie */
+    unsigned short repetitions;
+    unsigned short repetitionsRemaining;    
+}
+
++ (PSMovieAlertController *)controllerWithAlarm:(PSAlarm *)alarm movieAlert:(PSMovieAlert *)alert;
+
+- (id)initWithAlarm:(PSAlarm *)alarm movieAlert:(PSMovieAlert *)alert;
+
+@end
Index: /releases/Pester/1.1a1/Source/PSMovieAlertController.m
===================================================================
--- /releases/Pester/1.1a1/Source/PSMovieAlertController.m	(revision 44)
+++ /releases/Pester/1.1a1/Source/PSMovieAlertController.m	(revision 44)
@@ -0,0 +1,97 @@
+//
+//  PSMovieAlertController.m
+//  Pester
+//
+//  Created by Nicholas Riley on Sat Oct 26 2002.
+//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
+//
+
+#import "PSAlarmAlertController.h"
+#import "PSMovieAlertController.h"
+#import "PSMovieAlert.h"
+#import "NSMovie-NJRExtensions.h"
+#import <QuickTime/Movies.h>
+
+// XXX if you specify a truly tiny movie, obey the minimum window size to compensate
+
+@implementation PSMovieAlertController
+
++ (PSMovieAlertController *)controllerWithAlarm:(PSAlarm *)alarm movieAlert:(PSMovieAlert *)alert;
+{
+    return [[self alloc] initWithAlarm: alarm movieAlert: alert];
+}
+
+- (void)play;
+{
+    NSTimeInterval delay;
+    if (repetitions == 0) return;
+    if (IsMovieDone((Movie)theMovie) || repetitionsRemaining == repetitions) {
+        if (repetitionsRemaining == 0) {
+            [self close];
+            return;
+        }
+        repetitionsRemaining--;
+        [movieView gotoBeginning: self];
+        [movieView start: self];
+    }
+    delay = (GetMovieDuration((Movie)theMovie) - GetMovieTime((Movie)theMovie, NULL)) / (double)GetMovieTimeScale((Movie)theMovie);
+    [self performSelector: @selector(play) withObject: nil afterDelay: delay inModes: [NSArray arrayWithObject: NSDefaultRunLoopMode]];
+}
+
+- (id)initWithAlarm:(PSAlarm *)alarm movieAlert:(PSMovieAlert *)alert;
+{
+    if ([self initWithWindowNibName: @"Movie alert"]) {
+        NSMovie *movie = [alert movie];
+        NSWindow *window = [self window]; // connect outlets
+        [movieView setMovie: movie];
+        theMovie = [movie QTMovie];
+        if ([alert hasVideo]) {
+            NSRect screenRect = [[window screen] visibleFrame];
+            float magnification = 1.0;
+            NSSize movieSize;
+            NSRect frame;
+            screenRect.size.height -= [window frame].size.height - [[window contentView] frame].size.height; // account for height of window frame
+            while (1) {
+                movieSize = [movieView sizeForMagnification: magnification];
+                movieSize.height -= 16; // controller is hidden, but its size is included (documented, ergh)
+                if (movieSize.width > screenRect.size.width || movieSize.height > screenRect.size.height)
+                    magnification /= 2;
+                else
+                    break;
+            }
+            [window setContentSize: movieSize];
+            [window center];
+            frame = [window frame];
+            frame.origin.y -= 250; // appear below notifier window
+            if (frame.origin.y < screenRect.origin.y) frame.origin.y = screenRect.origin.y;
+            [window setFrame: frame display: NO];
+            [window setTitle: [alarm message]];
+            [[self window] orderFrontRegardless];
+        }
+        [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(close) name: PSAlarmAlertStopNotification object: nil];
+        repetitions = [alert repetitions];
+        repetitionsRemaining = repetitions;
+        if (![movie isStatic]) [self play]; // if it's an image, don't close the window automatically
+    }
+    return self;
+}
+
+- (void)dealloc;
+{
+    [[NSNotificationCenter defaultCenter] removeObserver: self];
+    [super dealloc];
+}
+
+@end
+
+@implementation PSMovieAlertController (NSWindowNotifications)
+
+- (void)windowWillClose:(NSNotification *)notification;
+{
+    repetitions = 0;
+    [movieView stop: self];
+    [self release];
+    // 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.
+}
+
+@end
Index: /releases/Pester/1.1a1/Source/PSNotifierAlert.h
===================================================================
--- /releases/Pester/1.1a1/Source/PSNotifierAlert.h	(revision 44)
+++ /releases/Pester/1.1a1/Source/PSNotifierAlert.h	(revision 44)
@@ -0,0 +1,16 @@
+//
+//  PSNotifierAlert.h
+//  Pester
+//
+//  Created by Nicholas Riley on Sat Oct 26 2002.
+//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
+//
+
+#import "PSAlert.h"
+
+
+@interface PSNotifierAlert : PSAlert {
+
+}
+
+@end
Index: /releases/Pester/1.1a1/Source/PSNotifierAlert.m
===================================================================
--- /releases/Pester/1.1a1/Source/PSNotifierAlert.m	(revision 44)
+++ /releases/Pester/1.1a1/Source/PSNotifierAlert.m	(revision 44)
@@ -0,0 +1,28 @@
+//
+//  PSNotifierAlert.m
+//  Pester
+//
+//  Created by Nicholas Riley on Sat Oct 26 2002.
+//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
+//
+
+#import "PSNotifierAlert.h"
+#import "PSAlarmNotifierController.h"
+
+static PSNotifierAlert *PSNotifierAlertShared;
+
+@implementation PSNotifierAlert
+
++ (PSAlert *)alert;
+{
+    if (PSNotifierAlertShared == nil)
+        PSNotifierAlertShared = [[PSNotifierAlert alloc] init];
+    return PSNotifierAlertShared;
+}
+
+- (void)triggerForAlarm:(PSAlarm *)alarm;
+{
+    [[PSAlarmNotifierController alloc] initWithAlarm: alarm];
+}
+
+@end
Index: /releases/Pester/1.1a1/Source/PSScriptAlert.h
===================================================================
--- /releases/Pester/1.1a1/Source/PSScriptAlert.h	(revision 44)
+++ /releases/Pester/1.1a1/Source/PSScriptAlert.h	(revision 44)
@@ -0,0 +1,20 @@
+//
+//  PSScriptAlert.h
+//  Pester
+//
+//  Created by Nicholas Riley on Sat Oct 26 2002.
+//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
+//
+
+#import "PSAlert.h"
+
+@class BDAlias;
+
+@interface PSScriptAlert : PSAlert {
+    BDAlias *alias;
+}
+
++ (PSScriptAlert *)alertWithScriptFileAlias:(BDAlias *)anAlias;
+- (id)initWithScriptFileAlias:(BDAlias *)anAlias;
+
+@end
Index: /releases/Pester/1.1a1/Source/PSScriptAlert.m
===================================================================
--- /releases/Pester/1.1a1/Source/PSScriptAlert.m	(revision 44)
+++ /releases/Pester/1.1a1/Source/PSScriptAlert.m	(revision 44)
@@ -0,0 +1,36 @@
+//
+//  PSScriptAlert.m
+//  Pester
+//
+//  Created by Nicholas Riley on Sat Oct 26 2002.
+//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
+//
+
+#import "PSScriptAlert.h"
+#import "BDAlias.h"
+#import "NDAppleScriptObject.h"
+
+@implementation PSScriptAlert
+
++ (PSScriptAlert *)alertWithScriptFileAlias:(BDAlias *)anAlias;
+{
+    return [[[self alloc] initWithScriptFileAlias: anAlias] autorelease];
+}
+
+- (id)initWithScriptFileAlias:(BDAlias *)anAlias;
+{
+    if ( (self = [super init]) != nil) {
+        alias = [anAlias retain];
+    }
+    return self;
+}
+
+- (void)triggerForAlarm:(PSAlarm *)alarm;
+{
+    NDAppleScriptObject *as = [NDAppleScriptObject appleScriptObjectWithContentsOfFile: [alias fullPath]];
+
+    if (as != nil) {
+        [as execute];
+    }
+}
+@end
Index: /releases/Pester/1.1a1/Source/PSSpeechAlert.h
===================================================================
--- /releases/Pester/1.1a1/Source/PSSpeechAlert.h	(revision 44)
+++ /releases/Pester/1.1a1/Source/PSSpeechAlert.h	(revision 44)
@@ -0,0 +1,21 @@
+//
+//  PSSpeechAlert.h
+//  Pester
+//
+//  Created by Nicholas Riley on Sat Oct 26 2002.
+//  Copyright (c) 2002 __MyCompanyName__. All rights reserved.
+//
+
+#import "PSAlert.h"
+#import "SUSpeaker.h"
+
+@interface PSSpeechAlert : PSAlert {
+    SUSpeaker *speaker;
+    NSString *voice;
+}
+
++ (PSSpeechAlert *)alertWithVoice:(NSString *)aVoice;
+
+- (id)initWithVoice:(NSString *)aVoice;
+
+@end
Index: /releases/Pester/1.1a1/Source/PSSpeechAlert.m
===================================================================
--- /releases/Pester/1.1a1/Source/PSSpeechAlert.m	(revision 44)
+++ /releases/Pester/1.1a1/Source/PSSpeechAlert.m	(revision 44)
@@ -0,0 +1,62 @@
+//
+//  PSSpeechAlert.m
+//  Pester
+//
+//  Created by Nicholas Riley on Sat Oct 26 2002.
+//  Copyright (c) 2002 __MyCompanyName__. All rights reserved.
+//
+
+#import "PSSpeechAlert.h"
+#import "PSAlarmAlertController.h"
+
+@implementation PSSpeechAlert
+
++ (PSSpeechAlert *)alertWithVoice:(NSString *)aVoice;
+{
+    return [[[self alloc] initWithVoice: aVoice] autorelease];
+}
+
+- (id)initWithVoice:(NSString *)aVoice;
+{
+    if ( (self = [super init]) != nil) {
+        voice = aVoice;
+    }
+    return self;
+}
+
+- (void)dealloc;
+{
+    [[NSNotificationCenter defaultCenter] removeObserver: self];
+    [speaker release]; speaker = nil;
+    [voice release]; voice = nil;
+    [super dealloc];
+}
+
+- (void)_stopSpeaking:(NSNotification *)notification;
+{
+    [speaker stopSpeaking];
+    // don't release here, we'll still get the didFinishSpeaking: message as a delegate
+}
+
+- (void)triggerForAlarm:(PSAlarm *)alarm;
+{
+    [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(_stopSpeaking:) name: PSAlarmAlertStopNotification object: nil];
+    
+    if ( (speaker = [[SUSpeaker alloc] init]) == nil) return;
+    [speaker setDelegate: self];
+    [speaker setVoice: [[SUSpeaker voiceNames] indexOfObject: voice] + 1];
+    [speaker speakText: [alarm message]];
+    
+    [self retain];
+}
+
+@end
+
+@implementation PSSpeechAlert (SUSpeakerDelegate)
+
+- (void)didFinishSpeaking:(SUSpeaker*)speaker;
+{
+    [self release];
+}
+
+@end
Index: /releases/Pester/1.1a1/Source/Pester.pbproj/nicholas.pbxuser
===================================================================
--- /releases/Pester/1.1a1/Source/Pester.pbproj/nicholas.pbxuser	(revision 44)
+++ /releases/Pester/1.1a1/Source/Pester.pbproj/nicholas.pbxuser	(revision 44)
@@ -0,0 +1,3621 @@
+// !$*UTF8*$!
+{
+	29B97313FDCFA39411CA2CEA = {
+		activeBuildStyle = 4A9504CDFFE6A4B311CA0CBA;
+		activeExecutable = F59B967103538BE201154278;
+		activeTarget = 29B97326FDCFA39411CA2CEA;
+		addToTargets = (
+			29B97326FDCFA39411CA2CEA,
+		);
+		breakpoints = (
+			F596730D0355CFA301407038,
+			F500E5370359719B01AEEDB1,
+			F51152D1036B632B01E23CC6,
+			F52441D303787D1001AEEDB1,
+		);
+		executables = (
+			F59B967103538BE201154278,
+		);
+		perUserDictionary = {
+			PBXPerProjectTemplateStateSaveDate = 58520961;
+			"PBXTemplateGeometry-F5314676015831810DCA290F" = {
+				ContentSize = "{685, 434}";
+				LeftSlideOut = {
+					Collapsed = NO;
+					Frame = "{{0, 23}, {685, 411}}";
+					Split0 = {
+						ActiveTab = 2;
+						Collapsed = NO;
+						Frame = "{{0, 0}, {685, 411}}";
+						Split0 = {
+							Frame = "{{0, 221}, {685, 190}}";
+						};
+						SplitCount = 1;
+						Tab0 = {
+							Debugger = {
+								Collapsed = NO;
+								Frame = "{{0, 0}, {952, 321}}";
+								Split0 = {
+									Frame = "{{0, 24}, {952, 297}}";
+									Split0 = {
+										Frame = "{{0, 0}, {468, 297}}";
+									};
+									Split1 = {
+										DebugVariablesTableConfiguration = (
+											Name,
+											126.803,
+											Value,
+											150.074,
+											Summary,
+											172.123,
+										);
+										Frame = "{{477, 0}, {475, 297}}";
+									};
+									SplitCount = 2;
+								};
+								SplitCount = 1;
+								Tab0 = {
+									Frame = "{{0, 0}, {100, 50}}";
+								};
+								Tab1 = {
+									Frame = "{{0, 0}, {100, 50}}";
+								};
+								TabCount = 2;
+								TabsVisible = YES;
+							};
+							Frame = "{{0, 0}, {952, 321}}";
+							LauncherConfigVersion = 7;
+						};
+						Tab1 = {
+							Frame = "{{0, 0}, {781, 452}}";
+							LauncherConfigVersion = 3;
+							Runner = {
+								Frame = "{{0, 0}, {781, 452}}";
+							};
+						};
+						Tab2 = {
+							BuildMessageFrame = "{{0, 0}, {687, 149}}";
+							BuildTranscriptFrame = "{{0, 158}, {687, 59}}";
+							Frame = "{{0, 0}, {685, 215}}";
+						};
+						Tab3 = {
+							Frame = "{{0, 0}, {612, 295}}";
+						};
+						TabCount = 4;
+						TabsVisible = NO;
+					};
+					SplitCount = 1;
+					Tab0 = {
+						Frame = "{{0, 0}, {300, 533}}";
+						GroupTreeTableConfiguration = (
+							TargetStatusColumn,
+							18,
+							MainColumn,
+							267,
+						);
+					};
+					Tab1 = {
+						ClassesFrame = "{{0, 0}, {280, 398}}";
+						ClassesTreeTableConfiguration = (
+							PBXBookColumnIdentifier,
+							20,
+							PBXClassColumnIdentifier,
+							237,
+						);
+						Frame = "{{0, 0}, {278, 659}}";
+						MembersFrame = "{{0, 407}, {280, 252}}";
+						MembersTreeTableConfiguration = (
+							PBXBookColumnIdentifier,
+							20,
+							PBXMethodColumnIdentifier,
+							236,
+						);
+					};
+					Tab2 = {
+						Frame = "{{0, 0}, {200, 100}}";
+					};
+					Tab3 = {
+						Frame = "{{0, 0}, {200, 100}}";
+						Split0 = {
+							Frame = "{{0, 0}, {200, 45}}";
+							TargetTreeTableConfiguration = (
+								ActiveTarget,
+								16,
+								TargetName,
+								168,
+							);
+						};
+						Split1 = {
+							BuildStyleTreeTableConfiguration = (
+								IsActive,
+								16,
+								Name,
+								169,
+							);
+							Frame = "{{0, 54}, {200, 46}}";
+						};
+						SplitCount = 2;
+					};
+					Tab4 = {
+						ExecutableTreeTableConfiguration = (
+							ActiveExecutable,
+							16,
+							ExecutableName,
+							217,
+						);
+						Frame = "{{0, 0}, {250, 100}}";
+					};
+					Tab5 = {
+						BreakpointsTreeTableConfiguration = (
+							breakpointColumn,
+							197,
+							enabledColumn,
+							31,
+						);
+						Frame = "{{0, 0}, {250, 100}}";
+					};
+					TabCount = 6;
+					TabsVisible = NO;
+				};
+				StatusViewVisible = YES;
+				Template = F5314676015831810DCA290F;
+				ToolbarVisible = YES;
+				WindowLocation = "{421, 52}";
+			};
+			"PBXTemplateGeometry-F5534CB2020F3F8A0DCA290F" = {
+				ContentSize = "{981, 229}";
+				LeftSlideOut = {
+					Collapsed = NO;
+					Frame = "{{0, 23}, {981, 206}}";
+					Split0 = {
+						ActiveTab = 1;
+						Collapsed = YES;
+						Frame = "{{0, 0}, {981, 206}}";
+						Split0 = {
+							Frame = "{{1e+06, 1e+06}, {981, 0}}";
+						};
+						SplitCount = 1;
+						Tab0 = {
+							Debugger = {
+								Collapsed = NO;
+								Frame = "{{0, 0}, {804, 321}}";
+								Split0 = {
+									Frame = "{{0, 24}, {804, 297}}";
+									Split0 = {
+										Frame = "{{0, 0}, {394, 297}}";
+									};
+									Split1 = {
+										DebugVariablesTableConfiguration = (
+											Name,
+											102.803,
+											Value,
+											125.074,
+											Summary,
+											147.123,
+										);
+										Frame = "{{403, 0}, {401, 297}}";
+									};
+									SplitCount = 2;
+								};
+								SplitCount = 1;
+								Tab0 = {
+									Frame = "{{0, 0}, {100, 50}}";
+								};
+								Tab1 = {
+									Frame = "{{0, 0}, {100, 50}}";
+								};
+								TabCount = 2;
+								TabsVisible = YES;
+							};
+							Frame = "{{0, 0}, {804, 321}}";
+							LauncherConfigVersion = 7;
+						};
+						Tab1 = {
+							Frame = "{{0, 0}, {981, 200}}";
+							LauncherConfigVersion = 3;
+							Runner = {
+								Frame = "{{0, 0}, {981, 200}}";
+							};
+						};
+						Tab2 = {
+							BuildMessageFrame = "{{0, 0}, {806, 237}}";
+							BuildTranscriptFrame = "{{0, 246}, {806, 3}}";
+							Frame = "{{0, 0}, {804, 247}}";
+						};
+						Tab3 = {
+							Frame = "{{0, 0}, {612, 295}}";
+						};
+						TabCount = 4;
+						TabsVisible = NO;
+					};
+					SplitCount = 1;
+					Tab0 = {
+						Frame = "{{0, 0}, {300, 533}}";
+						GroupTreeTableConfiguration = (
+							TargetStatusColumn,
+							18,
+							MainColumn,
+							267,
+						);
+					};
+					Tab1 = {
+						ClassesFrame = "{{0, 0}, {280, 398}}";
+						ClassesTreeTableConfiguration = (
+							PBXBookColumnIdentifier,
+							20,
+							PBXClassColumnIdentifier,
+							237,
+						);
+						Frame = "{{0, 0}, {278, 659}}";
+						MembersFrame = "{{0, 407}, {280, 252}}";
+						MembersTreeTableConfiguration = (
+							PBXBookColumnIdentifier,
+							20,
+							PBXMethodColumnIdentifier,
+							236,
+						);
+					};
+					Tab2 = {
+						Frame = "{{0, 0}, {200, 100}}";
+					};
+					Tab3 = {
+						Frame = "{{0, 0}, {200, 386}}";
+						Split0 = {
+							Frame = "{{0, 0}, {200, 186}}";
+							TargetTreeTableConfiguration = (
+								ActiveTarget,
+								16,
+								TargetName,
+								168,
+							);
+						};
+						Split1 = {
+							BuildStyleTreeTableConfiguration = (
+								IsActive,
+								16,
+								Name,
+								169,
+							);
+							Frame = "{{0, 195}, {200, 191}}";
+						};
+						SplitCount = 2;
+					};
+					Tab4 = {
+						ExecutableTreeTableConfiguration = (
+							ActiveExecutable,
+							16,
+							ExecutableName,
+							217,
+						);
+						Frame = "{{0, 0}, {250, 386}}";
+					};
+					Tab5 = {
+						BreakpointsTreeTableConfiguration = (
+							breakpointColumn,
+							197,
+							enabledColumn,
+							31,
+						);
+						Frame = "{{0, 0}, {250, 100}}";
+					};
+					TabCount = 6;
+					TabsVisible = NO;
+				};
+				StatusViewVisible = YES;
+				Template = F5534CB2020F3F8A0DCA290F;
+				ToolbarVisible = YES;
+				WindowLocation = "{18, -90}";
+			};
+			"PBXTemplateGeometry-F5CA7EC9015C08ED0DCA290F" = {
+				ContentSize = "{665, 594}";
+				LeftSlideOut = {
+					Collapsed = NO;
+					Frame = "{{0, 0}, {665, 594}}";
+					Split0 = {
+						ActiveTab = 3;
+						Collapsed = NO;
+						Frame = "{{0, 0}, {665, 594}}";
+						Split0 = {
+							Frame = "{{0, 304}, {665, 290}}";
+						};
+						SplitCount = 1;
+						Tab0 = {
+							Debugger = {
+								Collapsed = NO;
+								Frame = "{{0, 0}, {484, 208}}";
+								Split0 = {
+									Frame = "{{0, 24}, {484, 184}}";
+									Split0 = {
+										Frame = "{{0, 0}, {236, 184}}";
+									};
+									Split1 = {
+										DebugVariablesTableConfiguration = (
+											Name,
+											48.80299,
+											Value,
+											71.07401,
+											Summary,
+											93.123,
+										);
+										Frame = "{{245, 0}, {239, 184}}";
+									};
+									SplitCount = 2;
+								};
+								SplitCount = 1;
+								Tab0 = {
+									Frame = "{{0, 0}, {100, 50}}";
+								};
+								Tab1 = {
+									Frame = "{{0, 0}, {100, 50}}";
+								};
+								TabCount = 2;
+								TabsVisible = YES;
+							};
+							Frame = "{{0, 0}, {484, 208}}";
+							LauncherConfigVersion = 7;
+						};
+						Tab1 = {
+							Frame = "{{0, 0}, {664, 208}}";
+							LauncherConfigVersion = 3;
+							Runner = {
+								Frame = "{{0, 0}, {664, 208}}";
+							};
+						};
+						Tab2 = {
+							BuildMessageFrame = "{{0, 0}, {666, 43}}";
+							BuildTranscriptFrame = "{{0, 52}, {666, 0}}";
+							Frame = "{{0, 0}, {664, 50}}";
+						};
+						Tab3 = {
+							Frame = "{{0, 0}, {665, 298}}";
+						};
+						TabCount = 4;
+						TabsVisible = NO;
+					};
+					SplitCount = 1;
+					Tab0 = {
+						Frame = "{{0, 0}, {313, 531}}";
+						GroupTreeTableConfiguration = (
+							TargetStatusColumn,
+							18,
+							MainColumn,
+							280,
+						);
+					};
+					Tab1 = {
+						ClassesFrame = "{{0, 0}, {280, 398}}";
+						ClassesTreeTableConfiguration = (
+							PBXBookColumnIdentifier,
+							20,
+							PBXClassColumnIdentifier,
+							237,
+						);
+						Frame = "{{0, 0}, {278, 659}}";
+						MembersFrame = "{{0, 407}, {280, 252}}";
+						MembersTreeTableConfiguration = (
+							PBXBookColumnIdentifier,
+							20,
+							PBXMethodColumnIdentifier,
+							236,
+						);
+					};
+					Tab2 = {
+						Frame = "{{0, 0}, {200, 100}}";
+					};
+					Tab3 = {
+						Frame = "{{0, 0}, {200, 557}}";
+						Split0 = {
+							Frame = "{{0, 0}, {200, 270}}";
+							TargetTreeTableConfiguration = (
+								ActiveTarget,
+								16,
+								TargetName,
+								168,
+							);
+						};
+						Split1 = {
+							BuildStyleTreeTableConfiguration = (
+								IsActive,
+								16,
+								Name,
+								169,
+							);
+							Frame = "{{0, 279}, {200, 278}}";
+						};
+						SplitCount = 2;
+					};
+					Tab4 = {
+						ExecutableTreeTableConfiguration = (
+							ActiveExecutable,
+							16,
+							ExecutableName,
+							217,
+						);
+						Frame = "{{0, 0}, {250, 100}}";
+					};
+					Tab5 = {
+						BreakpointsTreeTableConfiguration = (
+							breakpointColumn,
+							197,
+							enabledColumn,
+							31,
+						);
+						Frame = "{{0, 0}, {250, 100}}";
+					};
+					TabCount = 6;
+					TabsVisible = NO;
+				};
+				StatusViewVisible = NO;
+				Template = F5CA7EC9015C08ED0DCA290F;
+				ToolbarVisible = NO;
+				WindowLocation = "{69, 216}";
+			};
+			"PBXTemplateGeometry-F5CA7ECB015C094F0DCA290F" = {
+				ContentSize = "{668, 621}";
+				LeftSlideOut = {
+					Collapsed = NO;
+					Frame = "{{0, 0}, {668, 621}}";
+					Split0 = {
+						Collapsed = NO;
+						Frame = "{{0, 0}, {668, 621}}";
+						Split0 = {
+							Frame = "{{0, 0}, {668, 621}}";
+						};
+						SplitCount = 1;
+						Tab0 = {
+							Debugger = {
+								Collapsed = NO;
+								Frame = "{{0, 0}, {484, 208}}";
+								Split0 = {
+									Frame = "{{0, 24}, {484, 184}}";
+									Split0 = {
+										Frame = "{{0, 0}, {236, 184}}";
+									};
+									Split1 = {
+										DebugVariablesTableConfiguration = (
+											Name,
+											48.80299,
+											Value,
+											71.07401,
+											Summary,
+											93.123,
+										);
+										Frame = "{{245, 0}, {239, 184}}";
+									};
+									SplitCount = 2;
+								};
+								SplitCount = 1;
+								Tab0 = {
+									Frame = "{{0, 0}, {100, 50}}";
+								};
+								Tab1 = {
+									Frame = "{{0, 0}, {100, 50}}";
+								};
+								TabCount = 2;
+								TabsVisible = YES;
+							};
+							Frame = "{{0, 0}, {484, 208}}";
+							LauncherConfigVersion = 7;
+						};
+						Tab1 = {
+							Frame = "{{0, 0}, {664, 208}}";
+							LauncherConfigVersion = 3;
+							Runner = {
+								Frame = "{{0, 0}, {664, 208}}";
+							};
+						};
+						Tab2 = {
+							BuildMessageFrame = "{{0, 0}, {666, 43}}";
+							BuildTranscriptFrame = "{{0, 52}, {666, 0}}";
+							Frame = "{{0, 0}, {664, 50}}";
+						};
+						Tab3 = {
+							Frame = "{{0, 0}, {612, 295}}";
+						};
+						TabCount = 4;
+						TabsVisible = NO;
+					};
+					SplitCount = 1;
+					Tab0 = {
+						Frame = "{{0, 0}, {313, 531}}";
+						GroupTreeTableConfiguration = (
+							TargetStatusColumn,
+							18,
+							MainColumn,
+							280,
+						);
+					};
+					Tab1 = {
+						ClassesFrame = "{{0, 0}, {280, 398}}";
+						ClassesTreeTableConfiguration = (
+							PBXBookColumnIdentifier,
+							20,
+							PBXClassColumnIdentifier,
+							237,
+						);
+						Frame = "{{0, 0}, {278, 659}}";
+						MembersFrame = "{{0, 407}, {280, 252}}";
+						MembersTreeTableConfiguration = (
+							PBXBookColumnIdentifier,
+							20,
+							PBXMethodColumnIdentifier,
+							236,
+						);
+					};
+					Tab2 = {
+						Frame = "{{0, 0}, {200, 100}}";
+					};
+					Tab3 = {
+						Frame = "{{0, 0}, {200, 557}}";
+						Split0 = {
+							Frame = "{{0, 0}, {200, 270}}";
+							TargetTreeTableConfiguration = (
+								ActiveTarget,
+								16,
+								TargetName,
+								168,
+							);
+						};
+						Split1 = {
+							BuildStyleTreeTableConfiguration = (
+								IsActive,
+								16,
+								Name,
+								169,
+							);
+							Frame = "{{0, 279}, {200, 278}}";
+						};
+						SplitCount = 2;
+					};
+					Tab4 = {
+						ExecutableTreeTableConfiguration = (
+							ActiveExecutable,
+							16,
+							ExecutableName,
+							217,
+						);
+						Frame = "{{0, 0}, {250, 100}}";
+					};
+					Tab5 = {
+						BreakpointsTreeTableConfiguration = (
+							breakpointColumn,
+							197,
+							enabledColumn,
+							31,
+						);
+						Frame = "{{0, 0}, {250, 100}}";
+					};
+					TabCount = 6;
+					TabsVisible = NO;
+				};
+				StatusViewVisible = NO;
+				Template = F5CA7ECB015C094F0DCA290F;
+				ToolbarVisible = NO;
+				WindowLocation = "{555, 89}";
+			};
+			PBXWorkspaceContents = (
+				{
+					LeftSlideOut = {
+						Split0 = {
+							Split0 = {
+								NavContent0 = {
+									bookmark = F515C7E2037E2212018DB5B3;
+									history = (
+										F51E9997037C98BC01827940,
+										F51E9998037C98BC01827940,
+										F51E999C037C98BC01827940,
+										F51E999E037C98BC01827940,
+										F51E99A1037C98BC01827940,
+										F51E99A3037C98BC01827940,
+										F51E99A4037C98BC01827940,
+										F51E99A8037C98BC01827940,
+										F51E99AA037C98BC01827940,
+										F51E99AB037C98BC01827940,
+										F51E99AE037C98BC01827940,
+										F51E99AF037C98BC01827940,
+										F51E99B0037C98BC01827940,
+										F51E99B1037C98BC01827940,
+										F515C76D037E2212018DB5B3,
+										F515C76E037E2212018DB5B3,
+										F515C76F037E2212018DB5B3,
+										F515C770037E2212018DB5B3,
+										F515C771037E2212018DB5B3,
+										F515C772037E2212018DB5B3,
+										F515C773037E2212018DB5B3,
+										F515C774037E2212018DB5B3,
+										F515C775037E2212018DB5B3,
+										F515C776037E2212018DB5B3,
+										F515C777037E2212018DB5B3,
+										F515C778037E2212018DB5B3,
+										F515C779037E2212018DB5B3,
+										F515C77A037E2212018DB5B3,
+										F515C77B037E2212018DB5B3,
+										F515C77C037E2212018DB5B3,
+										F515C77D037E2212018DB5B3,
+										F515C77E037E2212018DB5B3,
+										F515C77F037E2212018DB5B3,
+										F515C780037E2212018DB5B3,
+										F515C781037E2212018DB5B3,
+										F515C782037E2212018DB5B3,
+										F515C783037E2212018DB5B3,
+										F515C784037E2212018DB5B3,
+										F515C785037E2212018DB5B3,
+										F515C786037E2212018DB5B3,
+										F515C787037E2212018DB5B3,
+										F515C788037E2212018DB5B3,
+										F515C789037E2212018DB5B3,
+										F515C78A037E2212018DB5B3,
+										F515C78B037E2212018DB5B3,
+										F515C78C037E2212018DB5B3,
+										F515C78D037E2212018DB5B3,
+										F515C78E037E2212018DB5B3,
+										F515C78F037E2212018DB5B3,
+										F515C790037E2212018DB5B3,
+										F515C791037E2212018DB5B3,
+										F515C792037E2212018DB5B3,
+										F515C793037E2212018DB5B3,
+										F515C794037E2212018DB5B3,
+										F515C795037E2212018DB5B3,
+										F515C796037E2212018DB5B3,
+										F515C797037E2212018DB5B3,
+										F515C798037E2212018DB5B3,
+										F515C799037E2212018DB5B3,
+										F515C79A037E2212018DB5B3,
+										F515C79B037E2212018DB5B3,
+										F515C79C037E2212018DB5B3,
+										F515C79D037E2212018DB5B3,
+										F515C79E037E2212018DB5B3,
+										F515C79F037E2212018DB5B3,
+									);
+									prevStack = (
+										F51E99B3037C98BC01827940,
+										F51E99B4037C98BC01827940,
+										F51E99B5037C98BC01827940,
+										F51E99B6037C98BC01827940,
+										F51E99B7037C98BC01827940,
+										F51E99B8037C98BC01827940,
+										F51E99B9037C98BC01827940,
+										F51E99BA037C98BC01827940,
+										F51E99BB037C98BC01827940,
+										F51E99BC037C98BC01827940,
+										F51E99BD037C98BC01827940,
+										F51E99BE037C98BC01827940,
+										F51E99BF037C98BC01827940,
+										F51E99C0037C98BC01827940,
+										F51E99C1037C98BC01827940,
+										F51E99C2037C98BC01827940,
+										F51E99C3037C98BC01827940,
+										F51E99C4037C98BC01827940,
+										F51E99C5037C98BC01827940,
+										F51E99C6037C98BC01827940,
+										F51E99C7037C98BC01827940,
+										F515C7A0037E2212018DB5B3,
+										F515C7A1037E2212018DB5B3,
+										F515C7A2037E2212018DB5B3,
+										F515C7A3037E2212018DB5B3,
+										F515C7A4037E2212018DB5B3,
+										F515C7A5037E2212018DB5B3,
+										F515C7A6037E2212018DB5B3,
+										F515C7A7037E2212018DB5B3,
+										F515C7A8037E2212018DB5B3,
+										F515C7A9037E2212018DB5B3,
+										F515C7AA037E2212018DB5B3,
+										F515C7AB037E2212018DB5B3,
+										F515C7AC037E2212018DB5B3,
+										F515C7AD037E2212018DB5B3,
+										F515C7AE037E2212018DB5B3,
+										F515C7AF037E2212018DB5B3,
+										F515C7B0037E2212018DB5B3,
+										F515C7B1037E2212018DB5B3,
+										F515C7B2037E2212018DB5B3,
+										F515C7B3037E2212018DB5B3,
+										F515C7B4037E2212018DB5B3,
+										F515C7B5037E2212018DB5B3,
+										F515C7B6037E2212018DB5B3,
+										F515C7B7037E2212018DB5B3,
+										F515C7B8037E2212018DB5B3,
+										F515C7B9037E2212018DB5B3,
+										F515C7BA037E2212018DB5B3,
+										F515C7BB037E2212018DB5B3,
+										F515C7BC037E2212018DB5B3,
+										F515C7BD037E2212018DB5B3,
+										F515C7BE037E2212018DB5B3,
+										F515C7BF037E2212018DB5B3,
+										F515C7C0037E2212018DB5B3,
+										F515C7C1037E2212018DB5B3,
+										F515C7C2037E2212018DB5B3,
+										F515C7C3037E2212018DB5B3,
+										F515C7C4037E2212018DB5B3,
+										F515C7C5037E2212018DB5B3,
+										F515C7C6037E2212018DB5B3,
+										F515C7C7037E2212018DB5B3,
+										F515C7C8037E2212018DB5B3,
+										F515C7C9037E2212018DB5B3,
+										F515C7CA037E2212018DB5B3,
+										F515C7CB037E2212018DB5B3,
+										F515C7CC037E2212018DB5B3,
+										F515C7CD037E2212018DB5B3,
+										F515C7CE037E2212018DB5B3,
+										F515C7CF037E2212018DB5B3,
+										F515C7D0037E2212018DB5B3,
+										F515C7D1037E2212018DB5B3,
+										F515C7D2037E2212018DB5B3,
+										F515C7D3037E2212018DB5B3,
+										F515C7D4037E2212018DB5B3,
+										F515C7D5037E2212018DB5B3,
+										F515C7D6037E2212018DB5B3,
+										F515C7D7037E2212018DB5B3,
+										F515C7D8037E2212018DB5B3,
+										F515C7D9037E2212018DB5B3,
+										F515C7DA037E2212018DB5B3,
+										F515C7DB037E2212018DB5B3,
+										F515C7DC037E2212018DB5B3,
+										F515C7DD037E2212018DB5B3,
+										F515C7DE037E2212018DB5B3,
+										F515C7DF037E2212018DB5B3,
+										F515C7E0037E2212018DB5B3,
+										F515C7E1037E2212018DB5B3,
+									);
+								};
+								NavCount = 1;
+								NavGeometry0 = {
+									Frame = "{{0, 0}, {661, 659}}";
+									NavBarVisible = YES;
+								};
+							};
+							SplitCount = 1;
+							Tab0 = {
+								Debugger = {
+									Split0 = {
+										SplitCount = 2;
+									};
+									SplitCount = 1;
+									TabCount = 2;
+								};
+								LauncherConfigVersion = 7;
+							};
+							Tab1 = {
+								LauncherConfigVersion = 3;
+								Runner = {
+								};
+							};
+							TabCount = 4;
+						};
+						SplitCount = 1;
+						Tab1 = {
+							OptionsSetName = "Hierarchy, all classes";
+						};
+						Tab3 = {
+							SplitCount = 2;
+						};
+						TabCount = 6;
+					};
+				},
+				{
+					LeftSlideOut = {
+						Split0 = {
+							Split0 = {
+								NavCount = 1;
+								NavGeometry0 = {
+									Frame = "{{0, 0}, {981, 150}}";
+									NavBarVisible = YES;
+								};
+							};
+							SplitCount = 1;
+							Tab0 = {
+								Debugger = {
+									Split0 = {
+										SplitCount = 2;
+									};
+									SplitCount = 1;
+									TabCount = 2;
+								};
+								LauncherConfigVersion = 7;
+							};
+							Tab1 = {
+								LauncherConfigVersion = 3;
+								Runner = {
+								};
+							};
+							TabCount = 4;
+						};
+						SplitCount = 1;
+						Tab1 = {
+							OptionsSetName = "Hierarchy, all classes";
+						};
+						Tab3 = {
+							SplitCount = 2;
+						};
+						TabCount = 6;
+					};
+				},
+				{
+					LeftSlideOut = {
+						Split0 = {
+							Split0 = {
+								NavCount = 1;
+								NavGeometry0 = {
+									Frame = "{{0, 0}, {749, 262}}";
+									NavBarVisible = YES;
+								};
+							};
+							SplitCount = 1;
+							Tab0 = {
+								Debugger = {
+									Split0 = {
+										SplitCount = 2;
+									};
+									SplitCount = 1;
+									TabCount = 2;
+								};
+								LauncherConfigVersion = 7;
+							};
+							Tab1 = {
+								LauncherConfigVersion = 3;
+								Runner = {
+								};
+							};
+							TabCount = 4;
+						};
+						SplitCount = 1;
+						Tab1 = {
+							OptionsSetName = "Hierarchy, all classes";
+						};
+						Tab3 = {
+							SplitCount = 2;
+						};
+						TabCount = 6;
+					};
+				},
+				{
+					LeftSlideOut = {
+						Split0 = {
+							Split0 = {
+								NavCount = 1;
+								NavGeometry0 = {
+									Frame = "{{0, 0}, {685, 190}}";
+									NavBarVisible = YES;
+								};
+							};
+							SplitCount = 1;
+							Tab0 = {
+								Debugger = {
+									Split0 = {
+										SplitCount = 2;
+									};
+									SplitCount = 1;
+									TabCount = 2;
+								};
+								LauncherConfigVersion = 7;
+							};
+							Tab1 = {
+								LauncherConfigVersion = 3;
+								Runner = {
+								};
+							};
+							TabCount = 4;
+						};
+						SplitCount = 1;
+						Tab1 = {
+							OptionsSetName = "Hierarchy, all classes";
+						};
+						Tab3 = {
+							SplitCount = 2;
+						};
+						TabCount = 6;
+					};
+				},
+			);
+			PBXWorkspaceGeometries = (
+				{
+					ContentSize = "{966, 682}";
+					LeftSlideOut = {
+						ActiveTab = 0;
+						Collapsed = NO;
+						Frame = "{{0, 23}, {966, 659}}";
+						Split0 = {
+							Collapsed = NO;
+							Frame = "{{305, 0}, {661, 659}}";
+							Split0 = {
+								Frame = "{{0, 0}, {661, 659}}";
+							};
+							SplitCount = 1;
+							Tab0 = {
+								Debugger = {
+									Collapsed = NO;
+									Frame = "{{0, 0}, {681, 289}}";
+									Split0 = {
+										Frame = "{{0, 24}, {681, 265}}";
+										Split0 = {
+											Frame = "{{0, 0}, {333, 265}}";
+										};
+										Split1 = {
+											DebugVariablesTableConfiguration = (
+												Name,
+												82.80298,
+												Value,
+												104.074,
+												Summary,
+												126.123,
+											);
+											Frame = "{{342, 0}, {339, 265}}";
+										};
+										SplitCount = 2;
+									};
+									SplitCount = 1;
+									Tab0 = {
+										Frame = "{{0, 0}, {100, 50}}";
+									};
+									Tab1 = {
+										Frame = "{{0, 0}, {100, 50}}";
+									};
+									TabCount = 2;
+									TabsVisible = YES;
+								};
+								Frame = "{{0, 0}, {681, 289}}";
+								LauncherConfigVersion = 7;
+							};
+							Tab1 = {
+								Frame = "{{0, 0}, {681, 120}}";
+								LauncherConfigVersion = 3;
+								Runner = {
+									Frame = "{{0, 0}, {681, 120}}";
+								};
+							};
+							Tab2 = {
+								BuildMessageFrame = "{{0, 0}, {683, 127}}";
+								BuildTranscriptFrame = "{{0, 136}, {683, 100}}";
+								Frame = "{{0, 0}, {681, 234}}";
+							};
+							Tab3 = {
+								Frame = "{{0, 0}, {681, 238}}";
+							};
+							TabCount = 4;
+							TabsVisible = NO;
+						};
+						SplitCount = 1;
+						Tab0 = {
+							Frame = "{{0, 0}, {281, 659}}";
+							GroupTreeTableConfiguration = (
+								TargetStatusColumn,
+								18,
+								MainColumn,
+								248,
+							);
+						};
+						Tab1 = {
+							ClassesFrame = "{{0, 0}, {250, 397}}";
+							ClassesTreeTableConfiguration = (
+								PBXBookColumnIdentifier,
+								20,
+								PBXClassColumnIdentifier,
+								207,
+							);
+							Frame = "{{0, 0}, {248, 659}}";
+							MembersFrame = "{{0, 406}, {250, 253}}";
+							MembersTreeTableConfiguration = (
+								PBXBookColumnIdentifier,
+								20,
+								PBXMethodColumnIdentifier,
+								206,
+							);
+						};
+						Tab2 = {
+							Frame = "{{0, 0}, {217, 554}}";
+						};
+						Tab3 = {
+							Frame = "{{0, 0}, {239, 554}}";
+							Split0 = {
+								Frame = "{{0, 0}, {239, 268}}";
+								TargetTreeTableConfiguration = (
+									ActiveTarget,
+									16,
+									TargetName,
+									207,
+								);
+							};
+							Split1 = {
+								BuildStyleTreeTableConfiguration = (
+									IsActive,
+									16,
+									Name,
+									208,
+								);
+								Frame = "{{0, 277}, {239, 277}}";
+							};
+							SplitCount = 2;
+						};
+						Tab4 = {
+							ExecutableTreeTableConfiguration = (
+								ActiveExecutable,
+								16,
+								ExecutableName,
+								217,
+							);
+							Frame = "{{0, 0}, {250, 659}}";
+						};
+						Tab5 = {
+							BreakpointsTreeTableConfiguration = (
+								breakpointColumn,
+								197,
+								enabledColumn,
+								31,
+							);
+							Frame = "{{0, 0}, {250, 555}}";
+						};
+						TabCount = 6;
+						TabsVisible = YES;
+					};
+					StatusViewVisible = YES;
+					Template = 64ABBB4501FA494900185B06;
+					ToolbarVisible = YES;
+					WindowLocation = "{60, 88}";
+				},
+				{
+					ContentSize = "{973, 164}";
+					LeftSlideOut = {
+						Collapsed = NO;
+						Frame = "{{0, 23}, {973, 141}}";
+						Split0 = {
+							ActiveTab = 1;
+							Collapsed = YES;
+							Frame = "{{0, 0}, {973, 141}}";
+							Split0 = {
+								Frame = "{{1e+06, 1e+06}, {981, 0}}";
+							};
+							SplitCount = 1;
+							Tab0 = {
+								Debugger = {
+									Collapsed = NO;
+									Frame = "{{0, 0}, {804, 321}}";
+									Split0 = {
+										Frame = "{{0, 24}, {804, 297}}";
+										Split0 = {
+											Frame = "{{0, 0}, {394, 297}}";
+										};
+										Split1 = {
+											DebugVariablesTableConfiguration = (
+												Name,
+												102.803,
+												Value,
+												125.074,
+												Summary,
+												147.123,
+											);
+											Frame = "{{403, 0}, {401, 297}}";
+										};
+										SplitCount = 2;
+									};
+									SplitCount = 1;
+									Tab0 = {
+										Frame = "{{0, 0}, {100, 50}}";
+									};
+									Tab1 = {
+										Frame = "{{0, 0}, {100, 50}}";
+									};
+									TabCount = 2;
+									TabsVisible = YES;
+								};
+								Frame = "{{0, 0}, {804, 321}}";
+								LauncherConfigVersion = 7;
+							};
+							Tab1 = {
+								Frame = "{{0, 0}, {973, 135}}";
+								LauncherConfigVersion = 3;
+								Runner = {
+									Frame = "{{0, 0}, {973, 135}}";
+								};
+							};
+							Tab2 = {
+								BuildMessageFrame = "{{0, 0}, {806, 237}}";
+								BuildTranscriptFrame = "{{0, 246}, {806, 3}}";
+								Frame = "{{0, 0}, {804, 247}}";
+							};
+							Tab3 = {
+								Frame = "{{0, 0}, {612, 295}}";
+							};
+							TabCount = 4;
+							TabsVisible = NO;
+						};
+						SplitCount = 1;
+						Tab0 = {
+							Frame = "{{0, 0}, {300, 533}}";
+							GroupTreeTableConfiguration = (
+								TargetStatusColumn,
+								18,
+								MainColumn,
+								267,
+							);
+						};
+						Tab1 = {
+							ClassesFrame = "{{0, 0}, {280, 398}}";
+							ClassesTreeTableConfiguration = (
+								PBXBookColumnIdentifier,
+								20,
+								PBXClassColumnIdentifier,
+								237,
+							);
+							Frame = "{{0, 0}, {278, 659}}";
+							MembersFrame = "{{0, 407}, {280, 252}}";
+							MembersTreeTableConfiguration = (
+								PBXBookColumnIdentifier,
+								20,
+								PBXMethodColumnIdentifier,
+								236,
+							);
+						};
+						Tab2 = {
+							Frame = "{{0, 0}, {200, 100}}";
+						};
+						Tab3 = {
+							Frame = "{{0, 0}, {200, 386}}";
+							Split0 = {
+								Frame = "{{0, 0}, {200, 186}}";
+								TargetTreeTableConfiguration = (
+									ActiveTarget,
+									16,
+									TargetName,
+									168,
+								);
+							};
+							Split1 = {
+								BuildStyleTreeTableConfiguration = (
+									IsActive,
+									16,
+									Name,
+									169,
+								);
+								Frame = "{{0, 195}, {200, 191}}";
+							};
+							SplitCount = 2;
+						};
+						Tab4 = {
+							ExecutableTreeTableConfiguration = (
+								ActiveExecutable,
+								16,
+								ExecutableName,
+								217,
+							);
+							Frame = "{{0, 0}, {250, 386}}";
+						};
+						Tab5 = {
+							BreakpointsTreeTableConfiguration = (
+								breakpointColumn,
+								197,
+								enabledColumn,
+								31,
+							);
+							Frame = "{{0, 0}, {250, 100}}";
+						};
+						TabCount = 6;
+						TabsVisible = NO;
+					};
+					StatusViewVisible = YES;
+					Template = F5534CB2020F3F8A0DCA290F;
+					ToolbarVisible = YES;
+					WindowLocation = "{13, 43}";
+				},
+				{
+					ContentSize = "{773, 558}";
+					LeftSlideOut = {
+						Collapsed = NO;
+						Frame = "{{0, 23}, {773, 535}}";
+						Split0 = {
+							ActiveTab = 0;
+							Collapsed = NO;
+							Frame = "{{24, 0}, {749, 535}}";
+							Split0 = {
+								Frame = "{{0, 273}, {749, 262}}";
+							};
+							SplitCount = 1;
+							Tab0 = {
+								Debugger = {
+									Collapsed = NO;
+									Frame = "{{0, 0}, {749, 267}}";
+									Split0 = {
+										Frame = "{{0, 24}, {749, 243}}";
+										Split0 = {
+											Frame = "{{0, 0}, {366, 243}}";
+										};
+										Split1 = {
+											DebugVariablesTableConfiguration = (
+												Name,
+												93.80298,
+												Value,
+												116.074,
+												Summary,
+												138.123,
+											);
+											Frame = "{{375, 0}, {374, 243}}";
+										};
+										SplitCount = 2;
+									};
+									SplitCount = 1;
+									Tab0 = {
+										Frame = "{{0, 0}, {100, 50}}";
+									};
+									Tab1 = {
+										Frame = "{{0, 0}, {100, 50}}";
+									};
+									TabCount = 2;
+									TabsVisible = YES;
+								};
+								Frame = "{{0, 0}, {749, 267}}";
+								LauncherConfigVersion = 7;
+							};
+							Tab1 = {
+								Frame = "{{0, 0}, {664, 208}}";
+								LauncherConfigVersion = 3;
+								Runner = {
+									Frame = "{{0, 0}, {664, 208}}";
+								};
+							};
+							Tab2 = {
+								BuildMessageFrame = "{{0, 0}, {666, 43}}";
+								BuildTranscriptFrame = "{{0, 52}, {666, 0}}";
+								Frame = "{{0, 0}, {664, 50}}";
+							};
+							Tab3 = {
+								Frame = "{{0, 0}, {612, 295}}";
+							};
+							TabCount = 4;
+							TabsVisible = NO;
+						};
+						SplitCount = 1;
+						Tab0 = {
+							Frame = "{{0, 0}, {313, 531}}";
+							GroupTreeTableConfiguration = (
+								TargetStatusColumn,
+								18,
+								MainColumn,
+								280,
+							);
+						};
+						Tab1 = {
+							ClassesFrame = "{{0, 0}, {280, 398}}";
+							ClassesTreeTableConfiguration = (
+								PBXBookColumnIdentifier,
+								20,
+								PBXClassColumnIdentifier,
+								237,
+							);
+							Frame = "{{0, 0}, {278, 659}}";
+							MembersFrame = "{{0, 407}, {280, 252}}";
+							MembersTreeTableConfiguration = (
+								PBXBookColumnIdentifier,
+								20,
+								PBXMethodColumnIdentifier,
+								236,
+							);
+						};
+						Tab2 = {
+							Frame = "{{0, 0}, {200, 100}}";
+						};
+						Tab3 = {
+							Frame = "{{0, 0}, {200, 557}}";
+							Split0 = {
+								Frame = "{{0, 0}, {200, 270}}";
+								TargetTreeTableConfiguration = (
+									ActiveTarget,
+									16,
+									TargetName,
+									168,
+								);
+							};
+							Split1 = {
+								BuildStyleTreeTableConfiguration = (
+									IsActive,
+									16,
+									Name,
+									169,
+								);
+								Frame = "{{0, 279}, {200, 278}}";
+							};
+							SplitCount = 2;
+						};
+						Tab4 = {
+							ExecutableTreeTableConfiguration = (
+								ActiveExecutable,
+								16,
+								ExecutableName,
+								217,
+							);
+							Frame = "{{0, 0}, {250, 100}}";
+						};
+						Tab5 = {
+							BreakpointsTreeTableConfiguration = (
+								breakpointColumn,
+								197,
+								enabledColumn,
+								31,
+							);
+							Frame = "{{0, 0}, {250, 100}}";
+						};
+						TabCount = 6;
+						TabsVisible = YES;
+					};
+					StatusViewVisible = YES;
+					Template = F5CA7ECC015C09990DCA290F;
+					ToolbarVisible = YES;
+					WindowLocation = "{13, 212}";
+				},
+				{
+					ContentSize = "{685, 434}";
+					LeftSlideOut = {
+						Collapsed = NO;
+						Frame = "{{0, 23}, {685, 411}}";
+						Split0 = {
+							ActiveTab = 2;
+							Collapsed = NO;
+							Frame = "{{0, 0}, {685, 411}}";
+							Split0 = {
+								Frame = "{{0, 221}, {685, 190}}";
+							};
+							SplitCount = 1;
+							Tab0 = {
+								Debugger = {
+									Collapsed = NO;
+									Frame = "{{0, 0}, {952, 321}}";
+									Split0 = {
+										Frame = "{{0, 24}, {952, 297}}";
+										Split0 = {
+											Frame = "{{0, 0}, {468, 297}}";
+										};
+										Split1 = {
+											DebugVariablesTableConfiguration = (
+												Name,
+												126.803,
+												Value,
+												150.074,
+												Summary,
+												172.123,
+											);
+											Frame = "{{477, 0}, {475, 297}}";
+										};
+										SplitCount = 2;
+									};
+									SplitCount = 1;
+									Tab0 = {
+										Frame = "{{0, 0}, {100, 50}}";
+									};
+									Tab1 = {
+										Frame = "{{0, 0}, {100, 50}}";
+									};
+									TabCount = 2;
+									TabsVisible = YES;
+								};
+								Frame = "{{0, 0}, {952, 321}}";
+								LauncherConfigVersion = 7;
+							};
+							Tab1 = {
+								Frame = "{{0, 0}, {781, 452}}";
+								LauncherConfigVersion = 3;
+								Runner = {
+									Frame = "{{0, 0}, {781, 452}}";
+								};
+							};
+							Tab2 = {
+								BuildMessageFrame = "{{0, 0}, {687, 149}}";
+								BuildTranscriptFrame = "{{0, 158}, {687, 59}}";
+								Frame = "{{0, 0}, {685, 215}}";
+							};
+							Tab3 = {
+								Frame = "{{0, 0}, {612, 295}}";
+							};
+							TabCount = 4;
+							TabsVisible = NO;
+						};
+						SplitCount = 1;
+						Tab0 = {
+							Frame = "{{0, 0}, {300, 533}}";
+							GroupTreeTableConfiguration = (
+								TargetStatusColumn,
+								18,
+								MainColumn,
+								267,
+							);
+						};
+						Tab1 = {
+							ClassesFrame = "{{0, 0}, {280, 398}}";
+							ClassesTreeTableConfiguration = (
+								PBXBookColumnIdentifier,
+								20,
+								PBXClassColumnIdentifier,
+								237,
+							);
+							Frame = "{{0, 0}, {278, 659}}";
+							MembersFrame = "{{0, 407}, {280, 252}}";
+							MembersTreeTableConfiguration = (
+								PBXBookColumnIdentifier,
+								20,
+								PBXMethodColumnIdentifier,
+								236,
+							);
+						};
+						Tab2 = {
+							Frame = "{{0, 0}, {200, 100}}";
+						};
+						Tab3 = {
+							Frame = "{{0, 0}, {200, 100}}";
+							Split0 = {
+								Frame = "{{0, 0}, {200, 45}}";
+								TargetTreeTableConfiguration = (
+									ActiveTarget,
+									16,
+									TargetName,
+									168,
+								);
+							};
+							Split1 = {
+								BuildStyleTreeTableConfiguration = (
+									IsActive,
+									16,
+									Name,
+									169,
+								);
+								Frame = "{{0, 54}, {200, 46}}";
+							};
+							SplitCount = 2;
+						};
+						Tab4 = {
+							ExecutableTreeTableConfiguration = (
+								ActiveExecutable,
+								16,
+								ExecutableName,
+								217,
+							);
+							Frame = "{{0, 0}, {250, 100}}";
+						};
+						Tab5 = {
+							BreakpointsTreeTableConfiguration = (
+								breakpointColumn,
+								197,
+								enabledColumn,
+								31,
+							);
+							Frame = "{{0, 0}, {250, 100}}";
+						};
+						TabCount = 6;
+						TabsVisible = NO;
+					};
+					StatusViewVisible = YES;
+					Template = F5314676015831810DCA290F;
+					ToolbarVisible = YES;
+					WindowLocation = "{421, 52}";
+				},
+			);
+			PBXWorkspaceStateSaveDate = 58520961;
+		};
+		perUserProjectItems = {
+			F515C76D037E2212018DB5B3 = F515C76D037E2212018DB5B3;
+			F515C76E037E2212018DB5B3 = F515C76E037E2212018DB5B3;
+			F515C76F037E2212018DB5B3 = F515C76F037E2212018DB5B3;
+			F515C770037E2212018DB5B3 = F515C770037E2212018DB5B3;
+			F515C771037E2212018DB5B3 = F515C771037E2212018DB5B3;
+			F515C772037E2212018DB5B3 = F515C772037E2212018DB5B3;
+			F515C773037E2212018DB5B3 = F515C773037E2212018DB5B3;
+			F515C774037E2212018DB5B3 = F515C774037E2212018DB5B3;
+			F515C775037E2212018DB5B3 = F515C775037E2212018DB5B3;
+			F515C776037E2212018DB5B3 = F515C776037E2212018DB5B3;
+			F515C777037E2212018DB5B3 = F515C777037E2212018DB5B3;
+			F515C778037E2212018DB5B3 = F515C778037E2212018DB5B3;
+			F515C779037E2212018DB5B3 = F515C779037E2212018DB5B3;
+			F515C77A037E2212018DB5B3 = F515C77A037E2212018DB5B3;
+			F515C77B037E2212018DB5B3 = F515C77B037E2212018DB5B3;
+			F515C77C037E2212018DB5B3 = F515C77C037E2212018DB5B3;
+			F515C77D037E2212018DB5B3 = F515C77D037E2212018DB5B3;
+			F515C77E037E2212018DB5B3 = F515C77E037E2212018DB5B3;
+			F515C77F037E2212018DB5B3 = F515C77F037E2212018DB5B3;
+			F515C780037E2212018DB5B3 = F515C780037E2212018DB5B3;
+			F515C781037E2212018DB5B3 = F515C781037E2212018DB5B3;
+			F515C782037E2212018DB5B3 = F515C782037E2212018DB5B3;
+			F515C783037E2212018DB5B3 = F515C783037E2212018DB5B3;
+			F515C784037E2212018DB5B3 = F515C784037E2212018DB5B3;
+			F515C785037E2212018DB5B3 = F515C785037E2212018DB5B3;
+			F515C786037E2212018DB5B3 = F515C786037E2212018DB5B3;
+			F515C787037E2212018DB5B3 = F515C787037E2212018DB5B3;
+			F515C788037E2212018DB5B3 = F515C788037E2212018DB5B3;
+			F515C789037E2212018DB5B3 = F515C789037E2212018DB5B3;
+			F515C78A037E2212018DB5B3 = F515C78A037E2212018DB5B3;
+			F515C78B037E2212018DB5B3 = F515C78B037E2212018DB5B3;
+			F515C78C037E2212018DB5B3 = F515C78C037E2212018DB5B3;
+			F515C78D037E2212018DB5B3 = F515C78D037E2212018DB5B3;
+			F515C78E037E2212018DB5B3 = F515C78E037E2212018DB5B3;
+			F515C78F037E2212018DB5B3 = F515C78F037E2212018DB5B3;
+			F515C790037E2212018DB5B3 = F515C790037E2212018DB5B3;
+			F515C791037E2212018DB5B3 = F515C791037E2212018DB5B3;
+			F515C792037E2212018DB5B3 = F515C792037E2212018DB5B3;
+			F515C793037E2212018DB5B3 = F515C793037E2212018DB5B3;
+			F515C794037E2212018DB5B3 = F515C794037E2212018DB5B3;
+			F515C795037E2212018DB5B3 = F515C795037E2212018DB5B3;
+			F515C796037E2212018DB5B3 = F515C796037E2212018DB5B3;
+			F515C797037E2212018DB5B3 = F515C797037E2212018DB5B3;
+			F515C798037E2212018DB5B3 = F515C798037E2212018DB5B3;
+			F515C799037E2212018DB5B3 = F515C799037E2212018DB5B3;
+			F515C79A037E2212018DB5B3 = F515C79A037E2212018DB5B3;
+			F515C79B037E2212018DB5B3 = F515C79B037E2212018DB5B3;
+			F515C79C037E2212018DB5B3 = F515C79C037E2212018DB5B3;
+			F515C79D037E2212018DB5B3 = F515C79D037E2212018DB5B3;
+			F515C79E037E2212018DB5B3 = F515C79E037E2212018DB5B3;
+			F515C79F037E2212018DB5B3 = F515C79F037E2212018DB5B3;
+			F515C7A0037E2212018DB5B3 = F515C7A0037E2212018DB5B3;
+			F515C7A1037E2212018DB5B3 = F515C7A1037E2212018DB5B3;
+			F515C7A2037E2212018DB5B3 = F515C7A2037E2212018DB5B3;
+			F515C7A3037E2212018DB5B3 = F515C7A3037E2212018DB5B3;
+			F515C7A4037E2212018DB5B3 = F515C7A4037E2212018DB5B3;
+			F515C7A5037E2212018DB5B3 = F515C7A5037E2212018DB5B3;
+			F515C7A6037E2212018DB5B3 = F515C7A6037E2212018DB5B3;
+			F515C7A7037E2212018DB5B3 = F515C7A7037E2212018DB5B3;
+			F515C7A8037E2212018DB5B3 = F515C7A8037E2212018DB5B3;
+			F515C7A9037E2212018DB5B3 = F515C7A9037E2212018DB5B3;
+			F515C7AA037E2212018DB5B3 = F515C7AA037E2212018DB5B3;
+			F515C7AB037E2212018DB5B3 = F515C7AB037E2212018DB5B3;
+			F515C7AC037E2212018DB5B3 = F515C7AC037E2212018DB5B3;
+			F515C7AD037E2212018DB5B3 = F515C7AD037E2212018DB5B3;
+			F515C7AE037E2212018DB5B3 = F515C7AE037E2212018DB5B3;
+			F515C7AF037E2212018DB5B3 = F515C7AF037E2212018DB5B3;
+			F515C7B0037E2212018DB5B3 = F515C7B0037E2212018DB5B3;
+			F515C7B1037E2212018DB5B3 = F515C7B1037E2212018DB5B3;
+			F515C7B2037E2212018DB5B3 = F515C7B2037E2212018DB5B3;
+			F515C7B3037E2212018DB5B3 = F515C7B3037E2212018DB5B3;
+			F515C7B4037E2212018DB5B3 = F515C7B4037E2212018DB5B3;
+			F515C7B5037E2212018DB5B3 = F515C7B5037E2212018DB5B3;
+			F515C7B6037E2212018DB5B3 = F515C7B6037E2212018DB5B3;
+			F515C7B7037E2212018DB5B3 = F515C7B7037E2212018DB5B3;
+			F515C7B8037E2212018DB5B3 = F515C7B8037E2212018DB5B3;
+			F515C7B9037E2212018DB5B3 = F515C7B9037E2212018DB5B3;
+			F515C7BA037E2212018DB5B3 = F515C7BA037E2212018DB5B3;
+			F515C7BB037E2212018DB5B3 = F515C7BB037E2212018DB5B3;
+			F515C7BC037E2212018DB5B3 = F515C7BC037E2212018DB5B3;
+			F515C7BD037E2212018DB5B3 = F515C7BD037E2212018DB5B3;
+			F515C7BE037E2212018DB5B3 = F515C7BE037E2212018DB5B3;
+			F515C7BF037E2212018DB5B3 = F515C7BF037E2212018DB5B3;
+			F515C7C0037E2212018DB5B3 = F515C7C0037E2212018DB5B3;
+			F515C7C1037E2212018DB5B3 = F515C7C1037E2212018DB5B3;
+			F515C7C2037E2212018DB5B3 = F515C7C2037E2212018DB5B3;
+			F515C7C3037E2212018DB5B3 = F515C7C3037E2212018DB5B3;
+			F515C7C4037E2212018DB5B3 = F515C7C4037E2212018DB5B3;
+			F515C7C5037E2212018DB5B3 = F515C7C5037E2212018DB5B3;
+			F515C7C6037E2212018DB5B3 = F515C7C6037E2212018DB5B3;
+			F515C7C7037E2212018DB5B3 = F515C7C7037E2212018DB5B3;
+			F515C7C8037E2212018DB5B3 = F515C7C8037E2212018DB5B3;
+			F515C7C9037E2212018DB5B3 = F515C7C9037E2212018DB5B3;
+			F515C7CA037E2212018DB5B3 = F515C7CA037E2212018DB5B3;
+			F515C7CB037E2212018DB5B3 = F515C7CB037E2212018DB5B3;
+			F515C7CC037E2212018DB5B3 = F515C7CC037E2212018DB5B3;
+			F515C7CD037E2212018DB5B3 = F515C7CD037E2212018DB5B3;
+			F515C7CE037E2212018DB5B3 = F515C7CE037E2212018DB5B3;
+			F515C7CF037E2212018DB5B3 = F515C7CF037E2212018DB5B3;
+			F515C7D0037E2212018DB5B3 = F515C7D0037E2212018DB5B3;
+			F515C7D1037E2212018DB5B3 = F515C7D1037E2212018DB5B3;
+			F515C7D2037E2212018DB5B3 = F515C7D2037E2212018DB5B3;
+			F515C7D3037E2212018DB5B3 = F515C7D3037E2212018DB5B3;
+			F515C7D4037E2212018DB5B3 = F515C7D4037E2212018DB5B3;
+			F515C7D5037E2212018DB5B3 = F515C7D5037E2212018DB5B3;
+			F515C7D6037E2212018DB5B3 = F515C7D6037E2212018DB5B3;
+			F515C7D7037E2212018DB5B3 = F515C7D7037E2212018DB5B3;
+			F515C7D8037E2212018DB5B3 = F515C7D8037E2212018DB5B3;
+			F515C7D9037E2212018DB5B3 = F515C7D9037E2212018DB5B3;
+			F515C7DA037E2212018DB5B3 = F515C7DA037E2212018DB5B3;
+			F515C7DB037E2212018DB5B3 = F515C7DB037E2212018DB5B3;
+			F515C7DC037E2212018DB5B3 = F515C7DC037E2212018DB5B3;
+			F515C7DD037E2212018DB5B3 = F515C7DD037E2212018DB5B3;
+			F515C7DE037E2212018DB5B3 = F515C7DE037E2212018DB5B3;
+			F515C7DF037E2212018DB5B3 = F515C7DF037E2212018DB5B3;
+			F515C7E0037E2212018DB5B3 = F515C7E0037E2212018DB5B3;
+			F515C7E1037E2212018DB5B3 = F515C7E1037E2212018DB5B3;
+			F515C7E2037E2212018DB5B3 = F515C7E2037E2212018DB5B3;
+			F51E9997037C98BC01827940 = F51E9997037C98BC01827940;
+			F51E9998037C98BC01827940 = F51E9998037C98BC01827940;
+			F51E999C037C98BC01827940 = F51E999C037C98BC01827940;
+			F51E999E037C98BC01827940 = F51E999E037C98BC01827940;
+			F51E99A1037C98BC01827940 = F51E99A1037C98BC01827940;
+			F51E99A3037C98BC01827940 = F51E99A3037C98BC01827940;
+			F51E99A4037C98BC01827940 = F51E99A4037C98BC01827940;
+			F51E99A8037C98BC01827940 = F51E99A8037C98BC01827940;
+			F51E99AA037C98BC01827940 = F51E99AA037C98BC01827940;
+			F51E99AB037C98BC01827940 = F51E99AB037C98BC01827940;
+			F51E99AE037C98BC01827940 = F51E99AE037C98BC01827940;
+			F51E99AF037C98BC01827940 = F51E99AF037C98BC01827940;
+			F51E99B0037C98BC01827940 = F51E99B0037C98BC01827940;
+			F51E99B1037C98BC01827940 = F51E99B1037C98BC01827940;
+			F51E99B3037C98BC01827940 = F51E99B3037C98BC01827940;
+			F51E99B4037C98BC01827940 = F51E99B4037C98BC01827940;
+			F51E99B5037C98BC01827940 = F51E99B5037C98BC01827940;
+			F51E99B6037C98BC01827940 = F51E99B6037C98BC01827940;
+			F51E99B7037C98BC01827940 = F51E99B7037C98BC01827940;
+			F51E99B8037C98BC01827940 = F51E99B8037C98BC01827940;
+			F51E99B9037C98BC01827940 = F51E99B9037C98BC01827940;
+			F51E99BA037C98BC01827940 = F51E99BA037C98BC01827940;
+			F51E99BB037C98BC01827940 = F51E99BB037C98BC01827940;
+			F51E99BC037C98BC01827940 = F51E99BC037C98BC01827940;
+			F51E99BD037C98BC01827940 = F51E99BD037C98BC01827940;
+			F51E99BE037C98BC01827940 = F51E99BE037C98BC01827940;
+			F51E99BF037C98BC01827940 = F51E99BF037C98BC01827940;
+			F51E99C0037C98BC01827940 = F51E99C0037C98BC01827940;
+			F51E99C1037C98BC01827940 = F51E99C1037C98BC01827940;
+			F51E99C2037C98BC01827940 = F51E99C2037C98BC01827940;
+			F51E99C3037C98BC01827940 = F51E99C3037C98BC01827940;
+			F51E99C4037C98BC01827940 = F51E99C4037C98BC01827940;
+			F51E99C5037C98BC01827940 = F51E99C5037C98BC01827940;
+			F51E99C6037C98BC01827940 = F51E99C6037C98BC01827940;
+			F51E99C7037C98BC01827940 = F51E99C7037C98BC01827940;
+		};
+		projectwideBuildSettings = {
+		};
+		userBookmarkGroup = F59DC6D20353C43201AEEDB1;
+		wantsIndex = 1;
+		wantsSCM = -1;
+	};
+	29B97326FDCFA39411CA2CEA = {
+		activeExec = 0;
+		executables = (
+			F59B967103538BE201154278,
+		);
+	};
+	F500E5370359719B01AEEDB1 = {
+		isa = PBXSymbolicBreakpoint;
+		state = 1;
+		symbolName = szone_error;
+	};
+	F51152D1036B632B01E23CC6 = {
+		fileReference = F59E4793036AA5E9016B311C;
+		isa = PBXFileBreakpoint;
+		lineNumber = 180;
+		state = 1;
+	};
+	F515C769037DFB63018DB5B3 = {
+		fRef = F5F240B6036A862101FE7503;
+		isa = PBXTextBookmark;
+		name = "NJRFSObjectSelector.h: 13";
+		rLen = 0;
+		rLoc = 247;
+		rType = 0;
+		vrLen = 653;
+		vrLoc = 0;
+	};
+	F515C76A037E01E1018DB5B3 = {
+		fRef = F59DC6D40353C9E601AEEDB1;
+		isa = PBXTextBookmark;
+		name = "PSAlarmSetController.m: inAtChanged:";
+		rLen = 0;
+		rLoc = 8241;
+		rType = 0;
+		vrLen = 1674;
+		vrLoc = 7871;
+	};
+	F515C76D037E2212018DB5B3 = {
+		fRef = F515C7E7037E2212018DB5B3;
+		isa = PBXBookmark;
+	};
+	F515C76E037E2212018DB5B3 = {
+		fRef = F515C7F0037E2212018DB5B3;
+		isa = PBXTextBookmark;
+		name = "Controls.h: 99";
+		rLen = 0;
+		rLoc = 2493;
+		rType = 0;
+		vrLen = 2858;
+		vrLoc = 3060;
+	};
+	F515C76F037E2212018DB5B3 = {
+		fRef = F515C7F1037E2212018DB5B3;
+		isa = PBXTextBookmark;
+		name = "NSDate.h: 25";
+		rLen = 0;
+		rLoc = 521;
+		rType = 0;
+		vrLen = 1045;
+		vrLoc = 302;
+	};
+	F515C770037E2212018DB5B3 = {
+		fRef = F515C7F2037E2212018DB5B3;
+		isa = PBXBookmark;
+	};
+	F515C771037E2212018DB5B3 = {
+		fRef = F515C7F3037E2212018DB5B3;
+		isa = PBXTextBookmark;
+		name = "NSScanner.h: 34";
+		rLen = 0;
+		rLoc = 954;
+		rType = 0;
+		vrLen = 1256;
+		vrLoc = 0;
+	};
+	F515C772037E2212018DB5B3 = {
+		fRef = F515C7EB037E2212018DB5B3;
+		isa = PBXTextBookmark;
+		name = "NSClassDescription.h: 19";
+		rLen = 69;
+		rLoc = 532;
+		rType = 0;
+		vrLen = 954;
+		vrLoc = 0;
+	};
+	F515C773037E2212018DB5B3 = {
+		fRef = F515C7F5037E2212018DB5B3;
+		isa = PBXTextBookmark;
+		name = "NSCharacterSet.h: 17";
+		rLen = 41;
+		rLoc = 323;
+		rType = 0;
+		vrLen = 1609;
+		vrLoc = 0;
+	};
+	F515C774037E2212018DB5B3 = {
+		fRef = F515C7F6037E2212018DB5B3;
+		isa = PBXTextBookmark;
+		name = "NSRange.h: 12";
+		rLen = 11;
+		rLoc = 200;
+		rType = 0;
+		vrLen = 1213;
+		vrLoc = 0;
+	};
+	F515C775037E2212018DB5B3 = {
+		fRef = F515C7F7037E2212018DB5B3;
+		isa = PBXTextBookmark;
+		name = "ControlDefinitions.h: 1311";
+		rLen = 0;
+		rLoc = 51116;
+		rType = 0;
+		vrLen = 1192;
+		vrLoc = 49198;
+	};
+	F515C776037E2212018DB5B3 = {
+		fRef = F5F198D0036BE57401EB0372;
+		isa = PBXTextBookmark;
+		name = "NJRTableDelegate.m: key";
+		rLen = 0;
+		rLoc = 618;
+		rType = 0;
+		vrLen = 1133;
+		vrLoc = 0;
+	};
+	F515C777037E2212018DB5B3 = {
+		fRef = F515C7F8037E2212018DB5B3;
+		isa = PBXBookmark;
+	};
+	F515C778037E2212018DB5B3 = {
+		fRef = F517D6430356B8CC016D68B9;
+		isa = PBXTextBookmark;
+		name = "PSAlarms.h: 1";
+		rLen = 0;
+		rLoc = 0;
+		rType = 0;
+		vrLen = 612;
+		vrLoc = 0;
+	};
+	F515C779037E2212018DB5B3 = {
+		fRef = F515C7E3037E2212018DB5B3;
+		isa = PBXTextBookmark;
+		name = "NSFormatter.h: 14";
+		rLen = 44;
+		rLoc = 263;
+		rType = 0;
+		vrLen = 1230;
+		vrLoc = 0;
+	};
+	F515C77A037E2212018DB5B3 = {
+		fRef = F515C7FB037E2212018DB5B3;
+		isa = PBXTextBookmark;
+		name = "NSCalendarDate.h: 74";
+		rLen = 0;
+		rLoc = 2518;
+		rType = 0;
+		vrLen = 1061;
+		vrLoc = 1636;
+	};
+	F515C77B037E2212018DB5B3 = {
+		fRef = F59F6CB40354E7DA01AEEDB1;
+		isa = PBXTextBookmark;
+		name = "PSAlarm.h: shortDateString";
+		rLen = 0;
+		rLoc = 1079;
+		rType = 0;
+		vrLen = 861;
+		vrLoc = 453;
+	};
+	F515C77C037E2212018DB5B3 = {
+		fRef = F515C7FC037E2212018DB5B3;
+		isa = PBXBookmark;
+	};
+	F515C77D037E2212018DB5B3 = {
+		fRef = F515C7E8037E2212018DB5B3;
+		isa = PBXTextBookmark;
+		name = "NSMenu.h: 58";
+		rLen = 9;
+		rLoc = 1783;
+		rType = 0;
+		vrLen = 1289;
+		vrLoc = 872;
+	};
+	F515C77E037E2212018DB5B3 = {
+		fRef = F515C7FD037E2212018DB5B3;
+		isa = PBXTextBookmark;
+		name = "NSMenuItem.h: 1";
+		rLen = 0;
+		rLoc = 0;
+		rType = 0;
+		vrLen = 939;
+		vrLoc = 0;
+	};
+	F515C77F037E2212018DB5B3 = {
+		fRef = F515C7E5037E2212018DB5B3;
+		isa = PBXBookmark;
+	};
+	F515C780037E2212018DB5B3 = {
+		fRef = F515C7FF037E2212018DB5B3;
+		isa = PBXTextBookmark;
+		name = "NSPopUpButton.h: 78";
+		rLen = 25;
+		rLoc = 2241;
+		rType = 0;
+		vrLen = 950;
+		vrLoc = 1438;
+	};
+	F515C781037E2212018DB5B3 = {
+		fRef = F59E47B4036AAD55016B311C;
+		isa = PBXTextBookmark;
+		name = "PSAlarmAlertController.m: __VERSION__";
+		rLen = 0;
+		rLoc = 0;
+		rType = 0;
+		vrLen = 947;
+		vrLoc = 0;
+	};
+	F515C782037E2212018DB5B3 = {
+		fRef = F515C7EC037E2212018DB5B3;
+		isa = PBXBookmark;
+	};
+	F515C783037E2212018DB5B3 = {
+		fRef = F517D63E0356B80C016D68B9;
+		isa = PBXTextBookmark;
+		name = "PSAlarmsController.m: tableView:objectValueForTableColumn:row:";
+		rLen = 0;
+		rLoc = 1912;
+		rType = 0;
+		vrLen = 1480;
+		vrLoc = 0;
+	};
+	F515C784037E2212018DB5B3 = {
+		fRef = F517D63F0356B80C016D68B9;
+		isa = PBXTextBookmark;
+		name = "PSAlarmsController.h: 6";
+		rLen = 0;
+		rLoc = 107;
+		rType = 0;
+		vrLen = 393;
+		vrLoc = 0;
+	};
+	F515C785037E2212018DB5B3 = {
+		fRef = F5F240CC036A877301FE7503;
+		isa = PBXTextBookmark;
+		name = "NJRQTMediaPopUpButton.m: NJRQTMediaPopUpButtonMovieChangedNotification";
+		rLen = 45;
+		rLoc = 368;
+		rType = 0;
+		vrLen = 1441;
+		vrLoc = 0;
+	};
+	F515C786037E2212018DB5B3 = {
+		fRef = F5F19870036B822E01EB0372;
+		isa = PBXTextBookmark;
+		name = "NJRVoicePopUpButton.m: _previewVoice";
+		rLen = 0;
+		rLoc = 3092;
+		rType = 0;
+		vrLen = 1414;
+		vrLoc = 2129;
+	};
+	F515C787037E2212018DB5B3 = {
+		fRef = F5F240B5036A862101FE7503;
+		isa = PBXTextBookmark;
+		name = "NJRFSObjectSelector.m: __VERSION__";
+		rLen = 0;
+		rLoc = 0;
+		rType = 0;
+		vrLen = 1082;
+		vrLoc = 0;
+	};
+	F515C788037E2212018DB5B3 = {
+		fRef = F5F240B6036A862101FE7503;
+		isa = PBXTextBookmark;
+		name = "NJRFSObjectSelector.h: 13";
+		rLen = 0;
+		rLoc = 247;
+		rType = 0;
+		vrLen = 653;
+		vrLoc = 0;
+	};
+	F515C789037E2212018DB5B3 = {
+		fRef = F560E2F20357506401A4E466;
+		isa = PBXTextBookmark;
+		name = "PSApplication.h: 1";
+		rLen = 0;
+		rLoc = 0;
+		rType = 0;
+		vrLen = 609;
+		vrLoc = 0;
+	};
+	F515C78A037E2212018DB5B3 = {
+		fRef = F560E2F10357506401A4E466;
+		isa = PBXTextBookmark;
+		name = "PSApplication.m: 17";
+		rLen = 0;
+		rLoc = 348;
+		rType = 0;
+		vrLen = 1273;
+		vrLoc = 0;
+	};
+	F515C78B037E2212018DB5B3 = {
+		fRef = F515C7EF037E2212018DB5B3;
+		isa = PBXTextBookmark;
+		name = "NSCell.h: 140";
+		rLen = 0;
+		rLoc = 3906;
+		rType = 0;
+		vrLen = 1073;
+		vrLoc = 3530;
+	};
+	F515C78C037E2212018DB5B3 = {
+		fRef = F59DC6D30353C9E601AEEDB1;
+		isa = PBXTextBookmark;
+		name = "PSAlarmSetController.h: 24";
+		rLen = 19;
+		rLoc = 654;
+		rType = 0;
+		vrLen = 1601;
+		vrLoc = 205;
+	};
+	F515C78D037E2212018DB5B3 = {
+		fRef = F515C7E4037E2212018DB5B3;
+		isa = PBXTextBookmark;
+		name = "NSMatrix.h: 1";
+		rLen = 0;
+		rLoc = 0;
+		rType = 0;
+		vrLen = 1821;
+		vrLoc = 4505;
+	};
+	F515C78E037E2212018DB5B3 = {
+		fRef = F515C7E9037E2212018DB5B3;
+		isa = PBXTextBookmark;
+		name = "NSButtonCell.h: 154";
+		rLen = 0;
+		rLoc = 5038;
+		rType = 0;
+		vrLen = 1692;
+		vrLoc = 973;
+	};
+	F515C78F037E2212018DB5B3 = {
+		fRef = F515C7EA037E2212018DB5B3;
+		isa = PBXTextBookmark;
+		name = "NSActionCell.h: 1";
+		rLen = 0;
+		rLoc = 0;
+		rType = 0;
+		vrLen = 919;
+		vrLoc = 0;
+	};
+	F515C790037E2212018DB5B3 = {
+		fRef = F515C7F4037E2212018DB5B3;
+		isa = PBXBookmark;
+	};
+	F515C791037E2212018DB5B3 = {
+		fRef = F589C9C6035AA12B0155F4B2;
+		isa = PBXTextBookmark;
+		name = "Read Me.rtfd: 84";
+		rLen = 0;
+		rLoc = 6253;
+		rType = 0;
+		vrLen = 2489;
+		vrLoc = 5507;
+	};
+	F515C792037E2212018DB5B3 = {
+		fRef = F515C800037E2212018DB5B3;
+		isa = PBXTextBookmark;
+		name = "NSDateFormatter.h: 16";
+		rLen = 77;
+		rLoc = 258;
+		rType = 0;
+		vrLen = 399;
+		vrLoc = 0;
+	};
+	F515C793037E2212018DB5B3 = {
+		fRef = F59DC6FC0354188F01AEEDB1;
+		isa = PBXTextBookmark;
+		name = "NJRDateFormatter.h: 13";
+		rLen = 0;
+		rLoc = 270;
+		rType = 0;
+		vrLen = 557;
+		vrLoc = 0;
+	};
+	F515C794037E2212018DB5B3 = {
+		fRef = F515C801037E2212018DB5B3;
+		isa = PBXTextBookmark;
+		name = "NSString.h: 166";
+		rLen = 7;
+		rLoc = 6420;
+		rType = 0;
+		vrLen = 1667;
+		vrLoc = 5371;
+	};
+	F515C795037E2212018DB5B3 = {
+		fRef = 089C165DFE840E0CC02AAC07;
+		isa = PBXTextBookmark;
+		name = "English: 7";
+		rLen = 0;
+		rLoc = 223;
+		rType = 0;
+		vrLen = 223;
+		vrLoc = 0;
+	};
+	F515C796037E2212018DB5B3 = {
+		fRef = F515C7ED037E2212018DB5B3;
+		isa = PBXTextBookmark;
+		name = "NSUserDefaults.h: 52";
+		rLen = 0;
+		rLoc = 1606;
+		rType = 0;
+		vrLen = 2162;
+		vrLoc = 1561;
+	};
+	F515C797037E2212018DB5B3 = {
+		isa = PBXTargetBookmark;
+		trg = 29B97326FDCFA39411CA2CEA;
+		uiCtxt = {
+			buildPhasesExpandedSubviews = (
+				0,
+				1,
+				2,
+				3,
+			);
+			buildPhasesVisRect = "{{0, 0}, {646, 608}}";
+			buildSettingsExpandedSubviews = (
+				0,
+				1,
+				2,
+				3,
+				4,
+				5,
+				8,
+			);
+			buildSettingsVisRect = "{{0, 0}, {393, 256}}";
+			expertProductSettingsVisRect = "{{0, 0}, {464, 403}}";
+			guiProductSettingsExpandedSubviews = (
+				0,
+				1,
+				2,
+				3,
+				4,
+				5,
+				6,
+			);
+			guiProductSettingsVisRect = "{{0, 0}, {677, 577}}";
+			productSettingsIsSimple = YES;
+			selectedTab = productSettings;
+		};
+	};
+	F515C798037E2212018DB5B3 = {
+		exec = F59B967103538BE201154278;
+		isa = PBXExecutableBookmark;
+		uiCtxt = {
+			buildSettingsExpandedSubviews = (
+				0,
+				1,
+				2,
+				3,
+				4,
+				5,
+			);
+			buildSettingsVisRect = "{{0, 0}, {646, 637}}";
+		};
+	};
+	F515C799037E2212018DB5B3 = {
+		fRef = F59DC6DC0353DE5101AEEDB1;
+		isa = PBXTextBookmark;
+		name = "PSAlarmNotifierController.m: 29";
+		rLen = 0;
+		rLoc = 785;
+		rType = 0;
+		vrLen = 893;
+		vrLoc = 0;
+	};
+	F515C79A037E2212018DB5B3 = {
+		fRef = F59E47B5036AAD55016B311C;
+		isa = PBXTextBookmark;
+		name = "PSAlarmAlertController.h: 1";
+		rLen = 0;
+		rLoc = 0;
+		rType = 0;
+		vrLen = 493;
+		vrLoc = 0;
+	};
+	F515C79B037E2212018DB5B3 = {
+		fRef = F59DC6D40353C9E601AEEDB1;
+		isa = PBXTextBookmark;
+		name = "PSAlarmSetController.m: 38";
+		rLen = 0;
+		rLoc = 2312;
+		rType = 0;
+		vrLen = 2391;
+		vrLoc = 191;
+	};
+	F515C79C037E2212018DB5B3 = {
+		fRef = F59F6CB50354E7DA01AEEDB1;
+		isa = PBXTextBookmark;
+		name = "PSAlarm.m: initialize";
+		rLen = 0;
+		rLoc = 862;
+		rType = 0;
+		vrLen = 1349;
+		vrLoc = 8504;
+	};
+	F515C79D037E2212018DB5B3 = {
+		fRef = F59DC6E80353F58B01AEEDB1;
+		isa = PBXTextBookmark;
+		name = "NJRIntegerFilter.m: isPartialStringValid:proposedSelectedRange:originalString:originalSelectedRange:errorDescription:";
+		rLen = 0;
+		rLoc = 1008;
+		rType = 0;
+		vrLen = 1519;
+		vrLoc = 0;
+	};
+	F515C79E037E2212018DB5B3 = {
+		fRef = F59E4C0E036B43D2016B311C;
+		isa = PBXTextBookmark;
+		name = "NSMovie-NJRExtensions.h: 1";
+		rLen = 0;
+		rLoc = 0;
+		rType = 0;
+		vrLen = 283;
+		vrLoc = 0;
+	};
+	F515C79F037E2212018DB5B3 = {
+		fRef = F59DC6FD0354188F01AEEDB1;
+		isa = PBXTextBookmark;
+		name = "NJRDateFormatter.m: 177";
+		rLen = 0;
+		rLoc = 8262;
+		rType = 0;
+		vrLen = 1856;
+		vrLoc = 6454;
+	};
+	F515C7A0037E2212018DB5B3 = {
+		fRef = F515C7EE037E2212018DB5B3;
+		isa = PBXBookmark;
+	};
+	F515C7A1037E2212018DB5B3 = {
+		fRef = F560E2F10357506401A4E466;
+		isa = PBXTextBookmark;
+		name = "PSApplication.m: finishLaunching";
+		rLen = 0;
+		rLoc = 582;
+		rType = 0;
+		vrLen = 1184;
+		vrLoc = 0;
+	};
+	F515C7A2037E2212018DB5B3 = {
+		fRef = F515C7F9037E2212018DB5B3;
+		isa = PBXTextBookmark;
+		name = "NSDateFormatter.h: 1";
+		rLen = 0;
+		rLoc = 0;
+		rType = 0;
+		vrLen = 399;
+		vrLoc = 0;
+	};
+	F515C7A3037E2212018DB5B3 = {
+		fRef = F59DC6FD0354188F01AEEDB1;
+		isa = PBXTextBookmark;
+		name = "NJRDateFormatter.m: 13";
+		rLen = 0;
+		rLoc = 7242;
+		rType = 0;
+		vrLen = 1402;
+		vrLoc = 0;
+	};
+	F515C7A4037E2212018DB5B3 = {
+		fRef = F515C7E6037E2212018DB5B3;
+		isa = PBXTextBookmark;
+		name = "Controls.h: 99";
+		rLen = 0;
+		rLoc = 2493;
+		rType = 0;
+		vrLen = 2858;
+		vrLoc = 3060;
+	};
+	F515C7A5037E2212018DB5B3 = {
+		fRef = F515C7FA037E2212018DB5B3;
+		isa = PBXTextBookmark;
+		name = "ControlDefinitions.h: 1311";
+		rLen = 0;
+		rLoc = 51116;
+		rType = 0;
+		vrLen = 1192;
+		vrLoc = 49198;
+	};
+	F515C7A6037E2212018DB5B3 = {
+		fRef = F59DC6D40353C9E601AEEDB1;
+		isa = PBXTextBookmark;
+		name = "PSAlarmSetController.m: updateDateDisplay:";
+		rLen = 0;
+		rLoc = 6440;
+		rType = 0;
+		vrLen = 1444;
+		vrLoc = 4820;
+	};
+	F515C7A7037E2212018DB5B3 = {
+		fRef = F5F198D0036BE57401EB0372;
+		isa = PBXTextBookmark;
+		name = "NJRTableDelegate.m: key";
+		rLen = 0;
+		rLoc = 618;
+		rType = 0;
+		vrLen = 1133;
+		vrLoc = 0;
+	};
+	F515C7A8037E2212018DB5B3 = {
+		fRef = F517D63E0356B80C016D68B9;
+		isa = PBXTextBookmark;
+		name = "PSAlarmsController.m: tableView:objectValueForTableColumn:row:";
+		rLen = 0;
+		rLoc = 1912;
+		rType = 0;
+		vrLen = 1367;
+		vrLoc = 42;
+	};
+	F515C7A9037E2212018DB5B3 = {
+		fRef = F59F6CB50354E7DA01AEEDB1;
+		isa = PBXTextBookmark;
+		name = "PSAlarm.m: 164";
+		rLen = 26;
+		rLoc = 4918;
+		rType = 0;
+		vrLen = 877;
+		vrLoc = 4461;
+	};
+	F515C7AA037E2212018DB5B3 = {
+		fRef = F59F6CB40354E7DA01AEEDB1;
+		isa = PBXTextBookmark;
+		name = "PSAlarm.h: shortDateString";
+		rLen = 0;
+		rLoc = 1079;
+		rType = 0;
+		vrLen = 861;
+		vrLoc = 453;
+	};
+	F515C7AB037E2212018DB5B3 = {
+		fRef = F59DC6D40353C9E601AEEDB1;
+		isa = PBXTextBookmark;
+		name = "PSAlarmSetController.m: 32";
+		rLen = 0;
+		rLoc = 1013;
+		rType = 0;
+		vrLen = 1553;
+		vrLoc = 41;
+	};
+	F515C7AC037E2212018DB5B3 = {
+		fRef = F59F6CB50354E7DA01AEEDB1;
+		isa = PBXTextBookmark;
+		name = "PSAlarm.m: timeString";
+		rLen = 0;
+		rLoc = 5264;
+		rType = 0;
+		vrLen = 958;
+		vrLoc = 4645;
+	};
+	F515C7AD037E2212018DB5B3 = {
+		fRef = F59DC6FD0354188F01AEEDB1;
+		isa = PBXTextBookmark;
+		name = "NJRDateFormatter.m: stringByRemovingSurroundingWhitespace";
+		rLen = 0;
+		rLoc = 7242;
+		rType = 0;
+		vrLen = 1306;
+		vrLoc = 5125;
+	};
+	F515C7AE037E2212018DB5B3 = {
+		fRef = F560E2F10357506401A4E466;
+		isa = PBXTextBookmark;
+		name = "PSApplication.m: 17";
+		rLen = 0;
+		rLoc = 348;
+		rType = 0;
+		vrLen = 1044;
+		vrLoc = 0;
+	};
+	F515C7AF037E2212018DB5B3 = {
+		fRef = F59DC6D40353C9E601AEEDB1;
+		isa = PBXTextBookmark;
+		name = "PSAlarmSetController.m: awakeFromNib";
+		rLen = 0;
+		rLoc = 4259;
+		rType = 0;
+		vrLen = 1492;
+		vrLoc = 2539;
+	};
+	F515C7B0037E2212018DB5B3 = {
+		fRef = F59F6CB50354E7DA01AEEDB1;
+		isa = PBXTextBookmark;
+		name = "PSAlarm.m: timeString";
+		rLen = 0;
+		rLoc = 5264;
+		rType = 0;
+		vrLen = 958;
+		vrLoc = 4645;
+	};
+	F515C7B1037E2212018DB5B3 = {
+		fRef = F59DC6D40353C9E601AEEDB1;
+		isa = PBXTextBookmark;
+		name = "PSAlarmSetController.m: 42";
+		rLen = 0;
+		rLoc = 2578;
+		rType = 0;
+		vrLen = 2166;
+		vrLoc = 2829;
+	};
+	F515C7B2037E2212018DB5B3 = {
+		fRef = F589C9C6035AA12B0155F4B2;
+		isa = PBXTextBookmark;
+		name = "Read Me.rtfd: 66";
+		rLen = 0;
+		rLoc = 4448;
+		rType = 0;
+		vrLen = 715;
+		vrLoc = 2070;
+	};
+	F515C7B3037E2212018DB5B3 = {
+		fRef = F59E47B4036AAD55016B311C;
+		isa = PBXTextBookmark;
+		name = "PSAlarmAlertController.m: __VERSION__";
+		rLen = 0;
+		rLoc = 0;
+		rType = 0;
+		vrLen = 947;
+		vrLoc = 0;
+	};
+	F515C7B4037E2212018DB5B3 = {
+		fRef = F59DC6DC0353DE5101AEEDB1;
+		isa = PBXTextBookmark;
+		name = "PSAlarmNotifierController.m: close:";
+		rLen = 0;
+		rLoc = 850;
+		rType = 0;
+		vrLen = 813;
+		vrLoc = 0;
+	};
+	F515C7B5037E2212018DB5B3 = {
+		fRef = F517D63F0356B80C016D68B9;
+		isa = PBXTextBookmark;
+		name = "PSAlarmsController.h: 6";
+		rLen = 0;
+		rLoc = 107;
+		rType = 0;
+		vrLen = 393;
+		vrLoc = 0;
+	};
+	F515C7B6037E2212018DB5B3 = {
+		fRef = F589C9C6035AA12B0155F4B2;
+		isa = PBXTextBookmark;
+		name = "Read Me.rtfd: 76";
+		rLen = 0;
+		rLoc = 5056;
+		rType = 0;
+		vrLen = 2443;
+		vrLoc = 4667;
+	};
+	F515C7B7037E2212018DB5B3 = {
+		fRef = F59DC6D30353C9E601AEEDB1;
+		isa = PBXTextBookmark;
+		name = "PSAlarmSetController.h: 24";
+		rLen = 19;
+		rLoc = 654;
+		rType = 0;
+		vrLen = 1573;
+		vrLoc = 233;
+	};
+	F515C7B8037E2212018DB5B3 = {
+		fRef = F59DC6D40353C9E601AEEDB1;
+		isa = PBXTextBookmark;
+		name = "PSAlarmSetController.m: inAtChanged:";
+		rLen = 0;
+		rLoc = 9053;
+		rType = 0;
+		vrLen = 1762;
+		vrLoc = 7871;
+	};
+	F515C7B9037E2212018DB5B3 = {
+		fRef = F59DC6DC0353DE5101AEEDB1;
+		isa = PBXTextBookmark;
+		name = "PSAlarmNotifierController.m: initWithAlarm:";
+		rLen = 0;
+		rLoc = 782;
+		rType = 0;
+		vrLen = 893;
+		vrLoc = 0;
+	};
+	F515C7BA037E2212018DB5B3 = {
+		fRef = F5F240CC036A877301FE7503;
+		isa = PBXTextBookmark;
+		name = "NJRQTMediaPopUpButton.m: NJRQTMediaPopUpButtonMovieChangedNotification";
+		rLen = 45;
+		rLoc = 368;
+		rType = 0;
+		vrLen = 1441;
+		vrLoc = 0;
+	};
+	F515C7BB037E2212018DB5B3 = {
+		fRef = F59DC6D30353C9E601AEEDB1;
+		isa = PBXTextBookmark;
+		name = "PSAlarmSetController.h: 24";
+		rLen = 19;
+		rLoc = 654;
+		rType = 0;
+		vrLen = 1601;
+		vrLoc = 205;
+	};
+	F515C7BC037E2212018DB5B3 = {
+		fRef = F59DC6D40353C9E601AEEDB1;
+		isa = PBXTextBookmark;
+		name = "PSAlarmSetController.m: canRepeat";
+		rLen = 0;
+		rLoc = 9165;
+		rType = 0;
+		vrLen = 1712;
+		vrLoc = 7871;
+	};
+	F515C7BD037E2212018DB5B3 = {
+		fRef = F5F19870036B822E01EB0372;
+		isa = PBXTextBookmark;
+		name = "NJRVoicePopUpButton.m: _previewVoice";
+		rLen = 0;
+		rLoc = 3092;
+		rType = 0;
+		vrLen = 1414;
+		vrLoc = 2129;
+	};
+	F515C7BE037E2212018DB5B3 = {
+		fRef = F5F240B5036A862101FE7503;
+		isa = PBXTextBookmark;
+		name = "NJRFSObjectSelector.m: 169";
+		rLen = 0;
+		rLoc = 5771;
+		rType = 0;
+		vrLen = 1335;
+		vrLoc = 4793;
+	};
+	F515C7BF037E2212018DB5B3 = {
+		fRef = F5F240B6036A862101FE7503;
+		isa = PBXTextBookmark;
+		name = "NJRFSObjectSelector.h: 1";
+		rLen = 0;
+		rLoc = 0;
+		rType = 0;
+		vrLen = 633;
+		vrLoc = 0;
+	};
+	F515C7C0037E2212018DB5B3 = {
+		fRef = F5F240B5036A862101FE7503;
+		isa = PBXTextBookmark;
+		name = "NJRFSObjectSelector.m: 169";
+		rLen = 0;
+		rLoc = 5771;
+		rType = 0;
+		vrLen = 1335;
+		vrLoc = 4793;
+	};
+	F515C7C1037E2212018DB5B3 = {
+		fRef = F5F240B6036A862101FE7503;
+		isa = PBXTextBookmark;
+		name = "NJRFSObjectSelector.h: 13";
+		rLen = 0;
+		rLoc = 247;
+		rType = 0;
+		vrLen = 653;
+		vrLoc = 0;
+	};
+	F515C7C2037E2212018DB5B3 = {
+		fRef = F5F240B5036A862101FE7503;
+		isa = PBXTextBookmark;
+		name = "NJRFSObjectSelector.m: __VERSION__";
+		rLen = 0;
+		rLoc = 0;
+		rType = 0;
+		vrLen = 1082;
+		vrLoc = 0;
+	};
+	F515C7C3037E2212018DB5B3 = {
+		fRef = F5F240B6036A862101FE7503;
+		isa = PBXTextBookmark;
+		name = "NJRFSObjectSelector.h: 13";
+		rLen = 0;
+		rLoc = 247;
+		rType = 0;
+		vrLen = 653;
+		vrLoc = 0;
+	};
+	F515C7C4037E2212018DB5B3 = {
+		fRef = F589C9C6035AA12B0155F4B2;
+		isa = PBXTextBookmark;
+		name = "Read Me.rtfd: 74";
+		rLen = 0;
+		rLoc = 5019;
+		rType = 0;
+		vrLen = 2329;
+		vrLoc = 4496;
+	};
+	F515C7C5037E2212018DB5B3 = {
+		fRef = F560E2F20357506401A4E466;
+		isa = PBXTextBookmark;
+		name = "PSApplication.h: 1";
+		rLen = 0;
+		rLoc = 0;
+		rType = 0;
+		vrLen = 609;
+		vrLoc = 0;
+	};
+	F515C7C6037E2212018DB5B3 = {
+		fRef = F560E2F10357506401A4E466;
+		isa = PBXTextBookmark;
+		name = "PSApplication.m: 17";
+		rLen = 0;
+		rLoc = 348;
+		rType = 0;
+		vrLen = 1273;
+		vrLoc = 0;
+	};
+	F515C7C7037E2212018DB5B3 = {
+		fRef = F59DC6D40353C9E601AEEDB1;
+		isa = PBXTextBookmark;
+		name = "PSAlarmSetController.m: inAtChanged:";
+		rLen = 0;
+		rLoc = 8241;
+		rType = 0;
+		vrLen = 1674;
+		vrLoc = 7871;
+	};
+	F515C7C8037E2212018DB5B3 = {
+		fRef = F59DC6D30353C9E601AEEDB1;
+		isa = PBXTextBookmark;
+		name = "PSAlarmSetController.h: 24";
+		rLen = 19;
+		rLoc = 654;
+		rType = 0;
+		vrLen = 1601;
+		vrLoc = 205;
+	};
+	F515C7C9037E2212018DB5B3 = {
+		fRef = F59DC6D40353C9E601AEEDB1;
+		isa = PBXTextBookmark;
+		name = "PSAlarmSetController.m: inAtChanged:";
+		rLen = 0;
+		rLoc = 8183;
+		rType = 0;
+		vrLen = 1672;
+		vrLoc = 7871;
+	};
+	F515C7CA037E2212018DB5B3 = {
+		fRef = F515C7FE037E2212018DB5B3;
+		isa = PBXTextBookmark;
+		name = "NSString.h: 94";
+		rLen = 8;
+		rLoc = 3366;
+		rType = 0;
+		vrLen = 1678;
+		vrLoc = 2394;
+	};
+	F515C7CB037E2212018DB5B3 = {
+		fRef = F59DC6D40353C9E601AEEDB1;
+		isa = PBXTextBookmark;
+		name = "PSAlarmSetController.m: 28";
+		rLen = 0;
+		rLoc = 732;
+		rType = 0;
+		vrLen = 2140;
+		vrLoc = 0;
+	};
+	F515C7CC037E2212018DB5B3 = {
+		fRef = F589C9C6035AA12B0155F4B2;
+		isa = PBXTextBookmark;
+		name = "Read Me.rtfd: 84";
+		rLen = 0;
+		rLoc = 6253;
+		rType = 0;
+		vrLen = 2489;
+		vrLoc = 5507;
+	};
+	F515C7CD037E2212018DB5B3 = {
+		fRef = F59DC6FD0354188F01AEEDB1;
+		isa = PBXTextBookmark;
+		name = "NJRDateFormatter.m: nonWhitespace";
+		rLen = 0;
+		rLoc = 7242;
+		rType = 0;
+		vrLen = 1904;
+		vrLoc = 3912;
+	};
+	F515C7CE037E2212018DB5B3 = {
+		fRef = F59DC6D40353C9E601AEEDB1;
+		isa = PBXTextBookmark;
+		name = "PSAlarmSetController.m: 38";
+		rLen = 0;
+		rLoc = 2312;
+		rType = 0;
+		vrLen = 2391;
+		vrLoc = 191;
+	};
+	F515C7CF037E2212018DB5B3 = {
+		fRef = F59DC6FD0354188F01AEEDB1;
+		isa = PBXTextBookmark;
+		name = "NJRDateFormatter.m: localizedTimeFormatIncludingSeconds:";
+		rLen = 0;
+		rLoc = 2612;
+		rType = 0;
+		vrLen = 2395;
+		vrLoc = 2524;
+	};
+	F515C7D0037E2212018DB5B3 = {
+		fRef = F59DC6FC0354188F01AEEDB1;
+		isa = PBXTextBookmark;
+		name = "NJRDateFormatter.h: 20";
+		rLen = 0;
+		rLoc = 551;
+		rType = 0;
+		vrLen = 525;
+		vrLoc = 0;
+	};
+	F515C7D1037E2212018DB5B3 = {
+		fRef = F59DC6FD0354188F01AEEDB1;
+		isa = PBXTextBookmark;
+		name = "NJRDateFormatter.m: nonWhitespace";
+		rLen = 0;
+		rLoc = 7167;
+		rType = 0;
+		vrLen = 1845;
+		vrLoc = 4660;
+	};
+	F515C7D2037E2212018DB5B3 = {
+		fRef = F59DC6FC0354188F01AEEDB1;
+		isa = PBXTextBookmark;
+		name = "NJRDateFormatter.h: 13";
+		rLen = 0;
+		rLoc = 270;
+		rType = 0;
+		vrLen = 557;
+		vrLoc = 0;
+	};
+	F515C7D3037E2212018DB5B3 = {
+		fRef = F59DC6FD0354188F01AEEDB1;
+		isa = PBXTextBookmark;
+		name = "NJRDateFormatter.m: stringByRemovingSurroundingWhitespace";
+		rLen = 0;
+		rLoc = 6603;
+		rType = 0;
+		vrLen = 1764;
+		vrLoc = 4757;
+	};
+	F515C7D4037E2212018DB5B3 = {
+		fRef = F59DC6FC0354188F01AEEDB1;
+		isa = PBXTextBookmark;
+		name = "NJRDateFormatter.h: 13";
+		rLen = 0;
+		rLoc = 270;
+		rType = 0;
+		vrLen = 557;
+		vrLoc = 0;
+	};
+	F515C7D5037E2212018DB5B3 = {
+		fRef = F59DC6FD0354188F01AEEDB1;
+		isa = PBXTextBookmark;
+		name = "NJRDateFormatter.m: initWithDateFormat:allowNaturalLanguage:";
+		rLen = 0;
+		rLoc = 6622;
+		rType = 0;
+		vrLen = 1835;
+		vrLoc = 4067;
+	};
+	F515C7D6037E2212018DB5B3 = {
+		fRef = F59DC6FC0354188F01AEEDB1;
+		isa = PBXTextBookmark;
+		name = "NJRDateFormatter.h: 13";
+		rLen = 0;
+		rLoc = 270;
+		rType = 0;
+		vrLen = 557;
+		vrLoc = 0;
+	};
+	F515C7D7037E2212018DB5B3 = {
+		fRef = F59DC6FD0354188F01AEEDB1;
+		isa = PBXTextBookmark;
+		name = "NJRDateFormatter.m: ampmRange";
+		rLen = 0;
+		rLoc = 5875;
+		rType = 0;
+		vrLen = 2114;
+		vrLoc = 5177;
+	};
+	F515C7D8037E2212018DB5B3 = {
+		isa = PBXTargetBookmark;
+		trg = 29B97326FDCFA39411CA2CEA;
+		uiCtxt = {
+			buildPhasesExpandedSubviews = (
+				0,
+				1,
+				2,
+				3,
+			);
+			buildPhasesVisRect = "{{0, 0}, {646, 608}}";
+			buildSettingsExpandedSubviews = (
+				0,
+				1,
+				2,
+				3,
+				4,
+				5,
+				8,
+			);
+			buildSettingsVisRect = "{{0, 0}, {393, 256}}";
+			expertProductSettingsVisRect = "{{0, 0}, {464, 403}}";
+			guiProductSettingsExpandedSubviews = (
+				0,
+				1,
+				2,
+				3,
+				4,
+				5,
+				6,
+			);
+			guiProductSettingsVisRect = "{{0, 0}, {677, 577}}";
+			productSettingsIsSimple = YES;
+			selectedTab = productSettings;
+		};
+	};
+	F515C7D9037E2212018DB5B3 = {
+		exec = F59B967103538BE201154278;
+		isa = PBXExecutableBookmark;
+		uiCtxt = {
+			buildSettingsExpandedSubviews = (
+				0,
+				1,
+				2,
+				3,
+				4,
+				5,
+			);
+			buildSettingsVisRect = "{{0, 0}, {646, 637}}";
+		};
+	};
+	F515C7DA037E2212018DB5B3 = {
+		fRef = F59DC6DC0353DE5101AEEDB1;
+		isa = PBXTextBookmark;
+		name = "PSAlarmNotifierController.m: 29";
+		rLen = 0;
+		rLoc = 785;
+		rType = 0;
+		vrLen = 893;
+		vrLoc = 0;
+	};
+	F515C7DB037E2212018DB5B3 = {
+		fRef = F59E47B5036AAD55016B311C;
+		isa = PBXTextBookmark;
+		name = "PSAlarmAlertController.h: 1";
+		rLen = 0;
+		rLoc = 0;
+		rType = 0;
+		vrLen = 493;
+		vrLoc = 0;
+	};
+	F515C7DC037E2212018DB5B3 = {
+		fRef = F59DC6D40353C9E601AEEDB1;
+		isa = PBXTextBookmark;
+		name = "PSAlarmSetController.m: 38";
+		rLen = 0;
+		rLoc = 2312;
+		rType = 0;
+		vrLen = 2391;
+		vrLoc = 191;
+	};
+	F515C7DD037E2212018DB5B3 = {
+		fRef = F59DC6FD0354188F01AEEDB1;
+		isa = PBXTextBookmark;
+		name = "NJRDateFormatter.m: initialize";
+		rLen = 0;
+		rLoc = 324;
+		rType = 0;
+		vrLen = 1844;
+		vrLoc = 0;
+	};
+	F515C7DE037E2212018DB5B3 = {
+		fRef = F59F6CB50354E7DA01AEEDB1;
+		isa = PBXTextBookmark;
+		name = "PSAlarm.m: initialize";
+		rLen = 0;
+		rLoc = 862;
+		rType = 0;
+		vrLen = 1349;
+		vrLoc = 8504;
+	};
+	F515C7DF037E2212018DB5B3 = {
+		fRef = F59DC6E80353F58B01AEEDB1;
+		isa = PBXTextBookmark;
+		name = "NJRIntegerFilter.m: isPartialStringValid:proposedSelectedRange:originalString:originalSelectedRange:errorDescription:";
+		rLen = 0;
+		rLoc = 1008;
+		rType = 0;
+		vrLen = 1519;
+		vrLoc = 0;
+	};
+	F515C7E0037E2212018DB5B3 = {
+		fRef = F59DC6FD0354188F01AEEDB1;
+		isa = PBXTextBookmark;
+		name = "NJRDateFormatter.m: 177";
+		rLen = 0;
+		rLoc = 8262;
+		rType = 0;
+		vrLen = 1856;
+		vrLoc = 6454;
+	};
+	F515C7E1037E2212018DB5B3 = {
+		fRef = F59E4C0E036B43D2016B311C;
+		isa = PBXTextBookmark;
+		name = "NSMovie-NJRExtensions.h: 1";
+		rLen = 0;
+		rLoc = 0;
+		rType = 0;
+		vrLen = 283;
+		vrLoc = 0;
+	};
+	F515C7E2037E2212018DB5B3 = {
+		fRef = F59DC6FD0354188F01AEEDB1;
+		isa = PBXTextBookmark;
+		name = "NJRDateFormatter.m: localizedTimeFormatIncludingSeconds:";
+		rLen = 0;
+		rLoc = 7242;
+		rType = 0;
+		vrLen = 1867;
+		vrLoc = 6395;
+	};
+	F515C7E3037E2212018DB5B3 = {
+		isa = PBXFileReference;
+		name = NSFormatter.h;
+		path = /System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSFormatter.h;
+		refType = 0;
+	};
+	F515C7E4037E2212018DB5B3 = {
+		isa = PBXFileReference;
+		name = NSMatrix.h;
+		path = /System/Library/Frameworks/AppKit.framework/Versions/C/Headers/NSMatrix.h;
+		refType = 0;
+	};
+	F515C7E5037E2212018DB5B3 = {
+		isa = PBXFileReference;
+		name = NSPopUpButton.html;
+		path = /System/Library/Frameworks/AppKit.framework/Versions/C/Resources/English.lproj/Documentation/Reference/ObjC_classic/Classes/NSPopUpButton.html;
+		refType = 0;
+	};
+	F515C7E6037E2212018DB5B3 = {
+		isa = PBXFileReference;
+		name = Controls.h;
+		path = /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/Controls.h;
+		refType = 0;
+	};
+	F515C7E7037E2212018DB5B3 = {
+		isa = PBXFileReference;
+		name = index.html;
+		path = /Developer/Documentation/Carbon/text/DateTimeUtilities/Date_Time_an_nt_Utilities/index.html;
+		refType = 0;
+	};
+	F515C7E8037E2212018DB5B3 = {
+		isa = PBXFileReference;
+		name = NSMenu.h;
+		path = /System/Library/Frameworks/AppKit.framework/Versions/C/Headers/NSMenu.h;
+		refType = 0;
+	};
+	F515C7E9037E2212018DB5B3 = {
+		isa = PBXFileReference;
+		name = NSButtonCell.h;
+		path = /System/Library/Frameworks/AppKit.framework/Versions/C/Headers/NSButtonCell.h;
+		refType = 0;
+	};
+	F515C7EA037E2212018DB5B3 = {
+		isa = PBXFileReference;
+		name = NSActionCell.h;
+		path = /System/Library/Frameworks/AppKit.framework/Versions/C/Headers/NSActionCell.h;
+		refType = 0;
+	};
+	F515C7EB037E2212018DB5B3 = {
+		isa = PBXFileReference;
+		name = NSClassDescription.h;
+		path = /System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSClassDescription.h;
+		refType = 0;
+	};
+	F515C7EC037E2212018DB5B3 = {
+		isa = PBXFileReference;
+		name = NSUserDefaults.html;
+		path = /System/Library/Frameworks/Foundation.framework/Versions/C/Resources/English.lproj/Documentation/Reference/ObjC_classic/Classes/NSUserDefaults.html;
+		refType = 0;
+	};
+	F515C7ED037E2212018DB5B3 = {
+		isa = PBXFileReference;
+		name = NSUserDefaults.h;
+		path = /System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSUserDefaults.h;
+		refType = 0;
+	};
+	F515C7EE037E2212018DB5B3 = {
+		isa = PBXFileReference;
+		name = index.html;
+		path = /Developer/Documentation/Carbon/text/DateTimeUtilities/Date_Time_an_nt_Utilities/index.html;
+		refType = 0;
+	};
+	F515C7EF037E2212018DB5B3 = {
+		isa = PBXFileReference;
+		name = NSCell.h;
+		path = /System/Library/Frameworks/AppKit.framework/Versions/C/Headers/NSCell.h;
+		refType = 0;
+	};
+	F515C7F0037E2212018DB5B3 = {
+		isa = PBXFileReference;
+		name = Controls.h;
+		path = /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/Controls.h;
+		refType = 0;
+	};
+	F515C7F1037E2212018DB5B3 = {
+		isa = PBXFileReference;
+		name = NSDate.h;
+		path = /System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSDate.h;
+		refType = 0;
+	};
+	F515C7F2037E2212018DB5B3 = {
+		isa = PBXFileReference;
+		name = NSScanner.html;
+		path = /System/Library/Frameworks/Foundation.framework/Versions/C/Resources/English.lproj/Documentation/Reference/ObjC_classic/Classes/NSScanner.html;
+		refType = 0;
+	};
+	F515C7F3037E2212018DB5B3 = {
+		isa = PBXFileReference;
+		name = NSScanner.h;
+		path = /System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSScanner.h;
+		refType = 0;
+	};
+	F515C7F4037E2212018DB5B3 = {
+		isa = PBXFileReference;
+		name = NSButtonCell.html;
+		path = /System/Library/Frameworks/AppKit.framework/Versions/C/Resources/English.lproj/Documentation/Reference/ObjC_classic/Classes/NSButtonCell.html;
+		refType = 0;
+	};
+	F515C7F5037E2212018DB5B3 = {
+		isa = PBXFileReference;
+		name = NSCharacterSet.h;
+		path = /System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSCharacterSet.h;
+		refType = 0;
+	};
+	F515C7F6037E2212018DB5B3 = {
+		isa = PBXFileReference;
+		name = NSRange.h;
+		path = /System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSRange.h;
+		refType = 0;
+	};
+	F515C7F7037E2212018DB5B3 = {
+		isa = PBXFileReference;
+		name = ControlDefinitions.h;
+		path = /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/ControlDefinitions.h;
+		refType = 0;
+	};
+	F515C7F8037E2212018DB5B3 = {
+		isa = PBXFileReference;
+		name = NSCalendarDate.html;
+		path = /System/Library/Frameworks/Foundation.framework/Versions/C/Resources/English.lproj/Documentation/Reference/ObjC_classic/Classes/NSCalendarDate.html;
+		refType = 0;
+	};
+	F515C7F9037E2212018DB5B3 = {
+		isa = PBXFileReference;
+		name = NSDateFormatter.h;
+		path = /System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSDateFormatter.h;
+		refType = 0;
+	};
+	F515C7FA037E2212018DB5B3 = {
+		isa = PBXFileReference;
+		name = ControlDefinitions.h;
+		path = /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/ControlDefinitions.h;
+		refType = 0;
+	};
+	F515C7FB037E2212018DB5B3 = {
+		isa = PBXFileReference;
+		name = NSCalendarDate.h;
+		path = /System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSCalendarDate.h;
+		refType = 0;
+	};
+	F515C7FC037E2212018DB5B3 = {
+		isa = PBXFileReference;
+		name = NSMenu.html;
+		path = /System/Library/Frameworks/AppKit.framework/Versions/C/Resources/English.lproj/Documentation/Reference/ObjC_classic/Classes/NSMenu.html;
+		refType = 0;
+	};
+	F515C7FD037E2212018DB5B3 = {
+		isa = PBXFileReference;
+		name = NSMenuItem.h;
+		path = /System/Library/Frameworks/AppKit.framework/Versions/C/Headers/NSMenuItem.h;
+		refType = 0;
+	};
+	F515C7FE037E2212018DB5B3 = {
+		isa = PBXFileReference;
+		name = NSString.h;
+		path = /System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSString.h;
+		refType = 0;
+	};
+	F515C7FF037E2212018DB5B3 = {
+		isa = PBXFileReference;
+		name = NSPopUpButton.h;
+		path = /System/Library/Frameworks/AppKit.framework/Versions/C/Headers/NSPopUpButton.h;
+		refType = 0;
+	};
+	F515C800037E2212018DB5B3 = {
+		isa = PBXFileReference;
+		name = NSDateFormatter.h;
+		path = /System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSDateFormatter.h;
+		refType = 0;
+	};
+	F515C801037E2212018DB5B3 = {
+		isa = PBXFileReference;
+		name = NSString.h;
+		path = /System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSString.h;
+		refType = 0;
+	};
+	F517D63D0356AC88016D68B9 = {
+		fRef = F59F6CB50354E7DA01AEEDB1;
+		isa = PBXTextBookmark;
+		name = "PSAlarm.m: scheduledTimerWithTarget:selector:";
+		rLen = 0;
+		rLoc = 7029;
+		rType = 0;
+		vrLen = 822;
+		vrLoc = 4232;
+	};
+	F51E9997037C98BC01827940 = {
+		fRef = F5F2418A036A910B01FE7503;
+		isa = PBXTextBookmark;
+		name = "SoundFileManager.h: addSoundFilesFromDirectory:asType:";
+		rLen = 0;
+		rLoc = 585;
+		rType = 0;
+		vrLen = 931;
+		vrLoc = 0;
+	};
+	F51E9998037C98BC01827940 = {
+		fRef = F59DC6E90353F58B01AEEDB1;
+		isa = PBXTextBookmark;
+		name = "NJRIntegerFilter.h: 1";
+		rLen = 0;
+		rLoc = 0;
+		rType = 0;
+		vrLen = 247;
+		vrLoc = 0;
+	};
+	F51E999C037C98BC01827940 = {
+		fRef = F51E99D1037C98BC01827940;
+		isa = PBXTextBookmark;
+		name = "NSTextField.h: 35";
+		rLen = 6;
+		rLoc = 789;
+		rType = 0;
+		vrLen = 966;
+		vrLoc = 0;
+	};
+	F51E999E037C98BC01827940 = {
+		fRef = F51E99D3037C98BC01827940;
+		isa = PBXTextBookmark;
+		name = "NSWindow.h: 218";
+		rLen = 33;
+		rLoc = 8367;
+		rType = 0;
+		vrLen = 1190;
+		vrLoc = 7864;
+	};
+	F51E99A1037C98BC01827940 = {
+		fRef = F552A8AC0358E8B201AEEDB1;
+		isa = PBXTextBookmark;
+		name = "NJRScrollView.m: 1";
+		rLen = 0;
+		rLoc = 0;
+		rType = 0;
+		vrLen = 934;
+		vrLoc = 0;
+	};
+	F51E99A3037C98BC01827940 = {
+		fRef = F51E99CC037C98BC01827940;
+		isa = PBXTextBookmark;
+		name = "NSControl.h: 97";
+		rLen = 26;
+		rLoc = 2600;
+		rType = 0;
+		vrLen = 1409;
+		vrLoc = 2392;
+	};
+	F51E99A4037C98BC01827940 = {
+		fRef = F51E99CD037C98BC01827940;
+		isa = PBXTextBookmark;
+		name = "NSTextView.h: 63";
+		rLen = 30;
+		rLoc = 3007;
+		rType = 0;
+		vrLen = 1721;
+		vrLoc = 2053;
+	};
+	F51E99A8037C98BC01827940 = {
+		fRef = F51E99D0037C98BC01827940;
+		isa = PBXTextBookmark;
+		name = "NSNumberFormatter.h: 10";
+		rLen = 11;
+		rLoc = 249;
+		rType = 0;
+		vrLen = 1384;
+		vrLoc = 82;
+	};
+	F51E99AA037C98BC01827940 = {
+		fRef = F51E99D4037C98BC01827940;
+		isa = PBXTextBookmark;
+		name = "NSAttributedString.h: 25";
+		rLen = 0;
+		rLoc = 945;
+		rType = 0;
+		vrLen = 1325;
+		vrLoc = 0;
+	};
+	F51E99AB037C98BC01827940 = {
+		fRef = F51E99DC037C98BC01827940;
+		isa = PBXTextBookmark;
+		name = "NSTextStorage.h: 28";
+		rLen = 25;
+		rLoc = 1082;
+		rType = 0;
+		vrLen = 1267;
+		vrLoc = 0;
+	};
+	F51E99AE037C98BC01827940 = {
+		fRef = F51E99D5037C98BC01827940;
+		isa = PBXTextBookmark;
+		name = "DateTimeUtils.h: 340";
+		rLen = 12;
+		rLoc = 10376;
+		rType = 0;
+		vrLen = 979;
+		vrLoc = 9547;
+	};
+	F51E99AF037C98BC01827940 = {
+		fRef = F51E99D6037C98BC01827940;
+		isa = PBXBookmark;
+	};
+	F51E99B0037C98BC01827940 = {
+		fRef = F51E99D9037C98BC01827940;
+		isa = PBXBookmark;
+	};
+	F51E99B1037C98BC01827940 = {
+		fRef = F51E99DA037C98BC01827940;
+		isa = PBXBookmark;
+	};
+	F51E99B3037C98BC01827940 = {
+		fRef = F59DC6E80353F58B01AEEDB1;
+		isa = PBXTextBookmark;
+		name = "NJRIntegerFilter.m: 1";
+		rLen = 0;
+		rLoc = 0;
+		rType = 0;
+		vrLen = 1411;
+		vrLoc = 0;
+	};
+	F51E99B4037C98BC01827940 = {
+		fRef = F59DC6FD0354188F01AEEDB1;
+		isa = PBXTextBookmark;
+		name = "NJRDateFormatter.m: 13";
+		rLen = 0;
+		rLoc = 7242;
+		rType = 0;
+		vrLen = 1105;
+		vrLoc = 0;
+	};
+	F51E99B5037C98BC01827940 = {
+		fRef = F59DC6D40353C9E601AEEDB1;
+		isa = PBXTextBookmark;
+		name = "PSAlarmSetController.m: playSoundChanged:";
+		rLen = 43;
+		rLoc = 9214;
+		rType = 0;
+		vrLen = 1346;
+		vrLoc = 6574;
+	};
+	F51E99B6037C98BC01827940 = {
+		fRef = F5F2418A036A910B01FE7503;
+		isa = PBXTextBookmark;
+		name = "SoundFileManager.h: addSoundFilesFromDirectory:asType:";
+		rLen = 0;
+		rLoc = 585;
+		rType = 0;
+		vrLen = 931;
+		vrLoc = 0;
+	};
+	F51E99B7037C98BC01827940 = {
+		fRef = F59DC6E90353F58B01AEEDB1;
+		isa = PBXTextBookmark;
+		name = "NJRIntegerFilter.h: 1";
+		rLen = 0;
+		rLoc = 0;
+		rType = 0;
+		vrLen = 247;
+		vrLoc = 0;
+	};
+	F51E99B8037C98BC01827940 = {
+		fRef = F59DC6E80353F58B01AEEDB1;
+		isa = PBXTextBookmark;
+		name = "NJRIntegerFilter.m: 22";
+		rLen = 0;
+		rLoc = 1008;
+		rType = 0;
+		vrLen = 1360;
+		vrLoc = 159;
+	};
+	F51E99B9037C98BC01827940 = {
+		fRef = F59DC6D40353C9E601AEEDB1;
+		isa = PBXTextBookmark;
+		name = "PSAlarmSetController.m: playSoundChanged:";
+		rLen = 0;
+		rLoc = 9234;
+		rType = 0;
+		vrLen = 1346;
+		vrLoc = 6574;
+	};
+	F51E99BA037C98BC01827940 = {
+		fRef = F5F240B5036A862101FE7503;
+		isa = PBXTextBookmark;
+		name = "NJRFSObjectSelector.m: setEnabled:";
+		rLen = 34;
+		rLoc = 5647;
+		rType = 0;
+		vrLen = 1011;
+		vrLoc = 4974;
+	};
+	F51E99BB037C98BC01827940 = {
+		fRef = F5F19870036B822E01EB0372;
+		isa = PBXTextBookmark;
+		name = "NJRVoicePopUpButton.m: __VERSION__";
+		rLen = 0;
+		rLoc = 0;
+		rType = 0;
+		vrLen = 1247;
+		vrLoc = 0;
+	};
+	F51E99BC037C98BC01827940 = {
+		fRef = F5F240CC036A877301FE7503;
+		isa = PBXTextBookmark;
+		name = "NJRQTMediaPopUpButton.m: 350";
+		rLen = 191;
+		rLoc = 13074;
+		rType = 0;
+		vrLen = 1247;
+		vrLoc = 12256;
+	};
+	F51E99BD037C98BC01827940 = {
+		fRef = F5F19870036B822E01EB0372;
+		isa = PBXTextBookmark;
+		name = "NJRVoicePopUpButton.m: _previewVoice";
+		rLen = 0;
+		rLoc = 3092;
+		rType = 0;
+		vrLen = 1002;
+		vrLoc = 2384;
+	};
+	F51E99BE037C98BC01827940 = {
+		fRef = F59DC6D40353C9E601AEEDB1;
+		isa = PBXTextBookmark;
+		name = "PSAlarmSetController.m: controlTextDidChange:";
+		rLen = 0;
+		rLoc = 14675;
+		rType = 0;
+		vrLen = 842;
+		vrLoc = 12261;
+	};
+	F51E99BF037C98BC01827940 = {
+		fRef = F59DC6D30353C9E601AEEDB1;
+		isa = PBXTextBookmark;
+		name = "PSAlarmSetController.h: 41";
+		rLen = 0;
+		rLoc = 1396;
+		rType = 0;
+		vrLen = 1344;
+		vrLoc = 462;
+	};
+	F51E99C0037C98BC01827940 = {
+		fRef = F59DC6D40353C9E601AEEDB1;
+		isa = PBXTextBookmark;
+		name = "PSAlarmSetController.m: wasFirstResponder";
+		rLen = 0;
+		rLoc = 9733;
+		rType = 0;
+		vrLen = 1472;
+		vrLoc = 7170;
+	};
+	F51E99C1037C98BC01827940 = {
+		fRef = F59DC6D30353C9E601AEEDB1;
+		isa = PBXTextBookmark;
+		name = "PSAlarmSetController.h: 41";
+		rLen = 0;
+		rLoc = 1396;
+		rType = 0;
+		vrLen = 1344;
+		vrLoc = 462;
+	};
+	F51E99C2037C98BC01827940 = {
+		fRef = F59DC6D40353C9E601AEEDB1;
+		isa = PBXTextBookmark;
+		name = "PSAlarmSetController.m: doScriptChanged:";
+		rLen = 0;
+		rLoc = 10538;
+		rType = 0;
+		vrLen = 1411;
+		vrLoc = 0;
+	};
+	F51E99C3037C98BC01827940 = {
+		fRef = F517D63F0356B80C016D68B9;
+		isa = PBXTextBookmark;
+		name = "PSAlarmsController.h: 1";
+		rLen = 0;
+		rLoc = 0;
+		rType = 0;
+		vrLen = 393;
+		vrLoc = 0;
+	};
+	F51E99C4037C98BC01827940 = {
+		fRef = F51E99DB037C98BC01827940;
+		isa = PBXTextBookmark;
+		name = "DateTimeUtils.h: 340";
+		rLen = 12;
+		rLoc = 10376;
+		rType = 0;
+		vrLen = 979;
+		vrLoc = 9547;
+	};
+	F51E99C5037C98BC01827940 = {
+		fRef = F51E99DD037C98BC01827940;
+		isa = PBXBookmark;
+	};
+	F51E99C6037C98BC01827940 = {
+		fRef = F51E99CB037C98BC01827940;
+		isa = PBXBookmark;
+	};
+	F51E99C7037C98BC01827940 = {
+		fRef = F51E99D8037C98BC01827940;
+		isa = PBXBookmark;
+	};
+	F51E99CB037C98BC01827940 = {
+		isa = PBXFileReference;
+		name = carbon.html;
+		path = /Developer/Documentation/Carbon/carbon.html;
+		refType = 0;
+	};
+	F51E99CC037C98BC01827940 = {
+		isa = PBXFileReference;
+		name = NSControl.h;
+		path = /System/Library/Frameworks/AppKit.framework/Versions/C/Headers/NSControl.h;
+		refType = 0;
+	};
+	F51E99CD037C98BC01827940 = {
+		isa = PBXFileReference;
+		name = NSTextView.h;
+		path = /System/Library/Frameworks/AppKit.framework/Versions/C/Headers/NSTextView.h;
+		refType = 0;
+	};
+	F51E99D0037C98BC01827940 = {
+		isa = PBXFileReference;
+		name = NSNumberFormatter.h;
+		path = /System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSNumberFormatter.h;
+		refType = 0;
+	};
+	F51E99D1037C98BC01827940 = {
+		isa = PBXFileReference;
+		name = NSTextField.h;
+		path = /System/Library/Frameworks/AppKit.framework/Versions/C/Headers/NSTextField.h;
+		refType = 0;
+	};
+	F51E99D3037C98BC01827940 = {
+		isa = PBXFileReference;
+		name = NSWindow.h;
+		path = /System/Library/Frameworks/AppKit.framework/Versions/C/Headers/NSWindow.h;
+		refType = 0;
+	};
+	F51E99D4037C98BC01827940 = {
+		isa = PBXFileReference;
+		name = NSAttributedString.h;
+		path = /System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSAttributedString.h;
+		refType = 0;
+	};
+	F51E99D5037C98BC01827940 = {
+		isa = PBXFileReference;
+		name = DateTimeUtils.h;
+		path = /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CarbonCore.framework/Versions/A/Headers/DateTimeUtils.h;
+		refType = 0;
+	};
+	F51E99D6037C98BC01827940 = {
+		isa = PBXFileReference;
+		name = Converting_D_resentations.html;
+		path = /Developer/Documentation/Carbon/text/DateTimeUtilities/Date_Time_an_nt_Utilities/Functions/Converting_D_resentations.html;
+		refType = 0;
+	};
+	F51E99D8037C98BC01827940 = {
+		isa = PBXFileReference;
+		name = datetimeutilities.html;
+		path = /Developer/Documentation/Carbon/text/DateTimeUtilities/datetimeutilities.html;
+		refType = 0;
+	};
+	F51E99D9037C98BC01827940 = {
+		isa = PBXFileReference;
+		name = carbon.html;
+		path = /Developer/Documentation/Carbon/carbon.html;
+		refType = 0;
+	};
+	F51E99DA037C98BC01827940 = {
+		isa = PBXFileReference;
+		name = datetimeutilities.html;
+		path = /Developer/Documentation/Carbon/text/DateTimeUtilities/datetimeutilities.html;
+		refType = 0;
+	};
+	F51E99DB037C98BC01827940 = {
+		isa = PBXFileReference;
+		name = DateTimeUtils.h;
+		path = /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CarbonCore.framework/Versions/A/Headers/DateTimeUtils.h;
+		refType = 0;
+	};
+	F51E99DC037C98BC01827940 = {
+		isa = PBXFileReference;
+		name = NSTextStorage.h;
+		path = /System/Library/Frameworks/AppKit.framework/Versions/C/Headers/NSTextStorage.h;
+		refType = 0;
+	};
+	F51E99DD037C98BC01827940 = {
+		isa = PBXFileReference;
+		name = Converting_D_resentations.html;
+		path = /Developer/Documentation/Carbon/text/DateTimeUtilities/Date_Time_an_nt_Utilities/Functions/Converting_D_resentations.html;
+		refType = 0;
+	};
+	F52441D303787D1001AEEDB1 = {
+		fileReference = F59E4C0D036B43D2016B311C;
+		isa = PBXFileBreakpoint;
+		lineNumber = 32;
+		state = 1;
+	};
+	F560E2F5035750C101A4E466 = {
+		fRef = F560E2F10357506401A4E466;
+		isa = PBXTextBookmark;
+		name = "PSApplication.m: 16";
+		rLen = 0;
+		rLoc = 1510;
+		rType = 0;
+		vrLen = 296;
+		vrLoc = 0;
+	};
+	F560E2F80357779E01A4E466 = {
+		fRef = F517D6420356B8CC016D68B9;
+		isa = PBXTextBookmark;
+		name = "PSAlarms.m: allAlarms";
+		rLen = 0;
+		rLoc = 943;
+		rType = 0;
+		vrLen = 916;
+		vrLoc = 0;
+	};
+	F560E2FB035786FE01A4E466 = {
+		fRef = F517D63E0356B80C016D68B9;
+		isa = PBXTextBookmark;
+		name = "PSAlarmsController.m: numberOfRowsInTableView:";
+		rLen = 0;
+		rLoc = 1720;
+		rType = 0;
+		vrLen = 1085;
+		vrLoc = 0;
+	};
+	F596730D0355CFA301407038 = {
+		isa = PBXSymbolicBreakpoint;
+		state = 1;
+		symbolName = "-[_NSZombie methodSignatureForSelector:]";
+	};
+	F59B967103538BE201154278 = {
+		activeArgIndex = 2147483647;
+		argumentStrings = (
+		);
+		debuggerPlugin = GDBDebugging;
+		dylibVariantSuffix = "";
+		enableDebugStr = 1;
+		environmentEntries = (
+			{
+				active = YES;
+				name = NSZombieEnabled;
+				value = YES;
+			},
+		);
+		isa = PBXExecutable;
+		name = Pester;
+		shlibInfoDictList = (
+		);
+		sourceDirectories = (
+		);
+	};
+	F59DC6D20353C43201AEEDB1 = {
+		children = (
+			F59DC6D70353CA6401AEEDB1,
+			F59DC6D90353D35201AEEDB1,
+			F59DC6DA0353D72901AEEDB1,
+			F59DC6E20353E0EB01AEEDB1,
+			F517D63D0356AC88016D68B9,
+			F560E2F5035750C101A4E466,
+			F560E2F80357779E01A4E466,
+			F560E2FB035786FE01A4E466,
+			F5F19881036B90CD01EB0372,
+			F5F19882036B930001EB0372,
+			F515C769037DFB63018DB5B3,
+			F515C76A037E01E1018DB5B3,
+		);
+		isa = PBXBookmarkGroup;
+		name = Root;
+	};
+	F59DC6D70353CA6401AEEDB1 = {
+		fRef = F59DC6D40353C9E601AEEDB1;
+		isa = PBXTextBookmark;
+		name = "PSAlarmSetController.m: 16";
+		rLen = 0;
+		rLoc = 4938;
+		rType = 0;
+		vrLen = 291;
+		vrLoc = 0;
+	};
+	F59DC6D90353D35201AEEDB1 = {
+		fRef = F59DC6D40353C9E601AEEDB1;
+		isa = PBXTextBookmark;
+		name = "PSAlarmSetController.m: inAtChanged:";
+		rLen = 36;
+		rLoc = 8146;
+		rType = 0;
+		vrLen = 478;
+		vrLoc = 416;
+	};
+	F59DC6DA0353D72901AEEDB1 = {
+		fRef = F59DC6D30353C9E601AEEDB1;
+		isa = PBXTextBookmark;
+		name = "PSAlarmSetController.h: 25";
+		rLen = 0;
+		rLoc = 1439;
+		rType = 0;
+		vrLen = 758;
+		vrLoc = 0;
+	};
+	F59DC6E20353E0EB01AEEDB1 = {
+		fRef = F59DC6D30353C9E601AEEDB1;
+		isa = PBXTextBookmark;
+		name = "PSAlarmSetController.h: 31";
+		rLen = 0;
+		rLoc = 1800;
+		rType = 0;
+		vrLen = 826;
+		vrLoc = 0;
+	};
+	F5F19881036B90CD01EB0372 = {
+		fRef = F59DC6D40353C9E601AEEDB1;
+		isa = PBXTextBookmark;
+		name = "PSAlarmSetController.m: setAlarm:";
+		rLen = 0;
+		rLoc = 11739;
+		rType = 0;
+		vrLen = 1431;
+		vrLoc = 8406;
+	};
+	F5F19882036B930001EB0372 = {
+		fRef = F59DC6D30353C9E601AEEDB1;
+		isa = PBXTextBookmark;
+		name = "PSAlarmSetController.h: 14";
+		rLen = 0;
+		rLoc = 278;
+		rType = 0;
+		vrLen = 1330;
+		vrLoc = 385;
+	};
+}
Index: /releases/Pester/1.1a1/Source/Pester.pbproj/project.pbxproj
===================================================================
--- /releases/Pester/1.1a1/Source/Pester.pbproj/project.pbxproj	(revision 44)
+++ /releases/Pester/1.1a1/Source/Pester.pbproj/project.pbxproj	(revision 44)
@@ -0,0 +1,1466 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 38;
+	objects = {
+		080E96DDFE201D6D7F000001 = {
+			children = (
+				F560E2F20357506401A4E466,
+				F560E2F10357506401A4E466,
+				F560E311035787D501A4E466,
+				F560E314035787F901A4E466,
+				F59E47AF036AAC31016B311C,
+				F560E312035787E001A4E466,
+				F560E313035787ED01A4E466,
+			);
+			isa = PBXGroup;
+			name = Classes;
+			refType = 4;
+		};
+		089C165CFE840E0CC02AAC07 = {
+			children = (
+				089C165DFE840E0CC02AAC07,
+			);
+			isa = PBXVariantGroup;
+			name = InfoPlist.strings;
+			refType = 4;
+		};
+		089C165DFE840E0CC02AAC07 = {
+			fileEncoding = 10;
+			isa = PBXFileReference;
+			name = English;
+			path = English.lproj/InfoPlist.strings;
+			refType = 4;
+		};
+//080
+//081
+//082
+//083
+//084
+//100
+//101
+//102
+//103
+//104
+		1058C7A0FEA54F0111CA2CBB = {
+			children = (
+				1058C7A1FEA54F0111CA2CBB,
+				F5F240D0036A87E301FE7503,
+				F5F24187036A90D901FE7503,
+				F59E4BEA036B33EC016B311C,
+			);
+			isa = PBXGroup;
+			name = "Linked Frameworks";
+			refType = 4;
+		};
+		1058C7A1FEA54F0111CA2CBB = {
+			isa = PBXFrameworkReference;
+			name = Cocoa.framework;
+			path = /System/Library/Frameworks/Cocoa.framework;
+			refType = 0;
+		};
+		1058C7A2FEA54F0111CA2CBB = {
+			children = (
+				29B97325FDCFA39411CA2CEA,
+				29B97324FDCFA39411CA2CEA,
+			);
+			isa = PBXGroup;
+			name = "Other Frameworks";
+			refType = 4;
+		};
+		1058C7A3FEA54F0111CA2CBB = {
+			fileRef = 1058C7A1FEA54F0111CA2CBB;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+//100
+//101
+//102
+//103
+//104
+//170
+//171
+//172
+//173
+//174
+		17587328FF379C6511CA2CBB = {
+			isa = PBXApplicationReference;
+			path = Pester.app;
+			refType = 3;
+		};
+//170
+//171
+//172
+//173
+//174
+//190
+//191
+//192
+//193
+//194
+		19C28FACFE9D520D11CA2CBB = {
+			children = (
+				17587328FF379C6511CA2CBB,
+			);
+			isa = PBXGroup;
+			name = Products;
+			refType = 4;
+		};
+//190
+//191
+//192
+//193
+//194
+//290
+//291
+//292
+//293
+//294
+		29B97313FDCFA39411CA2CEA = {
+			buildStyles = (
+				4A9504CCFFE6A4B311CA0CBA,
+				4A9504CDFFE6A4B311CA0CBA,
+			);
+			isa = PBXProject;
+			mainGroup = 29B97314FDCFA39411CA2CEA;
+			projectDirPath = "";
+			targets = (
+				29B97326FDCFA39411CA2CEA,
+			);
+		};
+		29B97314FDCFA39411CA2CEA = {
+			children = (
+				080E96DDFE201D6D7F000001,
+				29B97315FDCFA39411CA2CEA,
+				29B97317FDCFA39411CA2CEA,
+				29B97323FDCFA39411CA2CEA,
+				19C28FACFE9D520D11CA2CBB,
+			);
+			isa = PBXGroup;
+			name = Pester;
+			path = "";
+			refType = 4;
+		};
+		29B97315FDCFA39411CA2CEA = {
+			children = (
+				29B97316FDCFA39411CA2CEA,
+			);
+			isa = PBXGroup;
+			name = "Other Sources";
+			path = "";
+			refType = 4;
+		};
+		29B97316FDCFA39411CA2CEA = {
+			isa = PBXFileReference;
+			path = main.m;
+			refType = 4;
+		};
+		29B97317FDCFA39411CA2CEA = {
+			children = (
+				F5243DA40378681101AEEDB1,
+				29B97318FDCFA39411CA2CEA,
+				F560E2EE03574F8A01A4E466,
+				F59DC6DF0353DF5E01AEEDB1,
+				F59E4BDE036B1D94016B311C,
+				089C165CFE840E0CC02AAC07,
+				F59DD44F0358ABF4017318D0,
+				F589C9C6035AA12B0155F4B2,
+				F59DC6E30353ED1201AEEDB1,
+			);
+			isa = PBXGroup;
+			name = Resources;
+			path = "";
+			refType = 4;
+		};
+		29B97318FDCFA39411CA2CEA = {
+			children = (
+				29B97319FDCFA39411CA2CEA,
+			);
+			isa = PBXVariantGroup;
+			name = MainMenu.nib;
+			path = "";
+			refType = 4;
+		};
+		29B97319FDCFA39411CA2CEA = {
+			isa = PBXFileReference;
+			name = English;
+			path = English.lproj/MainMenu.nib;
+			refType = 4;
+		};
+		29B97323FDCFA39411CA2CEA = {
+			children = (
+				1058C7A0FEA54F0111CA2CBB,
+				1058C7A2FEA54F0111CA2CBB,
+			);
+			isa = PBXGroup;
+			name = Frameworks;
+			path = "";
+			refType = 4;
+		};
+		29B97324FDCFA39411CA2CEA = {
+			isa = PBXFrameworkReference;
+			name = AppKit.framework;
+			path = /System/Library/Frameworks/AppKit.framework;
+			refType = 0;
+		};
+		29B97325FDCFA39411CA2CEA = {
+			isa = PBXFrameworkReference;
+			name = Foundation.framework;
+			path = /System/Library/Frameworks/Foundation.framework;
+			refType = 0;
+		};
+		29B97326FDCFA39411CA2CEA = {
+			buildPhases = (
+				29B97327FDCFA39411CA2CEA,
+				29B97328FDCFA39411CA2CEA,
+				29B9732BFDCFA39411CA2CEA,
+				29B9732DFDCFA39411CA2CEA,
+			);
+			buildSettings = {
+				FRAMEWORK_SEARCH_PATHS = "\"$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks\"";
+				HEADER_SEARCH_PATHS = "";
+				INSTALL_PATH = /;
+				LIBRARY_SEARCH_PATHS = "";
+				OTHER_CFLAGS = "";
+				PRODUCT_NAME = Pester;
+				SECTORDER_FLAGS = "";
+				USE_GCC3_PFE_SUPPORT = YES;
+				WARNING_CFLAGS = "-Wmost -Wno-four-char-constants -Wno-unknown-pragmas";
+				WRAPPER_EXTENSION = app;
+			};
+			dependencies = (
+			);
+			isa = PBXApplicationTarget;
+			name = Pester;
+			productInstallPath = /;
+			productName = Pester;
+			productReference = 17587328FF379C6511CA2CBB;
+			productSettingsXML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
+<!DOCTYPE plist SYSTEM \"file://localhost/System/Library/DTDs/PropertyList.dtd\">
+<plist version=\"0.9\">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>English</string>
+	<key>CFBundleExecutable</key>
+	<string>Pester</string>
+	<key>CFBundleGetInfoString</key>
+	<string>Pester 1.1a1, © 2002 Nicholas Riley</string>
+	<key>CFBundleIconFile</key>
+	<string>Application icon</string>
+	<key>CFBundleIdentifier</key>
+	<string>net.sabi.Pester</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>Pester</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.1a1</string>
+	<key>CFBundleSignature</key>
+	<string>Pstr</string>
+	<key>CFBundleVersion</key>
+	<string>1.1a1</string>
+	<key>NSMainNibFile</key>
+	<string>MainMenu</string>
+	<key>NSPrincipalClass</key>
+	<string>PSApplication</string>
+</dict>
+</plist>
+";
+			shouldUseHeadermap = 1;
+		};
+		29B97327FDCFA39411CA2CEA = {
+			buildActionMask = 2147483647;
+			files = (
+				F59DC6D50353C9E601AEEDB1,
+				F59DC6DD0353DE5101AEEDB1,
+				F59DC6EB0353F58B01AEEDB1,
+				F59DC6FE0354188F01AEEDB1,
+				F59F6CB60354E7DA01AEEDB1,
+				F517D6410356B80C016D68B9,
+				F517D6450356B8CC016D68B9,
+				F560E2F40357506501A4E466,
+				F560E310035787BC01A4E466,
+				F560E3170357895001A4E466,
+				F552A8AD0358E8B201AEEDB1,
+				F5F240B7036A862301FE7503,
+				F5F240BA036A867901FE7503,
+				F5F2418B036A910B01FE7503,
+				F59E479E036AA5E9016B311C,
+				F59E47A1036AA5E9016B311C,
+				F59E47A2036AA5E9016B311C,
+				F59E47A3036AA5E9016B311C,
+				F59E47A4036AA5E9016B311C,
+				F59E47AA036AAA6D016B311C,
+				F59E47AE036AAC1E016B311C,
+				F59E47B3036AACD1016B311C,
+				F59E47B7036AAD55016B311C,
+				F59E47BB036AB217016B311C,
+				F59E47BF036AB228016B311C,
+				F59E47C3036B1ADD016B311C,
+				F59E4BE9036B2E5E016B311C,
+				F59E4C10036B43D2016B311C,
+				F5F19868036B80AF01EB0372,
+				F5F1986F036B813101EB0372,
+				F5F19873036B822E01EB0372,
+				F5F198D3036BE57401EB0372,
+				F5F19910036D3B3101EB0372,
+				F515C768037DFB56018DB5B3,
+			);
+			isa = PBXHeadersBuildPhase;
+		};
+		29B97328FDCFA39411CA2CEA = {
+			buildActionMask = 2147483647;
+			files = (
+				F59DC6E10353DF5E01AEEDB1,
+				F59DC6E40353ED1201AEEDB1,
+				F560E2F003574F8A01A4E466,
+				F560E2F6035766F101A4E466,
+				F560E2F7035766F201A4E466,
+				F59DD4510358ABF7017318D0,
+				F589C9C7035AA12E0155F4B2,
+				F59E4BE0036B1D94016B311C,
+				F5243DA50378681101AEEDB1,
+			);
+			isa = PBXResourcesBuildPhase;
+		};
+		29B9732BFDCFA39411CA2CEA = {
+			buildActionMask = 2147483647;
+			files = (
+				29B9732CFDCFA39411CA2CEA,
+				F59DC6D60353C9E601AEEDB1,
+				F59DC6DE0353DE5101AEEDB1,
+				F59DC6EA0353F58B01AEEDB1,
+				F59DC6FF0354188F01AEEDB1,
+				F59F6CB70354E7DA01AEEDB1,
+				F517D6400356B80C016D68B9,
+				F517D6440356B8CC016D68B9,
+				F560E2F30357506401A4E466,
+				F560E30F035787BC01A4E466,
+				F560E3180357895001A4E466,
+				F552A8AE0358E8B201AEEDB1,
+				F5F240B8036A862301FE7503,
+				F5F240CB036A869401FE7503,
+				F5F240CE036A877301FE7503,
+				F59E479C036AA5E9016B311C,
+				F59E479D036AA5E9016B311C,
+				F59E479F036AA5E9016B311C,
+				F59E47A0036AA5E9016B311C,
+				F59E47A9036AAA6D016B311C,
+				F59E47AD036AAC1E016B311C,
+				F59E47B2036AACD1016B311C,
+				F59E47B6036AAD55016B311C,
+				F59E47BA036AB217016B311C,
+				F59E47BE036AB228016B311C,
+				F59E47C2036B1ADD016B311C,
+				F59E4BE8036B2E5E016B311C,
+				F59E4C0F036B43D2016B311C,
+				F5F19869036B80AF01EB0372,
+				F5F1986E036B813101EB0372,
+				F5F19872036B822E01EB0372,
+				F5F198D2036BE57401EB0372,
+				F5F198DB036BE75E01EB0372,
+				F5F1990F036D3B3101EB0372,
+			);
+			isa = PBXSourcesBuildPhase;
+		};
+		29B9732CFDCFA39411CA2CEA = {
+			fileRef = 29B97316FDCFA39411CA2CEA;
+			isa = PBXBuildFile;
+			settings = {
+				ATTRIBUTES = (
+				);
+			};
+		};
+		29B9732DFDCFA39411CA2CEA = {
+			buildActionMask = 2147483647;
+			files = (
+				1058C7A3FEA54F0111CA2CBB,
+				F5F24166036A87E401FE7503,
+				F5F24189036A90D901FE7503,
+				F59E4C0C036B33EC016B311C,
+			);
+			isa = PBXFrameworksBuildPhase;
+		};
+//290
+//291
+//292
+//293
+//294
+//4A0
+//4A1
+//4A2
+//4A3
+//4A4
+		4A9504CCFFE6A4B311CA0CBA = {
+			buildRules = (
+			);
+			buildSettings = {
+				COPY_PHASE_STRIP = NO;
+				OPTIMIZATION_CFLAGS = "-O0";
+			};
+			isa = PBXBuildStyle;
+			name = Development;
+		};
+		4A9504CDFFE6A4B311CA0CBA = {
+			buildRules = (
+			);
+			buildSettings = {
+				COPY_PHASE_STRIP = YES;
+			};
+			isa = PBXBuildStyle;
+			name = Deployment;
+		};
+//4A0
+//4A1
+//4A2
+//4A3
+//4A4
+//F50
+//F51
+//F52
+//F53
+//F54
+		F515C768037DFB56018DB5B3 = {
+			fileRef = F5F240CD036A877301FE7503;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F517D63E0356B80C016D68B9 = {
+			isa = PBXFileReference;
+			path = PSAlarmsController.m;
+			refType = 4;
+		};
+		F517D63F0356B80C016D68B9 = {
+			isa = PBXFileReference;
+			path = PSAlarmsController.h;
+			refType = 4;
+		};
+		F517D6400356B80C016D68B9 = {
+			fileRef = F517D63E0356B80C016D68B9;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F517D6410356B80C016D68B9 = {
+			fileRef = F517D63F0356B80C016D68B9;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F517D6420356B8CC016D68B9 = {
+			isa = PBXFileReference;
+			path = PSAlarms.m;
+			refType = 4;
+		};
+		F517D6430356B8CC016D68B9 = {
+			isa = PBXFileReference;
+			path = PSAlarms.h;
+			refType = 4;
+		};
+		F517D6440356B8CC016D68B9 = {
+			fileRef = F517D6420356B8CC016D68B9;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F517D6450356B8CC016D68B9 = {
+			fileRef = F517D6430356B8CC016D68B9;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F5243DA40378681101AEEDB1 = {
+			isa = PBXFileReference;
+			path = "Popup triangle.tiff";
+			refType = 4;
+		};
+		F5243DA50378681101AEEDB1 = {
+			fileRef = F5243DA40378681101AEEDB1;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F552A8AB0358E8B201AEEDB1 = {
+			isa = PBXFileReference;
+			path = NJRScrollView.h;
+			refType = 4;
+		};
+		F552A8AC0358E8B201AEEDB1 = {
+			isa = PBXFileReference;
+			path = NJRScrollView.m;
+			refType = 4;
+		};
+		F552A8AD0358E8B201AEEDB1 = {
+			fileRef = F552A8AB0358E8B201AEEDB1;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F552A8AE0358E8B201AEEDB1 = {
+			fileRef = F552A8AC0358E8B201AEEDB1;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F560E2EE03574F8A01A4E466 = {
+			children = (
+				F560E2EF03574F8A01A4E466,
+			);
+			isa = PBXVariantGroup;
+			name = Alarms.nib;
+			path = "";
+			refType = 4;
+		};
+		F560E2EF03574F8A01A4E466 = {
+			isa = PBXFileReference;
+			name = English;
+			path = English.lproj/Alarms.nib;
+			refType = 4;
+		};
+		F560E2F003574F8A01A4E466 = {
+			fileRef = F560E2EE03574F8A01A4E466;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F560E2F10357506401A4E466 = {
+			isa = PBXFileReference;
+			path = PSApplication.m;
+			refType = 4;
+		};
+		F560E2F20357506401A4E466 = {
+			isa = PBXFileReference;
+			path = PSApplication.h;
+			refType = 4;
+		};
+		F560E2F30357506401A4E466 = {
+			fileRef = F560E2F10357506401A4E466;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F560E2F40357506501A4E466 = {
+			fileRef = F560E2F20357506401A4E466;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F560E2F6035766F101A4E466 = {
+			fileRef = 29B97318FDCFA39411CA2CEA;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F560E2F7035766F201A4E466 = {
+			fileRef = 089C165CFE840E0CC02AAC07;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F560E30D035787BB01A4E466 = {
+			isa = PBXFileReference;
+			path = "NSTableView-NJRExtensions.m";
+			refType = 4;
+		};
+		F560E30E035787BC01A4E466 = {
+			isa = PBXFileReference;
+			path = "NSTableView-NJRExtensions.h";
+			refType = 4;
+		};
+		F560E30F035787BC01A4E466 = {
+			fileRef = F560E30D035787BB01A4E466;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F560E310035787BC01A4E466 = {
+			fileRef = F560E30E035787BC01A4E466;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F560E311035787D501A4E466 = {
+			children = (
+				F517D63F0356B80C016D68B9,
+				F517D63E0356B80C016D68B9,
+				F59DC6D30353C9E601AEEDB1,
+				F59DC6D40353C9E601AEEDB1,
+				F59E47B5036AAD55016B311C,
+				F59E47B4036AAD55016B311C,
+				F59DC6DB0353DE5101AEEDB1,
+				F59DC6DC0353DE5101AEEDB1,
+				F59E47C1036B1ADD016B311C,
+				F59E47C0036B1ADD016B311C,
+				F5F198D1036BE57401EB0372,
+				F5F198D0036BE57401EB0372,
+			);
+			isa = PBXGroup;
+			name = Controllers;
+			refType = 4;
+		};
+		F560E312035787E001A4E466 = {
+			children = (
+				F59F6CB40354E7DA01AEEDB1,
+				F59F6CB50354E7DA01AEEDB1,
+				F517D6430356B8CC016D68B9,
+				F517D6420356B8CC016D68B9,
+			);
+			isa = PBXGroup;
+			name = Models;
+			refType = 4;
+		};
+		F560E313035787ED01A4E466 = {
+			children = (
+				F5F19865036B806201EB0372,
+				F59E47A6036AA5FC016B311C,
+				F59E47A5036AA5F2016B311C,
+				F59DC6E90353F58B01AEEDB1,
+				F59DC6E80353F58B01AEEDB1,
+				F59DC6FC0354188F01AEEDB1,
+				F59DC6FD0354188F01AEEDB1,
+				F5F2418A036A910B01FE7503,
+				F5F1990E036D3B3101EB0372,
+				F5F1990D036D3B3101EB0372,
+				F59E4C0E036B43D2016B311C,
+				F59E4C0D036B43D2016B311C,
+				F5F198DA036BE75E01EB0372,
+				F5F198D9036BE75E01EB0372,
+			);
+			isa = PBXGroup;
+			name = Utilities;
+			refType = 4;
+		};
+		F560E314035787F901A4E466 = {
+			children = (
+				F560E30E035787BC01A4E466,
+				F560E30D035787BB01A4E466,
+				F552A8AB0358E8B201AEEDB1,
+				F552A8AC0358E8B201AEEDB1,
+				F560E3150357895001A4E466,
+				F560E3160357895001A4E466,
+				F5F240B6036A862101FE7503,
+				F5F240B5036A862101FE7503,
+				F5F240CD036A877301FE7503,
+				F5F240CC036A877301FE7503,
+				F5F19871036B822E01EB0372,
+				F5F19870036B822E01EB0372,
+			);
+			isa = PBXGroup;
+			name = Views;
+			path = "";
+			refType = 4;
+		};
+		F560E3150357895001A4E466 = {
+			isa = PBXFileReference;
+			path = NJRHistoryTrackingComboBox.h;
+			refType = 4;
+		};
+		F560E3160357895001A4E466 = {
+			isa = PBXFileReference;
+			path = NJRHistoryTrackingComboBox.m;
+			refType = 4;
+		};
+		F560E3170357895001A4E466 = {
+			fileRef = F560E3150357895001A4E466;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F560E3180357895001A4E466 = {
+			fileRef = F560E3160357895001A4E466;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F589C9C6035AA12B0155F4B2 = {
+			isa = PBXFileReference;
+			path = "Read Me.rtfd";
+			refType = 4;
+		};
+		F589C9C7035AA12E0155F4B2 = {
+			fileRef = F589C9C6035AA12B0155F4B2;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F59DC6D30353C9E601AEEDB1 = {
+			isa = PBXFileReference;
+			path = PSAlarmSetController.h;
+			refType = 4;
+		};
+		F59DC6D40353C9E601AEEDB1 = {
+			isa = PBXFileReference;
+			path = PSAlarmSetController.m;
+			refType = 4;
+		};
+		F59DC6D50353C9E601AEEDB1 = {
+			fileRef = F59DC6D30353C9E601AEEDB1;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F59DC6D60353C9E601AEEDB1 = {
+			fileRef = F59DC6D40353C9E601AEEDB1;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F59DC6DB0353DE5101AEEDB1 = {
+			isa = PBXFileReference;
+			path = PSAlarmNotifierController.h;
+			refType = 4;
+		};
+		F59DC6DC0353DE5101AEEDB1 = {
+			isa = PBXFileReference;
+			path = PSAlarmNotifierController.m;
+			refType = 4;
+		};
+		F59DC6DD0353DE5101AEEDB1 = {
+			fileRef = F59DC6DB0353DE5101AEEDB1;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F59DC6DE0353DE5101AEEDB1 = {
+			fileRef = F59DC6DC0353DE5101AEEDB1;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F59DC6DF0353DF5E01AEEDB1 = {
+			children = (
+				F59DC6E00353DF5E01AEEDB1,
+			);
+			isa = PBXVariantGroup;
+			name = Notifier.nib;
+			path = "";
+			refType = 4;
+		};
+		F59DC6E00353DF5E01AEEDB1 = {
+			isa = PBXFileReference;
+			name = English;
+			path = English.lproj/Notifier.nib;
+			refType = 4;
+		};
+		F59DC6E10353DF5E01AEEDB1 = {
+			fileRef = F59DC6DF0353DF5E01AEEDB1;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F59DC6E30353ED1201AEEDB1 = {
+			isa = PBXFileReference;
+			path = "Application icon.icns";
+			refType = 4;
+		};
+		F59DC6E40353ED1201AEEDB1 = {
+			fileRef = F59DC6E30353ED1201AEEDB1;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F59DC6E80353F58B01AEEDB1 = {
+			isa = PBXFileReference;
+			path = NJRIntegerFilter.m;
+			refType = 4;
+		};
+		F59DC6E90353F58B01AEEDB1 = {
+			isa = PBXFileReference;
+			path = NJRIntegerFilter.h;
+			refType = 4;
+		};
+		F59DC6EA0353F58B01AEEDB1 = {
+			fileRef = F59DC6E80353F58B01AEEDB1;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F59DC6EB0353F58B01AEEDB1 = {
+			fileRef = F59DC6E90353F58B01AEEDB1;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F59DC6FC0354188F01AEEDB1 = {
+			isa = PBXFileReference;
+			path = NJRDateFormatter.h;
+			refType = 4;
+		};
+		F59DC6FD0354188F01AEEDB1 = {
+			isa = PBXFileReference;
+			path = NJRDateFormatter.m;
+			refType = 4;
+		};
+		F59DC6FE0354188F01AEEDB1 = {
+			fileRef = F59DC6FC0354188F01AEEDB1;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F59DC6FF0354188F01AEEDB1 = {
+			fileRef = F59DC6FD0354188F01AEEDB1;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F59DD44F0358ABF4017318D0 = {
+			children = (
+				F59DD4500358ABF4017318D0,
+			);
+			isa = PBXVariantGroup;
+			name = Credits.html;
+			path = "";
+			refType = 4;
+		};
+		F59DD4500358ABF4017318D0 = {
+			isa = PBXFileReference;
+			name = English;
+			path = English.lproj/Credits.html;
+			refType = 4;
+		};
+		F59DD4510358ABF7017318D0 = {
+			fileRef = F59DD44F0358ABF4017318D0;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F59E4793036AA5E9016B311C = {
+			isa = PBXFileReference;
+			path = NDResourceFork.m;
+			refType = 4;
+		};
+		F59E4794036AA5E9016B311C = {
+			isa = PBXFileReference;
+			path = "NSURL+NDCarbonUtilities.m";
+			refType = 4;
+		};
+		F59E4795036AA5E9016B311C = {
+			isa = PBXFileReference;
+			path = NDAppleScriptObject.h;
+			refType = 4;
+		};
+		F59E4796036AA5E9016B311C = {
+			isa = PBXFileReference;
+			path = "NSAppleEventDescriptor+NDAppleScriptObject.m";
+			refType = 4;
+		};
+		F59E4797036AA5E9016B311C = {
+			isa = PBXFileReference;
+			path = NDAppleScriptObject.m;
+			refType = 4;
+		};
+		F59E4798036AA5E9016B311C = {
+			isa = PBXFileReference;
+			path = "NSAppleEventDescriptor+NDAppleScriptObject.h";
+			refType = 4;
+		};
+		F59E4799036AA5E9016B311C = {
+			isa = PBXFileReference;
+			path = NDResourceFork.h;
+			refType = 4;
+		};
+		F59E479A036AA5E9016B311C = {
+			isa = PBXFileReference;
+			path = NDAppleScriptObject_Protocols.h;
+			refType = 4;
+		};
+		F59E479B036AA5E9016B311C = {
+			isa = PBXFileReference;
+			path = "NSURL+NDCarbonUtilities.h";
+			refType = 4;
+		};
+		F59E479C036AA5E9016B311C = {
+			fileRef = F59E4793036AA5E9016B311C;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F59E479D036AA5E9016B311C = {
+			fileRef = F59E4794036AA5E9016B311C;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F59E479E036AA5E9016B311C = {
+			fileRef = F59E4795036AA5E9016B311C;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F59E479F036AA5E9016B311C = {
+			fileRef = F59E4796036AA5E9016B311C;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F59E47A0036AA5E9016B311C = {
+			fileRef = F59E4797036AA5E9016B311C;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F59E47A1036AA5E9016B311C = {
+			fileRef = F59E4798036AA5E9016B311C;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F59E47A2036AA5E9016B311C = {
+			fileRef = F59E4799036AA5E9016B311C;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F59E47A3036AA5E9016B311C = {
+			fileRef = F59E479A036AA5E9016B311C;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F59E47A4036AA5E9016B311C = {
+			fileRef = F59E479B036AA5E9016B311C;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F59E47A5036AA5F2016B311C = {
+			children = (
+				F59E4799036AA5E9016B311C,
+				F59E4793036AA5E9016B311C,
+				F59E479B036AA5E9016B311C,
+				F59E4794036AA5E9016B311C,
+				F59E4798036AA5E9016B311C,
+				F59E4796036AA5E9016B311C,
+				F59E4795036AA5E9016B311C,
+				F59E479A036AA5E9016B311C,
+				F59E4797036AA5E9016B311C,
+			);
+			isa = PBXGroup;
+			path = NDAppleScript;
+			refType = 4;
+		};
+		F59E47A6036AA5FC016B311C = {
+			children = (
+				F5F240B9036A867901FE7503,
+				F5F240CA036A869401FE7503,
+			);
+			isa = PBXGroup;
+			name = BDAlias;
+			path = "";
+			refType = 4;
+		};
+		F59E47A7036AAA6D016B311C = {
+			isa = PBXFileReference;
+			path = PSAlert.m;
+			refType = 4;
+		};
+		F59E47A8036AAA6D016B311C = {
+			isa = PBXFileReference;
+			path = PSAlert.h;
+			refType = 4;
+		};
+		F59E47A9036AAA6D016B311C = {
+			fileRef = F59E47A7036AAA6D016B311C;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F59E47AA036AAA6D016B311C = {
+			fileRef = F59E47A8036AAA6D016B311C;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F59E47AB036AAC1E016B311C = {
+			isa = PBXFileReference;
+			path = PSBeepAlert.m;
+			refType = 4;
+		};
+		F59E47AC036AAC1E016B311C = {
+			isa = PBXFileReference;
+			path = PSBeepAlert.h;
+			refType = 4;
+		};
+		F59E47AD036AAC1E016B311C = {
+			fileRef = F59E47AB036AAC1E016B311C;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F59E47AE036AAC1E016B311C = {
+			fileRef = F59E47AC036AAC1E016B311C;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F59E47AF036AAC31016B311C = {
+			children = (
+				F59E47A8036AAA6D016B311C,
+				F59E47A7036AAA6D016B311C,
+				F59E47B9036AB217016B311C,
+				F59E47B8036AB217016B311C,
+				F59E4BE7036B2E5E016B311C,
+				F59E4BE6036B2E5E016B311C,
+				F59E47B1036AACD1016B311C,
+				F59E47B0036AACD1016B311C,
+				F59E47AC036AAC1E016B311C,
+				F59E47AB036AAC1E016B311C,
+				F59E47BD036AB228016B311C,
+				F59E47BC036AB228016B311C,
+				F5F1986D036B813101EB0372,
+				F5F1986C036B813101EB0372,
+			);
+			isa = PBXGroup;
+			name = Alerts;
+			path = "";
+			refType = 4;
+		};
+		F59E47B0036AACD1016B311C = {
+			isa = PBXFileReference;
+			path = PSNotifierAlert.m;
+			refType = 4;
+		};
+		F59E47B1036AACD1016B311C = {
+			isa = PBXFileReference;
+			path = PSNotifierAlert.h;
+			refType = 4;
+		};
+		F59E47B2036AACD1016B311C = {
+			fileRef = F59E47B0036AACD1016B311C;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F59E47B3036AACD1016B311C = {
+			fileRef = F59E47B1036AACD1016B311C;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F59E47B4036AAD55016B311C = {
+			isa = PBXFileReference;
+			path = PSAlarmAlertController.m;
+			refType = 4;
+		};
+		F59E47B5036AAD55016B311C = {
+			isa = PBXFileReference;
+			path = PSAlarmAlertController.h;
+			refType = 4;
+		};
+		F59E47B6036AAD55016B311C = {
+			fileRef = F59E47B4036AAD55016B311C;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F59E47B7036AAD55016B311C = {
+			fileRef = F59E47B5036AAD55016B311C;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F59E47B8036AB217016B311C = {
+			isa = PBXFileReference;
+			path = PSDockBounceAlert.m;
+			refType = 4;
+		};
+		F59E47B9036AB217016B311C = {
+			isa = PBXFileReference;
+			path = PSDockBounceAlert.h;
+			refType = 4;
+		};
+		F59E47BA036AB217016B311C = {
+			fileRef = F59E47B8036AB217016B311C;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F59E47BB036AB217016B311C = {
+			fileRef = F59E47B9036AB217016B311C;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F59E47BC036AB228016B311C = {
+			isa = PBXFileReference;
+			path = PSMovieAlert.m;
+			refType = 4;
+		};
+		F59E47BD036AB228016B311C = {
+			isa = PBXFileReference;
+			path = PSMovieAlert.h;
+			refType = 4;
+		};
+		F59E47BE036AB228016B311C = {
+			fileRef = F59E47BC036AB228016B311C;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F59E47BF036AB228016B311C = {
+			fileRef = F59E47BD036AB228016B311C;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F59E47C0036B1ADD016B311C = {
+			isa = PBXFileReference;
+			path = PSMovieAlertController.m;
+			refType = 4;
+		};
+		F59E47C1036B1ADD016B311C = {
+			isa = PBXFileReference;
+			path = PSMovieAlertController.h;
+			refType = 4;
+		};
+		F59E47C2036B1ADD016B311C = {
+			fileRef = F59E47C0036B1ADD016B311C;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F59E47C3036B1ADD016B311C = {
+			fileRef = F59E47C1036B1ADD016B311C;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F59E4BDE036B1D94016B311C = {
+			children = (
+				F59E4BDF036B1D94016B311C,
+			);
+			isa = PBXVariantGroup;
+			name = "Movie alert.nib";
+			path = "";
+			refType = 4;
+		};
+		F59E4BDF036B1D94016B311C = {
+			isa = PBXFileReference;
+			name = English;
+			path = "English.lproj/Movie alert.nib";
+			refType = 4;
+		};
+		F59E4BE0036B1D94016B311C = {
+			fileRef = F59E4BDE036B1D94016B311C;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F59E4BE6036B2E5E016B311C = {
+			isa = PBXFileReference;
+			path = PSScriptAlert.m;
+			refType = 4;
+		};
+		F59E4BE7036B2E5E016B311C = {
+			isa = PBXFileReference;
+			path = PSScriptAlert.h;
+			refType = 4;
+		};
+		F59E4BE8036B2E5E016B311C = {
+			fileRef = F59E4BE6036B2E5E016B311C;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F59E4BE9036B2E5E016B311C = {
+			fileRef = F59E4BE7036B2E5E016B311C;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F59E4BEA036B33EC016B311C = {
+			isa = PBXFrameworkReference;
+			name = QuickTime.framework;
+			path = /System/Library/Frameworks/QuickTime.framework;
+			refType = 0;
+		};
+		F59E4C0C036B33EC016B311C = {
+			fileRef = F59E4BEA036B33EC016B311C;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F59E4C0D036B43D2016B311C = {
+			isa = PBXFileReference;
+			path = "NSMovie-NJRExtensions.m";
+			refType = 4;
+		};
+		F59E4C0E036B43D2016B311C = {
+			isa = PBXFileReference;
+			path = "NSMovie-NJRExtensions.h";
+			refType = 4;
+		};
+		F59E4C0F036B43D2016B311C = {
+			fileRef = F59E4C0D036B43D2016B311C;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F59E4C10036B43D2016B311C = {
+			fileRef = F59E4C0E036B43D2016B311C;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F59F6CB40354E7DA01AEEDB1 = {
+			isa = PBXFileReference;
+			path = PSAlarm.h;
+			refType = 4;
+		};
+		F59F6CB50354E7DA01AEEDB1 = {
+			isa = PBXFileReference;
+			path = PSAlarm.m;
+			refType = 4;
+		};
+		F59F6CB60354E7DA01AEEDB1 = {
+			fileRef = F59F6CB40354E7DA01AEEDB1;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F59F6CB70354E7DA01AEEDB1 = {
+			fileRef = F59F6CB50354E7DA01AEEDB1;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F5F19865036B806201EB0372 = {
+			children = (
+				F5F19866036B80AF01EB0372,
+				F5F19867036B80AF01EB0372,
+			);
+			isa = PBXGroup;
+			name = SpeechUtilities;
+			path = NDAppleScript;
+			refType = 4;
+		};
+		F5F19866036B80AF01EB0372 = {
+			isa = PBXFileReference;
+			path = SUSpeaker.h;
+			refType = 2;
+		};
+		F5F19867036B80AF01EB0372 = {
+			isa = PBXFileReference;
+			path = SUSpeaker.m;
+			refType = 2;
+		};
+		F5F19868036B80AF01EB0372 = {
+			fileRef = F5F19866036B80AF01EB0372;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F5F19869036B80AF01EB0372 = {
+			fileRef = F5F19867036B80AF01EB0372;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F5F1986C036B813101EB0372 = {
+			isa = PBXFileReference;
+			path = PSSpeechAlert.m;
+			refType = 4;
+		};
+		F5F1986D036B813101EB0372 = {
+			isa = PBXFileReference;
+			path = PSSpeechAlert.h;
+			refType = 4;
+		};
+		F5F1986E036B813101EB0372 = {
+			fileRef = F5F1986C036B813101EB0372;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F5F1986F036B813101EB0372 = {
+			fileRef = F5F1986D036B813101EB0372;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F5F19870036B822E01EB0372 = {
+			isa = PBXFileReference;
+			path = NJRVoicePopUpButton.m;
+			refType = 4;
+		};
+		F5F19871036B822E01EB0372 = {
+			isa = PBXFileReference;
+			path = NJRVoicePopUpButton.h;
+			refType = 4;
+		};
+		F5F19872036B822E01EB0372 = {
+			fileRef = F5F19870036B822E01EB0372;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F5F19873036B822E01EB0372 = {
+			fileRef = F5F19871036B822E01EB0372;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F5F198D0036BE57401EB0372 = {
+			isa = PBXFileReference;
+			path = NJRTableDelegate.m;
+			refType = 4;
+		};
+		F5F198D1036BE57401EB0372 = {
+			isa = PBXFileReference;
+			path = NJRTableDelegate.h;
+			refType = 4;
+		};
+		F5F198D2036BE57401EB0372 = {
+			fileRef = F5F198D0036BE57401EB0372;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F5F198D3036BE57401EB0372 = {
+			fileRef = F5F198D1036BE57401EB0372;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F5F198D9036BE75E01EB0372 = {
+			isa = PBXFileReference;
+			path = "NSTableView-NJRExtensions.m";
+			refType = 4;
+		};
+		F5F198DA036BE75E01EB0372 = {
+			isa = PBXFileReference;
+			path = "NSTableView-NJRExtensions.h";
+			refType = 4;
+		};
+		F5F198DB036BE75E01EB0372 = {
+			fileRef = F5F198D9036BE75E01EB0372;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F5F1990D036D3B3101EB0372 = {
+			isa = PBXFileReference;
+			path = "NSImage-NJRExtensions.m";
+			refType = 4;
+		};
+		F5F1990E036D3B3101EB0372 = {
+			isa = PBXFileReference;
+			path = "NSImage-NJRExtensions.h";
+			refType = 4;
+		};
+		F5F1990F036D3B3101EB0372 = {
+			fileRef = F5F1990D036D3B3101EB0372;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F5F19910036D3B3101EB0372 = {
+			fileRef = F5F1990E036D3B3101EB0372;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F5F240B5036A862101FE7503 = {
+			isa = PBXFileReference;
+			path = NJRFSObjectSelector.m;
+			refType = 4;
+		};
+		F5F240B6036A862101FE7503 = {
+			isa = PBXFileReference;
+			path = NJRFSObjectSelector.h;
+			refType = 4;
+		};
+		F5F240B7036A862301FE7503 = {
+			fileRef = F5F240B6036A862101FE7503;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F5F240B8036A862301FE7503 = {
+			fileRef = F5F240B5036A862101FE7503;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F5F240B9036A867901FE7503 = {
+			isa = PBXFileReference;
+			path = BDAlias.h;
+			refType = 4;
+		};
+		F5F240BA036A867901FE7503 = {
+			fileRef = F5F240B9036A867901FE7503;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F5F240CA036A869401FE7503 = {
+			isa = PBXFileReference;
+			path = BDAlias.m;
+			refType = 4;
+		};
+		F5F240CB036A869401FE7503 = {
+			fileRef = F5F240CA036A869401FE7503;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F5F240CC036A877301FE7503 = {
+			isa = PBXFileReference;
+			path = NJRQTMediaPopUpButton.m;
+			refType = 4;
+		};
+		F5F240CD036A877301FE7503 = {
+			isa = PBXFileReference;
+			path = NJRQTMediaPopUpButton.h;
+			refType = 4;
+		};
+		F5F240CE036A877301FE7503 = {
+			fileRef = F5F240CC036A877301FE7503;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F5F240D0036A87E301FE7503 = {
+			isa = PBXFrameworkReference;
+			name = Carbon.framework;
+			path = /System/Library/Frameworks/Carbon.framework;
+			refType = 0;
+		};
+		F5F24166036A87E401FE7503 = {
+			fileRef = F5F240D0036A87E301FE7503;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F5F24187036A90D901FE7503 = {
+			isa = PBXFrameworkReference;
+			name = MachineSettings.framework;
+			path = /System/Library/PrivateFrameworks/MachineSettings.framework;
+			refType = 0;
+		};
+		F5F24189036A90D901FE7503 = {
+			fileRef = F5F24187036A90D901FE7503;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F5F2418A036A910B01FE7503 = {
+			isa = PBXFileReference;
+			path = SoundFileManager.h;
+			refType = 4;
+		};
+		F5F2418B036A910B01FE7503 = {
+			fileRef = F5F2418A036A910B01FE7503;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+	};
+	rootObject = 29B97313FDCFA39411CA2CEA;
+}
Index: /releases/Pester/1.1a1/Source/SUSpeaker.h
===================================================================
--- /releases/Pester/1.1a1/Source/SUSpeaker.h	(revision 44)
+++ /releases/Pester/1.1a1/Source/SUSpeaker.h	(revision 44)
@@ -0,0 +1,40 @@
+//
+//  SUSpeaker.h
+//  SpeechTest
+//
+//  Created by raf on Sun Jan 28 2001.
+//  Copyright (c) 2000 Raphael Sebbe. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import <Carbon/Carbon.h>
+
+@interface SUSpeaker : NSObject 
+{
+    SpeechChannel _speechChannel;
+    id _delegate;
+    NSPort *_port;
+    
+    BOOL _usePort;
+    unsigned int _reserved1;
+    unsigned int _reserved2;
+}
+
++(NSArray*) voiceNames;
++ (NSString *)defaultVoice;
+
+-(void) setPitch:(float)pitch;
+-(void) setVoice:(int)index;
+-(void) speakText:(NSString*)text;
+-(void) stopSpeaking;
+
+-(void) setDelegate:(id)delegate;
+-(id) delegate;
+
+@end
+
+
+@interface NSObject (SUSpeakerDelegate)
+-(void) didFinishSpeaking:(SUSpeaker*)speaker;
+-(void) willSpeakWord:(SUSpeaker*)speaker at:(int)where length:(int)length;
+@end
Index: /releases/Pester/1.1a1/Source/SUSpeaker.m
===================================================================
--- /releases/Pester/1.1a1/Source/SUSpeaker.m	(revision 44)
+++ /releases/Pester/1.1a1/Source/SUSpeaker.m	(revision 44)
@@ -0,0 +1,296 @@
+//
+//  SUSpeaker.m
+//  SpeechTest
+//
+//  Created by raf on Sun Jan 28 2001.
+//  Copyright (c) 2000 Raphael Sebbe. All rights reserved.
+//
+
+#import "SUSpeaker.h"
+#include <unistd.h>
+#include <pthread.h>
+
+void MySpeechDoneCallback(SpeechChannel chan,SInt32 refCon);
+void MySpeechWordCallback (SpeechChannel chan, SInt32 refCon, UInt32 wordPos, 
+    UInt16 wordLen);
+
+@interface SUSpeaker (Private)
+-(void) setCallbacks;
+-(NSPort*) port;
+-(void) setReserved1:(unsigned int)r;
+-(void) setReserved2:(unsigned int)r;
+-(BOOL) usesPort;
+-(void) handleMessage:(unsigned)msgid;
+@end
+
+
+@implementation SUSpeaker
+
+/*"Returns the voice names in the same order as expected by setVoice:."*/
++(NSArray*) voiceNames
+{
+    NSMutableArray *voices = [NSMutableArray arrayWithCapacity:0];
+    short voiceCount;
+    OSErr error = noErr;
+    int voiceIndex;
+    
+    
+    
+    error = CountVoices(&voiceCount);
+    
+    if(error != noErr) return voices;
+    //NSLog(@"hello : %d", voiceCount);
+    for(voiceIndex=0; voiceIndex<voiceCount; voiceIndex++)
+    {
+        VoiceSpec	voiceSpec;
+        VoiceDescription voiceDescription;
+        
+        error = GetIndVoice(voiceIndex+1, &voiceSpec);
+        if(error != noErr) return voices;
+        error = GetVoiceDescription( &voiceSpec, &voiceDescription, sizeof(voiceDescription));
+        if(error == noErr)
+        {
+            NSString *voiceName = [[[NSString alloc] initWithCString:
+                &(voiceDescription.name[1]) length:voiceDescription.name[0]] autorelease];
+            //NSLog(voiceName);
+            
+            [voices addObject:voiceName];
+        }
+        else return voices;
+    }
+    return voices;
+}
+
++ (NSString *)defaultVoice;
+{
+    OSStatus err = noErr;
+    VoiceDescription voiceDescription;
+
+    err = GetVoiceDescription(NULL, &voiceDescription, sizeof(voiceDescription));
+
+    return [[[NSString alloc] initWithCString: &(voiceDescription.name[1]) length:voiceDescription.name[0]] autorelease];
+}
+
+-init
+{
+    NSRunLoop *loop = [NSRunLoop currentRunLoop];
+    [super init];
+
+    // we have 2 options here : we use a port or we don't.
+    // using a port means delegate message are invoked from the main 
+    // thread (runloop in which this object is created), otherwise, those message 
+    // are asynchronous.
+    if(loop != nil) 
+    {
+        _port = [[NSPort port] retain];
+        // we use a port so that the speech manager callbacks can talk to the main thread.
+        // That way, we can safely access interface elements from the delegate methods
+        
+        [_port setDelegate:self];
+        [loop addPort:_port forMode:NSDefaultRunLoopMode];
+        _usePort = YES;
+    }
+    else _usePort = NO;
+    
+    NewSpeechChannel(NULL, &_speechChannel); // NULL voice is default voice
+    [self setCallbacks];
+    return self;
+}
+
+-(void) dealloc
+{
+
+    [_port release];
+    if(_speechChannel != NULL)
+    {
+        DisposeSpeechChannel(_speechChannel);
+    }
+    
+    [super dealloc];
+}
+
+
+/*"Sets the pitch. Pitch is given in Hertz and should be comprised between 80 and 500, depending on the voice. Note that extreme value can make you app crash..."*/ 
+-(void) setPitch:(float)pitch
+{
+    int fixedPitch;
+    
+    
+    pitch = (pitch-90.0)/(300.0-90.0)*(65.0 - 30.0) + 30.0;
+    /* I don't know what Apple means with pitch between 30 and 65, so I convert that range to [90, 300]. I did not test frequencies correspond, though.*/
+    
+    fixedPitch = (int)pitch;
+    
+    fixedPitch = fixedPitch << 16; // fixed point
+    
+    if(_speechChannel != NULL)
+    {
+        SetSpeechPitch (_speechChannel, fixedPitch);
+    }
+}
+-(void) setVoice:(int)index
+{
+    VoiceSpec voice;
+    OSErr error = noErr;
+    
+    if(_speechChannel != NULL)
+    {
+        DisposeSpeechChannel(_speechChannel);
+        _speechChannel = NULL;
+    }
+    
+    error = GetIndVoice(index, &voice);
+    if(error == noErr)
+    {
+        NewSpeechChannel(&voice, &_speechChannel);
+        [self setCallbacks];
+    }
+}
+-(void) speakText:(NSString*)text
+{
+    //pid_t pid = getpid();
+    //pthread_t t = pthread_self();
+    //NSLog(@"pid : %d", t);
+
+    if(_speechChannel != NULL && text != nil)
+    {
+        //finished = NO;
+        SpeakText(_speechChannel, [text cString], [text length]);
+        //while(!finished) ;
+        //sleep(2);
+    }
+}
+-(void) stopSpeaking
+{
+    if(_speechChannel != NULL)
+    {
+        StopSpeech(_speechChannel);
+        if([_delegate respondsToSelector:@selector(didFinishSpeaking:)])
+        {
+            [_delegate didFinishSpeaking:self];
+        }
+    }
+}
+
+-(void) setDelegate:(id)delegate
+{
+    _delegate = delegate;
+}
+-(id) delegate
+{
+    return _delegate;
+}
+
+//--- Private ---
+
+-(void) setCallbacks
+{
+    if(_speechChannel != NULL)
+    {
+        SetSpeechInfo(_speechChannel, soSpeechDoneCallBack, &MySpeechDoneCallback);
+        SetSpeechInfo(_speechChannel, soWordCallBack, &MySpeechWordCallback);
+        SetSpeechInfo(_speechChannel, soRefCon, (const void*)self);
+    }
+}
+-(void) setReserved1:(unsigned int)r
+{
+    _reserved1 = r;
+}
+-(void) setReserved2:(unsigned int)r
+{
+    _reserved2 = r;
+}
+-(NSPort*) port
+{
+    return _port;
+}
+-(BOOL) usesPort
+{
+    return _usePort;
+}
+-(void) handleMessage:(unsigned)msgid
+{
+    if(msgid == 5)
+    {
+        if([_delegate respondsToSelector:@selector(willSpeakWord:at:length:)])
+        {
+            if(_reserved1 >= 0 && _reserved2 >= 0)
+                [_delegate willSpeakWord:self at:_reserved1 length:_reserved2];
+            else
+                [_delegate willSpeakWord:self at:0 length:0];
+        }
+    }
+    else if(msgid == 8)
+    {
+        if([_delegate respondsToSelector:@selector(didFinishSpeaking:)])
+        {
+            [_delegate didFinishSpeaking:self];
+        }
+    }
+}
+//--- NSPort delegate ---
+- (void)handlePortMessage:(NSPortMessage *)portMessage
+{
+    int msg = [portMessage msgid];
+    
+    [self handleMessage:msg];
+}
+
+@end
+
+void MySpeechDoneCallback(SpeechChannel chan,SInt32 refCon)
+{
+    SUSpeaker *speaker = (SUSpeaker*)refCon;
+    unsigned msg = 8;
+    //NSLog(@"Speech Done");
+    
+    if([speaker isKindOfClass:[SUSpeaker class]])
+    {
+        if([speaker usesPort])
+        {
+            NSPortMessage *message = [[NSPortMessage alloc] initWithSendPort:[speaker port]
+                receivePort:[speaker port] components:nil];
+        
+            [message setMsgid:msg];
+            [message sendBeforeDate:nil];
+            [message release];
+        }
+        else
+        {
+            // short-circuit port
+            [speaker handleMessage:msg];
+        }
+    } 
+}
+void MySpeechWordCallback(SpeechChannel chan, SInt32 refCon, UInt32 wordPos,UInt16 wordLen)
+{
+    SUSpeaker *speaker = (SUSpeaker*)refCon;
+    unsigned msg = 5;
+    //pid_t pid = getpid();
+    //pthread_t t = pthread_self();
+    //NSLog(@"pid : %d", t);
+    
+    
+    //NSLog(@"Word Done");
+    //while(1);
+    if([speaker isKindOfClass:[SUSpeaker class]])
+    {
+        [speaker setReserved1:wordPos];
+        [speaker setReserved2:wordLen];
+        
+        if([speaker usesPort])
+        {
+            NSPortMessage *message = [[NSPortMessage alloc] initWithSendPort:[speaker port]
+                receivePort:[speaker port] components:nil];
+        
+            [message setMsgid:msg];
+            [message sendBeforeDate:nil];
+            [message release];
+        }
+        else 
+        {
+            // short-circuit port
+            [speaker handleMessage:msg];
+        }
+    } 
+}
Index: /releases/Pester/1.1a1/Source/SoundFileManager.h
===================================================================
--- /releases/Pester/1.1a1/Source/SoundFileManager.h	(revision 44)
+++ /releases/Pester/1.1a1/Source/SoundFileManager.h	(revision 44)
@@ -0,0 +1,56 @@
+/*
+ *     Generated by class-dump (version 2.1.5).
+ *
+ *     class-dump is Copyright (C) 1997, 1999, 2000, 2001 by Steve Nygard.
+ */
+
+/*
+ * File: /Volumes/Mac OS X/System/Library/PrivateFrameworks/MachineSettings.framework/Versions/A/MachineSettings
+ */
+
+// Note: dumped from Mac OS X 10.1.5; Mac OS X 10.2.1's SoundFileManager is identical.
+
+@class SoundFile;
+
+@interface SoundFileManager
+{
+    NSMutableArray *_soundFiles;
+    NSFileManager *_fileManager;
+}
+
++ (void)initialize;
++ (SoundFileManager *)sharedSoundFileManager;
+- (id)init;
+- (void)dealloc;
+- (void)findSoundFiles;
+- (void)addSoundFilesFromDirectory:(NSString *)directory asType:(int)type;
+- (void)sortByType;
+- (void)sortByName;
+- (SoundFile *)soundFileAtIndex:(int)index;
+- (int)count;
+- (int)indexOfSoundFile:index;
+- (SoundFile *)soundFileFromPath:(NSString *)path;
+- (SoundFile *)soundFileWithName:(NSString *)name type:(int)type;
+
+@end
+
+@interface SoundFile
+{
+    int _type;
+    NSString *_name;
+}
+
+- (id)initWithName:(NSString *)name type:(int)type;
+- (void)dealloc;
+- (int)type;
+- (NSString *)name;
+- (NSString *)path;
+- (NSString *)pathWithoutExtension;
+- (void)setType:(int)type;
+- (void)setName:(NSString *)name;
+- (int)compareName:(SoundFile *)other;
+- (int)compareType:(SoundFile *)other;
+- (int)compareNameThenType:(SoundFile *)other;
+- (int)compareTypeThenName:(SoundFile *)other;
+
+@end
Index: /releases/Pester/1.1a1/Source/VERSION
===================================================================
--- /releases/Pester/1.1a1/Source/VERSION	(revision 44)
+++ /releases/Pester/1.1a1/Source/VERSION	(revision 44)
@@ -0,0 +1,1 @@
+1.1a1
Index: /releases/Pester/1.1a1/Source/main.m
===================================================================
--- /releases/Pester/1.1a1/Source/main.m	(revision 44)
+++ /releases/Pester/1.1a1/Source/main.m	(revision 44)
@@ -0,0 +1,14 @@
+//
+//  main.m
+//  Pester
+//
+//  Created by Nicholas Riley on Tue Oct 08 2002.
+//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+
+int main(int argc, const char *argv[])
+{
+    return NSApplicationMain(argc, argv);
+}
Index: /releases/Pester/1.1a1/package-Pester.sh
===================================================================
--- /releases/Pester/1.1a1/package-Pester.sh	(revision 44)
+++ /releases/Pester/1.1a1/package-Pester.sh	(revision 44)
@@ -0,0 +1,48 @@
+#!/bin/zsh
+
+# stuff to do:
+# hdiutil create DiskImage.dmg -megabytes <size> -layout NONE
+# hdid -nomount DiskImage.dmg
+# sudo /sbin/newfs_hfs -w -v VolumeName -b 4096 /dev/disk2
+# hdiutil eject /dev/disk2
+
+# also check out 'build' on mosxland.sf.net
+
+set -x -v
+
+PACKAGEDIR="$PWD"
+PRODUCT="Pester"
+
+cd "$PACKAGEDIR"/Source && \
+find . -name \*~\* -exec rm -r '{}' \; && \
+VERSION=`cat VERSION` && \
+DMG="$PRODUCT-$VERSION.dmg" VOL="$PRODUCT $VERSION" MOUNTPOINT="/Volumes/$VOL" && \
+DSTROOT="$PACKAGEDIR/$VOL" && \
+sudo rm -fr "$DSTROOT" && \
+rm -rf build/ && \
+pbxbuild install "DSTROOT=$DSTROOT" && \
+ditto -rsrc "$PACKAGEDIR"/Source "$DSTROOT"/Source && \
+ditto -rsrc "${PACKAGEDIR}/Read Me" "$DSTROOT" && \
+rm -rf "$DSTROOT"/Source/build "${DSTROOT}/Source/Read Me.rtfd" && \
+#breaks in 10.2 and later, sigh.
+#mkdir "$DSTROOT/Read Me.rtfd" && \
+#cd "$DSTROOT/Read Me.rtfd" && \
+#for i in "../Pester.app/Contents/Resources/Read Me.rtfd"/*; do ln -s "$i"; done && \
+find "$DSTROOT" -name ".svn" -exec sudo /bin/rm -rf "{}" \; ; \
+cd "$PACKAGEDIR" && \
+rm -f "$DMG" && \
+hdiutil create "$DMG" -megabytes 5 -ov -type UDIF && \
+DISK=`hdid -nomount "$DMG" | sed -ne ' /Apple_partition_scheme/ s|^/dev/\([^ ]*\).*$|\1|p'` && \
+newfs_hfs -v "$VOL" "/dev/r${DISK}s2" && \
+hdiutil eject "$DISK" && \
+hdid "$DMG" && \
+ditto -rsrc "$DSTROOT" "$MOUNTPOINT" && \
+chmod -R a+rX,u+w "$MOUNTPOINT" && \
+openUp "$MOUNTPOINT" && \
+# sleep 2 && \
+hdiutil eject $DISK && \
+# osascript -e "tell application \"Finder\" to eject disk \"$VOL\"" && \
+hdiutil convert "$DMG" -format UDZO -imagekey zlib-level=9 -o "z$DMG" && \
+mv "z$DMG" "$DMG" && \
+#scp "$DMG" ainaz:web/nriley/software/ && \
+:
