Thu, 21 Nov 2024 13:17:56 +0100
implement image viewer context menus (GTK)
/* * 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 UiMenuBuilder *current_builder; static UiMenuBuilder global_builder; static int menu_item_counter = 0; void uic_menu_init(void) { global_builder.current = cxLinkedListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS); current_builder = &global_builder; } static void add_menu(UiMenu *menu) { cx_linked_list_add( (void**)¤t_builder->menus_begin, (void**)¤t_builder->menus_end, offsetof(UiMenu, item.prev), offsetof(UiMenu, item.next), menu); } static void add_item(UiMenuItemI *item) { UiMenu *menu = cxListAt(current_builder->current, 0); cx_linked_list_add( (void**)&menu->items_begin, (void**)&menu->items_end, offsetof(UiMenu, item.prev), offsetof(UiMenu, item.next), item); } static void mitem_set_id(UiMenuItemI *item) { snprintf(item->id, 8, "%x", menu_item_counter++); } static char* nl_strdup(const char* s) { return s ? strdup(s) : NULL; } int* uic_copy_groups(const 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 * sizeof(int)); *ngroups = n; return newarray; } return NULL; } void ui_menu_create(const char *label) { // create menu UiMenu *menu = malloc(sizeof(UiMenu)); mitem_set_id(&menu->item); 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 (cxListSize(current_builder->current) == 0) { add_menu(menu); } else { add_item((UiMenuItemI*)menu); } uic_add_menu_to_stack(menu); } UIEXPORT void ui_menu_end(void) { cxListRemove(current_builder->current, 0); if(cxListSize(current_builder->current) == 0) { current_builder = &global_builder; } } void ui_menuitem_create(UiMenuItemArgs args) { UiMenuItem* item = malloc(sizeof(UiMenuItem)); mitem_set_id(&item->item); 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 = uic_copy_groups(args.groups, &item->ngroups); add_item((UiMenuItemI*)item); } void ui_menuseparator() { UiMenuItemI *item = malloc(sizeof(UiMenuItemI)); item->id[0] = 0; item->prev = NULL; item->next = NULL; item->type = UI_MENU_SEPARATOR; add_item((UiMenuItemI*)item); } void ui_menu_toggleitem_create(UiMenuToggleItemArgs args) { UiMenuCheckItem *item = malloc(sizeof(UiMenuCheckItem)); mitem_set_id(&item->item); 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 = uic_copy_groups(args.groups, &item->ngroups); add_item((UiMenuItemI*)item); } void ui_menu_radioitem_create(UiMenuToggleItemArgs args) { UiMenuCheckItem* item = malloc(sizeof(UiMenuCheckItem)); mitem_set_id(&item->item); 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 = uic_copy_groups(args.groups, &item->ngroups); add_item((UiMenuItemI*)item); } void ui_menu_itemlist_create(UiMenuItemListArgs args) { UiMenuItemList*item = malloc(sizeof(UiMenuItemList)); mitem_set_id(&item->item); item->item.prev = NULL; item->item.next = NULL; item->item.type = UI_MENU_ITEM_LIST; item->getvalue = args.getvalue; 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) { UiMenuItemList* item = malloc(sizeof(UiMenuItemList)); mitem_set_id(&item->item); item->item.prev = NULL; item->item.next = NULL; item->item.type = UI_MENU_CHECKITEM_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) { UiMenuItemList* item = malloc(sizeof(UiMenuItemList)); mitem_set_id(&item->item); item->item.prev = NULL; item->item.next = NULL; item->item.type = UI_MENU_RADIOITEM_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_builder->current, 0, menu); } UiMenu* uic_get_menu_list(void) { return current_builder->menus_begin; } UIEXPORT void ui_menu_close(void) { UiMenu* menu = cxListAt(current_builder->current, 0); menu->end = 1; } UIEXPORT int ui_menu_is_open(void) { UiMenu* menu = cxListAt(current_builder->current, 0); if (menu->end) { ui_menu_end(); return 0; } return 1; } void ui_contextmenu_builder(UiMenuBuilder **out_builder) { UiMenuBuilder *builder = malloc(sizeof(UiMenuBuilder)); builder->menus_begin = NULL; builder->menus_end = NULL; builder->current = cxLinkedListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS); current_builder = builder; *out_builder = builder; ui_menu_create(NULL); } static void free_menuitem(UiMenuItemI *item) { switch(item->type) { default: break; case UI_MENU: { UiMenu *menu = (UiMenu*)item; UiMenuItemI *m = menu->items_begin; while(m) { UiMenuItemI *next = m->next; free_menuitem(m); m = next; } break; } case UI_MENU_ITEM: { UiMenuItem *i = (UiMenuItem*)item; free(i->groups); free(i->label); free(i->stockid); free(i->icon); break; } case UI_MENU_CHECK_ITEM: { UiMenuCheckItem *i = (UiMenuCheckItem*)item; free(i->groups); free(i->label); free(i->stockid); free(i->icon); free(i->varname); break; } case UI_MENU_RADIO_ITEM: { UiMenuRadioItem *i = (UiMenuRadioItem*)item; free(i->groups); free(i->label); free(i->stockid); free(i->icon); //free(i->varname); break; } case UI_MENU_ITEM_LIST: { break; } case UI_MENU_CHECKITEM_LIST: { break; } case UI_MENU_RADIOITEM_LIST: { break; } } free(item); } void ui_menubuilder_free(UiMenuBuilder *builder) { UiMenuItemI *m = &builder->menus_begin->item; while(m) { UiMenuItemI *next = m->next; free_menuitem(m); m = next; } cxListDestroy(builder->current); free(builder); }