CCL Home Page
Up Directory CCL ScianTextBoxes
/*	ScianTextBoxes.c
    Routines for text boxes

    J. Lyons
    9/28/90

    3/26/91	added simple editing
    3/28/91 added routines to set font, size, line spacing
    3/29/91 added provision for different style text boxes
    4/19/91 added one-line editing 
    5/13/91 EMP moved an #ifdef FONTS4D around a fontHandle
    5/29/91 EMP removed lib headers
    7/17/91 added multiline editing
    7/22/91 added incredibly fancy frame for adjustable text boxes
    8/12/91 modified to use ScianFontSystem routines
    8/16/91 modified to use Set/Get2DIntBounds
    8/27/91 added ACTIVATED attribute
    8/28/91 added CUT, COPY, PASTE methods
    9/5/91	added TextHeight routine
    9/6/91	added help
    10/30/91 added shift-clicking to press method
    11/1/91 added double-clicking to press method
    11/4/91 added control window for textboxes (with EMP)
    1/8/92	added indentation control
    1/20/92	fixed linespace problem for large sizes
    1/22/92	made indentation behavior more like tabs, sort of
    4/9/92  EMP removed argument from NEWCTLWINDOW method
    6/13/92 EMP changed method prototypes to declarations
    8/30/92 EMP added opacity dependency and make method
    12/3/92 changed Press routine per new selection scheme
    12/8/92 added auto horiz scrolling to one-line text boxes
    1/21/93 did undo
    3/2/93  rewrote code for one-line textboxes to clean up auto-scrolling
*/

#include "Scian.h"
#include "ScianStyle.h"
#include "ScianTypes.h"
#include "ScianFontSystem.h"
#include "ScianWindows.h"
#include "ScianArrays.h"
#include "ScianLists.h"
#include "ScianDraw.h"
#include "ScianColors.h"
#include "ScianIDs.h"
#include "ScianErrors.h"
#include "ScianEvents.h"
#include "ScianScripts.h"
#include "ScianMethods.h"
#include "ScianDialogs.h"
#include "ScianControls.h"
#include "ScianHelp.h"
#include "ScianTitleBoxes.h"
#include "ScianTextBoxes.h"
#include "ScianSliders.h"
#include "ScianButtons.h"
#include "ScianDrawings.h"
#include "ScianSymbols.h"
#include "ScianSnap.h"

/* #define NEWWAY */

#define MAXLINE		255
#define MAXNLINES	20
#define MAXTEXTSIZE	96

/* Method declarations, NOT prototypes */
static ObjPtr EditTextBox();
static ObjPtr DrawTextBox();
static ObjPtr DrawOneLiner();
static ObjPtr PressTextBox();
static ObjPtr PressOneLiner();
static ObjPtr ShowTextBoxControls();
static ObjPtr TextBoxNotCurrent();
static ObjPtr CutText();
static ObjPtr CopyText();
static ObjPtr PasteText();
static ObjPtr MakeHelpString();
static ObjPtr SetTBVal();
static ObjPtr MakeTextBoxOpaque();

/* static function prototypes */
#ifdef PROTO

static void NewText(ObjPtr);
static char *NextLine(char *, int, char *);

#else

static void NewText();
static char *NextLine();

#endif



/*********************************************************************** GLOBALS */
ObjPtr textBoxClass;
static char *textBuf; /* pointer to buffer for drawing and editing text */
static int textBufSize; /* current size of the buffer */

static int indent;	/* current indent amount, in pixels */

#define TEXTBUFFERSIZE	1000
#define TEXTBUFFERINCR	100

void InitTextBoxes()
{
    ObjPtr list;
    
    textBoxClass = NewObject(controlClass, 0);
    AddToReferenceList(textBoxClass);
    SetVar(textBoxClass, NAME, NewString("Text Box"));
    SetVar(textBoxClass, TYPESTRING, NewString("text box"));
    SetMethod(textBoxClass, DRAW, DrawTextBox);
    SetMethod(textBoxClass, SETVAL, SetTBVal);
    SetMethod(textBoxClass, PRESS, PressTextBox);
    SetMethod(textBoxClass, KEYDOWN, EditTextBox);
    SetMethod(textBoxClass, CUT, CutText);
    SetMethod(textBoxClass, COPY, CopyText);
    SetMethod(textBoxClass, PASTE, PasteText);
    SetMethod(textBoxClass, SELECTALL, SelectAll);
    SetMethod(textBoxClass, PICKUP, (FuncTyp) 0);
    SetMethod(textBoxClass, MAKE1HELPSTRING, MakeHelpString);
    SetMethod(textBoxClass, NEWCTLWINDOW, ShowTextBoxControls);
    SetMethod(textBoxClass, YOURENOTCURRENT, TextBoxNotCurrent);
    
    /* stuff for undo to work */
    list = NewList();
    PrefixList(list, NewSymbol(BOUNDS));
    PrefixList(list, NewSymbol(BGNSEL));
    PrefixList(list, NewSymbol(ENDSEL));
    PrefixList(list, NewSymbol(TEXTFONT));
    PrefixList(list, NewSymbol(TEXTSIZE));
    PrefixList(list, NewSymbol(LINESPACE));
    PrefixList(list, NewSymbol(ALIGNMENT));
    PrefixList(list, NewSymbol(COLOR));
    PrefixList(list, NewSymbol(BACKGROUND));
    PrefixList(list, NewSymbol(LASTKEY));
    PrefixList(list, NewSymbol(VALUE));
    SetVar(textBoxClass, SNAPVARS, list);

    /*EMP stuff for opacity to work*/
    DeclareDependency(textBoxClass, OPAQUE, BACKGROUND);
    SetMethod(textBoxClass, OPAQUE, MakeTextBoxOpaque);

    /* get a buffer for drawing and editing text */
    textBuf = (char *) Alloc(textBufSize = TEXTBUFFERSIZE);
    if (!textBuf)
    {
	    OMErr();
	    textBufSize = 0;
    }
}

void KillTextBoxes()
{
	DeleteThing(textBoxClass);
	if (textBuf) Free(textBuf);
}

static ObjPtr MakeTextBoxOpaque(textBox)
ObjPtr textBox;
/*EMP - Makes a text box's OPAQUE variable*/
{
	SetVar(textBox, OPAQUE, GetVar(textBox, BACKGROUND) ? ObjTrue : ObjFalse);
	return ObjTrue;
}

#ifdef PROTO
ObjPtr NewTextBox(int left, int right, int bottom , int top, 
					int style, char *name, char *text)
#else
ObjPtr NewTextBox(left, right, bottom, top, style, name, text)
int left, right, bottom, top, style;
char *name, *text;
#endif
{
	ObjPtr newBox;

	if (left > right)
	{
		register int n;
		n = left; left = right; right = n;
	}
	if (bottom > top)
	{
		register int n;
		n = bottom; bottom = top; top = n;
	}
	newBox = NewObject(textBoxClass, 0);
	Set2DIntBounds(newBox, left, right, bottom, top);
	SetVar(newBox, CLASSID, NewInt(CLASS_TEXTBOX));
	SetVar(newBox, ACTIVATED, NewInt(true));
	SetVar(newBox, STYLE, NewInt(style));
	SetVar(newBox, NAME, NewString(name));
	if (style & WITH_PIT) SetVar(newBox, BACKGROUND, NewInt(TEXTBOXCOLOR));
	SetVar(newBox, VALUE, NewString(text));
	SetVar(newBox, CHANGED, ObjTrue); /* flag to line-break text before drawing first time */
	if (style & EDITABLE)
	{
		ObjPtr theLen = NewInt(strlen(text));
		SetVar(newBox, BGNSEL, theLen);
		SetVar(newBox, ENDSEL, theLen);
	}
	if (style & ONE_LINE)
	{
	    SetMethod(newBox, DRAW, DrawOneLiner);
	    SetMethod(newBox, PRESS, PressOneLiner);
	    SetVar(newBox, FORWARDVECTOR, ObjTrue); /* for auto-scroll */
	    SetVar(newBox, HOROFFSET, NewInt(0));
	}
	return newBox;
}

static ObjPtr MakeHelpString(textBox, theClass)
ObjPtr textBox, theClass;
{
	int style = GetInt(GetVar(textBox, STYLE));

	*textBuf = '\0';

	if (style & EDITABLE) strcpy(textBuf,"This is an editable text box. \
Click the left mouse button anywhere in the text to place the insertion point \
for adding new text. Drag through text to select it for editing. ");

	if (style & ADJUSTABLE) strcat(textBuf, "To change the size or location \
of this text box, first click anywhere in the text to select it. A frame will \
appear around the text with eight small handles. Drag any of the handles to change \
the size of the text box. Drag the frame itself to reposition the text.");

	if (style & ONE_LINE) strcat(textBuf, "If all the text will not fit \
within the box, it will automatically scroll as you add or delete text, drag \
through text to select it, or use the arrow keys to move the insertion point.");

	SetVar(theClass, HELPSTRING, *textBuf ? NewString(textBuf) : NULLOBJ);
	return ObjTrue;
}

