1 /* SLang Scrolling Window Routines */
2 /*
3 Copyright (C) 2004-2017,2018 John E. Davis
4 
5 This file is part of the S-Lang Library.
6 
7 The S-Lang Library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the
10 License, or (at your option) any later version.
11 
12 The S-Lang Library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with this library; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
20 USA.
21 */
22 
23 #include "slinclud.h"
24 
25 #include "slang.h"
26 #include "_slang.h"
27 
find_window_bottom(SLscroll_Window_Type * win)28 static void find_window_bottom (SLscroll_Window_Type *win)
29 {
30    unsigned int nrows;
31    unsigned int hidden_mask;
32    SLscroll_Type *bot, *cline, *last_bot;
33    unsigned int row;
34 
35    nrows = win->nrows;
36    hidden_mask = win->hidden_mask;
37    cline = win->current_line;
38 
39    win->window_row = row = 0;
40    last_bot = bot = win->top_window_line;
41 
42    while (row < nrows)
43      {
44 	if (bot == cline)
45 	  win->window_row = row;
46 
47 	last_bot = bot;
48 
49 	if (bot == NULL)
50 	  break;
51 
52 	bot = bot->next;
53 
54 	if (hidden_mask)
55 	  {
56 	     while ((bot != NULL) && (bot->flags & hidden_mask))
57 	       bot = bot->next;
58 	  }
59 
60 	row++;
61      }
62 
63    win->bot_window_line = last_bot;
64 }
65 
find_top_to_recenter(SLscroll_Window_Type * win)66 static int find_top_to_recenter (SLscroll_Window_Type *win)
67 {
68    unsigned int nrows;
69    unsigned int hidden_mask;
70    SLscroll_Type *prev, *last_prev, *cline;
71 
72    nrows = win->nrows;
73    cline = win->current_line;
74    hidden_mask = win->hidden_mask;
75 
76    nrows = nrows / 2;
77 
78    last_prev = prev = cline;
79 
80    while (nrows && (prev != NULL))
81      {
82 	nrows--;
83 	last_prev = prev;
84 	do
85 	  {
86 	     prev = prev->prev;
87 	  }
88 	while (hidden_mask
89 	       && (prev != NULL)
90 	       && (prev->flags & hidden_mask));
91      }
92 
93    if (prev == NULL) prev = last_prev;
94 
95    win->top_window_line = prev;
96    find_window_bottom (win);
97 
98    return 0;
99 }
100 
101 #define HAS_BORDER_CODE 1
SLscroll_find_top(SLscroll_Window_Type * win)102 int SLscroll_find_top (SLscroll_Window_Type *win)
103 {
104    unsigned int i;
105    SLscroll_Type *cline, *prev, *next;
106    SLscroll_Type *top_window_line;
107    unsigned int nrows;
108    unsigned int hidden_mask;
109    int scroll_mode;
110    unsigned int border;
111 
112    cline = win->current_line;
113    nrows = win->nrows;
114    scroll_mode = win->cannot_scroll;
115    border = win->border;
116    if (scroll_mode == 2)
117      border = 0;
118 
119    if ((cline == NULL) || (nrows <= 1))
120      {
121 	win->top_window_line = cline;
122 	find_window_bottom (win);
123 	return 0;
124      }
125 
126    hidden_mask = win->hidden_mask;
127 
128    /* Note: top_window_line might be a bogus pointer.  This means that I cannot
129     * access it unless it really corresponds to a pointer in the buffer.
130     */
131    top_window_line = win->top_window_line;
132 
133    if (top_window_line == NULL)
134      return find_top_to_recenter (win);
135 
136    /* Chances are that the current line is visible in the window.  This means
137     * that the top window line should be above it.
138     */
139    prev = cline;
140 
141    i = 0;
142 
143    while ((i < nrows) && (prev != NULL))
144      {
145 	if (prev == top_window_line)
146 	  {
147 	     SLscroll_Type *twl = top_window_line;
148 	     int dir = 0;
149 
150 	     if (i < border) dir = -1; else if (i + border >= nrows) dir = 1;
151 
152 	     if (dir) while (border)
153 	       {
154 		  if (dir < 0) twl = twl->prev;
155 		  else twl = twl->next;
156 
157 		  if (twl == NULL)
158 		    {
159 		       twl = top_window_line;
160 		       break;
161 		    }
162 		  if ((hidden_mask == 0)
163 		      || (0 == (twl->flags & hidden_mask)))
164 		    border--;
165 	       }
166 
167 	     win->top_window_line = twl;
168 	     find_window_bottom (win);
169 	     return 0;
170 	  }
171 
172 	do
173 	  {
174 	     prev = prev->prev;
175 	  }
176 	while (hidden_mask
177 	       && (prev != NULL)
178 	       && (prev->flags & hidden_mask));
179 	i++;
180      }
181 
182    /* Now check the borders of the window.  Perhaps the current line lies
183     * outsider the border by a line.  Only do this if terminal can scroll.
184     */
185 
186    if (scroll_mode == 1)
187      return find_top_to_recenter (win);
188    else if (scroll_mode == -1)
189      scroll_mode = 0;
190 
191    next = cline->next;
192    while (hidden_mask
193 	  && (next != NULL)
194 	  && (next->flags & hidden_mask))
195      next = next->next;
196 
197    if ((next != NULL)
198        && (next == top_window_line))
199      {
200 	/* The current line is one line above the window.  This means user
201 	 * has moved up past the top of the window.  If scroll_mode is set
202 	 * to scroll by pages, we need to do a page up.
203 	 */
204 
205 	win->top_window_line = cline;
206 	find_window_bottom (win);
207 
208 	if (scroll_mode) return SLscroll_pageup (win);
209 
210 	return 0;
211      }
212 
213    prev = cline->prev;
214 
215    while (hidden_mask
216 	  && (prev != NULL)
217 	  && (prev->flags & hidden_mask))
218      prev = prev->prev;
219 
220    if ((prev == NULL)
221        || (prev != win->bot_window_line))
222      return find_top_to_recenter (win);
223 
224    /* It looks like cline is below window by one line.  See what line should
225     * be at top to scroll it into view.  Only do this unless we are scrolling
226     * by pages.
227     */
228    if (scroll_mode)
229      {
230 	win->top_window_line = cline;
231 	find_window_bottom (win);
232 	return 0;
233      }
234 
235    i = 2;
236    while ((i < nrows) && (prev != NULL))
237      {
238 	do
239 	  {
240 	     prev = prev->prev;
241 	  }
242 	while (hidden_mask
243 	       && (prev != NULL)
244 	       && (prev->flags & hidden_mask));
245 	i++;
246      }
247 
248    if (prev != NULL)
249      {
250 	win->top_window_line = prev;
251 	find_window_bottom (win);
252 	return 0;
253      }
254 
255    return find_top_to_recenter (win);
256 }
257 
SLscroll_find_line_num(SLscroll_Window_Type * win)258 int SLscroll_find_line_num (SLscroll_Window_Type *win)
259 {
260    SLscroll_Type *cline, *l;
261    unsigned int n;
262    unsigned int hidden_mask;
263 
264    if (win == NULL) return -1;
265 
266    hidden_mask = win->hidden_mask;
267    cline = win->current_line;
268 
269    n = 1;
270 
271    l = win->lines;
272    while (l != cline)
273      {
274 	if ((hidden_mask == 0)
275 	    || (0 == (l->flags & hidden_mask)))
276 	  n++;
277 
278 	l = l->next;
279      }
280 
281    win->line_num = n;
282    n--;
283 
284    while (l != NULL)
285      {
286 	if ((hidden_mask == 0)
287 	    || (0 == (l->flags & hidden_mask)))
288 	  n++;
289 	l = l->next;
290      }
291    win->num_lines = n;
292 
293    return 0;
294 }
295 
SLscroll_next_n(SLscroll_Window_Type * win,unsigned int n)296 unsigned int SLscroll_next_n (SLscroll_Window_Type *win, unsigned int n)
297 {
298    unsigned int i;
299    unsigned int hidden_mask;
300    SLscroll_Type *l, *cline;
301 
302    if ((win == NULL)
303        || (NULL == (cline = win->current_line)))
304      return 0;
305 
306    hidden_mask = win->hidden_mask;
307    l = cline;
308    i = 0;
309    while (i < n)
310      {
311 	l = l->next;
312 	while (hidden_mask
313 	       && (l != NULL) && (l->flags & hidden_mask))
314 	  l = l->next;
315 
316 	if (l == NULL)
317 	  break;
318 
319 	i++;
320 	cline = l;
321      }
322 
323    win->current_line = cline;
324    win->line_num += i;
325    return i;
326 }
327 
SLscroll_prev_n(SLscroll_Window_Type * win,unsigned int n)328 unsigned int SLscroll_prev_n (SLscroll_Window_Type *win, unsigned int n)
329 {
330    unsigned int i;
331    unsigned int hidden_mask;
332    SLscroll_Type *l, *cline;
333 
334    if ((win == NULL)
335        || (NULL == (cline = win->current_line)))
336      return 0;
337 
338    hidden_mask = win->hidden_mask;
339    l = cline;
340    i = 0;
341    while (i < n)
342      {
343 	l = l->prev;
344 	while (hidden_mask
345 	       && (l != NULL) && (l->flags & hidden_mask))
346 	  l = l->prev;
347 
348 	if (l == NULL)
349 	  break;
350 
351 	i++;
352 	cline = l;
353      }
354 
355    win->current_line = cline;
356    win->line_num -= i;
357    return i;
358 }
359 
SLscroll_pageup(SLscroll_Window_Type * win)360 int SLscroll_pageup (SLscroll_Window_Type *win)
361 {
362    SLscroll_Type *l, *top;
363    unsigned int nrows, hidden_mask;
364 
365    if (win == NULL)
366      return -1;
367 
368    (void) SLscroll_find_top (win);
369 
370    nrows = win->nrows;
371 
372    if ((NULL != (top = win->top_window_line))
373        && (nrows > 2))
374      {
375 	unsigned int n = 0;
376 	hidden_mask = win->hidden_mask;
377 	l = win->current_line;
378 	while ((l != NULL) && (l != top))
379 	  {
380 	     l = l->prev;
381 	     if ((hidden_mask == 0)
382 		 || ((l != NULL) && (0 == (l->flags & hidden_mask))))
383 	       n++;
384 	  }
385 
386 	if (l != NULL)
387 	  {
388 	     unsigned int save_line_num;
389 	     int ret = 0;
390 
391 	     win->current_line = l;
392 	     win->line_num -= n;
393 
394 	     /* Compute a new top/bottom header */
395 	     save_line_num = win->line_num;
396 
397 	     if ((0 == SLscroll_prev_n (win, nrows - 1))
398 		 && (n == 0))
399 	       ret = -1;
400 
401 	     win->top_window_line = win->current_line;
402 	     win->current_line = l;
403 	     win->line_num = save_line_num;
404 
405 	     find_window_bottom (win);
406 	     return ret;
407 	  }
408      }
409 
410    if (nrows < 2) nrows++;
411    if (0 == SLscroll_prev_n (win, nrows - 1))
412      return -1;
413    return 0;
414 }
415 
SLscroll_pagedown(SLscroll_Window_Type * win)416 int SLscroll_pagedown (SLscroll_Window_Type *win)
417 {
418    SLscroll_Type *l, *bot;
419    unsigned int nrows, hidden_mask;
420 
421    if (win == NULL)
422      return -1;
423 
424    (void) SLscroll_find_top (win);
425 
426    nrows = win->nrows;
427 
428    if ((NULL != (bot = win->bot_window_line))
429        && (nrows > 2))
430      {
431 	unsigned int n = 0;
432 	hidden_mask = win->hidden_mask;
433 	l = win->current_line;
434 	while ((l != NULL) && (l != bot))
435 	  {
436 	     l = l->next;
437 	     if ((hidden_mask == 0)
438 		 || ((l != NULL) && (0 == (l->flags & hidden_mask))))
439 	       n++;
440 	  }
441 
442 	if (l != NULL)
443 	  {
444 	     win->current_line = l;
445 	     win->top_window_line = l;
446 	     win->line_num += n;
447 
448 	     find_window_bottom (win);
449 
450 	     if (n || (bot != win->bot_window_line))
451 	       return 0;
452 
453 	     return -1;
454 	  }
455      }
456 
457    if (nrows < 2) nrows++;
458    if (0 == SLscroll_next_n (win, nrows - 1))
459      return -1;
460    return 0;
461 }
462 
463