1/*
2 * Copyright (C) 1984-2015  Mark Nudelman
3 *
4 * You may distribute under the terms of either the GNU General Public
5 * License or the Less License, as specified in the README file.
6 *
7 * For more information, see the README file.
8 */
9
10
11/*
12 * Routines which jump to a new location in the file.
13 */
14
15#include "less.h"
16#include "position.h"
17
18extern int jump_sline;
19extern int squished;
20extern int screen_trashed;
21extern int sc_width, sc_height;
22extern int show_attn;
23extern int top_scroll;
24
25/*
26 * Jump to the end of the file.
27 */
28	public void
29jump_forw()
30{
31	POSITION pos;
32	POSITION end_pos;
33
34	if (ch_end_seek())
35	{
36		error("Cannot seek to end of file", NULL_PARG);
37		return;
38	}
39	/*
40	 * Note; lastmark will be called later by jump_loc, but it fails
41	 * because the position table has been cleared by pos_clear below.
42	 * So call it here before calling pos_clear.
43	 */
44	lastmark();
45	/*
46	 * Position the last line in the file at the last screen line.
47	 * Go back one line from the end of the file
48	 * to get to the beginning of the last line.
49	 */
50	pos_clear();
51	end_pos = ch_tell();
52	pos = back_line(end_pos);
53	if (pos == NULL_POSITION)
54		jump_loc((POSITION)0, sc_height-1);
55	else
56	{
57		jump_loc(pos, sc_height-1);
58		if (position(sc_height-1) != end_pos)
59			repaint();
60	}
61}
62
63/*
64 * Jump to the last buffered line in the file.
65 */
66	public void
67jump_forw_buffered()
68{
69	POSITION end;
70
71	if (ch_end_buffer_seek())
72	{
73		error("Cannot seek to end of buffers", NULL_PARG);
74		return;
75	}
76	end = ch_tell();
77	if (end != NULL_POSITION && end > 0)
78		jump_line_loc(end-1, sc_height-1);
79}
80
81/*
82 * Jump to line n in the file.
83 */
84	public void
85jump_back(linenum)
86	LINENUM linenum;
87{
88	POSITION pos;
89	PARG parg;
90
91	/*
92	 * Find the position of the specified line.
93	 * If we can seek there, just jump to it.
94	 * If we can't seek, but we're trying to go to line number 1,
95	 * use ch_beg_seek() to get as close as we can.
96	 */
97	pos = find_pos(linenum);
98	if (pos != NULL_POSITION && ch_seek(pos) == 0)
99	{
100		if (show_attn)
101			set_attnpos(pos);
102		jump_loc(pos, jump_sline);
103	} else if (linenum <= 1 && ch_beg_seek() == 0)
104	{
105		jump_loc(ch_tell(), jump_sline);
106		error("Cannot seek to beginning of file", NULL_PARG);
107	} else
108	{
109		parg.p_linenum = linenum;
110		error("Cannot seek to line number %n", &parg);
111	}
112}
113
114/*
115 * Repaint the screen.
116 */
117	public void
118repaint()
119{
120	struct scrpos scrpos;
121	/*
122	 * Start at the line currently at the top of the screen
123	 * and redisplay the screen.
124	 */
125	get_scrpos(&scrpos);
126	pos_clear();
127	jump_loc(scrpos.pos, scrpos.ln);
128}
129
130/*
131 * Jump to a specified percentage into the file.
132 */
133	public void
134jump_percent(percent, fraction)
135	int percent;
136	long fraction;
137{
138	POSITION pos, len;
139
140	/*
141	 * Determine the position in the file
142	 * (the specified percentage of the file's length).
143	 */
144	if ((len = ch_length()) == NULL_POSITION)
145	{
146		ierror("Determining length of file", NULL_PARG);
147		ch_end_seek();
148	}
149	if ((len = ch_length()) == NULL_POSITION)
150	{
151		error("Don't know length of file", NULL_PARG);
152		return;
153	}
154	pos = percent_pos(len, percent, fraction);
155	if (pos >= len)
156		pos = len-1;
157
158	jump_line_loc(pos, jump_sline);
159}
160
161/*
162 * Jump to a specified position in the file.
163 * Like jump_loc, but the position need not be
164 * the first character in a line.
165 */
166	public void
167jump_line_loc(pos, sline)
168	POSITION pos;
169	int sline;
170{
171	int c;
172
173	if (ch_seek(pos) == 0)
174	{
175		/*
176		 * Back up to the beginning of the line.
177		 */
178		while ((c = ch_back_get()) != '\n' && c != EOI)
179			;
180		if (c == '\n')
181			(void) ch_forw_get();
182		pos = ch_tell();
183	}
184	if (show_attn)
185		set_attnpos(pos);
186	jump_loc(pos, sline);
187}
188
189/*
190 * Jump to a specified position in the file.
191 * The position must be the first character in a line.
192 * Place the target line on a specified line on the screen.
193 */
194	public void
195jump_loc(pos, sline)
196	POSITION pos;
197	int sline;
198{
199	register int nline;
200	POSITION tpos;
201	POSITION bpos;
202
203	/*
204	 * Normalize sline.
205	 */
206	sline = adjsline(sline);
207
208	if ((nline = onscreen(pos)) >= 0)
209	{
210		/*
211		 * The line is currently displayed.
212		 * Just scroll there.
213		 */
214		nline -= sline;
215		if (nline > 0)
216			forw(nline, position(BOTTOM_PLUS_ONE), 1, 0, 0);
217		else
218			back(-nline, position(TOP), 1, 0);
219#if HILITE_SEARCH
220		if (show_attn)
221			repaint_hilite(1);
222#endif
223		return;
224	}
225
226	/*
227	 * Line is not on screen.
228	 * Seek to the desired location.
229	 */
230	if (ch_seek(pos))
231	{
232		error("Cannot seek to that file position", NULL_PARG);
233		return;
234	}
235
236	/*
237	 * See if the desired line is before or after
238	 * the currently displayed screen.
239	 */
240	tpos = position(TOP);
241	bpos = position(BOTTOM_PLUS_ONE);
242	if (tpos == NULL_POSITION || pos >= tpos)
243	{
244		/*
245		 * The desired line is after the current screen.
246		 * Move back in the file far enough so that we can
247		 * call forw() and put the desired line at the
248		 * sline-th line on the screen.
249		 */
250		for (nline = 0;  nline < sline;  nline++)
251		{
252			if (bpos != NULL_POSITION && pos <= bpos)
253			{
254				/*
255				 * Surprise!  The desired line is
256				 * close enough to the current screen
257				 * that we can just scroll there after all.
258				 */
259				forw(sc_height-sline+nline-1, bpos, 1, 0, 0);
260#if HILITE_SEARCH
261				if (show_attn)
262					repaint_hilite(1);
263#endif
264				return;
265			}
266			pos = back_line(pos);
267			if (pos == NULL_POSITION)
268			{
269				/*
270				 * Oops.  Ran into the beginning of the file.
271				 * Exit the loop here and rely on forw()
272				 * below to draw the required number of
273				 * blank lines at the top of the screen.
274				 */
275				break;
276			}
277		}
278		lastmark();
279		squished = 0;
280		screen_trashed = 0;
281		forw(sc_height-1, pos, 1, 0, sline-nline);
282	} else
283	{
284		/*
285		 * The desired line is before the current screen.
286		 * Move forward in the file far enough so that we
287		 * can call back() and put the desired line at the
288		 * sline-th line on the screen.
289		 */
290		for (nline = sline;  nline < sc_height - 1;  nline++)
291		{
292			pos = forw_line(pos);
293			if (pos == NULL_POSITION)
294			{
295				/*
296				 * Ran into end of file.
297				 * This shouldn't normally happen,
298				 * but may if there is some kind of read error.
299				 */
300				break;
301			}
302#if HILITE_SEARCH
303			pos = next_unfiltered(pos);
304#endif
305			if (pos >= tpos)
306			{
307				/*
308				 * Surprise!  The desired line is
309				 * close enough to the current screen
310				 * that we can just scroll there after all.
311				 */
312				back(nline+1, tpos, 1, 0);
313#if HILITE_SEARCH
314				if (show_attn)
315					repaint_hilite(1);
316#endif
317				return;
318			}
319		}
320		lastmark();
321		if (!top_scroll)
322			clear();
323		else
324			home();
325		screen_trashed = 0;
326		add_back_pos(pos);
327		back(sc_height-1, pos, 1, 0);
328	}
329}
330