#ifdef PROTO
void ActivateTextBox(ObjPtr textBox, Bool act)
#else
void ActivateTextBox(textBox, act)
ObjPtr textBox;
Bool act;
#endif
{
	if (act) SetVar(textBox, ACTIVATED, ObjTrue);
	else
	{
		SetVar(textBox, ACTIVATED, ObjFalse);
		Select(textBox, false);
	}
	ImInvalid(textBox);
}

static ObjPtr SetTBVal(textBox, theText)
ObjPtr textBox, theText;
{
	Bool deferChange;

	if (IsString(theText)) SetTextBox(textBox, GetString(theText));
	else if (IsInt(theText))
	{
		sprintf(tempStr, "%d", GetInt(theText));
		SetTextBox(textBox, tempStr);
	}
	else if (IsReal(theText))
	{
		sprintf(tempStr, "%g", GetReal(theText));
		SetTextBox(textBox, tempStr);
	}
	else return ObjFalse;

	return ObjTrue;
}

/******************************************************************** SET TEXT ROUTINES */
#ifdef PROTO
ObjPtr SetTextBox(ObjPtr textBox, char *text)
#else
ObjPtr SetTextBox(textBox, text)
ObjPtr textBox;
char *text;
#endif
{
	int style;
	
	style = GetInt(GetVar(textBox, STYLE));
	SetVar(textBox, VALUE, NewString(text));
	if (style & EDITABLE)
	{
		ObjPtr theLen = NewInt(strlen(text));
		SetVar(textBox, BGNSEL, theLen);
		SetVar(textBox, ENDSEL, theLen);
	}
	if (style & ONE_LINE)
	{
	    SetVar(textBox, HOROFFSET, NewInt(0));
	    SetVar(textBox, FORWARDVECTOR, ObjTrue);
	}
	SetVar(textBox, CHANGED, ObjTrue); 
	ImInvalid(textBox);
	ChangedValue(textBox);
	if (logging) LogControl(textBox);
	return NULLOBJ;
}

#ifdef PROTO
ObjPtr SetTextFont(ObjPtr textBox, char *fontName)
#else
ObjPtr SetTextFont(textBox, fontName)
ObjPtr textBox;
char *fontName;
#endif
{
	SetVar(textBox, TEXTFONT, NewString(fontName));
	SetVar(textBox, CHANGED, ObjTrue); 
	ImInvalid(textBox);
	return NULLOBJ;
}

#ifdef PROTO
ObjPtr SetTextSize(ObjPtr textBox, int textSize)
#else
ObjPtr SetTextSize(textBox, textSize)
ObjPtr textBox;
int textSize;
#endif
{
	if (textSize > 0 && textSize <= MAXTEXTSIZE)
		SetVar(textBox, TEXTSIZE, NewInt(textSize));
	SetVar(textBox, CHANGED, ObjTrue); 
	ImInvalid(textBox);
	return NULLOBJ;
}

#ifdef PROTO
ObjPtr SetTextLineSpace(ObjPtr textBox, int lineSpace)
#else
ObjPtr SetTextSize(textBox, lineSpace)
ObjPtr textBox;
int lineSpace;
#endif
{
	if (lineSpace > 0)
		SetVar(textBox, LINESPACE, NewInt(lineSpace));
	ImInvalid(textBox);
	return NULLOBJ;
}

#ifdef PROTO
ObjPtr SetTextAlign(ObjPtr textBox, int value)
#else
ObjPtr SetTextAlign(textBox, value)
ObjPtr textBox;
int value;
#endif
{
	SetVar(textBox, ALIGNMENT, NewInt(value));
	ImInvalid(textBox);
	return NULLOBJ;
}

#ifdef PROTO
ObjPtr SetTextColor(ObjPtr textBox, ObjPtr color)
#else
ObjPtr SetTextColor(textBox, color)
ObjPtr textBox, color;
#endif
{
	SetVar(textBox, COLOR, color);
	ImInvalid(textBox);
	return NULLOBJ;
}

#ifdef PROTO
ObjPtr SetTextBGColor(ObjPtr textBox, ObjPtr color)
#else
ObjPtr SetTextBGColor(textBox, color)
ObjPtr textBox, color;
#endif
{
	SetVar(textBox, BACKGROUND, color);
	ImInvalid(textBox);
	return NULLOBJ;
}

#ifdef PROTO
ObjPtr SetTextBoxStyle(ObjPtr textBox, int style)
#else
ObjPtr SetTextBoxStyle(textBox, style)
ObjPtr textBox;
int style;
#endif
{
	SetVar(textBox, STYLE, NewInt(style));
	if (style & ONE_LINE)
	{
	    SetMethod(textBox, DRAW, DrawOneLiner);
	    SetMethod(textBox, PRESS, PressOneLiner);
	    SetVar(textBox, FORWARDVECTOR, ObjTrue); /* for auto-scroll */
	    SetVar(textBox, HOROFFSET, NewInt(0));
	    SetVar(textBox, FORWARDVECTOR, ObjTrue);
	}
	else
	{
	    SetMethod(textBox, DRAW, DrawTextBox);
	    SetMethod(textBox, PRESS, PressTextBox);
	    SetVar(textBox, FORWARDVECTOR, NULLOBJ);
	    SetVar(textBox, HOROFFSET, NULLOBJ);
	    SetVar(textBox, FORWARDVECTOR, NULLOBJ);
	}
	SetVar(textBox, CHANGED, ObjTrue); 
	ImInvalid(textBox);
	return NULLOBJ;
}

#ifdef NEWWAY
/*********************************************************** NEW TEXT */
#ifdef PROTO
static void NewText(ObjPtr textBox)
#else
static void NewText(textBox)
ObjPtr textBox;
#endif
{
	/* puts "soft" eols (\r is used) into the text as needed */

	int left, right, bottom, top;
	int textLeft, textRight;
	ObjPtr theObj;
	char *text, *nxtLine, *bgnLine, *s, *t, *fontName, lineBuf[MAXLINE + 1];
	int len, width, size, style;

	Get2DIntBounds(textBox, &left, &right, &bottom, &top);

	MakeVar(textBox, VALUE);
	text = GetString(GetVar(textBox, VALUE));

	theObj = GetVar(textBox, STYLE);
	if (theObj) style = GetInt(theObj);
	else style = PLAIN;

	if (style & ONE_LINE)
	{
		SetVar(textBox, VALUE, NewString(text));
		return;
	}

	theObj = GetVar(textBox, TEXTFONT);
	if (theObj) fontName = GetString(theObj);
	else fontName = DEFFONT;

	theObj = GetVar(textBox, TEXTSIZE);
	if (theObj) size = GetInt(theObj);
	else size = DEFTEXTSIZE;

	if (style & WITH_PIT)
	{
		textLeft = left + TEXTMARGIN;
		textRight = right - TEXTMARGIN;
	}
	else if (style & (EDITABLE | ADJUSTABLE) || GetVar(textBox, BACKGROUND))
	{
		textLeft = left + HANDLESIZE + 2;
		textRight = right - HANDLESIZE - 2;
	}
	else /* default is no inset */
	{
		textLeft = left;
		textRight = right;
	}

	/*** CHANGE FOR INDENTATION ***/
	SetupFont(fontName, size);
	width = textRight - textLeft;
	nxtLine = text; /* set up for loop */
	t = textBuf;
	do {
		if (t - textBuf + MAXLINE > textBufSize) /* increase buffer size */
		{
			int tIndex = t - textBuf; /* remember position */
			textBuf = (char *) Realloc(textBuf, textBufSize + TEXTBUFFERINCR);
			if (!textBuf) /* failed */
			{
				OMErr();
				textBufSize = 0;
				return;
			}
			t = tIndex + textBuf; /* reset pointer in new buffer */
		}
		bgnLine = nxtLine;
		nxtLine = NextLine(bgnLine, width, s=lineBuf); /* (ignores existing soft CRs) */
		while (*t++ = *s++) ; /* copy line into textBuf */
		--t; /* back up to terminator */
		if (*nxtLine == '\n') ++nxtLine, *t++ = '\n';	/* paragraph */
		else *t++ = '\r'; /* soft eol */
	} while (*nxtLine);
	*t = '\0'; /* stuff terminator */
	SetVar(textBox, VALUE, NewString(textBuf));
	SetVar(textBox, CHANGED, ObjFalse);
	/* calling routine will handle draw, changed value */
}
#endif

