Browse Source

Change Qmenu items allocation to be dynamic

master
cancel 5 years ago
parent
commit
44e888f7da
  1. 84
      term_util.c

84
term_util.c

@ -68,11 +68,17 @@ struct Qmsg {
Qblock qblock;
};
struct Qmenu_item_extra {
U8 owns_string : 1;
U8 is_spacer : 1;
};
struct Qmenu {
Qblock qblock;
MENU* ncurses_menu;
ITEM* ncurses_items[32];
ITEM** ncurses_items;
Usz items_count;
Usz items_cap;
ITEM* initial_item;
int id;
};
@ -301,32 +307,77 @@ Qmenu* qmenu_create(int id) {
Qmenu* qm = (Qmenu*)malloc(sizeof(Qmenu));
qblock_init(&qm->qblock, Qblock_type_qmenu);
qm->ncurses_menu = NULL;
qm->ncurses_items[0] = NULL;
qm->ncurses_items = NULL;
qm->items_count = 0;
qm->items_cap = 0;
qm->initial_item = NULL;
qm->id = id;
return qm;
}
void qmenu_destroy(Qmenu* qm) { qmenu_free(qm); }
int qmenu_id(Qmenu const* qm) { return qm->id; }
static ORCA_FORCE_NO_INLINE void
qmenu_allocitems(Qmenu* qm, Usz count, ITEM*** out_items,
struct Qmenu_item_extra** out_extras) {
Usz old_count = qm->items_count;
// Add 1 for the extra null terminator guy
Usz new_count = old_count + count + 1;
Usz items_cap = qm->items_cap;
ITEM** items = qm->ncurses_items;
if (new_count > items_cap) {
// todo overflow check, realloc fail check
Usz old_cap = items_cap;
Usz new_cap = count < 16 ? 16 : orca_round_up_power2(new_count);
Usz new_size = new_cap * (sizeof(ITEM*) + sizeof(struct Qmenu_item_extra));
ITEM** new_items = (ITEM**)realloc(items, new_size);
if (!new_items)
exit(1);
items = new_items;
items_cap = new_cap;
// Move old extras data to new position
Usz old_extras_offset = sizeof(ITEM*) * old_cap;
Usz new_extras_offset = sizeof(ITEM*) * new_cap;
Usz old_extras_size = sizeof(struct Qmenu_item_extra) * old_count;
memmove((char*)items + new_extras_offset, (char*)items + old_extras_offset,
old_extras_size);
qm->ncurses_items = new_items;
qm->items_cap = new_cap;
}
// Not using new_count here in order to leave an extra 1 for the null
// terminator as required by ncurses.
qm->items_count = old_count + count;
Usz extras_offset = sizeof(ITEM*) * items_cap;
*out_items = items + old_count;
*out_extras =
(struct Qmenu_item_extra*)((char*)items + extras_offset) + old_count;
}
ORCA_FORCE_STATIC_INLINE struct Qmenu_item_extra*
qmenu_item_extras_ptr(Qmenu* qm) {
Usz offset = sizeof(ITEM*) * qm->items_cap;
return (struct Qmenu_item_extra*)((char*)qm->ncurses_items + offset);
}
void qmenu_set_title(Qmenu* qm, char const* title) {
qblock_set_title(&qm->qblock, title);
}
void qmenu_add_choice(Qmenu* qm, int id, char const* text) {
assert(id >= Qmenu_first_valid_user_choice_id);
ITEM* item = new_item(text, NULL);
set_item_userptr(item, (void*)(intptr_t)(id));
qm->ncurses_items[qm->items_count] = item;
++qm->items_count;
qm->ncurses_items[qm->items_count] = NULL;
ITEM** items;
struct Qmenu_item_extra* extras;
qmenu_allocitems(qm, 1, &items, &extras);
items[0] = new_item(text, NULL);
set_item_userptr(items[0], (void*)(intptr_t)(id));
extras[0].owns_string = false;
extras[0].is_spacer = false;
}
void qmenu_add_spacer(Qmenu* qm) {
ITEM* item = new_item(" ", NULL);
item_opts_off(item, O_SELECTABLE);
set_item_userptr(item, (void*)(intptr_t)Qmenu_spacer_unique_id);
qm->ncurses_items[qm->items_count] = item;
++qm->items_count;
qm->ncurses_items[qm->items_count] = NULL;
ITEM** items;
struct Qmenu_item_extra* extras;
qmenu_allocitems(qm, 1, &items, &extras);
items[0] = new_item(" ", NULL);
item_opts_off(items[0], O_SELECTABLE);
set_item_userptr(items[0], (void*)(intptr_t)Qmenu_spacer_unique_id);
extras[0].owns_string = false;
extras[0].is_spacer = true;
}
void qmenu_set_current_item(Qmenu* qm, int id) {
ITEM* found = NULL;
@ -356,8 +407,12 @@ void qmenu_push_to_nav(Qmenu* qm) {
// spacer item. This will probably only ever occur as a programming error,
// but we should try to avoid having to deal with qmenu_push_to_nav()
// returning a non-ignorable error for now.
if (qm->ncurses_items[0] == NULL)
if (qm->items_count == 0)
qmenu_add_spacer(qm);
// Allocating items always leaves an extra available item at the end. This is
// so we can assign a NULL to it here, since ncurses requires the array to be
// null terminated instead of using a count.
qm->ncurses_items[qm->items_count] = NULL;
qm->ncurses_menu = new_menu(qm->ncurses_items);
set_menu_mark(qm->ncurses_menu, " > ");
set_menu_fore(qm->ncurses_menu, A_BOLD);
@ -386,6 +441,7 @@ void qmenu_free(Qmenu* qm) {
for (Usz i = 0; i < qm->items_count; ++i) {
free_item(qm->ncurses_items[i]);
}
free(qm->ncurses_items);
free(qm);
}

Loading…
Cancel
Save