/*===========================================================================
  Copyright (C) 1986-2009 European Southern Observatory (ESO)
 
  This program is free software; you can redistribute it and/or 
  modify it under the terms of the GNU General Public License as 
  published by the Free Software Foundation; either version 2 of 
  the License, or (at your option) any later version.
 
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
 
  You should have received a copy of the GNU General Public 
  License along with this program; if not, write to the Free 
  Software Foundation, Inc., 675 Massachusetss Ave, Cambridge, 
  MA 02139, USA.
 
  Corresponding concerning ESO-MIDAS should be addressed as follows:
	Internet e-mail: midas@eso.org
	Postal address: European Southern Observatory
			Data Management Division 
			Karl-Schwarzschild-Strasse 2
			D 85748 Garching bei Muenchen 
			GERMANY
===========================================================================*/

/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

.TYPE		Module
.IDENTIFICATION twout.c
.AUTHOR    	Francois Ochsenbein [ESO-IPG]
.LANGUAGE  	C
.KEYWORDS	Window Management
.ENVIRONMENT	TermWindows
.COMMENTS	This module contains routines to perform output on windows.
\begin{TeX}
\end{TeX}

.VERSION  1.0	18-Aug-1986:	Creation

.VERSION  2.0	03-Dec-1986	New terminal-independant graphic characters
			with output buffering version.
.VERSION  2.1	29-Jun-1987: Extracted from Full Version SM_ZUOUT +
.VERSION  2.2	04-Dec-1987: For Usage with Proteus:
			if a window is Removed and Active,
			which should normally be incompatible,
			the window is Refreshed.
.VERSION 3.0	20-Mar-1988: Version '3'
.VERSION 3.1	12-Dec-1988: SetAttr(_ATTRIBUTE_) takes attribute of Cursor.
				(but without graphic)
.VERSION 3.2	20-Feb-1989: SetAttr returns Previous Attribute
.VERSION 3.3	31-Mar-1989: Added tw_wa (Write Achars).
		Corrected tw_line
.VERSION 3.4	16-Jun-1989: Corrected Bug in tw_write (don't limit between
				markers...)
.VERSION 3.5	29-Jun-1989: Added tw_rule, tw_mrule
.VERSION 3.6	08-Mar-1990: Modified tw_r (Refresh)
.VERSION 3.7	28-May-1990: Allow tw_out for hard terminals
.VERSION 3.8	06-Sep-1990: Corrected bug in tw_box (bad box output).
			Also, corrected tw_attr which was bad
			when Window = Screen
.VERSION 3.9	28-Nov-1990: Keep current attribute in tw_write even when
			SI / SO (shift in/out) are used.

 090901         last modif
-----------------------------------------------------------------*/

#define		DEBUG		0	/* For debugging only		*/

#define PM_LEVEL 	LEVEL_TW

#include <midas_def.h>


#define  TW_import	0	/* Do not use macro window definitions	*/
#include <twset.h>
#include <atype.h>


extern int tv_buffer(), tv_attr(), tv_goto(), tv_cl(), tv_wa();
extern int tv_rb(), tv_clear();
extern int tv_agoto(), tv_mvc(), tv_bell();

extern int tw_fill(), tw_attr(), tw_uattr(), tw_r(), tw_goto(), tw_rule();
extern int tw_st(), tw_chars(), tw_copw(), tw_flags();
extern int tw_line(), tw_uw(), tw_write(), tw_clear(), tw_dc();
extern int tw_nl(), tw_upw(), tw_uc(), tw_mvc(), tw_copy();

extern int pm_enter(), pm_iexit(), pm_tr2(), pm_ed_i();
extern int eh_ed_str2();


MID_EXTERN	WINDOW *Screen;

#if 	DEBUG
#define ed_level	3
#define ENTER_DEBUG(x)	ENTER(x)
#define EXIT_DEBUG(x)	EXIT(x)
#define DISPLAY(opt)	tw_ed(w,opt)
#else
#define DISPLAY(opt)
#define ENTER_DEBUG(x)	
#define EXIT_DEBUG(x)	return(x)
#endif

static int  J;
static char hard_term = 0;	/* Used by tw_out */

	MONITOR(TWOUT);


/*==========================================================================
 *			tw_attr
 *==========================================================================*/