/*********************************************************** TEXT HEIGHT */
#ifdef PROTO
int TextHeight(ObjPtr textBox)
#else
int TextHeight(textBox)
ObjPtr textBox;
#endif
{
#ifndef NEWWAY
	int left, right, bottom, top;
	int textLeft, textRight, textTop;
	ObjPtr theObj;
	char *text, *nxtLine, *bgnLine, lineBuf[MAXLINE + 1], *fontName;
	int y, width, size, align, lineSpace, style;
	int tabWid;

	Get2DIntBounds(textBox, &left, &right, &bottom, &top);

	MakeVar(textBox, VALUE);
	theObj = GetVar(textBox, VALUE);
	if (theObj) text = GetString(theObj);
	else text = "\0";
		
	theObj = GetVar(textBox, STYLE);
	if (theObj) style = GetInt(theObj);
	else style = PLAIN;

	theObj = GetVar(textBox, TEXTFONT);
	if (theObj) fontName = GetString(theObj);
	else fontName = DEFFONT;

	theObj = GetVar(textBox, TEXTSIZE);
	if (theObj) size = GetInt(theObj);
	else size = DEFTEXTSIZE;

	theObj = GetVar(textBox, ALIGNMENT);
	if (theObj) align = GetInt(theObj);
	else align = LEFTALIGN;

	theObj = GetVar(textBox, LINESPACE);
	if (theObj) lineSpace = GetInt(theObj);
	else lineSpace = 4 + size/3;

	MakeVar(textBox, BACKGROUND);

	if (style & WITH_PIT)
	{
		textLeft = left + TEXTMARGIN;
		textRight = right - TEXTMARGIN;
		textTop = top - TEXTMARGIN;
	}
	else if (style & (EDITABLE | ADJUSTABLE) || GetVar(textBox, BACKGROUND))
	{
		textLeft = left + HANDLESIZE + 2;
		textRight = right - HANDLESIZE - 2;
		textTop = top - HANDLESIZE - 2;
	}
	else /* default is no inset */
	{
		textLeft = left;
		textRight = right;
		textTop = top;
	}

	SetupFont(fontName, size);
	tabWid = TABWID*ChrWidth('0');
	y = textTop - size;
	width = textRight - textLeft;
	indent = 0;
	bgnLine = nxtLine = text; /* set up for loop */
	do {
		bgnLine = nxtLine;
		nxtLine = NextLine(bgnLine, width - indent, lineBuf);
		y -= size + lineSpace;
		if (*nxtLine == '\n')
		{
			++nxtLine; /* paragraph */
			indent = 0;
		}
		else indent += ChrCount(lineBuf, '\t')*tabWid;
	} while (*nxtLine);
	return textTop - y + size;
#else
	ObjPtr theObj;
	int n, size, lineSpace;
	char *t;
		
	if (GetPredicate(textBox, CHANGED)) NewText(textBox);

	MakeVar(textBox, VALUE);
	t = GetString(GetVar(textBox, VALUE));

	theObj = GetVar(textBox, TEXTSIZE);
	if (theObj) size = GetInt(theObj);
	else size = DEFTEXTSIZE;

	theObj = GetVar(textBox, LINESPACE);
	if (theObj) lineSpace = GetInt(theObj);
	else lineSpace = 4 + size/3;

	n = 0;
	while (*t)
	{
		if (*t == '\r' || *t == '\n') ++n;
		++t;
	}
	return n*(size + lineSpace);
#endif
}

int ChrCount(s, c)
char *s, c;
{
	int n=0;
	while (*s) if (*s++ == c) ++n;
	return n;
}

