|
|
@ -30,7 +30,8 @@ void term_util_init_colors() { |
|
|
|
} |
|
|
|
|
|
|
|
#define ORCA_CONTAINER_OF(ptr, type, member) \ |
|
|
|
((type*)((char*)(1 ? (ptr) : &((type*)0)->member) - offsetof(type, member))) |
|
|
|
((type *)((char *)(1 ? (ptr) : &((type *)0)->member) - \ |
|
|
|
offsetof(type, member))) |
|
|
|
|
|
|
|
struct Qmsg { |
|
|
|
Qblock qblock; |
|
|
@ -44,11 +45,11 @@ struct Qmenu_item_extra { |
|
|
|
|
|
|
|
struct Qmenu { |
|
|
|
Qblock qblock; |
|
|
|
MENU* ncurses_menu; |
|
|
|
ITEM** ncurses_items; |
|
|
|
MENU *ncurses_menu; |
|
|
|
ITEM **ncurses_items; |
|
|
|
Usz items_count; |
|
|
|
Usz items_cap; |
|
|
|
ITEM* initial_item; |
|
|
|
ITEM *initial_item; |
|
|
|
int id; |
|
|
|
// Flag for right-padding hack. Temp until we do our own menus
|
|
|
|
U8 has_submenu_item : 1; |
|
|
@ -56,8 +57,8 @@ struct Qmenu { |
|
|
|
|
|
|
|
struct Qform { |
|
|
|
Qblock qblock; |
|
|
|
FORM* ncurses_form; |
|
|
|
FIELD* ncurses_fields[32]; |
|
|
|
FORM *ncurses_form; |
|
|
|
FIELD *ncurses_fields[32]; |
|
|
|
Usz fields_count; |
|
|
|
int id; |
|
|
|
}; |
|
|
@ -73,7 +74,7 @@ void qnav_deinit() { |
|
|
|
while (qnav_stack.count != 0) |
|
|
|
qnav_stack_pop(); |
|
|
|
} |
|
|
|
static ORCA_FORCE_NO_INLINE void qnav_stack_push(Qblock* qb, int height, |
|
|
|
static ORCA_FORCE_NO_INLINE void qnav_stack_push(Qblock *qb, int height, |
|
|
|
int width) { |
|
|
|
#ifndef NDEBUG |
|
|
|
for (Usz i = 0; i < qnav_stack.count; ++i) { |
|
|
@ -83,7 +84,7 @@ static ORCA_FORCE_NO_INLINE void qnav_stack_push(Qblock* qb, int height, |
|
|
|
int top = 0, left = 0; |
|
|
|
int total_h = height + 2, total_w = width + 2; |
|
|
|
if (qnav_stack.count > 0) { |
|
|
|
WINDOW* w = qnav_stack.blocks[qnav_stack.count - 1]->outer_window; |
|
|
|
WINDOW *w = qnav_stack.blocks[qnav_stack.count - 1]->outer_window; |
|
|
|
int prev_y, prev_x, prev_h, prev_w; |
|
|
|
getbegyx(w, prev_y, prev_x); |
|
|
|
getmaxyx(w, prev_h, prev_w); |
|
|
@ -123,26 +124,26 @@ static ORCA_FORCE_NO_INLINE void qnav_stack_push(Qblock* qb, int height, |
|
|
|
qnav_stack.stack_changed = true; |
|
|
|
} |
|
|
|
|
|
|
|
Qblock* qnav_top_block() { |
|
|
|
Qblock *qnav_top_block() { |
|
|
|
if (qnav_stack.count == 0) |
|
|
|
return NULL; |
|
|
|
return qnav_stack.blocks[qnav_stack.count - 1]; |
|
|
|
} |
|
|
|
|
|
|
|
void qblock_init(Qblock* qb, Qblock_type_tag tag) { |
|
|
|
void qblock_init(Qblock *qb, Qblock_type_tag tag) { |
|
|
|
qb->tag = tag; |
|
|
|
qb->outer_window = NULL; |
|
|
|
qb->content_window = NULL; |
|
|
|
qb->title = NULL; |
|
|
|
} |
|
|
|
|
|
|
|
void qmenu_free(Qmenu* qm); |
|
|
|
void qform_free(Qform* qf); |
|
|
|
void qmenu_free(Qmenu *qm); |
|
|
|
void qform_free(Qform *qf); |
|
|
|
|
|
|
|
void qnav_free_block(Qblock* qb) { |
|
|
|
void qnav_free_block(Qblock *qb) { |
|
|
|
switch (qb->tag) { |
|
|
|
case Qblock_type_qmsg: { |
|
|
|
Qmsg* qm = qmsg_of(qb); |
|
|
|
Qmsg *qm = qmsg_of(qb); |
|
|
|
free(qm); |
|
|
|
} break; |
|
|
|
case Qblock_type_qmenu: { |
|
|
@ -158,9 +159,9 @@ void qnav_stack_pop() { |
|
|
|
assert(qnav_stack.count > 0); |
|
|
|
if (qnav_stack.count == 0) |
|
|
|
return; |
|
|
|
Qblock* qb = qnav_stack.blocks[qnav_stack.count - 1]; |
|
|
|
WINDOW* content_window = qb->content_window; |
|
|
|
WINDOW* outer_window = qb->outer_window; |
|
|
|
Qblock *qb = qnav_stack.blocks[qnav_stack.count - 1]; |
|
|
|
WINDOW *content_window = qb->content_window; |
|
|
|
WINDOW *outer_window = qb->outer_window; |
|
|
|
// erase any stuff underneath where this window is, in case it's outside of
|
|
|
|
// the grid in an area that isn't actively redraw
|
|
|
|
werase(outer_window); |
|
|
@ -173,13 +174,13 @@ void qnav_stack_pop() { |
|
|
|
qnav_stack.stack_changed = true; |
|
|
|
} |
|
|
|
|
|
|
|
void qblock_print_border(Qblock* qb, unsigned int attr) { |
|
|
|
void qblock_print_border(Qblock *qb, unsigned int attr) { |
|
|
|
wborder(qb->outer_window, ACS_VLINE | attr, ACS_VLINE | attr, |
|
|
|
ACS_HLINE | attr, ACS_HLINE | attr, ACS_ULCORNER | attr, |
|
|
|
ACS_URCORNER | attr, ACS_LLCORNER | attr, ACS_LRCORNER | attr); |
|
|
|
} |
|
|
|
|
|
|
|
void qblock_print_title(Qblock* qb, char const* title, int attr) { |
|
|
|
void qblock_print_title(Qblock *qb, char const *title, int attr) { |
|
|
|
wmove(qb->outer_window, 0, 1); |
|
|
|
attr_t attrs = A_NORMAL; |
|
|
|
short pair = 0; |
|
|
@ -191,41 +192,41 @@ void qblock_print_title(Qblock* qb, char const* title, int attr) { |
|
|
|
wattr_set(qb->outer_window, attrs, pair, NULL); |
|
|
|
} |
|
|
|
|
|
|
|
void qblock_set_title(Qblock* qb, char const* title) { qb->title = title; } |
|
|
|
void qblock_set_title(Qblock *qb, char const *title) { qb->title = title; } |
|
|
|
|
|
|
|
void qblock_print_frame(Qblock* qb, bool active) { |
|
|
|
void qblock_print_frame(Qblock *qb, bool active) { |
|
|
|
qblock_print_border(qb, active ? A_NORMAL : A_DIM); |
|
|
|
if (qb->title) { |
|
|
|
qblock_print_title(qb, qb->title, active ? A_NORMAL : A_DIM); |
|
|
|
} |
|
|
|
if (qb->tag == Qblock_type_qform) { |
|
|
|
Qform* qf = qform_of(qb); |
|
|
|
Qform *qf = qform_of(qb); |
|
|
|
if (qf->ncurses_form) { |
|
|
|
pos_form_cursor(qf->ncurses_form); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
WINDOW* qmsg_window(Qmsg* qm) { return qm->qblock.content_window; } |
|
|
|
WINDOW *qmsg_window(Qmsg *qm) { return qm->qblock.content_window; } |
|
|
|
|
|
|
|
void qmsg_set_title(Qmsg* qm, char const* title) { |
|
|
|
void qmsg_set_title(Qmsg *qm, char const *title) { |
|
|
|
qblock_set_title(&qm->qblock, title); |
|
|
|
} |
|
|
|
|
|
|
|
Qmsg* qmsg_push(int height, int width) { |
|
|
|
Qmsg* qm = malloc(sizeof(Qmsg)); |
|
|
|
Qmsg *qmsg_push(int height, int width) { |
|
|
|
Qmsg *qm = malloc(sizeof(Qmsg)); |
|
|
|
qblock_init(&qm->qblock, Qblock_type_qmsg); |
|
|
|
qnav_stack_push(&qm->qblock, height, width); |
|
|
|
return qm; |
|
|
|
} |
|
|
|
|
|
|
|
void qmsg_printf_push(char const* title, char const* fmt, ...) { |
|
|
|
void qmsg_printf_push(char const *title, char const *fmt, ...) { |
|
|
|
int titlewidth = title ? (int)strlen(title) : 0; |
|
|
|
va_list ap; |
|
|
|
va_start(ap, fmt); |
|
|
|
int msgbytes = vsnprintf(NULL, 0, fmt, ap); |
|
|
|
va_end(ap); |
|
|
|
char* buffer = malloc((Usz)msgbytes + 1); |
|
|
|
char *buffer = malloc((Usz)msgbytes + 1); |
|
|
|
if (!buffer) |
|
|
|
exit(1); |
|
|
|
va_start(ap, fmt); |
|
|
@ -251,8 +252,8 @@ void qmsg_printf_push(char const* title, char const* fmt, ...) { |
|
|
|
maxlinewidth = curlinewidth; |
|
|
|
int width = titlewidth > maxlinewidth ? titlewidth : maxlinewidth; |
|
|
|
width += 2; // 1 padding on left and right each
|
|
|
|
Qmsg* msg = qmsg_push(lines, width); // no wrapping yet, no real wcwidth, etc
|
|
|
|
WINDOW* msgw = qmsg_window(msg); |
|
|
|
Qmsg *msg = qmsg_push(lines, width); // no wrapping yet, no real wcwidth, etc
|
|
|
|
WINDOW *msgw = qmsg_window(msg); |
|
|
|
int i = 0; |
|
|
|
int offset = 0; |
|
|
|
for (;;) { |
|
|
@ -269,7 +270,7 @@ void qmsg_printf_push(char const* title, char const* fmt, ...) { |
|
|
|
qmsg_set_title(msg, title); |
|
|
|
} |
|
|
|
|
|
|
|
bool qmsg_drive(Qmsg* qm, int key) { |
|
|
|
bool qmsg_drive(Qmsg *qm, int key) { |
|
|
|
(void)qm; |
|
|
|
switch (key) { |
|
|
|
case ' ': |
|
|
@ -281,10 +282,10 @@ bool qmsg_drive(Qmsg* qm, int key) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
Qmsg* qmsg_of(Qblock* qb) { return ORCA_CONTAINER_OF(qb, Qmsg, qblock); } |
|
|
|
Qmsg *qmsg_of(Qblock *qb) { return ORCA_CONTAINER_OF(qb, Qmsg, qblock); } |
|
|
|
|
|
|
|
Qmenu* qmenu_create(int id) { |
|
|
|
Qmenu* qm = (Qmenu*)malloc(sizeof(Qmenu)); |
|
|
|
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 = NULL; |
|
|
@ -295,90 +296,90 @@ Qmenu* qmenu_create(int id) { |
|
|
|
qm->has_submenu_item = 0; |
|
|
|
return qm; |
|
|
|
} |
|
|
|
void qmenu_destroy(Qmenu* qm) { qmenu_free(qm); } |
|
|
|
int qmenu_id(Qmenu const* qm) { return qm->id; } |
|
|
|
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, Usz* out_idx, ITEM*** out_items, |
|
|
|
struct Qmenu_item_extra** out_extras) { |
|
|
|
qmenu_allocitems(Qmenu *qm, Usz count, Usz *out_idx, 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; |
|
|
|
ITEM **items = qm->ncurses_items; |
|
|
|
if (new_count > items_cap) { |
|
|
|
// todo overflow check, realloc fail check
|
|
|
|
Usz old_cap = items_cap; |
|
|
|
Usz new_cap = new_count < 32 ? 32 : 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); |
|
|
|
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_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); |
|
|
|
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; |
|
|
|
Usz extras_offset = sizeof(ITEM *) * items_cap; |
|
|
|
*out_idx = old_count; |
|
|
|
*out_items = items + old_count; |
|
|
|
*out_extras = |
|
|
|
(struct Qmenu_item_extra*)((char*)items + extras_offset) + old_count; |
|
|
|
(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); |
|
|
|
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); |
|
|
|
} |
|
|
|
// Get the curses menu item user pointer out, turn it to an int, and use it as
|
|
|
|
// an index into the 'extras' arrays.
|
|
|
|
ORCA_FORCE_STATIC_INLINE |
|
|
|
struct Qmenu_item_extra* qmenu_itemextra(struct Qmenu_item_extra* extras, |
|
|
|
ITEM* item) { |
|
|
|
struct Qmenu_item_extra *qmenu_itemextra(struct Qmenu_item_extra *extras, |
|
|
|
ITEM *item) { |
|
|
|
return extras + (int)(intptr_t)(item_userptr(item)); |
|
|
|
} |
|
|
|
void qmenu_set_title(Qmenu* qm, char const* title) { |
|
|
|
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) { |
|
|
|
void qmenu_add_choice(Qmenu *qm, int id, char const *text) { |
|
|
|
assert(id != 0); |
|
|
|
Usz idx; |
|
|
|
ITEM** items; |
|
|
|
struct Qmenu_item_extra* extras; |
|
|
|
ITEM **items; |
|
|
|
struct Qmenu_item_extra *extras; |
|
|
|
qmenu_allocitems(qm, 1, &idx, &items, &extras); |
|
|
|
items[0] = new_item(text, NULL); |
|
|
|
set_item_userptr(items[0], (void*)(uintptr_t)idx); |
|
|
|
set_item_userptr(items[0], (void *)(uintptr_t)idx); |
|
|
|
extras[0].user_id = id; |
|
|
|
extras[0].owns_string = false; |
|
|
|
extras[0].is_spacer = false; |
|
|
|
} |
|
|
|
void qmenu_add_submenu(Qmenu* qm, int id, char const* text) { |
|
|
|
void qmenu_add_submenu(Qmenu *qm, int id, char const *text) { |
|
|
|
assert(id != 0); |
|
|
|
qm->has_submenu_item = true; // don't add +1 right padding to subwindow
|
|
|
|
Usz idx; |
|
|
|
ITEM** items; |
|
|
|
struct Qmenu_item_extra* extras; |
|
|
|
ITEM **items; |
|
|
|
struct Qmenu_item_extra *extras; |
|
|
|
qmenu_allocitems(qm, 1, &idx, &items, &extras); |
|
|
|
items[0] = new_item(text, ">"); |
|
|
|
set_item_userptr(items[0], (void*)(uintptr_t)idx); |
|
|
|
set_item_userptr(items[0], (void *)(uintptr_t)idx); |
|
|
|
extras[0].user_id = id; |
|
|
|
extras[0].owns_string = false; |
|
|
|
extras[0].is_spacer = false; |
|
|
|
} |
|
|
|
void qmenu_add_printf(Qmenu* qm, int id, char const* fmt, ...) { |
|
|
|
void qmenu_add_printf(Qmenu *qm, int id, char const *fmt, ...) { |
|
|
|
va_list ap; |
|
|
|
va_start(ap, fmt); |
|
|
|
int textsize = vsnprintf(NULL, 0, fmt, ap); |
|
|
|
va_end(ap); |
|
|
|
char* buffer = malloc((Usz)textsize + 1); |
|
|
|
char *buffer = malloc((Usz)textsize + 1); |
|
|
|
if (!buffer) |
|
|
|
exit(1); |
|
|
|
va_start(ap, fmt); |
|
|
@ -387,33 +388,33 @@ void qmenu_add_printf(Qmenu* qm, int id, char const* fmt, ...) { |
|
|
|
if (printedsize != textsize) |
|
|
|
exit(1); // todo better handling?
|
|
|
|
Usz idx; |
|
|
|
ITEM** items; |
|
|
|
struct Qmenu_item_extra* extras; |
|
|
|
ITEM **items; |
|
|
|
struct Qmenu_item_extra *extras; |
|
|
|
qmenu_allocitems(qm, 1, &idx, &items, &extras); |
|
|
|
items[0] = new_item(buffer, NULL); |
|
|
|
set_item_userptr(items[0], (void*)(uintptr_t)idx); |
|
|
|
set_item_userptr(items[0], (void *)(uintptr_t)idx); |
|
|
|
extras[0].user_id = id; |
|
|
|
extras[0].owns_string = true; |
|
|
|
extras[0].is_spacer = false; |
|
|
|
} |
|
|
|
void qmenu_add_spacer(Qmenu* qm) { |
|
|
|
void qmenu_add_spacer(Qmenu *qm) { |
|
|
|
Usz idx; |
|
|
|
ITEM** items; |
|
|
|
struct Qmenu_item_extra* extras; |
|
|
|
ITEM **items; |
|
|
|
struct Qmenu_item_extra *extras; |
|
|
|
qmenu_allocitems(qm, 1, &idx, &items, &extras); |
|
|
|
items[0] = new_item(" ", NULL); |
|
|
|
item_opts_off(items[0], O_SELECTABLE); |
|
|
|
set_item_userptr(items[0], (void*)(uintptr_t)idx); |
|
|
|
set_item_userptr(items[0], (void *)(uintptr_t)idx); |
|
|
|
extras[0].user_id = 0; |
|
|
|
extras[0].owns_string = false; |
|
|
|
extras[0].is_spacer = true; |
|
|
|
} |
|
|
|
void qmenu_set_current_item(Qmenu* qm, int id) { |
|
|
|
ITEM** items = qm->ncurses_items; |
|
|
|
struct Qmenu_item_extra* extras = qmenu_item_extras_ptr(qm); |
|
|
|
ITEM* found = NULL; |
|
|
|
void qmenu_set_current_item(Qmenu *qm, int id) { |
|
|
|
ITEM **items = qm->ncurses_items; |
|
|
|
struct Qmenu_item_extra *extras = qmenu_item_extras_ptr(qm); |
|
|
|
ITEM *found = NULL; |
|
|
|
for (Usz i = 0, n = qm->items_count; i < n; i++) { |
|
|
|
ITEM* item = items[i]; |
|
|
|
ITEM *item = items[i]; |
|
|
|
if (qmenu_itemextra(extras, item)->user_id == id) { |
|
|
|
found = item; |
|
|
|
break; |
|
|
@ -427,13 +428,13 @@ void qmenu_set_current_item(Qmenu* qm, int id) { |
|
|
|
qm->initial_item = found; |
|
|
|
} |
|
|
|
} |
|
|
|
void qmenu_set_displayed_active(Qmenu* qm, bool active) { |
|
|
|
void qmenu_set_displayed_active(Qmenu *qm, bool active) { |
|
|
|
// Could add a flag in the Qmenu to avoid redundantly changing this stuff.
|
|
|
|
set_menu_fore(qm->ncurses_menu, active ? A_BOLD : A_DIM); |
|
|
|
set_menu_back(qm->ncurses_menu, active ? A_NORMAL : A_DIM); |
|
|
|
set_menu_grey(qm->ncurses_menu, active ? A_DIM : A_DIM); |
|
|
|
} |
|
|
|
void qmenu_push_to_nav(Qmenu* qm) { |
|
|
|
void qmenu_push_to_nav(Qmenu *qm) { |
|
|
|
// new_menu() will get angry if there are no items in the menu. We'll get a
|
|
|
|
// null pointer back, and our code will get angry. Instead, just add an empty
|
|
|
|
// spacer item. This will probably only ever occur as a programming error,
|
|
|
@ -474,36 +475,36 @@ void qmenu_push_to_nav(Qmenu* qm) { |
|
|
|
post_menu(qm->ncurses_menu); |
|
|
|
} |
|
|
|
|
|
|
|
void qmenu_free(Qmenu* qm) { |
|
|
|
void qmenu_free(Qmenu *qm) { |
|
|
|
unpost_menu(qm->ncurses_menu); |
|
|
|
free_menu(qm->ncurses_menu); |
|
|
|
struct Qmenu_item_extra* extras = qmenu_item_extras_ptr(qm); |
|
|
|
struct Qmenu_item_extra *extras = qmenu_item_extras_ptr(qm); |
|
|
|
for (Usz i = 0; i < qm->items_count; ++i) { |
|
|
|
ITEM* item = qm->ncurses_items[i]; |
|
|
|
struct Qmenu_item_extra* extra = qmenu_itemextra(extras, item); |
|
|
|
char const* freed_str = NULL; |
|
|
|
ITEM *item = qm->ncurses_items[i]; |
|
|
|
struct Qmenu_item_extra *extra = qmenu_itemextra(extras, item); |
|
|
|
char const *freed_str = NULL; |
|
|
|
if (extra->owns_string) |
|
|
|
freed_str = item_name(item); |
|
|
|
free_item(qm->ncurses_items[i]); |
|
|
|
if (freed_str) |
|
|
|
free((void*)freed_str); |
|
|
|
free((void *)freed_str); |
|
|
|
} |
|
|
|
free(qm->ncurses_items); |
|
|
|
free(qm); |
|
|
|
} |
|
|
|
|
|
|
|
ORCA_FORCE_NO_INLINE |
|
|
|
static void qmenu_drive_upordown(Qmenu* qm, int req_up_or_down) { |
|
|
|
struct Qmenu_item_extra* extras = qmenu_item_extras_ptr(qm); |
|
|
|
ITEM* starting = current_item(qm->ncurses_menu); |
|
|
|
static void qmenu_drive_upordown(Qmenu *qm, int req_up_or_down) { |
|
|
|
struct Qmenu_item_extra *extras = qmenu_item_extras_ptr(qm); |
|
|
|
ITEM *starting = current_item(qm->ncurses_menu); |
|
|
|
menu_driver(qm->ncurses_menu, req_up_or_down); |
|
|
|
ITEM* cur = current_item(qm->ncurses_menu); |
|
|
|
ITEM *cur = current_item(qm->ncurses_menu); |
|
|
|
for (;;) { |
|
|
|
if (!cur || cur == starting) |
|
|
|
break; |
|
|
|
if (!qmenu_itemextra(extras, cur)->is_spacer) |
|
|
|
break; |
|
|
|
ITEM* prev = cur; |
|
|
|
ITEM *prev = cur; |
|
|
|
menu_driver(qm->ncurses_menu, req_up_or_down); |
|
|
|
cur = current_item(qm->ncurses_menu); |
|
|
|
if (cur == prev) |
|
|
@ -511,8 +512,8 @@ static void qmenu_drive_upordown(Qmenu* qm, int req_up_or_down) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
bool qmenu_drive(Qmenu* qm, int key, Qmenu_action* out_action) { |
|
|
|
struct Qmenu_item_extra* extras = qmenu_item_extras_ptr(qm); |
|
|
|
bool qmenu_drive(Qmenu *qm, int key, Qmenu_action *out_action) { |
|
|
|
struct Qmenu_item_extra *extras = qmenu_item_extras_ptr(qm); |
|
|
|
switch (key) { |
|
|
|
case 27: { |
|
|
|
out_action->any.type = Qmenu_action_type_canceled; |
|
|
@ -521,7 +522,7 @@ bool qmenu_drive(Qmenu* qm, int key, Qmenu_action* out_action) { |
|
|
|
case ' ': |
|
|
|
case '\r': |
|
|
|
case KEY_ENTER: { |
|
|
|
ITEM* cur = current_item(qm->ncurses_menu); |
|
|
|
ITEM *cur = current_item(qm->ncurses_menu); |
|
|
|
out_action->picked.type = Qmenu_action_type_picked; |
|
|
|
out_action->picked.id = cur ? qmenu_itemextra(extras, cur)->user_id : 0; |
|
|
|
return true; |
|
|
@ -536,20 +537,20 @@ bool qmenu_drive(Qmenu* qm, int key, Qmenu_action* out_action) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
Qmenu* qmenu_of(Qblock* qb) { return ORCA_CONTAINER_OF(qb, Qmenu, qblock); } |
|
|
|
Qmenu *qmenu_of(Qblock *qb) { return ORCA_CONTAINER_OF(qb, Qmenu, qblock); } |
|
|
|
|
|
|
|
bool qmenu_top_is_menu(int id) { |
|
|
|
Qblock* qb = qnav_top_block(); |
|
|
|
Qblock *qb = qnav_top_block(); |
|
|
|
if (!qb) |
|
|
|
return false; |
|
|
|
if (qb->tag != Qblock_type_qmenu) |
|
|
|
return false; |
|
|
|
Qmenu* qm = qmenu_of(qb); |
|
|
|
Qmenu *qm = qmenu_of(qb); |
|
|
|
return qm->id == id; |
|
|
|
} |
|
|
|
|
|
|
|
Qform* qform_create(int id) { |
|
|
|
Qform* qf = (Qform*)malloc(sizeof(Qform)); |
|
|
|
Qform *qform_create(int id) { |
|
|
|
Qform *qf = (Qform *)malloc(sizeof(Qform)); |
|
|
|
qblock_init(&qf->qblock, Qblock_type_qform); |
|
|
|
qf->ncurses_form = NULL; |
|
|
|
qf->ncurses_fields[0] = NULL; |
|
|
@ -558,22 +559,22 @@ Qform* qform_create(int id) { |
|
|
|
return qf; |
|
|
|
} |
|
|
|
|
|
|
|
Qform* qform_of(Qblock* qb) { return ORCA_CONTAINER_OF(qb, Qform, qblock); } |
|
|
|
Qform *qform_of(Qblock *qb) { return ORCA_CONTAINER_OF(qb, Qform, qblock); } |
|
|
|
|
|
|
|
int qform_id(Qform const* qf) { return qf->id; } |
|
|
|
int qform_id(Qform const *qf) { return qf->id; } |
|
|
|
|
|
|
|
void qform_add_text_line(Qform* qf, int id, char const* initial) { |
|
|
|
FIELD* f = new_field(1, 30, 0, 0, 0, 0); |
|
|
|
void qform_add_text_line(Qform *qf, int id, char const *initial) { |
|
|
|
FIELD *f = new_field(1, 30, 0, 0, 0, 0); |
|
|
|
if (initial) |
|
|
|
set_field_buffer(f, 0, initial); |
|
|
|
set_field_userptr(f, (void*)(intptr_t)(id)); |
|
|
|
set_field_userptr(f, (void *)(intptr_t)(id)); |
|
|
|
field_opts_off(f, O_WRAP | O_BLANK | O_STATIC); |
|
|
|
qf->ncurses_fields[qf->fields_count] = f; |
|
|
|
++qf->fields_count; |
|
|
|
qf->ncurses_fields[qf->fields_count] = NULL; |
|
|
|
} |
|
|
|
|
|
|
|
void qform_push_to_nav(Qform* qf) { |
|
|
|
void qform_push_to_nav(Qform *qf) { |
|
|
|
qf->ncurses_form = new_form(qf->ncurses_fields); |
|
|
|
int form_min_h, form_min_w; |
|
|
|
scale_form(qf->ncurses_form, &form_min_h, &form_min_w); |
|
|
@ -586,11 +587,11 @@ void qform_push_to_nav(Qform* qf) { |
|
|
|
form_driver(qf->ncurses_form, REQ_END_LINE); |
|
|
|
} |
|
|
|
|
|
|
|
void qform_set_title(Qform* qf, char const* title) { |
|
|
|
void qform_set_title(Qform *qf, char const *title) { |
|
|
|
qblock_set_title(&qf->qblock, title); |
|
|
|
} |
|
|
|
|
|
|
|
void qform_free(Qform* qf) { |
|
|
|
void qform_free(Qform *qf) { |
|
|
|
curs_set(0); |
|
|
|
unpost_form(qf->ncurses_form); |
|
|
|
free_form(qf->ncurses_form); |
|
|
@ -600,7 +601,7 @@ void qform_free(Qform* qf) { |
|
|
|
free(qf); |
|
|
|
} |
|
|
|
|
|
|
|
bool qform_drive(Qform* qf, int key, Qform_action* out_action) { |
|
|
|
bool qform_drive(Qform *qf, int key, Qform_action *out_action) { |
|
|
|
switch (key) { |
|
|
|
case 27: { |
|
|
|
out_action->any.type = Qform_action_type_canceled; |
|
|
@ -644,7 +645,7 @@ bool qform_drive(Qform* qf, int key, Qform_action* out_action) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
static Usz size_without_trailing_spaces(char const* str) { |
|
|
|
static Usz size_without_trailing_spaces(char const *str) { |
|
|
|
Usz size = strlen(str); |
|
|
|
for (;;) { |
|
|
|
if (size == 0) |
|
|
@ -656,22 +657,22 @@ static Usz size_without_trailing_spaces(char const* str) { |
|
|
|
return size; |
|
|
|
} |
|
|
|
|
|
|
|
FIELD* qform_find_field(Qform const* qf, int id) { |
|
|
|
FIELD *qform_find_field(Qform const *qf, int id) { |
|
|
|
Usz count = qf->fields_count; |
|
|
|
for (Usz i = 0; i < count; ++i) { |
|
|
|
FIELD* f = qf->ncurses_fields[i]; |
|
|
|
FIELD *f = qf->ncurses_fields[i]; |
|
|
|
if ((int)(intptr_t)field_userptr(f) == id) |
|
|
|
return f; |
|
|
|
} |
|
|
|
return NULL; |
|
|
|
} |
|
|
|
|
|
|
|
bool qform_get_text_line(Qform const* qf, int id, oso** out) { |
|
|
|
FIELD* f = qform_find_field(qf, id); |
|
|
|
bool qform_get_text_line(Qform const *qf, int id, oso **out) { |
|
|
|
FIELD *f = qform_find_field(qf, id); |
|
|
|
if (!f) |
|
|
|
return false; |
|
|
|
form_driver(qf->ncurses_form, REQ_VALIDATION); |
|
|
|
char* buf = field_buffer(f, 0); |
|
|
|
char *buf = field_buffer(f, 0); |
|
|
|
if (!buf) |
|
|
|
return false; |
|
|
|
Usz trimmed = size_without_trailing_spaces(buf); |
|
|
|