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 #include "menu.h"
30
31 #include <stdarg.h>
32 #include <string.h>
33 #include <stdio.h>
34
35 #include <cx/linked_list.h>
36 #include <cx/array_list.h>
37
38
39 static UiMenuBuilder *current_builder;
40 static UiMenuBuilder global_builder;
41
42 static int menu_item_counter =
0;
43
44 void uic_menu_init(
void) {
45 global_builder.current = cxLinkedListCreate(cxDefaultAllocator,
NULL,
CX_STORE_POINTERS);
46 current_builder = &global_builder;
47 }
48
49 static void add_menu(UiMenu *menu) {
50 cx_linked_list_add(
51 (
void**)¤t_builder->menus_begin,
52 (
void**)¤t_builder->menus_end,
53 offsetof(UiMenu, item.prev),
54 offsetof(UiMenu, item.next),
55 menu);
56 }
57
58 static void add_item(UiMenuItemI *item) {
59 UiMenu *menu = cxListAt(current_builder->current,
0);
60 cx_linked_list_add(
61 (
void**)&menu->items_begin,
62 (
void**)&menu->items_end,
63 offsetof(UiMenu, item.prev),
64 offsetof(UiMenu, item.next),
65 item);
66 }
67
68 static void mitem_set_id(UiMenuItemI *item) {
69 snprintf(item->id,
8,
"%x", menu_item_counter++);
70 }
71
72 static char* nl_strdup(
const char* s) {
73 return s ? strdup(s) :
NULL;
74 }
75
76 int* uic_copy_groups(
const int* groups,
size_t *ngroups) {
77 *ngroups =
0;
78 if (!groups) {
79 return NULL;
80 }
81
82 size_t n;
83 for (n =
0; groups[n] > -
1; n++) { }
84
85 if (ngroups >
0) {
86 int* newarray = calloc(n+
1,
sizeof(
int));
87 memcpy(newarray, groups, n *
sizeof(
int));
88 newarray[n] = -
1;
89 *ngroups = n;
90 return newarray;
91 }
92 return NULL;
93 }
94
95 void ui_menu_create(
const char *label) {
96
97 UiMenu *menu = malloc(
sizeof(UiMenu));
98 mitem_set_id(&menu->item);
99 menu->item.prev =
NULL;
100 menu->item.next =
NULL;
101 menu->item.type =
UI_MENU;
102
103 menu->label = label;
104 menu->items_begin =
NULL;
105 menu->items_end =
NULL;
106 menu->parent =
NULL;
107
108 menu->end =
0;
109
110 if (cxListSize(current_builder->current) ==
0) {
111 add_menu(menu);
112 }
113 else {
114 add_item((UiMenuItemI*)menu);
115 }
116 uic_add_menu_to_stack(menu);
117 }
118
119 UIEXPORT void ui_menu_end(
void) {
120 cxListRemove(current_builder->current,
0);
121 if(cxListSize(current_builder->current) ==
0) {
122 current_builder = &global_builder;
123 }
124 }
125
126
127
128 void ui_menuitem_create(UiMenuItemArgs args) {
129 UiMenuItem* item = malloc(
sizeof(UiMenuItem));
130 mitem_set_id(&item->item);
131 item->item.prev =
NULL;
132 item->item.next =
NULL;
133 item->item.type =
UI_MENU_ITEM;
134
135 item->label = nl_strdup(args.label);
136 item->stockid = nl_strdup(args.stockid);
137 item->icon = nl_strdup(args.icon);
138 item->userdata = args.onclickdata;
139 item->callback = args.onclick;
140 item->groups = uic_copy_groups(args.groups, &item->ngroups);
141
142 add_item((UiMenuItemI*)item);
143 }
144
145 void ui_menuseparator() {
146 UiMenuItemI *item = malloc(
sizeof(UiMenuItemI));
147 item->id[
0] =
0;
148 item->prev =
NULL;
149 item->next =
NULL;
150 item->type =
UI_MENU_SEPARATOR;
151
152 add_item((UiMenuItemI*)item);
153 }
154
155 void ui_menu_toggleitem_create(UiMenuToggleItemArgs args) {
156 UiMenuCheckItem *item = malloc(
sizeof(UiMenuCheckItem));
157 mitem_set_id(&item->item);
158 item->item.prev =
NULL;
159 item->item.next =
NULL;
160 item->item.type =
UI_MENU_CHECK_ITEM;
161
162 item->label = nl_strdup(args.label);
163 item->stockid = nl_strdup(args.stockid);
164 item->icon = nl_strdup(args.icon);
165 item->varname = nl_strdup(args.varname);
166 item->userdata = args.onchangedata;
167 item->callback = args.onchange;
168 item->groups = uic_copy_groups(args.groups, &item->ngroups);
169
170 add_item((UiMenuItemI*)item);
171 }
172
173 void ui_menu_radioitem_create(UiMenuToggleItemArgs args) {
174 UiMenuCheckItem* item = malloc(
sizeof(UiMenuCheckItem));
175 mitem_set_id(&item->item);
176 item->item.prev =
NULL;
177 item->item.next =
NULL;
178 item->item.type =
UI_MENU_CHECK_ITEM;
179
180 item->label = nl_strdup(args.label);
181 item->stockid = nl_strdup(args.stockid);
182 item->icon = nl_strdup(args.icon);
183 item->varname = nl_strdup(args.varname);
184 item->userdata = args.onchangedata;
185 item->callback = args.onchange;
186 item->groups = uic_copy_groups(args.groups, &item->ngroups);
187
188 add_item((UiMenuItemI*)item);
189 }
190
191 void ui_menu_itemlist_create(UiMenuItemListArgs args) {
192 UiMenuItemList*item = malloc(
sizeof(UiMenuItemList));
193 mitem_set_id(&item->item);
194 item->item.prev =
NULL;
195 item->item.next =
NULL;
196 item->item.type =
UI_MENU_ITEM_LIST;
197 item->getvalue = args.getvalue;
198 item->callback = args.onselect;
199 item->userdata = args.onselectdata;
200 item->varname = nl_strdup(args.varname);
201
202 add_item((UiMenuItemI*)item);
203 }
204
205 void ui_menu_checkitemlist_create(UiMenuItemListArgs args) {
206 UiMenuItemList* item = malloc(
sizeof(UiMenuItemList));
207 mitem_set_id(&item->item);
208 item->item.prev =
NULL;
209 item->item.next =
NULL;
210 item->item.type =
UI_MENU_CHECKITEM_LIST;
211 item->callback = args.onselect;
212 item->userdata = args.onselectdata;
213 item->varname = nl_strdup(args.varname);
214
215 add_item((UiMenuItemI*)item);
216 }
217
218 void ui_menu_radioitemlist_create(UiMenuItemListArgs args) {
219 UiMenuItemList* item = malloc(
sizeof(UiMenuItemList));
220 mitem_set_id(&item->item);
221 item->item.prev =
NULL;
222 item->item.next =
NULL;
223 item->item.type =
UI_MENU_RADIOITEM_LIST;
224 item->callback = args.onselect;
225 item->userdata = args.onselectdata;
226 item->varname = nl_strdup(args.varname);
227
228 add_item((UiMenuItemI*)item);
229 }
230
231
232 void uic_add_menu_to_stack(UiMenu* menu) {
233 cxListInsert(current_builder->current,
0, menu);
234 }
235
236 UiMenu* uic_get_menu_list(
void) {
237 return current_builder->menus_begin;
238 }
239
240 UIEXPORT void ui_menu_close(
void) {
241 UiMenu* menu = cxListAt(current_builder->current,
0);
242 menu->end =
1;
243 }
244
245 UIEXPORT int ui_menu_is_open(
void) {
246 UiMenu* menu = cxListAt(current_builder->current,
0);
247 if (menu->end) {
248 ui_menu_end();
249 return 0;
250 }
251 return 1;
252 }
253
254
255 void ui_contextmenu_builder(UiMenuBuilder **out_builder) {
256 UiMenuBuilder *builder = malloc(
sizeof(UiMenuBuilder));
257 builder->menus_begin =
NULL;
258 builder->menus_end =
NULL;
259 builder->current = cxLinkedListCreate(cxDefaultAllocator,
NULL,
CX_STORE_POINTERS);
260 current_builder = builder;
261 *out_builder = builder;
262
263 ui_menu_create(
NULL);
264 }
265
266
267
268 static void free_menuitem(UiMenuItemI *item) {
269 switch(item->type) {
270 default:
break;
271 case UI_MENU: {
272 UiMenu *menu = (UiMenu*)item;
273 UiMenuItemI *m = menu->items_begin;
274 while(m) {
275 UiMenuItemI *next = m->next;
276 free_menuitem(m);
277 m = next;
278 }
279 break;
280 }
281 case UI_MENU_ITEM: {
282 UiMenuItem *i = (UiMenuItem*)item;
283 free(i->groups);
284 free(i->label);
285 free(i->stockid);
286 free(i->icon);
287 break;
288 }
289 case UI_MENU_CHECK_ITEM: {
290 UiMenuCheckItem *i = (UiMenuCheckItem*)item;
291 free(i->groups);
292 free(i->label);
293 free(i->stockid);
294 free(i->icon);
295 free(i->varname);
296 break;
297 }
298 case UI_MENU_RADIO_ITEM: {
299 UiMenuRadioItem *i = (UiMenuRadioItem*)item;
300 free(i->groups);
301 free(i->label);
302 free(i->stockid);
303 free(i->icon);
304
305 break;
306 }
307 case UI_MENU_ITEM_LIST: {
308 break;
309 }
310 case UI_MENU_CHECKITEM_LIST: {
311 break;
312 }
313 case UI_MENU_RADIOITEM_LIST: {
314 break;
315 }
316 }
317
318 free(item);
319 }
320
321 void ui_menubuilder_free(UiMenuBuilder *builder) {
322 UiMenuItemI *m = &builder->menus_begin->item;
323 while(m) {
324 UiMenuItemI *next = m->next;
325 free_menuitem(m);
326 m = next;
327 }
328 cxListDestroy(builder->current);
329 free(builder);
330 }
331