/************************************************************************** DRAW TEXT BOX */
static ObjPtr DrawTextBox(textBox)
ObjPtr textBox;
{
#ifdef GRAPHICS
	Bool active;
	int left, right, bottom, top;
	int textLeft, textRight, textBottom, textTop;
	int x,y;
	ObjPtr theObj, color, bgColor;
	char *s, *t, *text, *nxtLine, *bgnLine, lineBuf[MAXLINE + 1], *fontName;
	int lineStart, selStart, selEnd; /* distances for line and selection */
	int bgnSel, endSel; /* index to text selection */
	int align, length, width, center, size, lineSpace, style; 
	int tabWid, nTabs;

	Get2DIntBounds(textBox, &left, &right, &bottom, &top);
	if (IsDrawingRestricted(left, right, bottom, top)) return ObjFalse;

	active = GetPredicate(textBox, ACTIVATED);
	
	MakeVar(textBox, VALUE);
	theObj = GetStringVar("DrawTextBox", textBox, VALUE);
	if (theObj) text = GetString(theObj);
	else text = "\0";
		
	if ((length = strlen(text)) > textBufSize) /* grow buffer */
	{
		textBuf = (char *) Realloc(textBuf, length + TEXTBUFFERINCR);
		if (!textBuf) /* failed */
		{
			OMErr();
			textBufSize = 0;
			return NULLOBJ;
		}
	}

	theObj = GetIntVar("DrawTextBox", textBox, STYLE);
	if (theObj) style = GetInt(theObj);
	else style = PLAIN;

	theObj = GetVar(textBox, ALIGNMENT);
	if (theObj) align = GetInt(theObj);
	else align = LEFTALIGN;

	theObj = GetVar(textBox, TEXTFONT);
	if (theObj) fontName = GetString(theObj);
	else fontName = DEFFONT;

	theObj = GetVar(textBox, TEXTSIZE);
	if (theObj) size = GetInt(theObj);
	else size = DEFTEXTSIZE;

	theObj = GetVar(textBox, LINESPACE);
	if (theObj) lineSpace = GetInt(theObj);
	else lineSpace = 4 + size/3;

	MakeVar(textBox, COLOR);
	color = GetVar(textBox, COLOR);

	MakeVar(textBox, BACKGROUND);
	bgColor = GetVar(textBox, BACKGROUND);

	if (active && AmICurrent(textBox) && style & EDITABLE)
	{
		theObj = GetVar(textBox, BGNSEL);
		if (theObj) bgnSel = GetInt(theObj);
		else bgnSel = strlen(text);

		theObj = GetVar(textBox, ENDSEL);
		if (theObj) endSel = GetInt(theObj);
		else endSel = bgnSel;
	}

	if (style & WITH_PIT)
	{
		DrawSunkenRect(left, right, bottom, top,
			active ? TEXTBOXCOLOR : UIBACKGROUND);
		textLeft = left + TEXTMARGIN;
		textRight = right - TEXTMARGIN;
		textBottom = bottom + TEXTMARGIN;
		textTop = top - TEXTMARGIN;
	}
	else if (bgColor)
	{
		SetObjectColor(bgColor);
		FillRect(left, right, bottom, top);
		textLeft = left + HANDLESIZE + 2;
		textRight = right - HANDLESIZE - 2;
		textBottom = bottom + HANDLESIZE + 2;
		textTop = top - HANDLESIZE - 2;
	}
	else if (style & (EDITABLE | ADJUSTABLE))
	{
		textLeft = left + HANDLESIZE + 2;
		textRight = right - HANDLESIZE - 2;
		textBottom = bottom + HANDLESIZE + 2;
		textTop = top - HANDLESIZE - 2;
	}
	else /* default is no inset */
	{
		textLeft = left;
		textRight = right;
		textBottom = bottom;
		textTop = top;
	}
	if (style & ADJUSTABLE) SetClipRect(left, right, bottom, top);
	else SetClipRect(textLeft-1, textRight+1, textBottom-1, textTop+1);

	/* wake up, time to draw */
	SetupFont(fontName, size);
	y = textTop - size;
	width = textRight - textLeft;
	tabWid = TABWID*ChrWidth('0');
	indent = 0;
	center = (textLeft + textRight)/2;
	nxtLine = text; /* set up for loop */
	do {
		bgnLine = nxtLine;
		nxtLine = NextLine(bgnLine, width - indent, lineBuf);
		length = StrWidth(lineBuf);
		switch (align)
		{
		case CENTERALIGN:
           		lineStart = center - length/2;
			break;

		case RIGHTALIGN:
			lineStart = textRight - length;
			break;

		case LEFTALIGN:
		default:
			lineStart = textLeft + indent;
			break;
		}
		if (active && AmICurrent(textBox) && style & EDITABLE)
		{
			/* draw text cursor if in this line */
			if (bgnSel == endSel && text + bgnSel <= nxtLine
				&& text + bgnSel >= bgnLine)
			{
				/* find position of insertion point */
				int i = bgnSel - (bgnLine - text);

				strncpy(textBuf, bgnLine, i);
				textBuf[i] = '\0';
				selStart = StrWidth(textBuf);

				/* draw text cursor before bgnSel */
				FillUIRect(lineStart + selStart, lineStart + selStart + 1,
					y - lineSpace + 2, y + size + 2, TEXTCURSORCOLOR);
			}
			else if (text + endSel > bgnLine && text + bgnSel < nxtLine)
			{
				int selectColor;
				/* some of the selection is on this line */
				if (text + bgnSel < bgnLine) selStart = lineStart;
				else /* compute dist to start of selection */
				{
					int i = bgnSel - (bgnLine - text);

					strncpy(textBuf, bgnLine, i);
					textBuf[i] = '\0';
					selStart = lineStart + StrWidth(textBuf);
				}
				if (text + endSel >= nxtLine) selEnd = lineStart + length;
				else /* compute dist to end of selection */
				{
					int i = endSel - (bgnLine - text);

					strncpy(textBuf, bgnLine, i);
					textBuf[i] = '\0';
					selEnd = lineStart + StrWidth(textBuf);
				}
				/* now draw the selection rectangle for this line */
				selectColor = ChooseSelectionColor(textBox);
				FillUIRect(selStart, selEnd, y - lineSpace + 2, y + size + 2,
							selectColor);
			}
		}
		/* now draw the text */
		if (color) SetObjectColor(color);
		else SetUIColor(UITEXT);
		nTabs = 0; s = t = lineBuf; /* set up for loop */
		while (true)
		{
			if (*t == '\0')	/* end of line */
			{
				switch (align)
				{
				    case CENTERALIGN:
					DrawAString(CENTERALIGN, center, y, s);
					break;
					
				    case RIGHTALIGN:
					DrawAString(RIGHTALIGN, textRight, y, s);
					break;
					
				    case LEFTALIGN:
				    default:
					DrawAString(LEFTALIGN, lineStart, y, s);
					break;
				}
				break;
			}
			else if (*t == '\t') /* handle tab */
			{
				*t = '\0'; /* stuff terminator for this segment */
				DrawAString(LEFTALIGN, lineStart, y, s);
				indent = (++nTabs)*tabWid;
				lineStart = textLeft + indent;
				s = ++t;
				continue;
			}
			else ++t;						
		}
		if ((y -= size + lineSpace) < textBottom) break; /* whoa! past bottom of box */
		if (*nxtLine == '\n')
		{
			++nxtLine; /* paragraph */
			indent = 0;
		}
	} while (*nxtLine);

	if (IsSelected(textBox) && style & ADJUSTABLE)
	{
		/* Draw incredibly fancy frame for moving and resizing text box */

		int horCent = (left + right)/2;
		int vertCent = (bottom + top)/2;
#if 0
		FrameUIWideRect(left+INSET, right-INSET,
				bottom+INSET, top-INSET,
				OUTSIDEFRAMEWEIGHT, OUTSIDEFRAMECOLOR);
		FrameUIWideRect(left+INSET+OUTSIDEFRAMEWEIGHT,
				right-INSET-OUTSIDEFRAMEWEIGHT,
				bottom+INSET+OUTSIDEFRAMEWEIGHT,
				top-INSET-OUTSIDEFRAMEWEIGHT,
				INSIDEFRAMEWEIGHT, INSIDEFRAMECOLOR);
		FrameUIWideRect(left+INSET+OUTSIDEFRAMEWEIGHT+INSIDEFRAMEWEIGHT,
				right-INSET-OUTSIDEFRAMEWEIGHT-INSIDEFRAMEWEIGHT,
				bottom+INSET+OUTSIDEFRAMEWEIGHT+INSIDEFRAMEWEIGHT,
				top-INSET-OUTSIDEFRAMEWEIGHT-INSIDEFRAMEWEIGHT,
				OUTSIDEFRAMEWEIGHT, OUTSIDEFRAMECOLOR);
#else
		FrameUIWideRect(left+INSET, right-INSET,
				bottom+INSET, top-INSET,
				2*OUTSIDEFRAMEWEIGHT + INSIDEFRAMEWEIGHT + 1,
				OUTSIDEFRAMECOLOR);
		FrameUIWideRect(left+INSET+OUTSIDEFRAMEWEIGHT,
				right-INSET-OUTSIDEFRAMEWEIGHT,
				bottom+INSET+OUTSIDEFRAMEWEIGHT,
				top-INSET-OUTSIDEFRAMEWEIGHT,
				INSIDEFRAMEWEIGHT + 1, INSIDEFRAMECOLOR);
#endif
		/* Now draw the handles */
		/* center of sides */
		FillUIRect(left, left+HANDLESIZE,
				vertCent-HANDLESIZE/2, vertCent+HANDLESIZE/2, OUTSIDEFRAMECOLOR);
		FillUIRect(left+OUTSIDEFRAMEWEIGHT,
				left+HANDLESIZE-OUTSIDEFRAMEWEIGHT,
				vertCent-HANDLESIZE/2+OUTSIDEFRAMEWEIGHT,
				vertCent+HANDLESIZE/2-OUTSIDEFRAMEWEIGHT, INSIDEFRAMECOLOR);

		FillUIRect(right-HANDLESIZE, right,
				vertCent-HANDLESIZE/2, vertCent+HANDLESIZE/2, OUTSIDEFRAMECOLOR);
		FillUIRect(right-HANDLESIZE+OUTSIDEFRAMEWEIGHT,
				right-OUTSIDEFRAMEWEIGHT,
				vertCent-HANDLESIZE/2+OUTSIDEFRAMEWEIGHT,
				vertCent+HANDLESIZE/2-OUTSIDEFRAMEWEIGHT, INSIDEFRAMECOLOR);

		/* top edge */
		FillUIRect(horCent-HANDLESIZE/2, horCent+HANDLESIZE/2,
				top-HANDLESIZE, top, OUTSIDEFRAMECOLOR);
		FillUIRect(horCent-HANDLESIZE/2+OUTSIDEFRAMEWEIGHT,
				horCent+HANDLESIZE/2-OUTSIDEFRAMEWEIGHT,
				top-HANDLESIZE+OUTSIDEFRAMEWEIGHT,
				top-OUTSIDEFRAMEWEIGHT, INSIDEFRAMECOLOR);

		FillUIRect(left, left+HANDLESIZE,
				top-HANDLESIZE, top, OUTSIDEFRAMECOLOR);
		FillUIRect(left+OUTSIDEFRAMEWEIGHT,
				left+HANDLESIZE-OUTSIDEFRAMEWEIGHT,
				top-HANDLESIZE+OUTSIDEFRAMEWEIGHT,
				top-OUTSIDEFRAMEWEIGHT, INSIDEFRAMECOLOR);
		
		FillUIRect(right-HANDLESIZE, right,
				top-HANDLESIZE, top, OUTSIDEFRAMECOLOR);
		FillUIRect(right-HANDLESIZE+OUTSIDEFRAMEWEIGHT,
				right-OUTSIDEFRAMEWEIGHT,
				top-HANDLESIZE+OUTSIDEFRAMEWEIGHT,
				top-OUTSIDEFRAMEWEIGHT, INSIDEFRAMECOLOR);

		/* bottom edge */
		FillUIRect(horCent-HANDLESIZE/2, horCent+HANDLESIZE/2,
				bottom, bottom+HANDLESIZE, OUTSIDEFRAMECOLOR);
		FillUIRect(horCent-HANDLESIZE/2+OUTSIDEFRAMEWEIGHT,
				horCent+HANDLESIZE/2-OUTSIDEFRAMEWEIGHT,
				bottom+OUTSIDEFRAMEWEIGHT,
				bottom+HANDLESIZE-OUTSIDEFRAMEWEIGHT, INSIDEFRAMECOLOR);

		FillUIRect(left, left+HANDLESIZE,
				bottom, bottom+HANDLESIZE, OUTSIDEFRAMECOLOR);
		FillUIRect(left+OUTSIDEFRAMEWEIGHT,
				left+HANDLESIZE-OUTSIDEFRAMEWEIGHT,
				bottom+OUTSIDEFRAMEWEIGHT,
				bottom+HANDLESIZE-OUTSIDEFRAMEWEIGHT, INSIDEFRAMECOLOR);
		
		FillUIRect(right-HANDLESIZE, right,
				bottom, bottom+HANDLESIZE, OUTSIDEFRAMECOLOR);
		FillUIRect(right-HANDLESIZE+OUTSIDEFRAMEWEIGHT,
				right-OUTSIDEFRAMEWEIGHT,
				bottom+OUTSIDEFRAMEWEIGHT,
				bottom+HANDLESIZE-OUTSIDEFRAMEWEIGHT, INSIDEFRAMECOLOR);
	}
	RestoreClipRect();
#endif
	return ObjTrue;
}

