/*===========================================================================
  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 twutil.c
.AUTHOR    	Francois Ochsenbein [ESO-IPG]
.LANGUAGE  	C
.KEYWORDS	Window Management
.ENVIRONMENT	TermWindows
.COMMENTS	This module provides various utilities for the window functions,
		without any physical i/o. Only for internal use.
\begin{TeX}
\end{TeX}

.VERSION  1.0	14-Aug-1986:	Creation

.VERSION  2.0	03-Dec-1986	New terminal-independant graphic characters
			with output buffering version.
.VERSION  2.1	25-Jun-1987: Version '2'

.VERSION 3.0	20-Mar-1988: Version '3'
.VERSION 3.1	31-Mar-1989: Added tw_tra (get Achars from Window)
.VERSION 3.2	08-Mar-1990: Added third parameter to tw_uw
.VERSION 3.3	28-May-1990: Allow hard terminals...

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

#define		DEBUG		0	/* For debugging only		*/

#define PM_LEVEL 	LEVEL_TW	/* Only string-like functions 	*/

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

#if DEBUG
#define DISPLAY(opt)			tw_ed(w,opt)
#define ENTER_DEBUG(x)	ENTER(x)
#define EXIT_DEBUG(x)	EXIT(x)
	static WINDOW *wl;
#else
#define ENTER_DEBUG(x)	
#define EXIT_DEBUG(x)	return(x)
#define DISPLAY(opt)
#endif

extern int tv_ac();

extern int pm_enter(), pm_iexit();




MID_EXTERN	WINDOW 	*Screen;
MID_EXTERN	TERM    *terms;

MID_STATIC int step = 1;	/* Switch used in copy for tw_copy / tw_fill */
MID_STATIC int report = 1;	/* Switch used in copy to report modifications*/
static ACHAR fil;

	/* MONITOR(TWUTIL); */

/*==========================================================================
 *			copy
 *==========================================================================*/
static int  copy (wd, xd, as, len)
/*+++
.PURPOSE Copy a text (made of short integers), taking care of possible
	overlay, reporting modified chars.
.RETURNS New position in destination window
.REMARKS 
---*/
	WINDOW  *wd;		/* MOD: The Destination Window 		*/
	int	xd;		/* IN: Absolute destination position 	*/
	ACHAR 	*as;		/* IN: The Text to Copy			*/
	int 	len;		/* IN: Length of Text			*/
{
	register ACHAR *pd, *ps;
	ACHAR 	*pd1;
	int 	d, d1, l, J, i, j;
	LINE 	*lm;


  ENTER_DEBUG("+tw_copy/fill");

#if DEBUG
  TRACE_ED_STR2("Destination Window:   ", wd->id, 8);
  TRACE_ED_I   ("Destination Position: ", xd);
  TRACE_ED_I   ("Length: ", len);
#endif

  J = wd->Nj;
  pd = Aij(wd,xd/J,xd%J);
  
  if ((as >= pd) || (step == 0))		/* Use Left to Right	*/
  	for (ps = as, d = xd, d1 = d+len; d < d1; d += l)
  	{	i = d/J, j = d%J, pd = Aij(wd,i,j);
  		l = MIN(J-j, d1-d);		/* Chars that fit on the line */
  		for (pd1 = pd+l; (pd < pd1) && (*ps == *pd); ps+=step, pd++) ;
  		if (pd == pd1)	continue;	/* Nothing was modified	      */
		if (report)	lm = &M0(wd, i), *lm = MIN(*lm, pd);
		for (lm = &M1(wd, i); pd < pd1; ps+=step, pd++)
		{	if (*ps != *pd)	
			{	*pd = *ps;
				if (report)	*lm = MAX(*lm, pd);
			}
		}
  	}
  else					/* Use Right to Left 	*/
  	for (ps = as+len-1, d1 = xd-1, d = d1+len; d > d1; d -= l)
  	{	i = d/J, j = d%J, pd = Aij(wd,i,j);
  		l = MIN(j+1, d-d1);		/* Chars that fit on the line */
  		for (pd1 = pd-l; (pd > pd1) && (*ps == *pd); ps--, pd--) ;
  		if (pd == pd1)	continue;	/* Nothing was modified	      */
		if (report)	lm = &M1(wd, i), *lm = MAX(*lm, pd);
		for (lm = &M0(wd, i); pd > pd1; ps--, pd--)
		{	if (*ps != *pd)	
			{	*pd = *ps;
				if (report)	*lm = MIN(*lm, pd);
			}
		}
  	}

  FIN:
  EXIT_DEBUG((len > 0 ? xd+len : xd));
}

