#import "GUI.h"
#import "GUI_view.h"
#import <appkit/appkit.h>
#import <tcl.h>

// GUI.m
// Copyright 1995 by Florian Marquardt (a0047@freenet.uni-bayreuth.de)
// You may freely copy, distribute and reuse this code
// No warranties, of course.
// For the tcl7.3 - license, start PencilTWO and select "Copyright" in the Help Panel.
// When you implement tcl interfaces to other classes or other interesting additions,
// please mail me the code, so I can add it to the next release of Pencil!

// This file contains all Pencil-independent TCL commands that deal
// with the interface to (some of) NEXTSTEPs GUI objects
// Object id's are stored as ClientData in tcl-command -structures.
// The tcl commands will be given names like ".b4" (b for button), ".v8" etc.
// see GUI_newobject(), GUI_findobject(), GUI_deleteobject()

// the command names are kept in a list (search for COMMANDLIST)
// the "new" command creates new objects (e. g. "new window ...")

// commands are of the form:
// int
// GUI_commandname(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
// {
//	 if(argc>= necessary # of arguments, including cmd) {
//		if([CD isKindOf:[Necessary class]]) {
//			... do something ...
//			return TCL_OK;
//			}
//	}
//	ERR("Use: <class> cmd <arg1> <arg2> ...");
// }

// You should be able to compile this file independent of Pencil, e.g. into a bundle:
// load the bundle and call [[myBundle principalClass] initTCLCommands:(Tcl_Interp *)myTCLinterp: (id)self] (i.e. you must have created "myTCLinterp" before - with Tcl_CreateInterpreter())

Tcl_HashTable GUI_cmds; // cmd names

Tcl_HashTable GUI_new_cmds; // cmds for 'new' method

Tcl_HashTable GUI_actionTable; // table of tcl progs that are invoked when a button is pressed etc.

Tcl_HashTable GUI_idToName; // table id --> name as tcl cmd (for views)
 
struct GUI_cmd { char *name; int (*func)(); };
struct GUI_cmdo { char *name; Tcl_CmdProc *func; };

typedef int (myproc) _ANSI_ARGS_((Tcl_Interp *interp, int argc, char *argv[]));

#define ERR(a) { Tcl_SetResult(interp, a, TCL_STATIC); return TCL_ERROR; }
#define EQ(a,b) if(!strcmp(a,b))
#define SETRES(a) strcpy(interp->result, a)
#define ISKINDOF(a,b) if([(id)(a) isKindOf:(b)])
#define CD ((id)cd)

char GUI_string[200]; // general purpose string
NXRect *GUI_therect; // pointer to rectangle passed to drawSelf
NXEvent GUI_lastmousedown; // needed for dragging

// "general" Cmd, called when any object is invoked as a command
// looks at first argument to determine method to be called
int
GUI_generalCmd(ClientData cd, Tcl_Interp *interp, int argc, char * argv[])
{
	Tcl_HashEntry *entry;
	
	if(argc>=2)
	{
		if(entry=Tcl_FindHashEntry( &GUI_cmds, argv[1] ))
		{
			return ( (Tcl_CmdProc *) (Tcl_GetHashValue(entry))) ( cd, interp, argc, argv );
		}
	}
	ERR("GUI object: unknown method");
}

// create Hash Table for looking up "new"-cmds
void
GUI_createTable( Tcl_HashTable *tbl, struct GUI_cmd *list)
{
	int pos;
	
	Tcl_InitHashTable(tbl, TCL_STRING_KEYS);
	while(list->name) {
		Tcl_SetHashValue( Tcl_CreateHashEntry( tbl, list->name, &pos),
			list->func );
		++list;
	}
}

// same for methods (cmds acting on object)
void
GUI_createGenTable( Tcl_HashTable *tbl, struct GUI_cmdo *list)
{
	int pos;
	
	Tcl_InitHashTable(tbl, TCL_STRING_KEYS);
	while(list->name) {
		Tcl_SetHashValue( Tcl_CreateHashEntry( tbl, list->name, &pos),
			list->func );
		++list;
	}
}

// add prefix to command name if tcl variable _CmdPrefix is set
char GUI_cmdname[50];
char *GUI_cmd_name(Tcl_Interp *interp, char *name)
{
	char *s;
	
	s=Tcl_GetVar(interp, "_CmdPrefix", 0);
	if(s) {
		strcpy(GUI_cmdname, s);
		strcat(GUI_cmdname, name);
		return(GUI_cmdname);
	} else {
		return(name);
	}
}

// create a new tcl command (name is of form ".t23", ".B12" etc.) which has
// the given id attached to it as ClientData
char *GUI_newobject(Tcl_Interp *ip, char l, id theid)
{
	static int num=0;
	static char key[20];

	sprintf(key, ".%c%d", l, num++);
	Tcl_CreateCommand( ip, key, (Tcl_CmdProc *)GUI_generalCmd, (ClientData) theid, NULL);
	return(key);
}

// find object-id for given key (command-name, e. g. ".w4")
id GUI_findobject( Tcl_Interp *ip, char *key)
{
	static Tcl_CmdInfo info;
	
	if(Tcl_GetCommandInfo(ip, key, &info)) {
		return (id)info.clientData;
	} else
		return nil;
}

void GUI_removeAction(id what)
{
	Tcl_HashEntry *entry;
	
	if(entry=Tcl_FindHashEntry( &GUI_actionTable, (char *)what)) {
		free((char *)Tcl_GetHashValue(entry));
		Tcl_DeleteHashEntry( entry);
		}
}

// remove tcl command for an object
void GUI_deleteobject( Tcl_Interp *ip, char *name)
{	
	if(name[0]!=0 && (name[1]=='w' || name[1]=='v')) {		// a window / view, remove everything inside...
		Tcl_VarEval( ip, "_close ", name, NULL);
	}
	GUI_removeAction(GUI_findobject( ip, name));
	Tcl_DeleteCommand( ip, name);
}