static ObjPtr DrawOneLiner(textBox)
ObjPtr textBox;
{
#ifdef GRAPHICS
	Bool active, showEnd;
	int left, right, bottom, top;
	int textLeft, textRight, textBottom, textTop;
	ObjPtr theObj, color, bgColor;
	char *fontName, *text;
	int lineStart, selStart, selEnd; /* distances for line and selection */
	int bgnSel, endSel; /* index to text selection */
	int align, length, center, size, style, horOffset; 

	Get2DIntBounds(textBox, &left, &right, &bottom, &top);
	if (IsDrawingRestricted(left, right, bottom, top)) return ObjFalse;

	active = GetPredicate(textBox, ACTIVATED);
	
	MakeVar(textBox, VALUE);
	theObj = GetStringVar("DrawTextBox", textBox, VALUE);
	if (theObj) text = GetString(theObj);
	else text = "\0";
		
	theObj = GetIntVar("DrawTextBox", textBox, STYLE);
	if (theObj) style = GetInt(theObj);
	else style = PLAIN;

	theObj = GetVar(textBox, ALIGNMENT);
	if (theObj) align = GetInt(theObj);
	else align = LEFTALIGN;

	theObj = GetVar(textBox, TEXTFONT);
	if (theObj) fontName = GetString(theObj);
	else fontName = DEFFONT;

	theObj = GetVar(textBox, TEXTSIZE);
	if (theObj) size = GetInt(theObj);
	else size = DEFTEXTSIZE;

	theObj = GetVar(textBox, HOROFFSET);
	if (theObj) horOffset = GetInt(theObj);
	else horOffset = 0;

	MakeVar(textBox, COLOR);
	color = GetVar(textBox, COLOR);

	MakeVar(textBox, BACKGROUND);
	bgColor = GetVar(textBox, BACKGROUND);

	if (active && AmICurrent(textBox) && style & EDITABLE)
	{
		theObj = GetVar(textBox, BGNSEL);
		if (theObj) bgnSel = GetInt(theObj);
		else bgnSel = strlen(text);

		theObj = GetVar(textBox, ENDSEL);
		if (theObj) endSel = GetInt(theObj);
		else endSel = bgnSel;
	}

	if (style & WITH_PIT)
	{
		DrawSunkenRect(left, right, bottom, top,
			active ? TEXTBOXCOLOR : UIBACKGROUND);
		textLeft = left + TEXTMARGIN;
		textRight = right - TEXTMARGIN;
		textBottom = bottom + TEXTMARGIN;
		textTop = top - TEXTMARGIN;
	}
	else if (bgColor)
	{
		SetObjectColor(bgColor);
		FillRect(left, right, bottom, top);
		textLeft = left + HANDLESIZE + 2;
		textRight = right - HANDLESIZE - 2;
		textBottom = bottom + HANDLESIZE + 2;
		textTop = top - HANDLESIZE - 2;
	}
	else /* default is no inset */
	{
		textLeft = left;
		textRight = right;
		textBottom = bottom;
		textTop = top;
	}
	SetClipRect(textLeft-1, textRight+1, textBottom-1, textTop+1);
	
	SetupFont(fontName, size);
	length = StrWidth(text);
	center = (textLeft + textRight)/2;
	switch (align)
	{
		case CENTERALIGN:
           		lineStart = center - length/2;
			break;

		case RIGHTALIGN:
			lineStart = textRight - length;
			break;

		case LEFTALIGN:
		default:
			lineStart = textLeft;
			break;
	}
	if (active && AmICurrent(textBox) && style & EDITABLE)
	{
	    /* find co-ords of beginning and end of selection (without offset) */	
	    strncpy(textBuf, text, bgnSel);
	    textBuf[bgnSel] = '\0';
	    selStart = lineStart + StrWidth(textBuf);
	    if (bgnSel == endSel)
	    {
		selEnd = selStart + 1;
	    }
	    else
	    {
		int n = endSel - bgnSel;
		
		strncpy(textBuf, text + bgnSel, n);
		textBuf[n] = '\0';
		selEnd = selStart + StrWidth(textBuf);
	    }
	    
	    /* check horizontal offset, update if necessary */
	    showEnd = GetPredicate(textBox, FORWARDVECTOR);
	    /* NOTE: This variable is set in the PRESS and EDIT methods to 
	     * indicate which end of the selection to show when auto-scrolling.
	     */
	    if (showEnd) /* adjust offset to show the end of selection */
	    {
		if (selEnd - horOffset < textLeft)
		{
		    horOffset = selEnd - textLeft - 2;
		    theObj = NewInt(horOffset);
		    SetVar(textBox, HOROFFSET, theObj);
		}
		else if (selEnd - horOffset > textRight)
		{
		    horOffset = selEnd - textRight + 2;
		    theObj = NewInt(horOffset);
		    SetVar(textBox, HOROFFSET, theObj);
		}
	    }
	    else /* adust offset to show beginning of selection */
	    {
		if (selStart - horOffset < textLeft)
		{
		    horOffset = selStart - textLeft - 2;
		    theObj = NewInt(horOffset);
		    SetVar(textBox, HOROFFSET, theObj);
		}
		else if (selStart - horOffset > textRight)
		{
		    horOffset = selStart - textRight + 2;
		    theObj = NewInt(horOffset);
		    SetVar(textBox, HOROFFSET, theObj);
		}
	    }
	    /* draw text cursor or selection rectangle */
	    FillUIRect(selStart - horOffset, selEnd - horOffset,
			textBottom, textTop, ChooseSelectionColor(textBox));
	}
	/* now draw the text */
	if (color) SetObjectColor(color);
	else SetUIColor(UITEXT);
	switch (align)
	{
	    case CENTERALIGN:
		DrawAString(CENTERALIGN, center - horOffset, textTop - size, text);
		break;
		
	    case RIGHTALIGN:
		DrawAString(RIGHTALIGN, textRight - horOffset, textTop - size, text);
		break;
		
	    case LEFTALIGN:
	    default:
		DrawAString(LEFTALIGN, lineStart - horOffset, textTop - size, text);
		break;
	}
    RestoreClipRect();
#endif
    return ObjTrue;
}

