/*===========================================================================
  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 twcursor.c
.AUTHOR    	Francois Ochsenbein [ESO-IPG]
.LANGUAGE  	C
.KEYWORDS	Window Management
.ENVIRONMENT	TermWindows
.COMMENTS	This module contains routines to move cursor and
		scroll 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	25-Jun-1987: Version '2'
.VERSION  2.2	02-Dec-1987: No clear line for a NewLine
.VERSION 3.0	20-Mar-1988: Version '3'

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

#define		DEBUG		0	/* For debugging only		*/

#define PM_LEVEL 	LEVEL_TW

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

extern int tv_buffer(), tv_agoto(), tv_range();
extern int tv_sr(), tv_goto(), tv_mvc();

extern int tw_st(), tw_attr(), tw_nl(), tw_r(), tw_uw(), tw_fill();
extern int tw_copw(), tw_occluded(), tw_geth(), tw_uc(), tw_scroll();

extern int pm_enter(), pm_iexit();



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

MID_RSTATIC  short int step[2] = {-1, 1};
static  int J;

	MONITOR(TWCURSOR);

/*==========================================================================
 *			tw_chars
 *==========================================================================*/
int	tw_chars(w, chars)
/*+++
.DES Returns the number of characters before / after the cursor
.RET OK 
.REM Not traced. Depends on _FULL_ mode.
---*/
	WINDOW *w;		/* MOD: Window concerned	*/
	int chars[2]; 	/* OUT: chars at left of cursor [0], at right [1]*/
{

  J = w->Nj;

  if ((w->flags & FullW) && (w->pos >= w->marker[0]) && (w->pos < w->marker[1]))
  	chars[0] = w->pos - w->marker[0],
	chars[1] = w->marker[1] - w->pos;
  else	chars[0] = w->pos%J, 
  	chars[1] = (w->pos < w->marker[1] ? J - chars[0] : 0);
  
  return(OK);
}
 
/*==========================================================================
 *			tw_check
 *==========================================================================*/
int	tw_check(w)
/*+++
.DES Checks if the cursor position is within range.
.RET OK / NOK
.REM Not traced. Depends on _FULL_ mode.
---*/
	WINDOW *w;	/* IN: Window concerned */
{
	register int st;
	
  st = NOK;
  if (w->pos < w->marker[0]) 		w->pos = w->marker[0];
  else 	if (w->pos > w->marker[1]) 	w->pos = w->marker[1];
  else	st = OK;
  
  return(st);
}
 
/*==========================================================================
 *			tw_cur()
 *==========================================================================*/
int	tw_cur(saved)
/*+++++++++++
.PURPOSE Restore the cursor position.
.RETURNS OK
.REMARKS 
-----------------------------------------------------------------------*/
	TWSAVE	*saved;		/* IN: Old position + attribute */
{
	int	status;
	
  ENTER_DEBUG("tw_cur");

  Screen->pos = saved->pos, status = SetPosition(Screen->pos);
  tw_attr(Screen, saved->attr);

  EXIT_DEBUG(status);
}

/*==========================================================================
 *			tw_cus()
 *==========================================================================*/
int	tw_cus(saved)
/*++++++++++++++
.PURPOSE Save the current cursor position.
.RETURNS OK
.REMARKS Not traced.
---------------*/
	TWSAVE	*saved;		/* OUT: Current position + attribute */
{
  saved->pos    = Screen->pos;
  saved->attr   = (Screen->attr) >> 8;

  return(OK);
}
 
/*==========================================================================
 *			tw_agoto
 *==========================================================================*/
int	tw_agoto(w, p)
/*+++
.PURPOSE Position the cursor to a specified position on a window.
.RETURNS OK / NOK (Cursor put on an edge)
.REMARKS An attempt to position the cursor off the screen will place it 
	on an edge.
---*/
	WINDOW *w;	/* MOD: Window concerned	*/
	int	p;	/* IN: Position to move to	*/
{
  ENTER_TW("tw_agoto");

  CheckScreen(w);
  w->pos = p;
  status = tw_check(w);

  if(w->active)
  	tw_uc(w),		/* Update cursor */
	SetPosition(Screen->pos);

  DISPLAY(0);
  EXIT_TW (status);
}

/*==========================================================================
 *			tw_goto
 *==========================================================================*/
