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