0

I'm making a NSTextField that has the following characteristics:
1) allows integer values only (0-9)
2) is 1 or 2 digits long
3) min of 1, max of 99
4) if 0 is entered, the value should change back to 1
5) if delete is pressed and the cell is completely emptied, the value should change to 1 and be automatically selected (hi-lighted) so that the user can simply type a new value

I'm able to get this behavior by creating a custom formatter and a delegate, but I want to implement this solely in the custom formatter (to keep things "simple" I suppose).

Here's the code I have:

In the delegate file:

- (void)controlTextDidChange:(NSNotification *)aNotification
{
    if ([[txtfldSaveDuration stringValue] length]==0) {
        [txtfldSaveDuration setStringValue:@"1"];
    }
    if ([[txtfldSaveDuration stringValue] isEqualToString:@"0"]) {
        [txtfldSaveDuration setStringValue:@"1"];
    }
}

in the custom formatter file:

@implementation OnlyIntegerValueFormatter

- (BOOL)isPartialStringValid:(NSString*)partialString newEditingString:    (NSString**)newString errorDescription:(NSString**)error
{
    // necessary otherwise can't delete (to select) the first character
    if([partialString length] == 0) {
        return YES;
    }  
    // two integer max length (99)
    if([partialString length] > 2) {
        return NO;
    }
    // integers only
    NSScanner* scanner = [NSScanner scannerWithString:partialString];
    if(!([scanner scanInt:0] && [scanner isAtEnd])) {
        NSBeep();
        return NO;
    }    
    return YES;
}
@end

How can I simplify this?

tosa
  • 411
  • 1
  • 3
  • 23

1 Answers1

2

If you implement -isPartialStringValid:proposedSelectedRange:originalString:originalSelectedRange:errorDescription: instead, you get much more control, including over the resultant selected range.

Probably something like:

- (BOOL)isPartialStringValid:(NSString **)partialStringPtr
       proposedSelectedRange:(NSRangePointer)proposedSelRangePtr
              originalString:(NSString *)origString
       originalSelectedRange:(NSRange)origSelRange
            errorDescription:(NSString **)error
{
    if ([*partialStringPtr length] == 0)
    {
        *partialStringPtr = @"1";
        *proposedSelRangePtr = NSMakeRange(0, [*partialStringPtr length]);
        return NO;
    }

    // two integer max length (99)
    if ([*partialStringPtr length] > 2)
    {
        NSRange changed = NSMakeRange(origSelRange.location, [*partialStringPtr length] - (origString.length - origSelRange.length));
        NSRange excess;
        excess.length = [*partialStringPtr length] - 2;
        excess.location = changed.location + (changed.length - excess.length);
        *partialStringPtr = [*partialStringPtr stringByReplacingCharactersInRange:excess withString:@""];
        *proposedSelRangePtr = NSMakeRange(excess.location, 0);
        return NO;
    }
    // integers only
    NSScanner* scanner = [NSScanner scannerWithString:*partialStringPtr];
    scanner.charactersToBeSkipped = nil;
    if(!([scanner scanInt:0] && [scanner isAtEnd])) {
        *partialStringPtr = origString;
        *proposedSelRangePtr = origSelRange;
        NSBeep();
        return NO;
    }    
    return YES;
}
Ken Thomases
  • 88,520
  • 7
  • 116
  • 154
  • Thanks for this suggestion. However this allows letters to be entered (although they're selected, but the cursor key can deselect them) and 0 is a valid entry. I'll accept your answer since it probably just needs a few tweaks. – tosa Apr 10 '15 at 00:00
  • Yeh, it was just a couple of minutes to fix those two items. Thanks again! – tosa Apr 10 '15 at 00:08