diff --git a/src/openrct2-ui/windows/ObjectLoadError.cpp b/src/openrct2-ui/windows/ObjectLoadError.cpp index 1e58bdd72b..ecce57249d 100644 --- a/src/openrct2-ui/windows/ObjectLoadError.cpp +++ b/src/openrct2-ui/windows/ObjectLoadError.cpp @@ -284,323 +284,294 @@ static rct_widget window_object_load_error_widgets[] = { #endif WIDGETS_END, }; - -static rct_string_id get_object_type_string(ObjectType type); -static void window_object_load_error_close(rct_window *w); -static void window_object_load_error_update(rct_window *w); -static void window_object_load_error_mouseup(rct_window *w, rct_widgetindex widgetIndex); -static void window_object_load_error_scrollgetsize(rct_window *w, int32_t scrollIndex, int32_t *width, int32_t *height); -static void window_object_load_error_scrollmouseover(rct_window *w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords); -static void window_object_load_error_scrollmousedown(rct_window *w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords); -static void window_object_load_error_paint(rct_window *w, rct_drawpixelinfo *dpi); -static void window_object_load_error_scrollpaint(rct_window *w, rct_drawpixelinfo *dpi, int32_t scrollIndex); -#ifndef DISABLE_HTTP -static void window_object_load_error_download_all(rct_window* w); -static void window_object_load_error_update_list(rct_window* w); -#endif - -static rct_window_event_list window_object_load_error_events([](auto& events) -{ - events.close = &window_object_load_error_close; - events.mouse_up = &window_object_load_error_mouseup; - events.update = &window_object_load_error_update; - events.get_scroll_size = &window_object_load_error_scrollgetsize; - events.scroll_mousedown = &window_object_load_error_scrollmousedown; - events.scroll_mouseover = &window_object_load_error_scrollmouseover; - events.paint = &window_object_load_error_paint; - events.scroll_paint = &window_object_load_error_scrollpaint; -}); // clang-format on -static std::vector _invalid_entries; -static int32_t highlighted_index = -1; -static std::string file_path; -#ifndef DISABLE_HTTP -static ObjectDownloader _objDownloader; -static bool _updatedListAfterDownload; -#endif - /** * Returns an rct_string_id that represents an rct_object_entry's type. * * Could possibly be moved out of the window file if other * uses exist and a suitable location is found. */ -static rct_string_id get_object_type_string(ObjectType type) +static constexpr rct_string_id GetStringFromObjectType(const ObjectType type) { - rct_string_id result; switch (type) { case ObjectType::Ride: - result = STR_OBJECT_SELECTION_RIDE_VEHICLES_ATTRACTIONS; - break; + return STR_OBJECT_SELECTION_RIDE_VEHICLES_ATTRACTIONS; case ObjectType::SmallScenery: - result = STR_OBJECT_SELECTION_SMALL_SCENERY; - break; + return STR_OBJECT_SELECTION_SMALL_SCENERY; case ObjectType::LargeScenery: - result = STR_OBJECT_SELECTION_LARGE_SCENERY; - break; + return STR_OBJECT_SELECTION_LARGE_SCENERY; case ObjectType::Walls: - result = STR_OBJECT_SELECTION_WALLS_FENCES; - break; + return STR_OBJECT_SELECTION_WALLS_FENCES; case ObjectType::Banners: - result = STR_OBJECT_SELECTION_PATH_SIGNS; - break; + return STR_OBJECT_SELECTION_PATH_SIGNS; case ObjectType::Paths: - result = STR_OBJECT_SELECTION_FOOTPATHS; - break; + return STR_OBJECT_SELECTION_FOOTPATHS; case ObjectType::PathBits: - result = STR_OBJECT_SELECTION_PATH_EXTRAS; - break; + return STR_OBJECT_SELECTION_PATH_EXTRAS; case ObjectType::SceneryGroup: - result = STR_OBJECT_SELECTION_SCENERY_GROUPS; - break; + return STR_OBJECT_SELECTION_SCENERY_GROUPS; case ObjectType::ParkEntrance: - result = STR_OBJECT_SELECTION_PARK_ENTRANCE; - break; + return STR_OBJECT_SELECTION_PARK_ENTRANCE; case ObjectType::Water: - result = STR_OBJECT_SELECTION_WATER; - break; + return STR_OBJECT_SELECTION_WATER; default: - result = STR_UNKNOWN_OBJECT_TYPE; + return STR_UNKNOWN_OBJECT_TYPE; } - return result; } -/** - * Returns a newline-separated string listing all object names. - * Used for placing all names on the clipboard. - */ -static void copy_object_names_to_clipboard(rct_window* w) +class ObjectLoadErrorWindow final : public rct_window { - std::stringstream stream; - for (uint16_t i = 0; i < w->no_list_items; i++) +private: + std::vector _invalidEntries; + int32_t _highlightedIndex = -1; + std::string _filePath; +#ifndef DISABLE_HTTP + ObjectDownloader _objDownloader; + bool _updatedListAfterDownload{}; + + void DownloadAllObjects() { - const auto& entry = _invalid_entries[i]; - stream << entry.GetName(); - stream << PLATFORM_NEWLINE; + if (!_objDownloader.IsDownloading()) + { + _updatedListAfterDownload = false; + _objDownloader.Begin(_invalidEntries); + } } - auto clip = stream.str(); - OpenRCT2::GetContext()->GetUiContext()->SetClipboardText(clip.c_str()); -} + void UpdateObjectList() + { + const auto entries = _objDownloader.GetDownloadedEntries(); + for (auto& de : entries) + { + _invalidEntries.erase( + std::remove_if( + _invalidEntries.begin(), _invalidEntries.end(), + [de](const ObjectEntryDescriptor& e) { return de.GetName() == e.GetName(); }), + _invalidEntries.end()); + } + no_list_items = static_cast(_invalidEntries.size()); + } +#endif + + /** + * Returns a newline-separated string listing all object names. + * Used for placing all names on the clipboard. + */ + void CopyObjectNamesToClipboard() + { + std::stringstream stream; + for (uint16_t i = 0; i < no_list_items; i++) + { + const auto& entry = _invalidEntries[i]; + stream << entry.GetName(); + stream << PLATFORM_NEWLINE; + } + + const auto clip = stream.str(); + OpenRCT2::GetContext()->GetUiContext()->SetClipboardText(clip.c_str()); + } + + void SelectObjectFromList(const int32_t index) + { + if (index < 0 || index > no_list_items) + { + selected_list_item = -1; + } + else + { + selected_list_item = index; + } + widget_invalidate(this, WIDX_SCROLL); + } + +public: + void OnOpen() override + { + widgets = window_object_load_error_widgets; + enabled_widgets = (1ULL << WIDX_CLOSE) | (1ULL << WIDX_COPY_CURRENT) | (1ULL << WIDX_COPY_ALL) + | (1ULL << WIDX_DOWNLOAD_ALL); + + WindowInitScrollWidgets(this); + colours[0] = COLOUR_LIGHT_BLUE; + colours[1] = COLOUR_LIGHT_BLUE; + colours[2] = COLOUR_LIGHT_BLUE; + } + + void OnClose() override + { + _invalidEntries.clear(); + _invalidEntries.shrink_to_fit(); + } + + void OnMouseUp(const rct_widgetindex widgetIndex) override + { + switch (widgetIndex) + { + case WIDX_CLOSE: + window_close(this); + return; + case WIDX_COPY_CURRENT: + if (selected_list_item > -1 && selected_list_item < no_list_items) + { + const auto name = std::string(_invalidEntries[selected_list_item].GetName()); + OpenRCT2::GetContext()->GetUiContext()->SetClipboardText(name.c_str()); + } + break; + case WIDX_COPY_ALL: + CopyObjectNamesToClipboard(); + break; +#ifndef DISABLE_HTTP + case WIDX_DOWNLOAD_ALL: + DownloadAllObjects(); + break; +#endif + } + } + + void OnUpdate() override + { + frame_no++; + + // Check if the mouse is hovering over the list + if (!WidgetIsHighlighted(this, WIDX_SCROLL)) + { + _highlightedIndex = -1; + widget_invalidate(this, WIDX_SCROLL); + } + +#ifndef DISABLE_HTTP + _objDownloader.Update(); + + // Remove downloaded objects from our invalid entry list + if (_objDownloader.IsDownloading()) + { + // Don't do this too often as it isn't particularly efficient + if (frame_no % 64 == 0) + { + UpdateObjectList(); + } + } + else if (!_updatedListAfterDownload) + { + UpdateObjectList(); + _updatedListAfterDownload = true; + } +#endif + } + + ScreenSize OnScrollGetSize(const int32_t scrollIndex) override + { + return ScreenSize(0, no_list_items * SCROLLABLE_ROW_HEIGHT); + } + + void OnScrollMouseDown(const int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override + { + const auto selectedItem = screenCoords.y / SCROLLABLE_ROW_HEIGHT; + SelectObjectFromList(selectedItem); + } + + void OnScrollMouseOver(const int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override + { + // Highlight item that the cursor is over, or remove highlighting if none + const auto selectedItem = screenCoords.y / SCROLLABLE_ROW_HEIGHT; + if (selectedItem < 0 || selectedItem >= no_list_items) + _highlightedIndex = -1; + else + _highlightedIndex = selectedItem; + + widget_invalidate(this, WIDX_SCROLL); + } + + void OnDraw(rct_drawpixelinfo& dpi) override + { + WindowDrawWidgets(this, &dpi); + + // Draw explanatory message + auto ft = Formatter(); + ft.Add(STR_OBJECT_ERROR_WINDOW_EXPLANATION); + DrawTextWrapped(&dpi, windowPos + ScreenCoordsXY{ 5, 18 }, WW - 10, STR_BLACK_STRING, ft); + + // Draw file name + ft = Formatter(); + ft.Add(STR_OBJECT_ERROR_WINDOW_FILE); + ft.Add(_filePath.c_str()); + DrawTextEllipsised(&dpi, { windowPos.x + 5, windowPos.y + 43 }, WW - 5, STR_BLACK_STRING, ft); + } + + void OnScrollDraw(const int32_t scrollIndex, rct_drawpixelinfo& dpi) override + { + auto dpiCoords = ScreenCoordsXY{ dpi.x, dpi.y }; + gfx_fill_rect( + &dpi, { dpiCoords, dpiCoords + ScreenCoordsXY{ dpi.width - 1, dpi.height - 1 } }, ColourMapA[colours[1]].mid_light); + const int32_t listWidth = widgets[WIDX_SCROLL].width(); + + for (int32_t i = 0; i < no_list_items; i++) + { + ScreenCoordsXY screenCoords; + screenCoords.y = i * SCROLLABLE_ROW_HEIGHT; + if (screenCoords.y > dpi.y + dpi.height) + break; + + if (screenCoords.y + SCROLLABLE_ROW_HEIGHT < dpi.y) + continue; + + const auto screenRect = ScreenRect{ { 0, screenCoords.y }, + { listWidth, screenCoords.y + SCROLLABLE_ROW_HEIGHT - 1 } }; + // If hovering over item, change the color and fill the backdrop. + if (i == selected_list_item) + gfx_fill_rect(&dpi, screenRect, ColourMapA[colours[1]].darker); + else if (i == _highlightedIndex) + gfx_fill_rect(&dpi, screenRect, ColourMapA[colours[1]].mid_dark); + else if ((i & 1) != 0) // odd / even check + gfx_fill_rect(&dpi, screenRect, ColourMapA[colours[1]].light); + + // Draw the actual object entry's name... + screenCoords.x = NAME_COL_LEFT - 3; + + const auto& entry = _invalidEntries[i]; + + auto name = entry.GetName(); + char buffer[256]; + String::Set(buffer, sizeof(buffer), name.data(), name.size()); + gfx_draw_string(&dpi, screenCoords, buffer, { COLOUR_DARK_GREEN }); + + if (entry.Generation == ObjectGeneration::DAT) + { + // ... source game ... + const auto sourceStringId = object_manager_get_source_game_string(entry.Entry.GetSourceGame()); + DrawTextBasic(&dpi, { SOURCE_COL_LEFT - 3, screenCoords.y }, sourceStringId, {}, { COLOUR_DARK_GREEN }); + } + + // ... and type + const auto type = GetStringFromObjectType(entry.GetType()); + DrawTextBasic(&dpi, { TYPE_COL_LEFT - 3, screenCoords.y }, type, {}, { COLOUR_DARK_GREEN }); + } + } + + void Initialise(utf8* path, const size_t numMissingObjects, const ObjectEntryDescriptor* missingObjects) + { + _invalidEntries = std::vector(missingObjects, missingObjects + numMissingObjects); + + // Refresh list items and path + no_list_items = static_cast(numMissingObjects); + _filePath = path; + + Invalidate(); + } +}; rct_window* window_object_load_error_open(utf8* path, size_t numMissingObjects, const ObjectEntryDescriptor* missingObjects) { - _invalid_entries = std::vector(missingObjects, missingObjects + numMissingObjects); - // Check if window is already open - rct_window* window = window_bring_to_front_by_class(WC_OBJECT_LOAD_ERROR); + auto* window = window_bring_to_front_by_class(WC_OBJECT_LOAD_ERROR); if (window == nullptr) { - window = WindowCreateCentred(WW, WH, &window_object_load_error_events, WC_OBJECT_LOAD_ERROR, 0); - - window->widgets = window_object_load_error_widgets; - window->enabled_widgets = (1ULL << WIDX_CLOSE) | (1ULL << WIDX_COPY_CURRENT) | (1ULL << WIDX_COPY_ALL) - | (1ULL << WIDX_DOWNLOAD_ALL); - - WindowInitScrollWidgets(window); - window->colours[0] = COLOUR_LIGHT_BLUE; - window->colours[1] = COLOUR_LIGHT_BLUE; - window->colours[2] = COLOUR_LIGHT_BLUE; + window = WindowCreate(WC_OBJECT_LOAD_ERROR, WW, WH, 0); } - // Refresh list items and path - window->no_list_items = static_cast(numMissingObjects); - file_path = path; + static_cast(window)->Initialise(path, numMissingObjects, missingObjects); - window->Invalidate(); return window; } - -static void window_object_load_error_close(rct_window* w) -{ - _invalid_entries.clear(); - _invalid_entries.shrink_to_fit(); -} - -static void window_object_load_error_update(rct_window* w) -{ - w->frame_no++; - - // Check if the mouse is hovering over the list - if (!WidgetIsHighlighted(w, WIDX_SCROLL)) - { - highlighted_index = -1; - widget_invalidate(w, WIDX_SCROLL); - } - -#ifndef DISABLE_HTTP - _objDownloader.Update(); - - // Remove downloaded objects from our invalid entry list - if (_objDownloader.IsDownloading()) - { - // Don't do this too often as it isn't particularly efficient - if (w->frame_no % 64 == 0) - { - window_object_load_error_update_list(w); - } - } - else if (!_updatedListAfterDownload) - { - window_object_load_error_update_list(w); - _updatedListAfterDownload = true; - } -#endif -} - -static void window_object_load_error_mouseup(rct_window* w, rct_widgetindex widgetIndex) -{ - switch (widgetIndex) - { - case WIDX_CLOSE: - window_close(w); - break; - case WIDX_COPY_CURRENT: - if (w->selected_list_item > -1 && w->selected_list_item < w->no_list_items) - { - auto name = std::string(_invalid_entries[w->selected_list_item].GetName()); - OpenRCT2::GetContext()->GetUiContext()->SetClipboardText(name.c_str()); - } - break; - case WIDX_COPY_ALL: - copy_object_names_to_clipboard(w); - break; -#ifndef DISABLE_HTTP - case WIDX_DOWNLOAD_ALL: - window_object_load_error_download_all(w); - break; -#endif - } -} - -static void window_object_load_error_scrollmouseover(rct_window* w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords) -{ - // Highlight item that the cursor is over, or remove highlighting if none - int32_t selected_item; - selected_item = screenCoords.y / SCROLLABLE_ROW_HEIGHT; - if (selected_item < 0 || selected_item >= w->no_list_items) - highlighted_index = -1; - else - highlighted_index = selected_item; - - widget_invalidate(w, WIDX_SCROLL); -} - -static void window_object_load_error_select_element_from_list(rct_window* w, int32_t index) -{ - if (index < 0 || index > w->no_list_items) - { - w->selected_list_item = -1; - } - else - { - w->selected_list_item = index; - } - widget_invalidate(w, WIDX_SCROLL); -} - -static void window_object_load_error_scrollmousedown(rct_window* w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords) -{ - int32_t selected_item; - selected_item = screenCoords.y / SCROLLABLE_ROW_HEIGHT; - window_object_load_error_select_element_from_list(w, selected_item); -} - -static void window_object_load_error_scrollgetsize(rct_window* w, int32_t scrollIndex, int32_t* width, int32_t* height) -{ - *height = w->no_list_items * SCROLLABLE_ROW_HEIGHT; -} - -static void window_object_load_error_paint(rct_window* w, rct_drawpixelinfo* dpi) -{ - WindowDrawWidgets(w, dpi); - - // Draw explanatory message - auto ft = Formatter(); - ft.Add(STR_OBJECT_ERROR_WINDOW_EXPLANATION); - DrawTextWrapped(dpi, w->windowPos + ScreenCoordsXY{ 5, 18 }, WW - 10, STR_BLACK_STRING, ft); - - // Draw file name - ft = Formatter(); - ft.Add(STR_OBJECT_ERROR_WINDOW_FILE); - ft.Add(file_path.c_str()); - DrawTextEllipsised(dpi, { w->windowPos.x + 5, w->windowPos.y + 43 }, WW - 5, STR_BLACK_STRING, ft); -} - -static void window_object_load_error_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi, int32_t scrollIndex) -{ - auto dpiCoords = ScreenCoordsXY{ dpi->x, dpi->y }; - gfx_fill_rect( - dpi, { dpiCoords, dpiCoords + ScreenCoordsXY{ dpi->width - 1, dpi->height - 1 } }, ColourMapA[w->colours[1]].mid_light); - const int32_t list_width = w->widgets[WIDX_SCROLL].width(); - - for (int32_t i = 0; i < w->no_list_items; i++) - { - ScreenCoordsXY screenCoords; - screenCoords.y = i * SCROLLABLE_ROW_HEIGHT; - if (screenCoords.y > dpi->y + dpi->height) - break; - - if (screenCoords.y + SCROLLABLE_ROW_HEIGHT < dpi->y) - continue; - - auto screenRect = ScreenRect{ { 0, screenCoords.y }, { list_width, screenCoords.y + SCROLLABLE_ROW_HEIGHT - 1 } }; - // If hovering over item, change the color and fill the backdrop. - if (i == w->selected_list_item) - gfx_fill_rect(dpi, screenRect, ColourMapA[w->colours[1]].darker); - else if (i == highlighted_index) - gfx_fill_rect(dpi, screenRect, ColourMapA[w->colours[1]].mid_dark); - else if ((i & 1) != 0) // odd / even check - gfx_fill_rect(dpi, screenRect, ColourMapA[w->colours[1]].light); - - // Draw the actual object entry's name... - screenCoords.x = NAME_COL_LEFT - 3; - - const auto& entry = _invalid_entries[i]; - - auto name = entry.GetName(); - char buffer[256]; - String::Set(buffer, sizeof(buffer), name.data(), name.size()); - gfx_draw_string(dpi, screenCoords, buffer, { COLOUR_DARK_GREEN }); - - if (entry.Generation == ObjectGeneration::DAT) - { - // ... source game ... - rct_string_id sourceStringId = object_manager_get_source_game_string(entry.Entry.GetSourceGame()); - DrawTextBasic(dpi, { SOURCE_COL_LEFT - 3, screenCoords.y }, sourceStringId, {}, { COLOUR_DARK_GREEN }); - } - - // ... and type - rct_string_id type = get_object_type_string(entry.GetType()); - DrawTextBasic(dpi, { TYPE_COL_LEFT - 3, screenCoords.y }, type, {}, { COLOUR_DARK_GREEN }); - } -} - -#ifndef DISABLE_HTTP - -static void window_object_load_error_download_all(rct_window* w) -{ - if (!_objDownloader.IsDownloading()) - { - _updatedListAfterDownload = false; - _objDownloader.Begin(_invalid_entries); - } -} - -static void window_object_load_error_update_list(rct_window* w) -{ - auto entries = _objDownloader.GetDownloadedEntries(); - for (auto& de : entries) - { - _invalid_entries.erase( - std::remove_if( - _invalid_entries.begin(), _invalid_entries.end(), - [de](const ObjectEntryDescriptor& e) { return de.GetName() == e.GetName(); }), - _invalid_entries.end()); - w->no_list_items = static_cast(_invalid_entries.size()); - } -} - -#endif