bool list_items_match( const item *item, std::string sPattern ) { size_t iPos; bool hasExclude = false; if( sPattern.find( "-" ) != std::string::npos ) { hasExclude = true; } do { iPos = sPattern.find( "," ); std::string pat = ( iPos == std::string::npos ) ? sPattern : sPattern.substr( 0, iPos ); bool exclude = false; if( pat.substr( 0, 1 ) == "-" ) { exclude = true; pat = pat.substr( 1, pat.size() - 1 ); } else if( hasExclude ) { hasExclude = false; //If there are non exclusive items to filter, we flip this back to false. } std::string namepat = pat; std::transform( namepat.begin(), namepat.end(), namepat.begin(), tolower ); if( lcmatch( remove_color_tags( item->tname() ), namepat ) ) { return !exclude; } if( pat.find( "{", 0 ) != std::string::npos ) { std::string adv_pat_type = pat.substr( 1, pat.find( ":" ) - 1 ); std::string adv_pat_search = pat.substr( pat.find( ":" ) + 1, ( pat.find( "}" ) - pat.find( ":" ) ) - 1 ); std::transform( adv_pat_search.begin(), adv_pat_search.end(), adv_pat_search.begin(), tolower ); if( adv_pat_type == "c" && lcmatch( item->get_category().name, adv_pat_search ) ) { return !exclude; } else if( adv_pat_type == "m" ) { for( auto material : item->made_of_types() ) { if( lcmatch( material->name(), adv_pat_search ) ) { return !exclude; } } } else if( adv_pat_type == "dgt" && item->damage() > atoi( adv_pat_search.c_str() ) ) { return !exclude; } else if( adv_pat_type == "dlt" && item->damage() < atoi( adv_pat_search.c_str() ) ) { return !exclude; } } if( iPos != std::string::npos ) { sPattern = sPattern.substr( iPos + 1, sPattern.size() ); } } while( iPos != std::string::npos ); return hasExclude; }
std::vector<std::string> input_context::filter_strings_by_phrase( const std::vector<std::string> &strings, const std::string &phrase ) const { std::vector<std::string> filtered_strings; for( auto &str : strings ) { if( lcmatch( remove_color_tags( get_action_name( str ) ), phrase ) ) { filtered_strings.push_back( str ); } } return filtered_strings; }
std::vector<const recipe *> recipe_subset::search( const std::string &txt, const search_type key ) const { std::vector<const recipe *> res; std::copy_if( recipes.begin(), recipes.end(), std::back_inserter( res ), [&]( const recipe * r ) { if( !*r ) { return false; } switch( key ) { case search_type::name: return lcmatch( r->result_name(), txt ); case search_type::skill: return lcmatch( r->required_skills_string( nullptr ), txt ) || lcmatch( r->skill_used->name(), txt ); case search_type::primary_skill: return lcmatch( r->skill_used->name(), txt ); case search_type::component: return search_reqs( r->requirements().get_components(), txt ); case search_type::tool: return search_reqs( r->requirements().get_tools(), txt ); case search_type::quality: return search_reqs( r->requirements().get_qualities(), txt ); case search_type::quality_result: { const auto &quals = item::find_type( r->result() )->qualities; return std::any_of( quals.begin(), quals.end(), [&]( const std::pair<quality_id, int> &e ) { return lcmatch( e.first->name, txt ); } ); } case search_type::description_result: { const item result = r->create_result(); return lcmatch( remove_color_tags( result.info( true ) ), txt ); } default: return false; } } ); return res; }
void add_msg_string(std::string &&msg, game_message_type const type) { if (msg.length() == 0) { return; } // hide messages if dead if (g->u.is_dead_state()) { return; } if (type == m_debug && !debug_mode) { return; } if (coalesce_messages(msg, type)) { return; } while (messages.size() > 255) { messages.pop_front(); } messages.emplace_back(remove_color_tags(std::move(msg)), type); }
void Messages::dialog::do_filter( const std::string &filter_str ) { // Split the search string into type and text bool has_type_filter = false; game_message_type filter_type = m_neutral; std::string filter_text; const auto colon = filter_str.find( ':' ); if( colon != std::string::npos ) { has_type_filter = msg_type_from_name( filter_type, filter_str.substr( 0, colon ) ); filter_text = filter_str.substr( colon + 1 ); } else { filter_text = filter_str; } // Start filtering the log folded_filtered.clear(); for( size_t folded_ind = 0; folded_ind < folded_all.size(); ) { const size_t msg_ind = folded_all[folded_ind].first; const game_message &msg = player_messages.history( msg_ind ); const bool match = ( !has_type_filter || filter_type == msg.type ) && ci_find_substr( remove_color_tags( msg.get_with_count() ), filter_text ) >= 0; // Always advance the index, but only add to filtered list if the original message matches for( ; folded_ind < folded_all.size() && folded_all[folded_ind].first == msg_ind; ++folded_ind ) { if( match ) { folded_filtered.emplace_back( folded_ind ); } } } // Reset view if( log_from_top || max_lines > folded_filtered.size() ) { offset = 0; } else { offset = folded_filtered.size() - max_lines; } }
bool Player::create_new_character() { Window w_newch(0, 0, 80, 24); cuss::interface i_newch; if (!i_newch.load_from_file(CUSS_DIR + "/i_newchar_stats.cuss")) { return false; } New_char_screen cur_screen = NCS_STATS; Stat_selected cur_stat = STATSEL_STR; int* stat_value = &(stats.strength); /* We need to set up a list of traits which does NOT include the placeholder / * marker "traits" like TRAIT_MAX_GOOD and TRAIT_MAX_NEUTRAL etc. */ std::vector<Trait_id> selectable_traits; for (int i = 1; i < TRAIT_MAX_BAD; i++) { if (i != TRAIT_MAX_GOOD && i != TRAIT_MAX_NEUTRAL) { selectable_traits.push_back( Trait_id(i) ); } } std::vector<std::string> traits_list = get_trait_list (this); std::vector<std::string> profession_list = get_profession_list(this); name = ""; int points = 4; int num_traits = 0; i_newch.ref_data("num_points", &points); i_newch.ref_data("num_strength", &stats.strength); i_newch.ref_data("num_dexterity", &stats.dexterity); i_newch.ref_data("num_perception", &stats.perception); i_newch.ref_data("num_intelligence", &stats.intelligence); i_newch.set_data("text_description", get_stat_description(cur_stat)); i_newch.set_data("text_strength", "<c=ltblue>Strength<c=/>"); i_newch.set_data("text_dexterity", "<c=ltgray>Dexterity<c=/>"); i_newch.set_data("text_perception", "<c=ltgray>Perception<c=/>"); i_newch.set_data("text_intelligence", "<c=ltgray>Intelligence<c=/>"); bool done = false; while (!done) { // We'll exit this function via keypresses, always // Always set num_points! i_newch.draw(&w_newch); w_newch.refresh(); long ch = getch(); bool changed_screen = false; if (ch == '<') { cur_screen = New_char_screen( cur_screen - 1 ); if (cur_screen == NCS_CANCEL) { if (query_yn("Cancel character creation?")) { return false; } cur_screen = NCS_STATS; } else { changed_screen = true; } } else if (ch == '>') { cur_screen = New_char_screen( cur_screen + 1 ); if (cur_screen == NCS_DONE) { std::string reason_for_fail; if (points > 0) { reason_for_fail += "\nYou have unspent points!"; } if (profession == NULL) { reason_for_fail += "\nYou didn't choose a profession!"; } if (name.empty()) { reason_for_fail += "\nYour name is blank!"; } if (!reason_for_fail.empty()) { popup("Wait, you can't start the game yet!%s", reason_for_fail.c_str()); } else if (query_yn("Complete character and start the game?")) { done = true; } cur_screen = NCS_DESCRIPTION; } else { changed_screen = true; } } else { // We should be doing this with cuss keybindings, but... that gets complex. // Maybe one day I'll update cuss to be more friendly. switch (cur_screen) { case NCS_STATS: { bool changed_stat = false; switch (ch) { case '2': case 'j': case KEY_DOWN: if (cur_stat == STATSEL_INT) { cur_stat = STATSEL_STR; } else { cur_stat = Stat_selected( cur_stat + 1 ); } changed_stat = true; break; case '8': case 'k': case KEY_UP: if (cur_stat == STATSEL_STR) { cur_stat = STATSEL_INT; } else { cur_stat = Stat_selected( cur_stat - 1 ); } changed_stat = true; break; case '4': case 'h': case KEY_LEFT: if (*stat_value > 4) { if (*stat_value > 16) { points++; // Stats above 16 cost 2 points, so get extra back } points++; (*stat_value)--; } break; case '6': case 'l': case KEY_RIGHT: { int point_req = (*stat_value >= 16 ? 2 : 1); if (*stat_value < 20 && points >= point_req) { points -= point_req; (*stat_value)++; } } break; } // switch (ch) if (changed_stat) { // Update stat_value i_newch.set_data("text_strength", "<c=ltgray>Strength<c=/>"); i_newch.set_data("text_dexterity", "<c=ltgray>Dexterity<c=/>"); i_newch.set_data("text_perception", "<c=ltgray>Perception<c=/>"); i_newch.set_data("text_intelligence","<c=ltgray>Intelligence<c=/>"); i_newch.set_data("text_description", get_stat_description(cur_stat)); switch (cur_stat) { case STATSEL_STR: stat_value = &(stats.strength); i_newch.set_data("text_strength", "<c=ltblue>Strength<c=/>"); break; case STATSEL_DEX: stat_value = &(stats.dexterity); i_newch.set_data("text_dexterity", "<c=ltblue>Dexterity<c=/>"); break; case STATSEL_PER: stat_value = &(stats.perception); i_newch.set_data("text_perception", "<c=ltblue>Perception<c=/>"); break; case STATSEL_INT: stat_value = &(stats.intelligence); i_newch.set_data("text_intelligence", "<c=ltblue>Intelligence<c=/>"); break; } } } break; case NCS_TRAITS: { switch (ch) { case '2': case 'j': case KEY_DOWN: { i_newch.add_data("list_traits", 1); int sel = i_newch.get_int("list_traits"); Trait_id cur_trait = selectable_traits[sel]; i_newch.set_data("num_cost", abs(trait_cost(cur_trait))); if (trait_cost(cur_trait) >= 0) { i_newch.set_data("text_cost", "<c=yellow>Cost:<c=/>"); } else { i_newch.set_data("text_cost", "<c=yellow>Earns:<c=/>"); } if (trait_cost(cur_trait) > points) { i_newch.set_data("num_cost", c_red); } else { i_newch.set_data("num_cost", c_white); } i_newch.set_data("text_description", trait_description(cur_trait)); } break; case '8': case 'k': case KEY_UP: { i_newch.add_data("list_traits", -1); int sel = i_newch.get_int("list_traits"); Trait_id cur_trait = selectable_traits[sel]; i_newch.set_data("num_cost", abs(trait_cost(cur_trait))); if (trait_cost(cur_trait) >= 0) { i_newch.set_data("text_cost", "<c=yellow>Cost:<c=/>"); } else { i_newch.set_data("text_cost", "<c=yellow>Earns:<c=/>"); } if (trait_cost(cur_trait) > points) { i_newch.set_data("num_cost", c_red); } else { i_newch.set_data("num_cost", c_white); } i_newch.set_data("text_description", trait_description(cur_trait)); } break; case '\n': case ' ': { int sel = i_newch.get_int("list_traits"); Trait_id cur_trait = selectable_traits[sel]; if (has_trait(cur_trait)) { traits[cur_trait] = false; points += trait_cost(cur_trait); num_traits--; traits_list = get_trait_list(this); } else if (points >= trait_cost(cur_trait) && num_traits < 5){ traits[cur_trait] = true; points -= trait_cost(cur_trait); num_traits++; traits_list = get_trait_list(this); } i_newch.set_data("num_traits_left", 5 - num_traits); if (num_traits >= 5) { i_newch.set_data("num_traits_left", c_red); } } break; } // switch (ch) } break; case NCS_PROFESSION: { switch (ch) { case '2': case 'j': case KEY_DOWN: { i_newch.add_data("list_professions", 1); std::string prof_name = i_newch.get_str("list_professions"); prof_name = remove_color_tags(prof_name); Profession* cur_prof = PROFESSIONS.lookup_name(prof_name); if (!cur_prof) { debugmsg("No such profession as '%s'!", prof_name.c_str()); return false; } i_newch.set_data("text_description", cur_prof->description); } break; case '8': case 'k': case KEY_UP: { i_newch.add_data("list_professions", -1); std::string prof_name = i_newch.get_str("list_professions"); prof_name = remove_color_tags(prof_name); Profession* cur_prof = PROFESSIONS.lookup_name(prof_name); if (!cur_prof) { debugmsg("No such profession as '%s'!", prof_name.c_str()); return false; } i_newch.set_data("text_description", cur_prof->description); } break; case '\n': case ' ': { std::string prof_name = i_newch.get_str("list_professions"); prof_name = remove_color_tags(prof_name); Profession* cur_prof = PROFESSIONS.lookup_name(prof_name); if (!cur_prof) { debugmsg("No such profession as '%s'!", prof_name.c_str()); return false; } set_profession(cur_prof); profession_list = get_profession_list(this); } break; } // switch (ch) } break; case NCS_DESCRIPTION: { if (ch == '/') { male = !male; if (male) { i_newch.set_data("text_male", "<c=yellow>Male<c=/>"); i_newch.set_data("text_female", "<c=dkgray>Female<c=/>"); } else { i_newch.set_data("text_male", "<c=dkgray>Male<c=/>"); i_newch.set_data("text_female", "<c=yellow>Female<c=/>"); } } else { /* Let the interface handle name entry; this includes cursor movement, * backspace, etc. The only downside is that this allows entry of "invalid" * name characters like "'&^%$#@ etc. Bad? */ cuss::element* entry = i_newch.find_by_name("entry_name"); entry->handle_keypress(ch); } } break; } // switch (cur_screen) } // key pressed isn't '<' or '>' if (changed_screen) { std::string filename = CUSS_DIR + "/i_newchar_"; switch (cur_screen) { case NCS_STATS: filename += "stats.cuss"; break; case NCS_TRAITS: filename += "traits.cuss"; break; case NCS_PROFESSION: filename += "profession.cuss"; break; case NCS_DESCRIPTION: filename += "description.cuss"; break; } if (!i_newch.load_from_file(filename)) { return false; } i_newch.ref_data("num_points", &points); switch (cur_screen) { case NCS_STATS: cur_stat = STATSEL_STR; i_newch.set_data("text_strength", "<c=ltblue>Strength<c=/>"); i_newch.set_data("text_dexterity", "<c=ltgray>Dexterity<c=/>"); i_newch.set_data("text_perception", "<c=ltgray>Perception<c=/>"); i_newch.set_data("text_intelligence", "<c=ltgray>Intelligence<c=/>"); i_newch.ref_data("num_strength", &stats.strength); i_newch.ref_data("num_dexterity", &stats.dexterity); i_newch.ref_data("num_perception", &stats.perception); i_newch.ref_data("num_intelligence", &stats.intelligence); i_newch.set_data("text_description", get_stat_description(cur_stat)); break; case NCS_TRAITS: { i_newch.select("list_traits"); i_newch.ref_data("list_traits", &traits_list); int sel = i_newch.get_int("list_traits"); Trait_id cur_trait = selectable_traits[sel]; i_newch.set_data("text_description", trait_description(cur_trait)); i_newch.set_data("num_cost", abs(trait_cost(cur_trait))); if (trait_cost(cur_trait) >= 0) { i_newch.set_data("text_cost", "<c=yellow>Cost:<c=/>"); } else { i_newch.set_data("text_cost", "<c=yellow>Earns:<c=/>"); } if (trait_cost(cur_trait) > points) { i_newch.set_data("num_cost", c_red); } else { i_newch.set_data("num_cost", c_white); } i_newch.set_data("num_traits_left", 5 - num_traits); if (num_traits >= 5) { i_newch.set_data("num_traits_left", c_red); } } break; case NCS_PROFESSION: { i_newch.select("list_professions"); i_newch.ref_data("list_professions", &profession_list); std::string prof_name = i_newch.get_str("list_professions"); prof_name = remove_color_tags(prof_name); Profession* cur_prof = PROFESSIONS.lookup_name(prof_name); if (!cur_prof) { debugmsg("No such profession as '%s'!", prof_name.c_str()); return false; } i_newch.set_data("text_description", cur_prof->description); } break; case NCS_DESCRIPTION: i_newch.ref_data("entry_name", &name); if (male) { i_newch.set_data("text_male", "<c=yellow>Male<c=/>"); i_newch.set_data("text_female", "<c=dkgray>Female<c=/>"); } else { i_newch.set_data("text_male", "<c=dkgray>Male<c=/>"); i_newch.set_data("text_female", "<c=yellow>Female<c=/>"); } break; } // switch (cur_screen) } // if (changed_screen) } // Now set up our skills and equipment based on our profession if (!profession) { debugmsg("Character creation finished without a profession!"); return false; } std::vector<Item_type_chance> prof_items = profession->items.item_types; for (int i = 0; i < prof_items.size(); i++) { int count = prof_items[i].number; Item tmp_it(prof_items[i].item); for (int i = 0; i < count; i++) { if (tmp_it.get_item_class() == ITEM_CLASS_CLOTHING) { items_worn.push_back(tmp_it); } else { inventory.push_back(tmp_it); } } } skills = profession->skills; // The "Durable" trait needs to be set up here. if (has_trait(TRAIT_DURABLE)) { for (int i = 0; i < HP_PART_MAX; i++) { current_hp[i] = 115; max_hp[i] = 115; } } // Myopic characters get free glasses if (has_trait(TRAIT_MYOPIC)) { Item_type* glasses = ITEM_TYPES.lookup_name("glasses"); if (!glasses) { debugmsg("No item 'glasses' exists - required for the Myopic trait!"); return false; } Item tmp_it(glasses); items_worn.push_back(tmp_it); } return true; }
/** * Generate and refresh output */ void uimenu::show() { if (!started) { setup(); } werase(window); draw_border(window, border_color); if( !title.empty() ) { mvwprintz(window, 0, 1, border_color, "< "); wprintz( window, title_color, title ); wprintz(window, border_color, " >"); } std::string padspaces = std::string(w_width - 2 - pad_left - pad_right, ' '); const int text_lines = textformatted.size(); int estart = 1; if( !textformatted.empty() ) { for ( int i = 0; i < text_lines; i++ ) { trim_and_print( window, 1 + i, 2, getmaxx( window ) - 4, text_color, textformatted[i] ); } mvwputch(window, text_lines + 1, 0, border_color, LINE_XXXO); for ( int i = 1; i < w_width - 1; ++i) { mvwputch(window, text_lines + 1, i, border_color, LINE_OXOX); } mvwputch(window, text_lines + 1, w_width - 1, border_color, LINE_XOXX); estart += text_lines + 1; // +1 for the horizontal line. } calcStartPos( vshift, fselected, vmax, fentries.size() ); for ( int fei = vshift, si = 0; si < vmax; fei++, si++ ) { if ( fei < (int)fentries.size() ) { int ei = fentries [ fei ]; nc_color co = ( ei == selected ? hilight_color : ( entries[ ei ].enabled || entries[ei].force_color ? entries[ ei ].text_color : disabled_color ) ); if ( hilight_full ) { mvwprintz( window, estart + si, pad_left + 1, co, padspaces ); } if( entries[ ei ].hotkey >= 33 && entries[ ei ].hotkey < 126 ) { const nc_color hotkey_co = ei == selected ? hilight_color : hotkey_color; mvwprintz( window, estart + si, pad_left + 2, entries[ ei ].enabled ? hotkey_co : co, "%c", entries[ ei ].hotkey ); } if( padspaces.size() > 3 ) { // padspaces's length indicates the maximal width of the entry, it is used above to // activate the highlighting, it is used to override previous text there, but in both // cases printing starts at pad_left+1, here it starts at pad_left+4, so 3 cells less // to be used. const auto entry = utf8_wrapper( ei == selected ? remove_color_tags( entries[ ei ].txt ) : entries[ ei ].txt ); trim_and_print( window, estart + si, pad_left + 4, max_entry_len, co, "%s", entry.c_str() ); if( max_column_len && !entries[ ei ].ctxt.empty() ) { const auto centry = utf8_wrapper( ei == selected ? remove_color_tags( entries[ ei ].ctxt ) : entries[ ei ].ctxt ); trim_and_print( window, estart + si, getmaxx( window ) - max_column_len - 2, max_column_len, co, "%s", centry.c_str() ); } } mvwzstr menu_entry_extra_text = entries[ei].extratxt; if ( !menu_entry_extra_text.txt.empty() ) { mvwprintz( window, estart + si, pad_left + 1 + menu_entry_extra_text.left, menu_entry_extra_text.color, menu_entry_extra_text.txt ); } if ( menu_entry_extra_text.sym != 0 ) { mvwputch ( window, estart + si, pad_left + 1 + menu_entry_extra_text.left, menu_entry_extra_text.color, menu_entry_extra_text.sym ); } if ( callback != NULL && ei == selected ) { callback->select(ei, this); } } else { mvwprintz( window, estart + si, pad_left + 1, c_light_gray, padspaces ); } } if ( desc_enabled ) { // draw border mvwputch(window, w_height - desc_lines - 2, 0, border_color, LINE_XXXO); for ( int i = 1; i < w_width - 1; ++i) { mvwputch(window, w_height - desc_lines - 2, i, border_color, LINE_OXOX); } mvwputch(window, w_height - desc_lines - 2, w_width - 1, border_color, LINE_XOXX); // clear previous desc the ugly way for ( int y = desc_lines + 1; y > 1; --y ) { for ( int x = 2; x < w_width - 2; ++x) { mvwputch(window, w_height - y, x, text_color, " "); } } if( static_cast<size_t>( selected ) < entries.size() ){ fold_and_print( window, w_height - desc_lines - 1, 2, w_width - 4, text_color, entries[selected].desc ); } } if ( !filter.empty() ) { mvwprintz( window, w_height - 1, 2, border_color, "< %s >", filter.c_str() ); mvwprintz( window, w_height - 1, 4, text_color, filter ); } apply_scrollbar(); this->refresh(true); }
/** * Calculate sizes, populate arrays, initialize window */ void uimenu::setup() { bool w_auto = (w_width == -1 || w_width == -2 ); bool w_autofold = ( w_width == -2); // Space for a line between text and entries. Only needed if there is actually text. const int text_separator_line = text.empty() ? 0 : 1; 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; } if ( desc_enabled && !(w_auto && h_auto) ) { desc_enabled = false; // give up debugmsg( "desc_enabled without w_auto and h_auto (h: %d, w: %d)", static_cast<int>( h_auto ), static_cast<int>( w_auto ) ); } max_entry_len = 0; max_column_len = 0; std::vector<int> autoassign; int pad = pad_left + pad_right + 2; int descwidth_final = 0; // for description width guard for ( size_t i = 0; i < entries.size(); i++ ) { int txtwidth = utf8_width( remove_color_tags(entries[i].txt) ); int ctxtwidth = utf8_width( remove_color_tags(entries[i].ctxt) ); if ( txtwidth > max_entry_len ) { max_entry_len = txtwidth; } if ( ctxtwidth > max_column_len ) { max_column_len = ctxtwidth; } int clen = (ctxtwidth > 0) ? ctxtwidth + 2: 0; 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 + clen ) { w_width = txtwidth + pad + 4 + clen; } } else { if ( w_auto && w_width < txtwidth + pad + 4 + clen ) { w_width = txtwidth + pad + 4 + clen; // @todo: or +5 if header } } if ( desc_enabled ) { const int min_width = std::min( TERMX, std::max( w_width, descwidth_final ) ) - 4; const int max_width = TERMX - 4; int descwidth = find_minimum_fold_width( entries[i].desc, desc_lines, min_width, max_width ); descwidth += 4; // 2x border + 2x ' ' pad if ( descwidth_final < descwidth ) { descwidth_final = descwidth; } } if ( entries[ i ].text_color == c_red_red ) { entries[ i ].text_color = text_color; } fentries.push_back( i ); } size_t next_free_hotkey = 0; for( auto it = autoassign.begin(); it != autoassign.end() && next_free_hotkey < hotkeys.size(); ++it ) { while( next_free_hotkey < hotkeys.size() ) { const int setkey = hotkeys[next_free_hotkey]; next_free_hotkey++; if( keymap.count( setkey ) == 0 ) { entries[*it].hotkey = setkey; keymap[setkey] = *it; break; } } } if (desc_enabled) { if (descwidth_final > TERMX) { desc_enabled = false; // give up debugmsg("description would exceed terminal width (%d vs %d available)", descwidth_final, TERMX); } else if (descwidth_final > w_width) { w_width = descwidth_final; } } if(!text.empty() ) { int twidth = utf8_width( remove_color_tags(text) ); 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 (auto &l : textformatted) { const int w = utf8_width( remove_color_tags( l ) ); if ( w > realtextwidth ) { realtextwidth = w; } } if ( realtextwidth + 4 > w_width ) { w_width = realtextwidth + 4; } } } } else if ( textwidth != -1 ) { realtextwidth = textwidth; if( realtextwidth + 4 > w_width ) { w_width = realtextwidth + 4; } } if ( formattxt ) { textformatted = foldstring(text, realtextwidth); } } // shrink-to-fit if( desc_enabled ) { desc_lines = 0; for( const uimenu_entry &ent : entries ) { // -2 for borders, -2 for padding desc_lines = std::max<int>( desc_lines, foldstring( ent.desc, w_width - 4 ).size() ); } if( desc_lines <= 0 ) { desc_enabled = false; } } if( w_auto && w_width > TERMX ) { w_width = TERMX; } vmax = entries.size(); int additional_lines = 2 + text_separator_line + // add two for top & bottom borders static_cast<int>( textformatted.size() ); if( desc_enabled ) { additional_lines += desc_lines + 1; // add one for description separator line } if (h_auto) { w_height = vmax + additional_lines; } if ( w_height > TERMY ) { w_height = TERMY; } if( vmax + additional_lines > w_height ) { vmax = w_height - additional_lines; 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, %lu %d available screen rows are occupied by\n'%s\n(snip)\n%s'\nThis is probably a bug.\n", static_cast<unsigned long>( 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 ( (int)entries.size() <= vmax ) { scrollbar_auto = false; } window = catacurses::newwin( w_height, w_width, w_y, w_x ); fselected = selected; if(fselected < 0) { fselected = selected = 0; } else if(fselected >= static_cast<int>(entries.size())) { fselected = selected = static_cast<int>(entries.size()) - 1; } if(!entries.empty() && !entries[fselected].enabled) { for(size_t i = 0; i < entries.size(); ++i) { if(entries[i].enabled) { fselected = selected = i; break; } } } started = true; }
int tagless_length(const std::string &orig) { std::string tagless = remove_color_tags(orig); return tagless.length(); }
void string_input_popup::create_window() { nc_color title_color = c_light_red; nc_color desc_color = c_green; int titlesize = utf8_width( _title ); // Occupied horizontal space if( _max_length <= 0 ) { _max_length = _width; } // 2 for border (top and bottom) and 1 for the input text line. int w_height = 2 + 1; // |"w_width = width + titlesize (this text) + 5": _____ | int w_width = FULL_SCREEN_WIDTH; if( _width <= 0 ) { _width = std::max( 5, FULL_SCREEN_WIDTH - titlesize - 5 ); // Default if unspecified } else { _width = std::min( FULL_SCREEN_WIDTH - 20, _width ); w_width = _width + titlesize + 5; } std::vector<std::string> title_split = { _title }; if( w_width > FULL_SCREEN_WIDTH ) { // Out of horizontal space- wrap the title titlesize = FULL_SCREEN_WIDTH - _width - 5; w_width = FULL_SCREEN_WIDTH; for( int wraplen = w_width - 2; wraplen >= titlesize; wraplen-- ) { title_split = foldstring( _title, wraplen ); if( int( title_split.back().size() ) <= titlesize ) { break; } } w_height += int( title_split.size() ) - 1; } std::vector<std::string> descformatted; if( !_description.empty() ) { const int twidth = std::min( utf8_width( remove_color_tags( _description ) ), w_width - 4 ); descformatted = foldstring( _description, twidth ); w_height += descformatted.size(); } // length of title + border (left) + space _startx = titlesize + 2; // Below the description and below the top border _starty = 1 + descformatted.size(); if( _max_length <= 0 ) { _max_length = 1024; } _endx = w_width - 3; _position = -1; const int w_y = ( TERMY - w_height ) / 2; const int w_x = std::max( ( TERMX - w_width ) / 2, 0 ); w = catacurses::newwin( w_height, w_width, w_y, w_x ); draw_border( w ); for( size_t i = 0; i < descformatted.size(); ++i ) { trim_and_print( w, 1 + i, 1, w_width - 2, desc_color, descformatted[i] ); } for( int i = 0; i < int( title_split.size() ) - 1; i++ ) { mvwprintz( w, _starty++, i + 1, title_color, title_split[i] ); } right_print( w, _starty, w_width - titlesize - 1, title_color, title_split.back() ); _starty = w_height - 2; // The ____ looks better at the bottom right when the title folds }
void Messages::display_messages( const catacurses::window &ipk_target, const int left, const int top, const int right, const int bottom ) { if( !size() ) { return; } const int maxlength = right - left; int line = log_from_top ? top : bottom; if( log_from_top ) { for( int i = size() - 1; i >= 0; --i ) { if( line > bottom ) { break; } const game_message &m = player_messages.messages[i]; if( message_exceeds_ttl( m ) ) { break; } const nc_color col = m.get_color( player_messages.curmes ); std::string message_text = m.get_with_count(); if( !m.is_recent( player_messages.curmes ) ) { message_text = remove_color_tags( message_text ); } for( const std::string &folded : foldstring( message_text, maxlength ) ) { if( line > bottom ) { break; } // Redrawing line to ensure new messages similar to previous // messages will not be missed by screen readers wredrawln( ipk_target, line, 1 ); nc_color col_out = col; print_colored_text( ipk_target, line++, left, col_out, col, folded ); } } } else { for( int i = size() - 1; i >= 0; --i ) { if( line < top ) { break; } const game_message &m = player_messages.messages[i]; if( message_exceeds_ttl( m ) ) { break; } const nc_color col = m.get_color( player_messages.curmes ); std::string message_text = m.get_with_count(); if( !m.is_recent( player_messages.curmes ) ) { message_text = remove_color_tags( message_text ); } const auto folded_strings = foldstring( message_text, maxlength ); const auto folded_rend = folded_strings.rend(); for( auto string_iter = folded_strings.rbegin(); string_iter != folded_rend && line >= top; ++string_iter, line-- ) { // Redrawing line to ensure new messages similar to previous // messages will not be missed by screen readers wredrawln( ipk_target, line, 1 ); nc_color col_out = col; print_colored_text( ipk_target, line, left, col_out, col, *string_iter ); } } } player_messages.curmes = calendar::turn; }
/* * 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; } if ( desc_enabled && !(w_auto && h_auto) ) { desc_enabled = false; // give up debugmsg("desc_enabled without w_auto and h_auto (h: %d, w: %d)", h_auto, w_auto); } max_entry_len = 0; max_desc_len = 0; std::vector<int> autoassign; int pad = pad_left + pad_right + 2; int descwidth_final = 0; // for description width guard for ( size_t i = 0; i < entries.size(); i++ ) { int txtwidth = utf8_width(remove_color_tags( 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 ( desc_enabled ) { // subtract one from desc_lines for the reminder of the text int descwidth = utf8_width(entries[i].desc.c_str()) / (desc_lines - 1); descwidth += 4; // 2x border + 2x ' ' pad if ( descwidth_final < descwidth ) { descwidth_final = descwidth; } } if ( entries[ i ].text_color == c_red_red ) { entries[ i ].text_color = text_color; } fentries.push_back( i ); } size_t next_free_hotkey = 0; for( auto it = autoassign.begin(); it != autoassign.end() && next_free_hotkey < hotkeys.size(); ++it ) { while( next_free_hotkey < hotkeys.size() ) { const int setkey = hotkeys[next_free_hotkey]; next_free_hotkey++; if( keymap.count( setkey ) == 0 ) { entries[*it].hotkey = setkey; keymap[setkey] = *it; break; } } } if (desc_enabled) { if (descwidth_final > TERMX) { desc_enabled = false; // give up debugmsg("description would exceed terminal width (%d vs %d available)", descwidth_final, TERMX); } else if (descwidth_final > w_width) { w_width = descwidth_final; } } if (w_auto && w_width > TERMX) { w_width = TERMX; } if(!text.empty() ) { int twidth = utf8_width(remove_color_tags( 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 (auto &l : textformatted) { if ( utf8_width(l.c_str()) > realtextwidth ) { realtextwidth = utf8_width(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 (desc_enabled) { int w_height_final = w_height + desc_lines + 1; // add one for border if (w_height_final > TERMY) { desc_enabled = false; // give up debugmsg("with description height would exceed terminal height (%d vs %d available)", w_height_final, TERMY); } else { w_height = w_height_final; } } } if ( w_height > TERMY ) { w_height = TERMY; } vmax = entries.size(); if ( vmax + 3 + (int)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 ( (int)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; if(fselected < 0) { fselected = selected = 0; } else if(fselected >= static_cast<int>(entries.size())) { fselected = selected = static_cast<int>(entries.size()) - 1; } if(!entries.empty() && !entries[fselected].enabled) { for(size_t i = 0; i < entries.size(); ++i) { if(entries[i].enabled) { fselected = selected = i; break; } } } started = true; }