ui/common/menu.c

Tue, 26 Nov 2024 10:40:45 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Tue, 26 Nov 2024 10:40:45 +0100
changeset 88
e27526429d85
parent 77
5de33c2d94c6
child 90
f501f0efc9a8
permissions
-rw-r--r--

add context menu, implement 'Select All'

/*
 * 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**)&current_builder->menus_begin,
            (void**)&current_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);
}

mercurial