Index: trunk/Cocoa/Pester/Source/English.lproj/Alarms.nib/classes.nib
===================================================================
--- trunk/Cocoa/Pester/Source/English.lproj/Alarms.nib/classes.nib	(revision 363)
+++ trunk/Cocoa/Pester/Source/English.lproj/Alarms.nib/classes.nib	(revision 364)
@@ -1,27 +1,87 @@
-{
-    IBClasses = (
-        {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, 
-        {CLASS = NJRScrollView; LANGUAGE = ObjC; SUPERCLASS = NSScrollView; }, 
-        {
-            CLASS = NJRTableDelegate; 
-            LANGUAGE = ObjC; 
-            OUTLETS = {tableView = NSTableView; }; 
-            SUPERCLASS = NSObject; 
-        }, 
-        {
-            CLASS = NJRTableView; 
-            LANGUAGE = ObjC; 
-            OUTLETS = {typeSelectDisplay = id; }; 
-            SUPERCLASS = NSTableView; 
-        }, 
-        {CLASS = NSObject; LANGUAGE = ObjC; }, 
-        {
-            ACTIONS = {remove = id; }; 
-            CLASS = PSAlarmsController; 
-            LANGUAGE = ObjC; 
-            OUTLETS = {alarmList = NSTableView; removeButton = NSButton; }; 
-            SUPERCLASS = NSWindowController; 
-        }
-    ); 
-    IBVersion = 1; 
-}
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>IBClasses</key>
+	<array>
+		<dict>
+			<key>ACTIONS</key>
+			<dict>
+				<key>remove</key>
+				<string>id</string>
+			</dict>
+			<key>CLASS</key>
+			<string>PSAlarmsController</string>
+			<key>LANGUAGE</key>
+			<string>ObjC</string>
+			<key>OUTLETS</key>
+			<dict>
+				<key>alarmList</key>
+				<string>NSTableView</string>
+				<key>removeButton</key>
+				<string>NSButton</string>
+			</dict>
+			<key>SUPERCLASS</key>
+			<string>NSWindowController</string>
+		</dict>
+		<dict>
+			<key>CLASS</key>
+			<string>NJRTableDelegate</string>
+			<key>LANGUAGE</key>
+			<string>ObjC</string>
+			<key>OUTLETS</key>
+			<dict>
+				<key>tableView</key>
+				<string>NJRTableView</string>
+			</dict>
+			<key>SUPERCLASS</key>
+			<string>NSObject</string>
+		</dict>
+		<dict>
+			<key>CLASS</key>
+			<string>FirstResponder</string>
+			<key>LANGUAGE</key>
+			<string>ObjC</string>
+			<key>SUPERCLASS</key>
+			<string>NSObject</string>
+		</dict>
+		<dict>
+			<key>CLASS</key>
+			<string>NSObject</string>
+			<key>LANGUAGE</key>
+			<string>ObjC</string>
+		</dict>
+		<dict>
+			<key>CLASS</key>
+			<string>NJRScrollView</string>
+			<key>LANGUAGE</key>
+			<string>ObjC</string>
+			<key>SUPERCLASS</key>
+			<string>NSScrollView</string>
+		</dict>
+		<dict>
+			<key>CLASS</key>
+			<string>NJRTableView</string>
+			<key>LANGUAGE</key>
+			<string>ObjC</string>
+			<key>OUTLETS</key>
+			<dict>
+				<key>typeSelectDisplay</key>
+				<string>id</string>
+			</dict>
+			<key>SUPERCLASS</key>
+			<string>NSTableView</string>
+		</dict>
+		<dict>
+			<key>CLASS</key>
+			<string>NSTableView</string>
+			<key>LANGUAGE</key>
+			<string>ObjC</string>
+			<key>SUPERCLASS</key>
+			<string>NSControl</string>
+		</dict>
+	</array>
+	<key>IBVersion</key>
+	<string>1</string>
+</dict>
+</plist>
Index: trunk/Cocoa/Pester/Source/English.lproj/Alarms.nib/info.nib
===================================================================
--- trunk/Cocoa/Pester/Source/English.lproj/Alarms.nib/info.nib	(revision 363)
+++ trunk/Cocoa/Pester/Source/English.lproj/Alarms.nib/info.nib	(revision 364)
@@ -1,16 +1,20 @@
 <?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">
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
 <dict>
-	<key>IBDocumentLocation</key>
-	<string>41 63 356 240 0 0 1280 832 </string>
 	<key>IBFramework Version</key>
-	<string>291.0</string>
+	<string>629</string>
+	<key>IBLastKnownRelativeProjectPath</key>
+	<string>../../Pester.xcodeproj</string>
+	<key>IBOldestOS</key>
+	<integer>5</integer>
 	<key>IBOpenObjects</key>
 	<array>
-		<integer>8</integer>
+		<integer>9</integer>
 	</array>
 	<key>IBSystem Version</key>
-	<string>6I32</string>
+	<string>9B18</string>
+	<key>targetFramework</key>
+	<string>IBCocoaFramework</string>
 </dict>
 </plist>
Index: trunk/Cocoa/Pester/Source/English.lproj/MainMenu.nib/classes.nib
===================================================================
--- trunk/Cocoa/Pester/Source/English.lproj/MainMenu.nib/classes.nib	(revision 363)
+++ trunk/Cocoa/Pester/Source/English.lproj/MainMenu.nib/classes.nib	(revision 364)
@@ -97,5 +97,5 @@
 				<string>NSStepper</string>
 				<key>soundRepetitions</key>
-				<string>NSTextField</string>
+				<string>NJRValidatingField</string>
 				<key>soundRepetitionsLabel</key>
 				<string>NSTextField</string>
@@ -142,4 +142,12 @@
 		</dict>
 		<dict>
+			<key>CLASS</key>
+			<string>NSMenuItem</string>
+			<key>LANGUAGE</key>
+			<string>ObjC</string>
+			<key>SUPERCLASS</key>
+			<string>NSObject</string>
+		</dict>
+		<dict>
 			<key>ACTIONS</key>
 			<dict>
@@ -161,12 +169,4 @@
 		<dict>
 			<key>CLASS</key>
-			<string>NSMenuItem</string>
-			<key>LANGUAGE</key>
-			<string>ObjC</string>
-			<key>SUPERCLASS</key>
-			<string>NSObject</string>
-		</dict>
-		<dict>
-			<key>CLASS</key>
 			<string>FirstResponder</string>
 			<key>LANGUAGE</key>
@@ -199,4 +199,12 @@
 				<string>NSPopUpButton</string>
 			</dict>
+			<key>SUPERCLASS</key>
+			<string>NJRValidatingField</string>
+		</dict>
+		<dict>
+			<key>CLASS</key>
+			<string>NJRValidatingField</string>
+			<key>LANGUAGE</key>
+			<string>ObjC</string>
 			<key>SUPERCLASS</key>
 			<string>NSTextField</string>
Index: trunk/Cocoa/Pester/Source/English.lproj/MainMenu.nib/info.nib
===================================================================
--- trunk/Cocoa/Pester/Source/English.lproj/MainMenu.nib/info.nib	(revision 363)
+++ trunk/Cocoa/Pester/Source/English.lproj/MainMenu.nib/info.nib	(revision 364)
@@ -11,5 +11,5 @@
 	<key>IBOpenObjects</key>
 	<array>
-		<integer>169</integer>
+		<integer>507</integer>
 	</array>
 	<key>IBSystem Version</key>
Index: trunk/Cocoa/Pester/Source/Info-Pester.plist
===================================================================
--- trunk/Cocoa/Pester/Source/Info-Pester.plist	(revision 363)
+++ trunk/Cocoa/Pester/Source/Info-Pester.plist	(revision 364)
@@ -8,5 +8,5 @@
 	<string>Pester</string>
 	<key>CFBundleGetInfoString</key>
-	<string>Pester 1.1b5, © 2002–07 Nicholas Riley</string>
+	<string>Pester 1.1b6, © 2002–07 Nicholas Riley</string>
 	<key>CFBundleIconFile</key>
 	<string>Application icon</string>
@@ -20,9 +20,9 @@
 	<string>APPL</string>
 	<key>CFBundleShortVersionString</key>
-	<string>1.1b5</string>
+	<string>1.1b6</string>
 	<key>CFBundleSignature</key>
 	<string>Pest</string>
 	<key>CFBundleVersion</key>
-	<string>21</string>
+	<string>22</string>
 	<key>NSMainNibFile</key>
 	<string>MainMenu</string>
Index: trunk/Cocoa/Pester/Source/NJRHotKey.m
===================================================================
--- trunk/Cocoa/Pester/Source/NJRHotKey.m	(revision 363)
+++ trunk/Cocoa/Pester/Source/NJRHotKey.m	(revision 364)
@@ -93,10 +93,11 @@
 {
     if ( (self = [self init]) != nil) {
-        NS_DURING
+	@try {
             hotKeyCharacters = [[dict objectForKey: PLCharacters] retain];
             hotKeyModifierFlags = [[dict objectForKey: PLModifierFlags] unsignedIntValue];
             hotKeyCode = [[dict objectForKey: PLKeyCode] unsignedShortValue];
-        NS_HANDLER
-        NS_ENDHANDLER
+	}
+	@catch (NSException *exception) {
+	}
         if (hotKeyCharacters == nil || hotKeyCode == 0) {
             [self release];
Index: trunk/Cocoa/Pester/Source/NJRIntervalField.h
===================================================================
--- trunk/Cocoa/Pester/Source/NJRIntervalField.h	(revision 363)
+++ trunk/Cocoa/Pester/Source/NJRIntervalField.h	(revision 364)
@@ -10,5 +10,7 @@
 
 
-@interface NJRIntervalField : NSTextField {
+#import "NJRValidatingField.h"
+
+@interface NJRIntervalField : NJRValidatingField {
     IBOutlet NSPopUpButton *intervalUnits;
 }
@@ -17,5 +19,4 @@
 - (BOOL)setInterval:(NSTimeInterval)interval; // returns false if out of range
 
-- (void)handleDidFailToFormatString:(NSString *)string errorDescription:(NSString *)error label:(NSString *)label;
 
 @end
Index: trunk/Cocoa/Pester/Source/NJRIntervalField.m
===================================================================
--- trunk/Cocoa/Pester/Source/NJRIntervalField.m	(revision 363)
+++ trunk/Cocoa/Pester/Source/NJRIntervalField.m	(revision 364)
@@ -80,84 +80,3 @@
 }
 
-- (void)handleDidFailToFormatString:(NSString *)string errorDescription:(NSString *)error label:(NSString *)label;
-{
-    NSString *alertMessage = nil;
-    NSString *alternateButtonString = nil;
-    NSNumber *proposedValue = nil;
-    NSDictionary *contextInfo;
-
-    NSString *alertInformation = [NSString localizedStringWithFormat:
-        NSLocalizedString(@"The %@ field must be set to a value between %@ and %@.",
-                          @"Informative text for alert posed by text field when invalid value entered"),
-        label, [[self formatter] minimum], [[self formatter] maximum]];
-    NSString *defaultButtonString = NSLocalizedString(@"Edit", @"Name of Edit button");
-    NSString *otherButtonString = NSLocalizedString(@"Cancel", @"Name of Cancel button");
-
-    if ([error isEqualToString:
-        NSLocalizedStringFromTableInBundle(@"Fell short of minimum", @"Formatter",
-                                           [NSBundle bundleForClass:[NSFormatter class]],
-                                           @"Presented when user value smaller than minimum")]) {
-        proposedValue = [[self formatter] minimum];
-        alertMessage = [NSString stringWithFormat:
-            NSLocalizedString(@"%@ is too small for the %@ field.",
-                              @"Message text for alert posed by numeric field when too-small value entered"),
-            string, label];
-        alternateButtonString = [NSString localizedStringWithFormat:
-            NSLocalizedString(@"Set to %@",
-                              @"Name of alternate button for alert posed by numeric field when too-small value entered"),
-            proposedValue];
-    } else if ([error isEqualToString:
-        NSLocalizedStringFromTableInBundle(@"Maximum exceeded", @"Formatter",
-                                           [NSBundle bundleForClass:[NSFormatter class]],
-                                           @"Presented when user value larger than maximum")]) {
-        proposedValue = [[self formatter] maximum];
-        alertMessage = [NSString stringWithFormat:
-            NSLocalizedString(@"%@ is too large for the %@ field.",
-                              @"Message text for alert posed by numeric field when too-large value entered"),
-            string, label];
-        alternateButtonString = [NSString localizedStringWithFormat:
-            NSLocalizedString(@"Set to %@",
-                              @"Name of alternate button for alert posed by numeric field when too-large value entered"),
-            proposedValue];
-    } else if ([error isEqualToString:
-        NSLocalizedStringFromTableInBundle(@"Invalid number", @"Formatter",
-                                           [NSBundle bundleForClass:[NSFormatter class]],
-                                           @"Presented when user typed illegal characters: no valid object")]) {
-        alertMessage = [NSString stringWithFormat:
-            NSLocalizedString(@"%@ is not a valid entry for the %@ field.",
-                              @"Message text for alert posed by text field when invalid value entered"),
-            string, label];
-        alternateButtonString = nil;
-    }
-
-    contextInfo = [NSDictionary dictionaryWithObject: proposedValue forKey: @"proposedValue"];
-    [contextInfo retain];
-    NSBeep();
-    NSBeginAlertSheet(alertMessage, defaultButtonString, alternateButtonString, otherButtonString, [self window],
-                      self, @selector(validationFailedSheetDidEnd:returnCode:contextInfo:), NULL, contextInfo,
-                      alertInformation);
-}
-
-- (void)_propagateValidationChange;
-{
-    NSText *fieldEditor = [self currentEditor];
-    NSDictionary *userInfo = nil;
-    if (fieldEditor != nil) userInfo = [NSDictionary dictionaryWithObject: fieldEditor forKey: @"NSFieldEditor"];
-    [[NSNotificationCenter defaultCenter] postNotificationName: NSControlTextDidChangeNotification object: self userInfo: userInfo];
-    [self selectText: self];
-}
-
-- (void)validationFailedSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo {
-    // modal delegate callback method for NSBeginAlertSheet() function; called in the above method
-    if (returnCode == NSAlertOtherReturn) { // cancel
-        [self abortEditing]; // abort edit session and reinstate original value
-        [self _propagateValidationChange];
-    } else if (returnCode == NSAlertAlternateReturn) { // set to min/max/default value
-        [self setObjectValue: [(NSDictionary *)contextInfo objectForKey: @"proposedValue"]];
-        [self validateEditing];
-        [self _propagateValidationChange];
-    }
-    [(NSDictionary *)contextInfo release];
-}
-
 @end
Index: trunk/Cocoa/Pester/Source/NJRTableDelegate.m
===================================================================
--- trunk/Cocoa/Pester/Source/NJRTableDelegate.m	(revision 363)
+++ trunk/Cocoa/Pester/Source/NJRTableDelegate.m	(revision 364)
@@ -225,5 +225,5 @@
 @end
 
-@implementation NJRTableDelegate (NJRTableViewDelegate)
+@implementation NJRTableDelegate (NSTableViewDelegate)
 
 - (void)tableView:(NSTableView *)aTableView didClickTableColumn:(NSTableColumn *)inTableColumn
@@ -242,4 +242,18 @@
     [self _positionTypeSelectDisplay];
 }
+
+- (NSString *)tableView:(NSTableView *)aTableView toolTipForCell:(NSCell *)cell rect:(NSRectPointer)rect tableColumn:(NSTableColumn *)tc row:(int)rowIndex mouseLocation:(NSPoint)mouseLocation;
+{
+    id dataSource = [aTableView dataSource];
+    
+    if ([dataSource respondsToSelector: @selector(toolTipForRow:)])
+	return [dataSource toolTipForRow: rowIndex];
+    
+    return nil;
+}
+
+@end
+
+@implementation NJRTableDelegate (NJRTableViewDelegate)
 
 - (void)tableView:(NSTableView *)aTableView selectRowMatchingString:(NSString *)matchString;
Index: trunk/Cocoa/Pester/Source/NJRTableView.h
===================================================================
--- trunk/Cocoa/Pester/Source/NJRTableView.h	(revision 363)
+++ trunk/Cocoa/Pester/Source/NJRTableView.h	(revision 364)
@@ -11,5 +11,4 @@
 
 @interface NJRTableView : NSTableView {
-    NSMutableDictionary *toolTipRegionList;
     NSMutableString *typed;
     IBOutlet id typeSelectDisplay;
@@ -30,5 +29,5 @@
 
 - (void)removeSelectedRowsFromTableView:(NSTableView *)tableView;
-- (NSString *)tableView:(NSTableView *)aTableView toolTipForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex;
+- (NSString *)toolTipForRow:(int)rowIndex;
 
 @end
Index: trunk/Cocoa/Pester/Source/NJRTableView.m
===================================================================
--- trunk/Cocoa/Pester/Source/NJRTableView.m	(revision 363)
+++ trunk/Cocoa/Pester/Source/NJRTableView.m	(revision 364)
@@ -17,63 +17,4 @@
 
 @implementation NJRTableView
-
-- (id)initWithCoder:(NSCoder *)aDecoder;
-{
-    if ( (self = [super initWithCoder: aDecoder]) != nil) {
-        toolTipRegionList = [[NSMutableDictionary alloc] initWithCapacity: 20];
-    }
-    return self;
-}
-
-- (void)dealloc;
-{
-    [toolTipRegionList release];
-    [super dealloc];
-}
-
-#pragma mark tool tips
-
-- (void)reloadData;
-{
-    [toolTipRegionList removeAllObjects];
-    [self removeAllToolTips];
-    [super reloadData];
-}
-
-- (void)noteNumberOfRowsChanged;
-{
-    [toolTipRegionList removeAllObjects];
-    [self removeAllToolTips];
-    [super noteNumberOfRowsChanged];
-}
-
-- (NSString *)_keyForColumn:(int)columnIndex row:(int)rowIndex;
-{
-    return [NSString stringWithFormat:@"%d,%d", rowIndex, columnIndex];
-}
-
-- (NSString *)view:(NSView *)view stringForToolTip:(NSToolTipTag)tag point:(NSPoint)point userData:(void *)data;
-{
-    // ask our data source for the tool tip
-    if ([[self dataSource] respondsToSelector: @selector(tableView:toolTipForTableColumn:row:)]) {
-        if ([self rowAtPoint: point] >= 0) return [[self dataSource] tableView: self toolTipForTableColumn: [[self tableColumns] objectAtIndex: [self columnAtPoint: point]] row: [self rowAtPoint: point]];
-    }
-    return nil;
-}
-
-- (NSRect)frameOfCellAtColumn:(int)columnIndex row:(int)rowIndex;
-{
-    // this cell is apparently displayed, so we need to add a region for it
-    NSNumber *toolTipTag;
-    NSRect result = [super frameOfCellAtColumn: columnIndex row: rowIndex];
-    // check if cell is already in the list
-    NSString *cellKey = [self _keyForColumn: columnIndex row: rowIndex];
-    // remove old region
-    if (toolTipTag = [toolTipRegionList objectForKey: cellKey])
-        [self removeToolTip: [toolTipTag intValue]];
-    // add new region
-    [toolTipRegionList setObject: [NSNumber numberWithInt: [self addToolTipRect: result owner: self userData: cellKey]] forKey: cellKey];
-    return [super frameOfCellAtColumn: columnIndex row: rowIndex];
-}
 
 #pragma mark type selection
@@ -162,46 +103,3 @@
 }
 
-#pragma mark row coloring
-
-- (void)drawGridInClipRect:(NSRect)rect;
-{
-    NSRange columnRange = [self columnsInRect: rect];
-    int i;
-    // match iTunesÕ grid color
-    [[[NSColor gridColor] blendedColorWithFraction: 0.70 ofColor: [NSColor whiteColor]] set];
-    for (i = columnRange.location ; i < NSMaxRange(columnRange) ; i++) {
-        NSRect colRect = [self rectOfColumn: i];
-        int rightEdge = (int) 0.5 + colRect.origin.x + colRect.size.width;
-        [NSBezierPath strokeLineFromPoint: NSMakePoint(-0.5 + rightEdge, -0.5 + rect.origin.y)
-                                  toPoint: NSMakePoint(-0.5 + rightEdge, -0.5 + rect.origin.y + rect.size.height)];
-    }
-}
-
-- (void)highlightSelectionInClipRect:(NSRect)clipRect;
-{
-    NSColor *evenColor, *oddColor = [self backgroundColor];
-    float cellHeight = [self cellHeight];
-    NSRect visibleRect = [self visibleRect];
-    NSRect highlightRect;
-    
-    if ([NSColor respondsToSelector: @selector(alternateSelectedControlColor)])
-        evenColor = [[NSColor alternateSelectedControlColor] highlightWithLevel:0.90];
-    else // match iTunesÕ row background color
-        evenColor = [NSColor colorWithCalibratedRed: 0.929 green: 0.953 blue: 0.996 alpha:1.0];
-
-    highlightRect.origin = NSMakePoint(NSMinX(visibleRect), (int)(NSMinY(clipRect) / cellHeight) * cellHeight);
-    highlightRect.size = NSMakeSize(NSWidth(visibleRect), cellHeight);
-
-    while (NSMinY(highlightRect) < NSMaxY(clipRect)) {
-        NSRect clippedHighlightRect = NSIntersectionRect(highlightRect, clipRect);
-        int row = (int)((NSMinY(highlightRect) + cellHeight / 2.0) / cellHeight);
-        NSColor *rowColor = (row % 2 == 0) ? evenColor : oddColor;
-        [rowColor set];
-        NSRectFill(clippedHighlightRect);
-        highlightRect.origin.y += cellHeight;
-    }
-
-    [super highlightSelectionInClipRect: clipRect];
-}
-
 @end
Index: trunk/Cocoa/Pester/Source/NJRValidatingField.h
===================================================================
--- trunk/Cocoa/Pester/Source/NJRValidatingField.h	(revision 364)
+++ trunk/Cocoa/Pester/Source/NJRValidatingField.h	(revision 364)
@@ -0,0 +1,18 @@
+//
+//  NJRValidatingField.h
+//  Pester
+//
+//  Created by Nicholas Riley on 11/27/07.
+//  Copyright 2007 Nicholas Riley. All rights reserved.
+//
+
+#import <AppKit/AppKit.h>
+
+
+@interface NJRValidatingField : NSTextField {
+
+}
+
+- (void)handleDidFailToFormatString:(NSString *)string errorDescription:(NSString *)error label:(NSString *)label;
+
+@end
Index: trunk/Cocoa/Pester/Source/NJRValidatingField.m
===================================================================
--- trunk/Cocoa/Pester/Source/NJRValidatingField.m	(revision 364)
+++ trunk/Cocoa/Pester/Source/NJRValidatingField.m	(revision 364)
@@ -0,0 +1,94 @@
+//
+//  NJRValidatingField.m
+//  Pester
+//
+//  Created by Nicholas Riley on 11/27/07.
+//  Copyright 2007 Nicholas Riley. All rights reserved.
+//
+
+#import "NJRValidatingField.h"
+
+
+@implementation NJRValidatingField
+
+- (void)_propagateValidationChange;
+{
+    NSText *fieldEditor = [self currentEditor];
+    NSDictionary *userInfo = nil;
+    if (fieldEditor != nil) userInfo = [NSDictionary dictionaryWithObject: fieldEditor forKey: @"NSFieldEditor"];
+    [[NSNotificationCenter defaultCenter] postNotificationName: NSControlTextDidChangeNotification object: self userInfo: userInfo];
+    [self selectText: self];
+}
+
+- (void)validationFailedSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo {
+    // modal delegate callback method for NSBeginAlertSheet() function; called in the above method
+    if (returnCode == NSAlertOtherReturn) { // cancel
+        [self abortEditing]; // abort edit session and reinstate original value
+        [self _propagateValidationChange];
+    } else if (returnCode == NSAlertAlternateReturn) { // set to min/max/default value
+        [self setObjectValue: [(NSDictionary *)contextInfo objectForKey: @"proposedValue"]];
+        [self validateEditing];
+        [self _propagateValidationChange];
+    }
+    [(NSDictionary *)contextInfo release];
+}
+
+- (void)handleDidFailToFormatString:(NSString *)string errorDescription:(NSString *)error label:(NSString *)label;
+{
+    NSString *alertMessage = nil;
+    NSString *alternateButtonString = nil;
+    NSNumber *proposedValue = nil;
+    NSDictionary *contextInfo;
+
+    NSString *alertInformation = [NSString localizedStringWithFormat:
+        NSLocalizedString(@"The %@ field must be set to a value between %@ and %@.",
+                          @"Informative text for alert posed by text field when invalid value entered"),
+        label, [[self formatter] minimum], [[self formatter] maximum]];
+    NSString *defaultButtonString = NSLocalizedString(@"Edit", @"Name of Edit button");
+    NSString *otherButtonString = NSLocalizedString(@"Cancel", @"Name of Cancel button");
+
+    if ([error isEqualToString:
+        NSLocalizedStringFromTableInBundle(@"Fell short of minimum", @"Formatter",
+                                           [NSBundle bundleForClass:[NSFormatter class]],
+                                           @"Presented when user value smaller than minimum")]) {
+        proposedValue = [[self formatter] minimum];
+        alertMessage = [NSString stringWithFormat:
+            NSLocalizedString(@"%@ is too small for the %@ field.",
+                              @"Message text for alert posed by numeric field when too-small value entered"),
+            string, label];
+        alternateButtonString = [NSString localizedStringWithFormat:
+            NSLocalizedString(@"Set to %@",
+                              @"Name of alternate button for alert posed by numeric field when too-small value entered"),
+            proposedValue];
+    } else if ([error isEqualToString:
+        NSLocalizedStringFromTableInBundle(@"Maximum exceeded", @"Formatter",
+                                           [NSBundle bundleForClass:[NSFormatter class]],
+                                           @"Presented when user value larger than maximum")]) {
+        proposedValue = [[self formatter] maximum];
+        alertMessage = [NSString stringWithFormat:
+            NSLocalizedString(@"%@ is too large for the %@ field.",
+                              @"Message text for alert posed by numeric field when too-large value entered"),
+            string, label];
+        alternateButtonString = [NSString localizedStringWithFormat:
+            NSLocalizedString(@"Set to %@",
+                              @"Name of alternate button for alert posed by numeric field when too-large value entered"),
+            proposedValue];
+    } else if ([error isEqualToString:
+        NSLocalizedStringFromTableInBundle(@"Invalid number", @"Formatter",
+                                           [NSBundle bundleForClass:[NSFormatter class]],
+                                           @"Presented when user typed illegal characters: no valid object")]) {
+        alertMessage = [NSString stringWithFormat:
+            NSLocalizedString(@"%@ is not a valid entry for the %@ field.",
+                              @"Message text for alert posed by text field when invalid value entered"),
+            string, label];
+        alternateButtonString = nil;
+    }
+
+    contextInfo = [NSDictionary dictionaryWithObject: proposedValue forKey: @"proposedValue"];
+    [contextInfo retain];
+    NSBeep();
+    NSBeginAlertSheet(alertMessage, defaultButtonString, alternateButtonString, otherButtonString, [self window],
+                      self, @selector(validationFailedSheetDidEnd:returnCode:contextInfo:), NULL, contextInfo,
+                      alertInformation);
+}
+@end
Index: trunk/Cocoa/Pester/Source/NJRVoicePopUpButton.h
===================================================================
--- trunk/Cocoa/Pester/Source/NJRVoicePopUpButton.h	(revision 363)
+++ trunk/Cocoa/Pester/Source/NJRVoicePopUpButton.h	(revision 364)
@@ -8,9 +8,8 @@
 
 #import <AppKit/AppKit.h>
-#import "SUSpeaker.h"
 
 @interface NJRVoicePopUpButton : NSPopUpButton {
     id _delegate;
-    SUSpeaker *_speaker;
+    NSSpeechSynthesizer *_speaker;
 }
 
Index: trunk/Cocoa/Pester/Source/NJRVoicePopUpButton.m
===================================================================
--- trunk/Cocoa/Pester/Source/NJRVoicePopUpButton.m	(revision 363)
+++ trunk/Cocoa/Pester/Source/NJRVoicePopUpButton.m	(revision 364)
@@ -15,5 +15,5 @@
     NSMenu *menu;
     NSMenuItem *item;
-    NSArray *voiceNames = [SUSpeaker voiceNames];
+    NSArray *voices = [NSSpeechSynthesizer availableVoices];
 
     [self removeAllItems];
@@ -24,16 +24,20 @@
     [item setEnabled: NO];
     [menu addItem: [NSMenuItem separatorItem]];
-    if (voiceNames == nil || [voiceNames count] == 0) {
+    if (voices == nil || [voices count] == 0) {
         item = [menu addItemWithTitle: NSLocalizedString(@"Can't locate voices", "Voice popup menu item surrogate for voice list if no voices are found") 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: @""];
+        NSEnumerator *e = [voices objectEnumerator];
+        NSString *voice;
+        while ( (voice = [e nextObject]) != nil) {
+            item = [menu addItemWithTitle:
+		    [[NSSpeechSynthesizer attributesForVoice: voice] objectForKey: NSVoiceName]
+				   action: @selector(_previewVoice) keyEquivalent: @""];
+	    [item setRepresentedObject: voice];
             [item setTarget: self];
         }
     }
-    if (_speaker == nil) [self selectItemWithTitle: [SUSpeaker defaultVoice]];
+    if (_speaker == nil)
+	[self selectItemAtIndex: [menu indexOfItemWithRepresentedObject: [NSSpeechSynthesizer defaultVoice]]];
 }
 
@@ -54,7 +58,7 @@
 }
 
-- (SUSpeaker *)_speaker;
+- (NSSpeechSynthesizer *)_speaker;
 {
-    if (_speaker == nil) _speaker = [[SUSpeaker alloc] init];
+    if (_speaker == nil) _speaker = [[NSSpeechSynthesizer alloc] initWithVoice: nil];
     return _speaker;
 }
@@ -68,5 +72,5 @@
 - (void)setVoice:(NSString *)voice;
 {
-    int voiceIdx = [self indexOfItemWithTitle: voice];
+    int voiceIdx = [self indexOfItemWithRepresentedObject: voice];
     if (voiceIdx == -1) {
         [self _invalidateVoiceSelection];
@@ -78,15 +82,12 @@
 - (void)_previewVoice;
 {
-    NSString *voiceName = [self titleOfSelectedItem];
+    NSString *voice = [[self selectedItem] representedObject];
     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", nil, nil, nil, [self window], nil, nil, nil, nil, NSLocalizedString(@"The voice '%@' you selected could not be used.  An error of type %ld occurred while attempting to retrieve voice information.", "Message displayed in alert sheet when GetIndVoice returns an error"), voiceName, err);
+    if (![[self _speaker] setVoice: voice]) {
+	// XXX localize title
+        NSBeginAlertSheet(@"Voice not available", nil, nil, nil, [self window], nil, nil, nil, nil, NSLocalizedString(@"The voice '%@' you selected could not be used.", "Message displayed in alert sheet when -[NSSpeechSynthesizer setVoice:] returns an error"), [self titleOfSelectedItem]);
         [self _invalidateVoiceSelection];
         return;
@@ -94,19 +95,11 @@
 
     if (_delegate != nil && [_delegate respondsToSelector: @selector(voicePopUpButton:previewStringForVoice:)]) {
-        previewString = [_delegate voicePopUpButton: self previewStringForVoice: voiceName];
+        previewString = [_delegate voicePopUpButton: self previewStringForVoice: voice];
     }
 
-    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];
-        }
-    }
+    if (previewString == nil)
+        previewString = [[NSSpeechSynthesizer attributesForVoice: voice] objectForKey: NSVoiceDemoText];
 
-    [[self _speaker] setVoice: voiceIndex];
-    [_speaker speakText: previewString];
+    [_speaker startSpeakingString: previewString];
 }
 
Index: trunk/Cocoa/Pester/Source/PSAlarm.m
===================================================================
--- trunk/Cocoa/Pester/Source/PSAlarm.m	(revision 363)
+++ trunk/Cocoa/Pester/Source/PSAlarm.m	(revision 364)
@@ -357,5 +357,5 @@
 
     [string appendAttributedString:
-        [[NSString stringWithFormat: NSLocalizedString(@"At alarm time for %@:\n", "Alert list title in pretty description, %@ replaced with message"), [self message]] small]];
+        [[NSString stringWithFormat: NSLocalizedString(@"At alarm time for '%@':\n", "Alert list title in pretty description, %@ replaced with message"), [self message]] small]];
     if (alertList != nil) {
         [string appendAttributedString: alertList];
@@ -415,5 +415,11 @@
         // don't want to put this logic in setTimer or isValid because it can cause invalid alarms to be set (consider when someone clicks the "repeat" checkbox, then switches to a [nonrepeating, by design] date alarm, and enters a date that has passed: we do -not- want the alarm to magically morph into a repeating interval alarm)
         NSTimeInterval savedInterval = alarmInterval;
-        if (![self setTimer]) {
+        if ([self setTimer]) {
+	    // alarm is set, but not repeating - and the interval is wrong because it was computed from the date
+	    alarmInterval = savedInterval;
+	    [self setRepeating: YES];
+	} else {
+	    // alarm is now invalid: expired in the past, so we start the timer over again
+	    // We could potentially start counting from the expiration date (or expiration date + n * interval), but this doesn't match our existing behavior.
             alarmType = PSAlarmInterval;
             [self setInterval: savedInterval];
@@ -563,8 +569,8 @@
         }
         [self setMessage: [coder decodeObject]];
-        if (alarmType == PSAlarmSet) {
+        if (alarmType == PSAlarmSet)
             alarmType = PSAlarmDate;
-            [self setTimer];
-        }
+	// Note: the timer is not set here, so these alarms are inert.
+	// This helps make importing atomic (see -[PSAlarms importVersion1Alarms])
     }
     return self;
Index: trunk/Cocoa/Pester/Source/PSAlarmSetController.h
===================================================================
--- trunk/Cocoa/Pester/Source/PSAlarmSetController.h	(revision 363)
+++ trunk/Cocoa/Pester/Source/PSAlarmSetController.h	(revision 364)
@@ -13,4 +13,5 @@
 @class NJRIntervalField;
 @class NJRQTMediaPopUpButton;
+@class NJRValidatingField;
 @class NJRVoicePopUpButton;
 
@@ -34,5 +35,5 @@
     IBOutlet NJRQTMediaPopUpButton *sound;
     IBOutlet NSButton *soundVolumeButton;
-    IBOutlet NSTextField *soundRepetitions;
+    IBOutlet NJRValidatingField *soundRepetitions;
     IBOutlet NSStepper *soundRepetitionStepper;
     IBOutlet NSTextField *soundRepetitionsLabel;
Index: trunk/Cocoa/Pester/Source/PSAlarmSetController.m
===================================================================
--- trunk/Cocoa/Pester/Source/PSAlarmSetController.m	(revision 363)
+++ trunk/Cocoa/Pester/Source/PSAlarmSetController.m	(revision 364)
@@ -18,4 +18,5 @@
 #import "NJRQTMediaPopUpButton.h"
 #import "NJRSoundManager.h"
+#import "NJRValidatingField.h"
 #import "NJRVoicePopUpButton.h"
 #import "NSString-NJRExtensions.h"
@@ -87,14 +88,14 @@
     {
         NSDictionary *plAlerts = [defaults dictionaryForKey: PSAlertsSelected];
-        PSAlerts *alerts;
+        PSAlerts *alerts = nil;
         if (plAlerts == nil) {
             alerts = [[PSAlerts alloc] initWithPesterVersion1Alerts];
         } else {
-            NS_DURING
+            @try {
                 alerts = [[PSAlerts alloc] initWithPropertyList: plAlerts];
-            NS_HANDLER
-                NSRunAlertPanel(@"Unable to restore alerts", @"Pester could not restore recent alert information for one or more alerts in the Set Alarm window.  The default set of alerts will be used instead.\n\n%@", nil, nil, nil, [localException reason]);
+	    } @catch (NSException *exception) {
+                NSRunAlertPanel(@"Unable to restore alerts", @"Pester could not restore recent alert information for one or more alerts in the Set Alarm window.  The default set of alerts will be used instead.\n\n%@", nil, nil, nil, [exception reason]);
                 alerts = [[PSAlerts alloc] initWithPesterVersion1Alerts];
-            NS_ENDHANDLER
+            }
         }
         [self _readAlerts: alerts];
@@ -199,4 +200,20 @@
     old = [inAtMatrix cellWithTag: isInterval];
     NSAssert(new != old, @"in and at buttons should be distinct!");
+    
+    if (sender != nil) {
+	// XXX validation doesn't work properly for date/time, so we just universally cancel editing now
+        if (![[self window] makeFirstResponder: nil] && !isInterval) {
+	    // This works fine synchronously only if you're using the keyboard shortcut to switch in/at.  Directly activating the button, a delayed invocation is necessary.
+	    NSInvocation *i = [NSInvocation invocationWithMethodSignature:
+			       [inAtMatrix methodSignatureForSelector: @selector(selectCellWithTag:)]];
+	    int tag = [old tag];
+	    [i setSelector: @selector(selectCellWithTag:)];
+	    [i setTarget: inAtMatrix];
+	    [i setArgument: &tag atIndex: 2];
+	    [NSTimer scheduledTimerWithTimeInterval: 0 invocation: i repeats: NO];
+	    return;
+	}
+    }
+    
     [old setKeyEquivalent: [new keyEquivalent]];
     [old setKeyEquivalentModifierMask: [new keyEquivalentModifierMask]];
@@ -211,8 +228,7 @@
     [timeCalendarButton setEnabled: !isInterval];
     if (sender != nil)
-        [[self window] makeFirstResponder: isInterval ? (NSTextField *)timeInterval : timeOfDay];
-    if (!isInterval) { // need to do this every time the controls are enabled
+	[[self window] makeFirstResponder: isInterval ? (NSTextField *)timeInterval : timeOfDay];
+    if (!isInterval) // need to do this every time the controls are enabled
         [timeOfDay setNextKeyView: timeDate];
-    }
     // NSLog(@"UPDATING FROM inAtChanged");
     [self update: nil];
@@ -337,6 +353,6 @@
 {
     BOOL playSoundSelected = [playSound intValue];
-    BOOL canRepeat = playSoundSelected ? [sound canRepeat] : NO;
-    [sound setEnabled: NO]; //playSoundSelected]; // XXX temporary for 1.1b5
+    BOOL canRepeat = playSoundSelected; // ? [sound canRepeat] : NO; // XXX temporary for 1.1b6
+    [sound setEnabled: playSoundSelected];
     [soundRepetitions setEnabled: canRepeat];
     [soundVolumeButton setEnabled: canRepeat && [sound hasAudio]];
@@ -344,5 +360,5 @@
     [soundRepetitionsLabel setTextColor: canRepeat ? [NSColor controlTextColor] : [NSColor disabledControlTextColor]];
     if (playSoundSelected && sender == playSound) {
-        [[self window] makeFirstResponder: sound];
+        [[self window] makeFirstResponder: soundRepetitions]; // sound]; // XXX temporary for 1.1b6
     }
 }
@@ -437,5 +453,5 @@
     
     [alerts removeAlerts];
-    NS_DURING
+    @try {
         // dock bounce alert
         if ([bounceDockIcon state] == NSOnState)
@@ -467,13 +483,13 @@
         // speech alert
         if ([doSpeak intValue])
-            [alerts addAlert: [PSSpeechAlert alertWithVoice: [voice titleOfSelectedItem]]];
+            [alerts addAlert: [PSSpeechAlert alertWithVoice: [[voice selectedItem] representedObject]]];
         // wake alert
         if ([wakeUp intValue])
             [alerts addAlert: [PSWakeAlert alert]];
         [[NSUserDefaults standardUserDefaults] setObject: [alerts propertyListRepresentation] forKey: PSAlertsSelected];
-    NS_HANDLER
-        [self setStatus: [localException reason]];
-        NS_VALUERETURN(NO, BOOL);
-    NS_ENDHANDLER
+    } @catch (NSException *exception) {
+	[self setStatus: [exception reason]];
+        return NO;
+    }
     return YES;
 }
@@ -540,4 +556,6 @@
     if (control == timeInterval)
         [timeInterval handleDidFailToFormatString: string errorDescription: error label: @"alarm interval"];
+    else if (control == soundRepetitions)
+	[soundRepetitions handleDidFailToFormatString: string errorDescription: error label: @"alert repetitions"];
     return NO;
 }
Index: trunk/Cocoa/Pester/Source/PSAlarms.m
===================================================================
--- trunk/Cocoa/Pester/Source/PSAlarms.m	(revision 363)
+++ trunk/Cocoa/Pester/Source/PSAlarms.m	(revision 364)
@@ -178,5 +178,5 @@
     unsigned indexCount = [indices count], i = 0, alarmIndex;
     unsigned *indexArray = (unsigned *)malloc(indexCount * sizeof(unsigned));
-    NS_DURING
+    @try {
         while ( (n = [e nextObject]) != nil) {
             alarmIndex = [n intValue];
@@ -186,11 +186,8 @@
         }
         [alarms removeObjectsFromIndices: indexArray numIndices: indexCount];
+    } @finally {
         free(indexArray); indexArray = NULL;
         [self _changed];
-    NS_HANDLER
-        free(indexArray);
-        [self _changed];
-        [localException raise];
-    NS_ENDHANDLER
+    }
 }
 
