/* move4.c */

/* Author:
 *	Steve Kirkendall
 *	14407 SW Teal Blvd. #C
 *	Beaverton, OR 97005
 *	kirkenda@cs.pdx.edu
 */


/* This file contains movement functions which are screen-relative */

#include "config.h"
#include "vi.h"

/* This moves the cursor to a particular row on the screen */
/*ARGSUSED*/
MARK m_row(m, cnt, key)
	MARK	m;	/* the cursor position */
	long	cnt;	/* the row we'll move to */
	int	key;	/* the keystroke of this move - H/L/M */
{
	DEFAULT(1);

	/* calculate destination line based on key */
	cnt--;
	switch (key)
	{
	  case 'H':
		cnt = topline + cnt;
		break;

	  case 'M':
		cnt = topline + (LINES - 1) / 2;
		break;

	  case 'L':
		cnt = botline - cnt;
		break;
	}

	/* return the mark of the destination line */
	return MARK_AT_LINE(cnt);
}


/* This function repositions the current line to show on a given row */
MARK m_z(m, cnt, key)
	MARK	m;	/* the cursor */
	long	cnt;	/* the line number we're repositioning */
	int	key;	/* key struck after the z */
{
	long	newtop;
	int	i;

	/* Which line are we talking about? */
	if (cnt < 0 || cnt > nlines)
	{
		return MARK_UNSET;
	}
	if (cnt)
	{
		m = MARK_AT_LINE(cnt);
		newtop = cnt;
	}
	else
	{
		newtop = markline(m);
	}

	/* allow a "window size" number to be entered */
	for (i = 0; key >= '0' && key <= '9'; key = getkey(0))
	{
		i = i * 10 + key - '0';
	}
#ifndef CRUNCH
	if (i > 0 && i <= LINES - 1)
	{
		*o_window = i;
		wset = TRUE;
	}
#else
	/* the number is ignored if -DCRUNCH */
#endif

	/* figure out which line will have to be at the top of the screen */
	switch (key)
	{
	  case '\n':
#if OSK
	  case '\l':
#else
	  case '\r':
#endif
	  case '+':
		break;

	  case '.':
	  case 'z':
		newtop -= LINES / 2;
		break;

	  case '-':
		newtop -= LINES - 1;
		break;

	  default:
		return MARK_UNSET;
	}

	/* make the new topline take effect */
	redraw(MARK_UNSET, FALSE);
	if (newtop >= 1)
	{
		topline = newtop;
	}
	else
	{
		topline = 1L;
	}
	redrawrange(0L, INFINITY, INFINITY);

	/* The cursor doesn't move */
	return m;
}


/* This function scrolls the screen.  It does this by calling redraw() with
 * an off-screen line as the argument.  It will move the cursor if necessary
 * so that the cursor is on the new screen.
 */
/*ARGSUSED*/
MARK m_scroll(m, cnt, key)
	MARK	m;	/* the cursor position */
	long	cnt;	/* for some keys: the number of lines to scroll */
	int	key;	/* keystroke that causes this movement */
{
	MARK	tmp;	/* a temporary mark, used as arg to redraw() */

	/* adjust cnt, and maybe *o_scroll, depending of key */
	switch (key)
	{
	  case ctrl('F'):
	  case ctrl('B'):
		DEFAULT(1);
		redrawrange(0L, INFINITY, INFINITY); /* force complete redraw */
		cnt = cnt * (LINES - 1) - 2; /* keeps two old lines on screen */
		break;

	  case ctrl('E'):
	  case ctrl('Y'):
		DEFAULT(1);
		break;

	  case ctrl('U'):
	  case ctrl('D'):
		if (cnt == 0) /* default */
		{
			cnt = *o_scroll;
		}
		else
		{
			if (cnt > LINES - 1)
			{
				cnt = LINES - 1;
			}
			*o_scroll = cnt;
		}
		break;
	}

	/* scroll up or down, depending on key */
	switch (key)
	{
	  case ctrl('B'):
	  case ctrl('Y'):
	  case ctrl('U'):
		cnt = topline - cnt;
		if (cnt < 1L)
		{
			cnt = 1L;
			m = MARK_FIRST;
		}
		tmp = MARK_AT_LINE(cnt) + markidx(m);
		redraw(tmp, FALSE);
		if (markline(m) > botline)
		{
			m = MARK_AT_LINE(botline);
		}
		break;

	  case ctrl('F'):
	  case ctrl('E'):
	  case ctrl('D'):
		cnt = botline + cnt;
		if (cnt > nlines)
		{
			cnt = nlines;
			m = MARK_LAST;
		}
		tmp = MARK_AT_LINE(cnt) + markidx(m);
		redraw(tmp, FALSE);
		if (markline(m) < topline)
		{
			m = MARK_AT_LINE(topline);
		}
		break;
	}

	return m;
}