int	tw_attr(w, val)
/*+++
.PURPOSE Set attributes for the next output to window
.RETURNS The previous Attribute.
.REMARKS Attributes are defined as numbers to be OR'ed in termdef.h.
	The cursor position remains unchanged.
	The _ATTRIBUTE_ definition asks for the attribute of the
	current cursor position.
	Note that the asked attributes are XOR'ed with the initial
	attributes, to ensure that a not-null `val' parameter
	will result in some `stand-out' mode.
---*/
	WINDOW *w;		/* MOD: The window concerned	*/
	int val;		/* IN: Desired screen attributes	*/
{
	register int new_attr, old_attr, line;

  ENTER_TW("tw_attr");
  CheckScreen(w);

  DISPLAY(0);
  TRACE_ED_I("Asked attributes: ", val);

  old_attr = w->attr;

  if (val == _ATTRIBUTE_)	/* Take the attribute of the Cursor */
  {	J = w->Nj;
  	line = w->pos/J;
  	if (line >= w->Ni)	line = w->Ni - 1;
  	new_attr = ((*Aij(w, line, w->pos%J))&0xff00) | ' ';
  	new_attr &= ~(_GRAPHICS_<<8);
  }
  else	new_attr = val & (_GRAPHICS_|_BOLD_|_UNDERSCORE_|_BLINK_|_REVERSE_),
  	new_attr = (new_attr << 8) ^ w->attr_init;

  if ((w->active) && (Screen->attr != new_attr))
  {	Screen->attr = new_attr;
	SetAttr((new_attr) >> 8);
  }

  old_attr = (int)(w->attr_init^old_attr)>>8 ;
  w->attr  = new_attr;

  DISPLAY(0);
  EXIT_TW(old_attr);
}
 
/*======================================================================
 *			tw_box
 *======================================================================*/
int tw_box(w,topl,topc,boxl,boxc)
/*+++
.PURPOSE Draw a box on a window (with current attribute)
	and position the cursor at top left corner.
.RETURNS OK / NOK (impossible)
.REMARKS Value of 0 for box dimensions assume all usable space in the window.
	The cursor does not move if the box cannot be drawn.
---*/
	WINDOW *w;		/* MOD: The window concerned	*/
	int topl; /* IN: Line of upper-left corner (<0 for bottom adj.) */
	int topc; /* IN: Col. of upper-left corner (<0 for right adj.) */
	int boxl; /* IN: Line width of box (0 for complete, <0 for Centered) */
	int boxc; /* IN: Col. width of box (0 for complete, <0 for Centered) */
{
	short int wh[2], wd[2];	/* Final position & dimension of box */

  ENTER_TWA("tw_box");

#if DEBUG
	TRACE_ED_STR2("Window  =",w->id, 8);
	TRACE_ED_I("Top line=",topl);
	TRACE_ED_I("Top col.=",topc);
	TRACE_ED_I("Ext line=",boxl);
	TRACE_ED_I("Ext col.=",boxc);
#endif

  wh[0] = topl;		wh[1] = topc;
  wd[0] = boxl;		wd[1] = boxc;

		/* Adjust box dimensions	*/

  if_not(status = CheckBox(wh, wd, w->dim))	Return;
  if ((wd[0] < 2) || (wd[1] < 2))
  {	ERR_ED_STR2("Box requires more space on Window ", w->id, 8);
  	status = NOK;
  	Return;
  }

#if DEBUG
	TRACE_ED_I("Kept Top line=",wh[0]);
	TRACE_ED_I("Kept Top col.=",wh[1]);
	TRACE_ED_I("Kept Ext line=",wd[0]);
	TRACE_ED_I("Kept Ext col.=",wd[1]);
#endif

	/* Vertical Lines	*/
	
  tw_goto(w, wh[0], wh[1]), 		tw_rule(w, _DOWN_, wd[0]);
  tw_goto(w, wh[0], wh[1] + wd[1]-1), 	tw_rule(w, _DOWN_, wd[0]);

	/* Horizontal Lines	*/
	
  tw_goto(w, wh[0], wh[1]), 		tw_rule(w, _RIGHT_, wd[1]);
  tw_goto(w, wh[0] + wd[0]-1, wh[1]), 	tw_rule(w, _RIGHT_, wd[1]);

	/* Position Cursor	*/
	
  tw_goto(w, wh[0]+1, wh[1] + 1);

  DISPLAY(1);

  EXIT_TWA(status);
}

/*======================================================================
 *			tw_rule
 *======================================================================*/
