diff --git a/contributors.md b/contributors.md index 7d65c44e1d..d0f675bf8b 100644 --- a/contributors.md +++ b/contributors.md @@ -68,6 +68,7 @@ The following people are not part of the project team, but have been contributin * Thomas Delebo (delebota) - Misc. * Brian Callahan (ibara) - OpenBSD port. * Jens Heuseveldt (jensj12) - Mountain tool improvements, misc. +* Park Joon-Kyu (segfault87) - Misc. ## Bug fixes * (halfbro) diff --git a/data/language/en-GB.txt b/data/language/en-GB.txt index 205268fe4f..301a0bb681 100644 --- a/data/language/en-GB.txt +++ b/data/language/en-GB.txt @@ -4477,6 +4477,9 @@ STR_6167 :{SMALLFONT}{BLACK}Advanced STR_6168 :Title Sequence STR_6169 :Scenario selection STR_6170 :Tweaks +STR_6171 :Search +STR_6172 :{SMALLFONT}{BLACK}Search +STR_6173 :Please provide the name to search: ############# # Scenarios # diff --git a/resources/g2/icons/search.png b/resources/g2/icons/search.png new file mode 100644 index 0000000000..b9df419b0e Binary files /dev/null and b/resources/g2/icons/search.png differ diff --git a/resources/g2/sprites.json b/resources/g2/sprites.json index 2aea91bb1c..00cc7b4ddb 100644 --- a/resources/g2/sprites.json +++ b/resources/g2/sprites.json @@ -350,5 +350,8 @@ "x_offset": -14, "y_offset": -2, "palette": "keep" + }, + { + "path": "icons/search.png" } ] diff --git a/src/openrct2-ui/windows/GuestList.cpp b/src/openrct2-ui/windows/GuestList.cpp index 3a807c02c1..20522d96d7 100644 --- a/src/openrct2-ui/windows/GuestList.cpp +++ b/src/openrct2-ui/windows/GuestList.cpp @@ -23,6 +23,7 @@ #include #include #include +#include enum { PAGE_INDIVIDUAL, @@ -42,7 +43,8 @@ enum WINDOW_GUEST_LIST_WIDGET_IDX { WIDX_TRACKING, WIDX_TAB_1, WIDX_TAB_2, - WIDX_GUEST_LIST + WIDX_GUEST_LIST, + WIDX_FILTER_BY_NAME }; enum { @@ -77,13 +79,14 @@ static rct_widget window_guest_list_widgets[] = { { WWT_RESIZE, 1, 0, 349, 43, 329, 0xFFFFFFFF, STR_NONE }, // tab content panel { WWT_DROPDOWN, 1, 5, 84, 59, 70, STR_PAGE_1, STR_NONE }, // page dropdown { WWT_DROPDOWN_BUTTON, 1, 73, 83, 60, 69, STR_DROPDOWN_GLYPH, STR_NONE }, // page dropdown button - { WWT_DROPDOWN, 1, 120, 295, 59, 70, 0xFFFFFFFF, STR_INFORMATION_TYPE_TIP }, // information type dropdown - { WWT_DROPDOWN_BUTTON, 1, 284, 294, 60, 69, STR_DROPDOWN_GLYPH, STR_INFORMATION_TYPE_TIP }, // information type dropdown button + { WWT_DROPDOWN, 1, 120, 285, 59, 70, 0xFFFFFFFF, STR_INFORMATION_TYPE_TIP }, // information type dropdown + { WWT_DROPDOWN_BUTTON, 1, 274, 284, 60, 69, STR_DROPDOWN_GLYPH, STR_INFORMATION_TYPE_TIP }, // information type dropdown button { WWT_FLATBTN, 1, 297, 320, 46, 69, SPR_MAP, STR_SHOW_GUESTS_ON_MAP_TIP }, // map { WWT_FLATBTN, 1, 321, 344, 46, 69, SPR_TRACK_PEEP, STR_TRACKED_GUESTS_ONLY_TIP }, // tracking { WWT_TAB, 1, 3, 33, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_INDIVIDUAL_GUESTS_TIP }, // tab 1 { WWT_TAB, 1, 34, 64, 17, 43, IMAGE_TYPE_REMAP | SPR_TAB, STR_SUMMARISED_GUESTS_TIP }, // tab 2 { WWT_SCROLL, 1, 3, 346, 72, 326, SCROLL_BOTH, STR_NONE }, // guest list + { WWT_FLATBTN, 1, 293, 316, 46, 69, SPR_G2_SEARCH, STR_GUESTS_FILTER_BY_NAME_TIP }, // filter by name { WIDGETS_END }, }; @@ -101,6 +104,7 @@ static void window_guest_list_tooltip(rct_window* w, rct_widgetindex widgetIndex static void window_guest_list_invalidate(rct_window *w); static void window_guest_list_paint(rct_window *w, rct_drawpixelinfo *dpi); static void window_guest_list_scrollpaint(rct_window *w, rct_drawpixelinfo *dpi, sint32 scrollIndex); +static void window_guest_list_textinput(rct_window *w, rct_widgetindex widgetIndex, char *text); static rct_window_event_list window_guest_list_events = { nullptr, @@ -122,7 +126,7 @@ static rct_window_event_list window_guest_list_events = { window_guest_list_scrollmousedown, nullptr, window_guest_list_scrollmouseover, - nullptr, + window_guest_list_textinput, nullptr, nullptr, window_guest_list_tooltip, @@ -153,11 +157,15 @@ static uint32 _window_guest_list_groups_argument_2[240]; static uint8 _window_guest_list_groups_guest_faces[240 * 58]; static uint8 _window_guest_list_group_index[240]; +static char _window_guest_list_filter_name[32]; + static sint32 window_guest_list_is_peep_in_filter(rct_peep* peep); static void window_guest_list_find_groups(); static void get_arguments_from_peep(rct_peep *peep, uint32 *argument_1, uint32* argument_2); +static bool guest_should_be_visible(rct_peep *peep); + void window_guest_list_init_vars() { _window_guest_list_selected_tab = 0; @@ -191,7 +199,8 @@ rct_window * window_guest_list_open() (1 << WIDX_MAP) | (1 << WIDX_TRACKING) | (1 << WIDX_TAB_1) | - (1 << WIDX_TAB_2); + (1 << WIDX_TAB_2) | + (1 << WIDX_FILTER_BY_NAME); window_init_scroll_widgets(window); _window_guest_list_highlighted_index = -1; @@ -201,7 +210,9 @@ rct_window * window_guest_list_open() _window_guest_list_selected_page = 0; _window_guest_list_num_pages = 1; _window_guest_list_tracking_only = false; + _window_guest_list_filter_name[0] = '\0'; window_guest_list_widgets[WIDX_TRACKING].type = WWT_FLATBTN; + window_guest_list_widgets[WIDX_FILTER_BY_NAME].type = WWT_FLATBTN; window_guest_list_widgets[WIDX_PAGE_DROPDOWN].type = WWT_EMPTY; window_guest_list_widgets[WIDX_PAGE_DROPDOWN_BUTTON].type = WWT_EMPTY; window->var_492 = 0; @@ -320,6 +331,25 @@ static void window_guest_list_mouseup(rct_window *w, rct_widgetindex widgetIndex window_invalidate(w); w->scrolls[0].v_top = 0; break; + case WIDX_FILTER_BY_NAME: + if (strnlen(_window_guest_list_filter_name, sizeof(_window_guest_list_filter_name)) > 0) + { + // Unset the search filter. + _window_guest_list_filter_name[0] = '\0'; + w->pressed_widgets &= ~(1 << WIDX_FILTER_BY_NAME); + } + else + { + window_text_input_open( + w, + WIDX_FILTER_BY_NAME, + STR_GUESTS_FILTER_BY_NAME, + STR_GUESTS_ENTER_NAME_TO_SEARCH, + STR_STRING, + (uintptr_t) &_window_guest_list_filter_name, + sizeof(_window_guest_list_filter_name)); + } + break; } } @@ -358,8 +388,12 @@ static void window_guest_list_mousedown(rct_window *w, rct_widgetindex widgetInd _window_guest_list_selected_page = 0; _window_guest_list_num_pages = 1; window_guest_list_widgets[WIDX_TRACKING].type = WWT_EMPTY; + window_guest_list_widgets[WIDX_FILTER_BY_NAME].type = WWT_EMPTY; if (_window_guest_list_selected_tab == PAGE_INDIVIDUAL) + { window_guest_list_widgets[WIDX_TRACKING].type = WWT_FLATBTN; + window_guest_list_widgets[WIDX_FILTER_BY_NAME].type = WWT_FLATBTN; + } window_guest_list_widgets[WIDX_PAGE_DROPDOWN].type = WWT_EMPTY; window_guest_list_widgets[WIDX_PAGE_DROPDOWN_BUTTON].type = WWT_EMPTY; w->list_information_type = 0; @@ -468,7 +502,7 @@ static void window_guest_list_scrollgetsize(rct_window *w, sint32 scrollIndex, s if (_window_guest_list_selected_filter != -1) if (window_guest_list_is_peep_in_filter(peep)) continue; - if (_window_guest_list_tracking_only && !(peep->peep_flags & PEEP_FLAGS_TRACKING)) + if (!guest_should_be_visible(peep)) continue; numGuests++; } @@ -534,7 +568,7 @@ static void window_guest_list_scrollmousedown(rct_window *w, sint32 scrollIndex, if (_window_guest_list_selected_filter != -1) if (window_guest_list_is_peep_in_filter(peep)) continue; - if (_window_guest_list_tracking_only && !(peep->peep_flags & PEEP_FLAGS_TRACKING)) + if (!guest_should_be_visible(peep)) continue; if (i == 0) { @@ -614,6 +648,8 @@ static void window_guest_list_invalidate(rct_window *w) window_guest_list_widgets[WIDX_PAGE_DROPDOWN].text = pageNames[_window_guest_list_selected_page]; window_guest_list_widgets[WIDX_TRACKING].left = 321 - 350 + w->width; window_guest_list_widgets[WIDX_TRACKING].right = 344 - 350 + w->width; + window_guest_list_widgets[WIDX_FILTER_BY_NAME].left = 293 - 350 + w->width; + window_guest_list_widgets[WIDX_FILTER_BY_NAME].right = 316 - 350 + w->width; if (_window_guest_list_num_pages > 1) { window_guest_list_widgets[WIDX_PAGE_DROPDOWN].type = WWT_DROPDOWN; @@ -713,7 +749,7 @@ static void window_guest_list_scrollpaint(rct_window *w, rct_drawpixelinfo *dpi, gWindowMapFlashingFlags |= (1 << 0); sprite_set_flashing((rct_sprite*)peep, true); } - if (_window_guest_list_tracking_only && !(peep->peep_flags & PEEP_FLAGS_TRACKING)) + if (!guest_should_be_visible(peep)) continue; // Check if y is beyond the scroll control @@ -814,6 +850,14 @@ static void window_guest_list_scrollpaint(rct_window *w, rct_drawpixelinfo *dpi, } } +static void window_guest_list_textinput(rct_window *w, rct_widgetindex widgetIndex, char *text) +{ + if (text != nullptr && text[0] != '\0') + { + strncpy(_window_guest_list_filter_name, text, sizeof(_window_guest_list_filter_name)); + w->pressed_widgets |= (1 << WIDX_FILTER_BY_NAME); + } +} /** * returns 0 for in filter and 1 for not in filter @@ -998,3 +1042,23 @@ static void window_guest_list_find_groups() } while (++swap_position <= groupIndex); } } + +static bool guest_should_be_visible(rct_peep *peep) +{ + if (_window_guest_list_tracking_only && !(peep->peep_flags & PEEP_FLAGS_TRACKING)) + return false; + + if (_window_guest_list_filter_name[0] != '\0') + { + char formatted[256]; + + set_format_arg(0, rct_string_id, peep->name_string_idx); + set_format_arg(2, uint32, peep->id); + format_string(formatted, sizeof(formatted), peep->name_string_idx, gCommonFormatArgs); + + if (strcasestr(formatted, _window_guest_list_filter_name) == nullptr) + return false; + } + + return true; +} diff --git a/src/openrct2/localisation/string_ids.h b/src/openrct2/localisation/string_ids.h index a36976293d..b0dde5bf82 100644 --- a/src/openrct2/localisation/string_ids.h +++ b/src/openrct2/localisation/string_ids.h @@ -3818,6 +3818,10 @@ enum { STR_OPTIONS_SCENARIO_SELECTION = 6169, STR_OPTIONS_TWEAKS = 6170, + STR_GUESTS_FILTER_BY_NAME = 6171, + STR_GUESTS_FILTER_BY_NAME_TIP = 6172, + STR_GUESTS_ENTER_NAME_TO_SEARCH = 6173, + // Have to include resource strings (from scenarios and objects) for the time being now that language is partially working STR_COUNT = 32768 }; diff --git a/src/openrct2/sprites.h b/src/openrct2/sprites.h index e96761dbc8..b4f8ba827c 100644 --- a/src/openrct2/sprites.h +++ b/src/openrct2/sprites.h @@ -822,6 +822,8 @@ enum { SPR_G2_MINIATURE_RAILWAY_INSET_END_NW_SE = SPR_G2_MINIATURE_RAILWAY_BEGIN + 11, SPR_G2_MINIATURE_RAILWAY_LAST = SPR_G2_BEGIN + 105, + SPR_G2_SEARCH = SPR_G2_BEGIN + 106, + // 0x60000, chosen because it's a round hex number // of the last possible range of image ID values that is large enough to fit all csg1 sprites. SPR_CSG_BEGIN = 393216, diff --git a/src/openrct2/util/Util.cpp b/src/openrct2/util/Util.cpp index 799735ec59..cb346e1f3f 100644 --- a/src/openrct2/util/Util.cpp +++ b/src/openrct2/util/Util.cpp @@ -415,6 +415,44 @@ char *safe_strtrimleft(char *destination, const char *source, size_t size) return safe_strcpy(destination, source, size); } +#if !defined(_GNU_SOURCE) +char * strcasestr(const char * haystack, const char * needle) +{ + const char * p1 = haystack; + const char * p2 = needle; + const char * r = *p2 == 0 ? haystack : 0; + + while (*p1 != 0 && *p2 != 0) { + if (tolower((unsigned char) *p1) == tolower((unsigned char) *p2)) + { + if (r == 0) + r = p1; + p2++; + } + else + { + p2 = needle; + if (r != 0) + p1 = r + 1; + + if (tolower((unsigned char) *p1) == tolower((unsigned char) *p2)) + { + r = p1; + p2++; + } + else + { + r = 0; + } + } + + p1++; + } + + return *p2 == 0 ? (char *) r : 0; +} +#endif + bool utf8_is_bom(const char *str) { return str[0] == (char)(uint8)0xEF && str[1] == (char)(uint8)0xBB && str[2] == (char)(uint8)0xBF; diff --git a/src/openrct2/util/Util.h b/src/openrct2/util/Util.h index 2f2e13e330..2a9fc6df02 100644 --- a/src/openrct2/util/Util.h +++ b/src/openrct2/util/Util.h @@ -52,6 +52,9 @@ char *safe_strcpy(char * destination, const char * source, size_t num); char *safe_strcat(char *destination, const char *source, size_t size); char *safe_strcat_path(char *destination, const char *source, size_t size); char *safe_strtrimleft(char *destination, const char *source, size_t size); +#if !defined(_GNU_SOURCE) +char * strcasestr(const char * haystack, const char * needle); +#endif bool utf8_is_bom(const char *str); bool str_is_null_or_empty(const char *str);