示例#1
0
const std::string &input_context::handle_input()
{
    next_action.type = CATA_INPUT_ERROR;
    while(1) {
        next_action = inp_mngr.get_input_event(NULL);
        if (next_action.type == CATA_INPUT_TIMEOUT) {
            return TIMEOUT;
        }

        const std::string &action = input_to_action(next_action);

        // Special help action
        if(action == "HELP_KEYBINDINGS") {
            display_help();
            return ANY_INPUT;
        }

        if(next_action.type == CATA_INPUT_MOUSE) {
            if(!handling_coordinate_input) {
                continue; // Ignore this mouse input.
            }

            coordinate_input_received = true;
            coordinate_x = next_action.mouse_x;
            coordinate_y = next_action.mouse_y;
        } else {
            coordinate_input_received = false;
        }

        if(action != CATA_ERROR) {
            return action;
        }

        // If we registered to receive any input, return ANY_INPUT
        // to signify that an unregistered key was pressed.
        if(registered_any_input) {
            return ANY_INPUT;
        }

        // If it's an invalid key, just keep looping until the user
        // enters something proper.
    }
}
示例#2
0
std::string input_context::get_available_single_char_hotkeys(std::string requested_keys)
{
    for (std::vector<std::string>::const_iterator registered_action = registered_actions.begin();
         registered_action != registered_actions.end();
         ++registered_action) {

        const std::vector<input_event> &events = inp_mngr.get_input_for_action(*registered_action,
                category);
        for( const auto &events_event : events ) {
            // Only consider keyboard events without modifiers
            if( events_event.type == CATA_INPUT_KEYBOARD && 0 == events_event.modifiers.size() ) {
                requested_keys.erase( std::remove_if( requested_keys.begin(), requested_keys.end(),
                                                      ContainsPredicate<std::vector<long>, char>(
                                                          events_event.sequence ) ),
                                      requested_keys.end() );
            }
        }
    }

    return requested_keys;
}
示例#3
0
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);
}
示例#4
0
void input_context::display_menu()
{
    inp_mngr.reset_timeout();
    // Shamelessly stolen from help.cpp

    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 );

    int maxwidth = max( FULL_SCREEN_WIDTH, TERMX );
    int width = min( 80, maxwidth );
    int maxheight = max( FULL_SCREEN_HEIGHT, TERMY );
    int height = min( maxheight, ( int ) hotkeys.size() + LEGEND_HEIGHT + BORDER_SPACE );

    catacurses::window w_help = catacurses::newwin( height - 2, width - 2, maxheight / 2 - height / 2,
                                maxwidth / 2 - width / 2 );

    // 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 );
    org_registered_actions.erase( std::remove_if( org_registered_actions.begin(),
                                  org_registered_actions.end(),
    []( const std::string & a ) {
        return a == ANY_INPUT || a == COORDINATE;
    } ), org_registered_actions.end() );

    // colors of the keybindings
    static const nc_color global_key = c_light_gray;
    static const nc_color local_key = c_light_green;
    static const nc_color unbound_key = c_light_red;
    // (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 = height - LEGEND_HEIGHT - BORDER_SPACE; // -2 for the border
    // width of the legend
    const size_t legwidth = width - 4 - BORDER_SPACE;
    // 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" );

    std::vector<std::string> filtered_registered_actions = org_registered_actions;
    std::string filter_phrase;
    std::string action;
    long raw_input_char = 0;
    string_input_popup spopup;
    spopup.window( w_help, 4, 8, legwidth )
    .max_length( legwidth )
    .context( ctxt );

    while( true ) {
        werase( w_help );
        draw_border( w_help, BORDER_COLOR, _( "Keybindings" ), c_light_red );
        draw_scrollbar( w_help, scroll_offset, display_height,
                        filtered_registered_actions.size(), 10, 0, c_white, true );
        fold_and_print( w_help, 1, 2, legwidth, c_white, legend.str() );

        for( size_t i = 0; i + scroll_offset < filtered_registered_actions.size() &&
             i < display_height; i++ ) {
            const std::string &action_id = filtered_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 + 10, 2, c_dark_gray, "%c ", invlet );
            } else if( status == s_add || status == s_add_global ) {
                mvwprintz( w_help, i + 10, 2, c_blue, "%c ", invlet );
            } else if( status == s_remove ) {
                mvwprintz( w_help, i + 10, 2, c_blue, "%c ", invlet );
            } else {
                mvwprintz( w_help, i + 10, 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 + 10, 4, col, "%s: ", get_action_name( action_id ).c_str() );
            mvwprintz( w_help, i + 10, 52, col, "%s", get_desc( action_id ).c_str() );
        }

        // spopup.query_string() will call wrefresh( w_help )
        catacurses::refresh();

        spopup.text( filter_phrase );
        if( status == s_show ) {
            filter_phrase = spopup.query_string( false );
            action = ctxt.input_to_action( ctxt.get_raw_input() );
        } else {
            spopup.query_string( false, true );
            action = ctxt.handle_input();
        }
        raw_input_char = ctxt.get_raw_input().get_first_input();

        filtered_registered_actions = filter_strings_by_phrase( org_registered_actions, filter_phrase );
        if( scroll_offset > filtered_registered_actions.size() ) {
            scroll_offset = 0;
        }

        if( filtered_registered_actions.empty() && action != "QUIT" ) {
            continue;
        }

        // 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.
        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( hotkey_index == std::string::npos ) {
                continue;
            }
            const size_t action_index = hotkey_index + scroll_offset;
            if( action_index >= filtered_registered_actions.size() ) {
                continue;
            }
            const std::string &action_id = filtered_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 && ( !get_option<bool>( "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 );

                if( action_uses_input( action_id, new_event ) ) {
                    popup_getkey( _( "This key is already used for %s." ), name.c_str() );
                    status = s_show;
                    continue;
                }

                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( filtered_registered_actions.size() > display_height &&
                scroll_offset < filtered_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 < filtered_registered_actions.size() ) {
                scroll_offset += std::min( display_height, filtered_registered_actions.size() -
                                           display_height - scroll_offset );
            } else if( filtered_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( filtered_registered_actions.size() > display_height ) {
                scroll_offset = filtered_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() );
        }
    } else if( changed ) {
        inp_mngr.action_contexts.swap( old_action_contexts );
    }
    werase( w_help );
    wrefresh( w_help );
}
示例#5
0
bool input_context::action_uses_input( const std::string &action_id,
                                       const input_event &event ) const
{
    const auto &events = inp_mngr.get_action_attributes( action_id, category ).input_events;
    return std::find( events.begin(), events.end(), event ) != events.end();
}
示例#6
0
void input_manager::init() {
    init_keycode_mapping();

    std::ifstream data_file;
    picojson::value input_value;

    std::string file_name = "data/raw/keybindings.json";
    data_file.open(file_name.c_str());

    if(!data_file.good()) {
        throw "Could not read " + file_name;
    }

    data_file >> input_value;
    data_file.close();

    if(!input_value.is<picojson::array>()) {
        throw file_name + "is not an array";
    }

    //Crawl through once and create an entry for every definition
    const picojson::array& root = input_value.get<picojson::array>();

    for (picojson::array::const_iterator entry = root.begin();
         entry != root.end(); ++entry) {
        if( !(entry->is<picojson::object>()) ){
            debugmsg("Invalid keybinding setting, entry not a JSON object");
            continue;
        }

        // JSON object representing the action
        const picojson::value& action_object = *entry;

        const std::string& action_id = action_object.get("id").get<std::string>();

        std::string context = "default";
        if(action_object.contains("category")) {
            context = action_object.get("category").get<std::string>();
        }

        // Iterate over the bindings JSON array
        const picojson::array& keybindings = action_object.get("bindings").get<picojson::array>();
        for (picojson::array::const_iterator subentry = keybindings.begin();
             subentry != keybindings.end(); ++subentry) {

            const picojson::value& keybinding = *subentry;
            const std::string& input_method = keybinding.get("input_method").get<std::string>();
            input_event new_event;
            if(input_method == "keyboard") {
                new_event.type = CATA_INPUT_KEYBOARD;
            } else if(input_method == "gamepad") {
                new_event.type = CATA_INPUT_GAMEPAD;
            }

            if(keybinding.get("key").is<std::string>()) {
                const std::string& key = keybinding.get("key").get<std::string>();

                new_event.sequence.push_back(inp_mngr.get_keycode(key));
            } else if(keybinding.get("key").is<picojson::array>()) {
                picojson::array keys = keybinding.get("key").get<picojson::array>();
                for(int i=0; i<keys.size(); i++) {
                    const std::string& next_key = keybinding.get("key").get(i).get<std::string>();

                    new_event.sequence.push_back(inp_mngr.get_keycode(next_key));
                }
            }

            if(context == "default") {
                action_to_input[action_id].push_back(new_event);
            } else {
                action_contexts[context][action_id].push_back(new_event);
            }
        }

        if(!action_object.contains("name")) {
            actionID_to_name[action_id] = action_id;
        } else {
            actionID_to_name[action_id] = action_object.get("name").get<std::string>();
        }
    }
}