static ACHAR modtee(achar, pa)
/*+++
.PURPOSE Transforms Tee symbol into correct one.
.RETURNS Correct 
.REMARKS 
---*/
	ACHAR	achar;	/* IN: The Tee character 	*/
	ACHAR	*pa[4];	/* IN: Neighbour characters	*/
{
	ACHAR	attr, new;
	int	k, i, n, x;
	static	char cc[4] = { 	CornerChar(_UPLEFT_),  CornerChar(_UPRIGHT_), 
			  	CornerChar(_DOWNLEFT_),CornerChar(_DOWNRIGHT_)};

  attr = achar & 0xff00;
  new  = achar & 0x00ff;
  
  k = new - TeeChar(0);
  
	/* Convert Tee */
  
  for (i = k^2, n = 2; (--n >= 0) && ((int)new >= TeeChar(0)); i ^= 1)
  {
	if(isGraphic(*pa[i])) 
	{ 	x = (*pa[i] & 0x00ff);
		if (x == CornerChar(_CROSS_))	continue;
  		if (x == RuleChar(i>>1))	continue;
  		if (x != TeeChar(i^1))		continue;
	}
	if (k > i)	x = k, k = i, i = x;
	x = (k<<1) | (i-2);
	new = cc[x];
	break;
  }  

  if ((int)new >= TeeChar(0))	/* Still a tee... */
  {
	i = k^1;		/* Char to look for */
	n = i >> 1;
	if(isGraphic(*pa[i])) 
	{ 	x = (*pa[i] & 0x00ff);
		if (x == CornerChar(_CROSS_))	goto _Found;
  		if (x == RuleChar(n))		goto _Found;
  		if (x != TeeChar(i))		goto _Found;
	}
	new = RuleChar(1^n);
  }  

  _Found:
  return(new | attr);
}

static ACHAR modcross(achar, pa)
/*+++
.PURPOSE Transforms symbol into coorect one.
.RETURNS Correct 
.REMARKS 
---*/
	ACHAR	achar;	/* IN: The graphic character 	*/
	ACHAR	*pa[4];	/* IN: Neighbour characters	*/
{
	ACHAR	attr, new;
	int	k, x;
	static  char allowed_corner[4][2] = {
		{ _UPLEFT_  , _UPRIGHT_  },	/* for UP	*/
		{ _DOWNLEFT_, _DOWNRIGHT_},	/* for DOWN	*/
		{ _UPLEFT_  , _DOWNLEFT_ },	/* for LEFT	*/
		{ _UPRIGHT_ , _DOWNRIGHT_},	/* for RIGHT	*/
	};

  attr = achar & 0xff00;
  new  = achar & 0x00ff;
  
  if (new != CornerChar(_CROSS_))	return(modtee(achar, pa));
  
	/* Convert Cross */

  for (k = 0; (k < 4) && (new == CornerChar(_CROSS_)); k++)
  {
	if(isGraphic(*pa[k])) 
	{ 	x = (*pa[k] & 0x00ff);
		if (x == CornerChar(_CROSS_))	continue;
  		if (x == RuleChar(k>>1))	continue;
  		if (x == CornerChar(allowed_corner[k][0]))	continue;
  		if (x == CornerChar(allowed_corner[k][1]))	continue;
	}
	new = TeeChar(k); 
	break;
  }
  if (k <= 1)  	new = modtee(new, pa);		/* Convert Tee */

  return(new | attr);
}

