diff --git a/server/menu.c b/server/menu.c index 4a5c58e..c123766 100644 --- a/server/menu.c +++ b/server/menu.c @@ -64,6 +64,34 @@ menu_get_subitem(Menu * menu, int index) return NULL; } +/** + * Searches for a subitem with id item_id. This function ignores hidden + * entries completely. + * + * @return index of subitem if found and -1 otherwise. */ +static int +menu_get_index_of(Menu * menu, char * item_id) +{ + MenuItem * item; + int i = 0; + + debug (RPT_DEBUG, "%s( menu=[%s], item_id=%s )", __FUNCTION__, + ((menu != NULL) ? menu->id : "(null)"), item_id); + for (item = LL_GetFirst(menu->data.menu.contents); + item != NULL; + item = LL_GetNext(menu->data.menu.contents)) + { + if ( ! item->is_hidden) + { + if (strcmp(item_id, item->id) == 0) + return i; + /* hidden items don't count at all... */ + ++i; + } + } + return -1; +} + static int menu_visible_item_count(Menu * menu) { @@ -473,10 +501,57 @@ void menu_update_screen (MenuItem *menu, Screen *s) report (RPT_ERR, "%s: could not find widget: %s", __FUNCTION__, "downscroller"); } -MenuResult menu_process_input (Menu *menu, MenuToken token, char * key, bool extended) +MenuItem * menu_get_item_for_predecessor_check(Menu *menu) +{ + MenuItem *subitem = menu_get_subitem(menu, menu->data.menu.selector_pos); + if ( ! subitem) + return NULL; + switch (subitem->type) { + case MENUITEM_ACTION: + case MENUITEM_CHECKBOX: + case MENUITEM_RING: + // for types without own screen: look for menu's + // predecessor if its subitem doesn't have one. (Since + // menus can't have successors this problem arises + // only for predecessors.) + if (subitem->predecessor_id == NULL) + return menu; + return subitem; + case MENUITEM_MENU: + case MENUITEM_SLIDER: + case MENUITEM_NUMERIC: + case MENUITEM_ALPHA: + case MENUITEM_IP: + return menu; + default: + return NULL; + } +} + +MenuItem * menu_get_item_for_successor_check(Menu *menu) +{ + MenuItem *subitem = menu_get_subitem(menu, menu->data.menu.selector_pos); + if ( ! subitem) + return NULL; + switch (subitem->type) { + case MENUITEM_ACTION: + case MENUITEM_CHECKBOX: + case MENUITEM_RING: + return subitem; + case MENUITEM_MENU: + case MENUITEM_SLIDER: + case MENUITEM_NUMERIC: + case MENUITEM_ALPHA: + case MENUITEM_IP: + return menu; + default: + return NULL; + } +} + +MenuResult menu_process_input(Menu *menu, MenuToken token, char * key, bool extended) { MenuItem *subitem; - debug (RPT_DEBUG, "%s( menu=[%s], token=%d, key=\"%s\" )", __FUNCTION__, ((menu != NULL) ? menu->id : "(null)"), token, key); @@ -485,7 +560,11 @@ MenuResult menu_process_input (Menu *menu, MenuToken token, char * key, bool ext switch (token) { case MENUTOKEN_MENU: - return MENURESULT_CLOSE; + subitem = menu_get_item_for_predecessor_check(menu); + if ( ! subitem) + return MENURESULT_ERROR; + return menuitem_predecessor2menuresult( + subitem->predecessor_id, MENURESULT_CLOSE); case MENUTOKEN_ENTER: subitem = menu_get_subitem (menu, menu->data.menu.selector_pos); if (!subitem) @@ -494,8 +573,8 @@ MenuResult menu_process_input (Menu *menu, MenuToken token, char * key, bool ext case MENUITEM_ACTION: if (subitem->event_func) subitem->event_func (subitem, MENUEVENT_SELECT); - return subitem->data.action.menu_result; - //return MENURESULT_QUIT; + return menuitem_successor2menuresult( + subitem->successor_id, MENURESULT_NONE); case MENUITEM_CHECKBOX: if (subitem->data.checkbox.allow_gray) { subitem->data.checkbox.value = (subitem->data.checkbox.value + 1) % 3; @@ -505,12 +584,14 @@ MenuResult menu_process_input (Menu *menu, MenuToken token, char * key, bool ext } if (subitem->event_func) subitem->event_func (subitem, MENUEVENT_UPDATE); - return MENURESULT_NONE; + return menuitem_successor2menuresult( + subitem->successor_id, MENURESULT_NONE); case MENUITEM_RING: subitem->data.ring.value = (subitem->data.ring.value + 1) % LL_Length (subitem->data.ring.strings); if (subitem->event_func) subitem->event_func (subitem, MENUEVENT_UPDATE); - return MENURESULT_NONE; + return menuitem_successor2menuresult( + subitem->successor_id, MENURESULT_NONE); case MENUITEM_MENU: case MENUITEM_SLIDER: case MENUITEM_NUMERIC: @@ -554,6 +635,8 @@ MenuResult menu_process_input (Menu *menu, MenuToken token, char * key, bool ext break; switch (subitem->type) { case MENUITEM_CHECKBOX: + /* note: this dangerous looking code works since + * CheckboxValue is an enum >= 0. */ if (subitem->data.checkbox.allow_gray) { subitem->data.checkbox.value = (subitem->data.checkbox.value - 1) % 3; } @@ -564,7 +647,10 @@ MenuResult menu_process_input (Menu *menu, MenuToken token, char * key, bool ext subitem->event_func (subitem, MENUEVENT_UPDATE); return MENURESULT_NONE; case MENUITEM_RING: - subitem->data.ring.value = (subitem->data.ring.value - 1) % LL_Length (subitem->data.ring.strings); + /* ring: jump to the end if beginning is reached */ + subitem->data.ring.value = (subitem->data.ring.value < 1) + ? LL_Length (subitem->data.ring.strings) - 1 + : (subitem->data.ring.value - 1) % LL_Length (subitem->data.ring.strings); if (subitem->event_func) subitem->event_func (subitem, MENUEVENT_UPDATE); return MENURESULT_NONE; @@ -607,3 +693,26 @@ MenuResult menu_process_input (Menu *menu, MenuToken token, char * key, bool ext } return MENURESULT_ERROR; } + +/** positions current item pointer on subitem subitem_id. If subitem_id is + * hidden or not valid subitem of menu this function does nothing. */ +void menu_select_subitem(Menu *menu, char * subitem_id) +{ + assert(menu != NULL); + debug(RPT_DEBUG, "%s( menu=[%s], subitem_id=\"%s\" )", __FUNCTION__, + menu->id, subitem_id); + int position = menu_get_index_of(menu, subitem_id); + if (position < 0) + { + debug(RPT_DEBUG, "%s: subitem \"%s\" not found" + " or hidden in \"%s\", ignored", + __FUNCTION__, subitem_id, menu->id); + return; + } + // debug(RPT_DEBUG, "%s: %s->%s is at position %d," + // " current item is at menu position: %d, scroll: %d", + // __FUNCTION__, menu->id, subitem_id, position, + // menu->data.menu.selector_pos, menu->data.menu.scroll); + menu->data.menu.selector_pos = position; + menu->data.menu.scroll = position; +} diff --git a/server/menu.h b/server/menu.h index 556fc21..8f8660c 100644 --- a/server/menu.h +++ b/server/menu.h @@ -103,10 +103,28 @@ void menu_build_screen (Menu *menu, Screen *s); */ void menu_update_screen (Menu *menu, Screen *s); +/** + * For predecessor-Check: returns selected subitem of menu if this subitem + * has no own screen (action, checkbox, ...) and this subitem has a + * predecessor and menu otherwise. + * + * @return NULL on error. */ +MenuItem * menu_get_item_for_predecessor_check(Menu *menu); + +/** + * For successor-Check: returns selected subitem of menu if + * this subitem has no own screen (action, checkbox, ...) or menu + * otherwise. + * + * @return NULL on error. */ +MenuItem * menu_get_item_for_successor_check(Menu *menu); + /** Does something with the given input. * key is only used if token is MENUTOKEN_OTHER. * DO NOT CALL THIS FUNCTION, CALL menuitem_process_input INSTEAD ! */ MenuResult menu_process_input (Menu *menu, MenuToken token, char * key, bool extended); +/** positions current item pointer on subitem subitem_id. */ +void menu_select_subitem(Menu *menu, char * subitem_id); #endif diff --git a/server/menuscreens.c b/server/menuscreens.c index 10da976..6b44a0e 100644 --- a/server/menuscreens.c +++ b/server/menuscreens.c @@ -57,6 +57,11 @@ Menu * custom_main_menu = NULL; Menu * screens_menu = NULL; /* Local prototypes */ +static void handle_quit(); +static void handle_close(); +static void handle_none(); +static void handle_enter(); +static void handle_successor(); void menuscreen_switch_item (MenuItem * new_menuitem); void menuscreen_create_menu (); Menu* menuscreen_get_main (); @@ -242,11 +247,13 @@ static void handle_close() debug (RPT_DEBUG, "%s: Closing item", __FUNCTION__); menuscreen_switch_item( (active_menuitem == menuscreen_get_main()) - ? NULL : active_menuitem->parent); + ? NULL + : active_menuitem->parent); } static void handle_none() { + debug (RPT_DEBUG, "%s: Staying in item", __FUNCTION__); if (active_menuitem) { menuitem_update_screen (active_menuitem, menuscreen); @@ -266,21 +273,86 @@ static void handle_enter() menuscreen_switch_item (menu_get_current_item (active_menuitem)); } +static void handle_predecessor() +{ + MenuItem* item = (active_menuitem->type == MENUITEM_MENU) + ? menu_get_item_for_predecessor_check(active_menuitem) + : active_menuitem; + assert(item != NULL); + debug (RPT_DEBUG, "%s: Switching to registered predecessor '%s' of '%s'.", + __FUNCTION__, item->predecessor_id, item->id); + MenuItem *predecessor = menuitem_search( + item->predecessor_id, (Client*)active_menuitem->client); + if (predecessor == NULL) + { + // note: if _quit_, _close_, _none_ get here this + // would be an implementation error - they should + // have been handled via different MENURESULT codes. + report (RPT_ERR, "%s: cannot find predecessor '%s' of '%s'.", + __FUNCTION__, item->predecessor_id, item->id); + return; + } + switch (predecessor->type) { + case MENUITEM_ACTION: + case MENUITEM_CHECKBOX: + case MENUITEM_RING: + if (active_menuitem != predecessor->parent) + menuscreen_switch_item(predecessor->parent); + // this won't work for hidden subitems + menu_select_subitem(active_menuitem, item->predecessor_id); + menuitem_update_screen(active_menuitem, menuscreen); + break; + default: + if (predecessor->parent != NULL + && predecessor->parent->type == MENUITEM_MENU) + { + // update parent menu too + menu_select_subitem(predecessor->parent, predecessor->id); + } + menuscreen_switch_item(predecessor); + break; + } +} + static void handle_successor() { - debug (RPT_DEBUG, "%s: Switching to succeeding item %s.", - __FUNCTION__, active_menuitem->successor_id); - // FIXME: code duplication: see menu_commands:search_item() -# ifdef LCDPROC_PERMISSIVE_MENU_GOTO - MenuItem *top = ((Client*)active_menuitem->client)->menu; -# else - MenuItem *top = main_menu; -# endif /* LCDPROC_PERMISSIVE_MENU_GOTO */ - MenuItem *item = menu_find_item(top, active_menuitem->successor_id, true); - if (item == NULL) - report (RPT_ERR, "%s: cannot find successor %s.", __FUNCTION__, - active_menuitem->successor_id); - menuscreen_switch_item(item); + MenuItem* item = (active_menuitem->type == MENUITEM_MENU) + ? menu_get_item_for_successor_check(active_menuitem) + : active_menuitem; + assert(item != NULL); + debug (RPT_DEBUG, "%s: Switching to registered successor '%s' of '%s'.", + __FUNCTION__, item->successor_id, item->id); + MenuItem *successor = menuitem_search( + item->successor_id, (Client*)active_menuitem->client); + if (successor == NULL) + { + // note: if _quit_, _close_, _none_ get here this + // would be an implementation error - they should + // have been handled via different MENURESULT codes. + report (RPT_ERR, "%s: cannot find successor '%s' of '%s'.", + __FUNCTION__, item->successor_id, item->id); + return; + } + switch (successor->type) { + case MENUITEM_ACTION: + case MENUITEM_CHECKBOX: + case MENUITEM_RING: + if (active_menuitem != successor->parent) + menuscreen_switch_item(successor->parent); + // this won't work for hidden subitems + menu_select_subitem(active_menuitem, item->successor_id); + menuitem_update_screen(active_menuitem, menuscreen); + break; + default: + if (successor->parent != NULL + && successor->parent->type == MENUITEM_MENU) + { + // update parent menu too + menu_select_subitem(successor->parent, successor->id); + } + menuscreen_switch_item(successor); + break; + } } void menuscreen_key_handler (char *key) @@ -333,23 +405,20 @@ void menuscreen_key_handler (char *key) handle_enter(); break; case MENURESULT_CLOSE: - if (active_menuitem->successor_id != NULL) - { - if (strcmp("_quit_", active_menuitem->successor_id) == 0) - handle_quit(); - else if (strcmp("_close_", active_menuitem->successor_id) == 0) - handle_close(); - else if (strcmp("_none_", active_menuitem->successor_id) == 0) - handle_none(); - else - handle_successor(); - } - else - handle_close(); + handle_close(); break; case MENURESULT_QUIT: handle_quit(); break; + case MENURESULT_PREDECESSOR: + handle_predecessor(); + break; + case MENURESULT_SUCCESSOR: + handle_successor(); + break; + default: + assert(!"unexpected menuresult"); + break; } }