
/* Generated by Interface Builder */
// Uncopyrighted by Eric Celeste, 1991 <efc@mit.edu>

#import "ScrollTextField.h"
#import <stdlib.h>

@implementation ScrollTextField

//
// BIRTH, DEATH, & REBIRTH
//
- initFrame:(NXRect *)frameRect
 {
	NXRect rect = {0.0, 0.0, 0.0, 0.0};

	[super initFrame:frameRect];
	
	/*	Set up the text and attach it to the docView.
	*/
	[self getContentSize:&(rect.size)];
	myText = [self newText:&rect];
	[self setDocView:myText];
	[myText setSel:0 :0];	
	
	/*	This new filter is needed so that we can end editing with tabs
		but let returns go into the text. The NXEditorFilter() does not
		end on either, and the NXFieldFilter() ends on both. See
		ScrollingTextFilter() for more details. We make ourselves the
		delegate to take care of movement, see TextDidEnd:endChar: for
		more details.
	*/
    [myText setCharFilter:(NXCharFilterFunc)ScrollingTextFilter];
    [myText setDelegate:self];
	
	/*	The following two lines allow the resizing of the scrollview
		to be passed down to the docView (in this case, the text view 
		itself).
	*/
	[contentView setAutoresizeSubviews:YES];
	[contentView setAutosizing:NX_HEIGHTSIZABLE | NX_WIDTHSIZABLE];
	
	/*	Set the instance variables and other important attributes.
		These are done late in the initFrame so that the text will
		already be properly set up for those operation which effect
		the text (in myText).
	*/
	[[self setVertScrollerRequired:YES] setHorizScrollerRequired:NO];
	[self setBackgroundGray:NX_WHITE];
	[self setTextGray:NX_BLACK];
	[self setBorderType:NX_BEZEL];
	[self setSelectable:YES];
	[self setEditable:YES];
	[self setMonofont:YES];
	
	/*	Allocate memory for myString so that we can safely free
		it in the future.
	*/
	myString = (char *)malloc(1);
	
	return self;
 }

- awake
 {
 	[super awake];
	
	/*	This new filter is needed so that we can end editing with tabs
		but let returns go into the text. The NXEditorFilter() does not
		end on either, and the NXFieldFilter() ends on both. See
		ScrollingTextFilter() for more details. We make ourselves the
		delegate to take care of movement, see TextDidEnd:endChar: for
		more details.
	*/
    [myText setCharFilter:(NXCharFilterFunc)ScrollingTextFilter];
    [myText setDelegate:self];
	
	return self;
 }

- newText:(const NXRect *)frameRect
 {
	id	text;
	NXSize aSize = {1.0E38, 1.0E38};	// very big!
	
	text = [[Text alloc] 
				initFrame:frameRect
				text:NULL
				alignment:NX_LEFTALIGNED
	];
	
	[text setOpaque:YES];
	
	[[[[[text
			notifyAncestorWhenFrameChanged:YES]
			setVertResizable:YES]
			setHorizResizable:NO]
			setMonoFont:NO]
			setDelegate:self
	];
	
	[text setMinSize:&(frameRect->size)];
	[text setMaxSize:&aSize];
	[text setAutosizing:NX_HEIGHTSIZABLE | NX_WIDTHSIZABLE];
	
	[text setCharFilter:NXEditorFilter];
	return text;
 }

- (const char *)inspectorName
 {
	return "ScrollTextFieldInspector";
 }

- free
 {
 	free(myString);
 	[super free];
	return self;
 }
- read:(NXTypedStream*)stream
 {
    [super read:stream];
    NXReadTypes(stream,"ffiiii*@@@@", 
		&bgGray, &txtGray, 
		&border, &selectable, &editable, &monofont,
		&myString,
		&myText, &previousText,
		&nextText, &textDelegate
	);
    return self;
 }

- write:(NXTypedStream*)stream
 {
    [super write:stream];
    NXWriteTypes(stream,"ffiiii*@@@@", 
		&bgGray, &txtGray, 
		&border, &selectable, &editable, &monofont,
		&myString,
		&myText, &previousText,
		&nextText, &textDelegate
	);
	return self;
 }

//
// INSTANCE VARIABLES
//
- setBackgroundGray:(float)g
 {
	[myText setBackgroundGray:(bgGray = g)];
	[self display];
	return self;
 }

- (float)backgroundGray
 {
	return bgGray;
 }


- setTextGray:(float)g
 {
	[myText setTextGray:(txtGray = g)];
	[self display];
	return self;
 }

- (float)textGray
 {
	return txtGray;
 }


- setBorderType:(int)x
 {
	[super setBorderType:(border = x)];
	return self;
 }

