There are several ways to archive but in the end, they are both hard to maintain and cause performance down. Let's say you would build a lookup table to ref to, assume you have 4 buttons, an array with 5 entries -> the size of lookup table(worse case) is 4x4x4x4x4 = 1024
entries. Finding the corresponding action in this table per each button press is not that good in term of performance.
Let's back to what you wanna to archive, check below code for your ref.
By create a menu tree, bind callback to each menu item and keep track the path of current menu item, it's not that difficult to travel back and forth in menu tree. Once you enter a specific menu item, it's easy to access to its callback by the pointer that was assigned to.
#include <stdio.h>
#define MENU(name, text, nr_item, ...) \
static menu_t name = { \
{ text, NULL, nr_item, __VA_ARGS__ }, { 0 }, -1 \
}
#define MENU_ITEMS(...) (menu_item_t[]) { __VA_ARGS__ }
#define MAX_DEPTH 10
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
typedef struct menu_item menu_item_t;
struct menu_item {
const char *text;
void (*action)(menu_item_t*);
int nr_of_children;
menu_item_t *children;
};
typedef struct {
menu_item_t item;
menu_item_t* _history[MAX_DEPTH];
int _history_index;
} menu_t;
static inline void print_menu_history(menu_t *menu) {
int i = 0;
for (i = 0; i < MAX_DEPTH; i++) {
if (!menu->_history[i]) break;
printf("> %s ", menu->_history[i]->text);
}
printf("\n");
}
static inline void print_menu(menu_t *menu) {
menu_item_t *item = menu->_history[menu->_history_index];
int i;
printf("= %s\n", item->text);
for (i = 0; i < item->nr_of_children; i++) {
printf("- [%2d] %s\n", i, item->children[i].text);
}
printf("==========================================\n");
print_menu_history(menu);
printf("==========================================\n");
}
static inline void add_item_to_history(menu_t *menu, int position) {
if (menu->_history_index < 0) {
menu->_history_index = 0;
menu->_history[menu->_history_index] = &menu->item;
} else {
if (position < menu->_history[menu->_history_index]->nr_of_children)
menu->_history[++menu->_history_index] =
&menu->_history[menu->_history_index]->children[position];
}
}
static inline void exec_menu_action(menu_t *menu, int position) {
if (menu->_history[menu->_history_index]->action)
menu->_history[menu->_history_index]->action(menu->_history[menu->_history_index]);
}
static inline void exit_menu(menu_t *menu) {
if (menu->_history_index > 0)
menu->_history[menu->_history_index--] = NULL;
print_menu(menu);
}
static inline void enter_menu(menu_t *menu, int position) {
add_item_to_history(menu, position);
exec_menu_action(menu, position);
print_menu(menu);
}
static void menu_item_action(menu_item_t *item) {
if (item) printf("=========='%s' selected\n", item->text);
}
MENU(main_menu, "Menu", 2,
MENU_ITEMS(
{ "Item 1", menu_item_action, 3,
MENU_ITEMS(
{ "Item 1.1", menu_item_action, 3,
MENU_ITEMS(
{ "Item 1.1.1", menu_item_action, 0, NULL },
{ "Item 1.1.2", menu_item_action, 0, NULL },
{ "Item 1.1.3", menu_item_action, 0, NULL }
)
},
{ "Item 1.2", menu_item_action, 0, NULL },
{ "Item 1.3", menu_item_action, 0, NULL }
)
},
{ "Item 2", menu_item_action, 2,
MENU_ITEMS(
{ "Item 2.1", menu_item_action, 0, NULL },
{ "Item 2.2", menu_item_action, 0, NULL }
)
}
)
);
int main(int argc, char *argv[]) {
unsigned char c = 0;
int samples[] = {1, 0, 1, 0, 0};
enter_menu(&main_menu, -1);
for (c = 0; c < ARRAY_SIZE(samples); c++) {
enter_menu(&main_menu, c);
}
while(c != 'q') {
c = getchar();
if ('b' == c) exit_menu(&main_menu);
if ((c <= '9') && (c >= '0')) enter_menu(&main_menu, c - '0');
}
return 0;
}