@@ -314,13 +311,19 @@
     NSData *alarmData;
     PSAlarm *alarm;
-    while ( (alarmData = [e nextObject]) != nil) {
-        NS_DURING
-            alarm = [NSUnarchiver unarchiveObjectWithData: alarmData];
-        NS_HANDLER
-            alarm = nil;
-            // XXX
-        NS_ENDHANDLER
-        if (alarm != nil)
-            [alarms addObject: alarm];
+    NSMutableArray *importedAlarms = [[NSMutableArray alloc] initWithCapacity: [alarmsData count]];
+    @try {
+	while ( (alarmData = [e nextObject]) != nil) {
+	    alarm = [NSUnarchiver unarchiveObjectWithData: alarmData];
+	    if (alarm == nil)
+		@throw [NSException exceptionWithName: NSInternalInconsistencyException reason: @"Failed to decode Pester 1.0 alarm." userInfo: nil];
+	    [importedAlarms addObject: alarm];
+	    if (![alarm setTimer]) // expired
+		[alarms addObject: alarm];
+	}
+    } @catch (NSException *exception) {
+	[self removeAlarms: [NSSet setWithArray: importedAlarms]];
+	@throw;
+    } @finally {
+	[importedAlarms release];
     }
 }
Index: trunk/Cocoa/Pester/Source/PSAlarmsController.m
===================================================================
--- trunk/Cocoa/Pester/Source/PSAlarmsController.m	(revision 363)
+++ trunk/Cocoa/Pester/Source/PSAlarmsController.m	(revision 364)
@@ -121,8 +121,8 @@
 }
 