/*==========================================================================
 *			tw_copy
 *==========================================================================*/
int  tw_copy (wd, xd, ps, len)
/*+++
.PURPOSE Copy a text (made of short integers), taking care of possible
	overlay, reporting modified chars.
.RETURNS New position in destination window
.REMARKS 
---*/
	WINDOW  *wd;		/* MOD: The Destination Window 		*/
	int	xd;		/* IN: Absolute destination position 	*/
	ACHAR 	*ps;		/* IN: The Text to Copy			*/
	int 	len;		/* IN: Length of Text			*/
{
  step = 1;
  return(copy(wd, xd, ps, len));
}

/*==========================================================================
 *			tw_copw
 *==========================================================================*/
int  tw_copw (wd, xd, ws, xs, len)
/*+++
.PURPOSE Copy a text (made of short integers), taking care of possible
	overlay, reporting modified chars.
.RETURNS Length
.REMARKS 
---*/
	WINDOW  *wd;		/* MOD: The Destination Window 		*/
	int	xd;		/* IN: Absolute destination position 	*/
	WINDOW  *ws;		/* IN: The Source Window 		*/
	int	xs;		/* IN: Absolute source position 	*/
	int 	len;		/* IN: Length of strings		*/
{
	register ACHAR *pd, *ps;
	int 	s, s1, l, J, i, j, dx;

  ENTER_DEBUG("tw_copw");

#if DEBUG
  TRACE_ED_STR2("Destination Window:   ", wd->id, 8);
  TRACE_ED_I   ("Destination Position: ", xd);
  TRACE_ED_STR2("     Source Window:   ", ws->id, 8);
  TRACE_ED_I   ("     Source Position: ", xs);
  TRACE_ED_I   ("Length: ", len);
#endif

  J = wd->Nj, pd = Aij(wd,xd/J,xd%J);
  J = ws->Nj, ps = Aij(ws,xs/J,xs%J);
  step = 1;
  dx = xd - xs;

  if (ps >= pd)			/* Use Left to Right	*/
  	for (s = xs, s1 = s+len; s < s1; s += l)
  	{	j = s%J, ps = Aij(ws,s/J,j);
  		l = MIN(J-j, s1-s);		/* Chars that fit on the line */
		copy(wd, s+dx, ps, l);
  	}
  else				/* Use Right to Left	*/
	for (s1 = xs-1, s = s1+len; s > s1; s -= l)
  	{	j = s%J, ps = Aij(ws,s/J,j);
  		l = MIN(j+1, s-s1);		/* Chars that fit on the line */
		i = l-1, copy(wd, s+dx-i, ps-i, l);
	}
  
  FIN:
  EXIT_DEBUG((len > 0 ? xd+len : xd));
}
			
#if DEBUG
/*==========================================================================
 *			tw_ed
 *==========================================================================*/
int tw_ed (w, op)
/*+++
.PURPOSE Edit a window (for debugging purposes)
.RETURNS OK 
.REMARKS Option 0: only current parameters ---
	 Option 1: Init and current parameters ---
	 Option 2: Log also the text stored in a window ---
	 Option 3: Log also modified text.
	Not traced.
---*/
	WINDOW *w;		/* IN: Window to edit	*/
	int	op;		/* IN: Option: 0 1 2 3	*/
{ 	
	LINE 	*lw;
	register int  i, j;
	register ACHAR *b;
	static char msg[200];
	static char w_id[17] = "W 12345678.";

  oscopy(&w_id[2], w->id, 8);
  oscopy(msg, w_id, 10);
  sprintf(msg+10,
	": dim=[%d,%d], Home=[%d,%d], pos=%X, hw=%2X, aw=%2X",
	w->Ni, w->Nj, w->i0, w->j0, w->wpos, w->hw, w->attr_init);
  if(op>=1)	LOG(msg);

  sprintf(msg+10,
	": pos=[%d,%d], flags=%2X, attr=%2X, next=%d, parent=%d\0",
	w->pos/w->Nj, w->pos%w->Nj, w->flags, w->attr, Next(w), Parent(w));
  		LOG(msg);

  if(op <= 1)	return(OK);

  for (i=0, lw=ALine(w); i<w->Ni; i++,lw++)
  {	for (j=0, b= *lw; j<w->Nj; j++)
		msg[j] = (*(b++)) & HIGH;
	msg[j] = EOS;
	sprintf(msg+j, " <%d, %d>\0", M0(w,i) - *lw, M1(w,i)- *lw);
	LOG(msg);
  }
  
  return(OK);
}
#endif

