/*
 *	$Source: /u1/X/DECToolkit/src/RCS/Text.c,v $
 *	$Header: Text.c,v 1.2 86/12/17 16:05:41 swick Exp $
 */

#ifndef lint
static char *rcsid_Text_c = "$Header: Text.c,v 1.2 86/12/17 16:05:41 swick Exp $";
#endif	lint

#ifndef lint
static  char    *sccsid = "@(#)Text.c	1.17          12/11/86";
#endif lint
/*
 *			  COPYRIGHT 1986
 *		   DIGITAL EQUIPMENT CORPORATION
 *		       MAYNARD, MASSACHUSETTS
 *			ALL RIGHTS RESERVED.
 *
 * THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE AND
 * SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT CORPORATION.
 * DIGITAL MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR
 * ANY PURPOSE.  IT IS SUPPLIED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
 *
 * IF THE SOFTWARE IS MODIFIED IN A MANNER CREATING DERIVATIVE COPYRIGHT RIGHTS,
 * APPROPRIATE LEGENDS MAY BE PLACED ON THE DERIVATIVE WORK IN ADDITION TO THAT
 * SET FORTH ABOVE.
 *
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting documentation,
 * and that the name of Digital Equipment Corporation not be used in advertising
 * or publicity pertaining to distribution of the software without specific, 
 * written prior permission.
 */


/* File: Text.c */

#include <X/Xlib.h>
#include "Toolkit.h"	
#include "TextDisplay.h"	/** all text subwindow programs include **/
#include "Text.h"		

static initialize()
{
    FontInfo *finfo;

    myContext = UniqueEntryType();
    buf = (char *) Tmalloc(200);
    finfo = XOpenFont(DEFAULT_FONT);
    defaultSink = (TTextSink *) TCreateAsciiSink(finfo);
    initialized = 1;
}

/* Utility routines for support of TextSW */
#ifndef CURSORS
static DisplayInsertCursor (w, x, y, onOff)
  Window w;
  int x,y, onOff;
{
    int     fgnd;
    fgnd = onOff ? BlackPixel : WhitePixel;
    if (!insertCursorMask) {
	insertCursorMask = XStoreBitmap(insertCursor_width,
            insertCursor_height, insertCursor_mask);
    }
    XBitmapBitsPut(w, x - 3, y - 3,
	    insertCursor_width, insertCursor_height, insertCursor_bits,
	    fgnd, WhitePixel, insertCursorMask, GXcopy, AllPlanes);
}
#endif


/*
 * Procedure to make cursor visible.  It is used on editable windows.
 * The cursor will go will in ctx->i.position.
 */
static void InsertCursor (ctx, state)
  TextSWContext *ctx;
  enum InsertState state;
{
    int     x, y, onOff, line, visible;
    TTextBlock text;

    visible = LineAndXYForPosition(ctx, ctx->i.position, &line, &x, &y);
    if (state == on)
	onOff = 1;
    else
	onOff = 0;
    /** if the insert position is not currently on the screen->go get it **/
    if (x > xMargin &&
       ctx->i.position > 0 &&
       ctx->i.position >= (*(ctx->source->getLastPos))(ctx->source)) {
	  (*(ctx->source->read))(ctx->source, ctx->i.position - 1, &text, 1);
	  if (text.ptr[0] == '\n') {
	      x = xMargin;
	      y += (ctx->lt.entry[line + 1].y - ctx->lt.entry[line].y);
	  }
    }
    y += (ctx->lt.entry[line + 1].y - ctx->lt.entry[line].y);
    if (visible)
	DisplayInsertCursor(ctx->w, x, y, onOff);
}


/* 
 * given an x and y position this routine will return the byte offset
 * of that point in the source.
 */
static int PositionForXY (ctx, x, y)
  TextSWContext * ctx;
  int x,y;
{
 /* it is illegal to call this routine unless there is a valid line table! */
    int     width, fromx, line;
    TTextPosition position, resultstart, resultend;

    /*** figure out what line it is on ***/
    for (line = 0; line < ctx->lt.lines - 1; line++) {
	if (y <= ctx->lt.entry[line + 1].y)
	    break;
    }
    position = ctx->lt.entry[line].position;
    fromx = ctx->lt.entry[line].x;	/* starting x in line */
    width = x - fromx;			/* num of pix from starting of line */
    (*(ctx->sink->resolve)) (ctx->sink, ctx->source, position, fromx, width,
	    &resultstart, &resultend);
    if (resultstart > ctx->lt.entry[line + 1].position)
	resultstart = ctx->lt.entry[line + 1].position;
    return resultstart;
}


/***
 *** Given a window context and a byte offset, this routine will return
 *** the index into the line table of where the byte offset occurs.
 ***/
static int LineForPosition (ctx, position)
  TextSWContext *ctx;
  TTextPosition position;
  /* it is illegal to call this routine unless there is a valid line table!*/
{
    int     i, line;

    line = 0;
    if (position > ctx->lt.entry[0].position)
	for (i = 0; i < ctx->lt.lines; i++) {
	    if (position < ctx->lt.entry[line + 1].position
		|| (position == ctx->lt.entry[line + 1].position
		&&  position == (*(ctx->source->getLastPos))(ctx->source)))
		break;
	    else
		line++;
	}
    return line;
}


/***
 *** Given a window context and a byte offset, this routine figures out
 *** if the position is visible, and if it is visible it returns the
 *** index into the line table and the x,y coordinates.
 ***/
static int LineAndXYForPosition (ctx, pos, line, x, y)
  TextSWContext *ctx;
  TTextPosition pos;
  int *line, *x, *y;
  /* it is illegal to call this routine unless there is a valid line table!*/
{
    TTextPosition linePos, endPos;
    int     visible, realW, realH;

    *line = 0;
    *x = ctx->leftmargin;
    *y = yMargin;
    visible = IsPositionVisible(ctx, pos);
    /*** if it is in the line table determine the line, x, y  ***/
    if (visible) {
	*line = LineForPosition(ctx, pos);
	*y = ctx->lt.entry[*line].y;
	*x = ctx->lt.entry[*line].x;
	linePos = ctx->lt.entry[*line].position;
	(*(ctx->sink->findDistance)) (ctx->sink, ctx->source, linePos, *x, pos,
		&realW, &endPos, &realH);
	*x = *x + realW;
    }
    return visible;
}


