--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/common/menu.c Sun Jan 21 16:30:18 2024 +0100 @@ -0,0 +1,259 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2023 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "menu.h" + +#include <stdarg.h> +#include <string.h> + +#include <cx/linked_list.h> +#include <cx/array_list.h> + +static UiMenu *menus_begin; +static UiMenu *menus_end; +static CxList *current; + +static void add_menu(UiMenu *menu) { + cx_linked_list_add( + (void**)&menus_begin, + (void**)&menus_end, + offsetof(UiMenu, item.prev), + offsetof(UiMenu, item.next), + menu); +} + +static void add_item(UiMenuItemI *item) { + UiMenu *menu = cxListAt(current, 0); + cx_linked_list_add( + (void**)&menu->items_begin, + (void**)&menu->items_end, + offsetof(UiMenu, item.prev), + offsetof(UiMenu, item.next), + item); +} + +static char* nl_strdup(const char* s) { + return s ? strdup(s) : NULL; +} + +static int* copy_groups(int* groups, size_t *ngroups) { + *ngroups = 0; + if (!groups) { + return NULL; + } + + size_t n; + for (n = 0; groups[n] > -1; n++) { } + + if (ngroups > 0) { + int* newarray = calloc(n, sizeof(int)); + memcpy(newarray, groups, n); + *ngroups = n; + return newarray; + } + return NULL; +} + +void ui_menu_create(const char *label) { + if(!current) { + current = cxLinkedListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS); + } + + // create menu + UiMenu *menu = malloc(sizeof(UiMenu)); + menu->item.prev = NULL; + menu->item.next = NULL; + menu->item.type = UI_MENU; + + menu->label = label; + menu->items_begin = NULL; + menu->items_end = NULL; + menu->parent = NULL; + + menu->end = 0; + + if (current->size == 0) { + add_menu(menu); + } + else { + add_item((UiMenuItemI*)menu); + } + uic_add_menu_to_stack(menu); +} + +UIEXPORT void ui_menu_end(void) { + cxListRemove(current, 0); +} + + + +void ui_menuitem_create(UiMenuItemArgs args) { + if (!current) { + return; // error? + } + + UiMenuItem* item = malloc(sizeof(UiMenuItem)); + item->item.prev = NULL; + item->item.next = NULL; + item->item.type = UI_MENU_ITEM; + + item->label = nl_strdup(args.label); + item->stockid = nl_strdup(args.stockid); + item->icon = nl_strdup(args.icon); + item->userdata = args.onclickdata; + item->callback = args.onclick; + item->groups = copy_groups(args.groups, &item->ngroups); + + add_item((UiMenuItemI*)item); +} + +void ui_menuseparator() { + if(!current) { + return; + } + + UiMenuItemI *item = malloc(sizeof(UiMenuItemI)); + item->prev = NULL; + item->next = NULL; + item->type = UI_MENU_SEPARATOR; + + add_item((UiMenuItemI*)item); +} + +void ui_menu_toggleitem_create(UiMenuToggleItemArgs args) { + if(!current) { + return; + } + + UiMenuCheckItem *item = malloc(sizeof(UiMenuCheckItem)); + item->item.prev = NULL; + item->item.next = NULL; + item->item.type = UI_MENU_CHECK_ITEM; + + item->label = nl_strdup(args.label); + item->stockid = nl_strdup(args.stockid); + item->icon = nl_strdup(args.icon); + item->varname = nl_strdup(args.varname); + item->userdata = args.onchangedata; + item->callback = args.onchange; + item->groups = copy_groups(args.groups, &item->ngroups); + + add_item((UiMenuItemI*)item); +} + +void ui_menu_radioitem_create(UiMenuToggleItemArgs args) { + if (!current) { + return; + } + + UiMenuCheckItem* item = malloc(sizeof(UiMenuCheckItem)); + item->item.prev = NULL; + item->item.next = NULL; + item->item.type = UI_MENU_CHECK_ITEM; + + item->label = nl_strdup(args.label); + item->stockid = nl_strdup(args.stockid); + item->icon = nl_strdup(args.icon); + item->varname = nl_strdup(args.varname); + item->userdata = args.onchangedata; + item->callback = args.onchange; + item->groups = copy_groups(args.groups, &item->ngroups); + + add_item((UiMenuItemI*)item); +} + +void ui_menu_itemlist_create(UiMenuItemListArgs args) { + if(!current) { + return; + } + + UiMenuItemList*item = malloc(sizeof(UiMenuItemList)); + item->item.prev = NULL; + item->item.next = NULL; + item->item.type = UI_MENU_ITEM_LIST; + item->callback = args.onselect; + item->userdata = args.onselectdata; + item->varname = nl_strdup(args.varname); + + add_item((UiMenuItemI*)item); +} + +void ui_menu_checkitemlist_create(UiMenuItemListArgs args) { + if (!current) { + return; + } + + UiMenuItemList* item = malloc(sizeof(UiMenuItemList)); + item->item.prev = NULL; + item->item.next = NULL; + item->item.type = UI_MENU_ITEM_LIST; + item->callback = args.onselect; + item->userdata = args.onselectdata; + item->varname = nl_strdup(args.varname); + + add_item((UiMenuItemI*)item); +} + +void ui_menu_radioitemlist_create(UiMenuItemListArgs args) { + if (!current) { + return; + } + + UiMenuItemList* item = malloc(sizeof(UiMenuItemList)); + item->item.prev = NULL; + item->item.next = NULL; + item->item.type = UI_MENU_ITEM_LIST; + item->callback = args.onselect; + item->userdata = args.onselectdata; + item->varname = nl_strdup(args.varname); + + add_item((UiMenuItemI*)item); +} + + +void uic_add_menu_to_stack(UiMenu* menu) { + cxListInsert(current, 0, menu); +} + +UiMenu* uic_get_menu_list(void) { + return menus_begin; +} + +UIEXPORT void ui_menu_close(void) { + UiMenu* menu = cxListAt(current, 0); + menu->end = 1; +} + +UIEXPORT int ui_menu_is_open(void) { + UiMenu* menu = cxListAt(current, 0); + if (menu->end) { + ui_menu_end(); + return 0; + } + return 1; +}