// add tcl-id (".b7") to list of objects contained in a window (".c.w2")
void GUI_addToView(Tcl_Interp *ip, char *what, id what_id, char *to)
{
	id view;
	
	if(!to) to=Tcl_GetVar( ip, ".curwin", TCL_GLOBAL_ONLY);
	if(view=GUI_findobject( ip, to)) {
		sprintf(GUI_string, ".c%s", to);
		Tcl_SetVar2( ip, GUI_string, what, "", TCL_GLOBAL_ONLY);
		if([view isKindOf:[Window class]]) {
			[[view contentView] addSubview:what_id];
		} else
		if([view isKindOf: [View class]]) {
			[view addSubview: what_id];
		}
	}
}

// add new action to action table, key is id of object that sends the action
void GUI_insertAction(id forid, char *what)
{
	Tcl_HashEntry *entry;
	char *whatnew;
	int pos;
	
	entry=Tcl_CreateHashEntry(&GUI_actionTable, (char *)forid, &pos);
	if(entry) {
		whatnew=(char *)malloc(sizeof(char)*(strlen(what)+1));
		strcpy(whatnew, what);
		Tcl_SetHashValue( entry, whatnew);
	}
}

char *GUI_findAction(id sender)
{
	Tcl_HashEntry *entry;
	
	if(entry=Tcl_FindHashEntry( &GUI_actionTable, (char *)sender)) {
		return (char *)Tcl_GetHashValue(entry);
	} else
		return NULL;
}

// general "new" cmd:
int
GUI_newCmd(ClientData cd, Tcl_Interp *interp, int argc, char * argv[])
{
	Tcl_HashEntry *entry;
	
	if(argc>=2)
	{
		if(entry=Tcl_FindHashEntry( &GUI_new_cmds, argv[1] ))
		{
			return ( (myproc *) (Tcl_GetHashValue(entry))) ( interp, argc, argv );
		}
	}
	ERR("Usage: new window| button| textfield| image| view| scrollview| slider| browser");
}