/***
 *** this routine builds a line table for the given window context, starting
 *** at the given byte offset in the source.
 ***/
static void BuildLineTable (ctx, position)
  TextSWContext *ctx;
  TTextPosition position;
{
    int     x, y, width, realW, realH, line, lines;
    TTextPosition startPos, endPos;
    int     rebuild;
    TTextBlock text;

    rebuild = (position != ctx->lt.top);
    lines = (*(ctx->sink->maxLines)) (ctx->sink, ctx->height);
    if (ctx->lt.entry != NULL && lines != ctx->lt.lines) {
	free((char *) ctx->lt.entry);
	ctx->lt.entry = NULL;
    }
    if (ctx->lt.entry == NULL) {
	ctx->lt.entry =
	    (LineTableEntry *) Tmalloc(sizeof(LineTableEntry) * (lines + 1));
	for (line = 0; line < lines; line++) {
	    ctx->lt.entry[line].position = 0;
	    ctx->lt.entry[line].y = 0;
	}
	rebuild = TRUE;
    }
    else
	lines = ctx->lt.lines;
    if (rebuild) {
	ctx->lt.top = position;	
	ctx->lt.lines = lines;	
	startPos = position;
	y = yMargin;
	for (line = 0; line <= ctx->lt.lines; line++) {
	    x = ctx->leftmargin;
	    ctx->lt.entry[line].x = x;	
	    ctx->lt.entry[line].y = y; 
	    ctx->lt.entry[line].position = startPos; 
	    width = ctx->width - x;	

	    /** find the position - will read more source if necessary **/
	    (*(ctx->sink->findPosition)) (ctx->sink, ctx->source, startPos, x,
		    width, (ctx->options & wordBreak),
		    &endPos, &realW, &realH);
	    /** if not using word break -> keep reading until hit \n **/
	    if (!(ctx->options & wordBreak)) {
		(*(ctx->source->read)) (ctx->source, startPos, &text, 1);
		if (text.ptr[0] != '\n')
		    startPos = (*(ctx->source->scan)) (ctx->source, startPos, 
			    charSelection, right, ctx->s.not_in_word);
		    endPos = (*(ctx->source->scan)) 
			    (ctx->source,
			    (*(ctx->source->scan)) (ctx->source, startPos,
			      lineSelection, right, ctx->s.not_in_word),
			    charSelection, right, ctx->s.not_in_word);
	    }
	    ctx->lt.entry[line].endX = realW + x; 
	    startPos = endPos;
	    y = y + realH;
	}
    }
}


static void ForceBuildLineTable (ctx)
  TextSWContext *ctx;
{
    TTextPosition position;

    position = ctx->lt.top;
    ctx->lt.top++; /* ugly, but it works -- sets the rebuild boolean*/
    BuildLineTable(ctx, position);
}

#ifdef SCROLLBAR
static void SetScrollBar(ctx)
    TextSWContext *ctx;
{
    float   first, last;
    int     lastPos;
    if (ctx->sbar) {
	lastPos = (*(ctx->source->getLastPos))(ctx->source);
	if (lastPos > 0) {
	    first = ctx->lt.top * 100.0 / lastPos; /* Just an approximation */
	    last = ctx->lt.entry[ctx->lt.lines].position * 100.0 / lastPos;
	}
	else {
	    first = 0.0;
	    last = 100.0;
	}
	TSetScrollBarPercentages(ctx->sbar, first, last - first);
    }
}


static int ScrollUpDownProc (sbar, w, pix)
  Window sbar, w;
  int pix;
{
    TextSWContextPtr ctx;
    int     apix, line, target;
    TTextPosition pos;
    if (FindEntry(w, myContext, &ctx) != ERRNONE)
	return;
    apix = abs(pix);
    for (line = 1;
	    line < ctx->lt.lines && apix > ctx->lt.entry[line + 1].y;
	    line++);
    if (pix >= 0) {
	if (ctx->lt.entry[line].position > 0) {
	    BuildLineTable(ctx, ctx->lt.entry[line].position);
	    TMoveArea(ctx->w, ctx->leftmargin, ctx->lt.entry[line].y,
		    ctx->leftmargin, ctx->lt.entry[0].y, 9999, 9999);
	    SetScrollBar(ctx);
	}
    }
    else {
	target = ctx->lt.top;
	while (ctx->lt.top > 0 && ctx->lt.entry[line].position > target) {
	    pos = ctx->lt.top - 1;
	    if (pos > 0)
		pos--;
	    pos = (*(ctx->source->scan))(ctx->source, pos, lineSelection, left,
				    ctx->s.not_in_word);
	    BuildLineTable(ctx, pos);
	}
	if (ctx->lt.entry[line].position == target) {
	    TMoveArea(ctx->w, ctx->leftmargin,
		    ctx->lt.entry[0].y - ctx->lt.entry[line].y,
		    ctx->leftmargin, 0, 9999, 9999);
	    SetScrollBar(ctx);
	}
	else
	    DisplayTextSW(ctx);
    }
}


static int ThumbProc (sbar, w, where)
  Window sbar, w;
  float where;
{
    TextSWContextPtr ctx;
    TTextPosition position, lastPos;
    if (FindEntry(w, myContext, &ctx) != ERRNONE) return;
    lastPos = (*(ctx->source->getLastPos))(ctx->source);
    position = (where / 100.0) * lastPos;
    if (position >= lastPos)
	position = lastPos - 1;
    position = (*(ctx->source->scan))(ctx->source, position, lineSelection, left,
				 ctx->s.not_in_word);
    BuildLineTable(ctx, position);
    DisplayTextSW(ctx);
}
#endif SCROLLBAR


/***
 *** This routine will replace the text from pos1 to pos2 in a source.
 *** It will return the values updateFrom and updateTo, which are the 
 *** bytes that need to be redisplayed.
 ***/