/*==========================================================================*/
int tw_rule(w, direction, len)
/*+++
.PURPOSE Writes a rule, horizontal or vertical
.RETURNS OK 
.REMARKS Uses the graphic character if it exists.
.REMARKS The cursor moves ONLY if _RIGHT_.
---*/
	WINDOW *w;		/* MOD: The window concerned	*/
	int direction;	/* IN: The direction(_UP_, _LEFT_, etc) of the rule */
	int len;	/* IN: Rule length, in characters	*/
{
	int 	i, l, lls, x, dpos, dir;
	short	apos[2], incr[2], lim[2];
	int	status;
	ACHAR 	or_attr, special, cross, adda, *pa, *pan[4];
	char	cc, crule;
	static	ACHAR ab = ' ';

  ENTER_TWA("tw_rule");

  status 	= OK;
	/* Keep current window Attributes for Box Drawing
	 */
  or_attr = (w->attr & (~HIGH)) | Agraphic;

  dir = direction; 
  l = len;
  if (len  < 0) 			/* Change direction		*/
  {	dir = 1^direction;
	l   = -len;
  }
  lls = l;

  i = dir >> 1;				/* Direction horiz./vertical 	*/
  if (i > 1)	dir = _RIGHT_, i = 0;	/* Default: Horizontal...	*/
  incr[0] = 0, incr[1] = 0, incr[i] = (dir & 1 ? 1 : -1);

  crule   = RuleChar(i);
  special = crule | or_attr;		/* Get the rule char	*/
  cross   = CornerChar(_CROSS_) | or_attr;

  x = w->pos;
  apos[0] = x / w->dim[1], apos[1] = x % w->dim[1];
  
  dpos = (dir != _RIGHT_ );		/* Cursor Movement at end 	*/
  
  for (; (--l >= 0) && (apos[i] >= 0) && (apos[i] < w->dim[i]); 
  		apos[i] += incr[i])
  {
  	x = apos[0] * w->dim[1] + apos[1];
	pa = Aij(w, apos[0], apos[1]);
  	adda = 0;
	cc = *pa & 0xff;
	if (cc == ' ')			adda = special;
	else if (isGraphic(*pa))	adda = (cc == crule ? special : cross);
	if (adda)	tw_fill(w, x, 1, adda);
  	if (dir == _RIGHT_)	dpos += 1;
  }

	/* Now, reorganize the corners */

  x = w->pos;
  apos[0] = x / w->dim[1], apos[1] = x % w->dim[1];
  lim[0] = w->dim[0] - 1;	lim[1] = w->dim[1] - 1;
  
  for (l = lls; (--l >= 0) && (apos[i] >= 0) && (apos[i] < w->dim[i]); 
  		apos[i] += incr[i])
  {
	pa = Aij(w, apos[0], apos[1]);
	adda = *pa;
	if(*pa != cross)	continue;
	pan[_UP_]    = (apos[0] > 0      ? Aij(w, apos[0]-1, apos[1]) : &ab);
	pan[_LEFT_]  = (apos[1] > 0      ? Aij(w, apos[0], apos[1]-1) : &ab);
	pan[_DOWN_]  = (apos[0] < lim[0] ? Aij(w, apos[0]+1, apos[1]) : &ab);
	pan[_RIGHT_] = (apos[1] < lim[1] ? Aij(w, apos[0], apos[1]+1) : &ab);
	adda = modcross(*pa, pan);
  	if (adda != *pa)	
		tw_fill(w, apos[0] * w->dim[1] + apos[1], 1, adda);
  }

  w->pos += dpos;
  if (w->pos >= w->marker[1])	w->pos = w->marker[1] - 1;

  EXIT_TWA(status);
}

/*==========================================================================*/
int tw_mrule(w, len)
/*+++
.PURPOSE Modifies the rule at current line.
.RETURNS OK 
.REMARKS Useful to terminate a table. On return, cursor on beginning of next
	line.
---*/
	WINDOW 	*w;		/* MOD: The window concerned	*/
	int	len;		/* IN: Length to look for	*/
{
	int 	i, j, l;
	short	lim[2];
	int	status;
	ACHAR 	adda, *pa, *pan[4];
	static	ACHAR ab = ' ';

  ENTER_TWA("tw_mrule");

  status 	= OK;

  
  lim[0] = w->dim[0] - 1, 	lim[1] = w->dim[1] - 1;
  i = w->pos / w->dim[1];	/* Line to Modify */
  j = w->pos % w->dim[1];	/* Col  to Modify */
  
	/* Now, reorganize the corners */

  for (l = len; (--l >= 0) && (j <= lim[1]); j++, w->pos += 1) 
  {
	pa = Aij(w, i, j);
	if_not(isGraphic(*pa))	continue;
	adda = *pa & 0x00ff;
	if((int)adda < CornerChar(_CROSS_))	continue;
	pan[_UP_]    = (i > 0      ? Aij(w, i-1, j) : &ab);
	pan[_LEFT_]  = (j > 0      ? Aij(w, i, j-1) : &ab);
	pan[_DOWN_]  = (i < lim[0] ? Aij(w, i+1, j) : &ab);
	pan[_RIGHT_] = (j < lim[1] ? Aij(w, i, j+1) : &ab);
	adda = modcross(*pa, pan);
  	if (adda != *pa)	
		tw_fill(w, i * w->dim[1] + j, 1, adda);
  }

  w->pos = (i+1)*w->dim[1];	/* Next Line */
  if (w->pos >= w->marker[1])	w->pos = w->marker[1] - 1;

  EXIT_TWA(status);
}

/*==========================================================================
 *			tw_cline
 *==========================================================================*/
int	tw_cline(w, str, len)
/*+++
.PURPOSE Write a string at current location, truncating the text
	if it is too long to stand on the current line,
	and adding blanks before / after to have it centered.
.RETURNS Length of output string
.REMARKS The string is output to the screen if the window is active.
	Non-printable chars are translated to Rubbish.
---*/
	WINDOW *w;		/* MOD: Window concerned	*/
	char   *str;		/* IN: String to output	*/
	int	len;		/* IN: String length	*/
{
	int 	 l, nb, old_pos;

  ENTER_TWA("+tw_cline");
  TRACE_STR2(str,len);

  old_pos = w->pos;

  l = w->Nj - w->pos % w->Nj;		/* Remaining length in line	*/
  if (l < 1)		FINISH;
  if (len > l)		len = l;
  nb = (l-len)/2; 	/* Leading  blanks */  
  tw_fill(w, w->pos, nb, w->attr);
  w->pos += nb;

  tw_line(w, str, len);

  nb = (l-len) - nb;	/* Trailing blanks */
  tw_fill(w, w->pos, nb, w->attr);
  w->pos += nb;  

  EXIT_TWA(w->pos - old_pos);
}