-- (NSString *)tableView:(NSTableView *)aTableView toolTipForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex;
+- (NSString *)toolTipForRow:(int)rowIndex;
 {
     PSAlarm *alarm = [reorderedAlarms objectAtIndex: rowIndex];
-
+    
     return [[alarm prettyDescription] string];
 }
Index: trunk/Cocoa/Pester/Source/PSAlerts.m
===================================================================
--- trunk/Cocoa/Pester/Source/PSAlerts.m	(revision 363)
+++ trunk/Cocoa/Pester/Source/PSAlerts.m	(revision 364)
@@ -132,10 +132,15 @@
 {
     if ( (self = [self init]) != nil) {
-        NSArray *plAlerts = [dict objectForKey: PLAlerts];
-        NSEnumerator *e = [plAlerts objectEnumerator];
-        NSDictionary *alertDict;
-        while ( (alertDict = [e nextObject]) != nil) {
-            [self addAlert: [[PSAlert alloc] initWithPropertyList: alertDict]];
-        }
+	@try {
+	    NSArray *plAlerts = [dict objectForKey: PLAlerts];
+	    NSEnumerator *e = [plAlerts objectEnumerator];
+	    NSDictionary *alertDict;
+	    while ( (alertDict = [e nextObject]) != nil) {
+		[self addAlert: [[PSAlert alloc] initWithPropertyList: alertDict]];
+	    }
+	} @catch (NSException *e) {
+	    [self release];
+	    @throw;
+	}
     }
     return self;
Index: trunk/Cocoa/Pester/Source/PSApplication.m
===================================================================
--- trunk/Cocoa/Pester/Source/PSApplication.m	(revision 363)
+++ trunk/Cocoa/Pester/Source/PSApplication.m	(revision 364)
@@ -215,10 +215,10 @@
         switch (answer) {
             case NSAlertDefaultReturn:
-                NS_DURING
+                @try {
                     [allAlarms importVersion1Alarms];
-                NS_HANDLER
-                    NSRunAlertPanel(@"Error occurred importing alarms", @"Pester was unable to convert some alarms created with an older version. Those alarms which could be read have been converted. The previous-format alarms have been retained; try using an older version of Pester to read them.\n\n%@", nil, nil, nil, [localException reason]);
-                    NS_VOIDRETURN;
-                NS_ENDHANDLER
+                } @catch (NSException *exception) {
+                    NSRunAlertPanel(@"Error occurred importing alarms", @"Pester was unable to convert some alarms created with an older version. Those alarms which could be read have been converted. The previous-format alarms have been retained; try using an older version of Pester to read them.\n\n%@", nil, nil, nil, [exception reason]);
+                    return;
+                }
             case NSAlertAlternateReturn:
                 [allAlarms discardVersion1Alarms];
Index: trunk/Cocoa/Pester/Source/PSMediaAlert.m
===================================================================
--- trunk/Cocoa/Pester/Source/PSMediaAlert.m	(revision 363)
+++ trunk/Cocoa/Pester/Source/PSMediaAlert.m	(revision 364)
@@ -23,5 +23,10 @@
 {
     if ( (self = [super init]) != nil) {
-        repetitions = numReps;
+	// XXX not DRY, but can't think of a more sensible way
+ 	if (numReps < 1)
+	    numReps = 1;
+	else if (numReps > 99)
+	    numReps = 99;
+       repetitions = numReps;
     }
     return self;
Index: trunk/Cocoa/Pester/Source/PSSpeechAlert.h
===================================================================
--- trunk/Cocoa/Pester/Source/PSSpeechAlert.h	(revision 363)
+++ trunk/Cocoa/Pester/Source/PSSpeechAlert.h	(revision 364)
@@ -9,8 +9,6 @@
 #import "PSAlert.h"
 
-@class SUSpeaker;
-
 @interface PSSpeechAlert : PSAlert {
-    SUSpeaker *speaker;
+    NSSpeechSynthesizer *speaker;
     NSString *voice;
     PSAlarm *alarm;
Index: trunk/Cocoa/Pester/Source/PSSpeechAlert.m
===================================================================
--- trunk/Cocoa/Pester/Source/PSSpeechAlert.m	(revision 363)
+++ trunk/Cocoa/Pester/Source/PSSpeechAlert.m	(revision 364)
@@ -10,5 +10,4 @@
 #import "PSAlarmAlertController.h"
 #import "NSDictionary-NJRExtensions.h"
-#import "SUSpeaker.h"
 
 // property list keys
@@ -52,12 +51,8 @@
     [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(_stopSpeaking:) name: PSAlarmAlertStopNotification object: nil];
     
-    if ( (speaker = [[SUSpeaker alloc] init]) == nil) return;
+    if ( (speaker = [[NSSpeechSynthesizer alloc] initWithVoice: voice]) == nil) return;
     alarm = anAlarm;
     [speaker setDelegate: self];
-    unsigned voiceIndex = [[SUSpeaker voiceNames] indexOfObject: voice];
-    if (voiceIndex == NSNotFound)
-        voiceIndex = [[SUSpeaker voiceNames] indexOfObject: [SUSpeaker defaultVoice]];
-    [speaker setVoice: voiceIndex + 1];
-    [speaker speakText: [alarm message]];
+    [speaker startSpeakingString: [alarm message]];
 }
 
@@ -74,4 +69,7 @@
 {
     NSMutableDictionary *plAlert = [[super propertyListRepresentation] mutableCopy];
+    if (voice == nil)
+	@throw [NSException exceptionWithName: NSInvalidArgumentException
+				       reason: @"The selected voice is not available." userInfo: nil];
     [plAlert setObject: voice forKey: PLAlertVoice];
     return [plAlert autorelease];
@@ -88,7 +86,7 @@
 @end
 
-@implementation PSSpeechAlert (SUSpeakerDelegate)
+@implementation PSSpeechAlert (NSSpeechSynthesizerDelegate)
 
-- (void)didFinishSpeaking:(SUSpeaker *)aSpeaker;
+- (void)speechSynthesizer:(NSSpeechSynthesizer *)sender didFinishSpeaking:(BOOL)finishedSpeaking;
 {
     [self completedForAlarm: alarm];
Index: trunk/Cocoa/Pester/Source/PSTimer.m
===================================================================
--- trunk/Cocoa/Pester/Source/PSTimer.m	(revision 363)
+++ trunk/Cocoa/Pester/Source/PSTimer.m	(revision 364)
@@ -230,9 +230,10 @@
         if ([date timeIntervalSinceNow] > 30) {
             // NSLog(@"going to sleep, setting timer %@", PSTimerOnWake);
-            NS_DURING
+	    @try {
                 [PSPowerManager setWakeTime: [[PSTimerOnWake fireDate] addTimeInterval: -15]];
-            NS_HANDLER
-                [self performSelectorOnMainThread: @selector(_runScheduledWakeErrorPanel:) withObject: [localException description] waitUntilDone: YES];
-            NS_ENDHANDLER
+            } @catch (NSException *exception) {
+                [self performSelectorOnMainThread: @selector(_runScheduledWakeErrorPanel:)
+				       withObject: [exception description] waitUntilDone: YES];
+            }
             return YES;
         } else {
Index: trunk/Cocoa/Pester/Source/Pester.xcodeproj/nicholas.mode2v3
===================================================================
--- trunk/Cocoa/Pester/Source/Pester.xcodeproj/nicholas.mode2v3	(revision 363)
+++ trunk/Cocoa/Pester/Source/Pester.xcodeproj/nicholas.mode2v3	(revision 364)
@@ -230,4 +230,6 @@
 					<array>
 						<dict>
+							<key>BecomeActive</key>
+							<true/>
 							<key>ContentConfiguration</key>
 							<dict>
@@ -259,5 +261,11 @@
 										<string>29B97314FDCFA39411CA2CEA</string>
 										<string>080E96DDFE201D6D7F000001</string>
+										<string>F560E311035787D501A4E466</string>
+										<string>F560E314035787F901A4E466</string>
+										<string>F59E47AF036AAC31016B311C</string>
+										<string>F560E312035787E001A4E466</string>
 										<string>29B97317FDCFA39411CA2CEA</string>
+										<string>E10E71F3040C335800050002</string>
+										<string>E19DCBE503BE516000B934BF</string>
 										<string>E17562BC0CF8E7A800FC5057</string>
 									</array>
@@ -265,10 +273,11 @@
 									<array>
 										<array>
-											<integer>1</integer>
+											<integer>110</integer>
+											<integer>108</integer>
 											<integer>0</integer>
 										</array>
 									</array>
 									<key>PBXSmartGroupTreeModuleOutlineStateVisibleRectKey</key>
-									<string>{{0, 0}, {334, 441}}</string>
+									<string>{{0, 1497}, {334, 441}}</string>
 								</dict>
 								<key>PBXTopSmartGroupGIDs</key>
@@ -289,5 +298,5 @@
 								</array>
 								<key>RubberWindowFrame</key>
-								<string>1570 651 351 527 0 0 1920 1178 </string>
+								<string>1570 647 351 527 0 0 1920 1178 </string>
 							</dict>
 							<key>Module</key>
@@ -329,5 +338,5 @@
 									<array>
 										<array>
-											<integer>2</integer>
+											<integer>0</integer>
 										</array>
 									</array>
@@ -429,6 +438,6 @@
 			<key>TableOfContents</key>
 			<array>
-				<string>E1F961A10CF9F8E70087C980</string>
-				<string>E1F961A20CF9F8E70087C980</string>
+				<string>E15045D00CFCF47100DE47E9</string>
+				<string>E15045D10CFCF47100DE47E9</string>
 				<string>1C9437FD063B20B00039CFAC</string>
 				<string>1C9437FE063B20B00039CFAC</string>
@@ -463,9 +472,9 @@
 	<key>WindowOrderList</key>
 	<array>
+		<string>E1B123120CF664D6004975AA</string>
 		<string>/Users/nicholas/Documents/Development/Pester/Source/Pester.xcodeproj</string>
-		<string>E1B123120CF664D6004975AA</string>
 	</array>
 	<key>WindowString</key>
-	<string>1570 651 351 527 0 0 1920 1178 </string>
+	<string>1570 647 351 527 0 0 1920 1178 </string>
 	<key>WindowToolsV3</key>
 	<array>
@@ -620,4 +629,6 @@
 					<array>
 						<dict>
+							<key>BecomeActive</key>
+							<true/>
 							<key>ContentConfiguration</key>
 							<dict>
@@ -625,5 +636,5 @@
 								<string>1CD0528F0623707200166675</string>
 								<key>PBXProjectModuleLabel</key>
-								<string></string>
+								<string>NJRValidatingField.h</string>
 								<key>StatusBarVisibility</key>
 								<true/>
@@ -642,6 +653,4 @@
 						</dict>
 						<dict>
-							<key>BecomeActive</key>
-							<true/>
 							<key>ContentConfiguration</key>
 							<dict>
@@ -683,5 +692,5 @@
 			<array>
 				<string>1C530D52069F1CE1000CFCEE</string>
-				<string>E175647E0CF9EA4700FC5057</string>
+				<string>E15043790CFB91AF00DE47E9</string>
 				<string>1CD0528F0623707200166675</string>
 				<string>XCMainBuildResultsModuleGUID</string>
@@ -725,6 +734,6 @@
 										<key>sizes</key>
 										<array>
-											<string>{{0, 0}, {445, 380}}</string>
-											<string>{{445, 0}, {536, 380}}</string>
+											<string>{{0, 0}, {445, 368}}</string>
+											<string>{{445, 0}, {536, 368}}</string>
 										</array>
 									</dict>
@@ -741,6 +750,6 @@
 										<key>sizes</key>
 										<array>
-											<string>{{0, 0}, {981, 380}}</string>
-											<string>{{0, 380}, {981, 338}}</string>
+											<string>{{0, 0}, {981, 368}}</string>
+											<string>{{0, 368}, {981, 350}}</string>
 										</array>
 									</dict>
@@ -775,10 +784,10 @@
 									</array>
 									<key>Frame</key>
-									<string>{{445, 0}, {536, 380}}</string>
+									<string>{{445, 0}, {536, 368}}</string>
 									<key>RubberWindowFrame</key>
-									<string>491 171 981 759 0 0 1920 1178 </string>
+									<string>843 200 981 759 0 0 1920 1178 </string>
 								</dict>
 								<key>RubberWindowFrame</key>
-								<string>491 171 981 759 0 0 1920 1178 </string>
+								<string>843 200 981 759 0 0 1920 1178 </string>
 							</dict>
 							<key>Module</key>
@@ -803,16 +812,16 @@
 			<array>
 				<string>1C530D54069F1CE1000CFCEE</string>
-				<string>E17564230CF9EA2400FC5057</string>
+				<string>E15043670CFB8D4800DE47E9</string>
 				<string>1C162984064C10D400B95A72</string>
-				<string>E17564240CF9EA2400FC5057</string>
-				<string>E17564250CF9EA2400FC5057</string>
-				<string>E17564260CF9EA2400FC5057</string>
-				<string>E17564270CF9EA2400FC5057</string>
-				<string>E17564280CF9EA2400FC5057</string>
+				<string>E15043680CFB8D4800DE47E9</string>
+				<string>E15043690CFB8D4800DE47E9</string>
+				<string>E150436A0CFB8D4800DE47E9</string>
+				<string>E150436B0CFB8D4800DE47E9</string>
+				<string>E150436C0CFB8D4800DE47E9</string>
 			</array>
 			<key>ToolbarConfiguration</key>
 			<string>xcode.toolbar.config.debugV3</string>
 			<key>WindowString</key>
-			<string>491 171 981 759 0 0 1920 1178 </string>
+			<string>843 200 981 759 0 0 1920 1178 </string>
 			<key>WindowToolGUID</key>
 			<string>1C530D54069F1CE1000CFCEE</string>
@@ -841,5 +850,5 @@
 										<string>1CDD528C0622207200134675</string>
 										<key>PBXProjectModuleLabel</key>
-										<string>&lt;No Editor&gt;</string>
+										<string>PSApplication.m</string>
 										<key>StatusBarVisibility</key>
 										<true/>
@@ -848,7 +857,7 @@
 									<dict>
 										<key>Frame</key>
-										<string>{{0, 0}, {878, 347}}</string>
+										<string>{{0, 0}, {878, 241}}</string>
 										<key>RubberWindowFrame</key>
-										<string>47 178 878 716 0 0 1920 1178 </string>
+										<string>619 85 878 716 0 0 1920 1178 </string>
 									</dict>
 									<key>Module</key>
@@ -859,7 +868,9 @@
 							</array>
 							<key>Proportion</key>
-							<string>347pt</string>
-						</dict>
-						<dict>
+							<string>241pt</string>
+						</dict>
+						<dict>
+							<key>BecomeActive</key>
+							<true/>
 							<key>ContentConfiguration</key>
 							<dict>
@@ -872,12 +883,12 @@
 							<dict>
 								<key>Frame</key>
-								<string>{{0, 352}, {878, 323}}</string>
+								<string>{{0, 246}, {878, 429}}</string>
 								<key>RubberWindowFrame</key>
-								<string>47 178 878 716 0 0 1920 1178 </string>
+								<string>619 85 878 716 0 0 1920 1178 </string>
 							</dict>
 							<key>Module</key>
 							<string>PBXProjectFindModule</string>
 							<key>Proportion</key>
-							<string>323pt</string>
+							<string>429pt</string>
 						</dict>
 					</array>
@@ -897,11 +908,11 @@
 			<array>
 				<string>1C530D57069F1CE1000CFCEE</string>
-				<string>E17564860CF9F5F400FC5057</string>
-				<string>E17564870CF9F5F400FC5057</string>
+				<string>E15043970CFBA77500DE47E9</string>
+				<string>E15043980CFBA77500DE47E9</string>
 				<string>1CDD528C0622207200134675</string>
 				<string>1CD0528E0623707200166675</string>
 			</array>
 			<key>WindowString</key>
-			<string>47 178 878 716 0 0 1920 1178 </string>
+			<string>619 85 878 716 0 0 1920 1178 </string>
 			<key>WindowToolGUID</key>
 			<string>1C530D57069F1CE1000CFCEE</string>
@@ -938,16 +949,16 @@
 							<dict>
 								<key>Frame</key>
-								<string>{{0, 0}, {915, 284}}</string>
+								<string>{{0, 0}, {1144, 216}}</string>
 								<key>RubberWindowFrame</key>
-								<string>447 136 915 325 0 0 1920 1178 </string>
+								<string>677 91 1144 257 0 0 1920 1178 </string>
 							</dict>
 							<key>Module</key>
 							<string>PBXDebugCLIModule</string>
 							<key>Proportion</key>
-							<string>284pt</string>
+							<string>216pt</string>
 						</dict>
 					</array>
 					<key>Proportion</key>
-					<string>284pt</string>
+					<string>216pt</string>
 				</dict>
 			</array>
@@ -963,5 +974,5 @@
 			<array>
 				<string>1C530D5B069F1CE1000CFCEE</string>
-				<string>E17564290CF9EA2400FC5057</string>
+				<string>E150436D0CFB8D4800DE47E9</string>
 				<string>1C78EAAC065D492600B07095</string>
 			</array>
@@ -969,5 +980,5 @@
 			<string>xcode.toolbar.config.consoleV3</string>
 			<key>WindowString</key>
-			<string>447 136 915 325 0 0 1920 1178 </string>
+			<string>677 91 1144 257 0 0 1920 1178 </string>
 			<key>WindowToolGUID</key>
 			<string>1C530D5B069F1CE1000CFCEE</string>
@@ -1034,7 +1045,7 @@
 							<dict>
 								<key>Frame</key>
-								<string>{{0, 0}, {545, 0}}</string>
+								<string>{{0, 0}, {586, 0}}</string>
 								<key>RubberWindowFrame</key>
-								<string>934 569 545 283 0 0 1920 1178 </string>
+								<string>960 488 586 680 0 0 1920 1178 </string>
 							</dict>
 							<key>Module</key>
@@ -1054,5 +1065,5 @@
 									<key>PBXCVSModuleTreeModuleColumnWidthsKey</key>
 									<array>
-										<real>216</real>
+										<real>257</real>
 										<real>40.66162109375</real>
 										<real>53</real>
@@ -1075,22 +1086,20 @@
 								<key>PBXProjectModuleLabel</key>
 								<string>SCM Results</string>
-								<key>SCMActivityViewerShowingDefaultKey</key>
-								<string>{{0, 237}, {545, 0}}</string>
 							</dict>
 							<key>GeometryConfiguration</key>
 							<dict>
 								<key>Frame</key>
-								<string>{{0, 5}, {545, 237}}</string>
+								<string>{{0, 5}, {586, 634}}</string>
 								<key>RubberWindowFrame</key>
-								<string>934 569 545 283 0 0 1920 1178 </string>
+								<string>960 488 586 680 0 0 1920 1178 </string>
 							</dict>
 							<key>Module</key>
 							<string>PBXCVSModule</string>
 							<key>Proportion</key>
-							<string>237pt</string>
+							<string>634pt</string>
 						</dict>
 					</array>
 					<key>Proportion</key>
-					<string>242pt</string>
+					<string>639pt</string>
 				</dict>
 			</array>
@@ -1106,5 +1115,5 @@
 			<array>
 				<string>E1B123120CF664D6004975AA</string>
-				<string>E1F961A30CF9F8E70087C980</string>
+				<string>E150465E0CFCFB0200DE47E9</string>
 				<string>1C78EAB2065D492600B07095</string>
 				<string>1CD052920623707200166675</string>
@@ -1113,5 +1122,5 @@
 			<string>xcode.toolbar.config.scm</string>
 			<key>WindowString</key>
-			<string>934 569 545 283 0 0 1920 1178 </string>
+			<string>960 488 586 680 0 0 1920 1178 </string>
 			<key>WindowToolGUID</key>
 			<string>E1B123120CF664D6004975AA</string>
@@ -1234,6 +1243,6 @@
 			<key>TableOfContents</key>
 			<array>
-				<string>E1B1236F0CF67A4B004975AA</string>
-				<string>E1B123700CF67A4B004975AA</string>
+				<string>E15043BB0CFCC31200DE47E9</string>
+				<string>E15043BC0CFCC31200DE47E9</string>
 				<string>1CE0B1FE06471DED0097A5F4</string>
 				<string>1CA1AED706398EBD00589147</string>
@@ -1244,5 +1253,5 @@
 			<string>290 117 744 409 0 0 1920 1178 </string>
 			<key>WindowToolGUID</key>
-			<string>E1B1236F0CF67A4B004975AA</string>
+			<string>E15043BB0CFCC31200DE47E9</string>
 			<key>WindowToolIsVisible</key>
 			<false/>
@@ -1427,8 +1436,12 @@
 		</dict>
 		<dict>
+			<key>FirstTimeWindowDisplayed</key>
+			<false/>
 			<key>Identifier</key>
 			<string>windowTool.refactoring</string>
 			<key>IncludeInToolsMenu</key>
 			<integer>0</integer>
+			<key>IsVertical</key>
+			<true/>
 			<key>Layout</key>
 			<array>
@@ -1437,21 +1450,28 @@
 					<array>
 						<dict>
-							<key>BecomeActive</key>
-							<integer>1</integer>
+							<key>ContentConfiguration</key>
+							<dict>
+								<key>PBXProjectModuleGUID</key>
+								<string>E15044550CFCDC1D00DE47E9</string>
+							</dict>
 							<key>GeometryConfiguration</key>
 							<dict>
 								<key>Frame</key>
-								<string>{0, 0}, {500, 335}</string>
+								<string>{{0, 0}, {891, 710}}</string>
 								<key>RubberWindowFrame</key>
-								<string>{0, 0}, {500, 335}</string>
+								<string>371 185 891 751 0 0 1920 1178 </string>
+								<key>XCRefactoringSplitViewLowerHeight</key>
+								<real>310</real>
+								<key>XCRefactoringSplitViewTotalHeight</key>
+								<real>627</real>
 							</dict>
 							<key>Module</key>
 							<string>XCRefactoringModule</string>
 							<key>Proportion</key>
-							<string>100%</string>
+							<string>710pt</string>
 						</dict>
 					</array>
 					<key>Proportion</key>
-					<string>100%</string>
+					<string>710pt</string>
 				</dict>
 			</array>
@@ -1462,6 +1482,18 @@
 				<string>XCRefactoringModule</string>
 			</array>
+			<key>StatusbarIsVisible</key>
+			<true/>
+			<key>TableOfContents</key>
+			<array>
+				<string>E15044560CFCDC1D00DE47E9</string>
+				<string>E15044570CFCDC1D00DE47E9</string>
+				<string>E15044550CFCDC1D00DE47E9</string>
+			</array>
 			<key>WindowString</key>
-			<string>200 200 500 356 0 0 1920 1200 </string>
+			<string>371 185 891 751 0 0 1920 1178 </string>
+			<key>WindowToolGUID</key>
+			<string>E15044560CFCDC1D00DE47E9</string>
+			<key>WindowToolIsVisible</key>
+			<false/>
 		</dict>
 	</array>
Index: trunk/Cocoa/Pester/Source/Pester.xcodeproj/nicholas.pbxuser
===================================================================
--- trunk/Cocoa/Pester/Source/Pester.xcodeproj/nicholas.pbxuser	(revision 363)
+++ trunk/Cocoa/Pester/Source/Pester.xcodeproj/nicholas.pbxuser	(revision 364)
@@ -3,6 +3,6 @@
 	089C165DFE840E0CC02AAC07 /* English */ = {
 		uiCtxt = {
-			sepNavIntBoundsRect = "{{0, 0}, {813, 876}}";
-			sepNavSelRange = "{145, 0}";
+			sepNavIntBoundsRect = "{{0, 0}, {813, 899}}";
+			sepNavSelRange = "{105, 0}";
 			sepNavVisRange = "{0, 229}";
 			sepNavWindowFrame = "{{15, 238}, {872, 935}}";
@@ -19,8 +19,7 @@
 			F596730D0355CFA301407038 /* -[_NSZombie methodSignatureForSelector:] */,
 			F500E5370359719B01AEEDB1 /* szone_error */,
-			F5B15F74037E42EB01AEEDB1 /* -[NSException raise] */,
 			E1A9DEE703BF3C110089E8B2 /* -[_NSZombie release] */,
-			E145384C043A31D900050002 /* NJRHotKeyManager.m:158 */,
 			E17CA6640CF697FE00018B34 /* SetPortRGBForeColor */,
+			E15043CF0CFCC41500DE47E9 /* objc_exception_throw */,
 		);
 		codeSenseManager = E1B11EDD0CF5F2EB004975AA /* Code sense */;
@@ -60,5 +59,5 @@
 					315,
 					20,
-					48.16259765625,
+					48,
 					43,
 					43,
@@ -75,5 +74,5 @@
 				);
 			};
-			PBXPerProjectTemplateStateSaveDate = 217708774;
+			PBXPerProjectTemplateStateSaveDate = 217904241;
 			"PBXTemplateGeometry-F5314676015831810DCA290F" = {
 				ContentSize = "{618, 257}";
@@ -794,5 +793,5 @@
 				},
 			);
-			PBXWorkspaceStateSaveDate = 217708774;
+			PBXWorkspaceStateSaveDate = 217904241;
 		};
 		sourceControlManager = E1B11EDC0CF5F2EB004975AA /* Source Control */;
@@ -806,4 +805,12 @@
 			sepNavSelRange = "{208, 0}";
 			sepNavVisRange = "{0, 252}";
+		};
+	};
+	E10E71F4040C335800050002 /* English */ = {
+		uiCtxt = {
+			sepNavIntBoundsRect = "{{0, 0}, {993, 1503}}";
+			sepNavSelRange = "{1411, 0}";
+			sepNavVisRange = "{0, 3709}";
+			sepNavWindowFrame = "{{61, 198}, {1052, 933}}";
 		};
 	};
@@ -818,7 +825,7 @@
 	E11373150439FCC700050002 /* NJRHotKey.m */ = {
 		uiCtxt = {
-			sepNavIntBoundsRect = "{{0, 0}, {830, 1287}}";
-			sepNavSelRange = "{1856, 46}";
-			sepNavVisRange = "{1263, 1577}";
+			sepNavIntBoundsRect = "{{0, 0}, {830, 1298}}";
+			sepNavSelRange = "{2509, 35}";
+			sepNavVisRange = "{1321, 1515}";
 			sepNavWindowFrame = "{{199, 307}, {889, 698}}";
 		};
@@ -854,16 +861,24 @@
 	E130B9AD03B7C59700000028 /* PSPowerManager.m */ = {
 		uiCtxt = {
-			sepNavIntBoundsRect = "{{0, 0}, {817, 1045}}";
-			sepNavSelRange = "{386, 27}";
-			sepNavVisRange = "{24, 683}";
+			sepNavIntBoundsRect = "{{0, 0}, {817, 1078}}";
+			sepNavSelRange = "{387, 16}";
+			sepNavVisRange = "{180, 425}";
 			sepNavWindowFrame = "{{153, 349}, {889, 698}}";
 		};
 	};
+	E13919E203B9E4E100000028 /* NJRIntervalField.h */ = {
+		uiCtxt = {
+			sepNavIntBoundsRect = "{{0, 0}, {761, 780}}";
+			sepNavSelRange = "{419, 0}";
+			sepNavVisRange = "{0, 425}";
+			sepNavWindowFrame = "{{15, 49}, {834, 1124}}";
+		};
+	};
 	E13919E303B9E4E100000028 /* NJRIntervalField.m */ = {
 		uiCtxt = {
-			sepNavIntBoundsRect = "{{0, 0}, {761, 1980}}";
-			sepNavSelRange = "{3944, 0}";
-			sepNavVisRange = "{2218, 3841}";
-			sepNavWindowFrame = "{{15, 334}, {820, 839}}";
+			sepNavIntBoundsRect = "{{0, 0}, {761, 946}}";
+			sepNavSelRange = "{2674, 0}";
+			sepNavVisRange = "{329, 2345}";
+			sepNavWindowFrame = "{{364, 159}, {820, 839}}";
 		};
 	};
@@ -876,19 +891,33 @@
 		};
 	};