static ReplaceText (ctx, pos1, pos2, text, updateFrom, updateTo)
  TextSWContext *ctx;
  TTextPosition pos1, pos2;  /* update from pos1 to pos2 
			     /* NOTE: there values determine whether it is
			     /*       text insertion or text replacement */
  TTextBlock *text;	     /* new text to use */
  TTextPosition *updateFrom, *updateTo; /* RETURNED: what to redisplay */

  /* NOTE:
  /* it is illegal to call this routine unless there is a valid line table!*/
{
    int     i, x, y, delta, realW, realH, line, width, visible;
    TTextPosition startPos, endPos;
    TTextBlock text2;

    endPos = pos1;
    line = LineForPosition(ctx, pos1);  
    delta = (*(ctx->source->replace))(ctx->source, pos1, pos2, text); 

    /*** fixup all line table entries  and selection for edit */
    for (i = line + 1; i < ctx->lt.lines + 1; i++) {
	ctx->lt.entry[i].position = ctx->lt.entry[i].position + delta;
    }

    /*** want to update from the begining of the word to the left of the
    /*** starting position.  ***/
    if (text->length != 0)
	*updateFrom = (*(ctx->source->scan))(ctx->source, pos1,
		wordSelection, left, ctx->s.not_in_word);
    else
	*updateFrom = pos1;
    if (*updateFrom > ctx->lt.entry[0].position)
	(*updateFrom)--;

    /*** now that we know where to start from, check to see if it is in the 
     *** line table. (ie. visible) ***/
    startPos = *updateFrom;
    visible = LineAndXYForPosition(ctx, startPos, &line, &x, &y);
    if (visible) {
	for (i = line; i < ctx->lt.lines; i++) {/* fixup line table */
	    width = ctx->width - ctx->leftmargin - x;
	    (*(ctx->sink->findPosition)) (ctx->sink, ctx->source, startPos, x,
		    width, (ctx->options & wordBreak),
		    &endPos, &realW, &realH);

	    /** if not using workbreak -- find the \n **/
	    if (!(ctx->options & wordBreak)) {
		(*(ctx->source->read))(ctx->source, startPos, &text2, 1);
		if (text2.ptr[0] != '\n')
		    startPos = (*(ctx->source->scan))(ctx->source, startPos, 
			    charSelection, right, ctx->s.not_in_word);
		    endPos = (*(ctx->source->scan))(ctx->source,
			(*(ctx->source->scan))(ctx->source, startPos,
			    lineSelection, right, ctx->s.not_in_word),
			charSelection, right, ctx->s.not_in_word);
	    }

	    ctx->lt.entry[i].endX = realW + x;  /** ending x pixel in line */
	    ctx->lt.entry[i + 1].y = realH + ctx->lt.entry[i].y;
	    if ((endPos > pos1) && (endPos == ctx->lt.entry[i + 1].position)
				&& (endPos == ctx->lt.entry[i + 2].position))
		break;
	    ctx->lt.entry[i + 1].position = endPos;
	    startPos = endPos;
	    x = ctx->lt.entry[i + 1].x;
	}
    }

    *updateTo = endPos;  
    return delta;	
}


/***
 *** change the text source.
 ***/
static void NewTextSource(ctx, source, startPos)
  TextSWContext *ctx;
  TTextSource *source;
  TTextPosition startPos;
{
    ctx->source = source;	
    ctx->lt.top = startPos;	
    ctx->s.left = ctx->s.right = 0;
    ctx->i.position = startPos;	
    ForceBuildLineTable(ctx);
    DisplayTextSW(ctx);
}


/***
 *** This routine will Display text in the window assocated with ctx,
 *** from pos1 to pos2 (byte offsets), and will highlight the text if
 *** required.
 ***/
static void DisplayText(ctx, pos1, pos2, highlight)
  TextSWContext *ctx;
  TTextPosition pos1, pos2;  
  int highlight;
  /* NOTE:
  /* it is illegal to call this routine unless there is a valid line table!*/
{
    int     x, y, line, i, height, visible;
    TTextPosition startPos, endPos;

    if (pos1 < ctx->lt.top) pos1 = ctx->lt.top;
    visible = LineAndXYForPosition(ctx, pos1, &line, &x, &y);
    if (!visible)
	return;
    startPos = pos1;
    for (i = line; i < ctx->lt.lines; i++) {
	if (pos2 < ctx->lt.entry[i + 1].position)
	    endPos = pos2;
	else
	    endPos = ctx->lt.entry[i + 1].position;
	if (endPos > startPos)
	    (*(ctx->sink->display)) (
		    ctx->sink, ctx->source, ctx->w, x, y,
		    startPos, endPos, highlight);
	startPos = endPos;
	height = ctx->lt.entry[i + 1].y - ctx->lt.entry[i].y;
	XPixSet(ctx->w, ctx->lt.entry[i].endX, y, 999, height, WhitePixel);
	x = ctx->leftmargin;
	y = ctx->lt.entry[i + 1].y;
	if ((endPos == pos2) &&
		(endPos != (*(ctx->source->getLastPos)) (ctx->source)))
	    break;
    }
}


/***
 *** If 2 or more windows may not have hightlighted text in more than 1 of
 *** the windows, they have "mutually exclusive selection" (this is determined
 *** at create time).  When one window hightlights, the hightlighting in
 *** the other windows must be turned off.  This routine turns the other
 *** highlighting off. 
 ***/
static void mutual_exclusion_selection(ctx)
  TextSWContext *ctx;
{
  TextSWContext *previous_ctx;

  if ((window_with_selection != NULL) && (window_with_selection != ctx->w)) 
	/*** get the entry of the window that had the hightlighting ***/
	if (FindEntry(window_with_selection, myContext, &previous_ctx) 
            == ERRNONE) {

		/*** turn the hightlighting off ***/
		DisplayText(previous_ctx, previous_ctx->s.left,
			    previous_ctx->s.right, FALSE);
		previous_ctx->s.left = previous_ctx->s.right = 0;
	}
  /*** save the window with the new highlighting ***/
  window_with_selection = ctx->w;	
}

/***
 ***  This window performs the selection based upon hitting the
 ***  left mouse button (for right handers).  It will hightlight
 ***  either by char, word, or eoln.
 ***/
