1 /*
2  * Copyright 2008-2013 Various Authors
3  * Copyright 2004-2005 Timo Hirvonen
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of the
8  * License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "window.h"
20 #include "options.h"
21 #include "xmalloc.h"
22 #include "debug.h"
23 #include "utils.h"
24 
25 #include <stdlib.h>
26 
sel_changed(struct window * win)27 static void sel_changed(struct window *win)
28 {
29 	if (win->sel_changed)
30 		win->sel_changed();
31 	win->changed = 1;
32 }
33 
selectable(struct window * win,struct iter * iter)34 static int selectable(struct window *win, struct iter *iter)
35 {
36 	if (win->selectable)
37 		return win->selectable(iter);
38 	return 1;
39 }
40 
window_new(int (* get_prev)(struct iter *),int (* get_next)(struct iter *))41 struct window *window_new(int (*get_prev)(struct iter *), int (*get_next)(struct iter *))
42 {
43 	struct window *win;
44 
45 	win = xnew(struct window, 1);
46 	win->get_next = get_next;
47 	win->get_prev = get_prev;
48 	win->selectable = NULL;
49 	win->sel_changed = NULL;
50 	win->nr_rows = 1;
51 	win->changed = 1;
52 	iter_init(&win->head);
53 	iter_init(&win->top);
54 	iter_init(&win->sel);
55 	return win;
56 }
57 
window_free(struct window * win)58 void window_free(struct window *win)
59 {
60 	free(win);
61 }
62 
window_set_empty(struct window * win)63 void window_set_empty(struct window *win)
64 {
65 	iter_init(&win->head);
66 	iter_init(&win->top);
67 	iter_init(&win->sel);
68 	sel_changed(win);
69 }
70 
window_set_contents(struct window * win,void * head)71 void window_set_contents(struct window *win, void *head)
72 {
73 	struct iter first;
74 
75 	win->head.data0 = head;
76 	win->head.data1 = NULL;
77 	win->head.data2 = NULL;
78 	first = win->head;
79 	win->get_next(&first);
80 	win->top = first;
81 	win->sel = first;
82 	while (!selectable(win, &win->sel))
83 		win->get_next(&win->sel);
84 	sel_changed(win);
85 }
86 
window_set_nr_rows(struct window * win,int nr_rows)87 void window_set_nr_rows(struct window *win, int nr_rows)
88 {
89 	if (nr_rows < 1)
90 		return;
91 	win->nr_rows = nr_rows;
92 	window_changed(win);
93 	win->changed = 1;
94 }
95 
window_up(struct window * win,int rows)96 void window_up(struct window *win, int rows)
97 {
98 	struct iter iter;
99 	int upper_bound   = min_i(scroll_offset,  win->nr_rows/2);
100 	int buffer        = 0; /* rows between `old sel` and `old top` */
101 	int sel_up        = 0; /* selectable rows between `old sel` and `new sel` */
102 	int skipped       = 0; /* unselectable rows between `old sel` and `new sel` */
103 	int actual_offset = 0; /* rows between `new sel` and `new top` */
104 	int top_up        = 0; /* rows between `old top` and `new top` */
105 
106 	iter = win->top;
107 	while (!iters_equal(&iter, &win->sel)) {
108 		win->get_next(&iter);
109 		buffer++;
110 	}
111 
112 	iter = win->sel;
113 	while (sel_up < rows) {
114 		if (!win->get_prev(&iter)) {
115 			break;
116 		}
117 		if (selectable(win, &iter)) {
118 			sel_up++;
119 			win->sel = iter;
120 		} else {
121 			skipped++;
122 		}
123 	}
124 	/* if there is no selectable row above the current, we move win->top instead
125 	 * this is necessary when scroll_offset=0 to make the first album header visible */
126 	if (sel_up == 0) {
127 		skipped = 0;
128 		upper_bound = min_i(buffer+rows, win->nr_rows/2);
129 	}
130 
131 	iter = win->sel;
132 	while (actual_offset < upper_bound) {
133 		if (!win->get_prev(&iter)) {
134 			break;
135 		}
136 		actual_offset++;
137 	}
138 
139 	top_up = actual_offset + sel_up + skipped - buffer;
140 	while (top_up > 0) {
141 		win->get_prev(&win->top);
142 		top_up--;
143 	}
144 
145 	if (sel_up > 0 || actual_offset > 0)
146 		sel_changed(win);
147 }
148 
window_down(struct window * win,int rows)149 void window_down(struct window *win, int rows)
150 {
151 	struct iter iter;
152 	int upper_bound   = min_i(scroll_offset, (win->nr_rows-1)/2);
153 	int buffer        = 0; /* rows between `old sel` and `old bottom` */
154 	int sel_down      = 0; /* selectable rows between `old sel` and `new sel` */
155 	int skipped       = 0; /* unselectable rows between `old sel` and `new sel` */
156 	int actual_offset = 0; /* rows between `new sel` and `new bottom` */
157 	int top_down      = 0; /* rows between `old top` and `new top` */
158 
159 	buffer = win->nr_rows - 1;
160 	iter = win->top;
161 	while (!iters_equal(&iter, &win->sel)) {
162 		win->get_next(&iter);
163 		buffer--;
164 	}
165 
166 	iter = win->sel;
167 	while (sel_down < rows) {
168 		if (!win->get_next(&iter)) {
169 			break;
170 		}
171 		if (selectable(win, &iter)) {
172 			sel_down++;
173 			win->sel = iter;
174 		} else {
175 			skipped++;
176 		}
177 	}
178 	if (sel_down == 0) {
179 		skipped = 0;
180 		upper_bound = min_i(buffer+rows, (win->nr_rows-1)/2);
181 	}
182 
183 	iter = win->sel;
184 	while (actual_offset < upper_bound) {
185 		if (!win->get_next(&iter))
186 			break;
187 		actual_offset++;
188 	}
189 
190 	top_down = actual_offset + sel_down + skipped - buffer;
191 	while (top_down > 0) {
192 		win->get_next(&win->top);
193 		top_down--;
194 	}
195 
196 	if (sel_down > 0 || actual_offset > 0)
197 		sel_changed(win);
198 }
199 
200 /*
201  * minimize number of empty lines visible
202  * make sure selection is visible
203  */
window_changed(struct window * win)204 void window_changed(struct window *win)
205 {
206 	struct iter iter;
207 	int delta, rows;
208 
209 	if (iter_is_null(&win->head)) {
210 		BUG_ON(!iter_is_null(&win->top));
211 		BUG_ON(!iter_is_null(&win->sel));
212 		return;
213 	}
214 	BUG_ON(iter_is_null(&win->top));
215 	BUG_ON(iter_is_null(&win->sel));
216 
217 	/* make sure top and sel point to real row if possible */
218 	if (iter_is_head(&win->top)) {
219 		win->get_next(&win->top);
220 		win->sel = win->top;
221 		sel_changed(win);
222 		return;
223 	}
224 
225 	/* make sure the selected row is visible */
226 
227 	/* get distance between top and sel */
228 	delta = 0;
229 	iter = win->top;
230 	while (!iters_equal(&iter, &win->sel)) {
231 		if (!win->get_next(&iter)) {
232 			/* sel < top, scroll up until top == sel */
233 			while (!iters_equal(&win->top, &win->sel))
234 				win->get_prev(&win->top);
235 			goto minimize;
236 		}
237 		delta++;
238 	}
239 
240 	/* scroll down until sel is visible */
241 	while (delta > win->nr_rows - 1) {
242 		win->get_next(&win->top);
243 		delta--;
244 	}
245 minimize:
246 	/* minimize number of empty lines shown */
247 	iter = win->top;
248 	rows = 1;
249 	while (rows < win->nr_rows) {
250 		if (!win->get_next(&iter))
251 			break;
252 		rows++;
253 	}
254 	while (rows < win->nr_rows) {
255 		iter = win->top;
256 		if (!win->get_prev(&iter))
257 			break;
258 		win->top = iter;
259 		rows++;
260 	}
261 	win->changed = 1;
262 }
263 
window_row_vanishes(struct window * win,struct iter * iter)264 void window_row_vanishes(struct window *win, struct iter *iter)
265 {
266 	struct iter new = *iter;
267 	if (!win->get_next(&new) && !win->get_prev(&new)) {
268 		window_set_empty(win);
269 	}
270 
271 	BUG_ON(iter->data0 != win->head.data0);
272 	if (iters_equal(&win->top, iter)) {
273 		new = *iter;
274 		if (win->get_next(&new)) {
275 			win->top = new;
276 		} else {
277 			new = *iter;
278 			win->get_prev(&new);
279 			win->top = new;
280 		}
281 	}
282 	if (iters_equal(&win->sel, iter)) {
283 		/* calculate minimal distance to next selectable */
284 		int down = 0;
285 		int up = 0;
286 		new = *iter;
287 		do {
288 			if (!win->get_next(&new)) {
289 				down = 0;
290 				break;
291 			}
292 			down++;
293 		} while (!selectable(win, &new));
294 		new = *iter;
295 		do {
296 			if (!win->get_prev(&new)) {
297 				up = 0;
298 				break;
299 			}
300 			up++;
301 		} while (!selectable(win, &new));
302 		new = *iter;
303 		if (down > 0 && (up == 0 || down <= up)) {
304 			do {
305 				win->get_next(&new);
306 			} while (!selectable(win, &new));
307 		} else if (up > 0) {
308 			do {
309 				win->get_prev(&new);
310 			} while (!selectable(win, &new));
311 		} else {
312 			/* no selectable item left but window not empty */
313 			new.data1 = new.data2 = NULL;
314 		}
315 		win->sel = new;
316 		sel_changed(win);
317 	}
318 
319 	win->changed = 1;
320 }
321 
window_get_top(struct window * win,struct iter * iter)322 int window_get_top(struct window *win, struct iter *iter)
323 {
324 	*iter = win->top;
325 	return !iter_is_empty(iter);
326 }
327 
window_get_sel(struct window * win,struct iter * iter)328 int window_get_sel(struct window *win, struct iter *iter)
329 {
330 	*iter = win->sel;
331 	return !iter_is_empty(iter);
332 }
333 
window_get_prev(struct window * win,struct iter * iter)334 int window_get_prev(struct window *win, struct iter *iter)
335 {
336 	return win->get_prev(iter);
337 }
338 
window_get_next(struct window * win,struct iter * iter)339 int window_get_next(struct window *win, struct iter *iter)
340 {
341 	return win->get_next(iter);
342 }
343 
window_set_sel(struct window * win,struct iter * iter)344 void window_set_sel(struct window *win, struct iter *iter)
345 {
346 	int sel_nr, top_nr, bottom_nr;
347 	int upper_bound;
348 	struct iter tmp;
349 
350 	BUG_ON(iter_is_empty(&win->top));
351 	BUG_ON(iter_is_empty(iter));
352 	BUG_ON(iter->data0 != win->head.data0);
353 
354 	if (iters_equal(&win->sel, iter))
355 		return;
356 	win->sel = *iter;
357 
358 	tmp = win->head;
359 	win->get_next(&tmp);
360 	top_nr = 0;
361 	while (!iters_equal(&tmp, &win->top)) {
362 		win->get_next(&tmp);
363 		top_nr++;
364 	}
365 
366 	tmp = win->head;
367 	win->get_next(&tmp);
368 	sel_nr = 0;
369 	while (!iters_equal(&tmp, &win->sel)) {
370 		BUG_ON(!win->get_next(&tmp));
371 		sel_nr++;
372 	}
373 
374 	upper_bound = win->nr_rows / 2;
375 	if (scroll_offset < upper_bound)
376 		upper_bound = scroll_offset;
377 
378 	if (sel_nr < top_nr + upper_bound) { /* scroll up */
379 		tmp = win->head;
380 		win->get_next(&tmp);
381 		if (sel_nr < upper_bound) { /* no space above */
382 			win->top = tmp;
383 		} else {
384 			win->top = win->sel;
385 			while (upper_bound > 0) {
386 				win->get_prev(&win->top);
387 				upper_bound--;
388 			}
389 		}
390 	} else { /* scroll down */
391 		upper_bound = (win->nr_rows - 1) / 2;
392 		if (scroll_offset < upper_bound)
393 			upper_bound = scroll_offset;
394 
395 		tmp = win->sel;
396 		bottom_nr = sel_nr;
397 		if (sel_nr >= top_nr + win->nr_rows) { /* selected element not visible */
398 			while (sel_nr >= top_nr + win->nr_rows) {
399 				win->get_next(&win->top);
400 				top_nr++;
401 			}
402 		} else { /* selected element visible */
403 			while (bottom_nr + 1 < top_nr + win->nr_rows) {
404 				if (!win->get_next(&tmp)) { /* no space below */
405 					bottom_nr = sel_nr + upper_bound;
406 					break;
407 				}
408 				bottom_nr++;
409 			}
410 		}
411 
412 		while (bottom_nr < sel_nr + upper_bound) {
413 			if (!win->get_next(&tmp))
414 				break;
415 			bottom_nr++;
416 			win->get_next(&win->top);
417 		}
418 	}
419 	sel_changed(win);
420 }
421 
window_goto_top(struct window * win)422 void window_goto_top(struct window *win)
423 {
424 	struct iter old_sel;
425 
426 	old_sel = win->sel;
427 	win->sel = win->head;
428 	win->get_next(&win->sel);
429 	win->top = win->sel;
430 	while (!selectable(win, &win->sel))
431 		win->get_next(&win->sel);
432 	if (!iters_equal(&old_sel, &win->sel))
433 		sel_changed(win);
434 }
435 
window_goto_bottom(struct window * win)436 void window_goto_bottom(struct window *win)
437 {
438 	struct iter old_sel;
439 	int count;
440 
441 	old_sel = win->sel;
442 	win->sel = win->head;
443 	win->get_prev(&win->sel);
444 	win->top = win->sel;
445 	count = win->nr_rows - 1;
446 	while (count) {
447 		struct iter iter = win->top;
448 
449 		if (!win->get_prev(&iter))
450 			break;
451 		win->top = iter;
452 		count--;
453 	}
454 	while (!selectable(win, &win->sel))
455 		win->get_prev(&win->sel);
456 	if (!iters_equal(&old_sel, &win->sel))
457 		sel_changed(win);
458 }
459 
window_page_up(struct window * win)460 void window_page_up(struct window *win)
461 {
462 	struct iter sel = win->sel;
463 	struct iter top = win->top;
464 	int up;
465 
466 	for (up = 0; up < win->nr_rows - 1; up++) {
467 		if (!win->get_prev(&sel) || !win->get_prev(&top))
468 			break;
469 		if (selectable(win, &sel)) {
470 			win->sel = sel;
471 			win->top = top;
472 		}
473 	}
474 
475 	sel_changed(win);
476 }
477 
window_half_page_up(struct window * win)478 void window_half_page_up(struct window *win)
479 {
480 	struct iter sel = win->sel;
481 	struct iter top = win->top;
482 	int up;
483 
484 	for (up = 0; up < (win->nr_rows - 1) / 2; up++) {
485 		if (!win->get_prev(&sel) || !win->get_prev(&top))
486 			break;
487 		if (selectable(win, &sel)) {
488 			win->sel = sel;
489 			win->top = top;
490 		}
491 	}
492 
493 	sel_changed(win);
494 }
495 
window_bottom(struct window * win)496 static struct iter window_bottom(struct window *win)
497 {
498 	struct iter bottom = win->top;
499 	struct iter iter = win->top;
500 	int down;
501 
502 	for (down = 0; down < win->nr_rows - 1; down++) {
503 		if (!win->get_next(&iter))
504 			break;
505 		bottom = iter;
506 	}
507 
508 	return bottom;
509 }
510 
window_page_down(struct window * win)511 void window_page_down(struct window *win)
512 {
513 	struct iter sel = win->sel;
514 	struct iter bot = window_bottom(win);
515 	struct iter top = win->top;
516 	int down;
517 
518 	for (down = 0; down < win->nr_rows - 1; down++) {
519 		if (!win->get_next(&sel) || !win->get_next(&bot))
520 			break;
521 		win->get_next(&top);
522 		if (selectable(win, &sel)) {
523 			win->sel = sel;
524 			win->top = top;
525 		}
526 	}
527 
528 	sel_changed(win);
529 }
530 
window_half_page_down(struct window * win)531 void window_half_page_down(struct window *win)
532 {
533 	struct iter sel = win->sel;
534 	struct iter bot = window_bottom(win);
535 	struct iter top = win->top;
536 	int down;
537 
538 	for (down = 0; down < (win-> nr_rows - 1) / 2; down++) {
539 		if (!win->get_next(&sel) || !win->get_next(&bot))
540 			break;
541 		win->get_next(&top);
542 		if (selectable(win, &sel)) {
543 			win->sel = sel;
544 			win->top = top;
545 		}
546 	}
547 
548 	sel_changed(win);
549 }
550 
551 
window_scroll_down(struct window * win)552 void window_scroll_down(struct window *win)
553 {
554 	struct iter bot = window_bottom(win);
555 	struct iter top = win->top;
556 	if (!win->get_next(&bot)) return;
557 	if (!win->get_next(&top)) return;
558 	if (iters_equal(&win->top, &win->sel))
559 		win->get_next(&win->sel);
560 	win->top = top;
561 	while (!selectable(win, &win->sel))
562 		win->get_next(&win->sel);
563 	sel_changed(win);
564 }
565 
window_scroll_up(struct window * win)566 void window_scroll_up(struct window *win)
567 {
568 	struct iter top = win->top;
569 	if (!win->get_prev(&top)) return;
570 	struct iter bot = window_bottom(win);
571 	/* keep selected row on screen: */
572 	if (iters_equal(&bot, &win->sel))
573 		win->get_prev(&win->sel);
574 	win->top = top;
575 	while (!selectable(win, &win->sel))
576 		win->get_prev(&win->sel);
577 	sel_changed(win);
578 }
579 
window_goto_pos(struct window * win,int pos)580 static void window_goto_pos(struct window *win, int pos)
581 {
582 	struct iter old_sel;
583 	int i;
584 
585 	old_sel = win->sel;
586 	win->sel = win->top;
587 	for (i = 0; i < pos; i++)
588 		win->get_next(&win->sel);
589 	if (!iters_equal(&old_sel, &win->sel))
590 		sel_changed(win);
591 }
592 
window_page_top(struct window * win)593 void window_page_top(struct window *win)
594 {
595 	window_goto_pos(win, 0);
596 	while (!selectable(win, &win->sel))
597 		win->get_next(&win->sel);
598 }
599 
window_page_bottom(struct window * win)600 void window_page_bottom(struct window *win)
601 {
602 	window_goto_pos(win, win->nr_rows - 1);
603 	while (!selectable(win, &win->sel))
604 		win->get_prev(&win->sel);
605 }
606 
window_page_middle(struct window * win)607 void window_page_middle(struct window *win)
608 {
609 	window_goto_pos(win, win->nr_rows / 2);
610 	while (!selectable(win, &win->sel))
611 		win->get_next(&win->sel);
612 }
613 
window_get_nr_rows(struct window * win)614 int window_get_nr_rows(struct window *win)
615 {
616 	return win->nr_rows;
617 }
618