UNIXworkcode

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 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**)&current_builder->menus_begin, 68 (void**)&current_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 // create menu 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