static void DoSelection (ctx, position, time, motion)
  TextSWContext *ctx;
  TTextPosition position;	/*** position where mouse was pressed ***/
  unsigned short time;
  int motion;
{
    int     delta, newLeft, newRight;
    enum SelectionType newType;

    if (ctx->i.type != none)
	InsertCursor(ctx, off);
/**** mary commented this out.   Currently not using double clicks ***/	
/*  delta = (time < ctx->s.time) ? ctx->s.time - time : time - ctx->s.time;
    if (motion == TRUE)
	newType = ctx->s.type;
    else {
	if ((delta < 50) && ((position >= ctx->s.left)
		    && (position <= ctx->s.right)))/* multi-click event */
/*	    switch (ctx->s.type) {
		case charSelection: 
		    newType = wordSelection;
		    break;
		case wordSelection: 
		    newType = lineSelection;
		    break;
		case lineSelection: 
		    newType = allSelection;
		    break;
		case allSelection: 
		    newType = charSelection;
		    break;
	    }
	else			/* single-click event */
/*	    newType = charSelection;/* fool into Char selection */
/*    } */
/*** end of commenting out ---- mary ***/


/** for now, newType is always what was passed in at create time - mary **/
    newType = ctx->s.type;

    /*** find the left and right positions. ***/
    if (newType == charSelection) {
	newLeft  = position;	
	newRight = (*(ctx->source->scan))(ctx->source, position, newType, 
				right, ctx->s.not_in_word);
    } else {
	newLeft  = (*(ctx->source->scan))(ctx->source, position, newType, left,
				ctx->s.not_in_word);
        newRight = (*(ctx->source->scan))(ctx->source, position, newType, 
				right, ctx->s.not_in_word);
    }

    /*** do the udpate 
     *** 1) if mouse clicked out of source range, make s.left = s.right
     *** 2) only update insert position if insertion type is "edit"
     *** 3) redisplay the old hightlighted text and new hightlighted text.
     ***/
    if ((newLeft != ctx->s.left) || (newRight != ctx->s.right)
	    || (newType != ctx->s.type)) {
	DisplayText(ctx, ctx->s.left, ctx->s.right, FALSE);
	ctx->s.type = newType;
	ctx->s.left = newLeft;
	if (newRight > (*(ctx->source->getLastPos))(ctx->source)) 
		ctx->s.right    = newLeft;
	else 
		ctx->s.right    = newRight;
	DisplayText(ctx, ctx->s.left, ctx->s.right, TRUE);
	if (ctx->i.type == edit) 
		ctx->i.position = ctx->s.right;
    }

    if (ctx->i.type != none)
	InsertCursor(ctx, on);
/*    ctx->s.time = time; */
}


static void ExtendSelection (ctx, position)
  TextSWContext *ctx;
  TTextPosition position;
{
    TTextPosition newPos;

  if (ctx->i.type != none)
	InsertCursor(ctx, off);
  if (position >= ctx->s.right) {
    newPos = (*(ctx->source->scan))(ctx->source, position, ctx->s.type, right,
			       ctx->s.not_in_word);
    DisplayText(ctx, ctx->s.right, newPos, TRUE);
    ctx->s.right = newPos;
  }
  else if (position < ctx->s.left) {
    newPos = (*(ctx->source->scan))(ctx->source, position, ctx->s.type, left,
			       ctx->s.not_in_word);
    DisplayText(ctx, newPos, ctx->s.left, TRUE);
    ctx->s.left = newPos;
  }
  else /* inside selection - determine which end */
    if (position >= ctx->s.left+((ctx->s.right-ctx->s.left)/2)) {
      newPos = (*(ctx->source->scan))(ctx->source, position, ctx->s.type, 
			right, ctx->s.not_in_word);
      DisplayText(ctx, newPos, ctx->s.right, FALSE);
      ctx->s.right = newPos;
    }
    else {
      newPos = (*(ctx->source->scan))(ctx->source, position, ctx->s.type, left,
				 ctx->s.not_in_word);
      DisplayText(ctx, ctx->s.left, newPos, FALSE);
      ctx->s.left = newPos;
    }
  if (ctx->i.type != append)
      ctx->i.position = ctx->s.right;
  if (ctx->i.type != none)
	InsertCursor(ctx, on);
}

#ifdef PASTESELECT
static PasteSelection(ctx, position)
  TextSWContext *ctx;
  TTextPosition position;
{
    TTextPosition origpos, startPos, endPos, updateFrom, updateTo, f, t;
    TTextBlock text;
    int delta;
    if (position > ctx->s.left && position < ctx->s.right)
	return;
    DisplayText(ctx, ctx->s.left, ctx->s.right, FALSE);
    startPos = ctx->s.left;
    updateFrom = 99999;
    updateTo = 0;
    origpos = position;
    while (startPos < ctx->s.right) {
	startPos = (*(ctx->source->read))(ctx->source, startPos, &text,
		ctx->s.right - startPos);
	/** paste in the new text **/
	delta = ReplaceText(ctx, position, position, &text, &f, &t);
	if (f < updateFrom)
	    updateFrom = f;
	if (t > updateTo)
	    updateTo = t;
	position += delta;
	if (ctx->s.left >= origpos) {
	    startPos += delta;
	    ctx->s.right += delta;
	}
    }
    ctx->i.position = position + 1;
    DisplayText(ctx, updateFrom, updateTo, FALSE);
    ctx->s.left = origpos;
    ctx->s.right = position;
    DisplayText(ctx, ctx->s.left, ctx->s.right, TRUE);
    if (ctx->i.type != none)
	InsertCursor(ctx, on);
}
#endif PASTESELECT

static KillSelection(ctx)
  TextSWContext *ctx;
{
    TTextBlock text;
    TTextPosition updateFrom, updateTo;
    int i;
    if (ctx->i.position >= ctx->s.left && ctx->i.position <= ctx->s.right)
	ctx->i.position = ctx->s.left;
    text.length = 0;
    i = ReplaceText(ctx, ctx->s.left, ctx->s.right, &text, 
		&updateFrom, &updateTo);
    if (ctx->i.position > ctx->s.left)
	ctx->i.position -= i;
    ctx->s.left = ctx->s.right = 0;
    DisplayText(ctx, updateFrom, updateTo, FALSE);
}


static void ClearWindow (ctx)
  TextSWContext *ctx;
{
  XPixSet(ctx->w, 0, 0, 9999, 9999, WhitePixel);
}


static DisplayTextSW (ctx)
  TextSWContext *ctx;
{
    ClearWindow(ctx);
    BuildLineTable(ctx, ctx->lt.top);
    DisplayText(ctx, ctx->lt.top, ctx->s.left, FALSE);
    DisplayText(ctx, max(ctx->s.left, ctx->lt.top), ctx->s.right, TRUE);
    DisplayText(ctx, max(ctx->s.right, ctx->lt.top),
	    (*(ctx->source->getLastPos))(ctx->source), FALSE);
    if (ctx->i.type != none)
	InsertCursor(ctx, on);
}