- (int)borderType
 {
	return border;
 }

- setSelectable:(BOOL)x
 {
	[myText setSelectable:(selectable = x)];
	return self;
 }

- (BOOL)isSelectable
 {
	return selectable;
 }

- setEditable:(BOOL)x
 {
	[myText setEditable:(editable = x)];
	return self;
 }

- (BOOL)isEditable
 {
	return editable;
 }

- setMonofont:(BOOL)x
 {
	[myText setMonoFont:(monofont = x)];
	return self;
 }

- (BOOL)isMonofont
 {
	return monofont;
 }

- setTag:(int)anInt
 {
	// Makes anInt the Text object's tag
	[myText setTag:anInt];
	return self;
 }

- (int)tag
 {
	// Returns the Text object's tag
	return [myText tag];
 }

- setPreviousText:anObject;
 {
 	previousText = anObject;
	return self;
 }

- setNextText:anObject
 {
 	nextText = anObject;
	[nextText setPreviousText:self];
	return self;
 }

//
// DOCVIEW TEXT
//
- setStringValue:(const char *)string
 {
 	[myText setText:string];
	return self;
 }

- (const char *)stringValue;
 {
 	int length;
	
 	free(myString);
	myString = (char *)malloc((length = [myText textLength])+1);
	[myText getSubstring:myString start:0 length:length];
	myString[length] = '\0';
	return (const char *)myString;
 }

- selectText:sender
 {
 	[myText selectText:self];
	return self;
 }

//
// TEXT DELEGATE DUTIES
//
- textDidEnd:sender endChar:(unsigned short)whyEnd
 { 
	if (textDelegate) {
		if ([textDelegate respondsTo:@selector(textDidEnd:endChar:)]) {
			[textDelegate textDidEnd:sender endChar:whyEnd];
		}
	}	
	
	switch (whyEnd) { 
	case NX_RETURN:  
		/* this should never happen because of the filter set */
		break; 
	case NX_TAB:  
		/* make the next text object the first responder */
		if (nextText) {
			[nextText selectText:self];
		} else {
			[self selectText:self];
		}
		break; 
	case NX_BACKTAB:  
		/* make the previous text object the first responder */ 
		if (previousText) {
			[previousText selectText:self];
		} else {
			[self selectText:self];
		}
		break; 
	}
	
	return self;
 }

- (BOOL)textWillEnd:textObject
 { 
	if (textDelegate) {
		if ([textDelegate respondsTo:@selector(textWillEnd:)]) {
			return [textDelegate textWillEnd:textObject];
		}
	}	
	
	return NO;
 }

- (BOOL)textWillChange:textObject;
 { 
	if (editable) {
		if (textDelegate) {
			if ([textDelegate respondsTo:@selector(textWillChange:)]) {
				return [textDelegate textWillChange:textObject];
			}
		}
		return NO;
	}
	
	return YES;	
 }

- textDidChange:textObject;
 { 
	if (textDelegate) {
		if ([textDelegate respondsTo:@selector(textDidChange:)]) {
			[textDelegate textDidChange:textObject];
		}
	}	
	
	return self;
 }

- textDidGetKeys:textObject isEmpty:(BOOL)flag;
 { 
	if (textDelegate) {
		if ([textDelegate respondsTo:@selector(textDidGetKeys:isEmpty:)]) {
			[textDelegate textDidGetKeys:textObject isEmpty:flag];
		}
	}	
	
	return self;
 }

//
// UTILITIES
//
unsigned short ScrollingTextFilter(unsigned short theChar, int flags)
 { 
 	if (flags & NX_COMMANDMASK) {
 		return (NX_ILLEGAL);
 	}
	
	if (flags & NX_NUMERICPADMASK) {
		switch (theChar) {
			case 172: return (NX_LEFT);
			case 173: return (NX_UP);
			case 174: return (NX_RIGHT);
			case 175: return (NX_DOWN);
		}
	}
	
	switch (theChar) {
		case NX_DELETE:
				return (NX_BACKSPACE);
		case NX_CR:
				return ('\n');
		case NX_BTAB:
				return (NX_BACKTAB);
		case '\t':
				return ((flags & NX_SHIFTMASK) ? NX_BACKTAB : NX_TAB);
	}
	
	return ((theChar < ' ') ? NX_ILLEGAL : theChar);
 }

//
// TARGET-ACTION
//
/* --------------------------- comment out begins -------------
- setTarget:(id)anObject
 {
	target = anObject;
	return self;
 }

- setAction:(SEL)aSelector
 {
	action = aSelector;
	return self;
 }

- takeStringValueFrom:sender
 {
	return self;
 }

--------------------------- comment out begins -------------*/
// END

@end