-	E145384C043A31D900050002 /* NJRHotKeyManager.m:158 */ = {
-		isa = PBXFileBreakpoint;
+	E15043CF0CFCC41500DE47E9 /* objc_exception_throw */ = {
+		isa = PBXSymbolicBreakpoint;
 		actions = (
 		);
-		breakpointStyle = 0;
+		breakpointStyle = 1;
 		continueAfterActions = 0;
 		countType = 0;
 		delayBeforeContinue = 0;
-		fileReference = E145383F043A1DB200050002 /* NJRHotKeyManager.m */;
-		hitCount = 0;
+		hitCount = 1;
 		ignoreCount = 0;
-		lineNumber = 158;
-		location = Pester;
-		modificationTime = 217621338.571945;
-		state = 1;
+		location = libobjc.A.dylib;
+		modificationTime = 217892071.677906;
+		state = 2;
+		symbolName = objc_exception_throw;
+	};
+	E15044650CFCDC7E00DE47E9 /* NJRValidatingField.h */ = {
+		uiCtxt = {
+			sepNavIntBoundsRect = "{{0, 0}, {869, 213}}";
+			sepNavSelRange = "{170, 0}";
+			sepNavVisRange = "{0, 344}";
+		};
+	};
+	E15044660CFCDC7E00DE47E9 /* NJRValidatingField.m */ = {
+		uiCtxt = {
+			sepNavIntBoundsRect = "{{0, 0}, {775, 1122}}";
+			sepNavSelRange = "{0, 0}";
+			sepNavVisRange = "{0, 4696}";
+			sepNavWindowFrame = "{{15, 49}, {834, 1124}}";
+		};
 	};
 	E162698303B6189000000028 /* NSTableView-NJRExtensions.m */ = {
@@ -933,7 +962,7 @@
 		uiCtxt = {
 			sepNavIntBoundsRect = "{{0, 0}, {993, 897}}";
-			sepNavSelRange = "{419, 0}";
-			sepNavVisRange = "{0, 471}";
-			sepNavWindowFrame = "{{222, 51}, {1052, 933}}";
+			sepNavSelRange = "{369, 87}";
+			sepNavVisRange = "{0, 952}";
+			sepNavWindowFrame = "{{560, 24}, {1052, 933}}";
 		};
 	};
@@ -949,6 +978,6 @@
 		ignoreCount = 0;
 		location = "";
-		modificationTime = 217621338.896014;
-		state = 0;
+		modificationTime = 217892051.042254;
+		state = 2;
 		symbolName = SetPortRGBForeColor;
 	};