CheckResizeHeight(ctx)
  TextSWContext *ctx;
{
    TTextPosition endPos;
    int visible,line;
    WindowBox rbox, abox;
    if (ctx->options & resizeHeight) {
	endPos = (*(ctx->source->getLastPos))(ctx->source);
	visible = IsPositionVisible(ctx, endPos);
	line = LineForPosition(ctx, endPos);
	if (visible)
	    line = LineForPosition(ctx, endPos);
	else
	    line = ctx->lt.lines;
	if (line + 1 != ctx->lt.lines) {
#ifdef SCROLLBAR
	    rbox.x = 0;
	    rbox.y = 0;
	    rbox.w = ctx->width;
	    rbox.h = (*(ctx->sink->maxHeight)) (ctx->sink, line + 1) 
						+ (2 * yMargin) + 2;
	    TMakeGeometryRequest(ctx->w, resize, &rbox, &abox);
#endif
	}
    }
}


/** 
 ** This routine handles a text key (as opposed to a mouse button) 
 ** being hit. 
 **/
static void ProcessTextKey (event, ctx)
  XEvent *event;
  TextSWContext *ctx;
{
    int     nbytes;
    TTextPosition updateFrom, updateTo, endPos;
    TTextBlock text;

    text.ptr = XLookupMapping(event, &nbytes);
    if (nbytes == 0) return;
    text.length = nbytes;
    if (!(ctx->options & editable)) {
	XFeep(0);
	return;
    }
    if (ctx->i.type != none)
	InsertCursor(ctx, off);
    DisplayText(ctx, ctx->s.left, ctx->s.right, FALSE);

    switch (text.ptr[0]) {
	case BS: 
	    text.length = 0;
	    if (ctx->i.position > 0) {
		endPos = ctx->i.position;
		ctx->i.position = (*(ctx->source->scan))(ctx->source,
			ctx->i.position - 1, charSelection, left, 
			ctx->s.not_in_word);
		ReplaceText(ctx, ctx->i.position, endPos, &text,
			&updateFrom, &updateTo);
	    }
	    break;
	case DEL: 
	    text.length = -1;
	    if (ctx->i.position > 0) {
		endPos = ctx->i.position;
		/** deleting the first character is a special case **/
		if (ctx->i.position == 1)
			ctx->i.position = 0;
		else
			ctx->i.position = (*(ctx->source->scan))(ctx->source,
				ctx->i.position - 1, charSelection, left, 
				ctx->s.not_in_word) + 1;
		ReplaceText(ctx, ctx->i.position, endPos, &text,
			&updateFrom, &updateTo);
	    }
	    break;
	case '\014': 		/* ^L */
	    ForceBuildLineTable(ctx);
	    DisplayTextSW(ctx);
	    return;

	case CR:
	    text.ptr[0] = '\n';
	    ReplaceText(ctx, ctx->i.position, ctx->i.position, &text,
		    &updateFrom, &updateTo);
	    for (; text.length > 0; text.length--)
		ctx->i.position =
		    (*(ctx->source->scan))(ctx->source, ctx->i.position,
			charSelection, right, ctx->s.not_in_word);
	    break;

	default: 
	    ReplaceText(ctx, ctx->i.position, ctx->i.position, &text,
		    &updateFrom, &updateTo);
	    for (; text.length > 0; text.length--)
		ctx->i.position =
		    (*(ctx->source->scan))(ctx->source, ctx->i.position,
			charSelection, right, ctx->s.not_in_word);
	    break;

    }
    DisplayText(ctx, updateFrom, updateTo, FALSE);
    DisplayText(ctx, ctx->s.left, ctx->s.right, TRUE);
    if (ctx->i.type != none)
	InsertCursor(ctx, on);
    CheckResizeHeight(ctx);
    /*** if the text is notify, call the user routine **/
    if (index(ctx->notify.symbols, text.ptr[0]))
	(*(ctx->notify.routine)) (ctx->notify.tag);

}


/**
 ** This routine handles a user hitting a mouse button 
 **/
static void ProcessTextButtons (event, ctx)
  XEvent *event;
  TextSWContext *ctx;
{
    Window w, subW;
    int     x, y;
    TTextPosition position;
    XButtonEvent * buttonEvent;
    int     state;
    Status status;
    Cursor cursor;

    w = event->window;
    buttonEvent = (XButtonEvent *) event;
    switch (event->type) {
	case ButtonPressed: 
	case MouseMoved: 
	    position = PositionForXY(ctx, buttonEvent->x, buttonEvent->y);
	    if (  (position > 0) 
 	       && (position < (*(ctx->source->getLastPos))(ctx->source)) 
	       && (buttonEvent->x > ctx->lt.entry[LineForPosition(ctx,
			    position-1)].endX))
		position--;
	    if (event->type == ButtonPressed) {
		switch (buttonEvent->detail & ValueMask) {
		    case LeftButton: 
			DoSelection(ctx, position, buttonEvent->time, FALSE);
		        if (ctx->s.mutual_exclusion) 
		        	mutual_exclusion_selection(ctx);
			break;
		    case MiddleButton:
			if (buttonEvent->detail & ShiftMask) 
			    KillSelection(ctx);
#ifdef PASTESELECT
			else
			    PasteSelection(ctx, position);
#endif
			break;
		    case RightButton: 
			ExtendSelection(ctx, position);
			break;
		}
#ifdef CURSORS
		cursor = GetCursor("left_ptr");
#else
                cursor = XCreateCursor(left_ptr_width, left_ptr_height,
                            left_ptr_bits, left_ptr_mask_bits,
                            left_ptr_x_hot, left_ptr_y_hot,
                            BlackPixel, WhitePixel, GXcopy);
#endif
		status = TGrabMouse(w, cursor, buttonMask);
	    }
	    else {		/* must be mouse motion */
		state = ((XMouseMovedEvent *) event)->detail;
		if (state & LeftMask) {
		    DoSelection(ctx, position, buttonEvent->time, TRUE);
		    if (ctx->s.mutual_exclusion) 
			    mutual_exclusion_selection(ctx);
		} else
		    if (state & RightMask)
			ExtendSelection(ctx, position);
	    }
	    break;
	case ButtonReleased: 
	    TUngrabMouse();
	    break;
    }
}


/***
 *** handles expose region event.
 ***/
