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 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_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 //free(i->varname); 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