@@ -962,7 +991,7 @@
 	E18DA4090442C56D00050002 /* PSMediaAlert.m */ = {
 		uiCtxt = {
-			sepNavIntBoundsRect = "{{0, 0}, {761, 792}}";
-			sepNavSelRange = "{618, 0}";
-			sepNavVisRange = "{3, 1720}";
+			sepNavIntBoundsRect = "{{0, 0}, {761, 847}}";
+			sepNavSelRange = "{643, 0}";
+			sepNavVisRange = "{0, 1827}";
 			sepNavWindowFrame = "{{153, 208}, {820, 839}}";
 		};
@@ -970,7 +999,7 @@
 	E18DAAEB0442CC3E00050002 /* NJRSoundManager.m */ = {
 		uiCtxt = {
-			sepNavIntBoundsRect = "{{0, 0}, {761, 1254}}";
-			sepNavSelRange = "{846, 134}";
-			sepNavVisRange = "{0, 2251}";
+			sepNavIntBoundsRect = "{{0, 0}, {761, 1210}}";
+			sepNavSelRange = "{576, 0}";
+			sepNavVisRange = "{0, 2216}";
 			sepNavWindowFrame = "{{61, 292}, {820, 839}}";
 		};
@@ -996,7 +1025,7 @@
 	E1A18CAC03B4344C00000028 /* PSAlerts.m */ = {
 		uiCtxt = {
-			sepNavIntBoundsRect = "{{0, 0}, {830, 1617}}";
-			sepNavSelRange = "{2194, 0}";
-			sepNavVisRange = "{1314, 2197}";
+			sepNavIntBoundsRect = "{{0, 0}, {830, 1672}}";
+			sepNavSelRange = "{3864, 7}";
+			sepNavVisRange = "{1868, 2150}";
 			sepNavWindowFrame = "{{528, 47}, {889, 698}}";
 		};
@@ -1013,6 +1042,6 @@
 		ignoreCount = 0;
 		location = "";
-		modificationTime = 217621338.880709;
-		state = 0;
+		modificationTime = 217892051.041767;
+		state = 2;
 		symbolName = "-[_NSZombie release]";
 	};
@@ -1037,6 +1066,6 @@
 	E1B11F8B0CF5F30F004975AA /* Info-Pester.plist */ = {
 		uiCtxt = {
-			sepNavIntBoundsRect = "{{0, 0}, {736, 865}}";
-			sepNavSelRange = "{981, 0}";
+			sepNavIntBoundsRect = "{{0, 0}, {736, 888}}";
+			sepNavSelRange = "{346, 0}";
 			sepNavVisRange = "{0, 2337}";
 			sepNavWindowFrame = "{{549, 47}, {795, 924}}";
@@ -1113,8 +1142,24 @@
 	E1BF6E9103C928D200050002 /* PSTimer.m */ = {
 		uiCtxt = {
-			sepNavIntBoundsRect = "{{0, 0}, {940, 2908}}";
-			sepNavSelRange = "{5491, 0}";
-			sepNavVisRange = "{0, 3062}";
+			sepNavIntBoundsRect = "{{0, 0}, {920, 2842}}";
+			sepNavSelRange = "{4580, 0}";
+			sepNavVisRange = "{409, 1359}";
 			sepNavWindowFrame = "{{38, 50}, {999, 1128}}";
+		};
+	};
+	E1CDEC3D0444053F00AEEDB1 /* NJRNonCenteringWindow.h */ = {
+		uiCtxt = {
+			sepNavIntBoundsRect = "{{0, 0}, {993, 874}}";
+			sepNavSelRange = "{0, 0}";
+			sepNavVisRange = "{0, 242}";
+			sepNavWindowFrame = "{{176, 93}, {1052, 933}}";
+		};
+	};
+	E1CDEC3E0444053F00AEEDB1 /* NJRNonCenteringWindow.m */ = {
+		uiCtxt = {
+			sepNavIntBoundsRect = "{{0, 0}, {993, 874}}";
+			sepNavSelRange = "{0, 0}";
+			sepNavVisRange = "{0, 261}";
+			sepNavWindowFrame = "{{153, 114}, {1052, 933}}";
 		};
 	};
@@ -1142,10 +1187,10 @@
 		};
 	};
-	E1E9A33903C8EF6700050002 /* SUSpeaker.m */ = {
-		uiCtxt = {
-			sepNavIntBoundsRect = "{{0, 0}, {869, 3234}}";
-			sepNavSelRange = "{1839, 122}";
-			sepNavVisRange = "{1660, 639}";
-			sepNavWindowFrame = "{{314, 202}, {889, 698}}";
+	E1ECA00703C002B300F54C66 /* PSError.m */ = {
+		uiCtxt = {
+			sepNavIntBoundsRect = "{{0, 0}, {993, 874}}";
+			sepNavSelRange = "{254, 0}";
+			sepNavVisRange = "{0, 1270}";
+			sepNavWindowFrame = "{{153, 114}, {1052, 933}}";
 		};
 	};
@@ -1215,29 +1260,43 @@
 		ignoreCount = 0;
 		location = libSystem.B.dylib;
-		modificationTime = 217621338.572037;
-		state = 1;
+		modificationTime = 217892051.041262;
+		state = 2;
 		symbolName = szone_error;
 	};
 	F517D63E0356B80C016D68B9 /* PSAlarmsController.m */ = {
 		uiCtxt = {
-			sepNavIntBoundsRect = "{{0, 0}, {830, 2057}}";
-			sepNavSelRange = "{3828, 50}";
-			sepNavVisRange = "{2755, 1818}";
-			sepNavWindowFrame = "{{360, 160}, {889, 698}}";
+			sepNavIntBoundsRect = "{{0, 0}, {817, 2123}}";
+			sepNavSelRange = "{2816, 14}";
+			sepNavVisRange = "{2384, 686}";
+			sepNavWindowFrame = "{{744, 79}, {889, 698}}";
+		};
+	};
+	F517D63F0356B80C016D68B9 /* PSAlarmsController.h */ = {
+		uiCtxt = {
+			sepNavIntBoundsRect = "{{0, 0}, {830, 639}}";
+			sepNavSelRange = "{0, 0}";
+			sepNavVisRange = "{0, 460}";
 		};
 	};
 	F517D6420356B8CC016D68B9 /* PSAlarms.m */ = {
 		uiCtxt = {
-			sepNavIntBoundsRect = "{{0, 0}, {920, 3619}}";
-			sepNavSelRange = "{8316, 0}";
-			sepNavVisRange = "{7670, 1218}";
-			sepNavWindowFrame = "{{176, 187}, {820, 839}}";
+			sepNavIntBoundsRect = "{{0, 0}, {761, 3737}}";
+			sepNavSelRange = "{9736, 0}";
+			sepNavVisRange = "{7626, 2401}";
+			sepNavWindowFrame = "{{938, 170}, {820, 839}}";
+		};
+	};
+	F517D6430356B8CC016D68B9 /* PSAlarms.h */ = {
+		uiCtxt = {
+			sepNavIntBoundsRect = "{{0, 0}, {761, 780}}";
+			sepNavSelRange = "{0, 0}";
+			sepNavVisRange = "{0, 953}";
 		};
 	};
 	F560E2F10357506401A4E466 /* PSApplication.m */ = {
 		uiCtxt = {
-			sepNavIntBoundsRect = "{{0, 0}, {761, 3157}}";
-			sepNavSelRange = "{1549, 0}";
-			sepNavVisRange = "{0, 1956}";
+			sepNavIntBoundsRect = "{{0, 0}, {817, 2772}}";
+			sepNavSelRange = "{9502, 21}";
+			sepNavVisRange = "{8732, 887}";
 			sepNavWindowFrame = "{{586, 75}, {820, 839}}";
 		};
@@ -1268,7 +1327,7 @@
 	F589C9C6035AA12B0155F4B2 /* Read Me.rtfd */ = {
 		uiCtxt = {
-			sepNavIntBoundsRect = "{{0, 0}, {960, 4459}}";
-			sepNavSelRange = "{14260, 0}";
-			sepNavVisRect = "{{0, 2212}, {960, 1091}}";
+			sepNavIntBoundsRect = "{{0, 0}, {960, 4408}}";
+			sepNavSelRange = "{16, 0}";
+			sepNavVisRect = "{{0, 199}, {960, 1091}}";
 			sepNavWindowFrame = "{{15, 50}, {999, 1128}}";
 		};
@@ -1285,13 +1344,13 @@
 		ignoreCount = 0;
 		location = "";
-		modificationTime = 217621338.862668;
-		state = 0;
+		modificationTime = 217892051.040556;
+		state = 2;
 		symbolName = "-[_NSZombie methodSignatureForSelector:]";
 	};
 	F59DC6D30353C9E601AEEDB1 /* PSAlarmSetController.h */ = {
 		uiCtxt = {
-			sepNavIntBoundsRect = "{{0, 0}, {940, 1092}}";
-			sepNavSelRange = "{945, 9}";
-			sepNavVisRange = "{0, 2347}";
+			sepNavIntBoundsRect = "{{0, 0}, {775, 1065}}";
+			sepNavSelRange = "{1563, 0}";
+			sepNavVisRange = "{0, 2381}";
 			sepNavWindowFrame = "{{23, 50}, {999, 1128}}";
 		};
@@ -1299,15 +1358,15 @@
 	F59DC6D40353C9E601AEEDB1 /* PSAlarmSetController.m */ = {
 		uiCtxt = {
-			sepNavIntBoundsRect = "{{0, 0}, {940, 6539}}";
-			sepNavSelRange = "{4235, 0}";
-			sepNavVisRange = "{2184, 4891}";
-			sepNavWindowFrame = "{{507, 50}, {999, 1128}}";
+			sepNavIntBoundsRect = "{{0, 0}, {775, 7139}}";
+			sepNavSelRange = "{21486, 11}";
+			sepNavVisRange = "{18453, 3820}";
+			sepNavWindowFrame = "{{561, 54}, {834, 1124}}";
 		};
 	};
 	F59DC6DC0353DE5101AEEDB1 /* PSAlarmNotifierController.m */ = {
 		uiCtxt = {
-			sepNavIntBoundsRect = "{{0, 0}, {920, 2233}}";
-			sepNavSelRange = "{2975, 17}";
-			sepNavVisRange = "{2072, 999}";
+			sepNavIntBoundsRect = "{{0, 0}, {817, 2391}}";
+			sepNavSelRange = "{4583, 27}";
+			sepNavVisRange = "{4327, 730}";
 			sepNavWindowFrame = "{{613, 82}, {820, 839}}";
 		};
@@ -1344,8 +1403,8 @@
 	F59E47A7036AAA6D016B311C /* PSAlert.m */ = {
 		uiCtxt = {
-			sepNavIntBoundsRect = "{{0, 0}, {761, 869}}";
-			sepNavSelRange = "{1016, 17}";
-			sepNavVisRange = "{0, 1813}";
-			sepNavWindowFrame = "{{469, -22}, {820, 839}}";
+			sepNavIntBoundsRect = "{{0, 0}, {761, 891}}";
+			sepNavSelRange = "{1898, 0}";
+			sepNavVisRange = "{145, 1875}";
+			sepNavWindowFrame = "{{641, 165}, {820, 839}}";
 		};
 	};
@@ -1360,6 +1419,14 @@
 		uiCtxt = {
 			sepNavIntBoundsRect = "{{0, 0}, {1297, 781}}";
-			sepNavSelRange = "{872, 0}";
-			sepNavVisRange = "{32, 1931}";
+			sepNavSelRange = "{1311, 0}";
+			sepNavVisRange = "{0, 1940}";
+			sepNavWindowFrame = "{{360, 78}, {1356, 780}}";
+		};
+	};
+	F59E47AC036AAC1E016B311C /* PSBeepAlert.h */ = {
+		uiCtxt = {
+			sepNavIntBoundsRect = "{{0, 0}, {1297, 721}}";
+			sepNavSelRange = "{200, 12}";
+			sepNavVisRange = "{0, 370}";
 			sepNavWindowFrame = "{{360, 78}, {1356, 780}}";
 		};
@@ -1396,4 +1463,19 @@
 		};
 	};
+	F59E47BC036AB228016B311C /* PSMovieAlert.m */ = {
+		uiCtxt = {
+			sepNavIntBoundsRect = "{{0, 0}, {993, 1522}}";
+			sepNavSelRange = "{1930, 0}";
+			sepNavVisRange = "{1829, 2467}";
+			sepNavWindowFrame = "{{84, 177}, {1052, 933}}";
+		};
+	};
+	F59E47BD036AB228016B311C /* PSMovieAlert.h */ = {
+		uiCtxt = {
+			sepNavIntBoundsRect = "{{0, 0}, {993, 874}}";
+			sepNavSelRange = "{0, 0}";
+			sepNavVisRange = "{0, 598}";
+		};
+	};
 	F59E4BE6036B2E5E016B311C /* PSScriptAlert.m */ = {
 		uiCtxt = {
@@ -1414,46 +1496,77 @@
 	F59F6CB40354E7DA01AEEDB1 /* PSAlarm.h */ = {
 		uiCtxt = {
-			sepNavIntBoundsRect = "{{0, 0}, {830, 924}}";
+			sepNavIntBoundsRect = "{{0, 0}, {993, 913}}";
 			sepNavSelRange = "{0, 0}";
-			sepNavVisRange = "{0, 1854}";
+			sepNavVisRange = "{0, 2636}";
+			sepNavWindowFrame = "{{15, 240}, {1052, 933}}";
 		};
 	};
 	F59F6CB50354E7DA01AEEDB1 /* PSAlarm.m */ = {
 		uiCtxt = {
-			sepNavIntBoundsRect = "{{0, 0}, {828, 6292}}";
-			sepNavSelRange = "{823, 0}";
-			sepNavVisRange = "{0, 2454}";
-			sepNavWindowFrame = "{{214, 23}, {887, 884}}";
-		};
-	};
-	F5B15F74037E42EB01AEEDB1 /* -[NSException raise] */ = {
-		isa = PBXSymbolicBreakpoint;
-		actions = (
-		);
-		breakpointStyle = 0;
-		continueAfterActions = 0;
-		countType = 0;
-		delayBeforeContinue = 0;
-		hitCount = 0;
-		ignoreCount = 0;
-		location = CoreFoundation;
-		modificationTime = 217621338.572041;
-		state = 1;
-		symbolName = "-[NSException raise]";
+			sepNavIntBoundsRect = "{{0, 0}, {993, 6490}}";
+			sepNavSelRange = "{20070, 0}";
+			sepNavVisRange = "{17193, 2959}";
+			sepNavWindowFrame = "{{466, 241}, {1052, 933}}";
+		};
+	};
+	F5B79836035F64F80136A8DD /* NJRTableView.m */ = {
+		uiCtxt = {
+			sepNavIntBoundsRect = "{{0, 0}, {993, 1210}}";
+			sepNavSelRange = "{899, 0}";
+			sepNavVisRange = "{0, 2747}";
+		};
+	};
+	F5B79837035F64F80136A8DD /* NJRTableView.h */ = {
+		uiCtxt = {
+			sepNavIntBoundsRect = "{{0, 0}, {993, 897}}";
+			sepNavSelRange = "{646, 0}";
+			sepNavVisRange = "{0, 646}";
+			sepNavWindowFrame = "{{835, 245}, {1052, 933}}";
+		};
 	};
 	F5F1986C036B813101EB0372 /* PSSpeechAlert.m */ = {
 		uiCtxt = {
-			sepNavIntBoundsRect = "{{0, 0}, {761, 1078}}";
-			sepNavSelRange = "{932, 0}";
-			sepNavVisRange = "{177, 1917}";
-			sepNavWindowFrame = "{{533, 79}, {820, 839}}";
+			sepNavIntBoundsRect = "{{0, 0}, {920, 1056}}";
+			sepNavSelRange = "{1881, 0}";
+			sepNavVisRange = "{1560, 905}";
+			sepNavWindowFrame = "{{454, 61}, {1052, 933}}";
+		};
+	};
+	F5F1986D036B813101EB0372 /* PSSpeechAlert.h */ = {
+		uiCtxt = {
+			sepNavIntBoundsRect = "{{0, 0}, {817, 264}}";
+			sepNavSelRange = "{172, 0}";
+			sepNavVisRange = "{0, 264}";
 		};
 	};
 	F5F19870036B822E01EB0372 /* NJRVoicePopUpButton.m */ = {
 		uiCtxt = {
-			sepNavIntBoundsRect = "{{0, 0}, {869, 1496}}";
-			sepNavSelRange = "{514, 81}";
-			sepNavVisRange = "{226, 979}";
-			sepNavWindowFrame = "{{61, 433}, {889, 698}}";
+			sepNavIntBoundsRect = "{{0, 0}, {830, 1441}}";
+			sepNavSelRange = "{976, 0}";
+			sepNavVisRange = "{0, 1846}";
+			sepNavWindowFrame = "{{981, 73}, {889, 698}}";
+		};
+	};
+	F5F19871036B822E01EB0372 /* NJRVoicePopUpButton.h */ = {
+		uiCtxt = {
+			sepNavIntBoundsRect = "{{0, 0}, {830, 662}}";
+			sepNavSelRange = "{250, 0}";
+			sepNavVisRange = "{0, 583}";
+		};
+	};
+	F5F198D0036BE57401EB0372 /* NJRTableDelegate.m */ = {
+		uiCtxt = {
+			sepNavIntBoundsRect = "{{0, 0}, {993, 2970}}";
+			sepNavSelRange = "{8161, 0}";
+			sepNavVisRange = "{6950, 2655}";
+			sepNavWindowFrame = "{{236, 118}, {1052, 933}}";
+		};
+	};
+	F5F198D1036BE57401EB0372 /* NJRTableDelegate.h */ = {
+		uiCtxt = {
+			sepNavIntBoundsRect = "{{0, 0}, {993, 897}}";
+			sepNavSelRange = "{495, 46}";
+			sepNavVisRange = "{0, 546}";
+			sepNavWindowFrame = "{{130, 135}, {1052, 933}}";
 		};
 	};
Index: trunk/Cocoa/Pester/Source/Pester.xcodeproj/project.pbxproj
===================================================================
--- trunk/Cocoa/Pester/Source/Pester.xcodeproj/project.pbxproj	(revision 363)
+++ trunk/Cocoa/Pester/Source/Pester.xcodeproj/project.pbxproj	(revision 364)
@@ -8,8 +8,9 @@
 
 /* Begin PBXBuildFile section */
+		E15044670CFCDC7E00DE47E9 /* NJRValidatingField.m in Sources */ = {isa = PBXBuildFile; fileRef = E15044660CFCDC7E00DE47E9 /* NJRValidatingField.m */; };
 		E17562620CF8C50B00FC5057 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E17562610CF8C50B00FC5057 /* Sparkle.framework */; };
-		E17562720CF8C53F00FC5057 /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = E17562610CF8C50B00FC5057 /* Sparkle.framework */; };
+		E17562720CF8C53F00FC5057 /* Sparkle.framework in Copy Sparkle.framework */ = {isa = PBXBuildFile; fileRef = E17562610CF8C50B00FC5057 /* Sparkle.framework */; };
 		E17CA7BB0CF7D08C00018B34 /* DynaLoader.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E17CA7BA0CF7D08C00018B34 /* DynaLoader.a */; };
-		E17CA9AB0CF7F8CB00018B34 /* Manip.pm in CopyFiles */ = {isa = PBXBuildFile; fileRef = E17CA9A90CF7F8C200018B34 /* Manip.pm */; };
+		E17CA9AB0CF7F8CB00018B34 /* Manip.pm in Copy Perl Modules */ = {isa = PBXBuildFile; fileRef = E17CA9A90CF7F8C200018B34 /* Manip.pm */; };
 		E1B11F270CF5F30F004975AA /* Notifier.nib in Resources */ = {isa = PBXBuildFile; fileRef = F59DC6DF0353DF5E01AEEDB1 /* Notifier.nib */; };
 		E1B11F280CF5F30F004975AA /* Application icon.icns in Resources */ = {isa = PBXBuildFile; fileRef = F59DC6E30353ED1201AEEDB1 /* Application icon.icns */; };
@@ -75,5 +76,4 @@
 		E1B11F660CF5F30F004975AA /* PSError.m in Sources */ = {isa = PBXBuildFile; fileRef = E1ECA00703C002B300F54C66 /* PSError.m */; };
 		E1B11F670CF5F30F004975AA /* NJRCenteringMovieView.m in Sources */ = {isa = PBXBuildFile; fileRef = E1D358BA03C60CE900C8A18E /* NJRCenteringMovieView.m */; };
-		E1B11F680CF5F30F004975AA /* SUSpeaker.m in Sources */ = {isa = PBXBuildFile; fileRef = E1E9A33903C8EF6700050002 /* SUSpeaker.m */; };
 		E1B11F690CF5F30F004975AA /* PSTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = E1BF6E9103C928D200050002 /* PSTimer.m */; };
 		E1B11F6A0CF5F30F004975AA /* PSWakeAlert.m in Sources */ = {isa = PBXBuildFile; fileRef = E19FD39F03C98645001509F3 /* PSWakeAlert.m */; };
@@ -108,5 +108,5 @@
 
 /* Begin PBXCopyFilesBuildPhase section */
-		E17562730CF8C55D00FC5057 /* CopyFiles */ = {
+		E17562730CF8C55D00FC5057 /* Copy Sparkle.framework */ = {
 			isa = PBXCopyFilesBuildPhase;
 			buildActionMask = 2147483647;
@@ -114,9 +114,10 @@
 			dstSubfolderSpec = 10;
 			files = (
-				E17562720CF8C53F00FC5057 /* Sparkle.framework in CopyFiles */,
-			);
+				E17562720CF8C53F00FC5057 /* Sparkle.framework in Copy Sparkle.framework */,
+			);
+			name = "Copy Sparkle.framework";
 			runOnlyForDeploymentPostprocessing = 0;
 		};
-		E17CA9A80CF7F89900018B34 /* CopyFiles */ = {
+		E17CA9A80CF7F89900018B34 /* Copy Perl Modules */ = {
 			isa = PBXCopyFilesBuildPhase;
 			buildActionMask = 2147483647;
@@ -124,6 +125,7 @@
 			dstSubfolderSpec = 7;
 			files = (
-				E17CA9AB0CF7F8CB00018B34 /* Manip.pm in CopyFiles */,
-			);
+				E17CA9AB0CF7F8CB00018B34 /* Manip.pm in Copy Perl Modules */,
+			);
+			name = "Copy Perl Modules";
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -160,4 +162,6 @@
 		E14DEC820438969400050002 /* NJRHotKeyFieldCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NJRHotKeyFieldCell.h; sourceTree = "<group>"; };
 		E14DEC830438969400050002 /* NJRHotKeyFieldCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NJRHotKeyFieldCell.m; sourceTree = "<group>"; };
+		E15044650CFCDC7E00DE47E9 /* NJRValidatingField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NJRValidatingField.h; sourceTree = "<group>"; };
+		E15044660CFCDC7E00DE47E9 /* NJRValidatingField.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = NJRValidatingField.m; sourceTree = "<group>"; };
 		E162698303B6189000000028 /* NSTableView-NJRExtensions.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = "NSTableView-NJRExtensions.m"; sourceTree = "<group>"; };
 		E162698403B6189000000028 /* NSTableView-NJRExtensions.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = "NSTableView-NJRExtensions.h"; sourceTree = "<group>"; };
@@ -221,6 +225,4 @@
 		E1DEF116040014EB00050002 /* PSTimeDateEditor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSTimeDateEditor.h; sourceTree = "<group>"; };
 		E1DEF117040014EB00050002 /* PSTimeDateEditor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSTimeDateEditor.m; sourceTree = "<group>"; };
-		E1E9A33903C8EF6700050002 /* SUSpeaker.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = SUSpeaker.m; sourceTree = "<group>"; };
-		E1E9A33A03C8EF6700050002 /* SUSpeaker.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = SUSpeaker.h; sourceTree = "<group>"; };
 		E1ECA00603C002B300F54C66 /* PSError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSError.h; sourceTree = SOURCE_ROOT; };
 		E1ECA00703C002B300F54C66 /* PSError.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSError.m; sourceTree = SOURCE_ROOT; };
@@ -395,7 +397,7 @@
 			isa = PBXGroup;
 			children = (
-				F5243DA40378681101AEEDB1 /* Popup triangle.tiff */,
 				E18830410442B5FC00050002 /* Calendar icons */,
 				E18830420442B60000050002 /* Volume icons */,
+				F5243DA40378681101AEEDB1 /* Popup triangle.tiff */,
 				29B97318FDCFA39411CA2CEA /* MainMenu.nib */,
 				F560E2EE03574F8A01A4E466 /* Alarms.nib */,
@@ -526,13 +528,4 @@
 			);
 			name = Documentation;
-			sourceTree = "<group>";
-		};
-		E1E9A33D03C8EF6800050002 /* SpeechUtilities */ = {
-			isa = PBXGroup;
-			children = (
-				E1E9A33903C8EF6700050002 /* SUSpeaker.m */,
-				E1E9A33A03C8EF6700050002 /* SUSpeaker.h */,
-			);
-			name = SpeechUtilities;
 			sourceTree = "<group>";
 		};