static ProcessExposeRegion(event, ctx)
  XExposeRegionEvent *event;
  TextSWContextPtr ctx;
{
    TTextPosition pos1, pos2, resultend;
    int     line, y;
    LineTableEntryPtr entry;

    /** figure out starting line that was exposed **/
    line = LineForPosition(ctx, PositionForXY(ctx, event->x, event->y));
    while (line < ctx->lt.lines && ctx->lt.entry[line + 1].y < event->y)
	line++;
    while (line < ctx->lt.lines) {
	entry = &(ctx->lt.entry[line]);
	if (entry->y >= event->y + event->height)
	    break;
	(*(ctx->sink->resolve)) (ctx->sink, ctx->source, entry->position, 
		entry->x, event->x - entry->x, &pos1, &resultend);
	(*(ctx->sink->resolve)) (ctx->sink, ctx->source, entry->position, 
		entry->x, event->x + event->width - entry->x, &pos2, 
		&resultend);
	pos2++;
	if (pos1 < ctx->s.right && pos2 > ctx->s.left) {
	    DisplayText(ctx, pos1, ctx->s.left, FALSE);
	    DisplayText(ctx, max(pos1, ctx->s.left), min(pos2, ctx->s.right),
		    TRUE);
	    DisplayText(ctx, ctx->s.right, pos2, FALSE);
	}
	else
	    DisplayText(ctx, pos1, pos2, FALSE);
	line++;
    }
    if (ctx->i.type != none)
	InsertCursor(ctx, on);
}

/**
 ** This routine handles all text sub window events 
 **/
static int ProcessTextSWEvent (event)
  XEvent *event;
{
    TextSWContext *ctx;
    int     err, returnCode;
    WindowInfo info;

    err = FindEntry(event->window, myContext, &ctx);
    if (err != ERRNONE)
	return(NOTHANDLED);
    returnCode = PROCESSED;
    switch (event->type) {
	case ButtonPressed: 
	case ButtonReleased: 
	case MouseMoved: 
	    ProcessTextButtons(event, ctx);
	    break;
	case KeyPressed:
	    ProcessTextKey(event, ctx);
	    break;
	case ResizeWindow:
	    ctx->width = ((XExposeWindowEvent *) event)->width;
	    ctx->height = ((XExposeWindowEvent *) event)->height;
	    ForceBuildLineTable(ctx);
	    break;
	case ExposeWindow:
	    if (ctx->width  != ((XExposeWindowEvent *) event)->width ||
	        ctx->height != ((XExposeWindowEvent *) event)->height) {
		    ctx->width  = ((XExposeWindowEvent *) event)->width;
		    ctx->height = ((XExposeWindowEvent *) event)->height;
		    ForceBuildLineTable(ctx);
	    } else 
		if (event->subwindow == 0) DisplayTextSW(ctx);
	    break;
	case ExposeRegion: 
	    ProcessExposeRegion((XExposeRegionEvent *) event, ctx);
	    break;
        case ExposeCopy:
            break; /* work already done by ExposeRegion events */
	default: 
	    returnCode = 1;
    }
    return(returnCode);
}


/*** 
 *** Given a window context and a byte offset into the source, this routine
 *** will make the byte offset the starting position of the displayed text.
 ***/
static void TextReposition(ctx, startPos)
  TextSWContext *ctx;
  TTextPosition startPos;
{
        TTextBlock text;

	ctx->lt.top = (*(ctx->source->scan))(ctx->source, startPos, 
			lineSelection, left, ctx->s.not_in_word);
	ctx->s.left = ctx->s.right = 0;	  
	ForceBuildLineTable(ctx);
	DisplayTextSW(ctx);
}


/***
 *** highlight a region of text.
 ***/
static void HighlightText(ctx, pos)
  TextSWContext *ctx;
  TTextPosition *pos;
{
  TTextPosition nleft, nright;
 
  nleft = pos[0];
  nright = pos[1] + 1;
  if (nleft < ctx->s.left)
      DisplayText(ctx, nleft, min(nright, ctx->s.left), TRUE);
  if (nleft > ctx->s.left)
      DisplayText(ctx, ctx->s.left, min(nleft, ctx->s.right), FALSE);
  if (nright < ctx->s.right)
      DisplayText(ctx, max(nright, ctx->s.left), ctx->s.right, FALSE);
  if (nright > ctx->s.right)
      DisplayText(ctx, max(ctx->s.right, nleft), nright, TRUE);
  ctx->s.left = nleft;
  ctx->s.right = nright;
}

/***
 *** This routine passes back the string that is currently selected for
 *** a given window.  If the window passed in is part of a mutually exclusive
 *** set of windows, the routine bassed back the string that is currently
 *** selected for the set (ie. the selected string may be from a different 
 *** window)
 ***/
static char * GetHighlightText(ctx)
    TextSWContext *ctx;
{
    char   *result;
    TTextBlock text;
    TTextPosition end, nend;

    /*** find the ctx structure of window with text selection ***/
    if (ctx->s.mutual_exclusion) 
	    if (FindEntry(window_with_selection, myContext,&ctx) != ERRNONE) 
		return 0;

    /*** get the text and put it into "result" ***/
    result = (char *) Tmalloc(ctx->s.right - ctx->s.left +1);
    end = (*(ctx->source->read))(ctx->source, ctx->s.left, &text,
	    ctx->s.right - ctx->s.left);
    strncpy(result, text.ptr, ctx->s.right - ctx->s.left);
    result[ ctx->s.right - ctx->s.left ] = '\0';
    while (end < ctx->s.right) {
	nend = (*(ctx->source->read)) (ctx->source, end, &text, 
			ctx->s.right - end);
	strncat(result, text.ptr, ctx->s.right - end);
	end = nend;
    }
    return result;
}


/***
 *** This routine will replace text in within a source. 
 *** The starting and ending position will determine whether or not to
 *** insert the text or to write over the older text.  
 *** NOTE: This does nothing on a regular DiskSource.
 ***/
