aboutsummaryrefslogtreecommitdiffstats
path: root/src/ui/menu.c
blob: c0e8d58fc4dbef4b5cdd1d7959feb61875f694f1 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#include <string.h>
#include <stdlib.h>

#include "uimenu.internal.h"
#include "../macros.h"

void uimenu_item_button_init(struct uimenu_item_button *button, unsigned id, const char *text, uimenu_button_action *action)
{
  button->header.type = UMPS__MENU_TYPE_BUTTON;
  button->header.next = button->header.prev = NULL;

  button->id = id;
  button->text = text ? strdup(text) : NULL;
  button->enabled = true;
  button->action = action;
}

void uimenu_item_menu_init(struct uimenu_item_menu *menu, const char *text)
{
  menu->header.type = UMPS__MENU_TYPE_MENU;
  menu->header.next = menu->header.prev = NULL;

  menu->text = text ? strdup(text) : NULL;
  menu->head = menu->tail = NULL;
  menu->nchildren = 0;
}

struct uimenu_item_header *uimenu__item_menu_spacer_create(void)
{
  struct uimenu_item_header *spacer = malloc(sizeof(struct uimenu_item_header));
  spacer->type = UMPS__MENU_TYPE_SPACER;
  spacer->next = spacer->prev = NULL;
  return spacer;
}

/* menu items will be freed on removal */
void uimenu_menu_add_spacer(struct uimenu_item_menu *menu, struct uimenu_item_header *where, bool after)
{
  struct uimenu_item_header *spc = uimenu__item_menu_spacer_create();
  uimenu_menu_add(menu, where, spc, after);
}

void uimenu_menu_add(struct uimenu_item_menu *menu, struct uimenu_item_header *where, struct uimenu_item_header *item, bool after)
{
  if (!menu->head) {
    /* edge case: menu has no children :((((( */
    menu->head = menu->tail = item;
    item->next = item->prev = NULL;
    ++menu->nchildren;
    return;
  }

  umps_assert(where);

  if (after) {
    item->prev = where;
    item->next = where->next;
    where->next = item;

    if (!item->next) menu->tail = item;
  } else {
    item->next = where;
    item->prev = where->prev;
    where->prev = item;

    if (!item->prev) menu->head = item;
  }

  ++menu->nchildren;
}

void uimenu__item_free(struct uimenu_item_header *item)
{
  switch (item->type) {
    case UMPS__MENU_TYPE_SPACER:
      free(item);
      break;
    case UMPS__MENU_TYPE_BUTTON: {
      struct uimenu_item_button *btn = (struct uimenu_item_button *)item;
      free(btn->text);
      free(btn);
      break;
    }
    case UMPS__MENU_TYPE_MENU:
      uimenu_menu_free((struct uimenu_item_menu *)item);
      break;
    default:
      umps_trap;
  }
}

void uimenu_menu_remove(struct uimenu_item_menu *menu, struct uimenu_item_header *item)
{
  if (item->prev) {
    item->prev->next = item->next;
  } else {
    menu->head = item->next;
  }

  if (item->next) {
    item->next->prev = item->prev;
  } else {
    menu->tail = item->prev;
  }

  --menu->nchildren;
}

/* frees children */
void uimenu_menu_free(struct uimenu_item_menu *menu)
{
  for (struct uimenu_item_header *cur = menu->head, *next; cur; cur = next) {
    next = cur->next;
    uimenu__item_free(cur);
  }
  free(menu->text);
  free(menu);
}