@@ -582,5 +575,4 @@
 			isa = PBXGroup;
 			children = (
-				E1E9A33D03C8EF6800050002 /* SpeechUtilities */,
 				F59E47A6036AA5FC016B311C /* BDAlias */,
 				E1DEF116040014EB00050002 /* PSTimeDateEditor.h */,
@@ -633,4 +625,6 @@
 				F5F240CD036A877301FE7503 /* NJRQTMediaPopUpButton.h */,
 				F5F240CC036A877301FE7503 /* NJRQTMediaPopUpButton.m */,
+				E15044650CFCDC7E00DE47E9 /* NJRValidatingField.h */,
+				E15044660CFCDC7E00DE47E9 /* NJRValidatingField.m */,
 				F5F19871036B822E01EB0372 /* NJRVoicePopUpButton.h */,
 				F5F19870036B822E01EB0372 /* NJRVoicePopUpButton.m */,
@@ -685,7 +679,6 @@
 				E1B11F7E0CF5F30F004975AA /* Frameworks */,
 				E1B11F260CF5F30F004975AA /* Resources */,
-				E17CA9A80CF7F89900018B34 /* CopyFiles */,
-				E17562730CF8C55D00FC5057 /* CopyFiles */,
-				E1B11F860CF5F30F004975AA /* ShellScript */,
+				E17CA9A80CF7F89900018B34 /* Copy Perl Modules */,
+				E17562730CF8C55D00FC5057 /* Copy Sparkle.framework */,
 			);
 			buildRules = (
@@ -749,16 +742,4 @@
 		};
 /* End PBXResourcesBuildPhase section */