int	tw_goto(w, l, c)
/*+++
.PURPOSE Position the cursor to a specified line and column position on a 
	window.
.RETURNS OK / NOK (Cursor put on an edge)
.REMARKS An attempt to position the cursor off the screen will place it 
	on an edge.
---*/
	WINDOW *w;	/* MOD: Window concerned	*/
	int	l;	/* IN: Line number to move to	*/
	int	c;	/* IN: Column number to move to */
{
	short int ij[2];

  ENTER_TW("tw_goto");
  CheckScreen(w);

  DISPLAY(0);
#if DEBUG
  TRACE_ED_STR2("Moving on wind ",w->id, 8);
  TRACE_ED_I("       to line ",l);
  TRACE_ED_I("      ... col  ",c);
#endif

  ij[0] = l;	ij[1] = c;
  status = CheckCursor(ij, w->dim);
  w->pos = ij[0]*w->Nj + ij[1];

  if(w->active)
  	tw_uc(w);		/* Update cursor */
	SetPosition(Screen->pos);

  DISPLAY(0);
  EXIT_TW (status);
}

/*======================================================================
 *			tw_home
 *======================================================================*/
int tw_home(w)
/*+++
.PURPOSE Move the cursor to home position of a specified window.
.RETURNS OK
.REMARKS Moves to beginning of Range.
---*/
	WINDOW	*w;	/* MOD: Window concerned	*/
{

  ENTER_DEBUG("tw_home");

  status = tw_agoto(w, 0);
  
  EXIT_DEBUG(status);
}

/*======================================================================
 *			tw_mvc
 *======================================================================*/
int tw_mvc(w, direction, times)
/*+++
.PURPOSE Move the cursor on a window n times in the specified direction: 
	up, down, right, left or newline.
.RETURNS OK / NOK (attempt to move outside window, cursor on an edge).
.REMARKS A negative number of times asks for the opposite direction.
	The Insert Mode is switched off for vertical displacements.
	If the window is in Full Mode, horizontal displacements wrap lines,
	and displacements are limited within markers.
------*/
	WINDOW *w;	/* MOD: Window concerned			*/
	int direction;	/* IN: The direction of the displacement	*/
	int times;	/* IN: Number of times to move			*/
{
	register int k, n, d;
	char 	state_echo;
	short int new_pos[2];

  ENTER_TW("tw_mvc");
  CheckScreen(w);

  DISPLAY(0);
#if DEBUG
  TRACE_ED_STR2("Moving on wind ",w->id, 8);
  TRACE_ED_I("... Direction: ",direction);
#endif

  if (times == 0) 	FINISH;

  J = w->Nj;
  k = direction >> 1;		/* Leads to Horizontal, vertical, other	*/
  d = direction & 1;	
  new_pos[0] = w->pos/J;			/* Current column */
  new_pos[1] = w->pos%J;			/* Current column */
  n = times * step[d];

  switch(k)
  { case _VERTICAL_:		
	/* w->flags &= (~Imode);	/. Suppress the Insert Mode	*/
	new_pos[k] += n;
	n *= J;
	break;
    case _HORIZONTAL_:
	new_pos[k] += n;
	break;
    default:	n = times;		/* Issue New Lines		*/
	if (n <= 0)		FINISH;
	if (w->flags & FullW)
	{	w->pos = ((w->pos + n*J)/J)*J;
		break;
	}
	if (w->hw & Scroll)		/* Use Hardware Scrolling	*/
 		state_echo = 0;
	else	
	{	state_echo = w->active;
		tw_st(w, Echo, 0);
	}
	while(--n >=0)	status = tw_nl(w);
	if (state_echo)			/* Simulated Scrolling		*/
	{	tw_st(w, Echo, 1);
		tw_rw(w, 0, NULL_WINDOW);
	}
	FINISH;
  }

  if (w->flags & FullW)
	status = tw_agoto(w, n);
  else	status = tw_goto(w, new_pos[0], new_pos[1]);

  EXIT_TW(status);
}

/*==========================================================================
 *			tw_nl
 *==========================================================================*/
int	tw_nl(w)
/*+++
.DES New line on a window, taking into account possible scrolling.
.RET OK / NOK (was already at bottom, and no scrolling)
.REM The cursor position is at the beginning of the next (cleared) line.
	The insert mode is switched off.
---*/
	WINDOW *w;			/* MOD: Window concerned	*/
{
  ENTER_TW("tw_nl");

  CheckScreen(w);
  J = w->Nj;

  if (w->pos >= (w->marker[1] - J))
  { 	if(w->scroll) 			/* Echo performed in scroll fct	*/
	{	w->pos = (w->Ni-1)*J;
		status = tw_scroll(w,_UP_,1); 
		Return;
	}
	else	w->pos = w->marker[1], status = NOK;
  }
  else	w->pos = ((w->pos+J)/J)*J;

  if(w->active)
  	tw_uc(w);		/* Update cursor */
	SetPosition(Screen->pos);

  EXIT_TW(status);
}

