Пример #1
0
/**
 * Prints a list of descriptions for all parts to the screen inside of a boxed window
 * @param win The window to draw in.
 * @param max_y Draw no further than this y-coordinate.
 * @param width The width of the window.
 * @param &p The index of the part being examined.
 * @param start_at Which vehicle part to start printing at.
 * @param start_limit the part index beyond which the display is full
 */
void vehicle::print_vparts_descs( const catacurses::window &win, int max_y, int width, int &p,
                                  int &start_at, int &start_limit ) const
{
    if( p < 0 || p >= ( int )parts.size() ) {
        return;
    }

    std::vector<int> pl = this->parts_at_relative( parts[p].mount.x, parts[p].mount.y );
    std::ostringstream msg;

    int lines = 0;
    /*
     * start_at and start_limit interaction is little tricky
     * start_at and start_limit start at 0 when moving to a new frame
     * if all the descriptions are displayed in the window, start_limit stays at 0 and
     *    start_at is capped at 0 - so no scrolling at all.
     * if all the descriptions aren't displayed, start_limit jumps to the last displayed part
     *    and the next scrollthrough can start there - so scrolling down happens.
     * when the scroll reaches the point where all the remaining descriptions are displayed in
     *    the window, start_limit is set to start_at again.
     * on the next attempted scrolldown, start_limit is set to the nth item, and start_at is
     *    capped to the nth item, so no more scrolling down.
     * start_at can always go down, but never below 0, so scrolling up is only possible after
     *    some scrolling down has occurred.
     * important! the calling function needs to track p, start_at, and start_limit, and set
     *    start_limit to 0 if p changes.
     */
    start_at = std::max( 0, std::min( start_at, start_limit ) );
    if( start_at ) {
        msg << "<color_yellow>" << "<  " << _( "More parts here..." ) << "</color>\n";
        lines += 1;
    }
    for( size_t i = start_at; i < pl.size(); i++ ) {
        const vehicle_part &vp = parts[ pl [ i ] ];
        std::ostringstream possible_msg;
        std::string name_color = string_format( "<color_%1$s>",
                                                string_from_color( vp.is_broken() ? c_dark_gray : c_light_green ) );
        possible_msg << name_color << vp.name() << "</color>\n";
        std::string desc_color = string_format( "<color_%1$s>",
                                                string_from_color( vp.is_broken() ? c_dark_gray : c_light_gray ) );
        int new_lines = 2 + vp.info().format_description( possible_msg, desc_color, width - 2 );
        possible_msg << "</color>\n";
        if( lines + new_lines <= max_y ) {
            msg << possible_msg.str();
            lines += new_lines;
            start_limit = start_at;
        } else {
            msg << "<color_yellow>" << _( "More parts here..." ) << "  >" << "</color>\n";
            start_limit = i;
            break;
        }
    }
    werase( win );
    fold_and_print( win, 0, 1, width, c_light_gray, msg.str() );
    wrefresh( win );
}
Пример #2
0
std::vector<std::string> requirement_data::get_folded_tools_list(int width, nc_color col, const inventory &crafting_inv, int batch) const
{
    std::vector<std::string> output_buffer;
    std::ostringstream current_line;
    current_line << "<color_" << string_from_color(col) << ">" << _( "Tools required:" ) << "</color>";
    output_buffer.push_back(current_line.str());
    current_line.str("");
    if( tools.empty() && qualities.empty() ) {
        current_line << "<color_" << string_from_color(col) << ">" << "> " << "</color>";
        current_line << "<color_" << string_from_color(c_green) << ">" << _( "NONE" ) << "</color>";
        output_buffer.push_back(current_line.str());
        return output_buffer;
    }

    std::vector<std::string> folded_qualities = get_folded_list(width, crafting_inv, qualities);
    output_buffer.insert(output_buffer.end(), folded_qualities.begin(), folded_qualities.end());

    std::vector<std::string> folded_tools = get_folded_list(width, crafting_inv, tools, batch);
    output_buffer.insert(output_buffer.end(), folded_tools.begin(), folded_tools.end());
    return output_buffer;
}
Пример #3
0
std::vector<std::string> requirement_data::get_folded_components_list( int width, nc_color col, const inventory &crafting_inv, int batch) const
{
    std::vector<std::string> out_buffer;
    if( components.empty() ) {
        return out_buffer;
    }
    std::ostringstream current_line;
    current_line << "<color_" << string_from_color(col) << ">" << _( "Components required:" ) << "</color>";
    out_buffer.push_back(current_line.str());
    current_line.str("");

    std::vector<std::string> folded_buffer = get_folded_list(width, crafting_inv, components, batch);
    out_buffer.insert(out_buffer.end(), folded_buffer.begin(), folded_buffer.end());

    return out_buffer;
}
Пример #4
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);
}
Пример #5
0
void construction_menu()
{
    static bool hide_unconstructable = false;
    // only display constructions the player can theoretically perform
    std::vector<std::string> available;
    std::map<std::string, std::vector<std::string>> cat_available;
    load_available_constructions( available, cat_available, hide_unconstructable );

    if( available.empty() ) {
        popup( _( "You can not construct anything here." ) );
        return;
    }

    int w_height = TERMY;
    if( ( int )available.size() + 2 < w_height ) {
        w_height = available.size() + 2;
    }
    if( w_height < FULL_SCREEN_HEIGHT ) {
        w_height = FULL_SCREEN_HEIGHT;
    }

    const int w_width = std::max( FULL_SCREEN_WIDTH, TERMX * 2 / 3);
    const int w_y0 = ( TERMY > w_height ) ? ( TERMY - w_height ) / 2 : 0;
    const int w_x0 = ( TERMX > w_width ) ? ( TERMX - w_width ) / 2 : 0;
    WINDOW_PTR w_con_ptr {newwin( w_height, w_width, w_y0, w_x0 )};
    WINDOW *const w_con = w_con_ptr.get();

    const int w_list_width = int( .375 * w_width );
    const int w_list_height = w_height - 4;
    const int w_list_x0 = 1;
    WINDOW_PTR w_list_ptr {newwin( w_list_height, w_list_width, w_y0 + 3, w_x0 + w_list_x0 )};
    WINDOW *const w_list = w_list_ptr.get();

    draw_grid( w_con, w_list_width + w_list_x0 );

    //tabcount needs to be increased to add more categories
    int tabcount = 10;
    std::string construct_cat[] = {_( "All" ), _( "Constructions" ), _( "Furniture" ),
                                   _( "Digging and Mining" ), _( "Repairing" ),
                                   _( "Reinforcing" ), _( "Decorative" ),
                                   _( "Farming and Woodcutting" ), _( "Others" ),
                                   _( "Filter" )
                                  };

    bool update_info = true;
    bool update_cat = true;
    bool isnew = true;
    int tabindex = 0;
    int select = 0;
    int offset = 0;
    bool exit = false;
    std::string category_name = "";
    std::vector<std::string> constructs;
    //storage for the color text so it can be scrolled
    std::vector< std::vector < std::string > > construct_buffers;
    std::vector<std::string> full_construct_buffer;
    std::vector<int> construct_buffer_breakpoints;
    int total_project_breakpoints = 0;
    int current_construct_breakpoint = 0;
    bool previous_hide_unconstructable = false;
    //track the cursor to determine when to refresh the list of construction recipes
    int previous_tabindex = -1;
    int previous_select = -1;

    const inventory &total_inv = g->u.crafting_inventory();

    input_context ctxt( "CONSTRUCTION" );
    ctxt.register_action( "UP", _( "Move cursor up" ) );
    ctxt.register_action( "DOWN", _( "Move cursor down" ) );
    ctxt.register_action( "RIGHT", _( "Move tab right" ) );
    ctxt.register_action( "LEFT", _( "Move tab left" ) );
    ctxt.register_action( "PAGE_UP" );
    ctxt.register_action( "PAGE_DOWN" );
    ctxt.register_action( "CONFIRM" );
    ctxt.register_action( "TOGGLE_UNAVAILABLE_CONSTRUCTIONS" );
    ctxt.register_action( "QUIT" );
    ctxt.register_action( "HELP_KEYBINDINGS" );
    ctxt.register_action( "FILTER" );

    std::string filter;
    int previous_index = 0;
    do {
        if( update_cat ) {
            update_cat = false;
            switch( tabindex ) {
                case 0:
                    category_name = "ALL";
                    break;
                case 1:
                    category_name = "CONSTRUCT";
                    break;
                case 2:
                    category_name = "FURN";
                    break;
                case 3:
                    category_name = "DIG";
                    break;
                case 4:
                    category_name = "REPAIR";
                    break;
                case 5:
                    category_name = "REINFORCE";
                    break;
                case 6:
                    category_name = "DECORATE";
                    break;
                case 7:
                    category_name = "FARM_WOOD";
                    break;
                case 8:
                    category_name = "OTHER";
                    break;
                case 9:
                    category_name = "FILTER";
                    break;
            }

            if( category_name == "ALL" ) {
                constructs = available;
                previous_index = tabindex;
            } else if( category_name == "FILTER" ) {
                constructs.clear();
                std::copy_if( available.begin(), available.end(),
                    std::back_inserter( constructs ),
                    [&](const std::string &a){
                        return lcmatch(a, filter);
                    } );
            } else {
                constructs = cat_available[category_name];
                previous_index = tabindex;
            }
            if( isnew ){
                if( !uistate.last_construction.empty() ){
                    select = std::distance(constructs.begin(),
                                            std::find( constructs.begin(),
                                                        constructs.end(),
                                                        uistate.last_construction ));
                }
                filter = uistate.construction_filter;
            }
        }
        // Erase existing tab selection & list of constructions
        mvwhline( w_con, 1, 1, ' ', w_list_width );
        werase( w_list );
        // Print new tab listing
        mvwprintz( w_con, 1, 1, c_yellow, "<< %s >>", construct_cat[tabindex].c_str() );
        // Determine where in the master list to start printing
        calcStartPos( offset, select, w_list_height, constructs.size() );
        // Print the constructions between offset and max (or how many will fit)
        for( size_t i = 0; ( int )i < w_list_height && ( i + offset ) < constructs.size(); i++ ) {
            int current = i + offset;
            std::string con_name = constructs[current];
            bool highlight = ( current == select );

            trim_and_print( w_list, i, 0, w_list_width,
                            construction_color( con_name, highlight ), "%s",
                            con_name.c_str() );
        }

        if( update_info ) {
            update_info = false;
            // Clear out lines for tools & materials
            const int pos_x = w_list_width + w_list_x0 + 2;
            const int available_window_width = w_width - pos_x - 1;
            for( int i = 1; i < w_height - 1; i++ ) {
                mvwhline( w_con, i, pos_x, ' ', available_window_width );
            }

            nc_color color_stage = c_white;
            std::vector<std::string> notes;
            notes.push_back( string_format( _( "Press %s or %s to tab." ),
                             ctxt.get_desc( "LEFT" ).c_str(), ctxt.get_desc( "RIGHT" ).c_str() ) );
            notes.push_back( string_format( _( "Press %s to search." ),
                             ctxt.get_desc( "FILTER" ).c_str() ) );
            notes.push_back( string_format( _( "Press %s to toggle unavailable constructions." ),
                            ctxt.get_desc( "TOGGLE_UNAVAILABLE_CONSTRUCTIONS" ).c_str() ) );
            notes.push_back( string_format( _( "Press %s to view and edit key-bindings." ),
                            ctxt.get_desc( "HELP_KEYBINDINGS" ).c_str() ) );

            //leave room for top and bottom UI text
            const int available_buffer_height = w_height - 3 - 3 - (int)notes.size();

            // print the hotkeys regardless of if there are constructions
            for( size_t i = 0; i < notes.size(); ++i ) {
                trim_and_print( w_con, w_height - 1 - (int)notes.size() + (int)i, pos_x,
                                available_window_width, c_white, "%s", notes[i].c_str() );
            }

            if( !constructs.empty() ) {
                if( select >= (int) constructs.size() ){
                    select = 0;
                }
                std::string current_desc = constructs[select];
                // Print construction name
                trim_and_print( w_con, 1, pos_x, available_window_width, c_white,
                                "%s", current_desc.c_str() );

                //only reconstruct the project list when moving away from the current item, or when changing the display mode
                if( previous_select != select || previous_tabindex != tabindex ||
                    previous_hide_unconstructable != hide_unconstructable ) {
                    previous_select = select;
                    previous_tabindex = tabindex;
                    previous_hide_unconstructable = hide_unconstructable;

                    //construct the project list buffer

                    // Print stages and their requirement.
                    std::vector<construction *> options = constructions_by_desc( current_desc );

                    construct_buffers.clear();
                    total_project_breakpoints = 0;
                    current_construct_breakpoint = 0;
                    construct_buffer_breakpoints.clear();
                    full_construct_buffer.clear();
                    int stage_counter = 0;
                    for( std::vector<construction *>::iterator it = options.begin();
                         it != options.end(); ++it ) {
                        stage_counter++;
                        construction *current_con = *it;
                        if( hide_unconstructable && !can_construct( *current_con ) ) {
                            continue;
                        }
                        // Update the cached availability of components and tools in the requirement object
                        current_con->requirements->can_make_with_inventory( total_inv );

                        std::vector<std::string> current_buffer;
                        std::ostringstream current_line;

                        // display result only if more than one step.
                        // Assume single stage constructions should be clear
                        // in their description what their result is.
                        if( current_con->post_terrain != "" && options.size() > 1 ) {
                            //also print out stage number when multiple stages are available
                            current_line << _( "Stage #" ) << stage_counter;
                            current_buffer.push_back( current_line.str() );
                            current_line.str( "" );

                            std::string result_string;
                            if( current_con->post_is_furniture ) {
                                result_string = furn_str_id( current_con->post_terrain ).obj().name;
                            } else {
                                result_string = ter_str_id( current_con->post_terrain ).obj().name;
                            }
                            current_line << "<color_" << string_from_color( color_stage ) << ">" << string_format(
                                             _( "Result: %s" ), result_string.c_str() ) << "</color>";
                            std::vector<std::string> folded_result_string = foldstring( current_line.str(),
                                    available_window_width );
                            current_buffer.insert( current_buffer.end(), folded_result_string.begin(),
                                                   folded_result_string.end() );
                        }

                        current_line.str( "" );
                        // display required skill and difficulty
                        int pskill = g->u.get_skill_level( current_con->skill );
                        int diff = ( current_con->difficulty > 0 ) ? current_con->difficulty : 0;

                        current_line << "<color_" << string_from_color( ( pskill >= diff ? c_white : c_red ) ) << ">" <<
                                     string_format( _( "Skill Req: %d (%s)" ), diff,
                                                    current_con->skill.obj().name().c_str() ) << "</color>";
                        current_buffer.push_back( current_line.str() );
                        // TODO: Textify pre_flags to provide a bit more information.
                        // Example: First step of dig pit could say something about
                        // requiring diggable ground.
                        current_line.str( "" );
                        if( current_con->pre_terrain != "" ) {
                            std::string require_string;
                            if( current_con->pre_is_furniture ) {
                                require_string = furn_str_id( current_con->pre_terrain ).obj().name;
                            } else {
                                require_string = ter_str_id( current_con->pre_terrain ).obj().name;
                            }
                            current_line << "<color_" << string_from_color( color_stage ) << ">" << string_format(
                                             _( "Requires: %s" ), require_string.c_str() ) << "</color>";
                            std::vector<std::string> folded_result_string = foldstring( current_line.str(),
                                    available_window_width );
                            current_buffer.insert( current_buffer.end(), folded_result_string.begin(),
                                                   folded_result_string.end() );
                        }
                        // get pre-folded versions of the rest of the construction project to be displayed later

                        // get time needed
                        std::vector<std::string> folded_time = current_con->get_folded_time_string(
                                available_window_width );
                        current_buffer.insert( current_buffer.end(), folded_time.begin(), folded_time.end() );

                        std::vector<std::string> folded_tools = current_con->requirements->get_folded_tools_list(
                                available_window_width, color_stage, total_inv );
                        current_buffer.insert( current_buffer.end(), folded_tools.begin(), folded_tools.end() );

                        std::vector<std::string> folded_components = current_con->requirements->get_folded_components_list(
                                    available_window_width, color_stage, total_inv );
                        current_buffer.insert( current_buffer.end(), folded_components.begin(), folded_components.end() );

                        construct_buffers.push_back( current_buffer );
                    }

                    //determine where the printing starts for each project, so it can be scrolled to those points
                    size_t current_buffer_location = 0;
                    for( size_t i = 0; i < construct_buffers.size(); i++ ) {
                        construct_buffer_breakpoints.push_back( static_cast<int>( current_buffer_location ) );
                        full_construct_buffer.insert( full_construct_buffer.end(), construct_buffers[i].begin(),
                                                      construct_buffers[i].end() );

                        //handle text too large for one screen
                        if( construct_buffers[i].size() > static_cast<size_t>( available_buffer_height ) ) {
                            construct_buffer_breakpoints.push_back( static_cast<int>( current_buffer_location +
                                                                    static_cast<size_t>( available_buffer_height ) ) );
                        }
                        current_buffer_location += construct_buffers[i].size();
                        if( i < construct_buffers.size() - 1 ) {
                            full_construct_buffer.push_back( std::string( "" ) );
                            current_buffer_location++;
                        }
                    }
                    total_project_breakpoints = static_cast<int>( construct_buffer_breakpoints.size() );
                }
                if( current_construct_breakpoint > 0 ) {
                    // Print previous stage indicator if breakpoint is past the beginning
                    trim_and_print( w_con, 2, pos_x, available_window_width, c_white,
                                    _( "Press %s to show previous stage(s)." ),
                                    ctxt.get_desc( "PAGE_UP" ).c_str() );
                }
                if( static_cast<size_t>( construct_buffer_breakpoints[current_construct_breakpoint] +
                                         available_buffer_height ) < full_construct_buffer.size() ) {
                    // Print next stage indicator if more breakpoints are remaining after screen height
                    trim_and_print( w_con, w_height - 2 - (int)notes.size(), pos_x, available_window_width,
                                    c_white, _( "Press %s to show next stage(s)." ),
                                    ctxt.get_desc( "PAGE_DOWN" ).c_str() );
                }
                // Leave room for above/below indicators
                int ypos = 3;
                nc_color stored_color = color_stage;
                for( size_t i = static_cast<size_t>( construct_buffer_breakpoints[current_construct_breakpoint] );
                     i < full_construct_buffer.size(); i++ ) {
                    //the value of 3 is from leaving room at the top of window
                    if( ypos > available_buffer_height + 3 ) {
                        break;
                    }
                    print_colored_text( w_con, ypos++, ( w_list_width + w_list_x0 + 2 ), stored_color, color_stage, full_construct_buffer[i] );
                }
            }
        } // Finished updating

        draw_scrollbar( w_con, select, w_list_height, constructs.size(), 3 );
        wrefresh( w_con );
        wrefresh( w_list );

        const std::string action = ctxt.handle_input();
        if( action == "FILTER" ){
            filter = string_input_popup( _( "Search" ), 50, filter, "", _( "Filter" ), 100, false );
            if( !filter.empty() ){
                update_info = true;
                update_cat = true;
                tabindex = 9;
                select = 0;
            }else if( previous_index !=9 ){
                tabindex = previous_index;
                update_info = true;
                update_cat = true;
                select = 0;
            }
            uistate.construction_filter = filter;
        } else if( action == "DOWN" ) {
            update_info = true;
            if( select < ( int )constructs.size() - 1 ) {
                select++;
            } else {
                select = 0;
            }
        } else if( action == "UP" ) {
            update_info = true;
            if( select > 0 ) {
                select--;
            } else {
                select = constructs.size() - 1;
            }
        } else if( action == "LEFT" ) {
            update_info = true;
            update_cat = true;
            select = 0;
            tabindex--;
            if( tabindex < 0 ) {
                tabindex = tabcount - 1;
            }
        } else if( action == "RIGHT" ) {
            update_info = true;
            update_cat = true;
            select = 0;
            tabindex = ( tabindex + 1 ) % tabcount;
        } else if( action == "PAGE_UP" ) {
            update_info = true;
            if( current_construct_breakpoint > 0 ) {
                current_construct_breakpoint--;
            }
            if( current_construct_breakpoint < 0 ) {
                current_construct_breakpoint = 0;
            }
        } else if( action == "PAGE_DOWN" ) {
            update_info = true;
            if( current_construct_breakpoint < total_project_breakpoints - 1 ) {
                current_construct_breakpoint++;
            }
            if( current_construct_breakpoint >= total_project_breakpoints ) {
                current_construct_breakpoint = total_project_breakpoints - 1;
            }
        } else if( action == "QUIT" ) {
            exit = true;
        } else if( action == "HELP_KEYBINDINGS" ) {
            draw_grid( w_con, w_list_width + w_list_x0 );
        } else if( action == "TOGGLE_UNAVAILABLE_CONSTRUCTIONS" ) {
            update_info = true;
            update_cat = true;
            hide_unconstructable = !hide_unconstructable;
            select = 0;
            offset = 0;
            load_available_constructions( available, cat_available, hide_unconstructable );
        } else if( action == "CONFIRM" ) {
            if( constructs.empty() || select >= (int) constructs.size() ){
                continue;// Nothing to be done here
            }
            if( player_can_build( g->u, total_inv, constructs[select] ) ) {
                place_construction( constructs[select] );
                uistate.last_construction = constructs[select];
                exit = true;
            } else {
                popup( _( "You can't build that!" ) );
                draw_grid( w_con, w_list_width + w_list_x0 );
                update_info = true;
            }
        }
    } while( !exit );

    w_list_ptr.reset();
    w_con_ptr.reset();
    g->refresh_all();
}