/*==========================================================================
 *			tw_fill
 *==========================================================================*/
int  tw_fill (wd, xd, len, a)
/*+++
.PURPOSE Fill the destination window with the character a, reporting
	modifications.
.RETURNS New index.
.REMARKS 
---*/
	WINDOW  *wd;		/* MOD: The Destination Window 		*/
	int	xd;		/* IN: Absolute destination position 	*/
	int 	len;		/* IN: Number od chars			*/
	ACHAR	a;		/* IN: The fill `char'		*/
{
	register int n;

  ENTER_DEBUG("+tw_fill");

#if DEBUG
  TRACE_ED_I("Len = ", len);  
  TRACE_ED_I("Dest= ", xd);  
#endif

  step = 0, fil = a;
  n = copy(wd, xd, &fil, len);
  step = 1;

  EXIT_DEBUG(n);
}

/*==========================================================================
 *			posup
 *==========================================================================*/
static int posup(w, wp, pos)
/*+++
.PURPOSE Converts position to parent position
.RETURNS Parent position
.REMARKS 
---*/
	WINDOW *w;	/* IN: The source window 	*/
	WINDOW *wp;	/* IN: The parent window	*/
	int pos;	/* IN: Position on w		*/
{
  return ((pos/w->Nj) * wp->Nj + pos % w->Nj + w->homa);
}

/*==========================================================================
 *			tw_flags
 *==========================================================================*/
int tw_flags (w)
/*+++
.PURPOSE Set the wpos and hw flags, according to Terminal Capabilities.
.RETURNS OK 
.REMARKS *** For Internal Use *** 
---*/
	WINDOW *w;		/* MOD: Window to modify	*/
{ 	
	register WINDOW *wp;
	short int wh[2];
	int J;

  ENTER_DEBUG("tw_flags");

  wp = Parent(w);
  if (wp)	/* Subwindow inherits Echo and Present modes */
  {	w->flags |= wp->flags & (Echo|Present);
	wh[0] = wp->home[0], wh[1] = wp->home[1];
	J = wp->Nj;
  }
  else		/* Check if Hardware Scrolling Possible (cs capability)	*/
  	wh[0] = 0, wh[1] = 0, J = terms->dim[1];

  w->wpos = 0;
  wh[0] += w->home[0], wh[1] += w->home[1];
  if ( wh[0] == 0)				w->wpos |= Wu;
  if ((wh[0] + w->dim[0]) == terms->dim[0])	w->wpos |= Wd;
  if ( wh[1] == 0)				w->wpos |= Wl;
  if ((wh[1] + w->dim[1]) == terms->dim[1])	w->wpos |= Wr;
  w->homa = w->i0 * J + w->j0;			/* Position on parent window */

  w->hw   &= TERM_hard;
  if ( (w->flags & Scroll) 			/* Look only if required */
	&& (areSet(w->wpos, Wl|Wr))		/* Full-width window	*/
	&& (SearchCap("cs")))		/* cs indicate scrolling region */
		w->hw |= Scroll;

  EXIT_DEBUG(OK);
}

/*==========================================================================
 *			occluded
 *==========================================================================*/
static int occluded (w, I, J)
/*+++
.PURPOSE Reduce the column range if windows are occluding w.
.RETURNS Final length (J[1] - J[0])
.METHOD  J is reduced if windows on top overlap completely,
	or right side occludes a part, of left side.
---*/
	WINDOW 	*w;	/* IN: The source window	 	*/
	int	I;	/* IN: Screen line position		*/
	short int J[2];	/* MOD: Screen column positions		*/
{
	register WINDOW	*wc;
	short int j1;

  ENTER_DEBUG("occluded");

  for (wc = Previous(Screen); (wc != w) && (J[1] > J[0]); wc = Previous(wc))
  {	
#if DEBUG
	TRACE_ED_STR2("Examine Window: ", wc->id, 8);
#endif
	if_not(wc->flags & Present)	continue;	/* Removed window */
	if (I < wc->i0)			continue;	/* Line outside   */
	if (I >= wc->Ni + wc->i0)	continue;	/* Line outside   */
	j1 = wc->Nj + wc->j0;
	if (j1 >= J[0])	
	   {
           if (j1 >= J[1])
		   J[1] = MIN(J[1], wc->j0);
	   else if (wc->j0 <= J[0])
                   J[0] = j1;
           }
  }

  EXIT_DEBUG(J[1] - J[0]);
}