/*======================================================================
 *			tw_scroll
 *======================================================================*/
int tw_scroll(w, direction, times)
/*+++
.PURPOSE Scroll n lines on a window in the specified (_UP_ or _DOWN_) 
	direction.
.RETURNS OK 
.REMARKS A negative number of times asks for the opposite direction; _UP_ 
	is the default. The cursor does not move.
---*/
	WINDOW *w;		/* MOD: Window concerned	*/
	int direction;	/* IN: The direction of the displacement	*/
	int times;	/* IN: Number of lines to scroll		*/
{
	int i, n, k, L;
	char hw_scroll;
	short int home_pos[2];

  ENTER_TW("tw_scroll");
  CheckScreen(w);

#if DEBUG
  TRACE_ED_STR2("Scrolling window ",w->id, 8);
  DISPLAY(2);
#endif

  if (times == 0) 	Return;

  J = w->Nj;

  n = (direction == _DOWN_ ? -times : times);
  k = ABSOLUTE(n);

#if DEBUG
  TRACE_ED_I("Scrolling lines: ",n);
#endif

	/* If number of lines is large enough, window becomes clear	*/

  if (k >= w->Ni)	
  { 	if (w->attr == Screen->attr_init)	w->flags |= Clear; 
	n = w->Ni; 
	k = n;
  }

  L = (w->Ni - k)*J;		/* Number of chars to copy	*/
  i = k * J;			/* Number of blank chars	*/

		/* Scroll the memory copy of window, line per line	*/

  if (n < 0)	tw_copw(w, i, w, 0, L), L = 0;		/* Scroll Down	*/
  else		tw_copw(w, 0, w, i, L);			/* Scroll Up	*/
  tw_fill(w, L, i, w->attr_init);

  DISPLAY(2);

	/* Update now the screen, and write output	*/

  if(w->active)
  {	hw_scroll = ((w->hw) & Scroll) && (!(w->clear));
	if (hw_scroll && (tw_occluded(w,1) == 0))
	/* Next((Parent(w) ? Parent(w) : w)) == Screen) */
		/* Hardware scrolling is possible; however, if window
		 * is clear, better to use refresh, which is optimized
		 * for clear windows
		 * Don't forget that lines blanked by hardare scrolling
		 * are in screen normal attribute ...			*/
	{	
		if (w->attr_init != Screen->attr_init)	/* Attribute of blanked
							 * lines	*/
			tw_fill(w, L, i, Screen->attr_init);
	
		tw_uw(w,2);	/* Modify screen, but without marking lines
				 * as modified		*/
		tw_geth(w, home_pos);	/* Note: home_pos[1] always zero... */
		k = home_pos[0] + w->Ni - 1;
		SetScroll(home_pos[0], k);
		if (n < 0)	k = home_pos[0];
		CursorTo(k, 0);
		CursorDown(n);
		/*  w->pos = (k - home_pos[0]) * J; */
		if (w->attr_init != Screen->attr_init)	/* Attribute of blanked
							 * lines	*/
			tw_fill(w, L, i, w->attr_init);
	}
	tw_rw(w,0,NULL_WINDOW);
  }

  EXIT_TW(status);
}
 
/*==========================================================================
 *			tw_where
 *==========================================================================*/
int	tw_where(w, coo)
/*+++
.DES Returns the current cursor position in specified window.
.RET OK / NOK (outside limits of window / Range)
.REM Not traced.
---*/
	WINDOW *w;		/* MOD: Window to close	*/
	short int coo[2];	/* OUT: Returned position: line (0=top), col (0=left) */
{
  CheckScreen(w);

  J = w->Nj;
  
  coo[0] = w->pos/J; 	coo[1] = w->pos%J;

  return ((w->pos >= w->marker[0]) && (w->pos < w->marker[1]) ? OK : NOK);
}
 
/*==========================================================================
 *			tw_awhere
 *==========================================================================*/
int	tw_awhere(w)
/*+++
.DES Returns the current cursor position in specified window.
.RET Byte position
.REM Not traced.
---*/
	WINDOW *w;		/* MOD: Window to close	*/
{
  CheckScreen(w);
  return (w->pos);
}
 
