10Sduke/*
216032Sasmotrak * Copyright (C) 1984-2023  Mark Nudelman
30Sduke *
40Sduke * You may distribute under the terms of either the GNU General Public
50Sduke * License or the Less License, as specified in the README file.
60Sduke *
70Sduke * For more information, see the README file.
80Sduke */
90Sduke
100Sduke
110Sduke/*
120Sduke * Routines dealing with the "position" table.
130Sduke * This is a table which tells the position (in the input file) of the
140Sduke * first char on each currently displayed line.
150Sduke *
160Sduke * {{ The position table is scrolled by moving all the entries.
170Sduke *    Would be better to have a circular table
180Sduke *    and just change a couple of pointers. }}
192362Sohair */
202362Sohair
212362Sohair#include "less.h"
220Sduke#include "position.h"
230Sduke
240Sdukestatic POSITION *table = NULL;  /* The position table */
250Sdukestatic int table_size = 0;
260Sduke
270Sdukeextern int sc_width, sc_height;
280Sdukeextern int header_lines;
290Sduke
300Sduke/*
310Sduke * Return the starting file position of a line displayed on the screen.
320Sduke * The line may be specified as a line number relative to the top
330Sduke * of the screen, but is usually one of these special cases:
340Sduke *      the top (first) line on the screen
350Sduke *      the second line on the screen
360Sduke *      the bottom line on the screen
370Sduke *      the line after the bottom line on the screen
380Sduke */
390Sdukepublic POSITION position(int sindex)
400Sduke{
410Sduke	switch (sindex)
420Sduke	{
430Sduke	case BOTTOM:
440Sduke		sindex = sc_height - 2;
4511503Sweijun		break;
4611503Sweijun	case BOTTOM_PLUS_ONE:
470Sduke		sindex = sc_height - 1;
480Sduke		break;
490Sduke	case MIDDLE:
500Sduke		sindex = (sc_height - 1) / 2;
510Sduke		break;
520Sduke	}
530Sduke	return (table[sindex]);
540Sduke}
550Sduke
560Sduke/*
570Sduke * Add a new file position to the bottom of the position table.
580Sduke */
5911658Sweijunpublic void add_forw_pos(POSITION pos)
6011658Sweijun{
610Sduke	int i;
620Sduke
630Sduke	/*
64903Sweijun	 * Scroll the position table up.
65903Sweijun	 */
66903Sweijun	for (i = 1;  i < sc_height;  i++)
67903Sweijun		table[i-1] = table[i];
68903Sweijun	table[sc_height - 1] = pos;
690Sduke}
700Sduke
710Sduke/*
720Sduke * Add a new file position to the top of the position table.
730Sduke */
740Sdukepublic void add_back_pos(POSITION pos)
750Sduke{
760Sduke	int i;
770Sduke
780Sduke	/*
790Sduke	 * Scroll the position table down.
800Sduke	 */
810Sduke	for (i = sc_height - 1;  i > 0;  i--)
820Sduke		table[i] = table[i-1];
830Sduke	table[0] = pos;
8411503Sweijun}
8515059Sweijun
8611503Sweijun/*
8715059Sweijun * Initialize the position table, done whenever we clear the screen.
880Sduke */
8911503Sweijunpublic void pos_clear(void)
9011503Sweijun{
9111503Sweijun	int i;
9211503Sweijun
930Sduke	for (i = 0;  i < sc_height;  i++)
9411503Sweijun		table[i] = NULL_POSITION;
9515059Sweijun}
9611503Sweijun
970Sduke/*
9811503Sweijun * Allocate or reallocate the position table.
9911503Sweijun */
10015059Sweijunpublic void pos_init(void)
10111503Sweijun{
1020Sduke	struct scrpos scrpos;
10311503Sweijun
10411503Sweijun	if (sc_height <= table_size)
1050Sduke		return;
1060Sduke	/*
1070Sduke	 * If we already have a table, remember the first line in it
1080Sduke	 * before we free it, so we can copy that line to the new table.
1090Sduke	 */
1100Sduke	if (table != NULL)
1110Sduke	{
1120Sduke		get_scrpos(&scrpos, TOP);
1130Sduke		free((char*)table);
1140Sduke	} else
1150Sduke		scrpos.pos = NULL_POSITION;
1160Sduke	table = (POSITION *) ecalloc(sc_height, sizeof(POSITION));
1170Sduke	table_size = sc_height;
1180Sduke	pos_clear();
1190Sduke	if (scrpos.pos != NULL_POSITION)
1200Sduke		table[scrpos.ln-1] = scrpos.pos;
1210Sduke}
1220Sduke
1230Sduke/*
1240Sduke * See if the byte at a specified position is currently on the screen.
1250Sduke * Check the position table to see if the position falls within its range.
1260Sduke * Return the position table entry if found, -1 if not.
1270Sduke */
1280Sdukepublic int onscreen(POSITION pos)
1290Sduke{
1300Sduke	int i;
1310Sduke
1320Sduke	if (pos < table[0])
1330Sduke		return (-1);
1340Sduke	for (i = 1;  i < sc_height;  i++)
1350Sduke		if (pos < table[i])
1360Sduke			return (i-1);
1370Sduke	return (-1);
138903Sweijun}
1390Sduke
1400Sduke/*
1410Sduke * See if the entire screen is empty.
1420Sduke */
1430Sdukepublic int empty_screen(void)
1440Sduke{
1450Sduke	return (empty_lines(0, sc_height-1));
1460Sduke}
1470Sduke
1480Sdukepublic int empty_lines(int s, int e)
1490Sduke{
1500Sduke	int i;
1510Sduke
1520Sduke	for (i = s;  i <= e;  i++)
1530Sduke		if (table[i] != NULL_POSITION && table[i] != 0)
1540Sduke			return (0);
1550Sduke	return (1);
1560Sduke}
1570Sduke
1580Sduke/*
1590Sduke * Get the current screen position.
1600Sduke * The screen position consists of both a file position and
1610Sduke * a screen line number where the file position is placed on the screen.
1620Sduke * Normally the screen line number is 0, but if we are positioned
1630Sduke * such that the top few lines are empty, we may have to set
1640Sduke * the screen line to a number > 0.
1650Sduke */
1665976Ssflorespublic void get_scrpos(struct scrpos *scrpos, int where)
1675976Ssflores{
1680Sduke	int i;
1690Sduke	int dir;
1705976Ssflores	int last;
1710Sduke
1725976Ssflores	switch (where)
1730Sduke	{
1740Sduke	case TOP:
1750Sduke		i = 0; dir = +1; last = sc_height-2;
1760Sduke		break;
1770Sduke	case BOTTOM: case BOTTOM_PLUS_ONE:
1780Sduke		i = sc_height-2; dir = -1; last = 0;
1790Sduke		break;
1800Sduke	default:
1810Sduke		i = where;
1820Sduke		if (table[i] == NULL_POSITION) {
1830Sduke			scrpos->pos = NULL_POSITION;
1840Sduke			return;
1850Sduke		}
1860Sduke		/* Values of dir and last don't matter after this. */
1870Sduke		break;
18810925Sweijun	}
18910925Sweijun
19010925Sweijun	/*
19110925Sweijun	 * Find the first line on the screen which has something on it,
19210925Sweijun	 * and return the screen line number and the file position.
19310925Sweijun	 */
19410925Sweijun	for (;; i += dir)
1950Sduke	{
1960Sduke		if (table[i] != NULL_POSITION)
1970Sduke		{
1980Sduke			scrpos->ln = i+1;
1990Sduke			scrpos->pos = table[i];
2000Sduke			return;
2010Sduke		}
2020Sduke		if (i == last) break;
2030Sduke	}
2040Sduke	/*
2050Sduke	 * The screen is empty.
2060Sduke	 */
2070Sduke	scrpos->pos = NULL_POSITION;
2080Sduke}
2090Sduke
2100Sduke/*
2110Sduke * Adjust a screen line number to be a simple positive integer
2120Sduke * in the range { 0 .. sc_height-2 }.
2130Sduke * (The bottom line, sc_height-1, is reserved for prompts, etc.)
2140Sduke * The given "sline" may be in the range { 1 .. sc_height-1 }
2150Sduke * to refer to lines relative to the top of the screen (starting from 1),
2160Sduke * or it may be in { -1 .. -(sc_height-1) } to refer to lines
2170Sduke * relative to the bottom of the screen.
2180Sduke */
2190Sdukepublic int sindex_from_sline(int sline)
2200Sduke{
2210Sduke	/*
2220Sduke	 * Negative screen line number means
2230Sduke	 * relative to the bottom of the screen.
2240Sduke	 */
2250Sduke	if (sline < 0)
2260Sduke		sline += sc_height;
2270Sduke	/*
2280Sduke	 * Can't be less than 1 or greater than sc_height.
2290Sduke	 */
2300Sduke	if (sline <= 0)
2310Sduke		sline = 1;
2320Sduke	if (sline > sc_height)
2330Sduke		sline = sc_height;
2340Sduke	/*
2350Sduke	 * Return zero-based line number, not one-based.
2360Sduke	 */
2370Sduke	return (sline-1);
2380Sduke}
2390Sduke