[53] | 1 | //
|
---|
| 2 | // NJRIntervalField.m
|
---|
| 3 | // Pester
|
---|
| 4 | //
|
---|
| 5 | // Created by Nicholas Riley on Wed Dec 25 2002.
|
---|
| 6 | // Copyright (c) 2002 Nicholas Riley. All rights reserved.
|
---|
| 7 | //
|
---|
| 8 |
|
---|
| 9 | #import "NJRIntervalField.h"
|
---|
| 10 |
|
---|
| 11 | // XXX much implementation borrowed from DockCam, then factored; should replace DockCam interval selector with a NJRIntervalField at some point
|
---|
| 12 |
|
---|
| 13 | @implementation NJRIntervalField
|
---|
| 14 |
|
---|
| 15 | - (NSTimeInterval)interval;
|
---|
| 16 | {
|
---|
| 17 | NSText *editor = [self currentEditor];
|
---|
| 18 | id obj = nil;
|
---|
| 19 |
|
---|
| 20 | if (editor != nil) {
|
---|
| 21 | NSString *stringValue = [editor string];
|
---|
| 22 | if (![[self formatter] getObjectValue: &obj forString: stringValue errorDescription: NULL])
|
---|
| 23 | return 0;
|
---|
| 24 | } else {
|
---|
| 25 | obj = self;
|
---|
| 26 | }
|
---|
| 27 |
|
---|
| 28 | return [obj intValue] * [[intervalUnits selectedItem] tag];
|
---|
| 29 | }
|
---|
| 30 |
|
---|
| 31 | - (BOOL)setInterval:(NSTimeInterval)interval;
|
---|
| 32 | {
|
---|
| 33 | // we assume that the tags are in ascending order in the array
|
---|
| 34 | NSEnumerator *e = [[intervalUnits itemArray] reverseObjectEnumerator];
|
---|
| 35 | NSMenuItem *i;
|
---|
| 36 | int multiplierTag;
|
---|
| 37 |
|
---|
| 38 | while ( (i = [e nextObject]) != nil) {
|
---|
| 39 | multiplierTag = [i tag];
|
---|
| 40 | if (((int)interval % multiplierTag) == 0) {
|
---|
| 41 | NSFormatter *formatter = [self formatter];
|
---|
| 42 | int intervalValue = interval / multiplierTag;
|
---|
| 43 | if (formatter != nil) {
|
---|
| 44 | id ignored;
|
---|
| 45 | if (![formatter getObjectValue: &ignored forString: [formatter stringForObjectValue: [NSNumber numberWithInt: intervalValue]] errorDescription: NULL]) return NO;
|
---|
| 46 | }
|
---|
| 47 | [self setIntValue: intervalValue];
|
---|
| 48 | [intervalUnits selectItem: i];
|
---|
| 49 | return YES;
|
---|
| 50 | }
|
---|
| 51 | }
|
---|
| 52 | return NO;
|
---|
| 53 | }
|
---|
| 54 |
|
---|
| 55 | - (BOOL)textView:(NSTextView *)textView shouldChangeTextInRange:(NSRange)range replacementString:(NSString *)string;
|
---|
| 56 | {
|
---|
| 57 | unsigned length = [string length];
|
---|
| 58 | if (length != 0) {
|
---|
| 59 | unichar c;
|
---|
| 60 | int tag = -1;
|
---|
| 61 | c = [string characterAtIndex: length - 1];
|
---|
| 62 | switch (c) {
|
---|
| 63 | case 's': case 'S': tag = 1; break;
|
---|
| 64 | case 'm': case 'M': tag = 60; break;
|
---|
| 65 | case 'h': case 'H': tag = 60 * 60; break;
|
---|
| 66 | default: break;
|
---|
| 67 | }
|
---|
| 68 | if (tag != -1) [intervalUnits selectItemAtIndex: [intervalUnits indexOfItemWithTag: tag]];
|
---|
| 69 | }
|
---|
| 70 | return [super textView: textView shouldChangeTextInRange: range replacementString: string];
|
---|
| 71 | }
|
---|
| 72 |
|
---|
| 73 | - (void)handleDidFailToFormatString:(NSString *)string errorDescription:(NSString *)error label:(NSString *)label;
|
---|
| 74 | {
|
---|
| 75 | NSString *alertMessage;
|
---|
| 76 | NSString *alternateButtonString;
|
---|
| 77 | NSDecimalNumber *proposedValue;
|
---|
| 78 | NSDictionary *contextInfo;
|
---|
| 79 |
|
---|
| 80 | NSString *alertInformation = [NSString localizedStringWithFormat:
|
---|
| 81 | NSLocalizedString(@"The %@ field must be set to a value between %@ and %@.",
|
---|
| 82 | @"Informative text for alert posed by text field when invalid value entered"),
|
---|
| 83 | label, [[self formatter] minimum], [[self formatter] maximum]];
|
---|
| 84 | NSString *defaultButtonString = NSLocalizedString(@"Edit", @"Name of Edit button");
|
---|
| 85 | NSString *otherButtonString = NSLocalizedString(@"Cancel", @"Name of Cancel button");
|
---|
| 86 |
|
---|
| 87 | if ([error isEqualToString:
|
---|
| 88 | NSLocalizedStringFromTableInBundle(@"Fell short of minimum", @"Formatter",
|
---|
| 89 | [NSBundle bundleForClass:[NSFormatter class]],
|
---|
| 90 | @"Presented when user value smaller than minimum")]) {
|
---|
| 91 | proposedValue = [[self formatter] minimum];
|
---|
| 92 | alertMessage = [NSString stringWithFormat:
|
---|
| 93 | NSLocalizedString(@"Ò%@Ó is too small for the %@ field.",
|
---|
| 94 | @"Message text for alert posed by numeric field when too-small value entered"),
|
---|
| 95 | string, label];
|
---|
| 96 | alternateButtonString = [NSString localizedStringWithFormat:
|
---|
| 97 | NSLocalizedString(@"Set to %@",
|
---|
| 98 | @"Name of alternate button for alert posed by numeric field when too-small value entered"),
|
---|
| 99 | proposedValue];
|
---|
| 100 | } else if ([error isEqualToString:
|
---|
| 101 | NSLocalizedStringFromTableInBundle(@"Maximum exceeded", @"Formatter",
|
---|
| 102 | [NSBundle bundleForClass:[NSFormatter class]],
|
---|
| 103 | @"Presented when user value larger than maximum")]) {
|
---|
| 104 | proposedValue = [[self formatter] maximum];
|
---|
| 105 | alertMessage = [NSString stringWithFormat:
|
---|
| 106 | NSLocalizedString(@"Ò%@Ó is too large for the %@ field.",
|
---|
| 107 | @"Message text for alert posed by numeric field when too-large value entered"),
|
---|
| 108 | string, label];
|
---|
| 109 | alternateButtonString = [NSString localizedStringWithFormat:
|
---|
| 110 | NSLocalizedString(@"Set to %@",
|
---|
| 111 | @"Name of alternate button for alert posed by numeric field when too-large value entered"),
|
---|
| 112 | proposedValue];
|
---|
| 113 | } else if ([error isEqualToString:
|
---|
| 114 | NSLocalizedStringFromTableInBundle(@"Invalid number", @"Formatter",
|
---|
| 115 | [NSBundle bundleForClass:[NSFormatter class]],
|
---|
| 116 | @"Presented when user typed illegal characters: no valid object")]) {
|
---|
| 117 | alertMessage = [NSString stringWithFormat:
|
---|
| 118 | NSLocalizedString(@"Ò%@Ó is not a valid entry for the %@ field.",
|
---|
| 119 | @"Message text for alert posed by text field when invalid value entered"),
|
---|
| 120 | string, label];
|
---|
| 121 | alternateButtonString = nil;
|
---|
| 122 | }
|
---|
| 123 |
|
---|
| 124 | contextInfo = [NSDictionary dictionaryWithObject: proposedValue forKey: @"proposedValue"];
|
---|
| 125 | [contextInfo retain];
|
---|
| 126 | NSBeep();
|
---|
| 127 | NSBeginAlertSheet(alertMessage, defaultButtonString, alternateButtonString, otherButtonString, [self window],
|
---|
| 128 | self, @selector(validationFailedSheetDidEnd:returnCode:contextInfo:), NULL, contextInfo,
|
---|
| 129 | alertInformation);
|
---|
| 130 | }
|
---|
| 131 |
|
---|
| 132 | - (void)_propagateValidationChange;
|
---|
| 133 | {
|
---|
| 134 | NSText *fieldEditor = [self currentEditor];
|
---|
| 135 | NSDictionary *userInfo = nil;
|
---|
| 136 | if (fieldEditor != nil) userInfo = [NSDictionary dictionaryWithObject: fieldEditor forKey: @"NSFieldEditor"];
|
---|
| 137 | [[NSNotificationCenter defaultCenter] postNotificationName: NSControlTextDidChangeNotification object: self userInfo: userInfo];
|
---|
| 138 | [self selectText: self];
|
---|
| 139 | }
|
---|
| 140 |
|
---|
| 141 | - (void)validationFailedSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo {
|
---|
| 142 | // modal delegate callback method for NSBeginAlertSheet() function; called in the above method
|
---|
| 143 | if (returnCode == NSAlertOtherReturn) { // cancel
|
---|
| 144 | [self abortEditing]; // abort edit session and reinstate original value
|
---|
| 145 | [self _propagateValidationChange];
|
---|
| 146 | } else if (returnCode == NSAlertAlternateReturn) { // set to min/max/default value
|
---|
| 147 | [self setObjectValue: [(NSDictionary *)contextInfo objectForKey: @"proposedValue"]];
|
---|
| 148 | [self validateEditing];
|
---|
| 149 | [self _propagateValidationChange];
|
---|
| 150 | }
|
---|
| 151 | [(NSDictionary *)contextInfo release];
|
---|
| 152 | }
|
---|
| 153 |
|
---|
| 154 | @end
|
---|