static ObjPtr PressTextBox(textBox, mouseX, mouseY, flags)
ObjPtr textBox;
int mouseX, mouseY;
long flags;
{
#ifdef INTERACTIVE
    ObjPtr theObj;
    char *text, *t, *fontName, lineBuf[MAXLINE + 1];
    int i, j, cj, l, n, width, length;
    int style, align, size, nLines, lineSpace;
    float x;
    int y, mX, mY, offset[MAXNLINES * MAXLINE], lnx[3*(MAXNLINES + 1)];
    int left, right, bottom, top, hCent, vCent;
    int textLeft, textRight, textBot, textTop;
    int tabWid, nTabs;

    Get2DIntBounds(textBox, &left, &right, &bottom, &top);

    /* return if mouse outside text box */
    if (mouseX < left || mouseX > right || mouseY < bottom 
		    || mouseY > top) return ObjFalse;
    
    /* get text parameters and scan text */

    theObj = GetIntVar("PressTextBox", textBox, STYLE);
    if (!theObj) style = PLAIN;
    else style = GetInt(theObj);

    MakeVar(textBox, VALUE);
    theObj = GetVar(textBox, VALUE);
    if (theObj) text = GetString(theObj);
    else text = "\0";

    theObj = GetVar(textBox, ALIGNMENT);
    if (theObj) align = GetInt(theObj);
    else align = LEFTALIGN;

    theObj = GetVar(textBox, TEXTFONT);
    if (theObj) fontName = GetString(theObj);
    else fontName = DEFFONT;

    theObj = GetVar(textBox, TEXTSIZE);
    if (theObj) size = GetInt(theObj);
    else size = DEFTEXTSIZE;

    theObj = GetVar(textBox, LINESPACE);
    if (theObj) lineSpace = GetInt(theObj);
    else lineSpace = 4 + size/3;
    
    MakeVar(textBox, BACKGROUND);

    /* adjust bounds for background or pit */
    if (style & WITH_PIT)
    {
	textLeft = left + TEXTMARGIN;
	textRight = right - TEXTMARGIN;
	textBot = bottom + TEXTMARGIN;
	textTop = top - TEXTMARGIN;
    }
    else if (style & ADJUSTABLE || GetVar(textBox, BACKGROUND))
    {
	textLeft = left + (HANDLESIZE + 2);
	textRight = right - (HANDLESIZE + 2);
	textBot = bottom + (HANDLESIZE + 2);
	textTop = top - (HANDLESIZE + 2);
    }
    else
    {
	textLeft = left;
	textRight = right;
	textBot = bottom;
	textTop = top;
    }
    width = textRight - textLeft;

    SetupFont(fontName, size);
    tabWid = TABWID*ChrWidth('0');
/*
*	Build two arrays to keep character positions of text.
*	lnx[l] is the index of the first character of line l in text.
*	offset[i] is the total horiz offset to the middle of character i in text.
*/
    t = text; l = 0; nLines = 0; lnx[0] = 0; nTabs = 0; indent = 0; /* setup for loop */
    do {
	t = NextLine(t, width - indent, lineBuf);
	lnx[++l] = t - text; /* index to first char of next line */
	length = StrWidth(lineBuf);
		
	/* figure out where line begins */
	switch (align)
	{
	case CENTERALIGN:
		x = (textLeft + textRight)/2 - length/2;
		break;

	case RIGHTALIGN:
		x = textRight - length;
		break;

	case LEFTALIGN:
	default:
		x = textLeft + indent;
		break;
	}

	/* build next line of char offsets */
	for (i=lnx[l-1]; i MAXNLINES) break;
    } while (*t);

    nLines = l; /* number of lines of text */

    if (!IsSelected(textBox) && !GetVar(textBox, BACKGROUND))
    {
	/* see if the click is actually on text; if not, no hit */
	l = (textTop - mouseY)/(size + lineSpace);
	if (l < 0 || l >= nLines)
	{
	    return ObjFalse; /* above or below text */
	}
	if (mouseX < offset[lnx[l]] - ChrWidth(text[lnx[l]])
	    || mouseX > offset[lnx[l+1]-1])
	{
		return ObjFalse; /* to left or right of text */
	}
    }

    if (TOOL(flags) == T_HELP) /* help mode? */
    {
	ContextHelp(textBox);
	return ObjTrue;
    }
    
    /* return if not active */
    if (!GetPredicate(textBox, ACTIVATED)) return ObjTrue;

    /* return if not editable or adjustable */
    if (!(style & (EDITABLE | ADJUSTABLE))) return ObjTrue;
    
    if (style & ADJUSTABLE)	/* only adjustable TBs affect object selection */
    {
	if (!(flags & F_EXTEND) && !IsSelected(textBox))
	{
		/* new selection not already selected. Deselect the rest */
		DeselectAll();
	}

	if ((flags & F_EXTEND) && IsSelected(textBox) && !AmICurrent(textBox))
	{
		/*Deselect*/
		Select(textBox, false);
		return ObjTrue;
	}
	else if (!IsSelected(textBox))
	{
		/*Must select it*/
		Select(textBox, true);
	}
    }
    MakeMeCurrent(textBox);
    DrawMe(textBox);

    hCent = (left + right)/2;
    vCent = (bottom + top)/2;

    if (style & ADJUSTABLE) /* see if click is in frame */
    {
	Bool ml, mr, mb, mt;
	ml = mr = mb = mt = false;

	if (mouseX < left + HANDLESIZE) /* on left side */
	{
		if (mouseY > top - HANDLESIZE) /* top-left handle */
			mt = ml = true;
		else if (mouseY < bottom + HANDLESIZE) /* bottom-left handle */
			mb = ml = true;
		else if (mouseY > vCent - HANDLESIZE/2 && mouseY < vCent + HANDLESIZE/2)
			ml = true; /* middle-left handle */
		else ml = mr = mb = mt = true; /* in frame */
	}
	else if (mouseX > right - HANDLESIZE) /* on right side */
	{
		if (mouseY > top - HANDLESIZE) /* top-right handle */
			mt = mr = true;
		else if (mouseY < bottom + HANDLESIZE) /* bottom-right handle */
			mb = mr = true;
		else if (mouseY > vCent - HANDLESIZE/2 && mouseY < vCent + HANDLESIZE/2)
			mr = true; /* middle-right handle */
		else ml = mr = mb = mt = true; /* in frame */
	}
	else if (mouseY < bottom + HANDLESIZE) /* on bottom */
	{
		/* already handled (heh heh) corners */
		if (mouseX > hCent - HANDLESIZE/2 && mouseX < hCent + HANDLESIZE/2)
			mb = true; /* bottom middle handle */
		else ml = mr = mb = mt = true; /* in frame */
	}
	else if (mouseY > top - HANDLESIZE) /* on top */
	{
		/* already handled (heh heh) corners */
		if (mouseX > hCent - HANDLESIZE/2 && mouseX < hCent + HANDLESIZE/2)
			mt = true; /* middle top handle */
		else ml = mr = mb = mt = true; /* in frame */
	}
	else /* not on incredibly fancy frame */
	{
		if (!(style & EDITABLE)) return ObjTrue;
	}
	SaveForUndo(textBox);
	if (mr || ml || mb || mt) /* drag the incredibly fancy frame around */
	{
	    /* I am greatly indebted to my friend and colleague, Eric Pepke,
	       for the following code. Any errors or obfuscations are his. */
	    int initX = mouseX, initY = mouseY;
	    int newLeft, newRight, newBottom, newTop;
	    int oldNewLeft, oldNewRight, oldNewBottom, oldNewTop;

	    newLeft = oldNewLeft = left;
	    newRight = oldNewRight = right;
	    newBottom = oldNewBottom = bottom;
	    newTop = oldNewTop = top;

	    while (Mouse(&mX, &mY) && (mX == initX) && (mY == initY));

	    DrawSkeleton(true);

	    while (Mouse(&mX, &mY))
	    {
		if (ml) newLeft = left + mX - initX;
		if (mr) newRight = right + mX - initX;
		if (mb) newBottom = bottom + mY - initY;
		if (mt) newTop = top + mY - initY;

		if (flags & F_CONSTRAIN)
		{
		    /*Grid drag*/
		    if (ml && mr && mb && mt)
		    {
			/*Special case--whole object gridded
			  Only grid top left*/
			int width, height;
			width = newRight - newLeft;
			height = newTop - newBottom;
			newLeft = GRIDX(newLeft);
			newRight = newLeft + width;
			newTop = top - (GRIDY(top - newTop));
			newBottom = newTop - height;
		    }
		    else
		    {
			/*Normal case*/
			if (ml) newLeft = GRIDX(newLeft);
			if (mr) newRight = right - GRIDX(right - newRight);
			if (mb) newBottom = GRIDY(newBottom);
			if (mt) newTop = top - GRIDY(top - newTop);
		    }
		}
		if (ml && newLeft + 3 * HANDLESIZE > newRight)
				newLeft = newRight - 3 * HANDLESIZE;
		if (mr && newLeft + 3 * HANDLESIZE > newRight)
				newRight = newLeft + 3 * HANDLESIZE;
		if (mb && newBottom + 3 * HANDLESIZE > newTop)
				newBottom = newTop - 3 * HANDLESIZE;
		if (mt && newBottom + 3 * HANDLESIZE > newTop)
				newTop = newBottom + 3 * HANDLESIZE;
		if ((newLeft != oldNewLeft ||
				newRight != oldNewRight ||
				newBottom != oldNewBottom ||
				newTop != oldNewTop) &&
				newLeft < newRight &&
				newBottom < newTop)
		{
			Set2DIntBounds(textBox,
				newLeft, newRight, newBottom, newTop);
			oldNewLeft = newLeft;
			oldNewRight = newRight;
			oldNewBottom = newBottom;
			oldNewTop = newTop;
			DrawMe(textBox);
		}
	    }
	    DrawSkeleton(false);
	    if (logging)
	    {
		    char cmd[256];
		    MakeObjectName(tempStr, textBox);
		    sprintf(cmd, "set bounds %s [%d %d %d %d]\n",
			    tempStr, newLeft, newRight,
			    newBottom, newTop);
		    Log(cmd);
	    }
	    return ObjTrue;
	}
    }

    /* click is in text. First reset LASTKEY to 'no previous character' state */
    SetVar(textBox, LASTKEY, NewInt(-1));
    /* SaveForUndo(textBox); */
    
    if (flags & F_EXTEND)
    {
	i = GetInt(GetVar(textBox, GetPredicate(textBox, FORWARDVECTOR) ? BGNSEL : ENDSEL));
    }
    else
    {
	/* find line and character position of mouse down */
	l = (textTop - mouseY)/(size + lineSpace);
	if (l < 0) i = 0;
	else if (l >= nLines)
	{
		i = lnx[nLines];
	}
	else
	{
		i=lnx[l];
		while (offset[i] < mouseX && i= nLines)
	{
		j = lnx[nLines];
	}
	else 
	{
	    j=lnx[l];
	    while (offset[j] < mX && j j) /* make sure bgnSel < endSel */
	    {
		    SetVar(textBox, BGNSEL, NewInt(j));
		    SetVar(textBox, ENDSEL, NewInt(i));
		    SetVar(textBox, FORWARDVECTOR, ObjFalse);
	    }
	    else
	    {
		    SetVar(textBox, BGNSEL, NewInt(i));
		    SetVar(textBox, ENDSEL, NewInt(j));
		    SetVar(textBox, FORWARDVECTOR, ObjTrue);
	    }
	    DrawMe(textBox);
	    cj = j;	/* remember current position */
	}
    } while (Mouse(&mX, &mY));

    if (i == j && flags & F_DOUBLECLICK)
    {
	/* select the word at insPt */
	while (i > 0)
	{
		if (IsAlphaNum(text[i-1])) --i;
		else break;
	}
	while (text[j] != '\0')
	{
		if (IsAlphaNum(text[j])) ++j;
		else break;
	}
	SetVar(textBox, BGNSEL, NewInt(i));
	SetVar(textBox, ENDSEL, NewInt(j));
    }
#endif
    return ObjTrue;
}

static ObjPtr PressOneLiner(textBox, mouseX, mouseY, flags)
ObjPtr textBox;
int mouseX, mouseY;
long flags;
{
#ifdef INTERACTIVE
    ObjPtr theObj;
    char *text, *fontName;
    int i, j, cj, length, horOffset, w;
    int style, align, size, offset[MAXLINE];
    float x;
    int mX, mY;
    int left, right, bottom, top;
    int textLeft, textRight, textBot, textTop;

    Get2DIntBounds(textBox, &left, &right, &bottom, &top);

    /* return if mouse outside text box */
    if (mouseX < left || mouseX > right || mouseY < bottom 
		    || mouseY > top) return ObjFalse;

    if (TOOL(flags) == T_HELP) /* help mode? */
    {
	ContextHelp(textBox);
	return ObjTrue;
    }
    
    /* get text parameters and scan text */

    theObj = GetIntVar("PressOneLiner", textBox, STYLE);
    if (!theObj) style = PLAIN;
    else style = GetInt(theObj);

    /* return if not editable */
    if (!(style & EDITABLE)) return ObjTrue;

    /* return if not active */
    if (!GetPredicate(textBox, ACTIVATED)) return ObjTrue;

    MakeMeCurrent(textBox);

    MakeVar(textBox, VALUE);
    theObj = GetVar(textBox, VALUE);
    if (theObj) text = GetString(theObj);
    else text = "\0";

    theObj = GetVar(textBox, ALIGNMENT);
    if (theObj) align = GetInt(theObj);
    else align = LEFTALIGN;

    theObj = GetVar(textBox, TEXTFONT);
    if (theObj) fontName = GetString(theObj);
    else fontName = DEFFONT;

    theObj = GetVar(textBox, TEXTSIZE);
    if (theObj) size = GetInt(theObj);
    else size = DEFTEXTSIZE;

    theObj = GetVar(textBox, HOROFFSET);
    if (theObj) horOffset = GetInt(theObj);
    else horOffset = 0;

    MakeVar(textBox, BACKGROUND);

    /* adjust bounds for background or pit */
    if (style & WITH_PIT)
    {
	textLeft = left + TEXTMARGIN;
	textRight = right - TEXTMARGIN;
	textBot = bottom + TEXTMARGIN;
	textTop = top - TEXTMARGIN;
    }
    else if (GetVar(textBox, BACKGROUND))
    {
	textLeft = left + (HANDLESIZE + 2);
	textRight = right - (HANDLESIZE + 2);
	textBot = bottom + (HANDLESIZE + 2);
	textTop = top - (HANDLESIZE + 2);
    }
    else
    {
	textLeft = left;
	textRight = right;
	textBot = bottom;
	textTop = top;
    }

    SetupFont(fontName, size);
    length = StrWidth(text);
    
    /* figure out where line begins (without offset) */
    switch (align)
    {
        case CENTERALIGN:
	    x = (textLeft + textRight)/2 - length/2;
	    break;

        case RIGHTALIGN:
	    x = textRight - length;
	    break;

        case LEFTALIGN:
        default:
	    x = textLeft;
	    break;
    }
    for (i=0; text[i]; ++i)
    {
	w = ChrWidth(text[i]);
	offset[i] = x + w/2;
	x += w;
    }

    SetVar(textBox, LASTKEY, NewInt(-1)); /* no previous character */
    
    if (flags & F_EXTEND)
    {
	i = GetInt(GetVar(textBox, GetPredicate(textBox, FORWARDVECTOR) ? BGNSEL : ENDSEL));
    }
    else
    {
	/* Find character position of mouse down */
	for (i=0; text[i] && offset[i] - horOffset < mouseX; ++i) ;
    }
    
    /* track mouse and find char pos of end of selection */
    mX = mouseX; mY = mouseY; /* setup for at least one loop */
    cj = -1; /* forces redraw the first time */
    do {
	for (j=0; text[j] && offset[j] - horOffset < mX; ++j) ;
	if (j != cj) /* selection has changed */
	{
	    if (i > j) /* make sure bgnSel < endSel */
	    {
		    SetVar(textBox, BGNSEL, NewInt(j));
		    SetVar(textBox, ENDSEL, NewInt(i));
		    SetVar(textBox, FORWARDVECTOR, ObjFalse);
	    }
	    else
	    {
		    SetVar(textBox, BGNSEL, NewInt(i));
		    SetVar(textBox, ENDSEL, NewInt(j));
		    SetVar(textBox, FORWARDVECTOR, ObjTrue);
	    }
	    DrawMe(textBox);
	    cj = j;
	    horOffset = GetInt(GetVar(textBox, HOROFFSET));
	}
    } while (Mouse(&mX, &mY));
    
    if (i == j && flags & F_DOUBLECLICK)
    {
	/* select the word at insPt */
	while (i > 0)
	{
		if (IsAlphaNum(text[i-1])) --i;
		else break;
	}
	while (text[j] != '\0')
	{
		if (IsAlphaNum(text[j])) ++j;
		else break;
	}
	SetVar(textBox, BGNSEL, NewInt(i));
	SetVar(textBox, ENDSEL, NewInt(j));
    }
#endif
    return ObjTrue;
}

int IsAlphaNum(c)
char c;
{
    return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <='9';
}

static ObjPtr EditTextBox(textBox, key, flags)
ObjPtr textBox;
int key;
long flags;
{
#ifdef INTERACTIVE
    ObjPtr theObj, theText, theStyle, theInsPt, theEndSel;
    char *text;
    int i, j, len, style, insPt, endSel, lastKey;
    int left, right, bottom, top;

    if (!AmICurrent(textBox)) return ObjFalse; /* keyboard input not for me */

    theStyle = GetIntVar("EditTextBox", textBox, STYLE);
    if (!theStyle) return ObjTrue;
    style = GetInt(theStyle);
    if (!(style & EDITABLE)) return ObjTrue;

#if 0
    Not really reliable enough yet
    /*EMP Set up owner for faster redrawing*/
    if (style & ADJUSTABLE)
    {
	ObjPtr parent;
	parent = GetVar(textBox, PARENT);
	if (parent)
	{
	    if (key)
	    {printf("tempObscured is true\n");
		SetVar(parent, TEMPOBSCURED, ObjTrue);
	    }
	    else
	    {printf("tempObscured is false\n");
		SetVar(parent, TEMPOBSCURED, ObjFalse);
	    }
	}
    }
#endif
    Get2DIntBounds(textBox, &left, &right, &bottom, &top);

    /* adjust bounds */
    if (style & WITH_PIT)
    {
	    left += TEXTMARGIN;
	    right -= TEXTMARGIN;
	    bottom += TEXTMARGIN;
	    top -= TEXTMARGIN;
    }
    else if (style & ADJUSTABLE || GetVar(textBox, BACKGROUND))
    {
	    left += HANDLESIZE + 2;
	    right -= HANDLESIZE + 2;
	    bottom += HANDLESIZE + 2;
	    top -= HANDLESIZE + 2;
    }

    theText = GetStringVar("EditTextBox", textBox, VALUE);
    if (theText) text = GetString(theText);
    else text = "\0";
    
    if ((len = strlen(text)) > textBufSize) /* grow buffer */
    {
	    textBuf = (char *) Realloc(textBuf, len + TEXTBUFFERINCR);
	    if (!textBuf) /* failed */
	    {
		    OMErr();
		    textBufSize = 0;
		    return NULLOBJ;
	    }
    }

    theObj = GetVar(textBox, LASTKEY);
    if (theObj) lastKey = GetInt(theObj);
    else lastKey = -1;

    theInsPt = GetVar(textBox, BGNSEL);
    if (!theInsPt) insPt = len;
    else insPt = GetInt(theInsPt);

    theEndSel = GetVar(textBox, ENDSEL);
    if (!theEndSel) endSel = len;
    else endSel = GetInt(theEndSel);

    /* copy up to the insertion point */
    for(i=0; i 0) /* delete character before insPt */
	{
	    for(i = insPt, j = --insPt; i 0) --insPt;
	    }
	    else
	    {
		if (endSel > 0) --endSel;
	    }
	}
	else /* move insPt and endSel back */
	{
	    if (insPt > 0) --insPt;
	    endSel = insPt;
	}
	if (insPt <= endSel)
	{
	    SetVar(textBox, BGNSEL, NewInt(insPt));
	    SetVar(textBox, ENDSEL, NewInt(endSel));
	}
	else
	{
	    SetVar(textBox, BGNSEL, NewInt(endSel));
	    SetVar(textBox, ENDSEL, NewInt(insPt));
	}
	SetVar(textBox, LASTKEY, NewInt(-1));
	DrawMe(textBox);
    }
    else if (key == FK_RIGHT_ARROW)
    {
	if (flags & F_EXTEND)
	{
	    if (insPt == endSel) /* no selection */
	    {
		SetVar(textBox, FORWARDVECTOR, ObjTrue); /* set forward direction */
	    }
	    if (GetPredicate(textBox, FORWARDVECTOR))
	    {
		if (endSel < len) ++endSel;
	    }
	    else
	    {
		if (insPt < len) ++insPt;
	    }
	}
	else /*  move endSel and insPt forward */
	{
	    if (endSel < len) ++endSel;
	    insPt = endSel;
	}
	if (insPt <= endSel)
	{
	    SetVar(textBox, BGNSEL, NewInt(insPt));
	    SetVar(textBox, ENDSEL, NewInt(endSel));
	}
	else
	{
	    SetVar(textBox, BGNSEL, NewInt(endSel));
	    SetVar(textBox, ENDSEL, NewInt(insPt));
	}
	SetVar(textBox, LASTKEY, NewInt(-1));
	DrawMe(textBox);
    }
    else if (key == FK_UP_ARROW || key == FK_DOWN_ARROW)
    {
	if (style & ONE_LINE)
	{
	    /* just move to first or last char; scroll if necessary */
	    if (key == FK_UP_ARROW)
	    {
		if (flags & F_EXTEND)
		{
		    if (insPt == endSel) /* no selection */
			SetVar(textBox, FORWARDVECTOR, ObjFalse); /* set backward direction */
		    if (GetPredicate(textBox, FORWARDVECTOR)) endSel = 0;
		    else insPt = 0;
		}
		else insPt = endSel = 0;
	    }
	    else
	    {
		if (flags & F_EXTEND)
		{
		    if (insPt == endSel) /* no selection */
			SetVar(textBox, FORWARDVECTOR, ObjTrue); /* set forward direction */
		    if (GetPredicate(textBox, FORWARDVECTOR)) endSel = len;
		    else insPt = len;
		}
		else insPt = endSel = len;
	    }
	    if (insPt <= endSel)
	    {
		SetVar(textBox, BGNSEL, NewInt(insPt));
		SetVar(textBox, ENDSEL, NewInt(endSel));
	    }
	    else
	    {
		SetVar(textBox, BGNSEL, NewInt(endSel));
		SetVar(textBox, ENDSEL, NewInt(insPt));
	    }
	    SetVar(textBox, LASTKEY, NewInt(-1));
	    DrawMe(textBox);
	}
	else
	{
	    /* Oh, so you want to move the cursor up or down, eh? Well,
	       we can do that, it's no bother.  Really.  So what if we have 
	       to read all of the text and create a few arrays?  We don't
	       mind. It's no trouble at all, really.  Have a seat. */
    
	    int size, align, l, offset[MAXNLINES * MAXLINE], lnx[MAXNLINES + 1];
	    char *t, *fontName, lineBuf[MAXLINE + 1];
	    int width, length, tabWid, nTabs;
	    float x;
    
	    theObj = GetVar(textBox, ALIGNMENT);
	    if (theObj) align = GetInt(theObj);
	    else align = LEFTALIGN;
    
	    theObj = GetVar(textBox, TEXTFONT);
	    if (theObj) fontName = GetString(theObj);
	    else fontName = DEFFONT;
    
	    theObj = GetVar(textBox, TEXTSIZE);
	    if (theObj) size = GetInt(theObj);
	    else size = DEFTEXTSIZE;
    
	    MakeVar(textBox, BACKGROUND);
    
	    width = right - left;

	    SetupFont(fontName, size);
	    tabWid = TABWID*ChrWidth('0');
    
	    t = text; l = 0; lnx[0] = 0; indent = 0; /* setup for loop */
	    do {
		    t = NextLine(t, width - indent, lineBuf);
		    lnx[++l] = t - text; /* index to first char of next line */
		    length = StrWidth(lineBuf);
		    
		    /* figure out where line begins */
		    switch (align)
		    {
		    case CENTERALIGN:
			    x = (left + right)/2 - length/2;
			    break;
    
		    case RIGHTALIGN:
			    x = right - length;
			    break;
    
		    case LEFTALIGN:
		    default:
			    x = left + indent;
			    break;
		    }
    
		    /* build next line of char offsets */
		    for (i=lnx[l-1]; i MAXNLINES) break;
	    } while (*t);
    
	    /* find the line containing insPt */
	    for (i=1; i<=l; ++i) if (lnx[i] >= insPt) break;
    
	    /* line is i-1 */
    
	    if (key == FK_UP_ARROW)
	    {
		    int k;
    
		    if (i-1 == 0) return ObjTrue; /* already on first line */
    
		    for (k=lnx[i-2]; k offset[insPt]) break;
		    SetVar(textBox, BGNSEL, theObj = NewInt(k));
		    SetVar(textBox, ENDSEL, theObj);
		    SetVar(textBox, LASTKEY, NewInt(-1));
		    DrawMe(textBox);
	    }
	    else if (key == FK_DOWN_ARROW)
	    {
		    int k;
    
		    if (i == l) return ObjTrue; /* already on last line */
    
		    for (k=lnx[i]; k offset[insPt]) break;
		    SetVar(textBox, BGNSEL, theObj = NewInt(k));
		    SetVar(textBox, ENDSEL, theObj);
		    SetVar(textBox, LASTKEY, NewInt(-1));
		    DrawMe(textBox);
	    }
	}
    }
    else if (key == '\0')
    {
	    if (logging) LogControl(textBox);
	    ChangedValue(textBox);

	    /*** MakeMeCurrent(NULLOBJ); ***/
    }
    else if (key >= ' ' || key == '\t' && !(style & ONE_LINE))
    {
	if (lastKey < 0 || lastKey == '\b')
	{
	    SaveForUndo(textBox);
	}
	SetVar(textBox, LASTKEY, NewInt(key));

	if (style & ONE_LINE && len + 1 - (endSel - insPt) > MAXLINE)
		return ObjTrue; /* no more text will fit */
	
	/* add key at insertion point */
	textBuf[insPt] = key;
	for(i = endSel, j = ++insPt; i < len; ++i, ++j)
		textBuf[j] = text[i];
	textBuf[j] = '\0';
	SetVar(textBox, VALUE, NewString(textBuf));
	SetVar(textBox, BGNSEL, NewInt(insPt));
	SetVar(textBox, ENDSEL, NewInt(insPt));
	SetVar(textBox, CHANGED, ObjTrue);
	DrawMe(textBox);
    }