static void ApplicationReplacesText(ctx, replace)
  TextSWContext *ctx;
  TTextReplace  *replace;
{
    TTextPosition updateFrom, updateTo;

    if (ctx->i.type != none)
	InsertCursor(ctx, off);

    ReplaceText(ctx, replace->startPos, replace->endPos, &replace->text, 
		&updateFrom, &updateTo);

    /*** set the insert position ***/
    if (ctx->i.type == append)
	ctx->i.position = (*(ctx->source->getLastPos))(ctx->source);
    else {						
        ctx->i.position = replace->startPos;
        for (; replace->text.length > 0; replace->text.length--)
	    ctx->i.position = (*(ctx->source->scan))(ctx->source, 
			     ctx->i.position, charSelection, right, 
			     ctx->s.not_in_word);
    }

    /*** display the text ****/
    if (updateFrom < ctx->s.right && updateTo > ctx->s.left) {
	DisplayText(ctx, updateFrom, ctx->s.left, FALSE);
	DisplayText(ctx, max(updateFrom, ctx->s.left),
		    min(updateTo, ctx->s.right), TRUE);
	DisplayText(ctx, ctx->s.right, updateTo, FALSE);
    } else 
	DisplayText(ctx, updateFrom, updateTo, FALSE);
    if (ctx->i.type != none)
	InsertCursor(ctx, on);
}


/***
 *** This routine will unhighlight the text for a given window.  If the
 *** window is part of a mutually exclusive set, then the hightlighted
 *** text from the set will be unlighthighted (ie. It may be found in a
 *** different window).
 ***/
static UnhighlightText(ctx)
   TextSWContext *ctx;
{
    /*** find the ctx structure with the selected text ***/
    if (ctx->s.mutual_exclusion) 
	    if (FindEntry(window_with_selection, myContext,&ctx) != ERRNONE) 
		return 0;

    DisplayText(ctx, ctx->s.left, ctx->s.right, FALSE);
    ctx->s.left = ctx->s.right = 0;

}


/***
 *** This routine lets the application change the insertion position.
 *** It turns off the old position, and turns on the new position.
 ***/
static setInsertPos(ctx, pos)
  TextSWContext *ctx;
  TTextPosition pos;
{
   if (ctx->i.type != none)
	InsertCursor(ctx, off);
   ctx->i.position = pos;
   if (ctx->i.type != none)
	InsertCursor(ctx, on);
}


/***
 *** routine to let the application change the options on the text window.
 ***/
ChangeTextOptions(ctx, newOptions)
  TextSWContext *ctx;
  short newOptions;
{
   /*** this needs work ----- */
   /*** for now, only let them change editting status ***/
   if (newOptions & editable) {
        ctx->i.type = (enum InsertionType) (*(ctx->source->getEditType))();
	ctx->i.position = 0;
	if (ctx->i.type != none)
	   InsertCursor(ctx, on);
	ctx->options = ctx->options | editable;
   }
}


/*
 -------------------- Public routines ----------------------------
 */

/*** 
 ***  Given a window and an argument list, this routine will return the
 ***  corresponding values to each argument defined.
 ***/
Status TGetTextAttr(tw, arglist)
   Window tw;		/** tool window **/
   Targ *arglist;
{
    TextSWContext *ctx;
    WindowInfo info;
    int error;

    error = FindEntry(tw, myContext, &ctx);
    if (error != ERRNONE)
	return(NOTHANDLED);

    /***
     *** Parse input parameters.
     ***/
    while (arglist->name) {
	switch (arglist->name) {
	case T_TEXT_TOP:
   	   arglist->data = (caddr_t) ctx->lt.top;
	   break;
	case T_TEXT_SEL_TYPE:
	   arglist->data = (caddr_t) ctx->s.type;
	   break;
	case T_TEXT_SEL_MUTUAL_EXCLUDE:
	   arglist->data = (caddr_t) ctx->s.mutual_exclusion;
	   break;
	case T_TEXT_OPTIONS:
	   arglist->data = (caddr_t) ctx->options;
	   break;
	case T_TEXT_LEFT_MARGIN:
	   arglist->data = (caddr_t) ctx->leftmargin;
	   break;
 	case T_TEXT_INSERT_POS:
	   arglist->data = (caddr_t) ctx->i.position;
 	   break;
	case T_TEXT_WORD_DELIM:
	   arglist->data = (caddr_t) ctx->s.not_in_word;
	   break;
	case T_TEXT_NOTIFY_SYMBOLS:
	   arglist->data = (caddr_t) ctx->notify.symbols;
	   break;
	case T_TEXT_NOTIFY_ROUTINE:
	   arglist->data = (caddr_t) ctx->notify.routine;
	   break;
	case T_TEXT_NOTIFY_TAG:
	   arglist->data = (caddr_t) ctx->notify.tag;
	   break;
	case T_TEXT_FONT_INFO:
	   arglist->data = (caddr_t) ctx->sink;
	   break;
	case T_TEXT_SOURCE:
	   arglist->data = (caddr_t) ctx->source;
	   break;
	case T_TEXT_HIGHLIGHT:
	   arglist->data = (caddr_t) GetHighlightText(ctx);
	   break;
	case T_TEXT_WIDTH:	
	   XQueryWindow(ctx->w, &info);
	   arglist->data = (caddr_t) info.width;
	   break;
	case T_TEXT_HEIGHT:
	   XQueryWindow(ctx->w, &info);
	   arglist->data = (caddr_t) info.height;
	   break;
	case T_TEXT_BRWIDTH:
	   XQueryWindow(ctx->w, &info);
	   arglist->data = (caddr_t) info.bdrwidth;
	   break;
	default:
	   break;
	}
	arglist++;
    }		

}  


/***
 *** Given a window and an argument list, this routine performs the necessary
 *** operations to set the values of the defined arguments.
 *** NOTE: the operations are performed in the order that the arguments
 ***	   appear in the argument array.
 ***/
