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 (multiplierTag <= 0) continue;
|
---|
41 | if (((int)interval % multiplierTag) == 0) {
|
---|
42 | NSFormatter *formatter = [self formatter];
|
---|
43 | int intervalValue = interval / multiplierTag;
|
---|
44 | if (formatter != nil) {
|
---|
45 | id ignored;
|
---|
46 | if (![formatter getObjectValue: &ignored forString: [formatter stringForObjectValue: [NSNumber numberWithInt: intervalValue]] errorDescription: NULL]) return NO;
|
---|
47 | }
|
---|
48 | [self setIntValue: intervalValue];
|
---|
49 | [intervalUnits selectItem: i];
|
---|
50 | return YES;
|
---|
51 | }
|
---|
52 | }
|
---|
53 | return NO;
|
---|
54 | }
|
---|
55 |
|
---|
56 | - (BOOL)textView:(NSTextView *)textView shouldChangeTextInRange:(NSRange)range replacementString:(NSString *)string;
|
---|
57 | {
|
---|
58 | unsigned length = [string length];
|
---|
59 | if (length != 0) {
|
---|
60 | unichar c;
|
---|
61 | int tag = -1;
|
---|
62 | c = [string characterAtIndex: length - 1];
|
---|
63 | switch (c) {
|
---|
64 | case 's': case 'S': tag = 1; break;
|
---|
65 | case 'm': case 'M': tag = 60; break;
|
---|
66 | case 'h': case 'H': tag = 60 * 60; break;
|
---|
67 | case 'u': case 'U': tag = -2; break;
|
---|
68 | default: break;
|
---|
69 | }
|
---|
70 | if (tag != -1) {
|
---|
71 | int itemIndex = [intervalUnits indexOfItemWithTag: tag];
|
---|
72 | if (itemIndex != -1) {
|
---|
73 | [intervalUnits selectItemAtIndex: itemIndex];
|
---|
74 | [[intervalUnits menu] performActionForItemAtIndex: itemIndex];
|
---|
75 | }
|
---|
76 | if (tag < 0) return NO; // don't send update
|
---|
77 | }
|
---|
78 | }
|
---|
79 | return [super textView: textView shouldChangeTextInRange: range replacementString: string];
|
---|
80 | }
|
---|
81 |
|
---|
82 | - (void)handleDidFailToFormatString:(NSString *)string errorDescription:(NSString *)error label:(NSString *)label;
|
---|
83 | {
|
---|
84 | NSString *alertMessage = nil;
|
---|
85 | NSString *alternateButtonString = nil;
|
---|
86 | NSDecimalNumber *proposedValue = nil;
|
---|
87 | NSDictionary *contextInfo;
|
---|
88 |
|
---|
89 | NSString *alertInformation = [NSString localizedStringWithFormat:
|
---|
90 | NSLocalizedString(@"The %@ field must be set to a value between %@ and %@.",
|
---|
91 | @"Informative text for alert posed by text field when invalid value entered"),
|
---|
92 | label, [[self formatter] minimum], [[self formatter] maximum]];
|
---|
93 | NSString *defaultButtonString = NSLocalizedString(@"Edit", @"Name of Edit button");
|
---|
94 | NSString *otherButtonString = NSLocalizedString(@"Cancel", @"Name of Cancel button");
|
---|
95 |
|
---|
96 | if ([error isEqualToString:
|
---|
97 | NSLocalizedStringFromTableInBundle(@"Fell short of minimum", @"Formatter",
|
---|
98 | [NSBundle bundleForClass:[NSFormatter class]],
|
---|
99 | @"Presented when user value smaller than minimum")]) {
|
---|
100 | proposedValue = [[self formatter] minimum];
|
---|
101 | alertMessage = [NSString stringWithFormat:
|
---|
102 | NSLocalizedString(@"%@ is too small for the %@ field.",
|
---|
103 | @"Message text for alert posed by numeric field when too-small value entered"),
|
---|
104 | string, label];
|
---|
105 | alternateButtonString = [NSString localizedStringWithFormat:
|
---|
106 | NSLocalizedString(@"Set to %@",
|
---|
107 | @"Name of alternate button for alert posed by numeric field when too-small value entered"),
|
---|
108 | proposedValue];
|
---|
109 | } else if ([error isEqualToString:
|
---|
110 | NSLocalizedStringFromTableInBundle(@"Maximum exceeded", @"Formatter",
|
---|
111 | [NSBundle bundleForClass:[NSFormatter class]],
|
---|
112 | @"Presented when user value larger than maximum")]) {
|
---|
113 | proposedValue = [[self formatter] maximum];
|
---|
114 | alertMessage = [NSString stringWithFormat:
|
---|
115 | NSLocalizedString(@"%@ is too large for the %@ field.",
|
---|
116 | @"Message text for alert posed by numeric field when too-large value entered"),
|
---|
117 | string, label];
|
---|
118 | alternateButtonString = [NSString localizedStringWithFormat:
|
---|
119 | NSLocalizedString(@"Set to %@",
|
---|
120 | @"Name of alternate button for alert posed by numeric field when too-large value entered"),
|
---|
121 | proposedValue];
|
---|
122 | } else if ([error isEqualToString:
|
---|
123 | NSLocalizedStringFromTableInBundle(@"Invalid number", @"Formatter",
|
---|
124 | [NSBundle bundleForClass:[NSFormatter class]],
|
---|
125 | @"Presented when user typed illegal characters: no valid object")]) {
|
---|
126 | alertMessage = [NSString stringWithFormat:
|
---|
127 | NSLocalizedString(@"%@ is not a valid entry for the %@ field.",
|
---|
128 | @"Message text for alert posed by text field when invalid value entered"),
|
---|
129 | string, label];
|
---|
130 | alternateButtonString = nil;
|
---|
131 | }
|
---|
132 |
|
---|
133 | contextInfo = [NSDictionary dictionaryWithObject: proposedValue forKey: @"proposedValue"];
|
---|
134 | [contextInfo retain];
|
---|
135 | NSBeep();
|
---|
136 | NSBeginAlertSheet(alertMessage, defaultButtonString, alternateButtonString, otherButtonString, [self window],
|
---|
137 | self, @selector(validationFailedSheetDidEnd:returnCode:contextInfo:), NULL, contextInfo,
|
---|
138 | alertInformation);
|
---|
139 | }
|
---|
140 |
|
---|
141 | - (void)_propagateValidationChange;
|
---|
142 | {
|
---|
143 | NSText *fieldEditor = [self currentEditor];
|
---|
144 | NSDictionary *userInfo = nil;
|
---|
145 | if (fieldEditor != nil) userInfo = [NSDictionary dictionaryWithObject: fieldEditor forKey: @"NSFieldEditor"];
|
---|
146 | [[NSNotificationCenter defaultCenter] postNotificationName: NSControlTextDidChangeNotification object: self userInfo: userInfo];
|
---|
147 | [self selectText: self];
|
---|
148 | }
|
---|
149 |
|
---|
150 | - (void)validationFailedSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo {
|
---|
151 | // modal delegate callback method for NSBeginAlertSheet() function; called in the above method
|
---|
152 | if (returnCode == NSAlertOtherReturn) { // cancel
|
---|
153 | [self abortEditing]; // abort edit session and reinstate original value
|
---|
154 | [self _propagateValidationChange];
|
---|
155 | } else if (returnCode == NSAlertAlternateReturn) { // set to min/max/default value
|
---|
156 | [self setObjectValue: [(NSDictionary *)contextInfo objectForKey: @"proposedValue"]];
|
---|
157 | [self validateEditing];
|
---|
158 | [self _propagateValidationChange];
|
---|
159 | }
|
---|
160 | [(NSDictionary *)contextInfo release];
|
---|
161 | }
|
---|
162 |
|
---|
163 | @end
|
---|