ui/common/menu.c

changeset 431
bb7da585debc
parent 421
3b969399f962
child 440
7c4b9cba09ca
equal deleted inserted replaced
169:fe49cff3c571 431:bb7da585debc
1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 *
4 * Copyright 2023 Olaf Wintermann. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
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**)&current_builder->menus_begin,
52 (void**)&current_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 // create menu
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_RADIO_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 item->addseparator = args.addseparator;
202
203 add_item((UiMenuItemI*)item);
204 }
205
206 void ui_menu_checkitemlist_create(UiMenuItemListArgs args) {
207 UiMenuItemList* item = malloc(sizeof(UiMenuItemList));
208 mitem_set_id(&item->item);
209 item->item.prev = NULL;
210 item->item.next = NULL;
211 item->item.type = UI_MENU_CHECKITEM_LIST;
212 item->callback = args.onselect;
213 item->userdata = args.onselectdata;
214 item->varname = nl_strdup(args.varname);
215
216 add_item((UiMenuItemI*)item);
217 }
218
219 void ui_menu_radioitemlist_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_RADIOITEM_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
233 void uic_add_menu_to_stack(UiMenu* menu) {
234 cxListInsert(current_builder->current, 0, menu);
235 }
236
237 UiMenu* uic_get_menu_list(void) {
238 return current_builder->menus_begin;
239 }
240
241 UIEXPORT void ui_menu_close(void) {
242 UiMenu* menu = cxListAt(current_builder->current, 0);
243 menu->end = 1;
244 }
245
246 UIEXPORT int ui_menu_is_open(void) {
247 UiMenu* menu = cxListAt(current_builder->current, 0);
248 if (menu->end) {
249 ui_menu_end();
250 return 0;
251 }
252 return 1;
253 }
254
255
256 void ui_contextmenu_builder(UiMenuBuilder **out_builder) {
257 UiMenuBuilder *builder = malloc(sizeof(UiMenuBuilder));
258 builder->menus_begin = NULL;
259 builder->menus_end = NULL;
260 builder->current = cxLinkedListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS);
261 current_builder = builder;
262 *out_builder = builder;
263
264 ui_menu_create(NULL);
265 }
266
267
268
269 static void free_menuitem(UiMenuItemI *item) {
270 switch(item->type) {
271 default: break;
272 case UI_MENU: {
273 UiMenu *menu = (UiMenu*)item;
274 UiMenuItemI *m = menu->items_begin;
275 while(m) {
276 UiMenuItemI *next = m->next;
277 free_menuitem(m);
278 m = next;
279 }
280 break;
281 }
282 case UI_MENU_ITEM: {
283 UiMenuItem *i = (UiMenuItem*)item;
284 free(i->groups);
285 free(i->label);
286 free(i->stockid);
287 free(i->icon);
288 break;
289 }
290 case UI_MENU_CHECK_ITEM: {
291 UiMenuCheckItem *i = (UiMenuCheckItem*)item;
292 free(i->groups);
293 free(i->label);
294 free(i->stockid);
295 free(i->icon);
296 free(i->varname);
297 break;
298 }
299 case UI_MENU_RADIO_ITEM: {
300 UiMenuRadioItem *i = (UiMenuRadioItem*)item;
301 free(i->groups);
302 free(i->label);
303 free(i->stockid);
304 free(i->icon);
305 //free(i->varname);
306 break;
307 }
308 case UI_MENU_ITEM_LIST: {
309 break;
310 }
311 case UI_MENU_CHECKITEM_LIST: {
312 break;
313 }
314 case UI_MENU_RADIOITEM_LIST: {
315 break;
316 }
317 }
318
319 free(item);
320 }
321
322 void ui_menubuilder_free(UiMenuBuilder *builder) {
323 UiMenuItemI *m = &builder->menus_begin->item;
324 while(m) {
325 UiMenuItemI *next = m->next;
326 free_menuitem(m);
327 m = next;
328 }
329 cxListDestroy(builder->current);
330 free(builder);
331 }

mercurial