// methods:
int
GUI_free(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
{
	GUI_deleteobject(interp, argv[0]);
	ISKINDOF(cd, [Window class]) { [CD free]; } else {
		if([CD isMemberOf:[View class]]) [CD removeFromSuperview];
		[CD free]; }
	return TCL_OK;
}

int
GUI_remove(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
{
	GUI_deleteobject(interp, argv[0]);
	return TCL_OK;
}

int
GUI_select(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
{
	id sel;
	
	if(argc>=3 && ([CD isKindOf: [NXBrowser class]]  || [CD isKindOf: [Matrix class]])) {
	if([CD isKindOf: [NXBrowser class]]) sel=[CD matrixInColumn:0]; else sel=CD;
	[sel selectCellAt: atoi(argv[2]): 0];
	return TCL_OK;
	}
	ERR("? <browser> select <#>");
}

int
GUI_setValue(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
{
	if(argc>=3) {
		if([CD isKindOf:[Control class]]) {
			if([CD isMemberOf:[Button class]]) {
				int thestate;
				Tcl_ExprBoolean( interp, argv[2], &thestate);
				[CD setIntValue: thestate];
			}
		else
		if([CD isKindOf: [NXBrowser class]] || [CD isKindOf: [Matrix class]]) {
			id sel;
			int rows, cols;
			int i;
			int flag;
			int count;
			char **argvp;
			
			Tcl_SplitList( interp, argv[2], &count, &argvp);
			Tcl_SetResult(interp, "", TCL_VOLATILE);
			if([CD isKindOf: [NXBrowser class]]) sel=[CD matrixInColumn:0]; else sel=CD;
			[sel getNumRows: &rows numCols:&cols];
			if(rows>count) rows=count;
			[[CD window] disableDisplay];
			[sel selectCellAt:-1:-1];
			for(i=0;i<rows;i++) {
				Tcl_ExprBoolean( interp, argvp[i], &flag);
				if(flag) { [[[sel cellAt:i:0]   setParameter:NX_CELLHIGHLIGHTED to:1] setState:1]; }
			}
			free(argvp);
			[[CD window] reenableDisplay];
			[CD display];
			return TCL_OK;
		} else
				[CD setStringValue: argv[2]];
		return TCL_OK;
		}
	} 
	ERR("Use: <> setValue <string>");
}

int
GUI_setMultipleSelection(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
{
	if(argc>=3) {
		if([CD isKindOf: [NXBrowser class]]) {
			int flag;
			
			Tcl_ExprBoolean( interp, argv[2], &flag);
			[CD setMultipleSelectionEnabled: flag];
			return TCL_OK;
		}
	}
	ERR("Use: <browser> setMultipleSelection <flag>");
}

int
GUI_setEmptySelection(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
{
	if(argc>=3) {
		if([CD isKindOf: [NXBrowser class]]) {
			int flag;
			
			Tcl_ExprBoolean( interp, argv[2], &flag);
			[CD setEmptySelectionEnabled: flag];
			return TCL_OK;
		}
	}
	ERR("Use: <browser> setEmptySelection <flag>");
}

int
GUI_startDragging(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
{
	const char *const mytypes[1]= { NXAsciiPboardType  };
	id img;
	
	if(argc>=6) {
		if([CD isKindOf: [GUI_view class]] && (img=GUI_findobject( interp, argv[3]))) {
				Pasteboard *pboard = [Pasteboard newName:NXDragPboard];
				NXPoint mypos, none={ 0,0 };
				
				mypos.x=atof(argv[4]); mypos.y=atof(argv[5]);
				
				/* Declare the type of data and place it on the pasteboard. */
				[pboard declareTypes: mytypes num:1 owner: NULL];
				[pboard writeType: NXAsciiPboardType data: argv[2] length: strlen(argv[2])+1];

				/* Now invoke dragImage:.*/
				[CD dragImage: img at: &mypos offset: &none event: &GUI_lastmousedown 
       pasteboard:pboard source: CD slideBack: YES];
       				// Finished!
			return TCL_OK;
		}
	}
	ERR("Use: <view> startDragging <contents> <image> <x> <y>");
}

int
GUI_setDragProc(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
{
	if(argc>=3 && [CD isKindOf: [GUI_view class]]) {
		if(strcmp(argv[2], "")) {
			[CD setDragging: 1];
			sprintf( GUI_string, "%s.dr", argv[0]);
			Tcl_SetVar( interp, GUI_string, argv[2], TCL_GLOBAL_ONLY);
		} else [CD setDragging: 0];
		return TCL_OK;
	}
	ERR("Use: <view> setDragProc <proc>");
}

int
GUI_setDragSource(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
{
	if(argc>=3 && [CD isKindOf: [GUI_view class]]) { [CD setDragSource: atoi(argv[2])]; 
	return TCL_OK; }
	ERR("?: <view> setDragSource <flag>");
}

int
GUI_hideDisplay(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
{
	if(argc>=3 && [CD isKindOf: [Window class]]) {
	[CD disableDisplay];
	Tcl_Eval( interp, argv[2]);
	[CD reenableDisplay];
	return TCL_OK;
	}
	ERR("? <win> hideDisplay <cmds>");
}

int
GUI_setHorizScrolling(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
{
	if(argc>=3) {
		if([CD isKindOf: [ScrollView class]]) {
			int flag;
			
			Tcl_ExprBoolean( interp, argv[2], &flag);
			[CD setHorizScrollerRequired: flag];
			return TCL_OK;
		}
	}
	ERR("Use: <scrollview> setHorizScrolling <flag>");
}

int
GUI_setVertScrolling(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
{
	if(argc>=3) {
		if([CD isKindOf: [ScrollView class]]) {
			int flag;
			
			Tcl_ExprBoolean( interp, argv[2], &flag);
			[CD setVertScrollerRequired: flag];
			return TCL_OK;
		}
	}
	ERR("Use: <scrollview> setVertScrolling <flag>");
}

int
GUI_orderFront(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
{
	if([CD isKindOf: [Window class]]) {
		[CD makeKeyAndOrderFront: nil];
		return TCL_OK;
	}
	ERR("orderFront: window?");
}

int
GUI_close(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
{
	if([CD isKindOf: [Window class]]) {
		[CD orderOut: nil];
		return TCL_OK;
	}
	ERR("? <win> close");
}

int
GUI_composite(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
{
	if(argc>=5 && [CD isKindOf: [NXImage class]]) {
		NXPoint pt;
		
		pt.x=atof(argv[2]); pt.y=atof(argv[3]);
		if(argv[4][0]=='s') {
		[CD composite: NX_SOVER toPoint: &pt];
		} else {
		if(argv[4][0]=='c') {
		[CD composite: NX_COPY toPoint: &pt];
		} else {
		[[CD bestRepresentation] drawAt: &pt];
		}
		}
		return TCL_OK;
	
	}
	ERR("Use: <img> composite <x> <y> copy|sover|draw");
}

int
GUI_saveAs(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
{
	if(argc>=3 && [CD isKindOf: [NXImage class]]) {
	NXStream *s;
	
	if(s=NXOpenMemory( NULL, 0, NX_WRITEONLY)) {
	[CD writeTIFF: s];
	NXSaveToFile( s, argv[2]);
	NXClose(s);
	return TCL_OK;
	}
	}
	ERR("? <img> saveAs <file.tiff>");
}

int
GUI_setSize(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
{
	if(argc>=4 && [CD isKindOf: [NXImage class]]) {
	NXSize s;
	
	s.width=atof(argv[2]);
	s.height=atof(argv[3]);
	[CD setScalable: YES];
	[CD setSize: &s];
	return TCL_OK;
	}
	ERR("? <img> setSize <w> <h>");
}

int
GUI_size(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
{
	if([CD isKindOf: [NXImage class]]) {
	NXSize s;
	
	[CD getSize: &s];
	sprintf(interp->result, "%g %g", s.width, s.height);
	return TCL_OK;
	}
	ERR("? <img> size");
}

int
GUI_textLength(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
{
	if([CD isKindOf: [Text class]]) {
		sprintf( interp->result, "%d", [CD textLength]);
		return TCL_OK;
	}
	ERR("textLength: text!");
}

int
GUI_sel(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
{
	if([CD isKindOf: [Text class]]) {
		NXSelPt start, end;
		[CD getSel: &start: &end];
		sprintf( interp->result, "%d %d", (int)start.cp, (int)end.cp);
		return TCL_OK;
	}
	ERR("sel: text!");
}

int
GUI_setSel(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
{
	if(argc>=4 && [CD isKindOf: [Text class]]) {
		[CD setSel: atoi(argv[2]): atoi(argv[3])];
		return TCL_OK;
	}
	ERR("Use: <txt> setSel <start> <end>");
}

int
GUI_getString(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
{
	if(argc>=4 && [CD isKindOf: [Text class]]) {
		int length=atoi(argv[3]);
		Tcl_SetResult( interp, (char *)malloc(sizeof(char)*(length+1)), TCL_DYNAMIC);
		[CD getSubstring: interp->result start: atoi(argv[2]) length: length];
		interp->result[length]=0;
		return TCL_OK;
	}
	ERR("Use: <txt> getString <start> <length>");
}

int
GUI_line2pos(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
{
	if(argc>=3 && [CD isKindOf: [Text class]]) {
		sprintf(interp->result, "%d", [CD positionFromLine:atoi(argv[2])]);
		return TCL_OK;
	}
	ERR("<txt> line2pos <line#>");
}

int
GUI_pos2line(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
{
	if(argc>=3 && [CD isKindOf: [Text class]]) {
		sprintf(interp->result, "%d", [CD lineFromPosition:atoi(argv[2])]);
		return TCL_OK;
	}
	ERR("<txt> pos2line <pos#>");
}

int
GUI_setImage(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
{
	if([CD isKindOf:[Button class]]) {
	if(argc>=3) {
		id what;
		
		if((what=GUI_findobject( interp, argv[2])) && [what isKindOf:[NXImage class]]) {
			[CD setImage: what];
			if(argc>=4 && !strcmp( argv[3], "-above"))
				[CD setIconPosition: NX_ICONABOVE];
			else
				[CD setIconPosition: NX_ICONONLY];
			return TCL_OK;
		} else
			ERR("setImage: no such image");
	} else {
		ERR("Usage: <button> setImage <image> (-above)");
	} } else
		ERR("setImage: <button>!");
}

int
GUI_value(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
{
	ISKINDOF(cd, [Control class]) {
		if([CD isMemberOf: [Button class]]) Tcl_SetResult(interp, [CD intValue] ? "1":"0", TCL_STATIC);
		else
		if([CD isKindOf: [NXBrowser class]] || [CD isKindOf: [Matrix class]]) {
			id sel;
			int rows, cols;
			int i;
			
			Tcl_SetResult(interp, "", TCL_VOLATILE);
			if([CD isKindOf: [NXBrowser class]]) sel=[CD matrixInColumn:0]; else sel=CD;
			[sel getNumRows: &rows numCols:&cols];
			for(i=0;i<rows;i++) {
				if([[sel cellAt:i:0] state]) 
					Tcl_AppendElement( interp, "1");
				else
					Tcl_AppendElement( interp, "0");
			}
			return TCL_OK;
		}
		else
		Tcl_SetResult(interp, (char *)[CD stringValue], TCL_VOLATILE); return TCL_OK;
		}
	ERR("value: browser|textfield|slider|button?");
}

int
GUI_refresh(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
{
	if([CD isKindOf: [NXBrowser class]]) {
		[CD loadColumnZero];
		return TCL_OK;
	}
	ERR("Use: <browser> refresh");
}

int
GUI_setTitle(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
{
	if(argc>=3) {
	if([CD isKindOf: [NXBrowser class]])
	{
		[CD setTitle: argv[2] ofColumn:0];
		return TCL_OK;
	}
	else
	if([CD isKindOf: [Window class]] || [CD isKindOf: [Button class]]) {
		[CD setTitle: argv[2]];
		return TCL_OK;
	}
	}
	ERR("Use: <> setTitle <title>");
}

int
GUI_display(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
{
	if([CD isKindOf: [View class]] || [CD isKindOf: [Window class]]) {
		[CD display];
		return TCL_OK;
	}
	ERR("display: view/window?");
}

int
GUI_addText(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
{
	if(argc>=3 && [CD isKindOf:[Text class]]) {
		int txtlength=[CD textLength];
		
		if(argc<4)
			[CD setSel:txtlength:txtlength];
		[CD replaceSel:argv[2]];
		if(argc<4)
			[CD scrollSelToVisible];
		return TCL_OK;	
	}
	ERR("Use: <txt> addText <text>");
}

int
GUI_setFont(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
{
	if(argc>=4 && [CD isKindOf:[Text class]]) {
		int txtlength=[CD textLength];
		
		if(argc<5)
			[CD setSel:txtlength:txtlength+1];
		[CD setSelFont: [Font newFont: argv[2] size: atof(argv[3])]];
		return TCL_OK;
	}
	ERR("Use: <txt> setFont <fontname> <size>");
}

int
GUI_drawPS(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
{
	if(argc>=2) {
		DPSPrintf(DPSGetCurrentContext(), "\n%s\n", argv[1]);
		return TCL_OK;
	}
	ERR("Use: drawPS <PS-code>");
}

int
GUI_lockFocus(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
{
	if(argc>=3) {
	if([CD isKindOf:[View class]]) {
		[CD lockFocus];
		Tcl_Eval(interp, argv[2]);
		[CD unlockFocus];
		[[CD window] flushWindow];
		return TCL_OK;
	} else
	if([CD isKindOf: [NXImage class]]) {
		[CD lockFocus];
		Tcl_Eval(interp, argv[2]);
		[CD unlockFocus];
		return TCL_OK;
	}
	}
	ERR("Use: <view> lockFocus <drawing proc>");
}

int
GUI_setDrawSelf(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
{
	if(argc>=3) {
	sprintf(GUI_string, "%s.ds", argv[0]);
	Tcl_SetVar( interp, GUI_string, argv[2], TCL_GLOBAL_ONLY);
	return TCL_OK;
	}
	ERR("Use: <view> setDrawSelf <drawing proc>");
}

int
GUI_setAction(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
{
	if(argc>=3) {
	if([CD isKindOf:[Control class]] || [CD isKindOf: [Window class]]) {
	GUI_removeAction(CD);
	GUI_insertAction(CD, argv[2]);
	if([CD isKindOf: [Window class]]) {
		[CD setDelegate: GUI_findobject(interp, ".G0")];
	} else {
	[[CD setAction: @selector(doSomething:)] setTarget: GUI_findobject(interp, ".G0")];
	}
	return TCL_OK;
	}
	}
	ERR("Use: <control> setAction <action proc>");
}

int
GUI_setMouseDown(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
{
	if(argc>=3) {
	sprintf(GUI_string, "%s.ms", argv[0]);
	Tcl_SetVar( interp, GUI_string, argv[2], TCL_GLOBAL_ONLY);
	return TCL_OK;
	}
	ERR("Use: <view> setMouseDown <cmds>");
}

int
GUI_visibleRect(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
{
	sprintf(interp->result, "%g %g %g %g", GUI_therect->origin.x, GUI_therect->origin.y, GUI_therect->size.width, GUI_therect->size.height);
	return TCL_OK;
}

#define SETVALUE(a,b) sprintf(GUI_string, "%g", b); Tcl_SetVar(interp, a, GUI_string, 0)

int
GUI_mouseDragging(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
{
	if(argc>=3) {
 		int	oldMask;
		NXEvent thisEvent;
		NXEvent *te;
		BOOL queue=0, shouldLoop=YES;
		
    	oldMask = [[CD window] addToEventMask:NX_LMOUSEDRAGGEDMASK];

	if(Tcl_GetVar(interp, "_queueMouse", TCL_GLOBAL_ONLY)) queue=1;
	Tcl_SetVar(interp, "_mup", "0", 0);
    while (shouldLoop) {
       if(queue) {
        	do {
		te = [NXApp getNextEvent:(NX_LMOUSEUPMASK |
                                         NX_LMOUSEDRAGGEDMASK)];
    		} while(te->type==NX_LMOUSEDRAGGED && ([NXApp peekNextEvent:NX_LMOUSEDRAGGEDMASK into:&thisEvent]));
	} else
	{
	te = [NXApp getNextEvent:(NX_LMOUSEUPMASK |
                                         NX_LMOUSEDRAGGEDMASK)];
	}
	if(te->type==NX_LMOUSEUP) { Tcl_SetVar(interp, "_mup", "1", 0); shouldLoop=NO; }
	[CD convertPoint:&te->location fromView:nil];
	SETVALUE("_mx", te->location.x);
	SETVALUE("_my", te->location.y);
	Tcl_Eval(interp, argv[2]);
	if(queue) NXPing();
	}
    	[[CD window] setEventMask:oldMask];
	return TCL_OK;
	} else
		ERR("Use: <view> mouseDragging <cmds>");
}

int
GUI_autosize(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
{
	unsigned int mask=0;
	
	if([CD isKindOf: [View class]]) {
	while(argc-->=1) {
	if(argv[argc][0]=='n') mask|=NX_NOTSIZABLE;
	if(argv[argc][0]=='l') mask|=NX_MINXMARGINSIZABLE;
	if(argv[argc][0]=='w') mask|=NX_WIDTHSIZABLE;
	if(argv[argc][0]=='r') mask|=NX_MAXXMARGINSIZABLE;
	if(argv[argc][0]=='t') mask|=NX_MINYMARGINSIZABLE;
	if(argv[argc][0]=='h') mask|=NX_HEIGHTSIZABLE;
	if(argv[argc][0]=='b') mask|=NX_MAXYMARGINSIZABLE;
	}
	[CD setAutosizing: mask];
	[[CD superview] setAutoresizeSubviews: YES];
	return TCL_OK;
	}
	ERR("autosize: view?");
}

int
GUI_alertPanel(ClientData cd, Tcl_Interp *interp, int argc, char *argv[])
{
	int r;
	
	if(argc>=4) {
		if(argc>=6)
			r=NXRunAlertPanel(argv[1], argv[2], argv[3],argv[4],argv[5]);
		else
			if(argc>=5)
				r=NXRunAlertPanel(argv[1], argv[2], argv[3],argv[4], NULL);
			else
				r=NXRunAlertPanel(argv[1], argv[2], argv[3], NULL, NULL);
	sprintf(interp->result, "%d", r);
	return TCL_OK;
	}
	ERR("? alertPanel <title> <txt> <b1> (<b2> <b3>)");
}

// "new"-cmds:

// open window
int
GUI_wopen( Tcl_Interp *interp, int argc, char *argv[])
{
	NXRect mycontent;
	int style=0, bmask=0, backing=0, defer=NO, hide=NO;
	id mywindow;
	
	if(argc>=7)
	{
	mycontent.origin.x=atof(argv[2]);
	mycontent.origin.y=atof(argv[3]);
	mycontent.size.width=atof(argv[4]);
	mycontent.size.height=atof(argv[5]);
	if(argc>=8) {
		while(--argc>6) {
#define EQA(c) EQ(argv[argc], c)
			EQA("titled") style|=NX_TITLEDSTYLE;
			EQA("plain") style|=NX_PLAINSTYLE;
			EQA("resize") style|=NX_RESIZEBARSTYLE;
			EQA("close") bmask|=NX_CLOSEBUTTONMASK;
			EQA("buffered") backing|=NX_BUFFERED;
			EQA("miniaturize") bmask|=NX_MINIATURIZEBUTTONMASK;
			EQA("retained") backing|=NX_RETAINED;
			EQA("nonretained") backing|=NX_NONRETAINED;
			EQA("defer") defer=YES;
			EQA("hide") hide=YES;
		}
	} else {
		style=NX_RESIZEBARSTYLE;
		backing=NX_BUFFERED;
		bmask=NX_CLOSEBUTTONMASK | NX_MINIATURIZEBUTTONMASK;
	}
	if(mywindow=[[Window alloc] initContent: &mycontent
		style: style
		backing: backing
		buttonMask: bmask
		defer: defer]) {
		SETRES(GUI_newobject(interp, 'w', mywindow));
		[mywindow setTitle: argv[6]];
		[mywindow display];
		[mywindow setFreeWhenClosed: NO];
		Tcl_SetVar( interp, ".curwin", interp->result, TCL_GLOBAL_ONLY);
		if(!hide) [mywindow makeKeyAndOrderFront:nil];
		return TCL_OK;
	}
		ERR("window open: Couldn't");
	}
	else
	{
		ERR("Usage: new window <x> <y> <width> <height> <title> titled| plain| resize| miniaturize| close| buffered| retained| nonretained| defer| hide(...)");
	}
}

#define LOOKFORWINDOW(pos, whname, wh) if(argc>=(pos+2)) { if(!strcmp(argv[pos], "-window")) GUI_addToView( interp, whname, wh, argv[pos+1]); } else GUI_addToView( interp, whname, wh, NULL);

// new button
int
GUI_bnew(Tcl_Interp *interp, int argc, char *argv[])
{
	id button;
	NXRect mf;
	
	if(argc>= 8)
	{
		mf.origin.x=atof(argv[2]);
		mf.origin.y=atof(argv[3]);
		mf.size.width=atof(argv[4]);
		mf.size.height=atof(argv[5]);
		if(button=[[Button alloc] initFrame:&mf title:argv[6] tag:0 target:GUI_findobject(interp, ".G0") action:@selector(doSomething:) key:0 enabled: YES]) {
			SETRES(GUI_newobject(interp, 'b', button));
			if(argc>=9 && !strcmp(argv[8],"-switch")) {
				[button setType:NX_SWITCH];
				LOOKFORWINDOW(9, interp->result, button)
			} else {
				LOOKFORWINDOW(8, interp->result, button)
			}
			GUI_insertAction( button, argv[7]);
			return TCL_OK;
		}
	}
	ERR("Usage: new button <x> <y> <w> <h> <title> <action> (-switch) (-window <win>)");
}

// new textfield
int
GUI_tnew(Tcl_Interp *interp, int argc, char *argv[])
{
	id text;
	NXRect mf;
	
	if(argc>= 7)
	{
		mf.origin.x=atof(argv[2]);
		mf.origin.y=atof(argv[3]);
		mf.size.width=atof(argv[4]);
		mf.size.height=atof(argv[5]);
		if(text=[[[[TextField alloc] initFrame:&mf] setAction: @selector(doSomething:)] setTarget: GUI_findobject(interp, ".G0")]) {
			SETRES(GUI_newobject(interp, 't', text));
			LOOKFORWINDOW(7, interp->result, text)
			GUI_insertAction( text, argv[6]);
			return TCL_OK;
		}
	}
	ERR("Usage: new textfield <x> <y> <w> <h> <action> (-window <win>)");
}

int
GUI_newtext(Tcl_Interp *interp, int argc, char *argv[])
{
	id text;
	NXRect mf;
	
	if(argc>= 6)
	{
		mf.origin.x=atof(argv[2]);
		mf.origin.y=atof(argv[3]);
		mf.size.width=atof(argv[4]);
		mf.size.height=atof(argv[5]);
		if(text=[[Text alloc] initFrame:&mf text:"" alignment: NX_LEFTALIGNED]) {
			SETRES(GUI_newobject(interp, 'T', text));
			LOOKFORWINDOW(6, interp->result, text)
			[text setVertResizable:YES];
			[text setMinSize: &mf.size];
			mf.size.height=mf.size.width=1000000;
			[text setMaxSize: &mf.size];
			[text setMonoFont: NO];
			return TCL_OK;
		}
	}
	ERR("Usage: new text <x> <y> <w> <h> (-window <win>)");
}

int
GUI_newview(Tcl_Interp *interp, int argc, char *argv[])
{
	id view;
	NXRect mf;
	
	if(argc>= 7)
	{
		mf.origin.x=atof(argv[2]);
		mf.origin.y=atof(argv[3]);
		mf.size.width=atof(argv[4]);
		mf.size.height=atof(argv[5]);
		if(view=[[[GUI_view alloc] initFrame:&mf] setTheObject: GUI_findobject(interp, ".G0")]) {
			SETRES(GUI_newobject(interp, 'v', view));
			LOOKFORWINDOW(7, interp->result, view)
			[view setVarName: interp->result];
			sprintf(GUI_string, "%s.ds", interp->result);
			Tcl_SetVar( interp, GUI_string, argv[6], TCL_GLOBAL_ONLY);
			return TCL_OK;
		}
	}
	ERR("Usage: new view <x> <y> <w> <h> <drawSelf-proc> (-window <win>)");
}

int
GUI_newscrollview(Tcl_Interp *interp, int argc, char *argv[])
{
	id view;
	NXRect mf;
	
	if(argc>= 7)
	{
		id doc;
		char *winname=NULL;
		
		mf.origin.x=atof(argv[2]);
		mf.origin.y=atof(argv[3]);
		mf.size.width=atof(argv[4]);
		mf.size.height=atof(argv[5]);
		if(view=[[ScrollView alloc] initFrame:&mf]) {
			SETRES(GUI_newobject(interp, 'S', view));
			LOOKFORWINDOW(7, interp->result, view)
			[view setDocView: doc=GUI_findobject( interp, argv[6])];
			if(argc>=9) { if(!strcmp(argv[7], "-window")) winname=argv[8]; }
				else winname=Tcl_GetVar( interp, ".curwin", TCL_GLOBAL_ONLY);
			if(winname) {
			sprintf(GUI_string, ".c%s", winname);
			Tcl_SetVar2( interp, GUI_string, argv[6], "", TCL_GLOBAL_ONLY);
			}
			[view setBorderType: NX_BEZEL];
			[view setBackgroundGray: 1];
			if([doc isKindOf: [Text class]]) {
				[doc setHorizResizable:YES];
				[doc sizeToFit];
				[doc setHorizResizable:NO];
				[doc setBackgroundGray: 1];
			}
			[view setVertScrollerRequired:YES];
			return TCL_OK;
		}
	}
	ERR("Usage: new scrollview <x> <y> <w> <h>  <doc-view> (-window <win>)");
}

int
GUI_newimage(Tcl_Interp *interp, int argc, char *argv[])
{
	id img;
	
	if(argc>=2)
	{
		if(img=[NXImage alloc]) {
			if(argc>=4 && !strcmp(argv[2], "-load")) {
				if([img loadFromFile: argv[3]]) {
					SETRES(GUI_newobject(interp, 'i', img));
					return TCL_OK;
				} else
					ERR("new image: couldn't open");
			} else {
				if(argc>=5 && !strcmp(argv[2], "-size")) {
					NXSize s;
					
					s.width=atof(argv[3]);
					s.height=atof(argv[4]);
					[img setSize: &s];
					SETRES(GUI_newobject(interp, 'i', img));
					return TCL_OK;
				}
			}
		}
	}
	ERR("Usage: new image -load <filename>");
}

int
GUI_fromNIB(Tcl_Interp *interp, int argc, char *argv[])
{
	id gui;
	
	if(argc>=3)
	{
		gui=GUI_findobject(interp, ".G0");
		[gui setWinsToNil];
		if([NXApp loadNibFile: argv[2] owner: gui])
		{
			[gui lookForWindows];
			return TCL_OK;
		}
		else
			ERR("new fromNIB: couldn't load");
	}
	ERR("Use: new fromNIB <filename>");
}

int
GUI_newslider(Tcl_Interp *interp, int argc, char *argv[])
{
	id slider;
	NXRect mf;
	
	if(argc>= 10)
	{
		mf.origin.x=atof(argv[2]);
		mf.origin.y=atof(argv[3]);
		mf.size.width=atof(argv[4]);
		mf.size.height=atof(argv[5]);
		if([[[[[[slider=[Slider alloc] initFrame:&mf] setMinValue: atof(argv[6])] setMaxValue: atof(argv[7])] setStringValue: argv[8]] setTarget:GUI_findobject(interp, ".G0")] setAction:@selector(doSomething:)]) {
			SETRES(GUI_newobject(interp, 's', slider));
			LOOKFORWINDOW(10, interp->result, slider)
			GUI_insertAction( slider, argv[9]);
			return TCL_OK;
		}
	}
	ERR("Use: new slider <x> <y> <w> <h> <minVal> <maxVal> <val> <action> (-window <win>)");
}

int
GUI_newbrowser(Tcl_Interp *interp, int argc, char *argv[])
{
	id browser;
	NXRect mf;
	
	if(argc>= 8)
	{
		id gui;
		char *newname;
		
		mf.origin.x=atof(argv[2]);
		mf.origin.y=atof(argv[3]);
		mf.size.width=atof(argv[4]);
		mf.size.height=atof(argv[5]);
		gui=GUI_findobject(interp, ".G0");
		if([[[[browser=[NXBrowser alloc] initFrame:&mf] setTarget: gui] setAction:@selector(doSomething:)] setDelegate: gui]) {
			SETRES(GUI_newobject(interp, 'B', browser));
			LOOKFORWINDOW(8, interp->result, browser)
			GUI_insertAction( browser, argv[7]);
			sprintf(GUI_string, "%s.list", interp->result);
			Tcl_SetVar( interp, GUI_string, argv[6], TCL_GLOBAL_ONLY);
			if(newname=malloc(sizeof(char)*(strlen(interp->result)+1))) {
			strcpy(newname, interp->result);
			NXNameObject(newname, browser, nil);
			}
			[browser setHorizontalScrollButtonsEnabled: NO];
			[browser display];
			[browser loadColumnZero];
			return TCL_OK;
		}
	}
	ERR("Use: new browser <x> <y> <w> <h> <contents-list> <action> (-window <win>)");
}

/* COMMANDLIST */

struct GUI_cmdo GUI_thecmds[]={
{ "free", GUI_free },
{ "setValue", GUI_setValue },
{ "value", GUI_value },
{ "setImage", GUI_setImage },
{ "display", GUI_display },
{ "lockFocus", GUI_lockFocus },
{ "setDrawSelf", GUI_setDrawSelf },
{ "setMouseDown", GUI_setMouseDown },
{ "setAction", GUI_setAction },
{ "mouseDragging", GUI_mouseDragging },
{ "remove", GUI_remove },
{ "setTitle", GUI_setTitle },
{ "refresh", GUI_refresh },
{ "setMultipleSelection", GUI_setMultipleSelection },
{ "setEmptySelection", GUI_setEmptySelection },
{ "addText", GUI_addText },
{ "setHorizScrolling", GUI_setHorizScrolling },
{ "setVertScrolling", GUI_setVertScrolling },
{ "visibleRect", GUI_visibleRect },
{ "setFont", GUI_setFont },
{ "textLength", GUI_textLength },
{ "sel", GUI_sel },
{ "setSel", GUI_setSel },
{ "getString", GUI_getString },
{ "line2pos", GUI_line2pos },
{ "pos2line", GUI_pos2line },
{ "startDragging", GUI_startDragging },
{ "setDragProc", GUI_setDragProc },
{ "setDragSource", GUI_setDragSource },
{ "composite", GUI_composite },
{ "autosize", GUI_autosize },
{ "orderFront", GUI_orderFront },
{ "select", GUI_select },
{ "hideDisplay", GUI_hideDisplay },
{ "saveAs", GUI_saveAs },
{ "setSize", GUI_setSize },
{ "size", GUI_size },
{ "close", GUI_close },
{ NULL, NULL } };

// types of objects available in the "new" command and corresponding procedures
struct GUI_cmd GUI_thenewcmds[]={
{ "button", GUI_bnew },
{ "textfield", GUI_tnew },
{ "window", GUI_wopen },
{ "view", GUI_newview },
{ "image", GUI_newimage },
{ "slider", GUI_newslider },
{ "browser", GUI_newbrowser },
{ "text", GUI_newtext },
{ "scrollview", GUI_newscrollview },
{ "fromNIB", GUI_fromNIB },
{ NULL, NULL } };


// The class:

@implementation GUI
+ initTCLCommands:(Tcl_Interp *)ip:(id)sender
{
	id g;

	Tcl_CreateCommand(ip, GUI_cmd_name(ip, "new"),GUI_newCmd,0,NULL);
	Tcl_CreateCommand(ip, GUI_cmd_name(ip, "drawPS"),GUI_drawPS,0,NULL);
	Tcl_CreateCommand(ip, GUI_cmd_name(ip, "alertPanel"),GUI_alertPanel,0,NULL);
	GUI_createGenTable(&GUI_cmds, GUI_thecmds);
	GUI_createTable(&GUI_new_cmds, GUI_thenewcmds);
	Tcl_InitHashTable(&GUI_actionTable, TCL_ONE_WORD_KEYS);
	GUI_newobject(ip, 'G', [g=[GUI alloc] init: ip]);
	Tcl_GlobalEval( ip, "\n\
proc _close {w} {\n\
global .c$w\n\
if [info exists .c$w] {\n\
foreach n [array names .c$w] {catch [$n remove]}\n\
unset .c$w\n\
}\n\
}\nproc _newObject {n txt} {global .curwin;global .c${.curwin};set .c${.curwin}($n) 1;set this 1;set l [llength $txt];for {set i 0} {$i<$l} {incr i} {if $this {set this 0;catch {set nxt [lindex $txt [expr $i+1]]};case [lindex $txt $i] drawSelf {$n setDrawSelf $nxt} = {global $nxt;linkVar $nxt $n} action {$n setAction $nxt} name {global $nxt; set $nxt $n} list {global $n.list;set $n.list $nxt} eval {uplevel #0 $nxt}} {set this 1}}}\n\
proc _trc {a b c} {upvar $a u;global _traces; case $c r {set u [$_traces($a) value]} w {$_traces($a) setValue $u}}\n\
proc linkVar {v b} {global _traces $v; set _traces($v) $b; trace variable $v rw _trc}");
	return g;
}

- init: (Tcl_Interp *)ipp
{
	inp=ipp;
	return self;
}

// called by buttons etc., invokes tcl procedure
- doSomething:sender
{ 
	char *action;
	
	if(action=GUI_findAction(sender)) {
		if([sender isKindOf:[Control class]]) {
			char *val;
			
			if(val=(char *)[sender stringValue]) {
			Tcl_SetVar( inp, "_sValue", val, TCL_GLOBAL_ONLY); }
		}
		Tcl_GlobalEval(inp, action);	
	}
	return self;
}

#define SETVAL(a,b) sprintf(GUI_string, "%g", b); Tcl_SetVar(inp, a, GUI_string, TCL_GLOBAL_ONLY)

- (void)doDrawSelf: (id)view: (NXRect *)re: (NXRect *)bo: (char *)name
{
	GUI_therect=re;
	SETVAL("_width", bo->size.width);
	SETVAL("_height", bo->size.height);
	sprintf(GUI_string, "%s.ds", name);
	if(name=Tcl_GetVar( inp, GUI_string, TCL_GLOBAL_ONLY)) 
		Tcl_GlobalEval(inp, name);
}

- (void)doMouseDown:(id)view:(NXEvent *)te:(char *)name
{
	[view convertPoint: &te->location fromView: nil];
	
	sprintf(GUI_string, "%s.ms", name);
	if(name=Tcl_GetVar( inp, GUI_string, TCL_GLOBAL_ONLY))
	{
	SETVAL("_mx", te->location.x);
	SETVAL("_my", te->location.y);	
	Tcl_GlobalEval(inp, name);
	}
}

- takeDrag: (Pasteboard *)pb : (NXPoint *)pt :(char *)name
{
	char *data;
	int length;
	
	sprintf(GUI_string, "%s.dr", name);
	if(name=Tcl_GetVar( inp, GUI_string, TCL_GLOBAL_ONLY))
	{
		char **tb;
		
		if((tb=(char **)[pb types]) && tb[0]) {
		int i=0, ascii=NO;
		while(tb[i]) {  if(!strcmp(tb[i], NXAsciiPboardType)) {ascii=YES; break; } i++; }
		
		if((ascii && [pb readType: NXAsciiPboardType data: &data length: &length]) || [pb readType: NXFilenamePboardType data: &data length: &length]) {
		sprintf(GUI_string, "%g %g", pt->x, pt->y);
		Tcl_VarEval( inp, name, " ", GUI_string," {", data, "}", NULL);
		[pb deallocatePasteboardData: data length: length];
		return self;
		}
		}
	}
	
	return nil;
}

// methods for loading nib files and assigning a tcl object to each interface object in each window
- setWinsToNil
{
	win1=win2=win3=win4=win5=win6=win7=win8=win9=win10=nil;
	return self;
}

#define L(a) if(a) [self lookInWindow: a]

- lookForWindows
{
	L(win1);	
	L(win2);	
	L(win3);	
	L(win4);	
	L(win5);	
	L(win6);	
	L(win7);	
	L(win8);	
	L(win9);	
	L(win10);
	return self;	
}

- convertToGUIview: (id)view
{
	char *txt;
		
	if((txt=(char *)NXGetObjectName(view)) && txt[0]!=0) {
		NXRect theframe;
		id gview;
		char *newname;
		
		[view getFrame: &theframe];
		if(gview=[[[GUI_view alloc] initFrame:&theframe] setTheObject: GUI_findobject(inp, ".G0")]) {
			newname=GUI_newobject(inp, 'v', gview);
			[[view superview] addSubview: gview];
			[gview setVarName: newname];
			Tcl_VarEval(inp, "_newObject ", newname, " {", NXGetObjectName(view), "}", NULL);
			}
			[view free];
	}
	return self;
}

- lookIntoView: (id)view
{
	int i;
	id list;
	
	list=[view subviews];
	printf("inView %d\n", [list count]);
	for(i=[list count]-1; i>=0; i--) [self lookInView: [list objectAt:i]];
	return self;
}

- lookInView: (id)view
{
	char *thename=NULL;
	char *oname;
	
	oname=(char *)NXGetObjectName(view);
	if([view isKindOf: [Button class]]) thename=GUI_newobject(inp, 'b', view); else
	if([view isKindOf: [TextField class]]) thename=GUI_newobject(inp, 't', view); else
	if([view isKindOf: [Slider class]]) thename=GUI_newobject(inp, 's', view); else
	if([view isKindOf: [Text class]]) { thename=GUI_newobject(inp, 'T', view); if(!oname) oname=(char *)NXGetObjectName([[view superview] superview]); } else
	if([view isKindOf: [ScrollView class]]) { thename=GUI_newobject(inp, 'S', view); return [self lookInView:[view docView]]; } else
	if([view isMemberOf: [View class]]) return([self convertToGUIview: view]); else
	if([view isKindOf: [Box class]]) return [self lookIntoView: [[view subviews] objectAt: 0]]; else 
	if([view isKindOf: [Matrix class]]) thename=GUI_newobject(inp, 'm', view); else
	if([view isKindOf: [NXBrowser class]]) {
		char *newname;
		id gui;
		
		thename=GUI_newobject(inp, 'B', view);
		if(newname=malloc(sizeof(char)*(strlen(thename)+1))) {
		strcpy(newname, thename);
		NXNameObject(newname, view, nil);
		gui=GUI_findobject(inp, ".G0");
		[view setDelegate: gui];
		}
	}
 	else
		return self;
	Tcl_VarEval(inp, "_newObject ", thename, " {", oname, "}", NULL);
	return self;
}

- lookInWindow: (id)win
{
	Tcl_SetVar( inp, ".curwin", GUI_newobject(inp, 'w', win), TCL_GLOBAL_ONLY);
	Tcl_VarEval( inp, "lappend _newwins ${.curwin}", NULL);
	[self lookIntoView: [win contentView]];
	return self;
}

// Thanks to Jeff Martin for his StringList example...
- (int)browser:sender fillMatrix:matrix inColumn:(int)column
{
	int   i;
	id   cellList, theCell;
  	int count;
	char **argvp;
	char *name;
		
	name=(char *)NXGetObjectName(sender);
	sprintf(GUI_string, "%s.list", name);
	if(name=Tcl_GetVar( inp, GUI_string, TCL_GLOBAL_ONLY)) 
	{
	if(name=Tcl_GetVar( inp, name, TCL_GLOBAL_ONLY))
	{
	// split string "name" into string array argvPtr
	Tcl_SplitList( inp, name, &count, &argvp);
	// Set matrix to have the right number of cells.
	[matrix renewRows:count cols:1];
	// Get list of cells from the matrix.
	cellList = [matrix cellList];

	// For each cell set its value, set whether it is a leaf or not and 
	//   mark it loaded.
	for(i=0;i<[cellList count];i++) {
		theCell = [cellList objectAt:i];
		[theCell setStringValue:argvp[i]];
		[theCell setLeaf:YES];
		[theCell setLoaded:YES];
	}

	free(argvp);
	
	// Return the number of rows.
	return count;
	}
	}
	return 0;
}

- windowWillClose: sender
{
	return [self doSomething: sender];
}
@end