/*==========================================================================
 *			reset
 *==========================================================================*/
static int reset (w, I, J)
/*+++
.PURPOSE Reset text from windows on top of w, at screen line I, screen columns
	between J[0] and J[1].
.RETURNS 1 (there was occlusion) / 0 (nothing occluded)
.REMARKS 
---*/
	WINDOW 	*w;	/* IN: The source window	 	*/
	int	I;	/* IN: Screen line position		*/
	short int J[2];	/* IN: Screen column positions		*/
{
	register WINDOW	*wc;
	int 	i, j, l, status;

  ENTER_DEBUG("reset");

  status = 0;

#if DEBUG
  TRACE_ED_STR2("Init Window is: ", w->id, 8);
#endif
  
  for (wc = Next(w); wc != Screen; wc = Next(wc))
  {	
#if DEBUG
	TRACE_ED_STR2("Next Window is: ", wc->id, 8);
#endif
	if_not(wc->flags & Present)	continue;	/* Removed window */
	if (I < wc->i0)			continue;	/* Line outside   */
	i = I - wc->i0;					/* Relative line  */
	if (i >= wc->Ni)		continue;	/* Line outside   */
	j = J[0] - wc->j0;				/* Relative Col.  */
	l = J[1] - J[0];				/* Compute width  */
	if (j < 0)		l += j, j = 0;
	if (l > wc->Nj - j)	l = wc->Nj - j;
	if (l <= 0)		continue;
	status = 1;
	copy(Screen, I*Screen->Nj + j + wc->j0, Aij(wc, i, j), l);
  }

  EXIT_DEBUG(status);
}

/*==========================================================================
 *			tw_occluded
 *==========================================================================*/
int tw_occluded(w, opt)
/*+++
.PURPOSE Checks if a window / range is occluded
.RETURNS 0 (no occlusion) / 1 (there are occlusions)
.METHOD  Check line per line.
---*/
	WINDOW *w;		/* IN: Window to check		*/
	int	opt;		/* IN: Option 1 for whole window, 0 for range */
{ 	
	register WINDOW	*wp, *wc;
	int n, pos, l;
	short int I, J[2];
	int	limits[2];
 
  ENTER_DEBUG("tw_occluded");

  if (w == NULL_WINDOW)	EXIT_DEBUG(0);	/* Screen can't be occluded! */
  if (w == Screen)	EXIT_DEBUG(0);	/* Screen can't be occluded! */

  wp = (Parent(w) ? Parent(w) : w);
  if (wp == Previous(Screen))	EXIT_DEBUG(0);	/* No occlusion */

  if (opt)	limits[0] = 0, limits[1] = w->Ni * w->Nj;
  else		limits[0] = w->marker[0], limits[1] = w->marker[1];
  
  for (n = limits[0]; n < limits[1]; n += l)
  {	l = w->Nj;
	l = MIN(l, limits[1] - n);
	pos = (w == wp ? n : posup(w, wp, n));
	pos = posup(wp, Screen, pos);		/* Absolute position */
	I = pos/Screen->Nj, J[0] = pos%Screen->Nj;
	J[1] = J[0] + l;
  	for (wc = Previous(Screen); wc != wp; wc = Previous(wc))
  	{	if_not(wc->flags & Present)	continue;
		if ((I >= wc->i0) && (I < wc->Ni + wc->i0) && 
			(J[1] > wc->j0) && (J[0] < wc->Nj + wc->j0))
			EXIT_DEBUG(1);
  	}
  }
  EXIT_DEBUG(0);
}

/*==========================================================================
 *			tw_tr
 *==========================================================================*/
