OpenTTD Source 20241224-master-gf74b0cf984
error_gui.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6 */
7
10#include "stdafx.h"
12#include "core/mem_func.hpp"
13#include "landscape.h"
14#include "newgrf_text.h"
15#include "error.h"
16#include "viewport_func.h"
17#include "gfx_func.h"
18#include "string_func.h"
19#include "company_base.h"
20#include "company_func.h"
22#include "strings_func.h"
23#include "zoom_func.h"
24#include "window_func.h"
25#include "console_func.h"
26#include "window_gui.h"
27#include "timer/timer.h"
28#include "timer/timer_window.h"
29
31
32#include "table/strings.h"
33
34#include "safeguards.h"
35
36static constexpr NWidgetPart _nested_errmsg_widgets[] = {
38 NWidget(WWT_CLOSEBOX, COLOUR_RED),
39 NWidget(WWT_CAPTION, COLOUR_RED, WID_EM_CAPTION), SetDataTip(STR_ERROR_MESSAGE_CAPTION, STR_NULL),
41 NWidget(WWT_PANEL, COLOUR_RED),
44};
45
46static WindowDesc _errmsg_desc(
47 WDP_MANUAL, nullptr, 0, 0,
49 0,
50 _nested_errmsg_widgets
51);
52
53static constexpr NWidgetPart _nested_errmsg_face_widgets[] = {
55 NWidget(WWT_CLOSEBOX, COLOUR_RED),
56 NWidget(WWT_CAPTION, COLOUR_RED, WID_EM_CAPTION), SetDataTip(STR_ERROR_MESSAGE_CAPTION_OTHER_COMPANY, STR_NULL),
58 NWidget(WWT_PANEL, COLOUR_RED),
60 NWidget(WWT_EMPTY, COLOUR_RED, WID_EM_FACE), SetPadding(2, 0, 2, 2), SetFill(0, 1), SetMinimalSize(92, 119),
64};
65
66static WindowDesc _errmsg_face_desc(
67 WDP_MANUAL, nullptr, 0, 0,
69 0,
70 _nested_errmsg_face_widgets
71);
72
78 is_critical(data.is_critical), params(data.params), textref_stack_grffile(data.textref_stack_grffile), textref_stack_size(data.textref_stack_size),
79 summary_msg(data.summary_msg), detailed_msg(data.detailed_msg), extra_msg(data.extra_msg), position(data.position), face(data.face)
80{
81 memcpy(this->textref_stack, data.textref_stack, sizeof(this->textref_stack));
82}
83
96ErrorMessageData::ErrorMessageData(StringID summary_msg, StringID detailed_msg, bool is_critical, int x, int y, const GRFFile *textref_stack_grffile, uint textref_stack_size, const uint32_t *textref_stack, StringID extra_msg) :
97 is_critical(is_critical),
98 textref_stack_grffile(textref_stack_grffile),
99 textref_stack_size(textref_stack_size),
100 summary_msg(summary_msg),
101 detailed_msg(detailed_msg),
102 extra_msg(extra_msg),
103 face(INVALID_COMPANY)
104{
105 this->position.x = x;
106 this->position.y = y;
107
108 if (textref_stack_size > 0) MemCpyT(this->textref_stack, textref_stack, textref_stack_size);
109
111}
112
117{
118 if (this->detailed_msg == STR_ERROR_OWNED_BY) {
119 /* The parameters are set by SetDParamsForOwnedBy. */
121 if (company < MAX_COMPANIES) face = company;
122 }
123
124 /* Get parameters using type information */
126 CopyOutDParam(this->params, 20);
128}
129
135void ErrorMessageData::SetDParam(uint n, uint64_t v)
136{
137 if (n >= this->params.size()) this->params.resize(n + 1);
138 this->params[n] = v;
139}
140
146void ErrorMessageData::SetDParamStr(uint n, const char *str)
147{
148 if (n >= this->params.size()) this->params.resize(n + 1);
149 this->params[n] = str;
150}
151
157void ErrorMessageData::SetDParamStr(uint n, const std::string &str)
158{
159 if (n >= this->params.size()) this->params.resize(n + 1);
160 this->params[n] = str;
161}
162
167
170private:
174 TimeoutTimer<TimerWindow> display_timeout;
175
176public:
177 ErrmsgWindow(const ErrorMessageData &data) :
178 Window(data.HasFace() ? _errmsg_face_desc : _errmsg_desc),
179 ErrorMessageData(data),
180 display_timeout(std::chrono::seconds(3 * _settings_client.gui.errmsg_duration), [this]() {
181 this->Close();
182 })
183 {
184 this->InitNested();
185
186 /* Only start the timeout if the message is not critical. */
187 if (!this->is_critical) {
188 this->display_timeout.Reset();
189 }
190 }
191
193 {
194 switch (widget) {
195 case WID_EM_MESSAGE: {
196 CopyInDParam(this->params);
198
199 this->height_summary = GetStringHeight(this->summary_msg, size.width);
200 this->height_detailed = (this->detailed_msg == INVALID_STRING_ID) ? 0 : GetStringHeight(this->detailed_msg, size.width);
201 this->height_extra = (this->extra_msg == INVALID_STRING_ID) ? 0 : GetStringHeight(this->extra_msg, size.width);
202
204
205 uint panel_height = this->height_summary;
206 if (this->detailed_msg != INVALID_STRING_ID) panel_height += this->height_detailed + WidgetDimensions::scaled.vsep_wide;
207 if (this->extra_msg != INVALID_STRING_ID) panel_height += this->height_extra + WidgetDimensions::scaled.vsep_wide;
208
209 size.height = std::max(size.height, panel_height);
210 break;
211 }
212 case WID_EM_FACE:
213 size = maxdim(size, GetScaledSpriteSize(SPR_GRADIENT));
214 break;
215 }
216 }
217
219 {
220 /* Position (0, 0) given, center the window. */
221 if (this->position.x == 0 && this->position.y == 0) {
222 Point pt = {(_screen.width - sm_width) >> 1, (_screen.height - sm_height) >> 1};
223 return pt;
224 }
225
226 constexpr int distance_to_cursor = 200;
227
228 /* Position the error window just above the cursor. This makes the
229 * error window clearly visible, without being in the way of what
230 * the user is doing. */
231 Point pt;
232 pt.x = _cursor.pos.x - sm_width / 2;
233 pt.y = _cursor.pos.y - (distance_to_cursor + sm_height);
234
235 if (pt.y < GetMainViewTop()) {
236 /* Window didn't fit above cursor, so place it below. */
237 pt.y = _cursor.pos.y + distance_to_cursor;
238 }
239
240 return pt;
241 }
242
248 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
249 {
250 /* If company gets shut down, while displaying an error about it, remove the error message. */
251 if (this->face != INVALID_COMPANY && !Company::IsValidID(this->face)) this->Close();
252 }
253
254 void SetStringParameters(WidgetID widget) const override
255 {
256 if (widget == WID_EM_CAPTION) CopyInDParam(this->params);
257 }
258
259 void DrawWidget(const Rect &r, WidgetID widget) const override
260 {
261 switch (widget) {
262 case WID_EM_FACE: {
263 const Company *c = Company::Get(this->face);
265 break;
266 }
267
268 case WID_EM_MESSAGE:
269 CopyInDParam(this->params);
271
272 if (this->detailed_msg == INVALID_STRING_ID) {
273 DrawStringMultiLine(r, this->summary_msg, TC_FROMSTRING, SA_CENTER);
274 } else if (this->extra_msg == INVALID_STRING_ID) {
275 /* Extra space when message is shorter than company face window */
276 int extra = (r.Height() - this->height_summary - this->height_detailed - WidgetDimensions::scaled.vsep_wide) / 2;
277
278 /* Note: NewGRF supplied error message often do not start with a colour code, so default to white. */
279 DrawStringMultiLine(r.WithHeight(this->height_summary + extra, false), this->summary_msg, TC_WHITE, SA_CENTER);
280 DrawStringMultiLine(r.WithHeight(this->height_detailed + extra, true), this->detailed_msg, TC_WHITE, SA_CENTER);
281 } else {
282 /* Extra space when message is shorter than company face window */
283 int extra = (r.Height() - this->height_summary - this->height_detailed - this->height_extra - (WidgetDimensions::scaled.vsep_wide * 2)) / 3;
284
285 /* Note: NewGRF supplied error message often do not start with a colour code, so default to white. */
286 Rect top_section = r.WithHeight(this->height_summary + extra, false);
287 Rect bottom_section = r.WithHeight(this->height_extra + extra, true);
292 }
293
295 break;
296
297 default:
298 break;
299 }
300 }
301
302 void OnMouseLoop() override
303 {
304 /* Disallow closing the window too easily, if timeout is disabled */
305 if (_right_button_down && !this->is_critical) this->Close();
306 }
307
308 void Close([[maybe_unused]] int data = 0) override
309 {
312 this->Window::Close();
313 }
314
320 {
321 return this->is_critical;
322 }
323};
324
329{
331 _error_list.clear();
332}
333
336{
338 if (!_error_list.empty()) {
339 new ErrmsgWindow(_error_list.front());
340 _error_list.pop_front();
341 }
342}
343
350{
351 ErrmsgWindow *w = dynamic_cast<ErrmsgWindow *>(FindWindowById(WC_ERRMSG, 0));
352 if (_window_system_initialized && w != nullptr) {
353 if (w->IsCritical()) _error_list.push_front(*w);
355 w->Close();
356 }
357}
358
367void ShowErrorMessage(StringID summary_msg, int x, int y, CommandCost cc)
368{
370}
371
384void ShowErrorMessage(StringID summary_msg, StringID detailed_msg, WarningLevel wl, int x, int y, const GRFFile *textref_stack_grffile, uint textref_stack_size, const uint32_t *textref_stack, StringID extra_msg)
385{
386 assert(textref_stack_size == 0 || (textref_stack_grffile != nullptr && textref_stack != nullptr));
387 if (summary_msg == STR_NULL) summary_msg = STR_EMPTY;
388
389 if (wl != WL_INFO) {
390 /* Print message to console */
391
392 if (textref_stack_size > 0) StartTextRefStackUsage(textref_stack_grffile, textref_stack_size, textref_stack);
393
394 std::string message = GetString(summary_msg);
395 if (detailed_msg != INVALID_STRING_ID) {
396 message += " ";
397 AppendStringInPlace(message, detailed_msg);
398 }
399 if (extra_msg != INVALID_STRING_ID) {
400 message += " ";
401 AppendStringInPlace(message, extra_msg);
402 }
403
404 if (textref_stack_size > 0) StopTextRefStackUsage();
405
406 IConsolePrint(wl == WL_WARNING ? CC_WARNING : CC_ERROR, message);
407 }
408
409 bool is_critical = wl == WL_CRITICAL;
410
411 if (_game_mode == GM_BOOTSTRAP) return;
412 if (_settings_client.gui.errmsg_duration == 0 && !is_critical) return;
413
414 ErrorMessageData data(summary_msg, detailed_msg, is_critical, x, y, textref_stack_grffile, textref_stack_size, textref_stack, extra_msg);
415 data.CopyOutDParams();
416
417 ErrmsgWindow *w = dynamic_cast<ErrmsgWindow *>(FindWindowById(WC_ERRMSG, 0));
418 if (w != nullptr) {
419 if (w->IsCritical()) {
420 /* A critical error is currently shown. */
421 if (wl == WL_CRITICAL) {
422 /* Push another critical error in the queue of errors,
423 * but do not put other errors in the queue. */
424 _error_list.push_back(data);
425 }
426 return;
427 }
428 /* A non-critical error was shown. */
429 w->Close();
430 }
431 new ErrmsgWindow(data);
432}
433
434
440{
441 ErrmsgWindow *w = dynamic_cast<ErrmsgWindow *>(FindWindowById(WC_ERRMSG, 0));
442 if (w == nullptr) return false;
443 w->Close();
444 return true;
445}
446
453{
454 _error_list.splice(_error_list.end(), datas);
455}
456
463{
464 _error_list.push_back(data);
465}
Common return value for all commands.
uint GetTextRefStackSize() const
Returns the number of uint32_t values for the TextRefStack of the error message.
const uint32_t * GetTextRefStack() const
Returns a pointer to the values for the TextRefStack of the error message.
const GRFFile * GetTextRefStackGRF() const
Returns the NewGRF providing the TextRefStack of the error message.
StringID GetErrorMessage() const
Returns the error message of a command.
StringID GetExtraErrorMessage() const
Returns the extra error message of a command.
The data of the error message.
Definition error.h:31
StringID extra_msg
Extra error message shown in third line. Can be INVALID_STRING_ID.
Definition error.h:40
bool HasFace() const
Check whether error window shall display a company manager face.
Definition error.h:52
CompanyID face
Company belonging to the face being shown. INVALID_COMPANY if no face present.
Definition error.h:42
uint textref_stack_size
Number of uint32_t values to put on the TextRefStack for the error message.
Definition error.h:36
void CopyOutDParams()
Copy error parameters from current DParams.
Point position
Position of the error message window.
Definition error.h:41
const GRFFile * textref_stack_grffile
NewGRF that filled the TextRefStack for the error message.
Definition error.h:35
void SetDParam(uint n, uint64_t v)
Set a error string parameter.
ErrorMessageData(const ErrorMessageData &data)
Copy the given data into our instance.
Definition error_gui.cpp:77
void SetDParamStr(uint n, const char *str)
Set a rawstring parameter.
uint32_t textref_stack[16]
Values to put on the TextRefStack for the error message.
Definition error.h:37
StringID detailed_msg
Detailed error message showed in second line. Can be INVALID_STRING_ID.
Definition error.h:39
std::vector< StringParameterData > params
Backup of parameters of the message strings.
Definition error.h:34
StringID summary_msg
General error message showed in first line. Must be valid.
Definition error.h:38
bool is_critical
Whether the error message is critical.
Definition error.h:33
A timeout timer will fire once after the interval.
Definition timer.h:116
void Reset()
Reset the timer, so it will fire again after the timeout.
Definition timer.h:140
static WidgetDimensions scaled
Widget dimensions scaled for current zoom level.
Definition window_gui.h:28
int vsep_wide
Wide vertical spacing.
Definition window_gui.h:62
static const WidgetDimensions unscaled
Unscaled widget dimensions.
Definition window_gui.h:96
Definition of stuff that is very close to a company, like the company struct itself.
Functions related to companies.
static const int OWNED_BY_OWNER_IN_PARAMETERS_OFFSET
The index in the parameters for the owner information.
void DrawCompanyManagerFace(CompanyManagerFace cmf, Colours colour, const Rect &r)
Draws the face of a company manager's face.
Functionality related to the company manager's face.
Owner
Enum for all companies/owners.
@ INVALID_COMPANY
An invalid company.
@ MAX_COMPANIES
Maximum number of companies.
void IConsolePrint(TextColour colour_code, const std::string &string)
Handle the printing of text entered into the console or redirected there by any other means.
Definition console.cpp:89
Console functions used outside of the console code.
static const TextColour CC_WARNING
Colour for warning lines.
static const TextColour CC_ERROR
Colour for error lines.
Functions related to errors.
std::list< ErrorMessageData > ErrorList
Define a queue with errors.
Definition error.h:62
WarningLevel
Message severity/type.
Definition error.h:23
@ WL_WARNING
Other information.
Definition error.h:25
@ WL_CRITICAL
Critical errors, the MessageBox is shown in all cases.
Definition error.h:27
@ WL_INFO
Used for DoCommand-like (and some non-fatal AI GUI) errors/information.
Definition error.h:24
static ErrorList _error_list
The actual queue with errors.
void UnshowCriticalError()
Unshow the critical error.
void ClearErrorMessages()
Clear all errors from the queue.
void ScheduleErrorMessage(ErrorList &datas)
Schedule a list of errors.
bool _window_system_initialized
Whether the window system is initialized or not.
bool HideActiveErrorMessage()
Close active error message window.
void ShowErrorMessage(StringID summary_msg, int x, int y, CommandCost cc)
Display an error message in a window.
void ShowFirstError()
Show the first error of the queue.
Types related to the error widgets.
@ WID_EM_CAPTION
Caption of the window.
@ WID_EM_MESSAGE
Error message.
@ WID_EM_FACE
Error title.
Dimension maxdim(const Dimension &d1, const Dimension &d2)
Compute bounding box of both dimensions.
Geometry functions.
int GetStringHeight(std::string_view str, int maxw, FontSize fontsize)
Calculates height of string (in pixels).
Definition gfx.cpp:704
int DrawStringMultiLine(int left, int right, int top, int bottom, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly over multiple lines.
Definition gfx.cpp:774
bool _right_button_down
Is right mouse button pressed?
Definition gfx.cpp:43
Functions related to the gfx engine.
Dimension GetScaledSpriteSize(SpriteID sprid)
Scale sprite size for GUI.
Definition widget.cpp:54
@ SA_CENTER
Center both horizontally and vertically.
Definition gfx_type.h:353
constexpr NWidgetPart SetFill(uint16_t fill_x, uint16_t fill_y)
Widget part function for setting filling.
constexpr NWidgetPart SetPadding(uint8_t top, uint8_t right, uint8_t bottom, uint8_t left)
Widget part function for setting additional space around a widget.
constexpr NWidgetPart SetDataTip(uint32_t data, StringID tip)
Widget part function for setting the data and tooltip.
constexpr NWidgetPart SetMinimalSize(int16_t x, int16_t y)
Widget part function for setting the minimal size.
constexpr NWidgetPart NWidget(WidgetType tp, Colours col, WidgetID idx=-1)
Widget part function for starting a new 'real' widget.
constexpr NWidgetPart EndContainer()
Widget part function for denoting the end of a container (horizontal, vertical, WWT_FRAME,...
Functions related to OTTD's landscape.
Functions related to memory operations.
void MemCpyT(T *destination, const T *source, size_t num=1)
Type-safe version of memcpy().
Definition mem_func.hpp:23
void StartTextRefStackUsage(const GRFFile *grffile, uint8_t numEntries, const uint32_t *values)
Start using the TTDP compatible string code parsing.
void StopTextRefStackUsage()
Stop using the TTDP compatible string code parsing.
Header of Action 04 "universal holder" structure and functions.
A number of safeguards to prevent using unsafe methods.
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:56
Definition of base types and functions in a cross-platform compatible way.
Functions related to low-level strings.
void AppendStringInPlace(std::string &result, StringID string)
Resolve the given StringID and append in place into an existing std::string with all the associated D...
Definition strings.cpp:345
uint64_t GetDParam(size_t n)
Get the current string parameter at index n from the global string parameter array.
Definition strings.cpp:114
std::string GetString(StringID string)
Resolve the given StringID into a std::string with all the associated DParam lookups and formatting.
Definition strings.cpp:333
void CopyOutDParam(std::vector< StringParameterData > &backup, size_t num)
Copy num string parameters from the global string parameter array to the backup.
Definition strings.cpp:171
void CopyInDParam(const std::span< const StringParameterData > backup)
Copy the parameters from the backup into the global string parameter array.
Definition strings.cpp:159
Functions related to OTTD's strings.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
static const StringID INVALID_STRING_ID
Constant representing an invalid string (16bit in case it is used in savegames)
GUISettings gui
settings related to the GUI
Colours colour
Company colour.
CompanyManagerFace face
Face description of the president.
Point pos
logical mouse position
Definition gfx_type.h:125
Dimensions (a width and height) of a rectangle in 2D.
Window class for displaying an error message window.
bool IsCritical()
Check whether the currently shown error message was critical or not.
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
void Close(int data=0) override
Hide the window and all its child windows, and mark them for a later deletion.
uint height_extra
Height of the extra_msg string in pixels in the WID_EM_MESSAGE widget.
void OnMouseLoop() override
Called for every mouse loop run, which is at least once per (game) tick.
Point OnInitialPosition(int16_t sm_width, int16_t sm_height, int window_number) override
Compute the initial position of the window.
uint height_detailed
Height of the detailed_msg string in pixels in the WID_EM_MESSAGE widget.
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
uint height_summary
Height of the summary_msg string in pixels in the WID_EM_MESSAGE widget.
void SetStringParameters(WidgetID widget) const override
Initialize string parameters for a widget.
void UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize) override
Update size and resize step of a widget in the window.
Dynamic data of a loaded NewGRF.
Definition newgrf.h:108
uint8_t errmsg_duration
duration of error message
Partial widget specification to allow NWidgets to be written nested.
Coordinates of a point in 2D.
static bool IsValidID(size_t index)
Tests whether given index can be used to get valid (non-nullptr) Titem.
static Titem * Get(size_t index)
Returns Titem with given index.
Specification of a rectangle with absolute coordinates of all edges.
Rect WithHeight(int height, bool end=false) const
Copy Rect and set its height.
int Height() const
Get height of Rect.
High level window description.
Definition window_gui.h:159
Data structure for an opened window.
Definition window_gui.h:273
virtual void Close(int data=0)
Hide the window and all its child windows, and mark them for a later deletion.
Definition window.cpp:1047
ResizeInfo resize
Resize information.
Definition window_gui.h:314
const NWID * GetWidget(WidgetID widnum) const
Get the nested widget with number widnum from the nested widget tree.
Definition window_gui.h:977
void InitNested(WindowNumber number=0)
Perform complete initialization of the Window with nested widgets, to allow use.
Definition window.cpp:1746
WindowNumber window_number
Window number within the window class.
Definition window_gui.h:302
constexpr TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition tile_type.h:95
Definition of Interval and OneShot timers.
Definition of the Window system.
void SetRedErrorSquare(TileIndex tile)
Set a tile to display a red error square.
Functions related to (drawing on) viewports.
@ NWID_HORIZONTAL
Horizontal container.
Definition widget_type.h:75
@ WWT_PANEL
Simple depressed panel.
Definition widget_type.h:50
@ WWT_CAPTION
Window caption (window title between closebox and stickybox)
Definition widget_type.h:61
@ WWT_CLOSEBOX
Close box (at top-left of a window)
Definition widget_type.h:69
@ WWT_EMPTY
Empty widget, place holder to reserve space in widget tree.
Definition widget_type.h:48
int GetMainViewTop()
Return the top of the main view available for general use.
Definition window.cpp:2062
Window * FindWindowById(WindowClass cls, WindowNumber number)
Find a window by its class and window number.
Definition window.cpp:1098
Window functions not directly related to making/drawing windows.
Functions, definitions and such used only by the GUI.
@ WDP_MANUAL
Manually align the window (so no automatic location finding)
Definition window_gui.h:146
int WidgetID
Widget ID.
Definition window_type.h:18
@ WC_ERRMSG
Error message; Window numbers:
@ WC_NONE
No window, redirects to WC_MAIN_WINDOW.
Definition window_type.h:45
Functions related to zooming.