-
-/* Begin PBXShellScriptBuildPhase section */
-		E1B11F860CF5F30F004975AA /* ShellScript */ = {
-			isa = PBXShellScriptBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-			shellPath = /bin/sh;
-			shellScript = "/usr/bin/agvtool mvers -terse1 >| ${SRCROOT}/VERSION";
-		};
-/* End PBXShellScriptBuildPhase section */
 
 /* Begin PBXSourcesBuildPhase section */
@@ -806,5 +787,4 @@
 				E1B11F660CF5F30F004975AA /* PSError.m in Sources */,
 				E1B11F670CF5F30F004975AA /* NJRCenteringMovieView.m in Sources */,
-				E1B11F680CF5F30F004975AA /* SUSpeaker.m in Sources */,
 				E1B11F690CF5F30F004975AA /* PSTimer.m in Sources */,
 				E1B11F6A0CF5F30F004975AA /* PSWakeAlert.m in Sources */,
@@ -830,4 +810,5 @@
 				E1B121970CF62FD1004975AA /* NSMenuItem-NJRExtensions.m in Sources */,
 				E1B1219E0CF63858004975AA /* NSImage-NJRExtensions.m in Sources */,
+				E15044670CFCDC7E00DE47E9 /* NJRValidatingField.m in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -966,5 +947,5 @@
 			buildSettings = {
 				COPY_PHASE_STRIP = NO;
-				CURRENT_PROJECT_VERSION = 21;
+				CURRENT_PROJECT_VERSION = 22;
 				FRAMEWORK_SEARCH_PATHS = (
 					"$(FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_1)",
@@ -1004,5 +985,5 @@
 			buildSettings = {
 				COPY_PHASE_STRIP = YES;
-				CURRENT_PROJECT_VERSION = 21;
+				CURRENT_PROJECT_VERSION = 22;
 				FRAMEWORK_SEARCH_PATHS = (
 					"$(FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_1)",
Index: trunk/Cocoa/Pester/Source/SUSpeaker.h
===================================================================
--- trunk/Cocoa/Pester/Source/SUSpeaker.h	(revision 363)
+++ 	(revision )
@@ -1,40 +1,0 @@
-//
-//  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: trunk/Cocoa/Pester/Source/SUSpeaker.m
===================================================================
--- trunk/Cocoa/Pester/Source/SUSpeaker.m	(revision 363)
+++ 	(revision )
@@ -1,296 +1,0 @@
-//
-//  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: trunk/Cocoa/Pester/Source/VERSION
===================================================================
--- trunk/Cocoa/Pester/Source/VERSION	(revision 363)
+++ 	(revision )
@@ -1,1 +1,0 @@
-1.1b5