int tw_tr (w, str, len)
/*+++
.PURPOSE Translates to ASCII the contents of a window.
.RETURNS Length of translated text
.REMARKS Only range is translated. Attributes are lost (use tw_tra otherwise).
	The resulting string is NOT terminated with a null.
---*/
	WINDOW *w;		/* IN: Window to edit		*/
	char   *str;		/* OUT: Translated text		*/
	int	len;		/* IN: Size of string		*/
{ 	
	register char 	*p;
	ACHAR 	*pa;
	WINDOW	*wp;
	int s, s1, j, J;

  ENTER("+tw_tr");
  CheckScreen(w);  

  wp = (w ? w : Screen);
  len = MIN(len, wp->marker[1] - wp->pos);
  J = wp->Nj;
  
  for (s = wp->pos, s1 = s+len, p = str; s < s1; s+=j, p+=j)
  {	j = s%J, pa = Aij(wp, s/J, j);
  	j = MIN(J-j, s1-s);
  	tv_ac(pa, j, p);
  }

  EXIT(p-str);
}

/*==========================================================================
 *			tw_tra
 *==========================================================================*/
int tw_tra (w, str, len)
/*+++
.PURPOSE Get the contents of a window as a string of ACHAR's
.RETURNS Length of text
.REMARKS Only range is translated.
---*/
	WINDOW *w;		/* IN: Window to edit		*/
	ACHAR  *str;		/* OUT: text			*/
	int	len;		/* IN: Size of string		*/
{ 	
	ACHAR 	*p, *pa;
	WINDOW	*wp;
	int s, s1, j, J;

  ENTER("+tw_tra");
  CheckScreen(w);  

  wp = (w ? w : Screen);
  len = MIN(len, wp->marker[1] - wp->pos);
  J = wp->Nj;
  
  for (s = wp->pos, s1 = s+len, p = str; s < s1; )
  {	j = s%J, pa = Aij(wp, s/J, j);
  	j = MIN(J-j, s1-s);
  	s += j;
  	while (--j >= 0)	*(p++) = *(pa++);
  }

  EXIT(p-str);
}

/*==========================================================================
 *			tw_link
 *==========================================================================*/
int tw_link (w, wprec)
/*+++
.PURPOSE Link window w just following wprec; if wprec is NULL, delete link.
.RETURNS OK 
.REMARKS *** For Internal Use *** 
	The windows are linked from Screen: next pointer of Screen is
	lowest window, previous pointer of Screen is the top
	window. The next pointer of the top window is the 
	Screen. 
	Subwindows are linked with null pointers indicating
	start / end of Subwindow list.
---*/
	WINDOW *w;		/* MOD: Window to link		*/
	WINDOW *wprec;		/* MOD: preceding window	*/
{ 	
	register WINDOW *wc, *wp;

  ENTER_DEBUG("tw_link");

  DISPLAY(1);

#if DEBUG
  TRACE_ED_STR2("Relink Window: ", w->id, 8);
#endif

  if (w  == wprec)		FINISH;		/* Nothing to do...	*/

	/* Delete first the link	*/

#if DEBUG
  TraceOn(LEVEL_TW);
  TRACE_ED_STR2("=================Relink Window: ", w->id, 8);
#endif

  wp = Parent(w);	/* The parent Window 	*/

  wc = Next(w); 	if (wc) Previous(wc) = Previous(w);
  wc = Previous(w); 	if (wc) Next(wc) = Next(w);
  if(wp)		/* It's a child window */
  {	if(Child(wp) == w)	Child(wp) = Next(w);
  	wc = NULL_WINDOW;		/* Subwindow link to zero */
  }
  else			wc = w;		/* Window link to itself  */
  Previous(w) = wc, Next(w) = wc;

	/* Make now the link following wprec */

  if (wprec)
  {	wc = Next(wprec);		/* The next window */
  	Previous(w) = wprec, Next(w) = wc;
  	Next(wprec) = w;
  	if (wc)	Previous(wc) = w;
  }

	/* Display Links	*/

  FIN:

#if DEBUG
  TRACE("Complete list of Windows");
  if (Screen)
  for (wc = Next(Screen); wc != Screen; wc = Next(wc))
  {	if (wc == NULL_WINDOW)		break;
	TRACE_ED_STR2("Window ", wc->id, 8);
	for (wl = Child(wc); wl; wl = Next(wl))
	TRACE_ED_STR2("     SubWindow ", wl->id, 8);
  }
#endif

  EXIT_DEBUG(OK);
}

/*==========================================================================
 *			tw_upw
 *==========================================================================*/
int tw_upw (w)
/*+++
.PURPOSE Update parent Window.
.RETURNS OK 
.REMARKS 
---*/
	WINDOW *w;	/* MOD: The source window 	*/
{
	WINDOW *wp;

  ENTER_DEBUG("tw_upw");

  wp = Parent(w);
  if (wp) 
  {  	wp->attr = w->attr;
	if_not(w->clear)	wp->flags &= ~Clear;
	wp->pos = posup(w, wp, w->pos);
  }
  EXIT_DEBUG(OK);
}