/*==========================================================================
 *			tw_line
 *==========================================================================*/
int	tw_line(w, str, len)
/*+++
.PURPOSE Write a string at current location, truncating the text
	if it is too long to stand on the current line.
.RETURNS Length of output string
.REMARKS The string is output to the screen if the window is active.
	Non-printable chars are translated to Rubbish.
---*/
	WINDOW *w;	/* MOD: Window concerned	*/
	char   *str;	/* IN: String to output	*/
	int	len;	/* IN: String length	*/
{
	register ACHAR *p;
	int 	 l, i, j;
	LINE	 *lm0, *lm1;
	ACHAR	 a, new;
	int	 chars[2];

  ENTER_TWA("+tw_line");

	/* First, copy text to window memory	*/

  status = 0;			/* Default returned length	*/
  if (len <= 0) 	FINISH;

  DISPLAY(0);
  TRACE_STR2(str,len);

  J = w->Nj, i = w->pos/J, j = w->pos%J;
  
  l = w->Nj - j;		/* Remaining length in line	*/
  if (l > len)		l = len;
  if (l < 1)		FINISH;
  status = l;

  w->flags &= (~Clear);			/* Window not more clear ...	*/


	/* Don't forget the Imode possibility ...	*/

  if(w->imode)		
  {	tw_chars(w, chars);
	tw_copw(w, w->pos + l, w, w->pos, chars[1]-l);
  }
  w->pos += l;

	/* Copy the text to memory, with running attribute ...
	 * Take care of control chars!	*/

  a = w->attr & (~HIGH);

  p = Aij(w, i, j);
  lm0 = &M0(w, i);
  lm1 = &M1(w, i);

  for (; --l >=0; p++, str++)	
  {	
  	if (iscntrl(*str))	new = RubbishAchar();
	else			new = *str;
	new 	|= a;
	if (*p != new)
		*p = new,
  		*lm0 = MIN(p, *lm0),
  		*lm1 = MAX(p, *lm1);
  }

  DISPLAY(0);

	/* Output to screen if active	*/

  EXIT_TWA(status);
}
 
/*==========================================================================
 *			tw_mattr
 *==========================================================================*/
int	tw_mattr(w, len, attr)
/*+++
.PURPOSE Modify attribute from current cursor position over specified length
	to a new attribute.
.RETURNS Length of text which was modified.
.REMARKS Current attribute does not change; the cursor is moved
	to the end of the modified text.
---*/
	WINDOW *w;	/* MOD: Window concerned	*/
	int len;	/* IN: Length of text to modify	*/
	int attr;	/* IN: New attribute.		*/
{
	ACHAR	blank;
	int 	l, n;

  ENTER_TWA("tw_mattr");

  blank = ((attr & (_BOLD_|_UNDERSCORE_|_BLINK_|_REVERSE_)) << 8) 
  		^ w->attr_init;
  l = w->pos + len;
  n = w->dim[0] * w->dim[1];
  if (l > n)	l = n;

  l = tw_uattr(w, w->pos, l - w->pos, blank) - w->pos;
  w->pos += l;

  EXIT_TWA(l);
}

/*==========================================================================
 *			tw_mw
 *==========================================================================*/
int	tw_mw(w, hl, hc)
/*+++
.PURPOSE Move a window to a new Home Position
.RETURNS OK / NOK
.METHOD	 Remove first, then raise it.
.REMARKS Operation performed on parent window, if it's a subwindow.
---*/
	WINDOW *w;	/* MOD: Window concerned	*/
	int hl;		/* IN: New home line (<0 for position from bottom) */
	int hc;	/* IN: New home column (<0 for position from right edge) */
{
	WINDOW	*wp;
	int 	k, not_removed;
	short int h[2];

  ENTER_TW("tw_mw");
  if_not(w)	FINISH;		/* Can't move the Screen !	*/

  h[0] = hl;	h[1] = hc;

  wp = Parent(w);
  if(wp) 		/* Move the Parent Window... */
  {	for (k=0; k<2; k++)
  		if (h[k]>=0)	h[k] = MAX(h[k]-w->home[k], 0); 
  }
  else	wp = w;		

  not_removed = wp->flags & Present;
  if (not_removed)	tw_uw(wp, 4, NULL_WINDOW);    /* Remove Window	*/

  for (k=0; k<2; k++)
  {	if (h[k] < 0) 	h[k] = MAX(h[k] + Screen->dim[k], 0);
	h[k] = MIN(h[k], Screen->dim[k] - wp->dim[k]);
	wp->home[k] = h[k];
  }

  tw_flags(wp);

  if (not_removed)	tw_r(wp, 1, NULL_WINDOW);	/* Raise Window	*/

  EXIT_TW(OK);
}

