1 /*
2  * MOC - music on console
3  * Copyright (C) 2002 - 2006 Damian Pietras <daper@daper.net>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  */
11 
12 #ifdef HAVE_CONFIG_H
13 #include "config.h"
14 #endif
15 
16 #define _GNU_SOURCE
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <string.h>
20 
21 #ifdef HAVE_NCURSESW_H
22 # include <ncursesw/curses.h>
23 #elif HAVE_NCURSES_H
24 # include <ncurses.h>
25 #elif HAVE_CURSES_H
26 # include <curses.h>
27 #endif
28 
29 #include <assert.h>
30 #include <ctype.h>
31 
32 #include "common.h"
33 #include "compat.h"
34 #include "options.h"
35 #include "menu.h"
36 #include "files.h"
37 #include "rbtree.h"
38 #include "utf8.h"
39 
40 /* Draw menu item on a given position from the top of the menu. */
draw_item(const struct menu * menu,const struct menu_item * mi,const int pos,const int item_info_pos,int title_space,const int number_space,const int draw_selected)41 static void draw_item (const struct menu *menu, const struct menu_item *mi,
42 		const int pos, const int item_info_pos, int title_space,
43 		const int number_space, const int draw_selected)
44 {
45 	int title_width, queue_pos_len = 0;
46 	int ix, x;
47 	int y ATTR_UNUSED;
48 	char buf[32];
49 
50 	assert (menu != NULL);
51 	assert (mi != NULL);
52 	assert (pos >= 0);
53 	assert (item_info_pos > menu->posx
54 			|| (!menu->show_time && !menu->show_format));
55 	assert (title_space > 0);
56 	assert (number_space == 0 || number_space >= 2);
57 
58 	wmove (menu->win, pos, menu->posx);
59 
60 	if (number_space) {
61 		if (draw_selected && mi == menu->selected && mi == menu->marked)
62 			wattrset (menu->win, menu->info_attr_sel_marked);
63 		else if (draw_selected && mi == menu->selected)
64 			wattrset (menu->win, menu->info_attr_sel);
65 		else if (mi == menu->marked)
66 			wattrset (menu->win, menu->info_attr_marked);
67 		else
68 			wattrset (menu->win, menu->info_attr_normal);
69 		xwprintw (menu->win, "%*d ", number_space - 1, mi->num + 1);
70 	}
71 
72 	/* Set attributes */
73 	if (draw_selected && mi == menu->selected && mi == menu->marked)
74 		wattrset (menu->win, mi->attr_sel_marked);
75 	else if (draw_selected && mi == menu->selected)
76 		wattrset (menu->win, mi->attr_sel);
77 	else if (mi == menu->marked)
78 		wattrset (menu->win, mi->attr_marked);
79 	else
80 		wattrset (menu->win, mi->attr_normal);
81 
82 	/* Compute the length of the queue position if nonzero */
83 	if (mi->queue_pos) {
84 		sprintf (buf, "%d", mi->queue_pos);
85 		queue_pos_len = strlen(buf) + 2;
86 		title_space -= queue_pos_len;
87 	}
88 
89 	title_width = strwidth (mi->title);
90 
91 	getyx (menu->win, y, x);
92 	if (title_width <= title_space || mi->align == MENU_ALIGN_LEFT)
93 		xwaddnstr (menu->win, mi->title, title_space);
94 	else {
95 		char *ptr;
96 
97 		ptr = xstrtail (mi->title, title_space);
98 		xwaddstr (menu->win, ptr);
99 		free (ptr);
100 	}
101 
102 	/* Fill the remainder of the title field with spaces. */
103 	if (mi == menu->selected) {
104 		getyx (menu->win, y, ix);
105 		while (ix < x + title_space) {
106 			waddch (menu->win, ' ');
107 			ix += 1;
108 		}
109 	}
110 
111 	/* Description. */
112 	if (draw_selected && mi == menu->selected && mi == menu->marked)
113 		wattrset (menu->win, menu->info_attr_sel_marked);
114 	else if (draw_selected && mi == menu->selected)
115 		wattrset (menu->win, menu->info_attr_sel);
116 	else if (mi == menu->marked)
117 		wattrset (menu->win, menu->info_attr_marked);
118 	else
119 		wattrset (menu->win, menu->info_attr_normal);
120 	wmove (menu->win, pos, item_info_pos - queue_pos_len);
121 
122 	/* Position in queue. */
123 	if (mi->queue_pos) {
124 		xwaddstr (menu->win, "[");
125 		xwaddstr (menu->win, buf);
126 		xwaddstr (menu->win, "]");
127 	}
128 
129 	if (menu->show_time && menu->show_format
130 			&& (*mi->time || *mi->format))
131 		xwprintw (menu->win, "[%5s|%3s]",
132 				mi->time ? mi->time : "	 ",
133 				mi->format);
134 	else if (menu->show_time && mi->time[0])
135 		xwprintw (menu->win, "[%5s]", mi->time);
136 	else if (menu->show_format && mi->format[0])
137 		xwprintw (menu->win, "[%3s]", mi->format);
138 }
139 
menu_draw(const struct menu * menu,const int active)140 void menu_draw (const struct menu *menu, const int active)
141 {
142 	struct menu_item *mi;
143 	int title_width;
144 	int info_pos;
145 	int number_space = 0;
146 
147 	assert (menu != NULL);
148 
149 	if (menu->number_items) {
150 		int count = menu->nitems / 10;
151 
152 		number_space = 2; /* begin from 1 digit and a space char */
153 		while (count) {
154 			count /= 10;
155 			number_space++;
156 		}
157 	}
158 	else
159 		number_space = 0;
160 
161 	if (menu->show_time || menu->show_format) {
162 		title_width = menu->width - 2; /* -2 for brackets */
163 		if (menu->show_time)
164 			title_width -= 5; /* 00:00 */
165 		if (menu->show_format)
166 			title_width -= 3; /* MP3 */
167 		if (menu->show_time && menu->show_format)
168 			title_width--; /* for | */
169 		info_pos = title_width;
170 	}
171 	else {
172 		title_width = menu->width;
173 		info_pos = title_width;
174 	}
175 
176 	title_width -= number_space;
177 
178 	for (mi = menu->top; mi && mi->num - menu->top->num < menu->height;
179 			mi = mi->next)
180 		draw_item (menu, mi, mi->num - menu->top->num + menu->posy,
181 				menu->posx + info_pos, title_width,
182 				number_space, active);
183 }
184 
185 /* Move the cursor to the selected file. */
menu_set_cursor(const struct menu * m)186 void menu_set_cursor (const struct menu *m)
187 {
188 	assert (m != NULL);
189 
190 	if (m->selected)
191 		wmove (m->win, m->selected->num - m->top->num + m->posy, m->posx);
192 }
193 
rb_compare(const void * a,const void * b,void * adata ATTR_UNUSED)194 static int rb_compare (const void *a, const void *b, void *adata ATTR_UNUSED)
195 {
196 	struct menu_item *mia = (struct menu_item *)a;
197 	struct menu_item *mib = (struct menu_item *)b;
198 
199 	return strcmp (mia->file, mib->file);
200 }
201 
rb_fname_compare(const void * key,const void * data,void * adata ATTR_UNUSED)202 static int rb_fname_compare (const void *key, const void *data,
203 		void *adata ATTR_UNUSED)
204 {
205 	const char *fname = (const char *)key;
206 	const struct menu_item *mi = (const struct menu_item *)data;
207 
208 	return strcmp (fname, mi->file);
209 }
210 
211 /* menu_items must be malloc()ed memory! */
menu_new(WINDOW * win,const int posx,const int posy,const int width,const int height)212 struct menu *menu_new (WINDOW *win, const int posx, const int posy,
213 		const int width, const int height)
214 {
215 	struct menu *menu;
216 
217 	assert (win != NULL);
218 	assert (posx >= 0);
219 	assert (posy >= 0);
220 	assert (width > 0);
221 	assert (height > 0);
222 
223 	menu = (struct menu *)xmalloc (sizeof(struct menu));
224 
225 	menu->win = win;
226 	menu->items = NULL;
227 	menu->nitems = 0;
228 	menu->top = NULL;
229 	menu->last = NULL;
230 	menu->selected = NULL;
231 	menu->posx = posx;
232 	menu->posy = posy;
233 	menu->width = width;
234 	menu->height = height;
235 	menu->marked = NULL;
236 	menu->show_time = 0;
237 	menu->show_format = 0;
238 	menu->info_attr_normal = A_NORMAL;
239 	menu->info_attr_sel = A_NORMAL;
240 	menu->info_attr_marked = A_NORMAL;
241 	menu->info_attr_sel_marked = A_NORMAL;
242 	menu->number_items = 0;
243 
244 	rb_init_tree (&menu->search_tree, rb_compare, rb_fname_compare, NULL);
245 
246 	return menu;
247 }
248 
menu_add(struct menu * menu,const char * title,const enum file_type type,const char * file)249 struct menu_item *menu_add (struct menu *menu, const char *title,
250 		const enum file_type type, const char *file)
251 {
252 	struct menu_item *mi;
253 
254 	assert (menu != NULL);
255 	assert (title != NULL);
256 
257 	mi = (struct menu_item *)xmalloc (sizeof(struct menu_item));
258 
259 	mi->title = xstrdup (title);
260 	mi->type = type;
261 	mi->file = xstrdup (file);
262 	mi->num = menu->nitems;
263 
264 	mi->attr_normal = A_NORMAL;
265 	mi->attr_sel = A_NORMAL;
266 	mi->attr_marked = A_NORMAL;
267 	mi->attr_sel_marked = A_NORMAL;
268 	mi->align = MENU_ALIGN_LEFT;
269 
270 	mi->time[0] = 0;
271 	mi->format[0] = 0;
272 	mi->queue_pos = 0;
273 
274 	mi->next = NULL;
275 	mi->prev = menu->last;
276 	if (menu->last)
277 		menu->last->next = mi;
278 
279 	if (!menu->items)
280 		menu->items = mi;
281 	if (!menu->top)
282 		menu->top = menu->items;
283 	if (!menu->selected)
284 		menu->selected = menu->items;
285 
286 	if (file)
287 		rb_insert (&menu->search_tree, (void *)mi);
288 
289 	menu->last = mi;
290 	menu->nitems++;
291 
292 	return mi;
293 }
294 
menu_add_from_item(struct menu * menu,const struct menu_item * mi)295 static struct menu_item *menu_add_from_item (struct menu *menu,
296 		const struct menu_item *mi)
297 {
298 	struct menu_item *new;
299 
300 	assert (menu != NULL);
301 	assert (mi != NULL);
302 
303 	new = menu_add (menu, mi->title, mi->type, mi->file);
304 
305 	new->attr_normal = mi->attr_normal;
306 	new->attr_sel = mi->attr_sel;
307 	new->attr_marked = mi->attr_marked;
308 	new->attr_sel_marked = mi->attr_sel_marked;
309 
310 	strncpy(new->time, mi->time, FILE_TIME_STR_SZ);
311 	strncpy(new->format, mi->format, FILE_FORMAT_SZ);
312 
313 	return new;
314 }
315 
get_item_relative(struct menu_item * mi,int to_move)316 static struct menu_item *get_item_relative (struct menu_item *mi,
317 		int to_move)
318 {
319 	assert (mi != NULL);
320 
321 	while (to_move) {
322 		struct menu_item *prev = mi;
323 
324 		if (to_move > 0) {
325 			mi = mi->next;
326 			to_move--;
327 		}
328 		else {
329 			mi = mi->prev;
330 			to_move++;
331 		}
332 
333 		if (!mi) {
334 			mi = prev;
335 			break;
336 		}
337 	}
338 
339 	return mi;
340 }
341 
menu_update_size(struct menu * menu,const int posx,const int posy,const int width,const int height)342 void menu_update_size (struct menu *menu, const int posx, const int posy,
343 		const int width, const int height)
344 {
345 	assert (menu != NULL);
346 	assert (posx >= 0);
347 	assert (posy >= 0);
348 	assert (width > 0);
349 	assert (height > 0);
350 
351 	menu->posx = posx;
352 	menu->posy = posy;
353 	menu->width = width;
354 	menu->height = height;
355 
356 	if (menu->selected && menu->top
357 			&& menu->selected->num >= menu->top->num + menu->height)
358 		menu->selected = get_item_relative (menu->top,
359 				menu->height - 1);
360 }
361 
menu_item_free(struct menu_item * mi)362 static void menu_item_free (struct menu_item *mi)
363 {
364 	assert (mi != NULL);
365 	assert (mi->title != NULL);
366 
367 	free (mi->title);
368 	if (mi->file)
369 		free (mi->file);
370 
371 	free (mi);
372 }
373 
menu_free(struct menu * menu)374 void menu_free (struct menu *menu)
375 {
376 	struct menu_item *mi;
377 
378 	assert (menu != NULL);
379 
380 	mi = menu->items;
381 	while (mi) {
382 		struct menu_item *next = mi->next;
383 
384 		menu_item_free (mi);
385 		mi = next;
386 	}
387 
388 	rb_clear (&menu->search_tree);
389 
390 	free (menu);
391 }
392 
menu_driver(struct menu * menu,const enum menu_request req)393 void menu_driver (struct menu *menu, const enum menu_request req)
394 {
395 	assert (menu != NULL);
396 
397 	if (menu->nitems == 0)
398 		return;
399 
400 	if (req == REQ_DOWN && menu->selected->next) {
401 		menu->selected = menu->selected->next;
402 		if (menu->selected->num >= menu->top->num + menu->height) {
403 			menu->top = get_item_relative (menu->selected,
404 					-menu->height / 2);
405 			if (menu->top->num > menu->nitems - menu->height)
406 				menu->top = get_item_relative (menu->last,
407 						-menu->height + 1);
408 		}
409 	}
410 	else if (req == REQ_UP && menu->selected->prev) {
411 		menu->selected = menu->selected->prev;
412 		if (menu->top->num > menu->selected->num)
413 			menu->top = get_item_relative (menu->selected,
414 					-menu->height / 2);
415 	}
416 	else if (req == REQ_PGDOWN && menu->selected->num < menu->nitems - 1) {
417 		if (menu->selected->num + menu->height - 1 < menu->nitems - 1) {
418 			menu->selected = get_item_relative (menu->selected,
419 					menu->height - 1);
420 			menu->top = get_item_relative (menu->top,
421 					menu->height - 1);
422 			if (menu->top->num > menu->nitems - menu->height)
423 				menu->top = get_item_relative (menu->last,
424 						-menu->height + 1);
425 		}
426 		else {
427 			menu->selected = menu->last;
428 			menu->top = get_item_relative (menu->last,
429 					-menu->height + 1);
430 		}
431 	}
432 	else if (req == REQ_PGUP && menu->selected->prev) {
433 		if (menu->selected->num - menu->height + 1 > 0) {
434 			menu->selected = get_item_relative (menu->selected,
435 					-menu->height + 1);
436 			menu->top = get_item_relative (menu->top,
437 					-menu->height + 1);
438 		}
439 		else {
440 			menu->selected = menu->items;
441 			menu->top = menu->items;
442 		}
443 	}
444 	else if (req == REQ_TOP) {
445 		menu->selected = menu->items;
446 		menu->top = menu->items;
447 	}
448 	else if (req == REQ_BOTTOM) {
449 		menu->selected = menu->last;
450 		menu->top = get_item_relative (menu->selected,
451 				-menu->height + 1);
452 	}
453 }
454 
455 /* Return the index of the currently selected item. */
menu_curritem(struct menu * menu)456 struct menu_item *menu_curritem (struct menu *menu)
457 {
458 	assert (menu != NULL);
459 
460 	return menu->selected;
461 }
462 
make_item_visible(struct menu * menu,struct menu_item * mi)463 static void make_item_visible (struct menu *menu, struct menu_item *mi)
464 {
465 	assert (menu != NULL);
466 	assert (mi != NULL);
467 
468 	if (mi->num < menu->top->num || mi->num >= menu->top->num + menu->height) {
469 		menu->top = get_item_relative(mi, -menu->height/2);
470 
471 		if (menu->top->num > menu->nitems - menu->height)
472 			menu->top = get_item_relative (menu->last,
473 					-menu->height + 1);
474 	}
475 
476 	if (menu->selected) {
477 		if (menu->selected->num < menu->top->num ||
478 				menu->selected->num >= menu->top->num + menu->height)
479 			menu->selected = mi;
480 	}
481 }
482 
483 /* Make this item selected */
menu_setcurritem(struct menu * menu,struct menu_item * mi)484 static void menu_setcurritem (struct menu *menu, struct menu_item *mi)
485 {
486 	assert (menu != NULL);
487 	assert (mi != NULL);
488 
489 	menu->selected = mi;
490 	make_item_visible (menu, mi);
491 }
492 
493 /* Make the item with this title selected. */
menu_setcurritem_title(struct menu * menu,const char * title)494 void menu_setcurritem_title (struct menu *menu, const char *title)
495 {
496 	struct menu_item *mi;
497 
498 	/* Find it */
499 	for (mi = menu->top; mi; mi = mi->next)
500 		if (!strcmp(mi->title, title))
501 			break;
502 
503 	if (mi)
504 		menu_setcurritem (menu, mi);
505 }
506 
menu_find_by_position(struct menu * menu,const int num)507 static struct menu_item *menu_find_by_position (struct menu *menu,
508 		const int num)
509 {
510 	struct menu_item *mi;
511 
512 	assert (menu != NULL);
513 
514 	mi = menu->top;
515 	while (mi && mi->num != num)
516 		mi = mi->next;
517 
518 	return mi;
519 }
520 
menu_set_state(struct menu * menu,const struct menu_state * st)521 void menu_set_state (struct menu *menu, const struct menu_state *st)
522 {
523 	assert (menu != NULL);
524 
525 	if (!(menu->selected = menu_find_by_position(menu, st->selected_item)))
526 		menu->selected = menu->last;
527 
528 	if (!(menu->top = menu_find_by_position(menu, st->top_item)))
529 		menu->top = get_item_relative (menu->last, menu->height + 1);
530 }
531 
menu_set_items_numbering(struct menu * menu,const int number)532 void menu_set_items_numbering (struct menu *menu, const int number)
533 {
534 	assert (menu != NULL);
535 
536 	menu->number_items = number;
537 }
538 
menu_get_state(const struct menu * menu,struct menu_state * st)539 void menu_get_state (const struct menu *menu, struct menu_state *st)
540 {
541 	assert (menu != NULL);
542 
543 	st->top_item = menu->top ? menu->top->num : -1;
544 	st->selected_item = menu->selected ? menu->selected->num : -1;
545 }
546 
menu_unmark_item(struct menu * menu)547 void menu_unmark_item (struct menu *menu)
548 {
549 	assert (menu != NULL);
550 	menu->marked = NULL;
551 }
552 
553 /* Make a new menu from elements matching pattern. */
menu_filter_pattern(const struct menu * menu,const char * pattern)554 struct menu *menu_filter_pattern (const struct menu *menu, const char *pattern)
555 {
556 	struct menu *new;
557 	const struct menu_item *mi;
558 
559 	assert (menu != NULL);
560 	assert (pattern != NULL);
561 
562 	new = menu_new (menu->win, menu->posx, menu->posy, menu->width,
563 			menu->height);
564 	menu_set_show_time (new, menu->show_time);
565 	menu_set_show_format (new, menu->show_format);
566 	menu_set_info_attr_normal (new, menu->info_attr_normal);
567 	menu_set_info_attr_sel (new, menu->info_attr_sel);
568 	menu_set_info_attr_marked (new, menu->info_attr_marked);
569 	menu_set_info_attr_sel_marked (new, menu->info_attr_sel_marked);
570 
571 	for (mi = menu->items; mi; mi = mi->next)
572 		if (strcasestr(mi->title, pattern))
573 			menu_add_from_item (new, mi);
574 
575 	if (menu->marked)
576 		menu_mark_item (new, menu->marked->file);
577 
578 	return new;
579 }
580 
menu_item_set_attr_normal(struct menu_item * mi,const int attr)581 void menu_item_set_attr_normal (struct menu_item *mi, const int attr)
582 {
583 	assert (mi != NULL);
584 
585 	mi->attr_normal = attr;
586 }
587 
menu_item_set_attr_sel(struct menu_item * mi,const int attr)588 void menu_item_set_attr_sel (struct menu_item *mi, const int attr)
589 {
590 	assert (mi != NULL);
591 
592 	mi->attr_sel = attr;
593 }
594 
menu_item_set_attr_sel_marked(struct menu_item * mi,const int attr)595 void menu_item_set_attr_sel_marked (struct menu_item *mi, const int attr)
596 {
597 	assert (mi != NULL);
598 
599 	mi->attr_sel_marked = attr;
600 }
601 
menu_item_set_attr_marked(struct menu_item * mi,const int attr)602 void menu_item_set_attr_marked (struct menu_item *mi, const int attr)
603 {
604 	assert (mi != NULL);
605 
606 	mi->attr_marked = attr;
607 }
608 
menu_item_set_time(struct menu_item * mi,const char * time)609 void menu_item_set_time (struct menu_item *mi, const char *time)
610 {
611 	assert (mi != NULL);
612 
613 	mi->time[sizeof(mi->time)-1] = 0;
614 	strncpy (mi->time, time, sizeof(mi->time));
615 	assert (mi->time[sizeof(mi->time)-1] == 0);
616 }
617 
menu_item_set_format(struct menu_item * mi,const char * format)618 void menu_item_set_format (struct menu_item *mi, const char *format)
619 {
620 	assert (mi != NULL);
621 	assert (format != NULL);
622 
623 	mi->format[sizeof(mi->format)-1] = 0;
624 	strncpy (mi->format, format,
625 			sizeof(mi->format));
626 	assert (mi->format[sizeof(mi->format)-1]
627 			== 0);
628 }
629 
menu_item_set_queue_pos(struct menu_item * mi,const int pos)630 void menu_item_set_queue_pos (struct menu_item *mi, const int pos)
631 {
632 	assert (mi != NULL);
633 
634 	mi->queue_pos = pos;
635 }
636 
menu_set_show_time(struct menu * menu,const int t)637 void menu_set_show_time (struct menu *menu, const int t)
638 {
639 	assert (menu != NULL);
640 
641 	menu->show_time = t;
642 }
643 
menu_set_show_format(struct menu * menu,const int t)644 void menu_set_show_format (struct menu *menu, const int t)
645 {
646 	assert (menu != NULL);
647 
648 	menu->show_format = t;
649 }
650 
menu_set_info_attr_normal(struct menu * menu,const int attr)651 void menu_set_info_attr_normal (struct menu *menu, const int attr)
652 {
653 	assert (menu != NULL);
654 
655 	menu->info_attr_normal = attr;
656 }
657 
menu_set_info_attr_sel(struct menu * menu,const int attr)658 void menu_set_info_attr_sel (struct menu *menu, const int attr)
659 {
660 	assert (menu != NULL);
661 
662 	menu->info_attr_sel = attr;
663 }
664 
menu_set_info_attr_marked(struct menu * menu,const int attr)665 void menu_set_info_attr_marked (struct menu *menu, const int attr)
666 {
667 	assert (menu != NULL);
668 
669 	menu->info_attr_marked = attr;
670 }
671 
menu_set_info_attr_sel_marked(struct menu * menu,const int attr)672 void menu_set_info_attr_sel_marked (struct menu *menu, const int attr)
673 {
674 	assert (menu != NULL);
675 
676 	menu->info_attr_sel_marked = attr;
677 }
678 
menu_item_get_type(const struct menu_item * mi)679 enum file_type menu_item_get_type (const struct menu_item *mi)
680 {
681 	assert (mi != NULL);
682 
683 	return mi->type;
684 }
685 
menu_item_get_file(const struct menu_item * mi)686 char *menu_item_get_file (const struct menu_item *mi)
687 {
688 	assert (mi != NULL);
689 
690 	return xstrdup (mi->file);
691 }
692 
menu_item_set_title(struct menu_item * mi,const char * title)693 void menu_item_set_title (struct menu_item *mi, const char *title)
694 {
695 	assert (mi != NULL);
696 
697 	if (mi->title)
698 		free (mi->title);
699 	mi->title = xstrdup (title);
700 }
701 
menu_nitems(const struct menu * menu)702 int menu_nitems (const struct menu *menu)
703 {
704 	assert (menu != NULL);
705 
706 	return menu->nitems;
707 }
708 
menu_find(struct menu * menu,const char * fname)709 struct menu_item *menu_find (struct menu *menu, const char *fname)
710 {
711 	struct rb_node *x;
712 
713 	assert (menu != NULL);
714 	assert (fname != NULL);
715 
716 	x = rb_search (&menu->search_tree, fname);
717 	if (rb_is_null(x))
718 		return NULL;
719 
720 	return (struct menu_item *)x->data;
721 }
722 
menu_mark_item(struct menu * menu,const char * file)723 void menu_mark_item (struct menu *menu, const char *file)
724 {
725 	struct menu_item *item;
726 
727 	assert (menu != NULL);
728 	assert (file != NULL);
729 
730 	item = menu_find (menu, file);
731 	if (item)
732 		menu->marked = item;
733 }
734 
menu_renumber_items(struct menu * menu)735 static void menu_renumber_items (struct menu *menu)
736 {
737 	int i = 0;
738 	struct menu_item *mi;
739 
740 	assert (menu != NULL);
741 
742 	for (mi = menu->items; mi; mi = mi->next)
743 		mi->num = i++;
744 
745 	assert (i == menu->nitems);
746 }
747 
menu_delete(struct menu * menu,struct menu_item * mi)748 static void menu_delete (struct menu *menu, struct menu_item *mi)
749 {
750 	assert (menu != NULL);
751 	assert (mi != NULL);
752 
753 	if (mi->prev)
754 		mi->prev->next = mi->next;
755 	if (mi->next)
756 		mi->next->prev = mi->prev;
757 
758 	if (menu->items == mi)
759 		menu->items = mi->next;
760 	if (menu->last == mi)
761 		menu->last = mi->prev;
762 
763 	if (menu->marked == mi)
764 		menu->marked = NULL;
765 	if (menu->selected == mi)
766 		menu->selected = mi->next ? mi->next : mi->prev;
767 	if (menu->top == mi)
768 		menu->top = mi->next ? mi->next : mi->prev;
769 
770 	if (mi->file)
771 		rb_delete (&menu->search_tree, mi->file);
772 
773 	menu->nitems--;
774 	menu_renumber_items (menu);
775 
776 	menu_item_free (mi);
777 }
778 
menu_del_item(struct menu * menu,const char * fname)779 void menu_del_item (struct menu *menu, const char *fname)
780 {
781 	struct menu_item *mi;
782 
783 	assert (menu != NULL);
784 	assert (fname != NULL);
785 
786 	mi = menu_find (menu, fname);
787 	assert (mi != NULL);
788 
789 	menu_delete (menu, mi);
790 }
791 
menu_item_set_align(struct menu_item * mi,const enum menu_align align)792 void menu_item_set_align (struct menu_item *mi, const enum menu_align align)
793 {
794 	assert (mi != NULL);
795 
796 	mi->align = align;
797 }
798 
menu_setcurritem_file(struct menu * menu,const char * file)799 void menu_setcurritem_file (struct menu *menu, const char *file)
800 {
801 	struct menu_item *mi;
802 
803 	assert (menu != NULL);
804 	assert (file != NULL);
805 
806 	mi = menu_find (menu, file);
807 	if (mi)
808 		menu_setcurritem (menu, mi);
809 }
810 /* Return non-zero value if the item in in the visible part of the menu. */
menu_is_visible(const struct menu * menu,const struct menu_item * mi)811 int menu_is_visible (const struct menu *menu, const struct menu_item *mi)
812 {
813 	assert (menu != NULL);
814 	assert (mi != NULL);
815 
816 	if (mi->num >= menu->top->num
817 			&& mi->num < menu->top->num + menu->height)
818 		return 1;
819 
820 	return 0;
821 }
822 
menu_items_swap(struct menu * menu,struct menu_item * mi1,struct menu_item * mi2)823 static void menu_items_swap (struct menu *menu, struct menu_item *mi1,
824 		struct menu_item *mi2)
825 {
826 	int t;
827 
828 	assert (menu != NULL);
829 	assert (mi1 != NULL);
830 	assert (mi2 != NULL);
831 	assert (mi1 != mi2);
832 
833 	/* if they are next to each other, change the pointers so that mi2
834 	 * is the second one */
835 	if (mi2->next == mi1) {
836 		struct menu_item *i = mi1;
837 
838 		mi1 = mi2;
839 		mi2 = i;
840 	}
841 
842 	if (mi1->next == mi2) {
843 		if (mi2->next)
844 			mi2->next->prev = mi1;
845 		if (mi1->prev)
846 			mi1->prev->next = mi2;
847 
848 		mi1->next = mi2->next;
849 		mi2->prev = mi1->prev;
850 		mi1->prev = mi2;
851 		mi2->next = mi1;
852 	}
853 	else {
854 		if (mi2->next)
855 			mi2->next->prev = mi1;
856 		if (mi2->prev)
857 			mi2->prev->next = mi1;
858 		mi2->next = mi1->next;
859 		mi2->prev = mi1->prev;
860 
861 		if (mi1->next)
862 			mi1->next->prev = mi2;
863 		if (mi1->prev)
864 			mi1->prev->next = mi2;
865 		mi1->next = mi2->next;
866 		mi1->prev = mi2->prev;
867 	}
868 
869 	t = mi1->num;
870 	mi1->num = mi2->num;
871 	mi2->num = t;
872 
873 	if (menu->top == mi1)
874 		menu->top = mi2;
875 	else if (menu->top == mi2)
876 		menu->top = mi1;
877 
878 	if (menu->last == mi1)
879 		menu->last = mi2;
880 	else if (menu->last == mi2)
881 		menu->last = mi1;
882 
883 	if (menu->items == mi1)
884 		menu->items = mi2;
885 	else if (menu->items == mi2)
886 		menu->items = mi1;
887 }
888 
menu_swap_items(struct menu * menu,const char * file1,const char * file2)889 void menu_swap_items (struct menu *menu, const char *file1, const char *file2)
890 {
891 	struct menu_item *mi1, *mi2;
892 
893 	assert (menu != NULL);
894 	assert (file1 != NULL);
895 	assert (file2 != NULL);
896 
897 	if ((mi1 = menu_find(menu, file1)) && (mi2 = menu_find(menu, file2))
898 			&& mi1 != mi2) {
899 		menu_items_swap (menu, mi1, mi2);
900 
901 		/* make sure that the selected item is visible */
902 		menu_setcurritem (menu, menu->selected);
903 	}
904 }
905 
906 /* Make sure that this file is visible in the menu. */
menu_make_visible(struct menu * menu,const char * file)907 void menu_make_visible (struct menu *menu, const char *file)
908 {
909 	struct menu_item *mi;
910 
911 	assert (menu != NULL);
912 	assert (file != NULL);
913 
914 	if ((mi = menu_find(menu, file)))
915 		make_item_visible (menu, mi);
916 }
917