// Scrolling text window for use with TclObj.
// Copyright 1991 Manticore, Inc.  All Rights Reserved.
//   
// This code is adapted from:
// 
// TextView.m, implementation of scrolling text stuff for TextLab.
// Copyright 1989 NeXT, Inc.  All Rights Reserved.
// Author: Bruce Blumberg
// 
// You may freely copy, distribute and reuse the code in this example.
// Manticore and NeXT disclaim any warranty of any kind, expressed or
// implied, as to its fitness for any particular use.

#import <appkit/appkit.h>
#import "TclTV.h"
#import "TclObj.h"

// String filter for trapping user input of newline. All characters
// but newline are passed through to the text object. Newline causes
// variables in 'self' to be updated which causes further processing
// at a more felicitous time. Frankly I would have done the tcl command
// invocation from here except it seems that the text object is not in
// the right state to do vary much when this routine is called.

static char *
myTextFilter(id self, unsigned char *insertText,
	int *insertLength, int position)
{
	NXSelPt st, en;
	
	if (*insertLength == 1 && *insertText == '\n') {
		[self getSel:&st:&en];
		[self setSel:st.cp:st.cp];
		*insertLength = 0;  /* don't insert return characters */
		[[self delegate] cmdLoc:&st:&en];  /* signal delegate */
	}
	return ((char *)insertText);
}

@implementation TclTV:ScrollView

- initFrame:(const NXRect *)frameRect
{
    NXRect rect = {0.0, 0.0, 0.0, 0.0};
	
    /* initialize view */
    [super initFrame:frameRect];
    
    /* specify scrollbars */
    [[self setVertScrollerRequired:YES] setHorizScrollerRequired:NO];

    [self getContentSize:&(rect.size)];
    theText = [self newText:&rect];
    [self setDocView:theText];
    [theText setSel:0 :0];
    
    // 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];

    // Create enclosing window and bring it upfront
    [self makeEnclosingWindow:frameRect];
    [[[window setTitle:"Tcl commands"] display] makeKeyAndOrderFront:self];
	
	// create a tcl command buffer
	cmdBuf = Tcl_CreateCmdBuf();
    
    return self;
}

- makeEnclosingWindow:(const NXRect *)rect
{
    id	  textWindow;
    
    textWindow = [[Window alloc] initContent:rect
				 style:NX_TITLEDSTYLE
				 backing:NX_BUFFERED
				 buttonMask:NX_ALLBUTTONS
				 defer:NO];
    [textWindow setContentView:self];
    [textWindow setBackgroundGray:NX_WHITE];
    [textWindow setFreeWhenClosed:YES];
    
    return self;
}
    
- newText:(const NXRect *)frameRect
{
    id	text;
    NXSize aSize = {1.0E38, 1.0E38};
    
    text = [[Text alloc] initFrame:frameRect
    			 text:NULL
			 alignment:NX_LEFTALIGNED];
    [text setOpaque:YES];
    [[[[[text notifyAncestorWhenFrameChanged:YES]
				setVertResizable:YES]
				    setHorizResizable:NO]
					setMonoFont:YES]
					    setDelegate:self];
    
    [text setMinSize:&(frameRect->size)];
    [text setMaxSize:&aSize];
    [text setAutosizing:NX_HEIGHTSIZABLE | NX_WIDTHSIZABLE];
    
    [text setCharFilter:NXEditorFilter];
    [text setTextFilter:myTextFilter];
    [text setFont:[Font newFont:"Ohlfs" size:11.0]];
	return text;
}

- setCmdHandler:sender
{

	cmdHandler = sender;
	return self;
}

- cmdLoc:(NXSelPt *)start :(NXSelPt *)end
{

	doCmd = TRUE;
	cmdStart = *start;
	cmdEnd = *end;
	return self;
}

- addText:(const char *)text
{

	[theText replaceSel:text];
	return self;
}

- textDidGetKeys:sender isEmpty:(BOOL)flag
{
	static char *buf = NULL;
	static buflen = 0;
	int textlen;
	NXSelPt st, en;
	int cmdlen;
	int cmdstart;
	int lineno;
	int nextlinepos;
	char *tc;
	
	if (doCmd) {
	
		/* get the start and length of the command to send */
		textlen = [theText textLength];
		if (cmdStart.cp == cmdEnd.cp) {
			lineno = [theText lineFromPosition:cmdEnd.c1st];
			nextlinepos = [theText positionFromLine:lineno + 1];
			cmdstart = cmdStart.c1st;
			if (nextlinepos < 0) {
				cmdlen = textlen - cmdstart;
			} else {
				cmdlen = nextlinepos - cmdstart;
			}
		} else {
			cmdstart = cmdStart.cp;
			cmdlen = cmdEnd.cp - cmdStart.cp;
		}
		
		/* make sure that 'buf' is long enough and copy cmd to buf */
		if ((cmdlen + 1) > buflen) {
			if (buflen == 0) {
				buf = malloc(cmdlen + 128);
				buflen = cmdlen + 128;
			} else {
				buf = realloc(buf, cmdlen + 1);
				buflen = cmdlen + 1;
			}
		}
		[theText getSubstring:buf start:cmdstart length:cmdlen];
		buf[cmdlen++] = '\n';
		buf[cmdlen] = '\0';
		
		/* move the sel to end of text and make sure its on new line */
		[theText setSel:textlen:textlen];
		[theText getSel:&st:&en];
		if (en.cp != en.c1st) {
			[theText replaceSel:"\n"];
		}
		tc = Tcl_AssembleCmd(cmdBuf, buf);
		if (tc) {
			[cmdHandler doCmd:tc:self];
		}
		doCmd = FALSE;
	}
	return self;
}

- cursorVisible
{

	[theText scrollSelToVisible];
	return self;
}

- free
{
  
  	[cmdHandler viewLeaving:self];
    Tcl_DeleteCmdBuf(cmdBuf);  
    return [super free];
}

@end