#endif
    return ObjTrue;
}

/************************************************************ CUT, COPY, PASTE*/
static ObjPtr CutText(textBox)
ObjPtr textBox;
{
	ObjPtr p;
	int i, j, len, style, insPt, endSel;
	char *text;
	Bool deferChange;

	if (p = GetStringVar("CutText", textBox, VALUE)) text = GetString(p);
	else text = "\0";
	
	len = strlen(text);

	if (p = GetVar(textBox, BGNSEL)) insPt = GetInt(p);
	else insPt = len;

	if (p = GetVar(textBox, ENDSEL)) endSel = GetInt(p);
	else endSel = len;
	
	if (insPt == endSel) return NewString("\0");
	
	SaveForUndo(textBox);
	
	style = GetInt(GetVar(textBox, STYLE));

	/* copy the selection to textBuf */
	for (i=insPt, j=0; i textBufSize) /* grow buffer */
    {
	textBuf = (char *) Realloc(textBuf, len + TEXTBUFFERINCR);
	if (!textBuf) /* failed */
	{
	    OMErr();
	    textBufSize = 0;
	    return NULLOBJ;
	}
    }
    
    style = GetInt(GetVar(textBox, STYLE));
    if (style & ONE_LINE && len > MAXLINE) return NULLOBJ; /* won't fit */
    
    SaveForUndo(textBox);

    /* copy up to the insertion point */
    for (i=0; i width) break;
			if (inSpace)	/* beginning new word */
			{
				curWord = text;
				inSpace = false;
			}
			line[n++] = *text++;
		}
	}
	if (m == 0 && !inSpace)	/* single word too long for line */
	{
		/* look for a good place to break it */
		char *b;

		line[n] = '\0';
		if ((b = strrchr(line, '/')) || (b = strrchr(line, '-')))
		{
			*(b + 1) = '\0';
			return text - (line + n - (b + 1));
		}
		else
		{
			/* oh, well, this is a good place */
			return text;
		}
	}
	else
	{
		line[m] = '\0';
		if (inSpace)	/* skip to next word */
		{
			while (*curWord==' ') ++curWord;
		}
		return curWord;
	}
}
Modified: Sun Nov 17 17:00:00 1996 GMT
Page accessed 2717 times since Sat Apr 17 21:54:45 1999 GMT