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