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