/*==========================================================================
 *			tw_uc
 *==========================================================================*/
int tw_uc (w)
/*+++
.PURPOSE Update cursor position and attributes on the screen
.RETURNS OK 
.REMARKS 
---*/
	WINDOW *w;	/* MOD: The source window 	*/
{
	register WINDOW *wp;

  ENTER_DEBUG("tw_uc");

  DISPLAY(1);

	/* Report on Parent Window	*/
  
  wp = Parent(w);
  if (wp)	tw_upw(w);
  else		wp = w;

  Screen->pos = posup(wp, Screen, wp->pos);
  /* = wp->pos/wp->Nj) * Screen->Nj + wp->pos % wp->Nj + wp->homa; */
  Screen->attr = wp->attr;

  EXIT_DEBUG(OK);
}

/*==========================================================================
 *			tw_uflag
 *==========================================================================*/
int tw_uflag (w, option)
/*+++
.PURPOSE Specify if lines are modified
.RETURNS OK 
.REMARKS Option is zero to specify that all lines have no modifications,
	non-zero that lines are completely modified.
---------*/
	WINDOW *w;	/* MOD: Concerned window */
	int option;	/* IN: Fully modified / no modification */
{
	int i, j1;
	LINE *l0, *l1;
	ACHAR *b;
	
  if_not(w)	w = Screen;

  for (i = w->Ni, j1 = w->Nj-1; --i >=0; )
  {	l0 = &M0(w,i), l1 = &M1(w,i), b = Aij(w, i, 0);
  	if_not(Parent(w))	*l0 = b, *l1 = b + j1;	/* No Parent Window */
	else			*l0 = MIN(*l0, b),
				*l1 = MAX(*l1, b+j1);
	if_not(option)		/* All modification sentinels removed	*/
		*l1 = NULL_ACHAR, *l0 += terms->dim[1];
  }
  
  return(OK);
}

/*==========================================================================
 *			tw_ufl1
 *==========================================================================*/
int  tw_uattr(wd, xd, len, blank)
/*+++
.PURPOSE Change attribute for characters xd to (xd+len).
.RETURNS New position in destination window
.REMARKS 
---*/
	WINDOW  *wd;		/* MOD: The Destination Window 		*/
	int	xd;		/* IN: Absolute destination position 	*/
	int 	len;		/* IN: Length of Text			*/
	ACHAR	blank;		/* IN: New attribute			*/
{
	ACHAR 	*pd, *pd1;
	ACHAR	new_attr, nul_attr;
	int 	d, d1, l, J, i, j;
	LINE 	*lm0, *lm1;


  ENTER_DEBUG("+tw_uattr");

#if DEBUG
  TRACE_ED_STR2("Destination Window:   ", wd->id, 8);
  TRACE_ED_I   ("Destination Position: ", xd);
  TRACE_ED_I   ("Length: ", len);
#endif

  J = wd->Nj;
  nul_attr = (_REVERSE_|_UNDERSCORE_|_BLINK_|_BOLD_)<<8;
  new_attr = blank & nul_attr;
  
  for (d = xd, d1 = d+len; d < d1; d += l)
  {	i = d/J, j = d%J, pd = Aij(wd,i,j);
 	l = MIN(J-j, d1-d);		/* Chars that fit on the line */
	lm0 = &M0(wd, i), lm1 = &M1(wd, i);
  	for (pd1 = pd+l; pd < pd1; pd++) 
	{	if ((*pd & nul_attr) == new_attr)	continue;
		*pd = (*pd & (~nul_attr)) | new_attr;
		*lm0 = MIN(*lm0, pd);
		*lm1 = MAX(*lm1, pd);
	}
  }

  FIN:
  EXIT_DEBUG((len > 0 ? xd+len : xd));
}

/*==========================================================================
 *			tw_uw
 *==========================================================================*/