Status TSetTextAttr(tw, arglist)
    Window tw;		/** tool window **/
    Targ *arglist;
{
    TextSWContext *ctx;
    WindowInfo info;
    int error;

    /*** 
     *** Get the window context.
     ***/
    error = FindEntry(tw, myContext, &ctx);
    if (error != ERRNONE)
	return(NOTHANDLED);

    /***
     *** Parse input parameters.
     ***/
    while (arglist->name) {
	switch (arglist->name) {
	case T_TEXT_TOP:
 	   TextReposition(ctx, (TTextPosition) arglist->data);
	   break;
	case T_TEXT_SEL_TYPE:
	   ctx->s.type = (enum SelectionType) arglist->data;
	   break;
	case T_TEXT_SEL_MUTUAL_EXCLUDE:
	   ctx->s.mutual_exclusion = (int) arglist->data;
	   break;
	case T_TEXT_OPTIONS:
	   ChangeTextOptions(ctx, (short) arglist->data);
	   break;
 	case T_TEXT_INSERT_POS:
	   setInsertPos(ctx, (TTextPosition) arglist->data + 1);
	   break;
	case T_TEXT_WORD_DELIM:
	   ctx->s.not_in_word = (char *) arglist->data;
	   break;
	case T_TEXT_NOTIFY_SYMBOLS:
	   ctx->notify.symbols = (char *) arglist->data;
	   break;
	case T_TEXT_NOTIFY_ROUTINE:
	   ctx->notify.routine = (int(*)()) arglist->data;
	   break;
	case T_TEXT_NOTIFY_TAG:
	   ctx->notify.tag = (caddr_t) arglist->data;
	   break;
	case T_TEXT_SOURCE:
	   NewTextSource(ctx, (TTextSource *) arglist->data, 0);
	   break;
	case T_TEXT_HIGHLIGHT:
	   HighlightText(ctx, (int *) arglist->data);
	   break;
	case T_TEXT_UNHIGHLIGHT:
	   UnhighlightText(ctx);
	   break;
	case T_TEXT_REPLACE:
	   ApplicationReplacesText(ctx, (TTextReplace *) arglist->data);
	   break;
	case T_TEXT_LEFT_MARGIN:
	   ctx->leftmargin = abs((short)arglist->data);
	   ForceBuildLineTable(ctx);
	   DisplayTextSW(ctx);
 	   break;
	case T_TEXT_WIDTH:	
	   if ((int)arglist->data == 0) break;
	   XQueryWindow(ctx->w, &info);
	   XChangeWindow(ctx->w, abs((int)arglist->data), info.height);
  	   break;
	case T_TEXT_HEIGHT:
	   if ((int)arglist->data == 0) break;
	   XQueryWindow(ctx->w, &info);
	   XChangeWindow(ctx->w, info.width, abs((int)arglist->data));
	   break;
	case T_TEXT_BRWIDTH:
	   break;
	case T_TEXT_FONT_INFO:
	   break;
	default:
	   break;
	}
	arglist++;
    }		
}

/***
 *** Routine to set up the window context record, save the entry, and
 *** set xevents that need to be caught.
 ***/
Window TCreateTextSW (pw, arglist)
  Window pw;		/** parent window **/
  Targ   *arglist;

{
    TextSWContext *ctx;
    int     err;
    int     x, y, width, height, br_width;

    if (initialized == 0) initialize();
    ctx = (TextSWContext *) Tmalloc(sizeof(TextSWContext));

    /***
     *** set defaults 
     ***/
    ctx->i.position 	    = 0;
    ctx->lt.top 	    = 0;
    ctx->lt.entry 	    = NULL;
    ctx->s.left 	    = 0;
    ctx->s.right 	    = 0;
    ctx->s.type 	    = charSelection;
    ctx->s.not_in_word      = "";
    ctx->s.mutual_exclusion = 0;
    ctx->i.type 	    = none;
    ctx->notify.symbols    = "";
    ctx->notify.routine    = NULL;
    ctx->notify.tag	    = NULL;
    ctx->options 	    = 0;
    ctx->leftmargin 	    = xMargin;
    ctx->sbar 		    = 0;
    ctx->sink 		    = defaultSink;
    x = y 		    = 0;
    height = width 	    = 200;
    br_width 		    = 2;

    /***
     *** Parse input parameters.
     ***/
    while (arglist->name) {
	switch (arglist->name) {
	case T_TEXT_TOP:
	    ctx->lt.top = (TTextPosition) arglist->data;
 	    break;
	case T_TEXT_SEL_TYPE:
	    ctx->s.type = (enum SelectionType) arglist->data;
	    break;
	case T_TEXT_SEL_MUTUAL_EXCLUDE:
	    ctx->s.mutual_exclusion = (int) arglist->data;
	    break;
	case T_TEXT_OPTIONS:
	    ctx->options = (short) arglist->data;
	    ctx->i.type  = (ctx->options & editable) 
			     ? (*(ctx->source->getEditType)) ()
			     : none;
	    break;
	case T_TEXT_LEFT_MARGIN:
	    ctx->leftmargin = abs((short) arglist->data);
	    break;
 	case T_TEXT_INSERT_POS:
	    ctx->i.position = (TTextPosition) arglist->data;
	    break;
	case T_TEXT_WORD_DELIM:
	    ctx->s.not_in_word = (char *) arglist->data;
	    break;
	case T_TEXT_NOTIFY_SYMBOLS:
	    ctx->notify.symbols = (char *) arglist->data;
	    break;
	case T_TEXT_NOTIFY_ROUTINE:
	    ctx->notify.routine = (int(*)()) arglist->data;
	    break;
	case T_TEXT_NOTIFY_TAG:
	    ctx->notify.tag = (caddr_t) arglist->data;
	    break;
	case T_TEXT_FONT_INFO:
	    ctx->sink = (TTextSink *) 
			 TCreateAsciiSink((FontInfo *) arglist->data);
	    break;
	case T_TEXT_SOURCE:
	    ctx->source = (TTextSource *) arglist->data;
	    break;
	case T_TEXT_WIDTH:
	    width = abs((int)arglist->data);
	    break;
	case T_TEXT_HEIGHT:
	    height = abs((int)arglist->data);
	    break;
	case T_TEXT_BRWIDTH:
	    br_width = abs((int) arglist->data);
	    break;
	case T_TEXT_HIGHLIGHT:
	    break;
	default:
	    break;
	}
	arglist++;
    }		

    if (ctx->source == NULL) 
	ctx->source = (TTextSource *) TCreateStringSource(" ", 1);


    /*** create the window ***/
    ctx->w = XCreateWindow(pw, x, y, width, height, br_width, 
			   BlackPixmap, WhitePixmap);

    err = SaveEntry(ctx->w, myContext, ctx);
    TSetXEventDispatch(ctx->w, ProcessTextSWEvent, TEXT_EVENTS, 0);
    return ctx->w;
}


/***
 *** get rid of text sub window 
 ***/
void TDestroyTextSW (w)
  Window w;
{
    TextSWContext *ctx;
    int     err;

    err = FindEntry(w, myContext, &ctx);
    if (err == ERRNONE) {
	DeleteEntry(w, myContext);
	if (w != ctx->w) {
	    DeleteEntry(ctx->w, myContext);
#ifdef SCROLLBAR
	    TUnarrangeWithScrollBars(w);
#endif
	}
	TDestroyWindow(w);
	free(ctx);
    }
}

