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