int tw_uw (w, option, prev)
/*+++
.PURPOSE Copy to Screen modifications of a window
.RETURNS OK 
.REMARKS Option is used to specify which modifications are to be copied;
	it consists of 3 bits:
\begin{TeX}
\begin{itemize}
\item 	the last bit (1) specifies what to output on Screen:
	\begin{itemize}
	\item	0 : Report only the modified part of the window
	\item 	1 : Report whole window to Screen.
	\end{itemize}
\item	The previous bit (2) specifies:
	\begin{itemize}
	\item	0 : report modification flags on the screen
	\item 	1 : do not report modification flags on the screen
		(\eg after a scroll performed \via a hardware function)
	\end{itemize}
\item	The previous bit (4) specifies:
	\begin{itemize}
	\item	0 : ---
	\item 	1 : fill the portion of the Screen covered by window
		with blanks (removed window)
	\end{itemize}
\item	The previous bit (8) specifies:
	\begin{itemize}
	\item	0 : ---
	\item 	1 : Place the window just after prev (raise / lower /etc)
	\end{itemize}
\item	The previous bit (16) specifies that it's a hard terminal,
	and therefore the screen is blank.
\end{itemize}
\end{TeX}
---*/
	WINDOW 	*w;	/* MOD: The source window 	*/
	int	option;	/* IN: Specification of how to report modifications */
	WINDOW *prev;	/* IN: place window just after	*/
{
	LINE	*p0, *p1;
	int 	i, j, I;
	short int J[2];
	WINDOW	*wp, *wl;

  ENTER_DEBUG("tw_uw");

#if DEBUG
  tw_ed(w,2);
#endif

	/* Report modifications on Parent Window, if necessary	*/

  wp = Parent(w);
  if (wp)	tw_upw(w);
  else		wp = w;
  wl = (option & 8 ? prev : NULL_WINDOW);

	/* Modify Link if asked (option & 8)	*/

  if (wl)	tw_link(wp, wl);

	/* If the whole window is considered as modified, flag all lines */

  if (option & 1)	tw_uflag(wp, 1);

	/* Option 4 : Remove the (parent) window 
	 * and clear corresponding part of Screen		*/

  if (option & 4)
  {	if (wp->wpos == Screen->wpos)	Screen->flags |= Clear;
	for (I = wp->homa, i = wp->Ni; --i >= 0; I += Screen->Nj)
		tw_fill(Screen, I, wp->Nj, Screen->attr_init);
	tw_uflag(wp, 0);		/* Nothing modified	*/
  }

  if (wp == Screen)	/* The window is the Screen: Nothing else to do	*/
	FINISH;

	/* For hard terminals, the screen is always clear. */

  if (option & 16) {
	for (I = wp->homa, i = wp->Ni; --i >= 0; I += Screen->Nj)
		tw_fill(Screen, I, wp->Nj, Screen->attr_init);
	tw_uflag(Screen, 0);		/* Nothing modified	*/
  }


	/* Test if the screen will be clear	*/
  

  if ((wp->clear) && (Screen->wpos == wp->wpos))
	Screen->flags |= Clear;	  	/* Window occupies complete screen */

	/* Finally, copy text to Screen, taking occluding possibilities
	 * into account.	
 	 */

  J[0] = wp->j0;		/* Absolute column	*/
  J[1] = wp->j0 + wp->Nj;	/* End of column	*/

  for (i = wp->Ni; --i >=0; )
  {	p1 = &M1(wp,i);
	p0 = &M0(wp, i);		/* First modified `char'	*/
	I = i + wp->i0;			/* Line pos. on Screen		*/

  	if (*p1)			/* Modifications on this line.
					 * Never executed for RemoveWin */
 	{	j = *p0 - Aij(wp,i,0);	/* Pos of first modified `char' */
		J[0] = j + wp->j0 ;	/* Col. pos on Screen		*/
		J[1] = J[0]+ 1 + (*p1 - *p0);	/* Last column + 1	*/
		j = J[0];
		if (occluded(wp,I,J) > 0)	/* Not Completely occluded  */
		{	if_not(wp->clear)	Screen->flags &= ~Clear;
			if (option&2) report=0;	/* Don't report modifs	*/
			j = J[0] - j;		/* Offset from *p0	*/
			copy(Screen, I*Screen->Nj+J[0], *p0 + j, J[1]-J[0]);
			report=1;
		}
	}
	else 	if_not(wl)	continue;	/* No modif, no change in
						 * Windows order...	*/

		/* Reset text from upper windows	*/
	if (reset(wp, I, J))	Screen->flags &=~Clear;

	*p1 = NULL_ACHAR;
	*p0 += wp->Nj;
  }

#if DEBUG
	tw_ed(Screen,2);
#endif

  FIN:
  EXIT_DEBUG(OK);
}

