On Oct 24, 2013, at 9:01 AM, jonat...@mugginsoft.com wrote:

> Have I missed something or is access to a decent NSFormatter subclass to 
> handle NSTextField string length limiting troublesome?
> 
> There is some form on this:
> http://stackoverflow.com/questions/827014/how-to-limit-nstextfield-text-length-and-keep-it-always-upper-case/827598#827598
> http://www.cocoabuilder.com/archive/cocoa/184885-nsformatter-interfering-with-bindings-continuous-update.html
> 
> However the solutions provided have issues, some with bindings, and most 
> suggestions don't handle pasted text well.
> 
> I am currently using the following. Comments, suggested improvements etc 
> welcome.

I recall that this can be tricky. Below is a method that I dug up from an old 
NSFormatter subclass that handles both min/max lengths, non-stored delimiters 
and filtering to acceptable characters, which I recall is also paste-proof. I 
can send the whole class files if you care.

- (BOOL)isPartialStringValid:(NSString **)partial 
proposedSelectedRange:(NSRange *)proposedRange originalString:(NSString 
*)original originalSelectedRange:(NSRange)originalRange 
                        errorDescription:(NSString **)errorDescription
{
  // ignore empty
  if( ! [*partial length] ) return YES;
  
  // we want a mutable string
  NSMutableString *newString = [NSMutableString stringWithString:*partial];
  
  // if single deletion, adjust to ignore delimiters by changing what is 
expected to be deleted
  if( proposedRange->location == originalRange.location && originalRange.length 
== 1 && originalRange.location > 0 )
  {
        while( ! [allowedCharacters characterIsMember:[original 
characterAtIndex:proposedRange->location]] )
        {
          proposedRange->location--;
        }
        
        // re-adjust string & change orig
        [newString deleteCharactersInRange:NSMakeRange( 
proposedRange->location, originalRange.location - proposedRange->location )];
        originalRange.location = proposedRange->location;
  }
  
  NSUInteger proposedLocation = proposedRange->location;
  
  // strip down--this will include removing delimiters & updating location
  [newString setString:[self filterToAllowed:newString 
location:&proposedLocation]];
  
  // are we too long?
  if( maxLength > 0 && [newString length] > maxLength )
  {
        // delete from added
        NSUInteger delta = [newString length] - maxLength;
        proposedLocation -= delta;
        [newString deleteCharactersInRange:NSMakeRange( proposedLocation, delta 
)];
        NSBeep();
  }
  
  // determine the string being added: if deletion or filter actions make orig 
before proposed, consider empty added
  NSString *addedString = nil;
  if( originalRange.location < proposedLocation )
        addedString = [newString substringWithRange:NSMakeRange( 
originalRange.location, proposedLocation-originalRange.location )];
  
  if( [addedString length] )
  {
        addedString = [self caseFoldString:addedString precursor:[newString 
substringToIndex:originalRange.location]];
        [newString replaceCharactersInRange:NSMakeRange( 
originalRange.location, proposedLocation-originalRange.location ) 
withString:addedString];
  }
  else if( originalRange.location < [newString length] )
  {
        // if no added--i.e. deletion or all added was filtered out--only check 
case folding if not at end
        addedString = [self caseFoldString:[newString 
substringWithRange:NSMakeRange( originalRange.location, 1 )] 
precursor:[newString substringToIndex:originalRange.location]];
        [newString replaceCharactersInRange:NSMakeRange( 
originalRange.location, 1 ) withString:addedString];
  }
  
  // now format & check changed
  NSString *finalString = [self formatString:newString 
location:&proposedLocation];
  BOOL valid = [*partial isEqualToString:finalString];
  
  // update proposed location
  proposedRange->location = proposedLocation;
  
  // check completions
  if( completions )
  {
        NSUInteger i, count = [completions count];
        for( i=0; i<count; i++ )
        {
          NSString *proposed = [completions objectAtIndex:i];
          if( [proposed rangeOfString:finalString 
options:NSCaseInsensitiveSearch].location == 0 && [proposed length] >= 
[finalString length] )
          {
                *proposedRange = NSMakeRange( [finalString length], [proposed 
length] - [finalString length] );
                finalString = proposed;
                break;
          }
        }
  }
  
  // always set
  *partial = finalString;
  
  return valid;
}

HTH,

Keary Suska
Esoteritech, Inc.
"Demystifying technology for your home or business"


_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to