/*==========================================================================
 *			tw_out
 *==========================================================================*/
static int tw_out(i, j, str, len)
/*+++
.PURPOSE Ouput to screen, at (absolute) position [i,j], the `string' str;
.RETURNS OK / NOK
.REMARKS Not user-callable. Optimization of space outputs.
---*/
	int	i;		/* IN: Absolute output line		*/
	int	j;		/* IN: Absolute output column	*/
	ACHAR	*str;		/* IN: String to display on the terminal */
	int  len;		/* IN: Length of String			*/
{
	ACHAR	*p0, *p1;
	ACHAR 	a;
	int 	l;
	unsigned char done, right_clear;

  ENTER_DEBUG("tw_out");

  if (len <= 0) 	FINISH;		/* No output			*/

  done = 0;
  a = Screen->attr_init;
  p0 = str, p1 = p0 + len; 


	/* Skip leading blanks --- only useful if j=0 !	*/

  if ((j == 0) && (!hard_term))
  {	for ( ; (p0 < p1) && (*p0 == a); p0++)	;
	l  = p0 - str;			/* Number of leading blanks	*/

				/* Use ClearLine if full line empty	*/
	if ( (p0 == p1) && (l == Screen->Nj) )
	{	CursorTo(i, 0);
		ClearLine();
		FINISH;
	}
		/* Use Clear Left function if more than 5 leading blanks */
	if(l>5)
	{	CursorTo(i, l-1), done = 1;
		ClearLeft();	/* Clear chars at left of cursor (inclusive) */
		CursorRight(1);
		j = l;
	}
	else	p0 = str;
  }

  l = p1 - p0;
  if (l <= 0)		FINISH;			/* Line full of blanks	*/

	/* Set Cursor to its correct position	*/
  if_not(done)	  	CursorTo(i,j);

  right_clear = 0;		/* Right part of screen is to be cleared */

  /* SetAttr( (*p0)>>8);	Made by WriteAchars */

	/* Count trailing blanks --- if right adjusted!	*/
  if ((l+j) == Screen->Nj)
  {	while ( (p1 > p0) && (*(--p1) == a) ) right_clear = 1; 
	l = ++p1 - p0;
  }

	/* Output text between leading & trailing blanks	*/
  if (l)	status = WriteAchars(p0, l);

	/* Clear right part of Screen	*/
  if (right_clear)	ClearRight();

  FIN:
  EXIT_DEBUG(status);
}

/*==========================================================================
 *			tw_puts
 *==========================================================================*/
int	tw_puts(w, str)
/*+++
.PURPOSE Write an EOS-terminated string at current location
	on specified window, followed by a new line
.RETURNS OK / NOK
.REMARKS The physical output is only done after inclusion of all text in
	memory ... if the window is active!
---*/
	WINDOW *w;		/* MOD: Window concerned	*/
	char   *str;		/* IN: String to output	*/
{
  ENTER_TWA("tw_puts");

	/* If Hardware Scrolling is possible, use it	*/

  if (w->hw & Scroll)
  {	w->flags |= state_echo;
	state_echo = 0;
  }

  tw_write(w, str, strlen(str), 1);
  
  status = tw_nl(w);

  EXIT_TWA(status);
}
 
/*==========================================================================
 *			tw_r
 *==========================================================================*/
