const std::string &input_context::handle_input( const int timeout ) { inp_mngr.set_timeout( timeout ); const std::string &result = handle_input(); inp_mngr.reset_timeout(); return result; }
void input_context::display_help() { inp_mngr.set_timeout(-1); // Shamelessly stolen from help.cpp WINDOW *w_help = newwin(FULL_SCREEN_HEIGHT - 2, FULL_SCREEN_WIDTH - 2, 1 + (int)((TERMY > FULL_SCREEN_HEIGHT) ? (TERMY - FULL_SCREEN_HEIGHT) / 2 : 0), 1 + (int)((TERMX > FULL_SCREEN_WIDTH) ? (TERMX - FULL_SCREEN_WIDTH) / 2 : 0)); // has the user changed something? bool changed = false; // keybindings before the user changed anything. input_manager::t_action_contexts old_action_contexts(inp_mngr.action_contexts); // current status: adding/removing/showing keybindings enum { s_remove, s_add, s_add_global, s_show } status = s_show; // copy of registered_actions, but without the ANY_INPUT and COORDINATE, which should not be shown std::vector<std::string> org_registered_actions(registered_actions); std::vector<std::string>::iterator any_input = std::find(org_registered_actions.begin(), org_registered_actions.end(), ANY_INPUT); if (any_input != org_registered_actions.end()) { org_registered_actions.erase(any_input); } std::vector<std::string>::iterator coordinate = std::find(org_registered_actions.begin(), org_registered_actions.end(), COORDINATE); if (coordinate != org_registered_actions.end()) { org_registered_actions.erase(coordinate); } // colors of the keybindings static const nc_color global_key = c_ltgray; static const nc_color local_key = c_ltgreen; static const nc_color unbound_key = c_ltred; // (vertical) scroll offset size_t scroll_offset = 0; // height of the area usable for display of keybindings, excludes headers & borders const size_t display_height = FULL_SCREEN_HEIGHT - 9 - 2; // -2 for the border // width of the legend const size_t legwidth = FULL_SCREEN_WIDTH - 4 - 2; // keybindings help std::ostringstream legend; legend << "<color_" << string_from_color(unbound_key) << ">" << _("Unbound keys") << "</color>\n"; legend << "<color_" << string_from_color(local_key) << ">" << _("Keybinding active only on this screen") << "</color>\n"; legend << "<color_" << string_from_color(global_key) << ">" << _("Keybinding active globally") << "</color>\n"; legend << _("Press - to remove keybinding\nPress + to add local keybinding\nPress = to add global keybinding\n"); input_context ctxt("HELP_KEYBINDINGS"); ctxt.register_action("UP", _("Scroll up")); ctxt.register_action("DOWN", _("Scroll down")); ctxt.register_action("PAGE_DOWN"); ctxt.register_action("PAGE_UP"); ctxt.register_action("REMOVE"); ctxt.register_action("ADD_LOCAL"); ctxt.register_action("ADD_GLOBAL"); ctxt.register_action("QUIT"); ctxt.register_action("ANY_INPUT"); if (category != "HELP_KEYBINDINGS") { // avoiding inception! ctxt.register_action("HELP_KEYBINDINGS"); } std::string hotkeys = ctxt.get_available_single_char_hotkeys(display_help_hotkeys); while(true) { werase(w_help); draw_border(w_help); draw_scrollbar(w_help, scroll_offset, display_height, org_registered_actions.size() - display_height, 8); mvwprintz(w_help, 0, (FULL_SCREEN_WIDTH - utf8_width(_("Keybindings"))) / 2 - 1, c_ltred, " %s ", _("Keybindings")); fold_and_print(w_help, 1, 2, legwidth, c_white, legend.str()); for (size_t i = 0; i + scroll_offset < org_registered_actions.size() && i < display_height; i++) { const std::string &action_id = org_registered_actions[i + scroll_offset]; bool overwrite_default; const action_attributes &attributes = inp_mngr.get_action_attributes(action_id, category, &overwrite_default); char invlet; if (i < hotkeys.size()) { invlet = hotkeys[i]; } else { invlet = ' '; } if (status == s_add_global && overwrite_default) { // We're trying to add a global, but this action has a local // defined, so gray out the invlet. mvwprintz(w_help, i + 8, 2, c_dkgray, "%c ", invlet); } else if (status == s_add || status == s_add_global) { mvwprintz(w_help, i + 8, 2, c_blue, "%c ", invlet); } else if (status == s_remove) { mvwprintz(w_help, i + 8, 2, c_blue, "%c ", invlet); } else { mvwprintz(w_help, i + 8, 2, c_blue, " "); } nc_color col; if (attributes.input_events.empty()) { col = unbound_key; } else if (overwrite_default) { col = local_key; } else { col = global_key; } mvwprintz(w_help, i + 8, 4, col, "%s: ", get_action_name(action_id).c_str()); mvwprintz(w_help, i + 8, 52, col, "%s", get_desc(action_id).c_str()); } wrefresh(w_help); refresh(); // In addition to the modifiable hotkeys, we also check for hardcoded // keys, e.g. '+', '-', '=', in order to prevent the user from // entering an unrecoverable state. const std::string action = ctxt.handle_input(); const long raw_input_char = ctxt.get_raw_input().get_first_input(); if (action == "ADD_LOCAL" || raw_input_char == '+') { status = s_add; } else if (action == "ADD_GLOBAL" || raw_input_char == '=') { status = s_add_global; } else if (action == "REMOVE" || raw_input_char == '-') { status = s_remove; } else if (action == "ANY_INPUT") { const size_t hotkey_index = hotkeys.find_first_of(raw_input_char); if (status == s_show || hotkey_index == std::string::npos ) { continue; } const size_t action_index = hotkey_index + scroll_offset; if( action_index >= org_registered_actions.size() ) { continue; } const std::string &action_id = org_registered_actions[action_index]; // Check if this entry is local or global. bool is_local = false; inp_mngr.get_action_attributes(action_id, category, &is_local); const std::string name = get_action_name(action_id); if (status == s_remove && (!OPTIONS["QUERY_KEYBIND_REMOVAL"] || query_yn(_("Clear keys for %s?"), name.c_str()))) { // If it's global, reset the global actions. std::string category_to_access = category; if (!is_local) { category_to_access = default_context_id; } inp_mngr.remove_input_for_action(action_id, category_to_access); changed = true; } else if (status == s_add_global && is_local) { // Disallow adding global actions to an action that already has a local defined. popup(_("There are already local keybindings defined for this action, please remove them first.")); } else if (status == s_add || status == s_add_global) { const long newbind = popup_getkey(_("New key for %s:"), name.c_str()); const input_event new_event(newbind, CATA_INPUT_KEYBOARD); const std::string conflicts = get_conflicts(new_event); const bool has_conflicts = !conflicts.empty(); bool resolve_conflicts = false; if (has_conflicts) { resolve_conflicts = query_yn( _("This key conflicts with %s. Remove this key from the conflicting command(s), and continue?"), conflicts.c_str()); } if (!has_conflicts || resolve_conflicts) { if (resolve_conflicts) { clear_conflicting_keybindings(new_event); } // We might be adding a local or global action. std::string category_to_access = category; if (status == s_add_global) { category_to_access = default_context_id; } inp_mngr.add_input_for_action(action_id, category_to_access, new_event); changed = true; } } status = s_show; } else if (action == "DOWN") { if (scroll_offset < org_registered_actions.size() - display_height) { scroll_offset++; } } else if (action == "UP") { if (scroll_offset > 0) { scroll_offset--; } } else if (action == "PAGE_DOWN") { if( scroll_offset + display_height < org_registered_actions.size() ) { scroll_offset += std::min(display_height, org_registered_actions.size() - display_height - scroll_offset); } else if( org_registered_actions.size() > display_height ) { scroll_offset = 0; } } else if( action == "PAGE_UP" ) { if( scroll_offset >= display_height ) { scroll_offset -= display_height; } else if( scroll_offset > 0 ) { scroll_offset = 0; } else if( org_registered_actions.size() > display_height ) { scroll_offset = org_registered_actions.size() - display_height; } } else if (action == "QUIT") { if (status != s_show) { status = s_show; } else { break; } } else if (action == "HELP_KEYBINDINGS") { // update available hotkeys in case they've changed hotkeys = ctxt.get_available_single_char_hotkeys(display_help_hotkeys); } } if (changed && query_yn(_("Save changes?"))) { try { inp_mngr.save(); } catch(std::exception &err) { popup(_("saving keybindings failed: %s"), err.what()); } catch(std::string &err) { popup(_("saving keybindings failed: %s"), err.c_str()); } } else if(changed) { inp_mngr.action_contexts.swap(old_action_contexts); } werase(w_help); wrefresh(w_help); delwin(w_help); }