--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/common/menu.c Mon May 22 19:44:27 2023 +0200 @@ -0,0 +1,253 @@ +/* + * 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 <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); +} + +void ui_menu(char *label) { + if(!current) { + current = cxLinkedListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS); + } else { + // free current menu hierarchy + cxListClear(current); + } + + // 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; + + add_menu(menu); + cxListAdd(current, menu); +} + +void ui_submenu(char *label) { + UiMenu *menu = malloc(sizeof(UiMenu)); + menu->item.prev = NULL; + menu->item.next = NULL; + menu->item.type = UI_MENU_SUBMENU; + + menu->label = label; + menu->items_begin = NULL; + menu->items_end = NULL; + menu->parent = NULL; + + // add submenu to current menu + add_item((UiMenuItemI*)menu); + + // set the submenu to current menu + cxListInsert(current, 0, menu); +} + +void ui_submenu_end() { + if(current->size < 2) { + return; + } + cxListRemove(current, 0); +} + +void ui_menuitem(char *label, ui_callback f, void *userdata) { + ui_menuitem_gr(label, f, userdata, -1); +} + +void ui_menuitem_st(char *stockid, ui_callback f, void *userdata) { + ui_menuitem_stgr(stockid, f, userdata, -1); +} + +void ui_menuitem_gr(char *label, ui_callback f, void *userdata, ...) { + if(!current) { + return; + } + + UiMenuItem *item = malloc(sizeof(UiMenuItem)); + item->item.prev = NULL; + item->item.next = NULL; + item->item.type = UI_MENU_ITEM; + + item->label = label; + item->userdata = userdata; + item->callback = f; + item->groups = NULL; + + // add groups + va_list ap; + va_start(ap, userdata); + int group; + while((group = va_arg(ap, int)) != -1) { + if(!item->groups) { + item->groups = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(int), 8); + } + cxListAdd(item->groups, &group); + } + va_end(ap); + + add_item((UiMenuItemI*)item); +} + +void ui_menuitem_stgr(char *stockid, ui_callback f, void *userdata, ...) { + if(!current) { + return; + } + + UiStMenuItem *item = malloc(sizeof(UiStMenuItem)); + item->item.prev = NULL; + item->item.next = NULL; + item->item.type = UI_MENU_STOCK_ITEM; + + item->stockid = stockid; + item->userdata = userdata; + item->callback = f; + item->groups = NULL; + + // add groups + va_list ap; + va_start(ap, userdata); + int group; + while((group = va_arg(ap, int)) != -1) { + if(!item->groups) { + item->groups = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(int), 8); + } + cxListAdd(item->groups, &group); + } + va_end(ap); + + 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_checkitem(char *label, ui_callback f, void *userdata) { + if(!current) { + return; + } + + UiCheckItem *item = malloc(sizeof(UiCheckItem)); + item->item.prev = NULL; + item->item.next = NULL; + item->item.type = UI_MENU_CHECK_ITEM; + item->label = label; + item->callback = f; + item->userdata = userdata; + + add_item((UiMenuItemI*)item); +} + +void ui_checkitem_nv(char *label, char *vname) { + if(!current) { + return; + } + + UiCheckItemNV *item = malloc(sizeof(UiCheckItemNV)); + item->item.prev = NULL; + item->item.next = NULL; + item->item.type = UI_MENU_CHECK_ITEM_NV; + item->varname = vname; + item->label = label; + + add_item((UiMenuItemI*)item); +} + +void ui_menuitem_list(UiList *items, ui_callback f, void *userdata) { + 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 = f; + item->userdata = userdata; + item->list = items; + + add_item((UiMenuItemI*)item); +} + +void ui_menuitem_list_nv(const char *varname, ui_callback f, void *userdata) { + if(!current) { + return; + } + + UiMenuItemListNV *item = malloc(sizeof(UiMenuItemListNV)); + item->item.prev = NULL; + item->item.next = NULL; + item->item.type = UI_MENU_ITEM_LIST; + item->callback = f; + item->userdata = userdata; + item->varname = varname; + + add_item((UiMenuItemI*)item); +} + + + +UiMenu* uic_get_menu_list(void) { + return menus_begin; +}