int	tw_r(wx, option, prev)
/*+++
.PURPOSE Ouput (refresh) a window with following options:
\begin{TeX}
\begin{itemize}
\item	0: Output only last modifications of a window
\item	1: Output whole window
\item	2, 3: without reporting the locations of modified chars on Screen
\item	4: Remove Window
\item	8: Place Window at a specific location
\end{itemize}
\end{TeX}
.RETURNS OK 
.REMARKS An option of zero only writes modified part of a window;
	an option of 1 repaint completely the window.
	Up / Down physical clear not used.
	Removing a window also deactivates it.
---*/
	WINDOW *wx;			/* MOD: Window concerned	*/
	int	option;			/* IN: Option to repaint window	*/
	WINDOW *prev;			/* IN: place window just after	*/
{
	WINDOW *w, *wl;
	LINE *lx, *l0, *l1;
	int long l;
	int i, opt;
	char state_clear;

  ENTER_TW("tw_r");
  DISPLAY(0);

  w = wx;
  CheckScreen(w);  
  opt = option;
  hard_term   = (Screen->hw & TERM_hard) && 
  	((w->flags & _DISPLAY_) || (opt&1));
  if (hard_term)	opt |= 16;
  wl = (opt & 8 ? prev : NULL_WINDOW);


  	/* Move to Parent Window, if it's a subwindow	*/
  
  if (Parent(w))	tw_upw(w), w = Parent(w);

	/* Clear Completely the Screen for RefreshScreen	*/

  if ( (w == Screen) && (opt & 1) && (w->hw & Clear) )
		ClearScreen();

	/* Put Window on Top if Refresh, and 
	 * Update Screen Cursor and Attributes */

  if (w != Screen) 
  {	if (opt & 4)
  	{	opt |= 8;			/* Will be linked to bottom */
  		if_not(w->flags & Present)	FINISH;	/* Already removed */
  		tw_st(w, Present|Echo, 0);	/* Not more present */
  	}
	else 			
	{	tw_st(w, Present, 1);
		/* if_not(wx->flags & _DISPLAY_) */	tw_uc(w);
	}
	if (opt & 1)
	{	opt |= 8;		/* A new link will be performed	*/
		if_not (wl) wl = Previous(Screen);
	}
  }
  else	if (opt & 4)		/* Remove All Windows 	*/
  {	for (w = Next(Screen); w != Screen; w = Next(w))
		tw_st(w, Present|Echo, 0);
	Screen->pos = 0;	/* Cursor at Home	*/
  }


  if_not(wl)	wl = Screen;
  tw_uw(w, opt, wl);		/* Copy window to screen	*/

  state_clear = Screen->clear;
  if (state_clear)		ClearScreen();


  for (i=0, lx=ALine(Screen), l0 = Am0(Screen), l1 = Am1(Screen); 
  	i<Screen->Ni; i++, lx++, l0++, l1++)
  {	l = *l1 - *l0 + 1;
	if (l<=0 || l> 512)	continue;
	if (!state_clear) tw_out(i, *l0 - *lx, *l0, l);
	*l0 = *lx + Screen->Nj;
	*l1 = NULL_ACHAR;
  }

  SetPosition(Screen->pos);
  SetAttr((Screen->attr) >> 8);

  EXIT_TW(status);
}

/*==========================================================================
 *			tw_write
 *==========================================================================*/
int	tw_write(w, str, len, opt)
/*+++
.PURPOSE Write a string at current location, interpreting the control
	characters (opt != 0) or replacing them by rubbish (opt == 0), 
	and wrap text if necessary.
.RETURNS Number of characters effectively written before reaching the
	window bottom.
.REMARKS The physical output is only done after inclusion of all text in
	memory ... if the window is active!  Control chars that cannot 
	be interpretated are output as the `Rubbish' character.
---*/
	WINDOW *w;	/* MOD: Window concerned	*/
	char   	*str;	/* IN: String to output		*/
	int	len;	/* IN: String length		*/
	int	opt;	/* IN: non-zero to interpret control chars */
{
	char	 *p;
	int 	l, i, j, n, old_attr;
	char	*p_end;
	MID_STATIC	char tab[9] = "        ";/* Dealing with tabs	*/
	MID_STATIC  char control_char = EOS;

#define Agraph 	(_GRAPHICS_ << 8)

  ENTER_TWA("+tw_write");

  p = (char *)0;


	/* First, copy to text to window	*/

  if (len <= 0) 	Return;

  TRACE_STR2(str,len);
  DISPLAY(0);

  J = w->Nj;	n = w->dim[0]*w->dim[1];
  old_attr = _NORMAL_;
  
	/* If Hardware Scrolling is possible, use it	*/

  if (w->hw & Scroll)
  {	w->flags |= state_echo;
	state_echo = 0;
  }

  for ( p  = str, p_end = p + len; (p < p_end) && status; p += i)
  {	if (w->pos >= n) 
  	{	if (w->flags&Scroll)	tw_nl(w);
		else	{ status = NOK; break; }     /* Bottom reached	*/
	}
  	j = w->pos%J;
	l  = MIN(p_end-p, J - j);
	l  = MIN(l, n - w->pos);
	if (l <= 0)	break;
					/* Search first control char	*/
	i  = oscspan((unsigned char *)p, l, _PRINT_, main_ascii);
	tw_line(w, p, i), j += i;
	if (i == l)	continue;	/* Only normal characters...	*/

		/* Here only if a control char is encountered		*/

	control_char = *(p++ + i);
	if (opt == 0)		/* Don't process, i.e. replace by something
				 * leading to `default' in following switch   */
		control_char = EOS;

   	switch(control_char)
	{ case '\n':	status = tw_nl(w); 	break;	
	  case '\r':	tw_goto(w, w->pos/J, 0);	break;
	  case '\f':	tw_clear(w, _WHOLE_);	break;
	  case '\b':	tw_mvc(w, _LEFT_, 1);	break;
	  case  DEL:	tw_dc(w,-1);			break;
	  case  SI :	old_attr = tw_attr(w, _UNDERSCORE_);	break;
	  case  SO :	tw_attr(w, old_attr);		break;
	  case '\t':	l = 8 - ((j + w->j0) & 7);
			if (j + l >= J)	status = tw_nl(w);
			else	tw_line(w,tab,l);	break;
	  case   07:	Bell();				break;
	  default  :	tw_line(w, &control_char, 1);
							break;
	}
  }
  DISPLAY(0);

  EXIT_TWA(p-str);
}

