This is TurboTFCell.m in view mode; [Download] [Up]
/* TurboTFCell.m * * TurboTFCell is a subclass of TextFieldCell which supports * a) length-watching on a string -- maxLength [length of 0 * indicates that length-watching is not active] * b) auto-jumping to nextText if the maximum is reached * c) acceptsReturn will cause the Return character to be * interpreted literally rather than as a signal to end editing * * * You may freely copy, distribute, and reuse the code in this example. * NeXT disclaims any warranty of any kind, expressed or implied, as to its * fitness for any particular use. * * Written by: Sharon Zakhour * Created: Oct/91 */ #import "TurboTFCell.h" @implementation TurboTFCell /* In order for the length watching (and auto-jumping since that is * determined by length) to work properly, the character and text * filters need to know the identity of the cell they are working with. */ static id theCell = NULL; - init { return [self initTextCell:""]; } - initTextCell: (const char*) aString { [super initTextCell:aString]; // Initialize these to the default TextField behavior originalText = NULL; customTextFilter = (NXTextFilterFunc)lengthFilter; [self setAutoJump: NO forLength: 0]; [self setAcceptsReturn: NO]; return self; } - free { if (originalText) free(originalText); originalText = NULL; return[super free]; } /* The primary reason I've overridden select: and edit: (below) is to set * the local static variable "theCell" to self. The filter functions must know * about the current cell in order to query it for information on length, * autojumping, etc. The only way that a cell becomes active are * through one of these methods. */ - select:(const NXRect *)aRect inView:controlView editor:textObj delegate:anObject start:(int)selStart length:(int)selLength { /* do what the superclass would do */ [super select:aRect inView:controlView editor:textObj delegate:anObject start:selStart length:selLength]; theCell = self; if (maxLength && originalText) strcpy(originalText, [self stringValue]); [textObj setTextFilter:(NXTextFilterFunc)customTextFilter]; [textObj setCharFilter:autoJumpCharFilter]; return self; } /* The primary reason I've overridden edit: and select: (above) is to set * the local static variable "theCell" to self. The filter functions must know * about the current cell in order to query it for information on length, * autojumping, etc. The only way that a cell becomes active are * through one of these methods. */ - edit:(const NXRect *)aRect inView:controlView editor:textObj delegate:anObject event:(NXEvent *)theEvent { /* do what the superclass would do */ [super edit:aRect inView:controlView editor:textObj delegate:anObject event:theEvent]; theCell = self; if (maxLength && originalText) strcpy(originalText, [self stringValue]); [textObj setTextFilter:(NXTextFilterFunc)customTextFilter]; [textObj setCharFilter:autoJumpCharFilter]; return self; } - setMaxLength: (int) length { if (originalText) { free(originalText); originalText = NULL; } maxLength = length; if (length) originalText = (char *)malloc(maxLength); return self; } - (int) maxLength; { return maxLength; } - setAutoJump: (BOOL) flag forLength: (int)length { autoJump = flag; [self setMaxLength: length]; return self; } - (BOOL) autoJump { return autoJump; } - setAcceptsReturn: (BOOL) flag { acceptsReturn = flag; return self; } - (BOOL) acceptsReturn { return acceptsReturn; } - setOriginalText: (char *)aString { if (originalText) strcpy(originalText, aString); return self; } - (char *)originalText { return originalText; } - setCustomFilter: (NXTextFilterFunc) aFilter; { customTextFilter = (NXTextFilterFunc)aFilter; return self; } - (NXTextFilterFunc) customTextFilter { return (NXTextFilterFunc) customTextFilter; } /* Run the given string through the Cell's text filter to * see if it passes muster. */ - (BOOL) checkString: (char *)aString { int i, len; char *cp; id textObj; void *fooPtr; if (customTextFilter == (NXTextFilterFunc)nil) return YES; textObj = [[[self controlView] window] getFieldEditor :YES for: self]; for (i = 0, cp = aString; i < strlen(aString); i++, cp++) { len = 1; // fooPtr will eliminate a compiler warning about "unused value" fooPtr = (void*)(*(NXTextFilterFunc)customTextFilter)((id)textObj, (unsigned char *)cp, (int *)&len, (int)i); if (len == 0) return NO; } return YES; } // This is the default text filter. If another has been installed via the setCustomFilter: // method, then this one will not get called. char *lengthFilter(id textObj, char *inputText, int *inputLength, int position) { int maxLength; BOOL autoJump; maxLength = [theCell maxLength]; autoJump = [theCell autoJump]; if (maxLength && !autoJump && [textObj textLength] > maxLength) { *inputLength = 0; return ""; } return inputText; } // This could easily be extended to include letters with diacritics. // See Chapter 6 Summary for KeyInfo for a full listing of keyboard // event information. #define IS_VALID_ASCII(ch) ((ch) >= ' ' && (ch) <= '~') unsigned short autoJumpCharFilter(unsigned short charCode, int flags, unsigned short charSet) { NXSelPt start, end; NXEvent *event, fakeEvent; id textObj; int curLength, maxLength; BOOL autoJump, acceptsReturn; maxLength = [theCell maxLength]; autoJump = [theCell autoJump]; acceptsReturn = [theCell acceptsReturn]; event = [NXApp currentEvent]; textObj = [[[theCell controlView] window] getFieldEditor :YES for: theCell]; curLength = [textObj textLength] +1; /* Anything that's highlighted is going to get clobbered, so let's adjust */ [textObj getSel: &start : &end]; if (start.cp != end.cp) curLength -= end.cp - start.cp; // Currently acceptsReturn doesn't work very well with maxLength because // it counts newlines which you probably don't want. If you want something // more sophisticated then you'll have to parse the runs! if (acceptsReturn && charCode == NX_CR) return NXEditorFilter(charCode, flags, charSet); // Watch out for interpreted rather than real keystrokes -- let them // through even though we have reached our maximum length. if (autoJump && maxLength && curLength > maxLength && IS_VALID_ASCII(charCode)) { fakeEvent = *event; fakeEvent.time++; DPSPostEvent(&fakeEvent, YES); return NX_TAB; } return NXFieldFilter(charCode, flags, charSet); } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.