/** * Find the minimum width between max( min_width, 1 ) and * max( max_width, min_width, 1 ) to fold the string to no more than max_lines, * or no more than the minimum number of lines possible, assuming that * foldstring( width ).size() decreases monotonously with width. **/ static int find_minimum_fold_width( const std::string &str, int max_lines, int min_width, int max_width ) { if( str.empty() ) { return std::max( min_width, 1 ); } min_width = std::max( min_width, 1 ); // max_width could be further limited by the string width, but utf8_width is // not handling linebreaks properly. if( min_width < max_width ) { // If with max_width the string still folds to more than max_lines, find the // minimum width that folds the string to such number of lines instead. max_lines = std::max<int>( max_lines, foldstring( str, max_width ).size() ); while( min_width < max_width ) { int width = ( min_width + max_width ) / 2; // width may equal min_width, but will always be less than max_width. int lines = foldstring( str, width ).size(); // If the current width folds the string to no more than max_lines if( lines <= max_lines ) { // The minimum width is between min_width and width. max_width = width; } else { // The minimum width is between width + 1 and max_width. min_width = width + 1; } // The new interval will always be smaller than the previous one, // so the loop is guaranteed to end. } } return min_width; };
// Find the best width to fold the string to no more than max_lines, // Assuming that foldstring( width ).size() decreases monotonously w.r.t. width. static int find_best_fold_width( const std::string &str, const int max_lines, const int max_width_avail ) { if( str.empty() ) { return 0; } // min_width inclusive, max_width exclusive. int min_width = 1; // +1 for exclusion. int max_width = std::min( utf8_width( str ), max_width_avail ) + 1; while( min_width + 1 < max_width ) { int width = ( min_width + max_width ) / 2; int lines = foldstring( str, width ).size(); if( lines == max_lines ) { return width; } else if( lines < max_lines ) { // decrease width for more lines max_width = width; } else { // increase width for less lines min_width = width + 1; } } return min_width; };
void Messages::display_messages(WINDOW *const ipk_target, int const left, int const top, int const right, int const bottom) { if (!size()) { return; } int const maxlength = right - left; int line = bottom; for (int i = size() - 1; i >= 0; --i) { if (line < top) { break; } const game_message &m = player_messages.impl_->messages[i]; const nc_color col = m.get_color(player_messages.impl_->curmes); const auto folded_strings = foldstring(m.get_with_count(), maxlength); const auto folded_rend = folded_strings.rend(); for( auto string_iter = folded_strings.rbegin(); string_iter != folded_rend && line >= top; ++string_iter, line-- ) { mvwprintz(ipk_target, line, left, col, "%s", string_iter->c_str()); } } player_messages.impl_->curmes = calendar::turn.get_turn(); }
std::vector<std::string> requirement_data::get_folded_list( int width, const inventory &crafting_inv, const std::vector< std::vector<T> > &objs, int batch ) const { // hack: ensure 'cached' availability is up to date can_make_with_inventory( crafting_inv ); std::vector<std::string> out_buffer; for( const auto &comp_list : objs ) { const bool has_one = any_marked_available( comp_list ); std::ostringstream buffer; for( auto a = comp_list.begin(); a != comp_list.end(); ++a ) { if( a != comp_list.begin() ) { buffer << "<color_white> " << _( "OR" ) << "</color> "; } const std::string col = a->get_color( has_one, crafting_inv, batch ); buffer << "<color_" << col << ">" << a->to_string( batch ) << "</color>"; } std::vector<std::string> folded = foldstring( buffer.str(), width - 2 ); for( size_t i = 0; i < folded.size(); i++ ) { if( i == 0 ) { out_buffer.push_back( std::string( "> " ).append( folded[i] ) ); } else { out_buffer.push_back( std::string( " " ).append( folded[i] ) ); } } } return out_buffer; }
void popup_nowait(const char *mes, ...) { va_list ap; va_start(ap, mes); char buff[4096]; vsprintf(buff, mes, ap); va_end(ap); std::string tmp = buff; int width = 0; int height = 2; std::vector<std::string> folded = foldstring(tmp, 99999); height += folded.size(); for(int i=0; i<folded.size(); i++) { int cw = utf8_width(folded[i].c_str()); if(cw>width) { width = cw; } } width += 2; if (height > FULL_SCREEN_HEIGHT) height = FULL_SCREEN_HEIGHT; WINDOW *w = newwin(height, width, (TERMY-(height+1))/2, (TERMX > width) ? (TERMX-width)/2 : 0); wborder(w, LINE_XOXO, LINE_XOXO, LINE_OXOX, LINE_OXOX, LINE_OXXO, LINE_OOXX, LINE_XXOO, LINE_XOOX ); for(int i=0; i<folded.size(); i++) { mvwprintz(w, i+1, 1, c_white, folded[i].c_str()); } wrefresh(w); delwin(w); refresh(); }
int fold_and_print_from(WINDOW *w, int begin_y, int begin_x, int width, int begin_line, nc_color base_color, const std::string &text) { nc_color color = base_color; std::vector<std::string> textformatted; textformatted = foldstring(text, width); for (int line_num = 0; line_num < textformatted.size(); line_num++) { if (line_num >= begin_line) { wmove(w, line_num + begin_y - begin_line, begin_x); } // split into colourable sections std::vector<std::string> color_segments = split_by_color(textformatted[line_num]); // for each section, get the colour, and print it std::vector<std::string>::iterator it; for (it = color_segments.begin(); it != color_segments.end(); ++it) { if (!it->empty() && it->at(0) == '<') { color = get_color_from_tag(*it, base_color); } if (line_num >= begin_line) { std::string l = rm_prefix(*it); if(l != "--") { // -- is a newline! wprintz(w, color, "%s", rm_prefix(*it).c_str()); } } } } return textformatted.size(); };
// returns number of printed lines int fold_and_print(WINDOW* w, int begin_y, int begin_x, int width, nc_color base_color, const char *mes, ...) { va_list ap; va_start(ap,mes); char buff[6000]; //TODO replace Magic Number vsprintf(buff, mes, ap); va_end(ap); nc_color color = base_color; std::vector<std::string> textformatted; textformatted = foldstring(buff, width); for (int line_num=0; line_num<textformatted.size(); line_num++) { wmove(w, line_num+begin_y, begin_x); // split into colourable sections std::vector<std::string> color_segments = split_by_color(textformatted[line_num]); // for each section, get the colour, and print it std::vector<std::string>::iterator it; for (it = color_segments.begin(); it != color_segments.end(); ++it) { if (!it->empty() && it->at(0) == '<') { color = get_color_from_tag(*it, base_color); } wprintz(w, color, "%s", rm_prefix(*it).c_str()); } } return textformatted.size(); };
std::vector<std::string> Messages::dialog::filter_help_text( int width ) { const auto &help_fmt = _( "Format is [[TYPE]:]TEXT. The values for TYPE are: %s\n" "Examples:\n" " good:mutation\n" " :you pick up: 1\n" " crash!\n" ); std::stringstream type_text; const auto &type_list = msg_type_and_names(); for( auto it = type_list.begin(); it != type_list.end(); ++it ) { // Skip m_debug outside debug mode (but allow searching for it) if( debug_mode || it->first != m_debug ) { const auto &col_name = get_all_colors().get_name( msgtype_to_color( it->first ) ); auto next_it = std::next( it ); // Skip m_debug outside debug mode if( !debug_mode && next_it != type_list.end() && next_it->first == m_debug ) { next_it = std::next( next_it ); } if( next_it != type_list.end() ) { //~ the 2nd %s is a type name, this is used to format a list of type names type_text << string_format( pgettext( "message log", "<color_%s>%s</color>, " ), col_name, pgettext( "message type", it->second ) ); } else { //~ the 2nd %s is a type name, this is used to format the last type name in a list of type names type_text << string_format( pgettext( "message log", "<color_%s>%s</color>." ), col_name, pgettext( "message type", it->second ) ); } } } return foldstring( string_format( help_fmt, type_text.str() ), width ); }
virtual void select(int entnum, uimenu *menu) { const int starty = 3; const int startx = menu->w_width - menu->pad_right; itype *ity = item_controller->find_template(standard_itype_ids[entnum]); std::string padding = std::string(menu->pad_right - 1, ' '); for(int i = 0; i < lastlen + starty + 1; i++ ) { mvwprintw(menu->window, 1 + i, startx, "%s", padding.c_str() ); } if ( ity != NULL ) { tmp.make(item_controller->find_template(standard_itype_ids[entnum])); tmp.bday = g->turn; if (tmp.is_tool()) { tmp.charges = dynamic_cast<it_tool *>(tmp.type)->max_charges; } else if (tmp.is_ammo()) { tmp.charges = 100; } else if (tmp.is_gun()) { tmp.charges = 0; } else if (tmp.is_gunmod() && (tmp.has_flag("MODE_AUX") || tmp.typeId() == "spare_mag")) { tmp.charges = 0; } else { tmp.charges = -1; } if( tmp.is_stationary() ) { tmp.note = SNIPPET.assign( (dynamic_cast<it_stationary *>(tmp.type))->category ); } std::vector<std::string> desc = foldstring(tmp.info(true), menu->pad_right - 1); int dsize = desc.size(); if ( dsize > menu->w_height - 5 ) { dsize = menu->w_height - 5; } lastlen = dsize; std::string header = string_format("#%d: %s%s", entnum, standard_itype_ids[entnum].c_str(), ( incontainer ? " (contained)" : "" ) ); mvwprintz(menu->window, 1, startx + ( menu->pad_right - 1 - header.size() ) / 2, c_cyan, "%s", header.c_str() ); for(int i = 0; i < desc.size(); i++ ) { mvwprintw(menu->window, starty + i, startx, "%s", desc[i].c_str() ); } mvwprintz(menu->window, menu->w_height - 3, startx, c_green, "%s", msg.c_str()); msg = padding; mvwprintw(menu->window, menu->w_height - 2, startx, "[/] find, [f] container, [q]uit"); } }
std::string string_input_popup(std::string title, int width, std::string input, std::string desc, std::string identifier, int max_length ) { nc_color title_color = c_ltred; nc_color desc_color = c_green; std::vector<std::string> descformatted; int titlesize = utf8_width(title.c_str()); int startx = titlesize + 2; if ( max_length == 0 ) { max_length = width; } int w_height = 3; int iPopupWidth = (width == 0) ? FULL_SCREEN_WIDTH : width + titlesize + 4; if (iPopupWidth > FULL_SCREEN_WIDTH) { iPopupWidth = FULL_SCREEN_WIDTH; } if ( desc.size() > 0 ) { int twidth = utf8_width(desc.c_str()); if ( twidth > iPopupWidth - 4 ) { twidth = iPopupWidth - 4; } descformatted = foldstring(desc, twidth); w_height += descformatted.size(); } int starty = 1 + descformatted.size(); if ( max_length == 0 ) { max_length = 1024; } int w_y = (TERMY - w_height) / 2; int w_x = ((TERMX > iPopupWidth) ? (TERMX - iPopupWidth) / 2 : 0); WINDOW *w = newwin(w_height, iPopupWidth, w_y, ((TERMX > iPopupWidth) ? (TERMX - iPopupWidth) / 2 : 0)); wborder(w, LINE_XOXO, LINE_XOXO, LINE_OXOX, LINE_OXOX, LINE_OXXO, LINE_OOXX, LINE_XXOO, LINE_XOOX ); int endx = iPopupWidth - 3; for(int i = 0; i < descformatted.size(); i++ ) { mvwprintz(w, 1 + i, 1, desc_color, "%s", descformatted[i].c_str() ); } mvwprintz(w, starty, 1, title_color, "%s", title.c_str() ); long key = 0; int pos = -1; std::string ret = string_input_win(w, input, max_length, startx, starty, endx, true, key, pos, identifier, w_x, w_y, true ); werase(w); wrefresh(w); delwin(w); refresh(); return ret; }
int monster::print_info(WINDOW* w, int vStart, int vLines, int column) { // First line of w is the border; the next two are terrain info, and after that // is a blank line. w is 13 characters tall, and we can't use the last one // because it's a border as well; so we have lines 4 through 11. // w is also 48 characters wide - 2 characters for border = 46 characters for us // vStart added because 'help' text in targeting win makes helpful info hard to find // at a glance. const int vEnd = vStart + vLines; mvwprintz(w, vStart++, column, c_white, "%s ", name().c_str()); nc_color color = c_white; std::string attitude = ""; get_Attitude(color, attitude); wprintz(w, color, "%s", attitude.c_str()); if (has_effect("downed")) wprintz(w, h_white, _("On ground")); else if (has_effect("stunned")) wprintz(w, h_white, _("Stunned")); else if (has_effect("beartrap")) wprintz(w, h_white, _("Trapped")); std::string damage_info; nc_color col; if (hp >= type->hp) { damage_info = _("It is uninjured"); col = c_green; } else if (hp >= type->hp * .8) { damage_info = _("It is lightly injured"); col = c_ltgreen; } else if (hp >= type->hp * .6) { damage_info = _("It is moderately injured"); col = c_yellow; } else if (hp >= type->hp * .3) { damage_info = _("It is heavily injured"); col = c_yellow; } else if (hp >= type->hp * .1) { damage_info = _("It is severely injured"); col = c_ltred; } else { damage_info = _("it is nearly dead"); col = c_red; } mvwprintz(w, vStart++, column, col, "%s", damage_info.c_str()); std::vector<std::string> lines = foldstring(type->description, getmaxx(w) - 1 - column); int numlines = lines.size(); for (int i = 0; i < numlines && vStart <= vEnd; i++) mvwprintz(w, vStart++, column, c_white, "%s", lines[i].c_str()); return vStart; }
long popup(const std::string &text, PopupFlags flags) { int width = 0; int height = 2; std::vector<std::string> folded = foldstring(text, FULL_SCREEN_WIDTH - 2); height += folded.size(); for( size_t i = 0; i < folded.size(); ++i ) { int cw = utf8_width(folded[i].c_str()); if(cw > width) { width = cw; } } width += 2; WINDOW *w; if ((flags & PF_FULLSCREEN) != 0) { w = newwin(FULL_SCREEN_HEIGHT, FULL_SCREEN_WIDTH, (TERMY > FULL_SCREEN_HEIGHT) ? (TERMY - FULL_SCREEN_HEIGHT) / 2 : 0, (TERMX > FULL_SCREEN_WIDTH) ? (TERMX - FULL_SCREEN_WIDTH) / 2 : 0); } else if ((flags & PF_ON_TOP) == 0) { if (height > FULL_SCREEN_HEIGHT) { height = FULL_SCREEN_HEIGHT; } w = newwin(height, width, (TERMY - (height + 1)) / 2, (TERMX > width) ? (TERMX - width) / 2 : 0); } else { w = newwin(height, width, 0, (TERMX > width) ? (TERMX - width) / 2 : 0); } draw_border(w); for( size_t i = 0; i < folded.size(); ++i ) { mvwprintz(w, i + 1, 1, c_white, "%s", folded[i].c_str()); } long ch = 0; // Don't wait if not required. while((flags & PF_NO_WAIT) == 0) { wrefresh(w); ch = getch(); if ((flags & PF_GET_KEY) != 0) { // return the first key that got pressed. werase(w); break; } if (ch == ' ' || ch == '\n' || ch == KEY_ESCAPE) { // The usuall "escape menu/window" keys. werase(w); break; } } wrefresh(w); delwin(w); refresh(); return ch; }
void lua_console::read_stream( std::stringstream &stream, nc_color text_color ) { std::string line; while( std::getline( stream, line ) ) { for( auto str : foldstring( line, width ) ) { text_stack.push_back( {str, text_color} ); } } stream.str( std::string() ); // empty the buffer stream.clear(); }
int monster::print_info(WINDOW* w, int vStart, int vLines, int column) const { const int vEnd = vStart + vLines; mvwprintz(w, vStart++, column, c_white, "%s ", name().c_str()); nc_color color = c_white; std::string attitude = ""; get_Attitude(color, attitude); wprintz(w, color, "%s", attitude.c_str()); if (has_effect("downed")) { wprintz(w, h_white, _("On ground")); } else if (has_effect("stunned")) { wprintz(w, h_white, _("Stunned")); } else if (has_effect("beartrap")) { wprintz(w, h_white, _("Trapped")); } else if (has_effect("tied")) { wprintz(w, h_white, _("Tied")); } std::string damage_info; nc_color col; if (hp >= type->hp) { damage_info = _("It is uninjured"); col = c_green; } else if (hp >= type->hp * .8) { damage_info = _("It is lightly injured"); col = c_ltgreen; } else if (hp >= type->hp * .6) { damage_info = _("It is moderately injured"); col = c_yellow; } else if (hp >= type->hp * .3) { damage_info = _("It is heavily injured"); col = c_yellow; } else if (hp >= type->hp * .1) { damage_info = _("It is severely injured"); col = c_ltred; } else { damage_info = _("it is nearly dead"); col = c_red; } mvwprintz(w, vStart++, column, col, "%s", damage_info.c_str()); std::vector<std::string> lines = foldstring(type->description, getmaxx(w) - 1 - column); int numlines = lines.size(); for (int i = 0; i < numlines && vStart <= vEnd; i++) { mvwprintz(w, vStart++, column, c_white, "%s", lines[i].c_str()); } return vStart; }
std::vector<std::string> requirement_data::get_folded_list( int width, const inventory &crafting_inv, const std::vector< std::vector<T> > &objs, int batch, std::string hilite ) const { // hack: ensure 'cached' availability is up to date can_make_with_inventory( crafting_inv ); std::vector<std::string> out_buffer; for( const auto &comp_list : objs ) { const bool has_one = any_marked_available( comp_list ); std::ostringstream buffer; std::vector<std::string> buffer_has; bool already_has; for( auto a = comp_list.begin(); a != comp_list.end(); ++a ) { already_has = false; for( auto cont : buffer_has ) { if( cont == a->to_string( batch ) + a->get_color( has_one, crafting_inv, batch ) ) { already_has = true; break; } } if( already_has ) { continue; } if( a != comp_list.begin() ) { buffer << "<color_white> " << _( "OR" ) << "</color> "; } const std::string col = a->get_color( has_one, crafting_inv, batch ); if( !hilite.empty() && lcmatch( a->to_string( batch ), hilite ) ) { buffer << get_tag_from_color( yellow_background( color_from_string( col ) ) ); } else { buffer << "<color_" << col << ">"; } buffer << a->to_string( batch ) << "</color>" << "</color>"; buffer_has.push_back( a->to_string( batch ) + a->get_color( has_one, crafting_inv, batch ) ); } std::vector<std::string> folded = foldstring( buffer.str(), width - 2 ); for( size_t i = 0; i < folded.size(); i++ ) { if( i == 0 ) { out_buffer.push_back( std::string( "> " ).append( folded[i] ) ); } else { out_buffer.push_back( std::string( " " ).append( folded[i] ) ); } } } return out_buffer; }
// returns number of printed lines int fold_and_print(WINDOW* w, int begin_y, int begin_x, int width, nc_color color, const char *mes, ...) { va_list ap; va_start(ap,mes); char buff[6000]; //TODO replace Magic Number vsprintf(buff, mes, ap); va_end(ap); std::vector<std::string> textformatted; textformatted = foldstring(buff, width); for (int line_num=0; line_num<textformatted.size(); line_num++) { mvwprintz(w, line_num+begin_y, begin_x, color, "%s", textformatted[line_num].c_str()); } return textformatted.size(); };
void Messages::display_messages(WINDOW *ipk_target, int left, int top, int right, int bottom) { if (!size()) { return; } const int maxlength = right - left; int line = bottom; for (int i = size() - 1; i >= 0 && line >= top; i--) { const game_message &m = player_messages.messages[i]; const std::string mstr = m.get_with_count(); const nc_color col = m.get_color(); std::vector<std::string> folded = foldstring(mstr, maxlength); for (int j = folded.size() - 1; j >= 0 && line >= top; j--, line--) { mvwprintz(ipk_target, line, left, col, "%s", folded[j].c_str()); } } player_messages.curmes = int(calendar::turn); }
bool query_yn(const char *mes, ...) { va_list ap; va_start(ap, mes); char buff[1024]; vsprintf(buff, mes, ap); va_end(ap); bool force_uc = OPTIONS["FORCE_CAPITAL_YN"]; std::string query; if (force_uc) { query = string_format(_("%s (Y/N - Case Sensitive)"), buff); } else { query = string_format(_("%s (y/n)"), buff); } int win_width = utf8_width(query.c_str()) + 2; win_width = (win_width < FULL_SCREEN_WIDTH - 2 ? win_width : FULL_SCREEN_WIDTH - 2); std::vector<std::string> textformatted; textformatted = foldstring(query, win_width); WINDOW *w = newwin(textformatted.size() + 2, win_width, (TERMY - 3) / 2, (TERMX > win_width) ? (TERMX - win_width) / 2 : 0); fold_and_print(w, 1, 1, win_width, c_ltred, query.c_str()); draw_border(w); wrefresh(w); char ch; do { ch = getch(); } while (ch != '\n' && ch != ' ' && ch != KEY_ESCAPE && ch != 'Y' && ch != 'N' && (force_uc || (ch != 'y' && ch != 'n'))); werase(w); wrefresh(w); delwin(w); refresh(); if (ch == 'Y' || ch == 'y') { return true; } return false; }
void popup(const char *mes, ...) { va_list ap; va_start(ap, mes); char buff[4096]; vsprintf(buff, mes, ap); va_end(ap); std::string tmp = buff; int width = 0; int height = 2; std::vector<std::string> folded = foldstring(tmp, FULL_SCREEN_WIDTH - 2); height += folded.size(); for(int i = 0; i < folded.size(); i++) { int cw = utf8_width(folded[i].c_str()); if(cw > width) { width = cw; } } width += 2; if (height > FULL_SCREEN_HEIGHT) { height = FULL_SCREEN_HEIGHT; } WINDOW *w = newwin(height, width, (TERMY - (height + 1)) / 2, (TERMX > width) ? (TERMX - width) / 2 : 0); draw_border(w); for(int i = 0; i < folded.size(); i++) { mvwprintz(w, i + 1, 1, c_white, folded[i].c_str()); } wrefresh(w); char ch; do { ch = getch(); } while(ch != ' ' && ch != '\n' && ch != KEY_ESCAPE); werase(w); wrefresh(w); delwin(w); refresh(); }
void full_screen_popup(const char* mes, ...) { va_list ap; va_start(ap, mes); char buff[8192]; vsprintf(buff, mes, ap); va_end(ap); std::string tmp = buff; int width = 0; int height = 2; std::vector<std::string> folded = foldstring(tmp, FULL_SCREEN_WIDTH-3); height += folded.size(); for(int i=0; i<folded.size(); i++) { int cw = utf8_width(folded[i].c_str()); if(cw>width) { width = cw; } } width += 2; WINDOW *w = newwin(FULL_SCREEN_HEIGHT, FULL_SCREEN_WIDTH, (TERMY > FULL_SCREEN_HEIGHT) ? (TERMY-FULL_SCREEN_HEIGHT)/2 : 0, (TERMX > FULL_SCREEN_WIDTH) ? (TERMX-FULL_SCREEN_WIDTH)/2 : 0); wborder(w, LINE_XOXO, LINE_XOXO, LINE_OXOX, LINE_OXOX, LINE_OXXO, LINE_OOXX, LINE_XXOO, LINE_XOOX ); for(int i=0; i<folded.size(); i++) { mvwprintz(w, i+1, 2, c_white, folded[i].c_str()); } wrefresh(w); char ch; do ch = getch(); while(ch != ' ' && ch != '\n' && ch != KEY_ESCAPE); werase(w); wrefresh(w); delwin(w); refresh(); }
void popup_top(const char *mes, ...) { va_list ap; va_start(ap, mes); char buff[4096]; vsprintf(buff, mes, ap); va_end(ap); std::string tmp = buff; int width = 0; int height = 2; std::vector<std::string> folded = foldstring(tmp, 99999); height += folded.size(); for(int i=0; i<folded.size(); i++) { int cw = utf8_width(folded[i].c_str()); if(cw>width) { width = cw; } } width += 2; WINDOW *w = newwin(height, width, 0, (TERMX > width) ? (TERMX-width)/2 : 0); wborder(w, LINE_XOXO, LINE_XOXO, LINE_OXOX, LINE_OXOX, LINE_OXXO, LINE_OOXX, LINE_XXOO, LINE_XOOX ); for(int i=0; i<folded.size(); i++) { mvwprintz(w, i+1, 1, c_white, folded[i].c_str()); } wrefresh(w); char ch; do ch = getch(); while(ch != ' ' && ch != '\n' && ch != KEY_ESCAPE); werase(w); wrefresh(w); delwin(w); refresh(); }
void multipage(WINDOW *w, std::vector<std::string> text, std::string caption, int begin_y) { int height = getmaxy(w); int width = getmaxx(w); //Do not erase the current screen if it's not first line of the text if (begin_y == 0) { werase(w); } /* TODO: issue: # of lines in the paragraph > height -> inf. loop; solution: split this paragraph in two pieces; */ for (size_t i = 0; i < text.size(); i++) { if (begin_y == 0 && caption != "") { begin_y = fold_and_print(w, 0, 1, width - 2, c_white, caption) + 1; } std::vector<std::string> next_paragraph = foldstring(text[i].c_str(), width - 2); if (begin_y + next_paragraph.size() > height - ((i + 1) < text.size() ? 1 : 0)) { // Next page i--; mvwprintw(w, height - 1, 1, _("Press any key for more...")); wrefresh(w); refresh(); getch(); werase(w); begin_y = 0; } else { begin_y += fold_and_print(w, begin_y, 1, width - 2, c_white, text[i]) + 1; } } wrefresh(w); refresh(); getch(); }
const recipe *select_crafting_recipe( int &batch_size ) { if( normalized_names.empty() ) { translate_all(); } const int headHeight = 3; const int subHeadHeight = 2; const int freeWidth = TERMX - FULL_SCREEN_WIDTH; bool isWide = ( TERMX > FULL_SCREEN_WIDTH && freeWidth > 15 ); const int width = isWide ? ( freeWidth > FULL_SCREEN_WIDTH ? FULL_SCREEN_WIDTH * 2 : TERMX ) : FULL_SCREEN_WIDTH; const int wStart = ( TERMX - width ) / 2; const int tailHeight = isWide ? 3 : 4; const int dataLines = TERMY - ( headHeight + subHeadHeight ) - tailHeight; const int dataHalfLines = dataLines / 2; const int dataHeight = TERMY - ( headHeight + subHeadHeight ); const int infoWidth = width - FULL_SCREEN_WIDTH - 1; const recipe *last_recipe = nullptr; catacurses::window w_head = catacurses::newwin( headHeight, width, 0, wStart ); catacurses::window w_subhead = catacurses::newwin( subHeadHeight, width, 3, wStart ); catacurses::window w_data = catacurses::newwin( dataHeight, width, headHeight + subHeadHeight, wStart ); int item_info_x = infoWidth; int item_info_y = dataHeight - 3; int item_info_width = wStart + width - infoWidth; int item_info_height = headHeight + subHeadHeight; if( !isWide ) { item_info_x = 1; item_info_y = 1; item_info_width = 1; item_info_height = 1; } catacurses::window w_iteminfo = catacurses::newwin( item_info_y, item_info_x, item_info_height, item_info_width ); list_circularizer<std::string> tab( craft_cat_list ); list_circularizer<std::string> subtab( craft_subcat_list[tab.cur()] ); std::vector<const recipe *> current; std::vector<bool> available; const int componentPrintHeight = dataHeight - tailHeight - 1; //preserves component color printout between mode rotations nc_color rotated_color = c_white; int previous_item_line = -1; std::string previous_tab; std::string previous_subtab; item tmp; int line = 0; int ypos = 0; int scroll_pos = 0; bool redraw = true; bool keepline = false; bool done = false; bool batch = false; bool show_hidden = false; int batch_line = 0; int display_mode = 0; const recipe *chosen = nullptr; std::vector<iteminfo> thisItem; std::vector<iteminfo> dummy; input_context ctxt( "CRAFTING" ); ctxt.register_cardinal(); ctxt.register_action( "QUIT" ); ctxt.register_action( "CONFIRM" ); ctxt.register_action( "CYCLE_MODE" ); ctxt.register_action( "SCROLL_UP" ); ctxt.register_action( "SCROLL_DOWN" ); ctxt.register_action( "PREV_TAB" ); ctxt.register_action( "NEXT_TAB" ); ctxt.register_action( "FILTER" ); ctxt.register_action( "RESET_FILTER" ); ctxt.register_action( "TOGGLE_FAVORITE" ); ctxt.register_action( "HELP_RECIPE" ); ctxt.register_action( "HELP_KEYBINDINGS" ); ctxt.register_action( "CYCLE_BATCH" ); ctxt.register_action( "RELATED_RECIPES" ); ctxt.register_action( "HIDE_SHOW_RECIPE" ); const inventory &crafting_inv = g->u.crafting_inventory(); const std::vector<npc *> helpers = g->u.get_crafting_helpers(); std::string filterstring; const auto &available_recipes = g->u.get_available_recipes( crafting_inv, &helpers ); std::map<const recipe *, bool> availability_cache; do { if( redraw ) { // When we switch tabs, redraw the header redraw = false; if( ! keepline ) { line = 0; } else { keepline = false; } if( display_mode > 2 ) { display_mode = 2; } TAB_MODE m = ( batch ) ? BATCH : ( filterstring.empty() ) ? NORMAL : FILTERED; draw_recipe_tabs( w_head, tab.cur(), m ); draw_recipe_subtabs( w_subhead, tab.cur(), subtab.cur(), available_recipes, m ); show_hidden = false; available.clear(); if( batch ) { current.clear(); for( int i = 1; i <= 20; i++ ) { current.push_back( chosen ); available.push_back( chosen->requirements().can_make_with_inventory( crafting_inv, i ) ); } } else { std::vector<const recipe *> picking; if( !filterstring.empty() ) { auto qry = trim( filterstring ); if( qry.size() > 2 && qry[1] == ':' ) { switch( qry[0] ) { case 't': picking = available_recipes.search( qry.substr( 2 ), recipe_subset::search_type::tool ); break; case 'c': picking = available_recipes.search( qry.substr( 2 ), recipe_subset::search_type::component ); break; case 's': picking = available_recipes.search( qry.substr( 2 ), recipe_subset::search_type::skill ); break; case 'p': picking = available_recipes.search( qry.substr( 2 ), recipe_subset::search_type::primary_skill ); break; case 'Q': picking = available_recipes.search( qry.substr( 2 ), recipe_subset::search_type::quality ); break; case 'q': picking = available_recipes.search( qry.substr( 2 ), recipe_subset::search_type::quality_result ); break; case 'd': picking = available_recipes.search( qry.substr( 2 ), recipe_subset::search_type::description_result ); break; case 'm': { auto &learned = g->u.get_learned_recipes(); if( query_is_yes( qry ) ) { std::set_intersection( available_recipes.begin(), available_recipes.end(), learned.begin(), learned.end(), std::back_inserter( picking ) ); } else { std::set_difference( available_recipes.begin(), available_recipes.end(), learned.begin(), learned.end(), std::back_inserter( picking ) ); } break; } case 'h': { std::copy( available_recipes.begin(), available_recipes.end(), std::back_inserter( picking ) ); if( query_is_yes( qry ) ) { show_hidden = true; } break; } default: current.clear(); } } else { picking = available_recipes.search( qry ); } } else if( subtab.cur() == "CSC_*_FAVORITE" ) { picking = available_recipes.favorite(); } else if( subtab.cur() == "CSC_*_RECENT" ) { picking = available_recipes.recent(); } else { picking = available_recipes.in_category( tab.cur(), subtab.cur() != "CSC_ALL" ? subtab.cur() : "" ); } current.clear(); for( auto i : picking ) { if( ( uistate.hidden_recipes.find( i->ident() ) != uistate.hidden_recipes.end() ) == show_hidden ) { current.push_back( i ); } } if( !show_hidden ) { draw_hidden_amount( w_head, 0, picking.size() - current.size() ); } available.reserve( current.size() ); // cache recipe availability on first display for( const auto e : current ) { if( !availability_cache.count( e ) ) { availability_cache.emplace( e, e->requirements().can_make_with_inventory( crafting_inv ) ); } } if( subtab.cur() != "CSC_*_RECENT" ) { std::stable_sort( current.begin(), current.end(), []( const recipe * a, const recipe * b ) { return b->difficulty < a->difficulty; } ); std::stable_sort( current.begin(), current.end(), [&]( const recipe * a, const recipe * b ) { return availability_cache[a] && !availability_cache[b]; } ); } std::transform( current.begin(), current.end(), std::back_inserter( available ), [&]( const recipe * e ) { return availability_cache[e]; } ); } // current/available have been rebuilt, make sure our cursor is still in range if( current.empty() ) { line = 0; } else { line = std::min( line, static_cast<int>( current.size() ) - 1 ); } } // Clear the screen of recipe data, and draw it anew werase( w_data ); if( isWide ) { werase( w_iteminfo ); } if( isWide ) { mvwprintz( w_data, dataLines + 1, 5, c_white, _( "Press <ENTER> to attempt to craft object." ) ); wprintz( w_data, c_white, " " ); if( !filterstring.empty() ) { wprintz( w_data, c_white, _( "[E]: Describe, [F]ind, [R]eset, [m]ode, [s]how/hide, Re[L]ated, [*]Favorite, %s [?] keybindings" ), ( batch ) ? _( "cancel [b]atch" ) : _( "[b]atch" ) ); } else { wprintz( w_data, c_white, _( "[E]: Describe, [F]ind, [m]ode, [s]how/hide, Re[L]ated, [*]Favorite, %s [?] keybindings" ), ( batch ) ? _( "cancel [b]atch" ) : _( "[b]atch" ) ); } } else { if( !filterstring.empty() ) { mvwprintz( w_data, dataLines + 1, 5, c_white, _( "[E]: Describe, [F]ind, [R]eset, [m]ode, [s]how/hide, Re[L]ated, [*]Favorite, [b]atch [?] keybindings" ) ); } else { mvwprintz( w_data, dataLines + 1, 5, c_white, _( "[E]: Describe, [F]ind, [m]ode, [s]how/hide, Re[L]ated, [*]Favorite, [b]atch [?] keybindings" ) ); } mvwprintz( w_data, dataLines + 2, 5, c_white, _( "Press <ENTER> to attempt to craft object." ) ); } // Draw borders for( int i = 1; i < width - 1; ++i ) { // _ mvwputch( w_data, dataHeight - 1, i, BORDER_COLOR, LINE_OXOX ); } for( int i = 0; i < dataHeight - 1; ++i ) { // | mvwputch( w_data, i, 0, BORDER_COLOR, LINE_XOXO ); mvwputch( w_data, i, width - 1, BORDER_COLOR, LINE_XOXO ); } mvwputch( w_data, dataHeight - 1, 0, BORDER_COLOR, LINE_XXOO ); // _| mvwputch( w_data, dataHeight - 1, width - 1, BORDER_COLOR, LINE_XOOX ); // |_ int recmin = 0, recmax = current.size(); if( recmax > dataLines ) { if( line <= recmin + dataHalfLines ) { for( int i = recmin; i < recmin + dataLines; ++i ) { std::string tmp_name = current[i]->result_name(); if( batch ) { tmp_name = string_format( _( "%2dx %s" ), i + 1, tmp_name.c_str() ); } mvwprintz( w_data, i - recmin, 2, c_dark_gray, "" ); // Clear the line if( i == line ) { mvwprintz( w_data, i - recmin, 2, ( available[i] ? h_white : h_dark_gray ), utf8_truncate( tmp_name, 28 ).c_str() ); } else { mvwprintz( w_data, i - recmin, 2, ( available[i] ? c_white : c_dark_gray ), utf8_truncate( tmp_name, 28 ).c_str() ); } } } else if( line >= recmax - dataHalfLines ) { for( int i = recmax - dataLines; i < recmax; ++i ) { std::string tmp_name = current[i]->result_name(); if( batch ) { tmp_name = string_format( _( "%2dx %s" ), i + 1, tmp_name.c_str() ); } mvwprintz( w_data, dataLines + i - recmax, 2, c_light_gray, "" ); // Clear the line if( i == line ) { mvwprintz( w_data, dataLines + i - recmax, 2, ( available[i] ? h_white : h_dark_gray ), utf8_truncate( tmp_name, 28 ).c_str() ); } else { mvwprintz( w_data, dataLines + i - recmax, 2, ( available[i] ? c_white : c_dark_gray ), utf8_truncate( tmp_name, 28 ).c_str() ); } } } else { for( int i = line - dataHalfLines; i < line - dataHalfLines + dataLines; ++i ) { std::string tmp_name = current[i]->result_name(); if( batch ) { tmp_name = string_format( _( "%2dx %s" ), i + 1, tmp_name.c_str() ); } mvwprintz( w_data, dataHalfLines + i - line, 2, c_light_gray, "" ); // Clear the line if( i == line ) { mvwprintz( w_data, dataHalfLines + i - line, 2, ( available[i] ? h_white : h_dark_gray ), utf8_truncate( tmp_name, 28 ).c_str() ); } else { mvwprintz( w_data, dataHalfLines + i - line, 2, ( available[i] ? c_white : c_dark_gray ), utf8_truncate( tmp_name, 28 ).c_str() ); } } } } else { for( size_t i = 0; i < current.size() && i < static_cast<size_t>( dataHeight ) + 1; ++i ) { std::string tmp_name = current[i]->result_name(); if( batch ) { tmp_name = string_format( _( "%2dx %s" ), static_cast<int>( i ) + 1, tmp_name.c_str() ); } if( static_cast<int>( i ) == line ) { mvwprintz( w_data, i, 2, ( available[i] ? h_white : h_dark_gray ), utf8_truncate( tmp_name, 28 ).c_str() ); } else { mvwprintz( w_data, i, 2, ( available[i] ? c_white : c_dark_gray ), utf8_truncate( tmp_name, 28 ).c_str() ); } } } if( !current.empty() ) { int pane = FULL_SCREEN_WIDTH - 30 - 1; int count = batch ? line + 1 : 1; // batch size nc_color col = available[ line ] ? c_white : c_light_gray; const auto &req = current[ line ]->requirements(); draw_can_craft_indicator( w_head, 0, *current[line] ); wrefresh( w_head ); ypos = 0; auto qry = trim( filterstring ); std::string qry_comps; if( qry.compare( 0, 2, "c:" ) == 0 ) { qry_comps = qry.substr( 2 ); } std::vector<std::string> component_print_buffer; auto tools = req.get_folded_tools_list( pane, col, crafting_inv, count ); auto comps = req.get_folded_components_list( pane, col, crafting_inv, count, qry_comps ); component_print_buffer.insert( component_print_buffer.end(), tools.begin(), tools.end() ); component_print_buffer.insert( component_print_buffer.end(), comps.begin(), comps.end() ); if( !g->u.knows_recipe( current[line] ) ) { component_print_buffer.push_back( _( "Recipe not memorized yet" ) ); auto books_with_recipe = g->u.get_books_for_recipe( crafting_inv, current[line] ); std::string enumerated_books = enumerate_as_string( books_with_recipe.begin(), books_with_recipe.end(), []( itype_id type_id ) { return item::find_type( type_id )->nname( 1 ); } ); const std::string text = string_format( _( "Written in: %s" ), enumerated_books.c_str() ); std::vector<std::string> folded_lines = foldstring( text, pane ); component_print_buffer.insert( component_print_buffer.end(), folded_lines.begin(), folded_lines.end() ); } //handle positioning of component list if it needed to be scrolled int componentPrintOffset = 0; if( display_mode > 2 ) { componentPrintOffset = ( display_mode - 2 ) * componentPrintHeight; } if( component_print_buffer.size() < static_cast<size_t>( componentPrintOffset ) ) { componentPrintOffset = 0; if( previous_tab != tab.cur() || previous_subtab != subtab.cur() || previous_item_line != line ) { display_mode = 2; } else { display_mode = 0; } } //only used to preserve mode position on components when //moving to another item and the view is already scrolled previous_tab = tab.cur(); previous_subtab = subtab.cur(); previous_item_line = line; const int xpos = 30; if( display_mode == 0 ) { const int width = getmaxx( w_data ) - xpos - item_info_x; print_colored_text( w_data, ypos++, xpos, col, col, string_format( _( "Primary skill used: <color_cyan>%s</color>" ), ( !current[line]->skill_used ? _( "N/A" ) : current[line]->skill_used.obj().name() ) ) ); auto player_skill = g->u.get_skill_level( current[line]->skill_used ); std::string difficulty_color = current[ line ]->difficulty > player_skill ? "yellow" : "green"; print_colored_text( w_data, ypos++, xpos, col, col, string_format( _( "Difficulty: <color_%s>%d</color>" ), difficulty_color, current[ line ]->difficulty ) ); std::string skill_level_string = current[line]->skill_used ? string_format( _( "Your skill level: <color_%s>%d</color>" ), difficulty_color, player_skill ) : _( "Your skill level: <color_yellow>N/A</color>" ); print_colored_text( w_data, ypos++, xpos, col, col, skill_level_string ); ypos += fold_and_print( w_data, ypos, xpos, width, col, _( "Other skills used: %s" ), current[line]->required_skills_string( &g->u ) ); const int expected_turns = g->u.expected_time_to_craft( *current[line], count ) / to_moves<int>( 1_turns ); ypos += fold_and_print( w_data, ypos, xpos, pane, col, _( "Time to complete: <color_cyan>%s</color>" ), to_string( time_duration::from_turns( expected_turns ) ) ); print_colored_text( w_data, ypos++, xpos, col, col, string_format( _( "Dark craftable? <color_cyan>%s</color>" ), current[line]->has_flag( "BLIND_EASY" ) ? _( "Easy" ) : current[line]->has_flag( "BLIND_HARD" ) ? _( "Hard" ) : _( "Impossible" ) ) ); ypos += print_items( *current[line], w_data, ypos, xpos, col, batch ? line + 1 : 1 ); } //color needs to be preserved in case part of the previous page was cut off nc_color stored_color = col; if( display_mode > 2 ) { stored_color = rotated_color; } else { rotated_color = col; } int components_printed = 0; for( size_t i = static_cast<size_t>( componentPrintOffset ); i < component_print_buffer.size(); i++ ) { if( ypos >= componentPrintHeight ) { break; } components_printed++; print_colored_text( w_data, ypos++, xpos, stored_color, col, component_print_buffer[i] ); } if( ypos >= componentPrintHeight && component_print_buffer.size() > static_cast<size_t>( components_printed ) ) { mvwprintz( w_data, ypos++, xpos, col, _( "v (more)" ) ); rotated_color = stored_color; } if( isWide ) { if( last_recipe != current[line] ) { last_recipe = current[line]; tmp = current[line]->create_result(); } tmp.info( true, thisItem, count ); draw_item_info( w_iteminfo, tmp.tname(), tmp.type_name(), thisItem, dummy, scroll_pos, true, true, true, false, true ); } } draw_scrollbar( w_data, line, dataLines, recmax, 0 ); wrefresh( w_data ); if( isWide ) { wrefresh( w_iteminfo ); } const std::string action = ctxt.handle_input(); if( action == "CYCLE_MODE" ) { display_mode = display_mode + 1; if( display_mode <= 0 ) { display_mode = 0; } } else if( action == "LEFT" ) { std::string start = subtab.cur(); do { subtab.prev(); } while( subtab.cur() != start && available_recipes.empty_category( tab.cur(), subtab.cur() != "CSC_ALL" ? subtab.cur() : "" ) ); redraw = true; } else if( action == "SCROLL_UP" ) { scroll_pos--; } else if( action == "SCROLL_DOWN" ) { scroll_pos++; } else if( action == "PREV_TAB" ) { tab.prev(); subtab = list_circularizer<std::string>( craft_subcat_list[tab.cur()] );//default ALL redraw = true; } else if( action == "RIGHT" ) { std::string start = subtab.cur(); do { subtab.next(); } while( subtab.cur() != start && available_recipes.empty_category( tab.cur(), subtab.cur() != "CSC_ALL" ? subtab.cur() : "" ) ); redraw = true; } else if( action == "NEXT_TAB" ) { tab.next(); subtab = list_circularizer<std::string>( craft_subcat_list[tab.cur()] );//default ALL redraw = true; } else if( action == "DOWN" ) { line++; } else if( action == "UP" ) { line--; } else if( action == "CONFIRM" ) { if( available.empty() || !available[line] ) { popup( _( "You can't do that!" ) ); } else if( !g->u.check_eligible_containers_for_crafting( *current[line], ( batch ) ? line + 1 : 1 ) ) { // popup is already inside check } else { chosen = current[line]; batch_size = ( batch ) ? line + 1 : 1; done = true; } } else if( action == "HELP_RECIPE" ) { if( current.empty() ) { popup( _( "Nothing selected!" ) ); redraw = true; continue; } tmp = current[line]->create_result(); full_screen_popup( "%s\n%s", tmp.type_name( 1 ).c_str(), tmp.info( true ).c_str() ); redraw = true; keepline = true; } else if( action == "FILTER" ) { struct SearchPrefix { char key; std::string example; std::string description; }; std::vector<SearchPrefix> prefixes = { { 'q', _( "metal sawing" ), _( "<color_cyan>quality</color> of resulting item" ) }, //~ Example result description search term { 'd', _( "reach attack" ), _( "<color_cyan>full description</color> of resulting item (slow)" ) }, { 'c', _( "two by four" ), _( "<color_cyan>component</color> required to craft" ) }, { 'p', _( "tailoring" ), _( "<color_cyan>primary skill</color> used to craft" ) }, { 's', _( "cooking" ), _( "<color_cyan>any skill</color> used to craft" ) }, { 'Q', _( "fine bolt turning" ), _( "<color_cyan>quality</color> required to craft" ) }, { 't', _( "soldering iron" ), _( "<color_cyan>tool</color> required to craft" ) }, { 'h', _( "yes" ), _( "recipes which are <color_cyan>hidden</color> or not" ) }, { 'm', _( "no" ), _( "recipes which are <color_cyan>memorized</color> or not" ) }, }; int max_example_length = 0; for( const auto &prefix : prefixes ) { max_example_length = std::max( max_example_length, utf8_width( prefix.example ) ); } std::string spaces( max_example_length, ' ' ); std::string description = _( "The default is to search result names. Some single-character prefixes " "can be used with a colon (:) to search in other ways.\n" "\n" "<color_white>Examples:</color>\n" ); { std::string example_name = _( "shirt" ); auto padding = max_example_length - utf8_width( example_name ); description += string_format( _( " <color_white>%s</color>%.*s %s\n" ), example_name, padding, spaces, _( "<color_cyan>name</color> of resulting item" ) ); } for( const auto &prefix : prefixes ) { auto padding = max_example_length - utf8_width( prefix.example ); description += string_format( _( " <color_yellow>%c</color><color_white>:%s</color>%.*s %s\n" ), prefix.key, prefix.example, padding, spaces, prefix.description ); } string_input_popup() .title( _( "Search:" ) ) .width( 85 ) .description( description ) .desc_color( c_light_gray ) .edit( filterstring ); redraw = true; } else if( action == "QUIT" ) { chosen = nullptr; done = true; } else if( action == "RESET_FILTER" ) { filterstring.clear(); redraw = true; } else if( action == "CYCLE_BATCH" ) { if( current.empty() ) { popup( _( "Nothing selected!" ) ); redraw = true; continue; } batch = !batch; if( batch ) { batch_line = line; chosen = current[batch_line]; } else { line = batch_line; keepline = true; } redraw = true; } else if( action == "TOGGLE_FAVORITE" ) { keepline = true; redraw = true; if( current.empty() ) { popup( _( "Nothing selected!" ) ); continue; } if( uistate.favorite_recipes.find( current[line]->ident() ) != uistate.favorite_recipes.end() ) { uistate.favorite_recipes.erase( current[line]->ident() ); } else { uistate.favorite_recipes.insert( current[line]->ident() ); } } else if( action == "HIDE_SHOW_RECIPE" ) { if( current.empty() ) { popup( _( "Nothing selected!" ) ); redraw = true; continue; } if( show_hidden ) { uistate.hidden_recipes.erase( current[line]->ident() ); } else { uistate.hidden_recipes.insert( current[line]->ident() ); } redraw = true; } else if( action == "RELATED_RECIPES" ) { if( current.empty() ) { popup( _( "Nothing selected!" ) ); redraw = true; continue; } std::string recipe_name = peek_related_recipe( current[ line ], available_recipes ); if( recipe_name.empty() ) { keepline = true; } else { filterstring = recipe_name; } redraw = true; } if( line < 0 ) { line = current.size() - 1; } else if( line >= static_cast<int>( current.size() ) ) { line = 0; } } while( !done ); return chosen; }
/* * Calculate sizes, populate arrays, initialize window */ void uimenu::setup() { bool w_auto = (w_width == -1 || w_width == -2 ); bool w_autofold = ( w_width == -2); if ( w_auto ) { w_width = 4; if ( !title.empty() ) w_width = title.size() + 5; } bool h_auto = (w_height == -1); if ( h_auto ) { w_height = 4; } max_entry_len = 0; std::vector<int> autoassign; autoassign.clear(); int pad = pad_left + pad_right + 2; for ( int i = 0; i < entries.size(); i++ ) { int txtwidth = utf8_width(entries[ i ].txt.c_str()); if ( txtwidth > max_entry_len ) { max_entry_len = txtwidth; } if(entries[ i ].enabled) { if( entries[ i ].hotkey > 0 ) { keymap[ entries[ i ].hotkey ] = i; } else if ( entries[ i ].hotkey == -1 && i < 100 ) { autoassign.push_back(i); } if ( entries[ i ].retval == -1 ) { entries[ i ].retval = i; } if ( w_auto && w_width < txtwidth + pad + 4 ) { w_width = txtwidth + pad + 4; } } else { if ( w_auto && w_width < txtwidth + pad + 4 ) { w_width = txtwidth + pad + 4; // todo: or +5 if header } } if ( entries[ i ].text_color == C_UNSET_MASK ) { entries[ i ].text_color = text_color; } fentries.push_back( i ); } if ( !autoassign.empty() ) { int modifier = 0; //Increase this by one if assignment fails (the key is already used then). for ( int a = 0; a < autoassign.size(); ) { int setkey=-1; if ( (a + modifier) < 9 ) { setkey = (a + modifier) + 49; // 1-9; } else if ( (a + modifier) == 9 ) { setkey = (a + modifier) + 39; // 0; } else if ( (a + modifier) < 36 ) { setkey = (a + modifier) + 87; // a-z } else if ( (a + modifier) < 61 ) { setkey = (a + modifier) + 29; // A-Z } if ( setkey != -1 && keymap.count(setkey) <= 0 ) { int palloc = autoassign[ a ]; entries[ palloc ].hotkey = setkey; keymap[ setkey ] = palloc; a++; } else { modifier++; //Keymap.count was not <= 0 } } } if (w_auto && w_width > TERMX) { w_width = TERMX; } if(!text.empty() ) { int twidth = utf8_width(text.c_str()); bool formattxt=true; int realtextwidth = 0; if ( textwidth == -1 ) { if ( w_autofold || !w_auto ) { realtextwidth = w_width - 4; } else { realtextwidth = twidth; if ( twidth + 4 > w_width ) { if ( realtextwidth + 4 > TERMX ) { realtextwidth = TERMX - 4; } textformatted = foldstring(text, realtextwidth); formattxt=false; realtextwidth = 10; for ( int l=0; l < textformatted.size(); l++ ) { if ( utf8_width(textformatted[l].c_str()) > realtextwidth ) { realtextwidth = utf8_width(textformatted[l].c_str()); } } if ( realtextwidth + 4 > w_width ) { w_width = realtextwidth + 4; } } } } else if ( textwidth != -1 ) { realtextwidth = textwidth; } if ( formattxt == true ) { textformatted = foldstring(text, realtextwidth); } } if (h_auto) { w_height = 3 + textformatted.size() + entries.size(); } if ( w_height > TERMY ) { w_height = TERMY; } vmax = entries.size(); if ( vmax + 3 + textformatted.size() > w_height ) { vmax = w_height - 3 - textformatted.size(); if ( vmax < 1 ) { if (textformatted.empty()) { popup("Can't display menu options, 0 %d available screen rows are occupied\nThis is probably a bug.\n",TERMY); } else { popup("Can't display menu options, %d %d available screen rows are occupied by\n'%s\n(snip)\n%s'\nThis is probably a bug.\n", textformatted.size(), TERMY, textformatted[0].c_str(), textformatted[ textformatted.size() - 1 ].c_str() ); } } } if (w_x == -1) { w_x = int((TERMX - w_width) / 2); } if (w_y == -1) { w_y = int((TERMY - w_height) / 2); } if ( scrollbar_side == -1 ) { scrollbar_side = ( pad_left > 0 ? 1 : 0 ); } if ( entries.size() <= vmax ) { scrollbar_auto = false; } window = newwin(w_height, w_width, w_y, w_x); werase(window); draw_border(window, border_color); if( !title.empty() ) { mvwprintz(window, 0, 1, border_color, "< "); wprintz(window, title_color, "%s", title.c_str() ); wprintz(window, border_color, " >"); } fselected = selected; started = true; }
static void check_equal( IterResult beg_res, IterResult end_res, IterExpect beg_exp, IterExpect end_exp ) { CHECK( std::distance( beg_res, end_res ) == std::distance( beg_exp, end_exp ) ); IterResult result = beg_res; IterExpect expect = beg_exp; for( ; result != end_res && expect != end_exp; ++result, ++expect ) { CHECK( *result == *expect ); } } TEST_CASE( "fold-string" ) { SECTION( "Case 1 - test wrapping of lorem ipsum" ) { const auto folded = foldstring( "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque a.", 17 ); const auto expected = { /* 0123456789abcdefg */ "Lorem ipsum dolor", "sit amet, ", "consectetur ", "adipiscing elit. ", "Pellentesque a.", }; check_equal( folded.begin(), folded.end(), expected.begin(), expected.end() ); } SECTION( "Case 2 - test wrapping of Chinese" ) {
int monster::print_info(game *g, WINDOW* w, int vStart, int vLines, int column) { // First line of w is the border; the next two are terrain info, and after that // is a blank line. w is 13 characters tall, and we can't use the last one // because it's a border as well; so we have lines 4 through 11. // w is also 48 characters wide - 2 characters for border = 46 characters for us // vStart added because 'help' text in targeting win makes helpful info hard to find // at a glance. const int vEnd = vStart + vLines; mvwprintz(w, vStart++, column, c_white, "%s ", type->name.c_str()); switch (attitude(&(g->u))) { case MATT_FRIEND: wprintz(w, h_white, _("Friendly! ")); break; case MATT_FLEE: wprintz(w, c_green, _("Fleeing! ")); break; case MATT_IGNORE: wprintz(w, c_ltgray, _("Ignoring ")); break; case MATT_FOLLOW: wprintz(w, c_yellow, _("Tracking ")); break; case MATT_ATTACK: wprintz(w, c_red, _("Hostile! ")); break; default: wprintz(w, h_red, "BUG: Behavior unnamed. (monster.cpp:print_info)"); break; } if (has_effect(ME_DOWNED)) wprintz(w, h_white, _("On ground")); else if (has_effect(ME_STUNNED)) wprintz(w, h_white, _("Stunned")); else if (has_effect(ME_BEARTRAP)) wprintz(w, h_white, _("Trapped")); std::string damage_info; nc_color col; if (hp >= type->hp) { damage_info = _("It is uninjured"); col = c_green; } else if (hp >= type->hp * .8) { damage_info = _("It is lightly injured"); col = c_ltgreen; } else if (hp >= type->hp * .6) { damage_info = _("It is moderately injured"); col = c_yellow; } else if (hp >= type->hp * .3) { damage_info = _("It is heavily injured"); col = c_yellow; } else if (hp >= type->hp * .1) { damage_info = _("It is severly injured"); col = c_ltred; } else { damage_info = _("it is nearly dead"); col = c_red; } mvwprintz(w, vStart++, column, col, damage_info.c_str()); std::vector<std::string> lines = foldstring(type->description, getmaxx(w) - 1 - column); int numlines = lines.size(); for (int i = 0; i < numlines && vStart <= vEnd; i++) mvwprintz(w, vStart++, column, c_white, lines[i].c_str()); return vStart; }
void robot_finds_kitten::process_input(int input, WINDOW *w) { timespec ts; ts.tv_sec = 1; ts.tv_nsec = 0; int check_x = robot.x; int check_y = robot.y; switch (input) { case KEY_UP: /* up */ check_y--; break; case KEY_DOWN: /* down */ check_y++; break; case KEY_LEFT: /* left */ check_x--; break; case KEY_RIGHT: /* right */ check_x++; break; case 0: break; default: { /* invalid command */ for (int c = 0; c < rfkCOLS; c++) { mvwputch (w, 0, c, c_white, ' '); mvwputch (w, 1, c, c_white, ' '); } mvwprintz (w, 0, 0, c_white, _("Invalid command: Use direction keys or press 'q'.")); return; } } if (check_y < 3 || check_y > rfkLINES - 1 || check_x < 0 || check_x > rfkCOLS - 1) { return; } if (rfkscreen[check_x][check_y] != EMPTY) { switch (rfkscreen[check_x][check_y]) { case ROBOT: /* We didn't move. */ break; case KITTEN: {/* Found it! */ for (int c = 0; c < rfkCOLS; c++) { mvwputch (w, 0, c, c_white, ' '); } /* The grand cinema scene. */ for (int c = 0; c <= 3; c++) { wmove(w, 1, (rfkCOLS / 2) - 5 + c); wputch(w, c_white, ' '); wmove(w, 1, (rfkCOLS / 2) + 4 - c); wputch(w, c_white, ' '); wmove(w, 1, (rfkCOLS / 2) - 4 + c); if (input == KEY_LEFT || input == KEY_UP) { draw_kitten(w); } else { draw_robot(w); } wmove(w, 1, (rfkCOLS / 2) + 3 - c); if (input == KEY_LEFT || input == KEY_UP) { draw_robot(w); } else { draw_kitten(w); } wrefresh (w); nanosleep(&ts, NULL); } /* They're in love! */ mvwprintz(w, 0, ((rfkCOLS - 6) / 2) - 1, c_ltred, "<3<3<3"); wrefresh(w); nanosleep(&ts, NULL); for (int c = 0; c < rfkCOLS; c++) { mvwputch (w, 0, c, c_white, ' '); mvwputch (w, 1, c, c_white, ' '); } mvwprintz (w, 0, 0, c_white, _("You found kitten! Way to go, robot!")); wrefresh(w); ret = true; int ech = input; do { ech = getch (); } while ( ech == input ); } break; default: { for (int c = 0; c < rfkCOLS; c++) { mvwputch (w, 0, c, c_white, ' '); mvwputch (w, 1, c, c_white, ' '); } std::vector<std::string> bogusvstr = foldstring( getmessage( bogus_messages[rfkscreen[check_x][check_y] - 2]), rfkCOLS); for (size_t c = 0; c < bogusvstr.size(); c++) { mvwprintw (w, c, 0, "%s", bogusvstr[c].c_str()); } wrefresh(w); } break; } wmove(w, 2, 0); return; } /* Otherwise, move the robot. */ robot.x = check_x; robot.y = check_y; }
int vpart_info::format_description( std::ostringstream &msg, const std::string &format_color, int width ) const { int lines = 1; msg << _( "<color_white>Description</color>\n" ); msg << "> " << format_color; class::item base( item ); std::ostringstream long_descrip; if( ! description.empty() ) { long_descrip << _( description.c_str() ); } for( const auto &flagid : flags ) { if( flagid == "ALARMCLOCK" || flagid == "WATCH" ) { continue; } json_flag flag = json_flag::get( flagid ); if( ! flag.info().empty() ) { if( ! long_descrip.str().empty() ) { long_descrip << " "; } long_descrip << _( flag.info().c_str() ); } } if( ( has_flag( "SEAT" ) || has_flag( "BED" ) ) && ! has_flag( "BELTABLE" ) ) { json_flag nobelt = json_flag::get( "NONBELTABLE" ); long_descrip << " " << _( nobelt.info().c_str() ); } if( has_flag( "BOARDABLE" ) && has_flag( "OPENABLE" ) ) { json_flag nobelt = json_flag::get( "DOOR" ); long_descrip << " " << _( nobelt.info().c_str() ); } if( has_flag( "TURRET" ) ) { long_descrip << string_format( _( "\nRange: %1$5d Damage: %2$5.0f" ), base.gun_range( true ), base.gun_damage().total_damage() ); } if( ! long_descrip.str().empty() ) { const auto wrap_descrip = foldstring( long_descrip.str(), width ); msg << wrap_descrip[0]; for( size_t i = 1; i < wrap_descrip.size(); i++ ) { msg << "\n " << wrap_descrip[i]; } msg << "</color>\n"; lines += wrap_descrip.size(); } // borrowed from item.cpp and adjusted const quality_id quality_jack( "JACK" ); const quality_id quality_lift( "LIFT" ); for( const auto &qual : qualities ) { msg << "> " << format_color << string_format( _( "Has level %1$d %2$s quality" ), qual.second, qual.first.obj().name.c_str() ); if( qual.first == quality_jack || qual.first == quality_lift ) { msg << string_format( _( " and is rated at %1$d %2$s" ), static_cast<int>( convert_weight( qual.second * TOOL_LIFT_FACTOR ) ), weight_units() ); } msg << ".</color>\n"; lines += 1; } return lines; }
virtual void select(int entnum, uimenu *menu) override { if ( ! started ) { started = true; padding = std::string(menu->pad_right - 1, ' '); for( auto &traits_iter : mutation_branch::get_all() ) { vTraits.push_back( traits_iter.first ); pTraits[traits_iter.first] = ( p->has_trait( traits_iter.first ) ); } } auto &mdata = mutation_branch::get( vTraits[entnum] ); int startx = menu->w_width - menu->pad_right; for ( int i = 2; i < lastlen; i++ ) { mvwprintw(menu->window, i, startx, "%s", padding.c_str() ); } mvwprintw(menu->window, 3, startx, mdata.valid ? _("Valid") : _("Nonvalid")); int line2 = 4; if ( !mdata.prereqs.empty() ) { line2++; mvwprintz(menu->window, line2, startx, c_ltgray, _("Prereqs:")); for (auto &j : mdata.prereqs) { mvwprintz(menu->window, line2, startx + 11, mcolor(j), "%s", mutation_branch::get_name( j ).c_str()); line2++; } } if ( !mdata.prereqs2.empty() ) { line2++; mvwprintz(menu->window, line2, startx, c_ltgray, _("Prereqs, 2d:")); for (auto &j : mdata.prereqs2) { mvwprintz(menu->window, line2, startx + 15, mcolor(j), "%s", mutation_branch::get_name( j ).c_str()); line2++; } } if ( !mdata.threshreq.empty() ) { line2++; mvwprintz(menu->window, line2, startx, c_ltgray, _("Thresholds required:")); for (auto &j : mdata.threshreq) { mvwprintz(menu->window, line2, startx + 21, mcolor(j), "%s", mutation_branch::get_name( j ).c_str()); line2++; } } if ( !mdata.cancels.empty() ) { line2++; mvwprintz(menu->window, line2, startx, c_ltgray, _("Cancels:")); for (auto &j : mdata.cancels) { mvwprintz(menu->window, line2, startx + 11, mcolor(j), "%s", mutation_branch::get_name( j ).c_str()); line2++; } } if ( !mdata.replacements.empty() ) { line2++; mvwprintz(menu->window, line2, startx, c_ltgray, _("Becomes:")); for (auto &j : mdata.replacements) { mvwprintz(menu->window, line2, startx + 11, mcolor(j), "%s", mutation_branch::get_name( j ).c_str()); line2++; } } if ( !mdata.additions.empty() ) { line2++; mvwprintz(menu->window, line2, startx, c_ltgray, _("Add-ons:")); for (auto &j : mdata.additions) { mvwprintz(menu->window, line2, startx + 11, mcolor(j), "%s", mutation_branch::get_name( j ).c_str()); line2++; } } if ( !mdata.category.empty() ) { line2++; mvwprintz(menu->window, line2, startx, c_ltgray, _("Category:")); for (auto &j : mdata.category) { mvwprintw(menu->window, line2, startx + 11, "%s", j.c_str()); line2++; } } line2 += 2; mvwprintz(menu->window, line2, startx, c_ltgray, "pts: %d vis: %d ugly: %d", mdata.points, mdata.visibility, mdata.ugliness ); line2 += 2; std::vector<std::string> desc = foldstring( mdata.description, menu->pad_right - 1 ); for( auto &elem : desc ) { mvwprintz( menu->window, line2, startx, c_ltgray, "%s", elem.c_str() ); line2++; } lastlen = line2 + 1; mvwprintz(menu->window, menu->w_height - 3, startx, c_green, "%s", msg.c_str()); msg = padding; mvwprintw(menu->window, menu->w_height - 2, startx, _("[/] find, [q]uit")); };
void uimenu::setup() { bool w_auto = (w_width == -1 || w_width == -2 ); bool w_autofold = ( w_width == -2); int realtextwidth = 0; if ( w_auto ) { w_width = 4; if ( title.size() > 0 ) w_width = title.size() + 5; } bool h_auto = (w_height == -1); if ( h_auto ) { w_height = 4; } std::vector<int> autoassign; autoassign.clear(); int pad = pad_left + pad_right + 2; for ( int i = 0; i < entries.size(); i++ ) { int txtwidth = utf8_width(entries[ i ].txt.c_str()); if(entries[ i ].enabled) { if( entries[ i ].hotkey > 0 ) { keymap[ entries[ i ].hotkey ] = i; } else if ( entries[ i ].hotkey == -1 && i < 100 ) { autoassign.push_back(i); } if ( entries[ i ].retval == -1 ) { entries[ i ].retval = i; } if ( w_auto && w_width < txtwidth + pad + 4 ) { w_width = txtwidth + pad + 4; } } else { if ( w_auto && w_width < txtwidth + pad + 4 ) { w_width = txtwidth + pad + 4; // todo: or +5 if header } } if ( entries[ i ].text_color == C_UNSET_MASK ) { entries[ i ].text_color = text_color; } } if ( autoassign.size() > 0 ) { for ( int a = 0; a < autoassign.size(); a++ ) { int palloc = autoassign[ a ]; int setkey=-1; if ( palloc < 9 ) { setkey = palloc + 49; // 1-9; } else if ( palloc == 9 ) { setkey = palloc + 39; // 0; } else if ( palloc < 36 ) { setkey = palloc + 87; // a-z } else if ( palloc < 61 ) { setkey = palloc + 29; // A-Z } if ( setkey != -1 && keymap.find(setkey) == keymap.end() ) { entries[ palloc ].hotkey = setkey; keymap[ setkey ] = palloc; } } } if (w_auto && w_width > TERMX) { w_width = TERMX; } if(text.size() > 0 ) { int twidth = utf8_width(text.c_str()); bool formattxt=true; if ( textwidth == -1 ) { if ( w_autofold || !w_auto ) { realtextwidth = w_width - 4; } else { realtextwidth = twidth; if ( twidth + 4 > w_width ) { if ( realtextwidth + 4 > TERMX ) { realtextwidth = TERMX - 4; } textformatted = foldstring(text, realtextwidth); formattxt=false; realtextwidth = 10; for ( int l=0; l < textformatted.size(); l++ ) { if ( utf8_width(textformatted[l].c_str()) > realtextwidth ) { realtextwidth = utf8_width(textformatted[l].c_str()); } } if ( realtextwidth + 4 > w_width ) { w_width = realtextwidth + 4; } } } } else if ( textwidth != -1 ) { realtextwidth = textwidth; } if ( formattxt == true ) { textformatted = foldstring(text, realtextwidth); } } if (h_auto) { w_height = 2 + textformatted.size() + entries.size(); } vmax = entries.size(); if ( w_height > TERMY ) { w_height = TERMY; } if ( vmax + 2 + textformatted.size() > w_height ) { vmax = w_height - 2 - textformatted.size(); if ( vmax < 1 ) { popup("Can't display menu options, %d %d available screen rows are occupied by\n'%s\n(snip)\n%s'\nThis is probably a bug.\n", textformatted.size(),TERMY,textformatted[0].c_str(),textformatted[textformatted.size()-1].c_str() ); } } if (w_x == -1) { w_x = int((TERMX - w_width) / 2); } if (w_y == -1) { w_y = int((TERMY - w_height) / 2); } window = newwin(w_height, w_width, w_y, w_x); werase(window); wattron(window, border_color); wborder(window, LINE_XOXO, LINE_XOXO, LINE_OXOX, LINE_OXOX, LINE_OXXO, LINE_OOXX, LINE_XXOO, LINE_XOOX ); wattroff(window, border_color); if( title.size() > 0 ) { mvwprintz(window, 0, 1, border_color, "< "); wprintz(window, title_color, "%s", title.c_str() ); wprintz(window, border_color, " >"); } started = true; }