diff options
author | glx22 <glx@openttd.org> | 2021-05-15 23:12:25 +0200 |
---|---|---|
committer | Loïc Guilloux <glx22@users.noreply.github.com> | 2021-05-29 21:08:25 +0200 |
commit | 994bf19aef3dde6c9482434bdc51688f76a937ea (patch) | |
tree | 9b45b82d460cb00733144ba0dd64ebc6fde478c2 /src | |
parent | ef991b1772f5f8b4874f76ea715a9a95811ad979 (diff) | |
download | openttd-994bf19aef3dde6c9482434bdc51688f76a937ea.tar.xz |
Fix f6d5c01: Delay deletion when closing windows
Diffstat (limited to 'src')
35 files changed, 231 insertions, 166 deletions
diff --git a/src/ai/ai_gui.cpp b/src/ai/ai_gui.cpp index 49e37621e..1817148f4 100644 --- a/src/ai/ai_gui.cpp +++ b/src/ai/ai_gui.cpp @@ -196,7 +196,7 @@ struct AIListWindow : public Window { this->SetDirty(); if (click_count > 1) { this->ChangeAI(); - delete this; + this->Close(); } } break; @@ -204,12 +204,12 @@ struct AIListWindow : public Window { case WID_AIL_ACCEPT: { this->ChangeAI(); - delete this; + this->Close(); break; } case WID_AIL_CANCEL: - delete this; + this->Close(); break; } } @@ -227,7 +227,7 @@ struct AIListWindow : public Window { void OnInvalidateData(int data = 0, bool gui_scope = true) override { if (_game_mode == GM_NORMAL && Company::IsValidID(this->slot)) { - delete this; + this->Close(); return; } @@ -518,7 +518,7 @@ struct AISettingsWindow : public Window { } case WID_AIS_ACCEPT: - delete this; + this->Close(); break; case WID_AIS_RESET: @@ -655,7 +655,7 @@ struct ScriptTextfileWindow : public TextfileWindow { { const char *textfile = GetConfig(slot)->GetTextfile(file_type, slot); if (textfile == nullptr) { - delete this; + this->Close(); } else { this->LoadTextfile(textfile, (slot == OWNER_DEITY) ? GAME_DIR : AI_DIR); } @@ -744,10 +744,11 @@ struct AIConfigWindow : public Window { this->OnInvalidateData(0); } - ~AIConfigWindow() + void Close() override { DeleteWindowByClass(WC_AI_LIST); DeleteWindowByClass(WC_AI_SETTINGS); + this->Window::Close(); } void SetStringParameters(int widget) const override @@ -929,7 +930,7 @@ struct AIConfigWindow : public Window { break; case WID_AIC_CLOSE: - delete this; + this->Close(); break; case WID_AIC_CONTENT_DOWNLOAD: diff --git a/src/airport_gui.cpp b/src/airport_gui.cpp index 7eb8c6dce..1941a8602 100644 --- a/src/airport_gui.cpp +++ b/src/airport_gui.cpp @@ -76,10 +76,11 @@ struct BuildAirToolbarWindow : Window { this->last_user_action = WIDGET_LIST_END; } - ~BuildAirToolbarWindow() + void Close() override { if (this->IsWidgetLowered(WID_AT_AIRPORT)) SetViewportCatchmentStation(nullptr, true); if (_settings_client.gui.link_terraform_toolbar) DeleteWindowById(WC_SCEN_LAND_GEN, 0, false); + this->Window::Close(); } /** @@ -276,9 +277,10 @@ public: if (selectFirstAirport) this->SelectFirstAvailableAirport(true); } - virtual ~BuildAirportWindow() + void Close() override { DeleteWindowById(WC_SELECT_STATION, 0); + this->PickerWindowBase::Close(); } void SetStringParameters(int widget) const override diff --git a/src/bootstrap_gui.cpp b/src/bootstrap_gui.cpp index 7495e08ba..d8e4aedec 100644 --- a/src/bootstrap_gui.cpp +++ b/src/bootstrap_gui.cpp @@ -91,9 +91,10 @@ public: this->InitNested(1); } - ~BootstrapErrorWindow() + void Close() override { _exit_game = true; + this->Window::Close(); } void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override @@ -144,12 +145,13 @@ public: { } - ~BootstrapContentDownloadStatusWindow() + void Close() override { /* If we are not set to exit the game, it means the bootstrap failed. */ if (!_exit_game) { new BootstrapErrorWindow(); } + this->BaseNetworkContentDownloadStatusWindow::Close(); } void OnDownloadComplete(ContentID cid) override @@ -162,7 +164,7 @@ public: /* _exit_game is used to break out of the outer video driver's MainLoop. */ _exit_game = true; - delete this; + this->Close(); } }; @@ -201,9 +203,10 @@ public: } /** Stop listening to the content client events. */ - ~BootstrapAskForDownloadWindow() + void Close() override { _network_content_client.RemoveCallback(this); + this->Window::Close(); } void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override @@ -264,7 +267,7 @@ public: /* And once the meta data is received, start downloading it. */ _network_content_client.Select(ci->id); new BootstrapContentDownloadStatusWindow(); - delete this; + this->Close(); } }; diff --git a/src/bridge_gui.cpp b/src/bridge_gui.cpp index 07858e7af..4a9a56b79 100644 --- a/src/bridge_gui.cpp +++ b/src/bridge_gui.cpp @@ -249,7 +249,7 @@ public: if (i < 9 && i < this->bridges->size()) { /* Build the requested bridge */ this->BuildBridge(i); - delete this; + this->Close(); return ES_HANDLED; } return ES_NOT_HANDLED; @@ -263,7 +263,7 @@ public: uint i = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_BBS_BRIDGE_LIST); if (i < this->bridges->size()) { this->BuildBridge(i); - delete this; + this->Close(); } break; } diff --git a/src/company_gui.cpp b/src/company_gui.cpp index a23bda7bd..fe8c2326c 100644 --- a/src/company_gui.cpp +++ b/src/company_gui.cpp @@ -1586,7 +1586,7 @@ public: /* Cancel button */ case WID_SCMF_CANCEL: - delete this; + this->Close(); break; /* Load button */ @@ -2767,7 +2767,7 @@ struct BuyCompanyWindow : Window { { switch (widget) { case WID_BC_NO: - delete this; + this->Close(); break; case WID_BC_YES: diff --git a/src/console_gui.cpp b/src/console_gui.cpp index 731073866..e5d80fcba 100644 --- a/src/console_gui.cpp +++ b/src/console_gui.cpp @@ -185,10 +185,11 @@ struct IConsoleWindow : Window ResizeWindow(this, _screen.width, _screen.height / 3); } - ~IConsoleWindow() + void Close() override { _iconsole_mode = ICONSOLE_CLOSED; VideoDriver::GetInstance()->EditBoxLostFocus(); + this->Window::Close(); } /** diff --git a/src/date_gui.cpp b/src/date_gui.cpp index 8feb3f8ad..77b9a7c4c 100644 --- a/src/date_gui.cpp +++ b/src/date_gui.cpp @@ -147,7 +147,7 @@ struct SetDateWindow : Window { case WID_SD_SET_DATE: if (this->callback != nullptr) this->callback(this, ConvertYMDToDate(this->date.year, this->date.month, this->date.day)); - delete this; + this->Close(); break; } } diff --git a/src/depot_gui.cpp b/src/depot_gui.cpp index 0756eae2c..80774e900 100644 --- a/src/depot_gui.cpp +++ b/src/depot_gui.cpp @@ -291,11 +291,12 @@ struct DepotWindow : Window { OrderBackup::Reset(); } - ~DepotWindow() + void Close() override { DeleteWindowById(WC_BUILD_VEHICLE, this->window_number); DeleteWindowById(GetWindowClassForVehicleType(this->type), VehicleListIdentifier(VL_DEPOT_LIST, this->type, this->owner, this->GetDepotIndex()).Pack(), false); OrderBackup::Reset(this->window_number); + this->Window::Close(); } /** diff --git a/src/dock_gui.cpp b/src/dock_gui.cpp index 850b9d3f3..483a71d6b 100644 --- a/src/dock_gui.cpp +++ b/src/dock_gui.cpp @@ -101,10 +101,11 @@ struct BuildDocksToolbarWindow : Window { if (_settings_client.gui.link_terraform_toolbar) ShowTerraformToolbar(this); } - ~BuildDocksToolbarWindow() + void Close() override { if (_game_mode == GM_NORMAL && this->IsWidgetLowered(WID_DT_STATION)) SetViewportCatchmentStation(nullptr, true); if (_settings_client.gui.link_terraform_toolbar) DeleteWindowById(WC_SCEN_LAND_GEN, 0, false); + this->Window::Close(); } /** @@ -416,9 +417,10 @@ public: this->LowerWidget(_settings_client.gui.station_show_coverage + BDSW_LT_OFF); } - virtual ~BuildDocksStationWindow() + void Close() override { DeleteWindowById(WC_SELECT_STATION, 0); + this->PickerWindowBase::Close(); } void OnPaint() override diff --git a/src/engine_gui.cpp b/src/engine_gui.cpp index 77ce95413..bc0143be1 100644 --- a/src/engine_gui.cpp +++ b/src/engine_gui.cpp @@ -128,7 +128,7 @@ struct EnginePreviewWindow : Window { DoCommandP(0, this->window_number, 0, CMD_WANT_ENGINE_PREVIEW); FALLTHROUGH; case WID_EP_NO: - if (!_shift_pressed) delete this; + if (!_shift_pressed) this->Close(); break; } } @@ -138,7 +138,7 @@ struct EnginePreviewWindow : Window { if (!gui_scope) return; EngineID engine = this->window_number; - if (Engine::Get(engine)->preview_company != _local_company) delete this; + if (Engine::Get(engine)->preview_company != _local_company) this->Close(); } }; diff --git a/src/error_gui.cpp b/src/error_gui.cpp index 7b7413228..70b403a68 100644 --- a/src/error_gui.cpp +++ b/src/error_gui.cpp @@ -250,7 +250,7 @@ public: void OnInvalidateData(int data = 0, bool gui_scope = true) override { /* If company gets shut down, while displaying an error about it, remove the error message. */ - if (this->face != INVALID_COMPANY && !Company::IsValidID(this->face)) delete this; + if (this->face != INVALID_COMPANY && !Company::IsValidID(this->face)) this->Close(); } void SetStringParameters(int widget) const override @@ -298,20 +298,21 @@ public: void OnMouseLoop() override { /* Disallow closing the window too easily, if timeout is disabled */ - if (_right_button_down && !this->display_timer.HasElapsed()) delete this; + if (_right_button_down && !this->display_timer.HasElapsed()) this->Close(); } void OnRealtimeTick(uint delta_ms) override { if (this->display_timer.CountElapsed(delta_ms) == 0) return; - delete this; + this->Close(); } - ~ErrmsgWindow() + void Close() override { SetRedErrorSquare(INVALID_TILE); if (_window_system_initialized) ShowFirstError(); + this->Window::Close(); } /** @@ -354,7 +355,7 @@ void UnshowCriticalError() if (_window_system_initialized && w != nullptr) { if (w->IsCritical()) _error_list.push_front(*w); _window_system_initialized = false; - delete w; + w->Close(); } } @@ -403,18 +404,20 @@ void ShowErrorMessage(StringID summary_msg, StringID detailed_msg, WarningLevel data.CopyOutDParams(); ErrmsgWindow *w = (ErrmsgWindow*)FindWindowById(WC_ERRMSG, 0); - if (w != nullptr && w->IsCritical()) { - /* A critical error is currently shown. */ - if (wl == WL_CRITICAL) { - /* Push another critical error in the queue of errors, - * but do not put other errors in the queue. */ - _error_list.push_back(data); + if (w != nullptr) { + if (w->IsCritical()) { + /* A critical error is currently shown. */ + if (wl == WL_CRITICAL) { + /* Push another critical error in the queue of errors, + * but do not put other errors in the queue. */ + _error_list.push_back(data); + } + return; } - } else { - /* Nothing or a non-critical error was shown. */ - delete w; - new ErrmsgWindow(data); + /* A non-critical error was shown. */ + w->Close(); } + new ErrmsgWindow(data); } @@ -425,7 +428,7 @@ void ShowErrorMessage(StringID summary_msg, StringID detailed_msg, WarningLevel bool HideActiveErrorMessage() { ErrmsgWindow *w = (ErrmsgWindow*)FindWindowById(WC_ERRMSG, 0); if (w == nullptr) return false; - delete w; + w->Close(); return true; } diff --git a/src/fios_gui.cpp b/src/fios_gui.cpp index 40a082d64..c04092f6b 100644 --- a/src/fios_gui.cpp +++ b/src/fios_gui.cpp @@ -398,12 +398,13 @@ public: } } - virtual ~SaveLoadWindow() + void Close() override { /* pause is only used in single-player, non-editor mode, non menu mode */ if (!_networking && _game_mode != GM_EDITOR && _game_mode != GM_MENU) { DoCommandP(0, PM_PAUSED_SAVELOAD, 0, CMD_PAUSE); } + this->Window::Close(); } void DrawWidget(const Rect &r, int widget) const override @@ -617,12 +618,12 @@ public: _file_to_saveload.SetTitle(this->selected->title); if (this->abstract_filetype == FT_HEIGHTMAP) { - delete this; + this->Close(); ShowHeightmapLoad(); } else if (!_load_check_data.HasNewGrfs() || _load_check_data.grf_compatibility != GLC_NOT_FOUND || _settings_client.gui.UserIsAllowedToChangeNewGRFs()) { _switch_mode = (_game_mode == GM_EDITOR) ? SM_LOAD_SCENARIO : SM_LOAD_GAME; ClearErrorMessages(); - delete this; + this->Close(); } break; } @@ -688,7 +689,7 @@ public: _file_to_saveload.SetName(name); _file_to_saveload.SetTitle(file->title); - delete this; + this->Close(); ShowHeightmapLoad(); } } @@ -749,7 +750,7 @@ public: EventState OnKeyPress(WChar key, uint16 keycode) override { if (keycode == WKC_ESC) { - delete this; + this->Close(); return ES_HANDLED; } diff --git a/src/goal_gui.cpp b/src/goal_gui.cpp index 9fdca309b..922f5f5d5 100644 --- a/src/goal_gui.cpp +++ b/src/goal_gui.cpp @@ -384,17 +384,17 @@ struct GoalQuestionWindow : public Window { switch (widget) { case WID_GQ_BUTTON_1: DoCommandP(0, this->window_number, this->button[0], CMD_GOAL_QUESTION_ANSWER); - delete this; + this->Close(); break; case WID_GQ_BUTTON_2: DoCommandP(0, this->window_number, this->button[1], CMD_GOAL_QUESTION_ANSWER); - delete this; + this->Close(); break; case WID_GQ_BUTTON_3: DoCommandP(0, this->window_number, this->button[2], CMD_GOAL_QUESTION_ANSWER); - delete this; + this->Close(); break; } } diff --git a/src/highscore_gui.cpp b/src/highscore_gui.cpp index 356235fa0..e36935c1e 100644 --- a/src/highscore_gui.cpp +++ b/src/highscore_gui.cpp @@ -64,7 +64,7 @@ struct EndGameHighScoreBaseWindow : Window { void OnClick(Point pt, int widget, int click_count) override { - delete this; + this->Close(); } EventState OnKeyPress(WChar key, uint16 keycode) override @@ -79,7 +79,7 @@ struct EndGameHighScoreBaseWindow : Window { case WKC_RETURN: case WKC_ESC: case WKC_SPACE: - delete this; + this->Close(); return ES_HANDLED; default: @@ -122,10 +122,11 @@ struct EndGameWindow : EndGameHighScoreBaseWindow { MarkWholeScreenDirty(); } - ~EndGameWindow() + void Close() override { if (!_networking) DoCommandP(0, PM_PAUSED_NORMAL, 0, CMD_PAUSE); // unpause ShowHighscoreTable(this->window_number, this->rank); + this->EndGameHighScoreBaseWindow::Close(); } void OnPaint() override @@ -169,11 +170,13 @@ struct HighScoreWindow : EndGameHighScoreBaseWindow { this->rank = ranking; } - ~HighScoreWindow() + void Close() override { if (_game_mode != GM_MENU) ShowVitalWindows(); if (!_networking && !this->game_paused_by_player) DoCommandP(0, PM_PAUSED_NORMAL, 0, CMD_PAUSE); // unpause + + this->EndGameHighScoreBaseWindow::Close(); } void OnPaint() override diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp index f7061d328..59b0282d5 100644 --- a/src/misc_gui.cpp +++ b/src/misc_gui.cpp @@ -759,20 +759,20 @@ struct TooltipsWindow : public Window { /* Always close tooltips when the cursor is not in our window. */ if (!_cursor.in_window) { - delete this; + this->Close(); return; } /* We can show tooltips while dragging tools. These are shown as long as * we are dragging the tool. Normal tooltips work with hover or rmb. */ switch (this->close_cond) { - case TCC_RIGHT_CLICK: if (!_right_button_down) delete this; break; - case TCC_HOVER: if (!_mouse_hovering) delete this; break; + case TCC_RIGHT_CLICK: if (!_right_button_down) this->Close(); break; + case TCC_HOVER: if (!_mouse_hovering) this->Close(); break; case TCC_NONE: break; case TCC_EXIT_VIEWPORT: { Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y); - if (w == nullptr || IsPtInWindowViewport(w, _cursor.pos.x, _cursor.pos.y) == nullptr) delete this; + if (w == nullptr || IsPtInWindowViewport(w, _cursor.pos.x, _cursor.pos.y) == nullptr) this->Close(); break; } } @@ -1090,18 +1090,19 @@ struct QueryStringWindow : public Window FALLTHROUGH; case WID_QS_CANCEL: - delete this; + this->Close(); break; } } - ~QueryStringWindow() + void Close() override { if (!this->editbox.handled && this->parent != nullptr) { Window *parent = this->parent; - this->parent = nullptr; // so parent doesn't try to delete us again + this->parent = nullptr; // so parent doesn't try to close us again parent->OnQueryTextFinished(nullptr); } + this->Window::Close(); } }; @@ -1167,9 +1168,10 @@ struct QueryWindow : public Window { this->InitNested(WN_CONFIRM_POPUP_QUERY); } - ~QueryWindow() + void Close() override { if (this->proc != nullptr) this->proc(this->parent, false); + this->Window::Close(); } void FindWindowPlacementAndResize(int def_width, int def_height) override @@ -1222,7 +1224,7 @@ struct QueryWindow : public Window { Window *parent = this->parent; /* Prevent the destructor calling the callback function */ this->proc = nullptr; - delete this; + this->Close(); if (proc != nullptr) { proc(parent, true); proc = nullptr; @@ -1230,7 +1232,7 @@ struct QueryWindow : public Window { break; } case WID_Q_NO: - delete this; + this->Close(); break; } } @@ -1248,7 +1250,7 @@ struct QueryWindow : public Window { FALLTHROUGH; case WKC_ESC: - delete this; + this->Close(); return ES_HANDLED; } return ES_NOT_HANDLED; @@ -1289,13 +1291,13 @@ void ShowQuery(StringID caption, StringID message, Window *parent, QueryCallback { if (parent == nullptr) parent = FindWindowById(WC_MAIN_WINDOW, 0); - for (const Window *w : Window::Iterate()) { + for (Window *w : Window::Iterate()) { if (w->window_class != WC_CONFIRM_POPUP_QUERY) continue; - const QueryWindow *qw = (const QueryWindow *)w; + QueryWindow *qw = dynamic_cast<QueryWindow *>(w); if (qw->parent != parent || qw->proc != callback) continue; - delete qw; + qw->Close(); break; } diff --git a/src/network/network_chat_gui.cpp b/src/network/network_chat_gui.cpp index 068a5314e..53546aa87 100644 --- a/src/network/network_chat_gui.cpp +++ b/src/network/network_chat_gui.cpp @@ -307,9 +307,10 @@ struct NetworkChatWindow : public Window { PositionNetworkChatWindow(this); } - ~NetworkChatWindow() + void Close() override { InvalidateWindowData(WC_NEWS_WINDOW, 0, 0); + this->Window::Close(); } void FindWindowPlacementAndResize(int def_width, int def_height) override @@ -470,7 +471,7 @@ struct NetworkChatWindow : public Window { FALLTHROUGH; case WID_NC_CLOSE: /* Cancel */ - delete this; + this->Close(); break; } } @@ -497,7 +498,7 @@ struct NetworkChatWindow : public Window { */ void OnInvalidateData(int data = 0, bool gui_scope = true) override { - if (data == this->dest) delete this; + if (data == this->dest) this->Close(); } }; diff --git a/src/network/network_content_gui.cpp b/src/network/network_content_gui.cpp index dd9590e99..8c75cb572 100644 --- a/src/network/network_content_gui.cpp +++ b/src/network/network_content_gui.cpp @@ -109,9 +109,10 @@ BaseNetworkContentDownloadStatusWindow::BaseNetworkContentDownloadStatusWindow(W this->InitNested(WN_NETWORK_STATUS_WINDOW_CONTENT_DOWNLOAD); } -BaseNetworkContentDownloadStatusWindow::~BaseNetworkContentDownloadStatusWindow() +void BaseNetworkContentDownloadStatusWindow::Close() { _network_content_client.RemoveCallback(this); + this->Window::Close(); } void BaseNetworkContentDownloadStatusWindow::DrawWidget(const Rect &r, int widget) const @@ -171,8 +172,7 @@ public: this->parent = FindWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_CONTENT_LIST); } - /** Free whatever we've allocated */ - ~NetworkContentDownloadStatusWindow() + void Close() override { TarScanner::Mode mode = TarScanner::NONE; for (auto ctype : this->receivedTypes) { @@ -254,6 +254,8 @@ public: /* Always invalidate the download window; tell it we are going to be gone */ InvalidateWindowData(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_CONTENT_LIST, 2); + + this->BaseNetworkContentDownloadStatusWindow::Close(); } void OnClick(Point pt, int widget, int click_count) override @@ -261,7 +263,7 @@ public: if (widget == WID_NCDS_CANCELOK) { if (this->downloaded_bytes != this->total_bytes) { _network_content_client.CloseConnection(); - delete this; + this->Close(); } else { /* If downloading succeeded, close the online content window. This will close * the current window as well. */ @@ -549,10 +551,10 @@ public: this->InvalidateData(); } - /** Free everything we allocated */ - ~NetworkContentListWindow() + void Close() override { _network_content_client.RemoveCallback(this); + this->Window::Close(); } void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override @@ -837,7 +839,7 @@ public: break; case WID_NCL_CANCEL: - delete this; + this->Close(); break; case WID_NCL_OPEN_URL: @@ -941,7 +943,7 @@ public: { if (!success) { ShowErrorMessage(STR_CONTENT_ERROR_COULD_NOT_CONNECT, INVALID_STRING_ID, WL_ERROR); - delete this; + this->Close(); return; } diff --git a/src/network/network_content_gui.h b/src/network/network_content_gui.h index 4853101bb..dd9d9cfdf 100644 --- a/src/network/network_content_gui.h +++ b/src/network/network_content_gui.h @@ -32,11 +32,7 @@ public: */ BaseNetworkContentDownloadStatusWindow(WindowDesc *desc); - /** - * Free everything associated with this window. - */ - ~BaseNetworkContentDownloadStatusWindow(); - + void Close() override; void DrawWidget(const Rect &r, int widget) const override; void OnDownloadProgress(const ContentInfo *ci, int bytes) override; }; diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index 322e128cf..7f179a05f 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -2597,7 +2597,7 @@ struct NetworkCompanyPasswordWindow : public Window { FALLTHROUGH; case WID_NCP_CANCEL: - delete this; + this->Close(); break; case WID_NCP_SAVE_AS_DEFAULT_PASSWORD: diff --git a/src/newgrf_gui.cpp b/src/newgrf_gui.cpp index 9e86a575f..a9b43ea9d 100644 --- a/src/newgrf_gui.cpp +++ b/src/newgrf_gui.cpp @@ -424,7 +424,7 @@ struct NewGRFParametersWindow : public Window { break; case WID_NP_ACCEPT: - delete this; + this->Close(); break; } } @@ -662,7 +662,7 @@ struct NewGRFWindow : public Window, NewGRFScanCallback { this->OnInvalidateData(GOID_NEWGRF_CURRENT_LOADED); } - ~NewGRFWindow() + void Close() override { DeleteWindowByClass(WC_GRF_PARAMETERS); DeleteWindowByClass(WC_TEXTFILE); @@ -674,6 +674,11 @@ struct NewGRFWindow : public Window, NewGRFScanCallback { ReloadNewGRFData(); } + this->Window::Close(); + } + + ~NewGRFWindow() + { /* Remove the temporary copy of grf-list used in window */ ClearGRFConfigList(&this->actives); } @@ -2121,13 +2126,13 @@ struct SavePresetWindow : public Window { } case WID_SVP_CANCEL: - delete this; + this->Close(); break; case WID_SVP_SAVE: { Window *w = FindWindowById(WC_GAME_OPTIONS, WN_GAME_OPTIONS_NEWGRF_STATE); if (w != nullptr && !StrEmpty(this->presetname_editbox.text.buf)) w->OnQueryTextFinished(this->presetname_editbox.text.buf); - delete this; + this->Close(); break; } } diff --git a/src/news_gui.cpp b/src/news_gui.cpp index 8dc66c846..513624b3e 100644 --- a/src/news_gui.cpp +++ b/src/news_gui.cpp @@ -479,7 +479,7 @@ struct NewsWindow : Window { switch (widget) { case WID_N_CLOSEBOX: NewsWindow::duration = 0; - delete this; + this->Close(); _forced_news = nullptr; break; @@ -1028,7 +1028,7 @@ static void ShowNewsMessage(const NewsItem *ni) bool HideActiveNewsMessage() { NewsWindow *w = (NewsWindow*)FindWindowById(WC_NEWS_WINDOW, 0); if (w == nullptr) return false; - delete w; + w->Close(); return true; } diff --git a/src/osk_gui.cpp b/src/osk_gui.cpp index 92b15eaba..6fb1ee5a0 100644 --- a/src/osk_gui.cpp +++ b/src/osk_gui.cpp @@ -170,7 +170,7 @@ struct OskWindow : public Window { return; } } - delete this; + this->Close(); break; case WID_OSK_CANCEL: @@ -182,7 +182,7 @@ struct OskWindow : public Window { qs->text.Assign(this->orig_str_buf); qs->text.MovePos(WKC_END); this->OnEditboxChanged(WID_OSK_TEXT); - delete this; + this->Close(); } break; } @@ -205,7 +205,7 @@ struct OskWindow : public Window { void OnFocusLost() override { VideoDriver::GetInstance()->EditBoxLostFocus(); - delete this; + this->Close(); } }; diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp index 86f718984..bb5244ae7 100644 --- a/src/rail_gui.cpp +++ b/src/rail_gui.cpp @@ -434,10 +434,11 @@ struct BuildRailToolbarWindow : Window { if (_settings_client.gui.link_terraform_toolbar) ShowTerraformToolbar(this); } - ~BuildRailToolbarWindow() + void Close() override { if (this->IsWidgetLowered(WID_RAT_BUILD_STATION)) SetViewportCatchmentStation(nullptr, true); if (_settings_client.gui.link_terraform_toolbar) DeleteWindowById(WC_SCEN_LAND_GEN, 0, false); + this->Window::Close(); } /** @@ -1028,9 +1029,10 @@ public: this->InvalidateData(); } - virtual ~BuildRailStationWindow() + void Close() override { DeleteWindowById(WC_SELECT_STATION, 0); + this->PickerWindowBase::Close(); } /** Sort station classes by StationClassID. */ @@ -1695,9 +1697,10 @@ public: this->OnInvalidateData(); } - ~BuildSignalWindow() + void Close() override { _convert_signal_button = false; + this->PickerWindowBase::Close(); } void OnInit() override diff --git a/src/road_gui.cpp b/src/road_gui.cpp index ae84404a2..c12e94c16 100644 --- a/src/road_gui.cpp +++ b/src/road_gui.cpp @@ -289,10 +289,11 @@ struct BuildRoadToolbarWindow : Window { if (_settings_client.gui.link_terraform_toolbar) ShowTerraformToolbar(this); } - ~BuildRoadToolbarWindow() + void Close() override { if (_game_mode == GM_NORMAL && (this->IsWidgetLowered(WID_ROT_BUS_STATION) || this->IsWidgetLowered(WID_ROT_TRUCK_STATION))) SetViewportCatchmentStation(nullptr, true); if (_settings_client.gui.link_terraform_toolbar) DeleteWindowById(WC_SCEN_LAND_GEN, 0, false); + this->Window::Close(); } /** @@ -1095,9 +1096,10 @@ struct BuildRoadStationWindow : public PickerWindowBase { this->window_class = (rs == ROADSTOP_BUS) ? WC_BUS_STATION : WC_TRUCK_STATION; } - virtual ~BuildRoadStationWindow() + void Close() override { DeleteWindowById(WC_SELECT_STATION, 0); + this->PickerWindowBase::Close(); } void OnPaint() override diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index 559c39587..6fae298bc 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -172,11 +172,12 @@ struct GameOptionsWindow : Window { this->OnInvalidateData(0); } - ~GameOptionsWindow() + void Close() override { DeleteWindowById(WC_CUSTOM_CURRENCY, 0); DeleteWindowByClass(WC_TEXTFILE); if (this->reload) _switch_mode = SM_MENU; + this->Window::Close(); } /** diff --git a/src/signs_gui.cpp b/src/signs_gui.cpp index 93c4dd431..d7d93c905 100644 --- a/src/signs_gui.cpp +++ b/src/signs_gui.cpp @@ -527,7 +527,7 @@ struct SignWindow : Window, SignList { FALLTHROUGH; case WID_QES_CANCEL: - delete this; + this->Close(); break; } } @@ -592,5 +592,5 @@ void DeleteRenameSignWindow(SignID sign) { SignWindow *w = dynamic_cast<SignWindow *>(FindWindowById(WC_QUERY_STRING, WN_QUERY_STRING_SIGN)); - if (w != nullptr && w->cur_sign == sign) delete w; + if (w != nullptr && w->cur_sign == sign) w->Close(); } diff --git a/src/smallmap_gui.cpp b/src/smallmap_gui.cpp index bc0a7b447..7827620af 100644 --- a/src/smallmap_gui.cpp +++ b/src/smallmap_gui.cpp @@ -1095,7 +1095,12 @@ SmallMapWindow::SmallMapWindow(WindowDesc *desc, int window_number) : Window(des SmallMapWindow::~SmallMapWindow() { delete this->overlay; +} + +/* virtual */ void SmallMapWindow::Close() +{ this->BreakIndustryChainLink(); + this->Window::Close(); } /** diff --git a/src/smallmap_gui.h b/src/smallmap_gui.h index e2fa9074d..a5552333c 100644 --- a/src/smallmap_gui.h +++ b/src/smallmap_gui.h @@ -178,6 +178,7 @@ public: void SmallMapCenterOnCurrentPos(); Point GetStationMiddle(const Station *st) const; + void Close() override; void SetStringParameters(int widget) const override; void OnInit() override; void OnPaint() override; diff --git a/src/station_gui.cpp b/src/station_gui.cpp index ab0eb5d1a..7fb1b66a4 100644 --- a/src/station_gui.cpp +++ b/src/station_gui.cpp @@ -1334,7 +1334,7 @@ struct StationViewWindow : public Window { this->owner = Station::Get(window_number)->owner; } - ~StationViewWindow() + void Close() override { DeleteWindowById(WC_TRAINS_LIST, VehicleListIdentifier(VL_STATION_LIST, VEH_TRAIN, this->owner, this->window_number).Pack(), false); DeleteWindowById(WC_ROADVEH_LIST, VehicleListIdentifier(VL_STATION_LIST, VEH_ROAD, this->owner, this->window_number).Pack(), false); @@ -1342,6 +1342,7 @@ struct StationViewWindow : public Window { DeleteWindowById(WC_AIRCRAFT_LIST, VehicleListIdentifier(VL_STATION_LIST, VEH_AIRCRAFT, this->owner, this->window_number).Pack(), false); SetViewportCatchmentStation(Station::Get(this->window_number), false); + this->Window::Close(); } /** @@ -2285,11 +2286,12 @@ struct SelectStationWindow : Window { _thd.freeze = true; } - ~SelectStationWindow() + void Close() override { if (_settings_client.gui.station_show_coverage) SetViewportCatchmentStation(nullptr, true); _thd.freeze = false; + this->Window::Close(); } void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override @@ -2424,7 +2426,7 @@ static bool StationJoinerNeeded(const CommandContainer &cmd, TileArea ta) Window *selection_window = FindWindowById(WC_SELECT_STATION, 0); if (selection_window != nullptr) { /* Abort current distant-join and start new one */ - delete selection_window; + selection_window->Close(); UpdateTileSelection(); } diff --git a/src/town_gui.cpp b/src/town_gui.cpp index bd5893561..021590cb9 100644 --- a/src/town_gui.cpp +++ b/src/town_gui.cpp @@ -336,9 +336,10 @@ public: this->SetWidgetDisabledState(WID_TV_CHANGE_NAME, _networking && !_network_server); } - ~TownViewWindow() + void Close() override { SetViewportCatchmentTown(Town::Get(this->window_number), false); + this->Window::Close(); } void SetStringParameters(int widget) const override diff --git a/src/vehicle_gui.cpp b/src/vehicle_gui.cpp index 006664491..28155f724 100644 --- a/src/vehicle_gui.cpp +++ b/src/vehicle_gui.cpp @@ -1033,9 +1033,9 @@ struct RefitWindow : public Window { if (this->order == INVALID_VEH_ORDER_ID) { bool delete_window = this->selected_vehicle == v->index && this->num_vehicles == UINT8_MAX; - if (DoCommandP(v->tile, this->selected_vehicle, this->cargo->cargo | this->cargo->subtype << 8 | this->num_vehicles << 16, GetCmdRefitVeh(v)) && delete_window) delete this; + if (DoCommandP(v->tile, this->selected_vehicle, this->cargo->cargo | this->cargo->subtype << 8 | this->num_vehicles << 16, GetCmdRefitVeh(v)) && delete_window) this->Close(); } else { - if (DoCommandP(v->tile, v->index, this->cargo->cargo | this->order << 16, CMD_ORDER_REFIT)) delete this; + if (DoCommandP(v->tile, v->index, this->cargo->cargo | this->order << 16, CMD_ORDER_REFIT)) this->Close(); } } break; @@ -2718,12 +2718,13 @@ public: this->UpdateButtonStatus(); } - ~VehicleViewWindow() + void Close() override { DeleteWindowById(WC_VEHICLE_ORDERS, this->window_number, false); DeleteWindowById(WC_VEHICLE_REFIT, this->window_number, false); DeleteWindowById(WC_VEHICLE_DETAILS, this->window_number, false); DeleteWindowById(WC_VEHICLE_TIMETABLE, this->window_number, false); + this->Window::Close(); } void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override diff --git a/src/waypoint_gui.cpp b/src/waypoint_gui.cpp index 40c3401b0..946d123c5 100644 --- a/src/waypoint_gui.cpp +++ b/src/waypoint_gui.cpp @@ -74,9 +74,10 @@ public: this->OnInvalidateData(0); } - ~WaypointWindow() + void Close() override { DeleteWindowById(GetWindowClassForVehicleType(this->vt), VehicleListIdentifier(VL_STATION_LIST, this->vt, this->owner, this->window_number).Pack(), false); + this->Window::Close(); } void SetStringParameters(int widget) const override diff --git a/src/widgets/dropdown.cpp b/src/widgets/dropdown.cpp index e838d7231..77b54d564 100644 --- a/src/widgets/dropdown.cpp +++ b/src/widgets/dropdown.cpp @@ -184,12 +184,11 @@ struct DropdownWindow : Window { this->scrolling_timer = GUITimer(MILLISECONDS_PER_TICK); } - ~DropdownWindow() + void Close() override { - /* Make the dropdown "invisible", so it doesn't affect new window placement. + /* Finish closing the dropdown, so it doesn't affect new window placement. * Also mark it dirty in case the callback deals with the screen. (e.g. screenshots). */ - *this->z_position = nullptr; - this->SetDirty(); + this->Window::Close(); Window *w2 = FindWindowById(this->parent_wnd_class, this->parent_wnd_num); if (w2 != nullptr) { @@ -200,7 +199,7 @@ struct DropdownWindow : Window { } } - virtual Point OnInitialPosition(int16 sm_width, int16 sm_height, int window_number) + Point OnInitialPosition(int16 sm_width, int16 sm_height, int window_number) override { return this->position; } @@ -237,7 +236,7 @@ struct DropdownWindow : Window { return false; } - virtual void DrawWidget(const Rect &r, int widget) const + void DrawWidget(const Rect &r, int widget) const override { if (widget != WID_DM_ITEMS) return; @@ -265,7 +264,7 @@ struct DropdownWindow : Window { } } - virtual void OnClick(Point pt, int widget, int click_count) + void OnClick(Point pt, int widget, int click_count) override { if (widget != WID_DM_ITEMS) return; int item; @@ -276,7 +275,7 @@ struct DropdownWindow : Window { } } - virtual void OnRealtimeTick(uint delta_ms) + void OnRealtimeTick(uint delta_ms) override { if (!this->scrolling_timer.Elapsed(delta_ms)) return; this->scrolling_timer.SetInterval(MILLISECONDS_PER_TICK); @@ -293,22 +292,19 @@ struct DropdownWindow : Window { } } - virtual void OnMouseLoop() + void OnMouseLoop() override { Window *w2 = FindWindowById(this->parent_wnd_class, this->parent_wnd_num); if (w2 == nullptr) { - delete this; + this->Close(); return; } if (this->click_delay != 0 && --this->click_delay == 0) { - /* Make the dropdown "invisible", so it doesn't affect new window placement. + /* Close the dropdown, so it doesn't affect new window placement. * Also mark it dirty in case the callback deals with the screen. (e.g. screenshots). */ - *this->z_position = nullptr; - this->SetDirty(); - + this->Close(); w2->OnDropdownSelect(this->parent_button, this->selected_index); - delete this; return; } @@ -318,7 +314,7 @@ struct DropdownWindow : Window { if (!_left_button_clicked) { this->drag_mode = false; if (!this->GetDropDownItem(item)) { - if (this->instant_close) delete this; + if (this->instant_close) this->Close(); return; } this->click_delay = 2; @@ -509,7 +505,7 @@ int HideDropDownMenu(Window *pw) if (pw->window_class == dw->parent_wnd_class && pw->window_number == dw->parent_wnd_num) { int parent_button = dw->parent_button; - delete dw; + dw->Close(); return parent_button; } } diff --git a/src/window.cpp b/src/window.cpp index 036b17bcc..aae3efffb 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -56,6 +56,21 @@ static Window *_last_scroll_window = nullptr; ///< Window of the last scroll eve /** List of windows opened at the screen sorted from the front to back. */ WindowList _z_windows; +/** List of closed windows to delete. */ +/* static */ std::vector<Window *> Window::closed_windows; + +/** + * Delete all closed windows. + */ +/* static */ void Window::DeleteClosedWindows() +{ + for (Window *w : Window::closed_windows) delete w; + Window::closed_windows.clear(); + + /* Remove dead entries from the window list */ + _z_windows.remove(nullptr); +} + /** If false, highlight is white, otherwise the by the widget defined colour. */ bool _window_highlight_colour = false; @@ -705,7 +720,7 @@ static void DispatchLeftClickEvent(Window *w, int x, int y, int click_count) } case WWT_CLOSEBOX: // 'X' - delete w; + w->Close(); return; case WWT_CAPTION: // 'Title bar' @@ -793,7 +808,7 @@ static void DispatchRightClickEvent(Window *w, int x, int y) /* Right-click close is enabled and there is a closebox */ if (_settings_client.gui.right_mouse_wnd_close && w->nested_root->GetWidgetOfType(WWT_CLOSEBOX)) { - delete w; + w->Close(); } else if (_settings_client.gui.hover_delay_ms == 0 && !w->OnTooltip(pt, wid->index, TCC_RIGHT_CLICK) && wid->tool_tip != 0) { GuiShowTooltips(w, wid->tool_tip, 0, nullptr, TCC_RIGHT_CLICK); } @@ -1068,16 +1083,21 @@ void Window::DeleteChildWindows(WindowClass wc) const { Window *child = FindChildWindow(this, wc); while (child != nullptr) { - delete child; + child->Close(); child = FindChildWindow(this, wc); } } /** - * Remove window and all its child windows from the window stack. + * Hide the window and all its child windows, and mark them for a later deletion. */ -Window::~Window() +void Window::Close() { + /* Don't close twice. */ + if (*this->z_position == nullptr) return; + + *this->z_position = nullptr; + if (_thd.window_class == this->window_class && _thd.window_number == this->window_number) { ResetObjectToPlace(); @@ -1094,21 +1114,29 @@ Window::~Window() /* Make sure we don't try to access this window as the focused window when it doesn't exist anymore. */ if (_focused_window == this) { - /* Virtual functions get called statically in destructors, so make it explicit to remove any confusion. */ - this->Window::OnFocusLost(); + this->OnFocusLost(); _focused_window = nullptr; } this->DeleteChildWindows(); - if (this->viewport != nullptr) DeleteWindowViewport(this); - this->SetDirty(); + Window::closed_windows.push_back(this); +} + +/** + * Remove window and all its child windows from the window stack. + */ +Window::~Window() +{ + /* Make sure the window is closed, deletion is allowed only in Window::DeleteClosedWindows(). */ + assert(*this->z_position == nullptr); + + if (this->viewport != nullptr) DeleteWindowViewport(this); + free(this->nested_array); // Contents is released through deletion of #nested_root. delete this->nested_root; - - *this->z_position = nullptr; } /** @@ -1151,7 +1179,7 @@ void DeleteWindowById(WindowClass cls, WindowNumber number, bool force) { Window *w = FindWindowById(cls, number); if (w != nullptr && (force || (w->flags & WF_STICKY) == 0)) { - delete w; + w->Close(); } } @@ -1164,7 +1192,7 @@ void DeleteWindowByClass(WindowClass cls) /* Note: the container remains stable, even when deleting windows. */ for (Window *w : Window::Iterate()) { if (w->window_class == cls) { - delete w; + w->Close(); } } } @@ -1180,7 +1208,7 @@ void DeleteCompanyWindows(CompanyID id) /* Note: the container remains stable, even when deleting windows. */ for (Window *w : Window::Iterate()) { if (w->owner == id) { - delete w; + w->Close(); } } @@ -1818,9 +1846,11 @@ void UnInitWindowSystem() { UnshowCriticalError(); - for (Window *w : Window::Iterate()) delete w; + for (Window *w : Window::Iterate()) w->Close(); - _z_windows.clear(); + Window::DeleteClosedWindows(); + + assert(_z_windows.empty()); } /** @@ -2968,7 +2998,7 @@ static void CheckSoftLimit() if (deletable_count <= _settings_client.gui.window_soft_limit) break; assert(last_deletable != nullptr); - delete last_deletable; + last_deletable->Close(); } } @@ -2983,14 +3013,8 @@ void InputLoop() CheckSoftLimit(); - /* Remove dead entries from the window list */ - for (auto it = _z_windows.begin(); it != _z_windows.end(); ) { - if (*it == nullptr) { - it = _z_windows.erase(it); - } else { - ++it; - } - } + /* Process scheduled window deletion. */ + Window::DeleteClosedWindows(); if (_input_events_this_tick != 0) { /* The input loop is called only once per GameLoop() - so we can clear the counter here */ @@ -3237,7 +3261,7 @@ void CallWindowGameTickEvent() void DeleteNonVitalWindows() { /* Note: the container remains stable, even when deleting windows. */ - for (const Window *w : Window::Iterate()) { + for (Window *w : Window::Iterate()) { if (w->window_class != WC_MAIN_WINDOW && w->window_class != WC_SELECT_GAME && w->window_class != WC_MAIN_TOOLBAR && @@ -3245,7 +3269,7 @@ void DeleteNonVitalWindows() w->window_class != WC_TOOLTIPS && (w->flags & WF_STICKY) == 0) { // do not delete windows which are 'pinned' - delete w; + w->Close(); } } } @@ -3263,9 +3287,9 @@ void DeleteAllNonVitalWindows() DeleteNonVitalWindows(); /* Note: the container remains stable, even when deleting windows. */ - for (const Window *w : Window::Iterate()) { + for (Window *w : Window::Iterate()) { if (w->flags & WF_STICKY) { - delete w; + w->Close(); } } } @@ -3288,9 +3312,9 @@ void DeleteAllMessages() void DeleteConstructionWindows() { /* Note: the container remains stable, even when deleting windows. */ - for (const Window *w : Window::Iterate()) { + for (Window *w : Window::Iterate()) { if (w->window_desc->flags & WDF_CONSTRUCTION) { - delete w; + w->Close(); } } @@ -3483,12 +3507,11 @@ void RelocateAllWindows(int neww, int newh) } /** - * Destructor of the base class PickerWindowBase - * Main utility is to stop the base Window destructor from triggering - * a free while the child will already be free, in this case by the ResetObjectToPlace(). + * Hide the window and all its child windows, and mark them for a later deletion. + * Always call ResetObjectToPlace() when closing a PickerWindow. */ -PickerWindowBase::~PickerWindowBase() +void PickerWindowBase::Close() { - *this->z_position = nullptr; // stop the ancestor from freeing the already (to be) child ResetObjectToPlace(); + this->Window::Close(); } diff --git a/src/window_gui.h b/src/window_gui.h index 1195c6c49..c84ad333b 100644 --- a/src/window_gui.h +++ b/src/window_gui.h @@ -277,6 +277,9 @@ enum TooltipCloseCondition { * Data structure for an opened window */ struct Window : ZeroedMemoryAllocator { +private: + static std::vector<Window *> closed_windows; + protected: void InitializeData(WindowNumber window_number); void InitializePositionSize(int x, int y, int min_width, int min_height); @@ -284,11 +287,12 @@ protected: std::vector<int> scheduled_invalidation_data; ///< Data of scheduled OnInvalidateData() calls. + /* Protected to prevent deletion anywhere outside Window::DeleteClosedWindows(). */ + virtual ~Window(); + public: Window(WindowDesc *desc); - virtual ~Window(); - /** * Helper allocation function to disallow something. * Don't allow arrays; arrays of Windows are pointless as you need @@ -506,6 +510,8 @@ public: static int SortButtonWidth(); void DeleteChildWindows(WindowClass wc = WC_INVALID) const; + virtual void Close(); + static void DeleteClosedWindows(); void SetDirty() const; void ReInit(int rx = 0, int ry = 0); @@ -916,7 +922,7 @@ public: this->parent = parent; } - virtual ~PickerWindowBase(); + void Close() override; }; Window *BringWindowToFrontById(WindowClass cls, WindowNumber number); |