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