ui/common/menu.c

Wed, 30 Oct 2024 15:32:38 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Wed, 30 Oct 2024 15:32:38 +0100
branch
newapi
changeset 376
5578494147cb
parent 367
1c3af8e31d8e
permissions
-rw-r--r--

fix condvar windows implementation

/*
 * 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 int menu_item_counter = 0;

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 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) {
    if(!current) {
        current = cxLinkedListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS);
    }
    
    // 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) == 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));
    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() {
    if(!current) {
        return;
    }
    
    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) {
    if(!current) {
        return;
    }
    
    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) {
    if (!current) {
        return;
    }

    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) {
    if(!current) {
        return;
    }
    
    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) {
    if (!current) {
        return;
    }

    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) {
    if (!current) {
        return;
    }

    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) {
    if (!current) {
        current = cxLinkedListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS);
    }

    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;
}

mercurial