diff --git a/plugins/listcolumn.h b/plugins/listcolumn.h index e90f2f5fb..0576ca4e4 100644 --- a/plugins/listcolumn.h +++ b/plugins/listcolumn.h @@ -134,6 +134,29 @@ public: } } + virtual void tokenizeSearch (vector *dest, const string search) + { + if (!search.empty()) + split_string(dest, search, " "); + } + + virtual bool showEntry(const ListEntry *entry, const vector &search_tokens) + { + if (!search_tokens.empty()) + { + string item_string = toLower(entry->text); + for (auto si = search_tokens.begin(); si != search_tokens.end(); si++) + { + if (!si->empty() && item_string.find(*si) == string::npos && + entry->keywords.find(*si) == string::npos) + { + return false; + } + } + } + return true; + } + void filterDisplay() { ListEntry *prev_selected = (getDisplayListSize() > 0) ? display_list[highlighted_index] : NULL; @@ -141,29 +164,13 @@ public: search_string = toLower(search_string); vector search_tokens; - if (!search_string.empty()) - split_string(&search_tokens, search_string, " "); + tokenizeSearch(&search_tokens, search_string); for (size_t i = 0; i < list.size(); i++) { ListEntry *entry = &list[i]; - bool include_item = true; - if (!search_string.empty()) - { - string item_string = toLower(list[i].text); - for (auto si = search_tokens.begin(); si != search_tokens.end(); si++) - { - if (!si->empty() && item_string.find(*si) == string::npos && - list[i].keywords.find(*si) == string::npos) - { - include_item = false; - break; - } - } - } - - if (include_item) + if (showEntry(entry, search_tokens)) { display_list.push_back(entry); if (entry == prev_selected) @@ -347,14 +354,19 @@ public: return list.size(); } + virtual bool validSearchInput (unsigned char c) + { + return (c >= 'a' && c <= 'z') || c == ' '; + } + bool feed(set *input) { feed_mouse_set_highlight = feed_changed_highlight = false; - if (input->count(interface_key::CURSOR_UP)) + if (input->count(interface_key::STANDARDSCROLL_UP)) { changeHighlight(-1); } - else if (input->count(interface_key::CURSOR_DOWN)) + else if (input->count(interface_key::STANDARDSCROLL_DOWN)) { changeHighlight(1); } @@ -383,7 +395,7 @@ public: // Search query typing mode always on df::interface_key last_token = get_string_key(input); int charcode = Screen::keyToChar(last_token); - if ((charcode >= 96 && charcode <= 123) || charcode == 32) + if (charcode >= 0 && validSearchInput((unsigned char)charcode)) { // Standard character search_string += char(charcode); diff --git a/plugins/stocks.cpp b/plugins/stocks.cpp index d6611e1de..85fedb9f2 100644 --- a/plugins/stocks.cpp +++ b/plugins/stocks.cpp @@ -599,8 +599,115 @@ class StockListColumn : public ListColumn OutputString(color, x, y, get_quality_name(quality)); } } + + virtual bool validSearchInput (unsigned char c) + { + switch (c) + { + case '(': + case ')': + return true; + break; + default: + break; + } + string &search_string = ListColumn::search_string; + if (c == '^' && !search_string.size()) + return true; + else if (c == '$' && search_string.size()) + { + if (search_string == "^") + return false; + if (search_string[search_string.size() - 1] != '$') + return true; + } + return ListColumn::validSearchInput(c); + } + + std::string getRawSearch(const std::string s) + { + string raw_search = s; + if (raw_search.size() && raw_search[0] == '^') + raw_search.erase(0, 1); + if (raw_search.size() && raw_search[raw_search.size() - 1] == '$') + raw_search.erase(raw_search.size() - 1, 1); + return toLower(raw_search); + } + + virtual void tokenizeSearch (vector *dest, const string search) + { + string raw_search = getRawSearch(search); + ListColumn::tokenizeSearch(dest, raw_search); + } + + virtual bool showEntry (const ListEntry *entry, const vector &search_tokens) + { + string &search_string = ListColumn::search_string; + if (!search_string.size()) + return true; + + bool match_start = false, match_end = false; + string raw_search = getRawSearch(search_string); + if (search_string.size() && search_string[0] == '^') + match_start = true; + if (search_string.size() && search_string[search_string.size() - 1] == '$') + match_end = true; + + if (!ListColumn::showEntry(entry, search_tokens)) + return false; + + string item_name = toLower(Items::getDescription(entry->elem->entries[0], 0, false)); + + if ((match_start || match_end) && raw_search.size() > item_name.size()) + return false; + if (match_start && item_name.compare(0, raw_search.size(), raw_search) != 0) + return false; + if (match_end && item_name.compare(item_name.size() - raw_search.size(), raw_search.size(), raw_search) != 0) + return false; + + return true; + } }; +class search_help : public dfhack_viewscreen +{ +public: + void feed (std::set *input) + { + if (input->count(interface_key::HELP)) + return; + if (Screen::isDismissed(this)) + return; + Screen::dismiss(this); + if (!input->count(interface_key::LEAVESCREEN) && !input->count(interface_key::SELECT)) + parent->feed(input); + } + void render() + { + static std::string text = + "\7 Flag names can be\n" + " searched for - e.g. job,\n" + " inventory, dump, forbid\n" + "\n" + "\7 Use ^ to match the start\n" + " of a name, and/or $ to\n" + " match the end of a name"; + if (Screen::isDismissed(this)) + return; + parent->render(); + int left_margin = gps->dimx - SIDEBAR_WIDTH; + int x = left_margin, y = 2; + Screen::fillRect(Screen::Pen(' ', 0, 0), left_margin - 1, 1, gps->dimx - 2, gps->dimy - 4); + Screen::fillRect(Screen::Pen(' ', 0, 0), left_margin - 1, 1, left_margin - 1, gps->dimy - 2); + OutputString(COLOR_WHITE, x, y, "Search help", true, left_margin); + ++y; + vector lines; + split_string(&lines, text, "\n"); + for (auto line = lines.begin(); line != lines.end(); ++line) + OutputString(COLOR_WHITE, x, y, line->c_str(), true, left_margin); + } + std::string getFocusString() { return "stocks_view/search_help"; } +}; class ViewscreenStocks : public dfhack_viewscreen { @@ -662,6 +769,10 @@ public: Screen::dismiss(this); return; } + else if (input->count(interface_key::HELP)) + { + Screen::show(new search_help); + } bool key_processed = false; switch (selected_column) @@ -921,19 +1032,18 @@ public: ++y; OutputHotkeyString(x, y, "Clear All", "Shift-C", true, left_margin); OutputHotkeyString(x, y, "Enable All", "Shift-E", true, left_margin); - OutputHotkeyString(x, y, "Toggle Grouping", "TAB", true, left_margin); + OutputHotkeyString(x, y, "Toggle Grouping", interface_key::CHANGETAB, true, left_margin); ++y; - OutputString(COLOR_WHITE, x, y, "Qual: "); - OutputHotkeyString(x, y, "Min: ", "-+"); - OutputString(COLOR_BROWN, x, y, get_quality_name(min_quality), false, left_margin); - ++x; - OutputHotkeyString(x, y, "Max: ", "/*"); + OutputHotkeyString(x, y, "Min Qual: ", "-+"); + OutputString(COLOR_BROWN, x, y, get_quality_name(min_quality), true, left_margin); + OutputHotkeyString(x, y, "Max Qual: ", "/*"); OutputString(COLOR_BROWN, x, y, get_quality_name(max_quality), true, left_margin); OutputHotkeyString(x, y, "Min Wear: ", "Shift-W"); OutputString(COLOR_BROWN, x, y, int_to_string(min_wear), true, left_margin); - ++y; + if (gps->dimy > 27) + ++y; OutputString(COLOR_BROWN, x, y, "Actions ("); OutputString(COLOR_LIGHTGREEN, x, y, int_to_string(items_column.getDisplayedListSize())); OutputString(COLOR_BROWN, x, y, " Items)", true, left_margin); @@ -941,14 +1051,13 @@ public: OutputHotkeyString(x, y, "Dump", "-D", true, left_margin); OutputHotkeyString(x, y, "Forbid ", "Shift-F", false, left_margin); OutputHotkeyString(x, y, "Melt", "-M", true, left_margin); + OutputHotkeyString(x, y, "Mark for Trade", "Shift-T", true, left_margin, + depot_info.canTrade() ? COLOR_WHITE : COLOR_DARKGREY); OutputHotkeyString(x, y, "Apply to: ", "Shift-A"); OutputString(COLOR_BROWN, x, y, (apply_to_all) ? "Listed" : "Selected", true, left_margin); - if (depot_info.canTrade()) - OutputHotkeyString(x, y, "Mark for Trade", "Shift-T", true, left_margin); - y = gps->dimy - 5; - OutputString(COLOR_LIGHTRED, x, y, "Flag names can also", true, left_margin); - OutputString(COLOR_LIGHTRED, x, y, "be searched for", true, left_margin); + y = gps->dimy - 4; + OutputHotkeyString(x, y, "Search help", interface_key::HELP, true, left_margin); } std::string getFocusString() { return "stocks_view"; } diff --git a/plugins/uicommon.h b/plugins/uicommon.h index a54b3aa04..7182ee2f2 100644 --- a/plugins/uicommon.h +++ b/plugins/uicommon.h @@ -112,6 +112,12 @@ static void OutputHotkeyString(int &x, int &y, const char *text, const char *hot OutputString(text_color, x, y, display, newline, left_margin); } +static void OutputHotkeyString(int &x, int &y, const char *text, df::interface_key hotkey, + bool newline = false, int left_margin = 0, int8_t text_color = COLOR_WHITE, int8_t hotkey_color = COLOR_LIGHTGREEN) +{ + OutputHotkeyString(x, y, text, DFHack::Screen::getKeyDisplay(hotkey).c_str(), newline, left_margin, text_color, hotkey_color); +} + static void OutputLabelString(int &x, int &y, const char *text, const char *hotkey, const string &label, bool newline = false, int left_margin = 0, int8_t text_color = COLOR_WHITE, int8_t hotkey_color = COLOR_LIGHTGREEN) { @@ -142,6 +148,12 @@ static void OutputToggleString(int &x, int &y, const char *text, const char *hot OutputString(COLOR_GREY, x, y, "Off", newline, left_margin); } +static void OutputToggleString(int &x, int &y, const char *text, df::interface_key hotkey, bool state, bool newline = true, + int left_margin = 0, int8_t color = COLOR_WHITE, int8_t hotkey_color = COLOR_LIGHTGREEN) +{ + OutputToggleString(x, y, text, DFHack::Screen::getKeyDisplay(hotkey).c_str(), state, newline, left_margin, color, hotkey_color); +} + inline string int_to_string(const int n) { return static_cast( &(ostringstream() << n) )->str();