/*==========================================================================
 *			tw_wa
 *==========================================================================*/
int	tw_wa(w, astr, len)
/*+++
.PURPOSE Write, at current location, the ACHAR string
.RETURNS Number of characters effectively written before reaching the
	window bottom.
.REMARKS Cursor position not modified.
---*/
	WINDOW *w;		/* MOD: Window concerned	*/
	ACHAR   *astr;		/* IN: ACHAR string 		*/
	int	len;		/* IN: Number of `characters' 	*/
{
	int	n;
	
  ENTER_TWA("tw_wa");

  n = w->dim[0]*w->dim[1] - w->pos;
  n = MIN(len, n);
  if (n < 0)	n = 0;
  
  tw_copy(w, w->pos, astr, n);

  EXIT_TWA(n);
}

/*==========================================================================
 *			tw_wf
 *==========================================================================*/
int	tw_wf(w, ch, len)
/*+++
.PURPOSE Write, at current location, the character ch repeated
	len times.
.RETURNS Number of characters effectively written before reaching the
	window bottom.
.REMARKS Control characters are output as rubbish.
---*/
	WINDOW *w;		/* MOD: Window concerned	*/
	int   	ch;		/* IN: Fill character		*/
	int	len;		/* IN: Number of fill characters to issue*/
{
	ACHAR 	a;
	int	n;
	
  ENTER_TWA("tw_wf");

  a = (w->attr & 0xff00) | (ch & 0xff);

  n = w->dim[0]*w->dim[1]- w->pos;
  n = MIN(len, n);
  if (n < 0)	n = 0;
  
  tw_fill(w, w->pos, n, a);

  EXIT_TWA(n);
}

#if 0	/* Useless... */
/*==========================================================================
 *			tw_wt
 *==========================================================================*/
int	tw_wt(w, direction, len)
/*+++
.PURPOSE Write a rule (horizontal / vertical) from current position
	in the specified (_UP_, _DOWN_, _LEFT_, RIGHT_) direction.
.RETURNS Number of rule chars effectively written
.REMARKS The rule is truncated if margins would be passed.
	Cursor and Attributes don't move.
---*/
	WINDOW *w;		/* MOD: Window concerned	*/
	int	direction;	/* IN: Rule direction		*/
	int	len;		/* IN: Length of the rule	*/
{
	int 	i, j, J, k, n;
	int	incr;
	ACHAR	special;

#define Agraph 	(_GRAPHICS_ << 8)

  ENTER_TWA("tw_wt");

	/* Test good direction: take RIGHT as default 	*/

  J = w->Nj;
  i = w->pos / J, j = w->pos % J;
  
  switch(direction)
  {
    default:		direction = _RIGHT_;
    case _RIGHT_:	n = J - j;	break;
    case _LEFT_:	n = j + 1;	break;
    case _UP_:		n = i + 1;	break;
    case _DOWN_:	n = w->Ni - i;	break;
  }

  if (n > len)	n = len;
  if (n <= 0)			Return;
  
  special = (w->attr & ~HIGH) | Agraphic;

  if (direction&2)		/* Horizontal	*/
  {
	special |= RuleChar(_HORIZONTAL_);
	i = w->pos;
	if_not(direction & 1)	i -= (n-1);
	tw_fill(w, i, special, n);
  }
  else				/* Vertical 	*/
  {
	special |= RuleChar(_VERTICAL_);
	i = w->pos;
	if_not(direction & 1)	i -= (n-1)*J;
	for (j=n; --j>=0; i += J)
		tw_fill(w, i, special, 1);
  }
   
  EXIT_TWA(n);
}
#endif
