OpenTTD Source 20260129-master-g2bb01bd0e4
newgrf_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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
10#include "stdafx.h"
11#include "error.h"
12#include "settings_gui.h"
13#include "newgrf.h"
14#include "strings_func.h"
15#include "window_func.h"
16#include "gamelog.h"
17#include "settings_type.h"
18#include "settings_func.h"
19#include "dropdown_type.h"
20#include "dropdown_func.h"
21#include "network/network.h"
23#include "sortlist_type.h"
24#include "stringfilter_type.h"
25#include "querystring_gui.h"
27#include "newgrf_text.h"
28#include "textfile_gui.h"
29#include "tilehighlight_func.h"
30#include "fios.h"
31#include "timer/timer.h"
32#include "timer/timer_window.h"
33#include "zoom_func.h"
35
37#include "widgets/misc_widget.h"
38
39#include "table/sprites.h"
40#include "table/strings.h"
41
42#include "safeguards.h"
43
48{
49 /* Do not show errors when entering the main screen */
50 if (_game_mode == GM_MENU) return;
51
52 for (const auto &c : _grfconfig) {
53 /* Only show Fatal and Error level messages */
54 if (c->errors.empty()) continue;
55
56 const GRFError &error = c->errors.back();
57 if (error.severity != STR_NEWGRF_ERROR_MSG_FATAL && error.severity != STR_NEWGRF_ERROR_MSG_ERROR) continue;
58
59 std::vector<StringParameter> params;
60 params.emplace_back(c->GetName());
61 params.emplace_back(error.message != STR_NULL ? error.message : STR_JUST_RAW_STRING);
62 params.emplace_back(error.custom_message);
63 params.emplace_back(c->filename);
64 params.emplace_back(error.data);
65 for (const uint32_t &value : error.param_value) params.emplace_back(value);
66
67 if (error.severity == STR_NEWGRF_ERROR_MSG_FATAL) {
68 ShowErrorMessage(GetEncodedStringWithArgs(STR_NEWGRF_ERROR_FATAL_POPUP, params), {}, WL_CRITICAL);
69 } else {
70 ShowErrorMessage(GetEncodedStringWithArgs(STR_NEWGRF_ERROR_POPUP, params), {}, WL_ERROR);
71 }
72 break;
73 }
74}
75
76static StringID GetGRFPaletteString(uint8_t palette)
77{
78 if (palette & GRFP_BLT_32BPP) {
79 return (palette & GRFP_USE_WINDOWS) ? STR_NEWGRF_SETTINGS_PALETTE_LEGACY_32BPP : STR_NEWGRF_SETTINGS_PALETTE_DEFAULT_32BPP;
80 }
81 return (palette & GRFP_USE_WINDOWS) ? STR_NEWGRF_SETTINGS_PALETTE_LEGACY : STR_NEWGRF_SETTINGS_PALETTE_DEFAULT;
82}
83
84static void ShowNewGRFInfo(const GRFConfig &c, const Rect &r, bool show_params)
85{
86 Rect tr = r.Shrink(WidgetDimensions::scaled.frametext);
87 for (const GRFError &error : c.errors) {
88 std::array<StringParameter, 3 + std::tuple_size_v<decltype(error.param_value)>> params{};
89 auto it = params.begin();
90 *it++ = error.custom_message; // is skipped by built-in messages
91 *it++ = c.filename;
92 *it++ = error.data;
93 for (const uint32_t &value : error.param_value) *it++ = value;
94
95 tr.top = DrawStringMultiLine(tr, GetString(error.severity, GetStringWithArgs(error.message != STR_NULL ? error.message : STR_JUST_RAW_STRING, {params.begin(), it})));
96 }
97
98 /* Draw filename or not if it is not known (GRF sent over internet) */
99 if (!c.filename.empty()) {
100 tr.top = DrawStringMultiLine(tr, GetString(STR_NEWGRF_SETTINGS_FILENAME, c.filename));
101 }
102
103 /* Prepare and draw GRF ID */
104 tr.top = DrawStringMultiLine(tr, GetString(STR_NEWGRF_SETTINGS_GRF_ID, fmt::format("{:08X}", std::byteswap(c.ident.grfid))));
105
107 tr.top = DrawStringMultiLine(tr, GetString(STR_NEWGRF_SETTINGS_VERSION, c.version));
108 }
110 tr.top = DrawStringMultiLine(tr, GetString(STR_NEWGRF_SETTINGS_MIN_VERSION, c.min_loadable_version));
111 }
112
113 /* Prepare and draw MD5 sum */
114 tr.top = DrawStringMultiLine(tr, GetString(STR_NEWGRF_SETTINGS_MD5SUM, FormatArrayAsHex(c.ident.md5sum)));
115
116 /* Show GRF parameter list */
117 if (show_params) {
118 if (!c.param.empty()) {
119 tr.top = DrawStringMultiLine(tr, GetString(STR_NEWGRF_SETTINGS_PARAMETER, STR_JUST_RAW_STRING, GRFBuildParamList(c)));
120 } else {
121 tr.top = DrawStringMultiLine(tr, GetString(STR_NEWGRF_SETTINGS_PARAMETER, STR_NEWGRF_SETTINGS_PARAMETER_NONE, std::monostate{}));
122 }
123
124 /* Draw the palette of the NewGRF */
125 tr.top = DrawStringMultiLine(tr, GetString(STR_NEWGRF_SETTINGS_PALETTE, GetGRFPaletteString(c.palette)));
126 }
127
128 /* Show flags */
129 if (c.status == GCS_NOT_FOUND) tr.top = DrawStringMultiLine(tr, STR_NEWGRF_SETTINGS_NOT_FOUND);
130 if (c.status == GCS_DISABLED) tr.top = DrawStringMultiLine(tr, STR_NEWGRF_SETTINGS_DISABLED);
131 if (c.flags.Test(GRFConfigFlag::Invalid)) tr.top = DrawStringMultiLine(tr, STR_NEWGRF_SETTINGS_INCOMPATIBLE);
132 if (c.flags.Test(GRFConfigFlag::Compatible)) tr.top = DrawStringMultiLine(tr, STR_NEWGRF_COMPATIBLE_LOADED);
133
134 /* Draw GRF info if it exists */
135 if (auto desc = c.GetDescription(); desc.has_value() && !desc->empty()) {
136 tr.top = DrawStringMultiLine(tr, GetString(STR_JUST_RAW_STRING, std::move(*desc)), TC_BLACK);
137 } else {
138 tr.top = DrawStringMultiLine(tr, STR_NEWGRF_SETTINGS_NO_INFO);
139 }
140}
141
148 int32_t clicked_button = INT32_MAX;
149 bool clicked_increase = false;
150 bool clicked_dropdown = false;
151 bool closing_dropdown = false;
152 int32_t clicked_row = INT32_MAX;
153 int line_height = 0;
154 Scrollbar *vscroll = nullptr;
155 bool action14present = false;
156 bool editable = false;
157
158 NewGRFParametersWindow(WindowDesc &desc, bool is_baseset, GRFConfig &c, bool editable) : Window(desc),
159 grf_config(c),
161 {
162 this->action14present = (this->grf_config.num_valid_params != GRFConfig::MAX_NUM_PARAMS || !this->grf_config.param_info.empty());
163
164 this->CreateNestedTree();
165 this->GetWidget<NWidgetCore>(WID_NP_CAPTION)->SetStringTip(is_baseset ? STR_BASEGRF_PARAMETERS_CAPTION : STR_NEWGRF_PARAMETERS_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS);
166 this->vscroll = this->GetScrollbar(WID_NP_SCROLLBAR);
167 this->GetWidget<NWidgetStacked>(WID_NP_SHOW_NUMPAR)->SetDisplayedPlane(this->action14present ? SZSP_HORIZONTAL : 0);
168 this->GetWidget<NWidgetStacked>(WID_NP_SHOW_DESCRIPTION)->SetDisplayedPlane(this->action14present ? 0 : SZSP_HORIZONTAL);
169 this->FinishInitNested(); // Initializes 'this->line_height' as side effect.
170
171 this->SetWidgetDisabledState(WID_NP_RESET, !this->editable);
172
173 this->InvalidateData();
174 }
175
186
192 bool HasParameterInfo(uint nr) const
193 {
194 return nr < this->grf_config.param_info.size() && this->grf_config.param_info[nr].has_value();
195 }
196
204 {
205 return this->HasParameterInfo(nr) ? this->grf_config.param_info[nr].value() : GetDummyParameterInfo(nr);
206 }
207
208 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
209 {
210 switch (widget) {
212 case WID_NP_NUMPAR_INC: {
213 size.width = std::max(SETTING_BUTTON_WIDTH / 2, GetCharacterHeight(FS_NORMAL));
214 size.height = std::max(SETTING_BUTTON_HEIGHT, GetCharacterHeight(FS_NORMAL));
215 break;
216 }
217
218 case WID_NP_NUMPAR: {
219 Dimension d = GetStringBoundingBox(GetString(this->GetWidget<NWidgetCore>(widget)->GetString(), GetParamMaxValue(GRFConfig::MAX_NUM_PARAMS)));
220 d.width += padding.width;
221 d.height += padding.height;
222 size = maxdim(size, d);
223 break;
224 }
225
227 this->line_height = std::max(SETTING_BUTTON_HEIGHT, GetCharacterHeight(FS_NORMAL)) + padding.height;
228
229 resize.width = 1;
230 fill.height = resize.height = this->line_height;
231 size.height = 5 * this->line_height;
232 break;
233
235 /* Minimum size of 4 lines. The 500 is the default size of the window. */
237 for (const auto &par_info : this->grf_config.param_info) {
238 if (!par_info.has_value()) continue;
239 auto desc = GetGRFStringFromGRFText(par_info->desc);
240 if (!desc.has_value()) continue;
241 Dimension d = GetStringMultiLineBoundingBox(*desc, suggestion);
243 suggestion = maxdim(d, suggestion);
244 }
245 size.height = suggestion.height;
246 break;
247 }
248 }
249
250 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
251 {
252 switch (widget) {
253 case WID_NP_NUMPAR:
254 return GetString(STR_NEWGRF_PARAMETERS_NUM_PARAM, this->vscroll->GetCount());
255
256 default:
257 return this->Window::GetWidgetString(widget, stringid);
258 }
259 }
260
261 std::pair<StringParameter, StringParameter> GetValueParams(const GRFParameterInfo &par_info, uint32_t value) const
262 {
263 if (par_info.type == PTYPE_BOOL) return {value != 0 ? STR_CONFIG_SETTING_ON : STR_CONFIG_SETTING_OFF, {}};
264
265 auto it = std::ranges::lower_bound(par_info.value_names, value, std::less{}, &GRFParameterInfo::ValueName::first);
266 if (it != std::end(par_info.value_names) && it->first == value) {
267 if (auto label = GetGRFStringFromGRFText(it->second); label.has_value()) return {STR_JUST_RAW_STRING, *label};
268 }
269
270 return {STR_JUST_INT, value};
271 }
272
273 std::string GetSettingString(const GRFParameterInfo &par_info, int i, uint32_t value) const
274 {
275 auto [param1, param2] = this->GetValueParams(par_info, value);
276 auto name = GetGRFStringFromGRFText(par_info.name);
277 return name.has_value()
278 ? GetString(STR_NEWGRF_PARAMETERS_SETTING, STR_JUST_RAW_STRING, std::string(*name), param1, param2)
279 : GetString(STR_NEWGRF_PARAMETERS_SETTING, STR_NEWGRF_PARAMETERS_DEFAULT_NAME, i + 1, param1, param2);
280 }
281
282 void DrawWidget(const Rect &r, WidgetID widget) const override
283 {
284 if (widget == WID_NP_DESCRIPTION) {
285 if (!this->HasParameterInfo(this->clicked_row)) return;
286 const GRFParameterInfo &par_info = this->GetParameterInfo(this->clicked_row);
287 auto desc = GetGRFStringFromGRFText(par_info.desc);
288 if (!desc.has_value()) return;
290 return;
291 } else if (widget != WID_NP_BACKGROUND) {
292 return;
293 }
294
295 Rect ir = r.Shrink(WidgetDimensions::scaled.frametext, RectPadding::zero);
296 bool rtl = _current_text_dir == TD_RTL;
297 uint buttons_left = rtl ? ir.right - SETTING_BUTTON_WIDTH : ir.left;
299
300 int button_y_offset = (this->line_height - SETTING_BUTTON_HEIGHT) / 2;
301 int text_y_offset = (this->line_height - GetCharacterHeight(FS_NORMAL)) / 2;
302 for (int32_t i = this->vscroll->GetPosition(); this->vscroll->IsVisible(i) && i < this->vscroll->GetCount(); i++) {
303 GRFParameterInfo &par_info = this->GetParameterInfo(i);
304 uint32_t current_value = this->grf_config.GetValue(par_info);
305 bool selected = (i == this->clicked_row);
306
307 if (par_info.type == PTYPE_BOOL) {
308 DrawBoolButton(buttons_left, ir.top + button_y_offset, COLOUR_YELLOW, COLOUR_MAUVE, current_value != 0, this->editable);
309 } else if (par_info.type == PTYPE_UINT_ENUM) {
310 if (par_info.complete_labels) {
311 DrawDropDownButton(buttons_left, ir.top + button_y_offset, COLOUR_YELLOW, this->clicked_row == i && this->clicked_dropdown, this->editable);
312 } else {
313 DrawArrowButtons(buttons_left, ir.top + button_y_offset, COLOUR_YELLOW, (this->clicked_button == i) ? 1 + (this->clicked_increase != rtl) : 0, this->editable && current_value > par_info.min_value, this->editable && current_value < par_info.max_value);
314 }
315 }
316
317 DrawString(tr.left, tr.right, ir.top + text_y_offset, this->GetSettingString(par_info, i, current_value), selected ? TC_WHITE : TC_LIGHT_BLUE);
318 ir.top += this->line_height;
319 }
320 }
321
322 void OnPaint() override
323 {
324 if (this->closing_dropdown) {
325 this->closing_dropdown = false;
326 this->clicked_dropdown = false;
327 }
328 this->DrawWidgets();
329 }
330
331 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
332 {
333 switch (widget) {
335 if (this->editable && !this->action14present && !this->grf_config.param.empty()) {
336 this->grf_config.param.pop_back();
337 this->InvalidateData();
339 }
340 break;
341
343 if (this->editable && !this->action14present && this->grf_config.param.size() < this->grf_config.num_valid_params) {
344 this->grf_config.param.emplace_back(0);
345 this->InvalidateData();
347 }
348 break;
349
350 case WID_NP_BACKGROUND: {
351 if (!this->editable) break;
352 int32_t num = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_NP_BACKGROUND);
353 if (num >= this->vscroll->GetCount()) break;
354
355 if (this->clicked_row != num) {
358 this->clicked_row = num;
359 this->clicked_dropdown = false;
360 }
361
362 Rect r = this->GetWidget<NWidgetBase>(widget)->GetCurrentRect().Shrink(WidgetDimensions::scaled.frametext, RectPadding::zero);
363 int x = pt.x - r.left;
364 if (_current_text_dir == TD_RTL) x = r.Width() - 1 - x;
365
366 GRFParameterInfo &par_info = this->GetParameterInfo(num);
367
368 /* One of the arrows is clicked */
369 uint32_t old_val = this->grf_config.GetValue(par_info);
370 if (par_info.type != PTYPE_BOOL && IsInsideMM(x, 0, SETTING_BUTTON_WIDTH) && par_info.complete_labels) {
371 if (this->clicked_dropdown) {
372 /* unclick the dropdown */
374 this->clicked_dropdown = false;
375 this->closing_dropdown = false;
376 } else {
377 int rel_y = (pt.y - r.top) % this->line_height;
378
379 Rect wi_rect;
380 wi_rect.left = pt.x - (_current_text_dir == TD_RTL ? SETTING_BUTTON_WIDTH - 1 - x : x);;
381 wi_rect.right = wi_rect.left + SETTING_BUTTON_WIDTH - 1;
382 wi_rect.top = pt.y - rel_y + (this->line_height - SETTING_BUTTON_HEIGHT) / 2;
383 wi_rect.bottom = wi_rect.top + SETTING_BUTTON_HEIGHT - 1;
384
385 /* For dropdowns we also have to check the y position thoroughly, the mouse may not above the just opening dropdown */
386 if (pt.y >= wi_rect.top && pt.y <= wi_rect.bottom) {
387 this->clicked_dropdown = true;
388 this->closing_dropdown = false;
389
390 DropDownList list;
391 for (const auto &[value, name] : par_info.value_names) {
392 auto text = GetGRFStringFromGRFText(name);
393 assert(text.has_value()); // ensured by "complete_labels"
394 list.push_back(MakeDropDownListStringItem(GetString(STR_JUST_RAW_STRING, std::string(*text)), value));
395 }
396
397 ShowDropDownListAt(this, std::move(list), old_val, WID_NP_SETTING_DROPDOWN, wi_rect, COLOUR_ORANGE);
398 }
399 }
400 } else if (IsInsideMM(x, 0, SETTING_BUTTON_WIDTH)) {
401 uint32_t val = old_val;
402 if (par_info.type == PTYPE_BOOL) {
403 val = !val;
404 } else {
405 if (x >= SETTING_BUTTON_WIDTH / 2) {
406 /* Increase button clicked */
407 if (val < par_info.max_value) val++;
408 this->clicked_increase = true;
409 } else {
410 /* Decrease button clicked */
411 if (val > par_info.min_value) val--;
412 this->clicked_increase = false;
413 }
414 }
415 if (val != old_val) {
416 this->grf_config.SetValue(par_info, val);
417
418 this->clicked_button = num;
419 this->unclick_timeout.Reset();
420 }
421 } else if (par_info.type == PTYPE_UINT_ENUM && !par_info.complete_labels && click_count >= 2) {
422 /* Display a query box so users can enter a custom value. */
423 ShowQueryString(GetString(STR_JUST_INT, old_val), STR_CONFIG_SETTING_QUERY_CAPTION, 10, this, CS_NUMERAL, {});
424 }
425 this->SetDirty();
426 break;
427 }
428
429 case WID_NP_RESET:
430 if (!this->editable) break;
431 this->grf_config.SetParameterDefaults();
432 this->InvalidateData();
434 break;
435 }
436 }
437
438 void OnQueryTextFinished(std::optional<std::string> str) override
439 {
440 if (!str.has_value() || str->empty()) return;
441 auto value = ParseInteger<int32_t>(*str, 10, true);
442 if (!value.has_value()) return;
443 GRFParameterInfo &par_info = this->GetParameterInfo(this->clicked_row);
444 this->grf_config.SetValue(par_info, *value);
445 this->SetDirty();
446 }
447
448 void OnDropdownSelect(WidgetID widget, int index, int) override
449 {
450 if (widget != WID_NP_SETTING_DROPDOWN) return;
451 assert(this->clicked_dropdown);
452 GRFParameterInfo &par_info = this->GetParameterInfo(this->clicked_row);
453 this->grf_config.SetValue(par_info, index);
454 this->SetDirty();
455 }
456
457 void OnDropdownClose(Point, WidgetID widget, int, int, bool) override
458 {
459 if (widget != WID_NP_SETTING_DROPDOWN) return;
460 /* We cannot raise the dropdown button just yet. OnClick needs some hint, whether
461 * the same dropdown button was clicked again, and then not open the dropdown again.
462 * So, we only remember that it was closed, and process it on the next OnPaint, which is
463 * after OnClick. */
464 assert(this->clicked_dropdown);
465 this->closing_dropdown = true;
466 this->SetDirty();
467 }
468
469 void OnResize() override
470 {
471 this->vscroll->SetCapacityFromWidget(this, WID_NP_BACKGROUND);
472 }
473
479 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
480 {
481 if (!gui_scope) return;
482 if (!this->action14present) {
483 this->SetWidgetDisabledState(WID_NP_NUMPAR_DEC, !this->editable || this->grf_config.param.empty());
484 this->SetWidgetDisabledState(WID_NP_NUMPAR_INC, !this->editable || std::size(this->grf_config.param) >= this->grf_config.num_valid_params);
485 }
486
487 this->vscroll->SetCount(this->action14present ? this->grf_config.num_valid_params : this->grf_config.param.size());
488 if (this->clicked_row != INT32_MAX && this->clicked_row >= this->vscroll->GetCount()) {
489 this->clicked_row = INT32_MAX;
491 }
492 }
493
495 TimeoutTimer<TimerWindow> unclick_timeout = {std::chrono::milliseconds(150), [this]() {
496 this->clicked_button = INT32_MAX;
497 this->SetDirty();
498 }};
499};
501
502
503static constexpr std::initializer_list<NWidgetPart> _nested_newgrf_parameter_widgets = {
505 NWidget(WWT_CLOSEBOX, COLOUR_MAUVE),
506 NWidget(WWT_CAPTION, COLOUR_MAUVE, WID_NP_CAPTION),
507 NWidget(WWT_DEFSIZEBOX, COLOUR_MAUVE),
508 EndContainer(),
509 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_NP_SHOW_NUMPAR),
510 NWidget(WWT_PANEL, COLOUR_MAUVE), SetResize(1, 0), SetFill(1, 0), SetPIP(4, 0, 4),
511 NWidget(NWID_HORIZONTAL), SetPIP(4, 0, 4),
514 NWidget(WWT_TEXT, INVALID_COLOUR, WID_NP_NUMPAR), SetResize(1, 0), SetFill(1, 0), SetPadding(0, 0, 0, 4),
515 EndContainer(),
516 EndContainer(),
517 EndContainer(),
521 EndContainer(),
523 NWidget(WWT_PANEL, COLOUR_MAUVE, WID_NP_DESCRIPTION), SetResize(1, 0), SetFill(1, 0),
524 EndContainer(),
525 EndContainer(),
527 NWidget(WWT_PUSHTXTBTN, COLOUR_MAUVE, WID_NP_RESET), SetStringTip(STR_NEWGRF_PARAMETERS_RESET, STR_NEWGRF_PARAMETERS_RESET_TOOLTIP),
528 NWidget(WWT_PANEL, COLOUR_MAUVE), SetResize(1, 0), SetFill(1, 0),
529 EndContainer(),
530 NWidget(WWT_RESIZEBOX, COLOUR_MAUVE),
531 EndContainer(),
532};
533
536 WDP_CENTER, "settings_newgrf_config", 500, 208,
538 {},
539 _nested_newgrf_parameter_widgets
540);
541
542void OpenGRFParameterWindow(bool is_baseset, GRFConfig &c, bool editable)
543{
545 new NewGRFParametersWindow(_newgrf_parameters_desc, is_baseset, c, editable);
546}
547
550 const GRFConfig *grf_config = nullptr;
551
553 {
554 this->ConstructWindow();
555
556 auto textfile = this->grf_config->GetTextfile(file_type);
557 this->LoadTextfile(textfile.value(), NEWGRF_DIR);
558 }
559
560 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
561 {
562 if (widget == WID_TF_CAPTION) {
563 return GetString(stringid, STR_CONTENT_TYPE_NEWGRF, this->grf_config->GetName());
564 }
565
566 return this->Window::GetWidgetString(widget, stringid);
567 }
568};
569
570void ShowNewGRFTextfileWindow(Window *parent, TextfileType file_type, const GRFConfig *c)
571{
572 parent->CloseChildWindowById(WC_TEXTFILE, file_type);
573 new NewGRFTextfileWindow(parent, file_type, c);
574}
575
576typedef std::map<uint32_t, const GRFConfig *> GrfIdMap;
577
583static void FillGrfidMap(const GRFConfigList &lst, GrfIdMap &grfid_map)
584{
585 for (const auto &c : lst) {
586 grfid_map.emplace(c->ident.grfid, c.get());
587 }
588}
589
590static void NewGRFConfirmationCallback(Window *w, bool confirmed);
591static void ShowSavePresetWindow(std::string_view initial_text);
592
598
599 static const uint EDITBOX_MAX_SIZE = 50;
600
601 static Listing last_sorting;
603 static const std::initializer_list<GUIGRFConfigList::SortFunction * const> sorter_funcs;
604 static const std::initializer_list<GUIGRFConfigList::FilterFunction * const> filter_funcs;
605
607 const GRFConfig *avail_sel = nullptr;
608 int avail_pos = -1;
611
613
614 GRFConfigList actives{};
616
617 GRFConfigList &orig_list;
618 bool editable = false;
619 bool show_params = false;
620 bool execute = false;
621 int preset = -1;
622 int active_over = -1;
623 bool modified = false;
624
625 Scrollbar *vscroll = nullptr;
626 Scrollbar *vscroll2 = nullptr;
627
628 NewGRFWindow(WindowDesc &desc, bool editable, bool show_params, bool execute, GRFConfigList &orig_list) : Window(desc), filter_editbox(EDITBOX_MAX_SIZE), orig_list(orig_list)
629 {
630 this->editable = editable;
631 this->execute = execute;
632 this->show_params = show_params;
633
634 CopyGRFConfigList(this->actives, orig_list, false);
636
637 this->CreateNestedTree();
638 this->vscroll = this->GetScrollbar(WID_NS_SCROLLBAR);
639 this->vscroll2 = this->GetScrollbar(WID_NS_SCROLL2BAR);
640
641 this->GetWidget<NWidgetStacked>(WID_NS_SHOW_REMOVE)->SetDisplayedPlane(this->editable ? 0 : 1);
642 this->GetWidget<NWidgetStacked>(WID_NS_SHOW_EDIT)->SetDisplayedPlane(this->editable ? 0 : (this->show_params ? 1 : SZSP_HORIZONTAL));
643 this->GetWidget<NWidgetStacked>(WID_NS_SHOW_APPLY)->SetDisplayedPlane(this->editable && this->execute ? 0 : SZSP_VERTICAL);
645
647 this->filter_editbox.cancel_button = QueryString::ACTION_CLEAR;
648 if (editable) {
650 } else {
652 }
653
654 this->avails.SetListing(this->last_sorting);
655 this->avails.SetFiltering(this->last_filtering);
656 this->avails.SetSortFuncs(this->sorter_funcs);
657 this->avails.SetFilterFuncs(this->filter_funcs);
658 this->avails.ForceRebuild();
659
661 }
662
663 void Close([[maybe_unused]] int data = 0) override
664 {
667
668 if (this->editable && this->modified && !this->execute && !_exit_game) {
669 CopyGRFConfigList(this->orig_list, this->actives, true);
670 ResetGRFConfig(false);
672 }
673
674 this->Window::Close();
675 }
676
677 int GetCurrentActivePosition() const
678 {
679 if (this->active_sel != nullptr) {
680 auto it = std::ranges::find_if(this->actives, [this](const auto &c) { return c.get() == this->active_sel; });
681 if (it != std::end(this->actives)) return static_cast<int>(std::distance(std::begin(this->actives), it));
682 }
683 return -1;
684 }
685
691 {
692 GrfIdMap grfid_map;
693 FillGrfidMap(this->actives, grfid_map);
694
695 for (const auto &a : _all_grfs) {
696 GrfIdMap::const_iterator iter = grfid_map.find(a->ident.grfid);
697 if (iter != grfid_map.end() && a->version > iter->second->version) return true;
698 }
699 return false;
700 }
701
704 {
705 GrfIdMap grfid_map;
706 FillGrfidMap(this->actives, grfid_map);
707
708 for (const auto &a : _all_grfs) {
709 GrfIdMap::iterator iter = grfid_map.find(a->ident.grfid);
710 if (iter == grfid_map.end() || iter->second->version >= a->version) continue;
711
712 auto c = std::ranges::find_if(this->actives, [&iter](const auto &grfconfig) { return grfconfig.get() == iter->second; });
713 assert(c != std::end(this->actives));
714 auto d = std::make_unique<GRFConfig>(*a);
715 if (d->IsCompatible((*c)->version)) {
716 d->CopyParams(**c);
717 } else {
718 d->SetParameterDefaults();
719 }
720 if (this->active_sel == c->get()) {
723 this->active_sel = nullptr;
724 }
725 *c = std::move(d);
726 iter->second = c->get();
727 }
728 }
729
730 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
731 {
732 switch (widget) {
733 case WID_NS_FILE_LIST:
734 {
735 Dimension d = maxdim(GetScaledSpriteSize(SPR_SQUARE), GetScaledSpriteSize(SPR_WARNING_SIGN));
736 fill.height = resize.height = std::max<uint>(d.height + 2U, GetCharacterHeight(FS_NORMAL));
737 size.height = std::max(size.height, padding.height + 6 * resize.height);
738 break;
739 }
740
742 {
743 Dimension d = maxdim(GetScaledSpriteSize(SPR_SQUARE), GetScaledSpriteSize(SPR_WARNING_SIGN));
744 fill.height = resize.height = std::max<uint>(d.height + 2U, GetCharacterHeight(FS_NORMAL));
745 size.height = std::max(size.height, padding.height + 8 * resize.height);
746 break;
747 }
748
750 Dimension dim = GetStringBoundingBox(STR_NEWGRF_SETTINGS_INFO_TITLE);
751 size.height = std::max(size.height, dim.height + WidgetDimensions::scaled.frametext.Vertical());
752 size.width = std::max(size.width, dim.width + WidgetDimensions::scaled.frametext.Horizontal());
753 break;
754 }
755
757 size.height = std::max<uint>(size.height, WidgetDimensions::scaled.framerect.Vertical() + 10 * GetCharacterHeight(FS_NORMAL));
758 break;
759
760 case WID_NS_PRESET_LIST: {
761 Dimension d = GetStringBoundingBox(STR_NUM_CUSTOM);
762 for (const auto &i : this->grf_presets) {
763 d = maxdim(d, GetStringBoundingBox(GetString(STR_JUST_RAW_STRING, i)));
764 }
765 d.width += padding.width;
766 size = maxdim(d, size);
767 break;
768 }
769
772 Dimension d = GetStringBoundingBox(STR_NEWGRF_SETTINGS_FIND_MISSING_CONTENT_BUTTON);
773 size = maxdim(d, GetStringBoundingBox(STR_INTRO_ONLINE_CONTENT));
774 size.width += padding.width;
775 size.height += padding.height;
776 break;
777 }
778 }
779 }
780
781 void OnResize() override
782 {
783 this->vscroll->SetCapacityFromWidget(this, WID_NS_FILE_LIST, WidgetDimensions::scaled.framerect.Vertical());
784 this->vscroll2->SetCapacityFromWidget(this, WID_NS_AVAIL_LIST, WidgetDimensions::scaled.framerect.Vertical());
785 }
786
787 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
788 {
789 switch (widget) {
791 if (this->preset == -1) return GetString(STR_NUM_CUSTOM);
792
793 return this->grf_presets[this->preset];
794
795 default:
796 return this->Window::GetWidgetString(widget, stringid);
797 }
798 }
799
805 inline PaletteID GetPalette(const GRFConfig &c) const
806 {
807 PaletteID pal;
808
809 /* Pick a colour */
810 switch (c.status) {
811 case GCS_NOT_FOUND:
812 case GCS_DISABLED:
813 pal = PALETTE_TO_RED;
814 break;
815 case GCS_ACTIVATED:
816 pal = PALETTE_TO_GREEN;
817 break;
818 default:
819 pal = PALETTE_TO_BLUE;
820 break;
821 }
822
823 /* Do not show a "not-failure" colour when it actually failed to load */
824 if (pal != PALETTE_TO_RED) {
826 pal = PALETTE_TO_GREY;
827 } else if (c.flags.Test(GRFConfigFlag::Compatible)) {
828 pal = PALETTE_TO_ORANGE;
829 }
830 }
831
832 return pal;
833 }
834
835 void DrawWidget(const Rect &r, WidgetID widget) const override
836 {
837 switch (widget) {
838 case WID_NS_FILE_LIST: {
839 const Rect br = r.Shrink(WidgetDimensions::scaled.bevel);
841
842 Rect tr = r.Shrink(WidgetDimensions::scaled.framerect);
843 uint step_height = this->GetWidget<NWidgetBase>(WID_NS_FILE_LIST)->resize_y;
844 Dimension square = GetSpriteSize(SPR_SQUARE);
845 Dimension warning = GetSpriteSize(SPR_WARNING_SIGN);
846 int square_offset_y = (step_height - square.height) / 2;
847 int warning_offset_y = (step_height - warning.height) / 2;
848 int offset_y = (step_height - GetCharacterHeight(FS_NORMAL)) / 2;
849
850 bool rtl = _current_text_dir == TD_RTL;
851 uint text_left = rtl ? tr.left : tr.left + square.width + 13;
852 uint text_right = rtl ? tr.right - square.width - 13 : tr.right;
853 uint square_left = rtl ? tr.right - square.width - 3 : tr.left + 3;
854 uint warning_left = rtl ? tr.right - square.width - warning.width - 8 : tr.left + square.width + 8;
855
856 int i = 0;
857 for (const auto &c : this->actives) {
858 if (this->vscroll->IsVisible(i)) {
859 std::string text = c->GetName();
860 bool h = (this->active_sel == c.get());
861 PaletteID pal = this->GetPalette(*c);
862
863 if (h) {
864 GfxFillRect(br.left, tr.top, br.right, tr.top + step_height - 1, PC_DARK_BLUE);
865 } else if (i == this->active_over) {
866 /* Get index of current selection. */
867 int active_sel_pos = this->GetCurrentActivePosition();
868 if (active_sel_pos != this->active_over) {
869 uint top = (active_sel_pos < 0 || this->active_over < active_sel_pos) ? tr.top + 1 : tr.top + step_height - 2;
870 GfxFillRect(tr.left, top - 1, tr.right, top + 1, PC_GREY);
871 }
872 }
873 DrawSprite(SPR_SQUARE, pal, square_left, tr.top + square_offset_y);
874 if (!c->errors.empty()) DrawSprite(SPR_WARNING_SIGN, 0, warning_left, tr.top + warning_offset_y);
875 uint txtoffset = c->errors.empty() ? 0 : warning.width;
876 DrawString(text_left + (rtl ? 0 : txtoffset), text_right - (rtl ? txtoffset : 0), tr.top + offset_y, std::move(text), h ? TC_WHITE : TC_ORANGE);
877 tr.top += step_height;
878 }
879 i++;
880 }
881 if (i == this->active_over && this->vscroll->IsVisible(i)) { // Highlight is after the last GRF entry.
882 GfxFillRect(tr.left, tr.top, tr.right, tr.top + 2, PC_GREY);
883 }
884 break;
885 }
886
887 case WID_NS_AVAIL_LIST: {
888 const Rect br = r.Shrink(WidgetDimensions::scaled.bevel);
889 GfxFillRect(br, this->active_over == -2 ? PC_DARK_GREY : PC_BLACK);
890
891 Rect tr = r.Shrink(WidgetDimensions::scaled.framerect);
892 uint step_height = this->GetWidget<NWidgetBase>(WID_NS_AVAIL_LIST)->resize_y;
893 int offset_y = (step_height - GetCharacterHeight(FS_NORMAL)) / 2;
894
895 auto [first, last] = this->vscroll2->GetVisibleRangeIterators(this->avails);
896 for (auto it = first; it != last; ++it) {
897 const GRFConfig *c = *it;
898 bool h = (c == this->avail_sel);
899 std::string text = c->GetName();
900
901 if (h) GfxFillRect(br.left, tr.top, br.right, tr.top + step_height - 1, PC_DARK_BLUE);
902 DrawString(tr.left, tr.right, tr.top + offset_y, std::move(text), h ? TC_WHITE : TC_SILVER);
903 tr.top += step_height;
904 }
905 break;
906 }
907
909 /* Create the nice darker rectangle at the details top. */
910 GfxFillRect(r.Shrink(WidgetDimensions::scaled.bevel), GetColourGradient(COLOUR_MAUVE, SHADE_NORMAL));
911 DrawString(r.left, r.right, CentreBounds(r.top, r.bottom, GetCharacterHeight(FS_NORMAL)), STR_NEWGRF_SETTINGS_INFO_TITLE, TC_FROMSTRING, SA_HOR_CENTER);
912 break;
913 }
914
915 case WID_NS_NEWGRF_INFO: {
916 const GRFConfig *selected = this->active_sel;
917 if (selected == nullptr) selected = this->avail_sel;
918 if (selected != nullptr) {
919 ShowNewGRFInfo(*selected, r, this->show_params);
920 }
921 break;
922 }
923 }
924 }
925
926 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
927 {
928 if (widget >= WID_NS_NEWGRF_TEXTFILE && widget < WID_NS_NEWGRF_TEXTFILE + TFT_CONTENT_END) {
929 if (this->active_sel == nullptr && this->avail_sel == nullptr) return;
930
931 ShowNewGRFTextfileWindow(this, (TextfileType)(widget - WID_NS_NEWGRF_TEXTFILE), this->active_sel != nullptr ? this->active_sel : this->avail_sel);
932 return;
933 }
934
935 switch (widget) {
936 case WID_NS_PRESET_LIST: {
937 DropDownList list;
938
939 /* Add 'None' option for clearing list */
940 list.push_back(MakeDropDownListStringItem(STR_NONE, -1));
941
942 for (uint i = 0; i < this->grf_presets.size(); i++) {
943 list.push_back(MakeDropDownListStringItem(std::string{this->grf_presets[i]}, i));
944 }
945
946 this->CloseChildWindows(WC_QUERY_STRING); // Remove the parameter query window
947 ShowDropDownList(this, std::move(list), this->preset, WID_NS_PRESET_LIST);
948 break;
949 }
950
951 case WID_NS_OPEN_URL: {
952 const GRFConfig *c = (this->avail_sel == nullptr) ? this->active_sel : this->avail_sel;
953 auto url = c->GetURL();
954 if (url.has_value()) OpenBrowser(std::move(*url));
955 break;
956 }
957
959 ShowSavePresetWindow((this->preset == -1) ? std::string_view{} : this->grf_presets[this->preset]);
960 break;
961
963 if (this->preset == -1) return;
964
965 DeleteGRFPresetFromConfig(this->grf_presets[this->preset]);
967 this->preset = -1;
968 this->InvalidateData();
969 this->CloseChildWindows(WC_QUERY_STRING); // Remove the parameter query window
970 break;
971
972 case WID_NS_MOVE_UP: { // Move GRF up
973 if (this->active_sel == nullptr || !this->editable) break;
974
975 int pos = this->GetCurrentActivePosition();
976 if (pos <= 0) break;
977
978 std::swap(this->actives[pos - 1], this->actives[pos]);
979
980 this->vscroll->ScrollTowards(pos - 1);
981 this->preset = -1;
983 break;
984 }
985
986 case WID_NS_MOVE_DOWN: { // Move GRF down
987 if (this->active_sel == nullptr || !this->editable) break;
988
989 int pos = this->GetCurrentActivePosition();
990 if (pos == -1 || static_cast<size_t>(pos) >= this->actives.size() - 1) break;
991
992 std::swap(this->actives[pos], this->actives[pos + 1]);
993
994 this->vscroll->ScrollTowards(pos + 1);
995 this->preset = -1;
997 break;
998 }
999
1000 case WID_NS_FILE_LIST: { // Select an active GRF.
1002
1003 const GRFConfig *old_sel = this->active_sel;
1004 uint i = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_NS_FILE_LIST, WidgetDimensions::scaled.framerect.top);
1005 if (i < this->actives.size()) {
1006 this->active_sel = this->actives[i].get();
1007 } else {
1008 this->active_sel = nullptr;
1009 }
1010 if (this->active_sel != old_sel) {
1013 }
1014 this->avail_sel = nullptr;
1015 this->avail_pos = -1;
1016
1017 this->InvalidateData();
1018 if (click_count == 1) {
1019 if (this->editable && this->active_sel != nullptr) SetObjectToPlaceWnd(SPR_CURSOR_MOUSE, PAL_NONE, HT_DRAG, this);
1020 break;
1021 }
1022 /* With double click, continue */
1023 [[fallthrough]];
1024 }
1025
1026 case WID_NS_REMOVE: { // Remove GRF
1027 if (this->active_sel == nullptr || !this->editable) break;
1030
1031 /* Choose the next GRF file to be the selected file. */
1032 int pos = this->GetCurrentActivePosition();
1033 if (pos < 0) break;
1034
1035 auto it = std::next(std::begin(this->actives), pos);
1036 it = this->actives.erase(it);
1037 if (this->actives.empty()) {
1038 this->active_sel = nullptr;
1039 } else if (it == std::end(this->actives)) {
1040 this->active_sel = this->actives.back().get();
1041 } else {
1042 this->active_sel = it->get();
1043 }
1044 this->preset = -1;
1045 this->avail_pos = -1;
1046 this->avail_sel = nullptr;
1047 this->avails.ForceRebuild();
1049 break;
1050 }
1051
1052 case WID_NS_UPGRADE: { // Upgrade GRF.
1053 if (!this->editable || this->actives.empty()) break;
1056 break;
1057 }
1058
1059 case WID_NS_AVAIL_LIST: { // Select a non-active GRF.
1061
1062 auto it = this->vscroll2->GetScrolledItemFromWidget(this->avails, pt.y, this, WID_NS_AVAIL_LIST, WidgetDimensions::scaled.framerect.top);
1063 this->active_sel = nullptr;
1065 if (it != std::end(this->avails)) {
1066 if (this->avail_sel != *it) this->CloseChildWindows(WC_TEXTFILE);
1067 this->avail_sel = *it;
1068 this->avail_pos = static_cast<int>(std::distance(std::begin(this->avails), it));
1069 }
1070 this->InvalidateData();
1071 if (click_count == 1) {
1072 if (this->editable && this->avail_sel != nullptr && !this->avail_sel->flags.Test(GRFConfigFlag::Invalid)) SetObjectToPlaceWnd(SPR_CURSOR_MOUSE, PAL_NONE, HT_DRAG, this);
1073 break;
1074 }
1075 /* With double click, continue */
1076 [[fallthrough]];
1077 }
1078
1079 case WID_NS_ADD:
1080 if (this->avail_sel == nullptr || !this->editable || this->avail_sel->flags.Test(GRFConfigFlag::Invalid)) break;
1081
1082 this->AddGRFToActive();
1083 break;
1084
1085 case WID_NS_APPLY_CHANGES: // Apply changes made to GRF list
1086 if (!this->editable) break;
1087
1088 ShowQuery(
1089 GetEncodedString(STR_NEWGRF_POPUP_CAUTION_CAPTION),
1090 GetEncodedString(STR_NEWGRF_CONFIRMATION_TEXT),
1091 this,
1093 );
1094
1095 this->CloseChildWindows(WC_QUERY_STRING); // Remove the parameter query window
1096 break;
1097
1099 case WID_NS_SET_PARAMETERS: { // Edit parameters
1100 if (this->active_sel == nullptr || !this->show_params || this->active_sel->num_valid_params == 0) break;
1101
1102 OpenGRFParameterWindow(false, *this->active_sel, this->editable);
1104 break;
1105 }
1106
1108 if (this->active_sel != nullptr && this->editable) {
1109 this->active_sel->palette ^= GRFP_USE_MASK;
1110 this->SetDirty();
1112 }
1113 break;
1114
1117 if (!_network_available) {
1118 ShowErrorMessage(GetEncodedString(STR_NETWORK_ERROR_NOTAVAILABLE), {}, WL_ERROR);
1119 } else {
1120 this->CloseChildWindows(WC_QUERY_STRING); // Remove the parameter query window
1121
1123 }
1124 break;
1125
1128 RequestNewGRFScan(this);
1129 break;
1130 }
1131 }
1132
1133 void OnNewGRFsScanned() override
1134 {
1135 if (this->active_sel == nullptr) this->CloseChildWindows(WC_TEXTFILE);
1136 this->avail_sel = nullptr;
1137 this->avail_pos = -1;
1138 this->avails.ForceRebuild();
1139 this->CloseChildWindows(WC_QUERY_STRING); // Remove the parameter query window
1140 }
1141
1142 void OnDropdownSelect(WidgetID widget, int index, int) override
1143 {
1144 if (widget != WID_NS_PRESET_LIST) return;
1145 if (!this->editable) return;
1146
1148 this->preset = index;
1149
1150 if (index != -1) {
1151 this->actives = LoadGRFPresetFromConfig(this->grf_presets[index]);
1152 }
1153 this->avails.ForceRebuild();
1154
1158 this->active_sel = nullptr;
1160 }
1161
1162 void OnQueryTextFinished(std::optional<std::string> str) override
1163 {
1164 if (!str.has_value()) return;
1165
1166 SaveGRFPresetToConfig(*str, this->actives);
1167 this->grf_presets = GetGRFPresetList();
1168
1169 /* Switch to this preset */
1170 for (uint i = 0; i < this->grf_presets.size(); i++) {
1171 if (this->grf_presets[i] == str) {
1172 this->preset = i;
1173 break;
1174 }
1175 }
1176
1177 this->InvalidateData();
1178 }
1179
1184 {
1185 /* Update scrollbars */
1186 this->vscroll->SetCount(this->actives.size() + 1); // Reserve empty space for drag and drop handling.
1187
1188 if (this->avail_pos >= 0) this->vscroll2->ScrollTowards(this->avail_pos);
1189 }
1190
1196 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
1197 {
1198 if (!gui_scope) return;
1199 switch (data) {
1200 default:
1201 /* Nothing important to do */
1202 break;
1203
1205 /* Search the list for items that are now found and mark them as such. */
1206 for (auto &c : this->actives) {
1207 bool compatible = c->flags.Test(GRFConfigFlag::Compatible);
1208 if (c->status != GCS_NOT_FOUND && !compatible) continue;
1209
1210 const GRFConfig *f = FindGRFConfig(c->ident.grfid, FGCM_EXACT, compatible ? &c->original_md5sum : &c->ident.md5sum);
1211 if (f == nullptr || f->flags.Test(GRFConfigFlag::Invalid)) continue;
1212
1213 c = std::make_unique<GRFConfig>(*f);
1214 }
1215
1216 this->avails.ForceRebuild();
1217 [[fallthrough]];
1218
1220 this->modified = false;
1222 break;
1223
1225 this->preset = -1;
1226 [[fallthrough]];
1227
1230
1231 /* Changes have been made to the list of active NewGRFs */
1232 this->modified = true;
1233
1234 break;
1235 }
1236
1237 this->BuildAvailables();
1238
1239 this->SetWidgetDisabledState(WID_NS_APPLY_CHANGES, !((this->editable && this->modified) || _settings_client.gui.newgrf_developer_tools));
1240 this->SetWidgetsDisabledState(!this->editable,
1243 );
1244 this->SetWidgetDisabledState(WID_NS_ADD, !this->editable || this->avail_sel == nullptr || this->avail_sel->flags.Test(GRFConfigFlag::Invalid));
1245 this->SetWidgetDisabledState(WID_NS_UPGRADE, !this->editable || this->actives.empty() || !this->CanUpgradeCurrent());
1246
1247 bool disable_all = this->active_sel == nullptr || !this->editable;
1248 this->SetWidgetsDisabledState(disable_all,
1252 );
1253
1254 const GRFConfig *selected_config = (this->avail_sel == nullptr) ? this->active_sel : this->avail_sel;
1255 for (TextfileType tft = TFT_CONTENT_BEGIN; tft < TFT_CONTENT_END; tft++) {
1256 this->SetWidgetDisabledState(WID_NS_NEWGRF_TEXTFILE + tft, selected_config == nullptr || !selected_config->GetTextfile(tft).has_value());
1257 }
1258 this->SetWidgetDisabledState(WID_NS_OPEN_URL, selected_config == nullptr || !selected_config->GetURL().has_value());
1259
1260 this->SetWidgetDisabledState(WID_NS_SET_PARAMETERS, !this->show_params || this->active_sel == nullptr || this->active_sel->num_valid_params == 0);
1261 this->SetWidgetDisabledState(WID_NS_VIEW_PARAMETERS, !this->show_params || this->active_sel == nullptr || this->active_sel->num_valid_params == 0);
1262 this->SetWidgetDisabledState(WID_NS_TOGGLE_PALETTE, disable_all ||
1264
1265 if (!disable_all) {
1266 /* All widgets are now enabled, so disable widgets we can't use */
1267 if (this->active_sel == this->actives.front().get()) this->DisableWidget(WID_NS_MOVE_UP);
1268 if (this->active_sel == this->actives.back().get()) this->DisableWidget(WID_NS_MOVE_DOWN);
1269 }
1270
1271 this->SetWidgetDisabledState(WID_NS_PRESET_DELETE, this->preset == -1);
1272
1273 bool has_missing = false;
1274 bool has_compatible = false;
1275 for (const auto &c : this->actives) {
1276 has_missing |= c->status == GCS_NOT_FOUND;
1277 has_compatible |= c->flags.Test(GRFConfigFlag::Compatible);
1278 }
1279 StringID text;
1280 StringID tool_tip;
1281 if (has_missing || has_compatible) {
1282 text = STR_NEWGRF_SETTINGS_FIND_MISSING_CONTENT_BUTTON;
1283 tool_tip = STR_NEWGRF_SETTINGS_FIND_MISSING_CONTENT_TOOLTIP;
1284 } else {
1285 text = STR_INTRO_ONLINE_CONTENT;
1286 tool_tip = STR_INTRO_TOOLTIP_ONLINE_CONTENT;
1287 }
1288 this->GetWidget<NWidgetCore>(WID_NS_CONTENT_DOWNLOAD)->SetStringTip(text, tool_tip);
1289 this->GetWidget<NWidgetCore>(WID_NS_CONTENT_DOWNLOAD2)->SetStringTip(text, tool_tip);
1290
1291 this->SetWidgetDisabledState(WID_NS_PRESET_SAVE, has_missing);
1292 }
1293
1294 EventState OnKeyPress([[maybe_unused]] char32_t key, uint16_t keycode) override
1295 {
1296 if (!this->editable) return ES_NOT_HANDLED;
1297
1298 if (this->vscroll2->UpdateListPositionOnKeyPress(this->avail_pos, keycode) == ES_NOT_HANDLED) return ES_NOT_HANDLED;
1299
1300 if (this->avail_pos >= 0) {
1301 this->active_sel = nullptr;
1303 if (this->avail_sel != this->avails[this->avail_pos]) this->CloseChildWindows(WC_TEXTFILE);
1304 this->avail_sel = this->avails[this->avail_pos];
1305 this->vscroll2->ScrollTowards(this->avail_pos);
1306 this->InvalidateData(0);
1307 }
1308
1309 return ES_HANDLED;
1310 }
1311
1312 void OnEditboxChanged(WidgetID widget) override
1313 {
1314 if (!this->editable) return;
1315
1316 if (widget == WID_NS_FILTER) {
1317 string_filter.SetFilterTerm(this->filter_editbox.text.GetText());
1319 this->avails.ForceRebuild();
1320 this->InvalidateData(0);
1321 }
1322 }
1323
1324 void OnDragDrop(Point pt, WidgetID widget) override
1325 {
1326 if (!this->editable) return;
1327
1328 if (widget == WID_NS_FILE_LIST) {
1329 if (this->active_sel != nullptr) {
1330 int from_pos = this->GetCurrentActivePosition();
1331
1332 /* Gets the drag-and-drop destination offset. Ignore the last dummy line. */
1333 int to_pos = std::min(this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_NS_FILE_LIST, WidgetDimensions::scaled.framerect.top), this->vscroll->GetCount() - 2);
1334 if (to_pos != from_pos) { // Don't move NewGRF file over itself.
1335 if (to_pos > from_pos) ++to_pos;
1336
1337 auto from = std::next(std::begin(this->actives), from_pos);
1338 auto to = std::next(std::begin(this->actives), to_pos);
1339 Slide(from, std::next(from), to);
1340
1341 this->vscroll->ScrollTowards(to_pos);
1342 this->preset = -1;
1344 }
1345 } else if (this->avail_sel != nullptr) {
1346 int to_pos = std::min(this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_NS_FILE_LIST, WidgetDimensions::scaled.framerect.top), this->vscroll->GetCount() - 1);
1347 this->AddGRFToActive(to_pos);
1349 }
1350 } else if (widget == WID_NS_AVAIL_LIST && this->active_sel != nullptr) {
1351 /* Remove active NewGRF file by dragging it over available list. */
1352 Point dummy = {-1, -1};
1353 this->OnClick(dummy, WID_NS_REMOVE, 1);
1355 }
1356
1358
1359 if (this->active_over != -1) {
1360 /* End of drag-and-drop, hide dragged destination highlight. */
1361 this->SetWidgetDirty(this->active_over == -2 ? WID_NS_AVAIL_LIST : WID_NS_FILE_LIST);
1362 this->active_over = -1;
1363 }
1364 }
1365
1366 void OnMouseDrag(Point pt, WidgetID widget) override
1367 {
1368 if (!this->editable) return;
1369
1370 if (widget == WID_NS_FILE_LIST && (this->active_sel != nullptr || this->avail_sel != nullptr)) {
1371 /* An NewGRF file is dragged over the active list. */
1372 int to_pos = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_NS_FILE_LIST, WidgetDimensions::scaled.framerect.top);
1373 /* Skip the last dummy line if the source is from the active list. */
1374 to_pos = std::min(to_pos, this->vscroll->GetCount() - (this->active_sel != nullptr ? 2 : 1));
1375
1376 if (to_pos != this->active_over) {
1377 this->active_over = to_pos;
1379 }
1380 } else if (widget == WID_NS_AVAIL_LIST && this->active_sel != nullptr) {
1381 this->active_over = -2;
1383 } else if (this->active_over != -1) {
1384 this->SetWidgetDirty(this->active_over == -2 ? WID_NS_AVAIL_LIST : WID_NS_FILE_LIST);
1385 this->active_over = -1;
1386 }
1387 }
1388
1389private:
1391 static bool NameSorter(const GRFConfig * const &a, const GRFConfig * const &b)
1392 {
1393 std::string name_a = StrMakeValid(a->GetName(), {}); // Make a copy without control codes.
1394 std::string name_b = StrMakeValid(b->GetName(), {}); // Make a copy without control codes.
1395 int i = StrNaturalCompare(name_a, name_b, true); // Sort by name (natural sorting).
1396 if (i != 0) return i < 0;
1397
1398 i = a->version - b->version;
1399 if (i != 0) return i < 0;
1400
1401 return a->ident.md5sum < b->ident.md5sum;
1402 }
1403
1405 static bool TagNameFilter(const GRFConfig * const *a, StringFilter &filter)
1406 {
1407 filter.ResetState();
1408 filter.AddLine((*a)->GetName());
1409 filter.AddLine((*a)->filename);
1410 if (auto desc = (*a)->GetDescription(); desc.has_value()) filter.AddLine(*desc);
1411 return filter.GetState();;
1412 }
1413
1414 void BuildAvailables()
1415 {
1416 if (!this->avails.NeedRebuild()) return;
1417
1418 this->avails.clear();
1419
1420 for (const auto &c : _all_grfs) {
1421 if (std::ranges::any_of(this->actives, [&c](const auto &gc) { return gc->ident.HasGrfIdentifier(c->ident.grfid, &c->ident.md5sum); })) continue;
1422
1424 this->avails.push_back(c.get());
1425 } else {
1427 /* Never triggers; FindGRFConfig returns either c, or a newer version of c. */
1428 assert(best != nullptr);
1429
1430 /*
1431 * If the best version is 0, then all NewGRF with this GRF ID
1432 * have version 0, so for backward compatibility reasons we
1433 * want to show them all.
1434 * If we are the best version, then we definitely want to
1435 * show that NewGRF!.
1436 */
1437 if (best->version == 0 || best->ident.HasGrfIdentifier(c->ident.grfid, &c->ident.md5sum)) {
1438 this->avails.push_back(c.get());
1439 }
1440 }
1441 }
1442
1443 this->avails.Filter(this->string_filter);
1444 this->avails.RebuildDone();
1445 this->avails.Sort();
1446
1447 if (this->avail_sel != nullptr) {
1448 this->avail_pos = find_index(this->avails, this->avail_sel);
1449 if (this->avail_pos == -1) {
1450 this->avail_sel = nullptr;
1451 }
1452 }
1453
1454 this->vscroll2->SetCount(this->avails.size()); // Update the scrollbar
1455 }
1456
1462 bool AddGRFToActive(int ins_pos = -1)
1463 {
1464 if (this->avail_sel == nullptr || !this->editable || this->avail_sel->flags.Test(GRFConfigFlag::Invalid)) return false;
1465
1467
1468 /* Get number of non-static NewGRFs. */
1469 size_t count = std::ranges::count_if(this->actives, [](const auto &gc) { return !gc->flags.Test(GRFConfigFlag::Static); });
1470 if (count >= NETWORK_MAX_GRF_COUNT) {
1471 ShowErrorMessage(GetEncodedString(STR_NEWGRF_TOO_MANY_NEWGRFS), {}, WL_INFO);
1472 return false;
1473 }
1474
1475 /* Check for duplicate GRF ID. */
1476 if (std::ranges::any_of(this->actives, [&grfid = this->avail_sel->ident.grfid](const auto &gc) { return gc->ident.grfid == grfid; })) {
1477 ShowErrorMessage(GetEncodedString(STR_NEWGRF_DUPLICATE_GRFID), {}, WL_INFO);
1478 return false;
1479 }
1480
1481 auto entry = (ins_pos >= 0 && static_cast<size_t>(ins_pos) < std::size(this->actives))
1482 ? std::next(std::begin(this->actives), ins_pos)
1483 : std::end(this->actives);
1484
1485 /* Copy GRF details from scanned list. */
1486 entry = this->actives.insert(entry, std::make_unique<GRFConfig>(*this->avail_sel));
1487 (*entry)->SetParameterDefaults();
1488
1489 /* Select next (or previous, if last one) item in the list. */
1490 int new_pos = this->avail_pos + 1;
1491 if (new_pos >= (int)this->avails.size()) new_pos = this->avail_pos - 1;
1492 this->avail_pos = new_pos;
1493 if (new_pos >= 0) this->avail_sel = this->avails[new_pos];
1494
1495 this->avails.ForceRebuild();
1497 return true;
1498 }
1499};
1500
1505void ShowMissingContentWindow(const GRFConfigList &list)
1506{
1507 /* Only show the things in the current list, or everything when nothing's selected */
1508 ContentVector cv;
1509 for (const auto &c : list) {
1510 if (c->status != GCS_NOT_FOUND && !c->flags.Test(GRFConfigFlag::Compatible)) continue;
1511
1512 auto ci = std::make_unique<ContentInfo>();
1513 ci->type = CONTENT_TYPE_NEWGRF;
1515 ci->name = c->GetName();
1516 ci->unique_id = std::byteswap(c->ident.grfid);
1518 cv.push_back(std::move(ci));
1519 }
1520 ShowNetworkContentListWindow(cv.empty() ? nullptr : &cv, CONTENT_TYPE_NEWGRF);
1521}
1522
1525
1526const std::initializer_list<NewGRFWindow::GUIGRFConfigList::SortFunction * const> NewGRFWindow::sorter_funcs = {
1527 &NameSorter,
1528};
1529
1530const std::initializer_list<NewGRFWindow::GUIGRFConfigList::FilterFunction * const> NewGRFWindow::filter_funcs = {
1531 &TagNameFilter,
1532};
1533
1541public:
1542 static const uint MAX_EXTRA_INFO_WIDTH;
1543 static const uint MIN_EXTRA_FOR_3_COLUMNS;
1544
1545 std::unique_ptr<NWidgetBase> avs{};
1546 std::unique_ptr<NWidgetBase> acs{};
1547 std::unique_ptr<NWidgetBase> inf{};
1548 bool editable = true;
1549
1550 NWidgetNewGRFDisplay(std::unique_ptr<NWidgetBase> &&avs, std::unique_ptr<NWidgetBase> &&acs, std::unique_ptr<NWidgetBase> &&inf) : NWidgetBase(NWID_CUSTOM)
1551 , avs(std::move(avs))
1552 , acs(std::move(acs))
1553 , inf(std::move(inf))
1554 {
1555 }
1556
1557 void SetupSmallestSize(Window *w) override
1558 {
1559 /* Copy state flag from the window. */
1560 assert(dynamic_cast<NewGRFWindow *>(w) != nullptr);
1561 NewGRFWindow *ngw = (NewGRFWindow *)w;
1562 this->editable = ngw->editable;
1563
1564 this->avs->SetupSmallestSize(w);
1565 this->acs->SetupSmallestSize(w);
1566 this->inf->SetupSmallestSize(w);
1567
1568 uint min_avs_width = this->avs->smallest_x + this->avs->padding.Horizontal();
1569 uint min_acs_width = this->acs->smallest_x + this->acs->padding.Horizontal();
1570 uint min_inf_width = this->inf->smallest_x + this->inf->padding.Horizontal();
1571
1572 uint min_avs_height = this->avs->smallest_y + this->avs->padding.Vertical();
1573 uint min_acs_height = this->acs->smallest_y + this->acs->padding.Vertical();
1574 uint min_inf_height = this->inf->smallest_y + this->inf->padding.Vertical();
1575
1576 /* Smallest window is in two column mode. */
1577 this->smallest_x = std::max(min_avs_width, min_acs_width) + WidgetDimensions::scaled.hsep_wide + min_inf_width;
1578 this->smallest_y = std::max(min_inf_height, min_acs_height + WidgetDimensions::scaled.vsep_wide + min_avs_height);
1579
1580 /* Filling. */
1581 this->fill_x = std::lcm(this->avs->fill_x, this->acs->fill_x);
1582 if (this->inf->fill_x > 0 && (this->fill_x == 0 || this->fill_x > this->inf->fill_x)) this->fill_x = this->inf->fill_x;
1583
1584 this->fill_y = this->avs->fill_y;
1585 if (this->acs->fill_y > 0 && (this->fill_y == 0 || this->fill_y > this->acs->fill_y)) this->fill_y = this->acs->fill_y;
1586 this->fill_y = std::lcm(this->fill_y, this->inf->fill_y);
1587
1588 /* Resizing. */
1589 this->resize_x = std::lcm(this->avs->resize_x, this->acs->resize_x);
1590 if (this->inf->resize_x > 0 && (this->resize_x == 0 || this->resize_x > this->inf->resize_x)) this->resize_x = this->inf->resize_x;
1591
1592 this->resize_y = this->avs->resize_y;
1593 if (this->acs->resize_y > 0 && (this->resize_y == 0 || this->resize_y > this->acs->resize_y)) this->resize_y = this->acs->resize_y;
1594 this->resize_y = std::lcm(this->resize_y, this->inf->resize_y);
1595
1596 /* Make sure the height suits the 3 column (resp. not-editable) format; the 2 column format can easily fill space between the lists */
1597 this->smallest_y = ComputeMaxSize(min_acs_height, this->smallest_y + this->resize_y - 1, this->resize_y);
1598 }
1599
1600 void AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl) override
1601 {
1602 this->StoreSizePosition(sizing, x, y, given_width, given_height);
1603
1604 uint min_avs_width = this->avs->smallest_x + this->avs->padding.Horizontal();
1605 uint min_acs_width = this->acs->smallest_x + this->acs->padding.Horizontal();
1606 uint min_inf_width = this->inf->smallest_x + this->inf->padding.Horizontal();
1607
1608 uint min_list_width = std::max(min_avs_width, min_acs_width); // Smallest width of the lists such that they have equal width (incl padding).
1609 uint avs_extra_width = min_list_width - min_avs_width; // Additional width needed for avs to reach min_list_width.
1610 uint acs_extra_width = min_list_width - min_acs_width; // Additional width needed for acs to reach min_list_width.
1611
1612 /* Use 2 or 3 columns? */
1613 uint min_three_columns = min_avs_width + min_acs_width + min_inf_width + 2 * WidgetDimensions::scaled.hsep_wide;
1614 uint min_two_columns = min_list_width + min_inf_width + WidgetDimensions::scaled.hsep_wide;
1615 bool use_three_columns = this->editable && (min_three_columns + ScaleGUITrad(MIN_EXTRA_FOR_3_COLUMNS) <= given_width);
1616
1617 /* Info panel is a separate column in both modes. Compute its width first. */
1618 uint extra_width, inf_width;
1619 if (use_three_columns) {
1620 extra_width = given_width - min_three_columns;
1621 inf_width = std::min<uint>(ScaleGUITrad(MAX_EXTRA_INFO_WIDTH), extra_width / 2);
1622 } else {
1623 extra_width = given_width - min_two_columns;
1624 inf_width = std::min<uint>(ScaleGUITrad(MAX_EXTRA_INFO_WIDTH), extra_width / 2);
1625 }
1626 inf_width = ComputeMaxSize(this->inf->smallest_x, this->inf->smallest_x + inf_width, this->inf->GetHorizontalStepSize(sizing));
1627 extra_width -= inf_width - this->inf->smallest_x;
1628
1629 uint inf_height = ComputeMaxSize(this->inf->smallest_y, given_height, this->inf->GetVerticalStepSize(sizing));
1630
1631 if (use_three_columns) {
1632 /* Three column display, first make both lists equally wide, then divide whatever is left between both lists.
1633 * Only keep track of what avs gets, all other space goes to acs. */
1634 uint avs_width = std::min(avs_extra_width, extra_width);
1635 extra_width -= avs_width;
1636 extra_width -= std::min(acs_extra_width, extra_width);
1637 avs_width += extra_width / 2;
1638
1639 avs_width = ComputeMaxSize(this->avs->smallest_x, this->avs->smallest_x + avs_width, this->avs->GetHorizontalStepSize(sizing));
1640
1641 uint acs_width = given_width - // Remaining space, including horizontal padding.
1642 inf_width - this->inf->padding.Horizontal() -
1643 avs_width - this->avs->padding.Horizontal() - 2 * WidgetDimensions::scaled.hsep_wide;
1644 acs_width = ComputeMaxSize(min_acs_width, acs_width, this->acs->GetHorizontalStepSize(sizing)) -
1645 this->acs->padding.Horizontal();
1646
1647 /* Never use fill_y on these; the minimal size is chosen, so that the 3 column view looks nice */
1648 uint avs_height = ComputeMaxSize(this->avs->smallest_y, given_height, this->avs->resize_y);
1649 uint acs_height = ComputeMaxSize(this->acs->smallest_y, given_height, this->acs->resize_y);
1650
1651 /* Assign size and position to the children. */
1652 if (rtl) {
1653 x += this->inf->padding.left;
1654 this->inf->AssignSizePosition(sizing, x, y + this->inf->padding.top, inf_width, inf_height, rtl);
1655 x += inf_width + this->inf->padding.right + WidgetDimensions::scaled.hsep_wide;
1656 } else {
1657 x += this->avs->padding.left;
1658 this->avs->AssignSizePosition(sizing, x, y + this->avs->padding.top, avs_width, avs_height, rtl);
1659 x += avs_width + this->avs->padding.right + WidgetDimensions::scaled.hsep_wide;
1660 }
1661
1662 x += this->acs->padding.left;
1663 this->acs->AssignSizePosition(sizing, x, y + this->acs->padding.top, acs_width, acs_height, rtl);
1664 x += acs_width + this->acs->padding.right + WidgetDimensions::scaled.hsep_wide;
1665
1666 if (rtl) {
1667 x += this->avs->padding.left;
1668 this->avs->AssignSizePosition(sizing, x, y + this->avs->padding.top, avs_width, avs_height, rtl);
1669 } else {
1670 x += this->inf->padding.left;
1671 this->inf->AssignSizePosition(sizing, x, y + this->inf->padding.top, inf_width, inf_height, rtl);
1672 }
1673 } else {
1674 /* Two columns, all space in extra_width goes to both lists. Since the lists are underneath each other,
1675 * the column is min_list_width wide at least. */
1676 uint avs_width = ComputeMaxSize(this->avs->smallest_x, this->avs->smallest_x + avs_extra_width + extra_width,
1677 this->avs->GetHorizontalStepSize(sizing));
1678 uint acs_width = ComputeMaxSize(this->acs->smallest_x, this->acs->smallest_x + acs_extra_width + extra_width,
1679 this->acs->GetHorizontalStepSize(sizing));
1680
1681 uint min_avs_height = (!this->editable) ? 0 : this->avs->smallest_y + this->avs->padding.Vertical() + WidgetDimensions::scaled.vsep_wide;
1682 uint min_acs_height = this->acs->smallest_y + this->acs->padding.Vertical();
1683 uint extra_height = given_height - min_acs_height - min_avs_height;
1684
1685 /* Never use fill_y on these; instead use WidgetDimensions::scaled.vsep_wide as filler */
1686 uint avs_height = ComputeMaxSize(this->avs->smallest_y, this->avs->smallest_y + extra_height / 2, this->avs->resize_y);
1687 if (this->editable) extra_height -= avs_height - this->avs->smallest_y;
1688 uint acs_height = ComputeMaxSize(this->acs->smallest_y, this->acs->smallest_y + extra_height, this->acs->resize_y);
1689
1690 /* Assign size and position to the children. */
1691 if (rtl) {
1692 x += this->inf->padding.left;
1693 this->inf->AssignSizePosition(sizing, x, y + this->inf->padding.top, inf_width, inf_height, rtl);
1694 x += inf_width + this->inf->padding.right + WidgetDimensions::scaled.hsep_wide;
1695
1696 this->acs->AssignSizePosition(sizing, x + this->acs->padding.left, y + this->acs->padding.top, acs_width, acs_height, rtl);
1697 if (this->editable) {
1698 this->avs->AssignSizePosition(sizing, x + this->avs->padding.left, y + given_height - avs_height - this->avs->padding.bottom, avs_width, avs_height, rtl);
1699 } else {
1700 this->avs->AssignSizePosition(sizing, 0, 0, this->avs->smallest_x, this->avs->smallest_y, rtl);
1701 }
1702 } else {
1703 this->acs->AssignSizePosition(sizing, x + this->acs->padding.left, y + this->acs->padding.top, acs_width, acs_height, rtl);
1704 if (this->editable) {
1705 this->avs->AssignSizePosition(sizing, x + this->avs->padding.left, y + given_height - avs_height - this->avs->padding.bottom, avs_width, avs_height, rtl);
1706 } else {
1707 this->avs->AssignSizePosition(sizing, 0, 0, this->avs->smallest_x, this->avs->smallest_y, rtl);
1708 }
1709 uint dx = this->acs->current_x + this->acs->padding.Horizontal();
1710 if (this->editable) {
1711 dx = std::max(dx, this->avs->current_x + this->avs->padding.Horizontal());
1712 }
1713 x += dx + WidgetDimensions::scaled.hsep_wide + this->inf->padding.left;
1714 this->inf->AssignSizePosition(sizing, x, y + this->inf->padding.top, inf_width, inf_height, rtl);
1715 }
1716 }
1717 }
1718
1719 void FillWidgetLookup(WidgetLookup &widget_lookup) override
1720 {
1721 this->NWidgetBase::FillWidgetLookup(widget_lookup);
1722 this->avs->FillWidgetLookup(widget_lookup);
1723 this->acs->FillWidgetLookup(widget_lookup);
1724 this->inf->FillWidgetLookup(widget_lookup);
1725 }
1726
1727 NWidgetCore *GetWidgetFromPos(int x, int y) override
1728 {
1729 if (!IsInsideBS(x, this->pos_x, this->current_x) || !IsInsideBS(y, this->pos_y, this->current_y)) return nullptr;
1730
1731 NWidgetCore *nw = (this->editable) ? this->avs->GetWidgetFromPos(x, y) : nullptr;
1732 if (nw == nullptr) nw = this->acs->GetWidgetFromPos(x, y);
1733 if (nw == nullptr) nw = this->inf->GetWidgetFromPos(x, y);
1734 return nw;
1735 }
1736
1737 void Draw(const Window *w) override
1738 {
1739 if (this->editable) this->avs->Draw(w);
1740 this->acs->Draw(w);
1741 this->inf->Draw(w);
1742 }
1743};
1744
1747
1748static constexpr std::initializer_list<NWidgetPart> _nested_newgrf_actives_widgets = {
1750 /* Left side, presets. */
1753 NWidget(WWT_TEXT, INVALID_COLOUR), SetStringTip(STR_NEWGRF_SETTINGS_SELECT_PRESET),
1754 SetPadding(0, WidgetDimensions::unscaled.hsep_wide, 0, 0),
1755 NWidget(WWT_DROPDOWN, COLOUR_YELLOW, WID_NS_PRESET_LIST), SetFill(1, 0), SetResize(1, 0),
1756 SetToolTip(STR_NEWGRF_SETTINGS_PRESET_LIST_TOOLTIP),
1757 EndContainer(),
1759 NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_PRESET_SAVE), SetFill(1, 0), SetResize(1, 0),
1760 SetStringTip(STR_NEWGRF_SETTINGS_PRESET_SAVE, STR_NEWGRF_SETTINGS_PRESET_SAVE_TOOLTIP),
1761 NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_PRESET_DELETE), SetFill(1, 0), SetResize(1, 0),
1762 SetStringTip(STR_NEWGRF_SETTINGS_PRESET_DELETE, STR_NEWGRF_SETTINGS_PRESET_DELETE_TOOLTIP),
1763 EndContainer(),
1764 EndContainer(),
1765
1766 NWidget(WWT_FRAME, COLOUR_MAUVE), SetStringTip(STR_NEWGRF_SETTINGS_ACTIVE_LIST), SetPIP(0, WidgetDimensions::unscaled.vsep_wide, 0),
1767 /* Left side, active grfs. */
1769 NWidget(WWT_PANEL, COLOUR_MAUVE),
1770 NWidget(WWT_INSET, COLOUR_MAUVE, WID_NS_FILE_LIST), SetMinimalSize(100, 1), SetPadding(2),
1771 SetFill(1, 1), SetResize(1, 1), SetScrollbar(WID_NS_SCROLLBAR), SetToolTip(STR_NEWGRF_SETTINGS_FILE_TOOLTIP),
1772 EndContainer(),
1773 EndContainer(),
1775 EndContainer(),
1776
1777 /* Buttons. */
1778 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_NS_SHOW_REMOVE),
1780 NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_REMOVE), SetFill(1, 0), SetResize(1, 0),
1781 SetStringTip(STR_NEWGRF_SETTINGS_REMOVE, STR_NEWGRF_SETTINGS_REMOVE_TOOLTIP),
1783 NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_MOVE_UP), SetFill(1, 0), SetResize(1, 0),
1784 SetStringTip(STR_NEWGRF_SETTINGS_MOVEUP, STR_NEWGRF_SETTINGS_MOVEUP_TOOLTIP),
1785 NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_MOVE_DOWN), SetFill(1, 0), SetResize(1, 0),
1786 SetStringTip(STR_NEWGRF_SETTINGS_MOVEDOWN, STR_NEWGRF_SETTINGS_MOVEDOWN_TOOLTIP),
1787 EndContainer(),
1788 NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_UPGRADE), SetFill(1, 0), SetResize(1, 0),
1789 SetStringTip(STR_NEWGRF_SETTINGS_UPGRADE, STR_NEWGRF_SETTINGS_UPGRADE_TOOLTIP),
1790 EndContainer(),
1791
1793 NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_RESCAN_FILES2), SetFill(1, 0), SetResize(1, 0),
1794 SetStringTip(STR_NEWGRF_SETTINGS_RESCAN_FILES, STR_NEWGRF_SETTINGS_RESCAN_FILES_TOOLTIP),
1795 NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_CONTENT_DOWNLOAD2), SetFill(1, 0), SetResize(1, 0),
1796 SetStringTip(STR_INTRO_ONLINE_CONTENT, STR_INTRO_TOOLTIP_ONLINE_CONTENT),
1797 EndContainer(),
1798 EndContainer(),
1799 EndContainer(),
1800 EndContainer(),
1801};
1802
1803static constexpr std::initializer_list<NWidgetPart> _nested_newgrf_availables_widgets = {
1804 NWidget(WWT_FRAME, COLOUR_MAUVE), SetStringTip(STR_NEWGRF_SETTINGS_INACTIVE_LIST), SetPIP(0, WidgetDimensions::unscaled.vsep_wide, 0),
1805 /* Left side, available grfs, filter edit box. */
1807 NWidget(WWT_TEXT, INVALID_COLOUR), SetFill(0, 1), SetStringTip(STR_NEWGRF_FILTER_TITLE),
1808 NWidget(WWT_EDITBOX, COLOUR_MAUVE, WID_NS_FILTER), SetFill(1, 0), SetResize(1, 0),
1809 SetStringTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP),
1810 EndContainer(),
1811
1812 /* Left side, available grfs. */
1814 NWidget(WWT_PANEL, COLOUR_MAUVE),
1815 NWidget(WWT_INSET, COLOUR_MAUVE, WID_NS_AVAIL_LIST), SetMinimalSize(100, 1), SetPadding(2),
1817 EndContainer(),
1818 EndContainer(),
1820 EndContainer(),
1821
1822 /* Left side, available grfs, buttons. */
1824 NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_ADD), SetFill(1, 0), SetResize(1, 0),
1825 SetStringTip(STR_NEWGRF_SETTINGS_ADD, STR_NEWGRF_SETTINGS_ADD_FILE_TOOLTIP),
1827 NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_RESCAN_FILES), SetFill(1, 0), SetResize(1, 0),
1828 SetStringTip(STR_NEWGRF_SETTINGS_RESCAN_FILES, STR_NEWGRF_SETTINGS_RESCAN_FILES_TOOLTIP),
1829 NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_CONTENT_DOWNLOAD), SetFill(1, 0), SetResize(1, 0),
1830 SetStringTip(STR_INTRO_ONLINE_CONTENT, STR_INTRO_TOOLTIP_ONLINE_CONTENT),
1831 EndContainer(),
1832 EndContainer(),
1833 EndContainer(),
1834};
1835
1836static constexpr std::initializer_list<NWidgetPart> _nested_newgrf_infopanel_widgets = {
1838 /* Right side, info panel. */
1839 NWidget(WWT_PANEL, COLOUR_MAUVE),
1840 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_NS_NEWGRF_INFO_TITLE), SetFill(1, 0), SetResize(1, 0),
1841 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_NS_NEWGRF_INFO), SetFill(1, 1), SetResize(1, 1), SetMinimalSize(150, 100),
1842 EndContainer(),
1843
1844 /* Right side, info buttons. */
1847 NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_OPEN_URL), SetFill(1, 0), SetResize(1, 0),
1848 SetStringTip(STR_CONTENT_OPEN_URL, STR_CONTENT_OPEN_URL_TOOLTIP),
1850 SetStringTip(STR_TEXTFILE_VIEW_README, STR_TEXTFILE_VIEW_README_TOOLTIP),
1851 EndContainer(),
1854 SetStringTip(STR_TEXTFILE_VIEW_CHANGELOG, STR_TEXTFILE_VIEW_CHANGELOG_TOOLTIP),
1856 SetStringTip(STR_TEXTFILE_VIEW_LICENCE, STR_TEXTFILE_VIEW_LICENCE_TOOLTIP),
1857 EndContainer(),
1858 EndContainer(),
1859
1860 /* Right side, config buttons. */
1861 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_NS_SHOW_EDIT),
1864 NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_SET_PARAMETERS), SetFill(1, 0), SetResize(1, 0),
1865 SetStringTip(STR_NEWGRF_SETTINGS_SET_PARAMETERS),
1866 NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_TOGGLE_PALETTE), SetFill(1, 0), SetResize(1, 0),
1867 SetStringTip(STR_NEWGRF_SETTINGS_TOGGLE_PALETTE, STR_NEWGRF_SETTINGS_TOGGLE_PALETTE_TOOLTIP),
1868 EndContainer(),
1869 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_NS_SHOW_APPLY),
1870 NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_APPLY_CHANGES), SetFill(1, 0), SetResize(1, 0),
1871 SetStringTip(STR_NEWGRF_SETTINGS_APPLY_CHANGES),
1872 EndContainer(),
1873 EndContainer(),
1874 NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_VIEW_PARAMETERS), SetFill(1, 0), SetResize(1, 0),
1875 SetStringTip(STR_NEWGRF_SETTINGS_SHOW_PARAMETERS),
1876 EndContainer(),
1877 EndContainer(),
1878};
1879
1881std::unique_ptr<NWidgetBase> NewGRFDisplay()
1882{
1883 std::unique_ptr<NWidgetBase> avs = MakeNWidgets(_nested_newgrf_availables_widgets, nullptr);
1884 std::unique_ptr<NWidgetBase> acs = MakeNWidgets(_nested_newgrf_actives_widgets, nullptr);
1885 std::unique_ptr<NWidgetBase> inf = MakeNWidgets(_nested_newgrf_infopanel_widgets, nullptr);
1886
1887 return std::make_unique<NWidgetNewGRFDisplay>(std::move(avs), std::move(acs), std::move(inf));
1888}
1889
1890/* Widget definition of the manage newgrfs window */
1891static constexpr std::initializer_list<NWidgetPart> _nested_newgrf_widgets = {
1893 NWidget(WWT_CLOSEBOX, COLOUR_MAUVE),
1894 NWidget(WWT_CAPTION, COLOUR_MAUVE), SetStringTip(STR_NEWGRF_SETTINGS_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1895 NWidget(WWT_DEFSIZEBOX, COLOUR_MAUVE),
1896 EndContainer(),
1897 NWidget(WWT_PANEL, COLOUR_MAUVE),
1899 /* Resize button. */
1901 NWidget(NWID_SPACER), SetFill(1, 0), SetResize(1, 0),
1902 NWidget(WWT_RESIZEBOX, COLOUR_MAUVE), SetResizeWidgetTypeTip(RWV_HIDE_BEVEL, STR_TOOLTIP_RESIZE),
1903 EndContainer(),
1904 EndContainer(),
1905};
1906
1907/* Window definition of the manage newgrfs window */
1908static WindowDesc _newgrf_desc(
1909 WDP_CENTER, "settings_newgrf", 300, 263,
1911 {},
1912 _nested_newgrf_widgets
1913);
1914
1920static void NewGRFConfirmationCallback(Window *w, bool confirmed)
1921{
1922 if (confirmed) {
1925 NewGRFWindow *nw = dynamic_cast<NewGRFWindow*>(w);
1926 assert(nw != nullptr);
1927
1929 _gamelog.GRFUpdate(_grfconfig, nw->actives); // log GRF changes
1930 CopyGRFConfigList(nw->orig_list, nw->actives, false);
1933
1934 /* Show new, updated list */
1935 int pos = nw->GetCurrentActivePosition();
1936
1937 CopyGRFConfigList(nw->actives, nw->orig_list, false);
1938
1939 if (nw->active_sel != nullptr) {
1940 /* Set current selection from position */
1941 if (static_cast<size_t>(pos) >= nw->actives.size()) {
1942 nw->active_sel = nw->actives.back().get();
1943 } else {
1944 auto it = std::next(std::begin(nw->actives), pos);
1945 nw->active_sel = it->get();
1946 }
1947 }
1948 nw->avails.ForceRebuild();
1949 nw->modified = false;
1950
1951 w->InvalidateData();
1952
1953 ReInitAllWindows(false);
1955 }
1956}
1957
1958
1959
1968void ShowNewGRFSettings(bool editable, bool show_params, bool exec_changes, GRFConfigList &config)
1969{
1971 new NewGRFWindow(_newgrf_desc, editable, show_params, exec_changes, config);
1972}
1973
1975static constexpr std::initializer_list<NWidgetPart> _nested_save_preset_widgets = {
1977 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
1978 NWidget(WWT_CAPTION, COLOUR_GREY), SetStringTip(STR_SAVE_PRESET_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1979 NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
1980 EndContainer(),
1981 NWidget(WWT_PANEL, COLOUR_GREY),
1983 NWidget(WWT_INSET, COLOUR_GREY, WID_SVP_PRESET_LIST), SetPadding(2, 1, 2, 2),
1984 SetToolTip(STR_SAVE_PRESET_LIST_TOOLTIP), SetResize(1, 10), SetScrollbar(WID_SVP_SCROLLBAR), EndContainer(),
1986 EndContainer(),
1987 EndContainer(),
1988 NWidget(WWT_PANEL, COLOUR_GREY),
1989 NWidget(WWT_EDITBOX, COLOUR_GREY, WID_SVP_EDITBOX), SetPadding(2, 2, 2, 2), SetFill(1, 0), SetResize(1, 0),
1990 SetStringTip(STR_SAVE_PRESET_TITLE, STR_SAVE_PRESET_EDITBOX_TOOLTIP),
1991 EndContainer(),
1993 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SVP_SAVE), SetStringTip(STR_SAVE_PRESET_SAVE, STR_SAVE_PRESET_SAVE_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
1994 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
1995 EndContainer(),
1996};
1997
2000 WDP_CENTER, "save_preset", 140, 110,
2004);
2005
2007struct SavePresetWindow : public Window {
2010 Scrollbar *vscroll = nullptr;
2011 int selected = -1;
2012
2017 SavePresetWindow(std::string_view initial_text) : Window(_save_preset_desc), presetname_editbox(32)
2018 {
2019 this->presets = GetGRFPresetList();
2020 if (!initial_text.empty()) {
2021 for (uint i = 0; i < this->presets.size(); i++) {
2022 if (this->presets[i] == initial_text) {
2023 this->selected = i;
2024 break;
2025 }
2026 }
2027 }
2028
2030 this->presetname_editbox.ok_button = WID_SVP_SAVE;
2031
2032 this->CreateNestedTree();
2033 this->vscroll = this->GetScrollbar(WID_SVP_SCROLLBAR);
2034 this->FinishInitNested(0);
2035
2036 this->vscroll->SetCount(this->presets.size());
2038 this->presetname_editbox.text.Assign(initial_text);
2039 }
2040
2041 ~SavePresetWindow() override
2042 {
2043 }
2044
2045 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
2046 {
2047 switch (widget) {
2048 case WID_SVP_PRESET_LIST: {
2049 fill.height = resize.height = GetCharacterHeight(FS_NORMAL);
2050 size.height = 0;
2051 for (uint i = 0; i < this->presets.size(); i++) {
2053 size.width = std::max(size.width, d.width + padding.width);
2054 }
2055 size.height = ClampU((uint)this->presets.size(), 5, 20) * resize.height + padding.height;
2056 break;
2057 }
2058 }
2059 }
2060
2061 void DrawWidget(const Rect &r, WidgetID widget) const override
2062 {
2063 switch (widget) {
2064 case WID_SVP_PRESET_LIST: {
2065 const Rect br = r.Shrink(WidgetDimensions::scaled.bevel);
2066 GfxFillRect(br, PC_BLACK);
2067
2068 uint step_height = this->GetWidget<NWidgetBase>(WID_SVP_PRESET_LIST)->resize_y;
2069 int offset_y = (step_height - GetCharacterHeight(FS_NORMAL)) / 2;
2070 Rect tr = r.Shrink(WidgetDimensions::scaled.framerect);
2071
2072 auto [first, last] = this->vscroll->GetVisibleRangeIterators(this->presets);
2073 for (auto it = first; it != last; ++it) {
2074 int row = static_cast<int>(std::distance(std::begin(this->presets), it));
2075 if (row == this->selected) GfxFillRect(br.left, tr.top, br.right, tr.top + step_height - 1, PC_DARK_BLUE);
2076
2077 DrawString(tr.left, tr.right, tr.top + offset_y, *it, (row == this->selected) ? TC_WHITE : TC_SILVER);
2078 tr.top += step_height;
2079 }
2080 break;
2081 }
2082 }
2083 }
2084
2085 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
2086 {
2087 switch (widget) {
2088 case WID_SVP_PRESET_LIST: {
2089 auto it = this->vscroll->GetScrolledItemFromWidget(this->presets, pt.y, this, WID_SVP_PRESET_LIST);
2090 if (it != this->presets.end()) {
2091 this->selected = it - this->presets.begin();
2092 this->presetname_editbox.text.Assign(*it);
2095 }
2096 break;
2097 }
2098
2099 case WID_SVP_SAVE: {
2101 if (w != nullptr) {
2102 auto text = this->presetname_editbox.text.GetText();
2103 if (!text.empty()) w->OnQueryTextFinished(std::string{text});
2104 }
2105 this->Close();
2106 break;
2107 }
2108 }
2109 }
2110
2111 void OnResize() override
2112 {
2113 this->vscroll->SetCapacityFromWidget(this, WID_SVP_PRESET_LIST, WidgetDimensions::scaled.framerect.Vertical());
2114 }
2115};
2116
2121static void ShowSavePresetWindow(std::string_view initial_text)
2122{
2124 new SavePresetWindow(initial_text);
2125}
2126
2128static constexpr std::initializer_list<NWidgetPart> _nested_scan_progress_widgets = {
2129 NWidget(WWT_CAPTION, COLOUR_GREY), SetStringTip(STR_NEWGRF_SCAN_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
2130 NWidget(WWT_PANEL, COLOUR_GREY),
2132 NWidget(WWT_LABEL, INVALID_COLOUR), SetStringTip(STR_NEWGRF_SCAN_MESSAGE), SetFill(1, 0),
2133 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SP_PROGRESS_BAR), SetFill(1, 0),
2134 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SP_PROGRESS_TEXT), SetFill(1, 0), SetMinimalSize(400, 0),
2135 EndContainer(),
2136 EndContainer(),
2137};
2138
2141 WDP_CENTER, {}, 0, 0,
2143 {},
2145);
2146
2149 std::string last_name{};
2150 int scanned = 0;
2151
2154 {
2155 this->InitNested(1);
2156 }
2157
2158 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
2159 {
2160 switch (widget) {
2162 size = GetStringBoundingBox(GetString(STR_GENERATION_PROGRESS, GetParamMaxValue(100)));
2163 /* We need some spacing for the 'border' */
2166 break;
2167
2168 case WID_SP_PROGRESS_TEXT: {
2169 uint64_t max_digits = GetParamMaxDigits(4);
2170 /* We really don't know the width. We could determine it by scanning the NewGRFs,
2171 * but this is the status window for scanning them... */
2172 size.width = std::max<uint>(size.width, GetStringBoundingBox(GetString(STR_NEWGRF_SCAN_STATUS, max_digits, max_digits)).width + padding.width);
2174 break;
2175 }
2176 }
2177 }
2178
2179 void DrawWidget(const Rect &r, WidgetID widget) const override
2180 {
2181 switch (widget) {
2182 case WID_SP_PROGRESS_BAR: {
2183 /* Draw the % complete with a bar and a text */
2185 Rect ir = r.Shrink(WidgetDimensions::scaled.bevel);
2186 uint percent = scanned * 100 / std::max(1U, _settings_client.gui.last_newgrf_count);
2187 DrawFrameRect(ir.WithWidth(ir.Width() * percent / 100, _current_text_dir == TD_RTL), COLOUR_MAUVE, {});
2188 DrawString(ir.left, ir.right, CentreBounds(ir.top, ir.bottom, GetCharacterHeight(FS_NORMAL)), GetString(STR_GENERATION_PROGRESS, percent), TC_FROMSTRING, SA_HOR_CENTER);
2189 break;
2190 }
2191
2193 DrawString(r.left, r.right, r.top, GetString(STR_NEWGRF_SCAN_STATUS, this->scanned, _settings_client.gui.last_newgrf_count), TC_FROMSTRING, SA_HOR_CENTER);
2194
2195 DrawString(r.left, r.right, r.top + GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal, this->last_name, TC_BLACK, SA_HOR_CENTER);
2196 break;
2197 }
2198 }
2199
2205 void UpdateNewGRFScanStatus(uint num, std::string &&name)
2206 {
2207 this->last_name = std::move(name);
2208 this->scanned = num;
2210
2211 this->SetDirty();
2212 }
2213};
2214
2220void UpdateNewGRFScanStatus(uint num, std::string &&name)
2221{
2223 if (w == nullptr) w = new ScanProgressWindow();
2224 w->UpdateNewGRFScanStatus(num, std::move(name));
2225}
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
List template of 'things' T to sort in a GUI.
bool Filter(FilterFunction *decide, F filter_data)
Filter the list.
void RebuildDone()
Notify the sortlist that the rebuild is done.
void SetFiltering(Filtering f)
Import filter conditions.
void SetListing(Listing l)
Import sort conditions.
void SetFilterState(bool state)
Enable or disable the filter.
void SetFilterFuncs(std::span< FilterFunction *const > n_funcs)
Hand the filter function pointers to the GUIList.
bool NeedRebuild() const
Check if a rebuild is needed.
void ForceRebuild()
Force that a rebuild is needed.
bool Sort(Comp compare)
Sort the list.
void SetSortFuncs(std::span< SortFunction *const > n_funcs)
Hand the sort function pointers to the GUIList.
void StartAction(GamelogActionType at)
Stores information about new action, but doesn't allocate it Action is allocated only when there is a...
Definition gamelog.cpp:65
void StopAction()
Stops logging of any changes.
Definition gamelog.cpp:74
void GRFUpdate(const GRFConfigList &oldg, const GRFConfigList &newg)
Compares two NewGRF lists and logs any change.
Definition gamelog.cpp:606
Baseclass for nested widgets.
void StoreSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height)
Store size and position.
uint resize_x
Horizontal resize step (0 means not resizable).
uint fill_x
Horizontal fill stepsize (from initial size, 0 means not resizable).
uint smallest_x
Smallest horizontal size of the widget in a filled window.
uint current_x
Current horizontal size (after resizing).
int pos_y
Vertical position of top-left corner of the widget in the window.
int pos_x
Horizontal position of top-left corner of the widget in the window.
uint smallest_y
Smallest vertical size of the widget in a filled window.
uint fill_y
Vertical fill stepsize (from initial size, 0 means not resizable).
uint resize_y
Vertical resize step (0 means not resizable).
uint current_y
Current vertical size (after resizing).
virtual void FillWidgetLookup(WidgetLookup &widget_lookup)
Fill the Window::widget_lookup with pointers to nested widgets in the tree.
Definition widget.cpp:915
Base class for a 'real' widget.
NWidgetCore * GetWidgetFromPos(int x, int y) override
Retrieve a widget by its position.
Definition widget.cpp:1265
Custom nested widget container for the NewGRF gui.
bool editable
Editable status of the parent NewGRF window (if false, drop all widgets that make the window editable...
void FillWidgetLookup(WidgetLookup &widget_lookup) override
Fill the Window::widget_lookup with pointers to nested widgets in the tree.
std::unique_ptr< NWidgetBase > acs
Widget with the active grfs list and buttons.
NWidgetCore * GetWidgetFromPos(int x, int y) override
Retrieve a widget by its position.
static const uint MAX_EXTRA_INFO_WIDTH
Maximal additional width given to the panel.
void AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl) override
Assign size and position to the widget.
void Draw(const Window *w) override
Draw the widgets of the tree.
std::unique_ptr< NWidgetBase > inf
Info panel.
static const uint MIN_EXTRA_FOR_3_COLUMNS
Minimal additional width needed before switching to 3 columns.
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
std::unique_ptr< NWidgetBase > avs
Widget with the available grfs list and buttons.
Scrollbar data structure.
bool IsVisible(size_type item) const
Checks whether given current item is visible in the list.
void SetCount(size_t num)
Sets the number of elements in the list.
auto GetScrolledItemFromWidget(Tcontainer &container, int clickpos, const Window *const w, WidgetID widget, int padding=0, int line_height=-1) const
Return an iterator pointing to the element of a scrolled widget that a user clicked in.
size_type GetScrolledRowFromWidget(int clickpos, const Window *const w, WidgetID widget, int padding=0, int line_height=-1) const
Compute the row of a scrolled widget that a user clicked in.
Definition widget.cpp:2425
void SetCapacityFromWidget(Window *w, WidgetID widget, int padding=0)
Set capacity of visible elements from the size and resize properties of a widget.
Definition widget.cpp:2499
size_type GetCount() const
Gets the number of elements in the list.
EventState UpdateListPositionOnKeyPress(int &list_position, uint16_t keycode) const
Update the given list position as if it were on this scroll bar when the given keycode was pressed.
Definition widget.cpp:2446
void ScrollTowards(size_type position)
Scroll towards the given position; if the item is visible nothing happens, otherwise it will be shown...
auto GetVisibleRangeIterators(Tcontainer &container) const
Get a pair of iterators for the range of visible elements in a container.
size_type GetPosition() const
Gets the position of the first visible element in the list.
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
RectPadding framerect
Standard padding inside many panels.
Definition window_gui.h:40
RectPadding frametext
Padding inside frame with text.
Definition window_gui.h:41
static WidgetDimensions scaled
Widget dimensions scaled for current zoom level.
Definition window_gui.h:30
int vsep_normal
Normal vertical spacing.
Definition window_gui.h:58
int vsep_wide
Wide vertical spacing.
Definition window_gui.h:60
int hsep_wide
Wide horizontal spacing.
Definition window_gui.h:62
static const WidgetDimensions unscaled
Unscaled widget dimensions.
Definition window_gui.h:93
RectPadding bevel
Bevel thickness, affected by "scaled bevels" game option.
Definition window_gui.h:38
static const uint NETWORK_MAX_GRF_COUNT
Maximum number of GRFs that can be sent.
Definition config.h:88
int find_index(Container const &container, typename Container::const_reference item)
Helper function to get the index of an item Consider using std::set, std::unordered_set or std::flat_...
auto Slide(TIter first, TIter last, TIter position) -> std::pair< TIter, TIter >
Move elements between first and last to a new position, rotating elements in between as necessary.
void ShowDropDownListAt(Window *w, DropDownList &&list, int selected, WidgetID button, Rect wi_rect, Colours wi_colour, DropDownOptions options)
Show a drop down list.
Definition dropdown.cpp:402
void ShowDropDownList(Window *w, DropDownList &&list, int selected, WidgetID button, uint width, DropDownOptions options)
Show a drop down list.
Definition dropdown.cpp:418
Functions related to the drop down widget.
Types related to the drop down widget.
std::vector< std::unique_ptr< const DropDownListItem > > DropDownList
A drop down list is a collection of drop down list items.
Functions related to errors.
@ WL_ERROR
Errors (eg. saving/loading failed)
Definition error.h:26
@ 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
void ShowErrorMessage(EncodedString &&summary_msg, int x, int y, CommandCost &cc)
Display an error message in a window.
@ NEWGRF_DIR
Subdirectory for all NewGRFs.
Definition fileio_type.h:97
Declarations for savegames operations.
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
Definition fontcache.cpp:87
Gamelog _gamelog
Gamelog instance.
Definition gamelog.cpp:31
Functions to be called to log fundamental changes to the game.
@ GLAT_GRF
GRF changed.
Definition gamelog.h:19
Dimension maxdim(const Dimension &d1, const Dimension &d2)
Compute bounding box of both dimensions.
Geometry functions.
int CentreBounds(int min, int max, int size)
Determine where to position a centred object.
Dimension GetSpriteSize(SpriteID sprid, Point *offset, ZoomLevel zoom)
Get the size of a sprite.
Definition gfx.cpp:968
Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
Return the string dimension in pixels.
Definition gfx.cpp:897
int DrawString(int left, int right, int top, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly truncated to make it fit in its allocated space.
Definition gfx.cpp:668
void DrawSprite(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub, ZoomLevel zoom)
Draw a sprite, not in a viewport.
Definition gfx.cpp:1034
Dimension GetStringMultiLineBoundingBox(StringID str, const Dimension &suggestion)
Calculate string bounding box for multi-line strings.
Definition gfx.cpp:751
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:785
void GfxFillRect(int left, int top, int right, int bottom, const std::variant< PixelColour, PaletteID > &colour, FillRectMode mode)
Applies a certain FillRectMode-operation to a rectangle [left, right] x [top, bottom] on the screen.
Definition gfx.cpp:115
Dimension GetScaledSpriteSize(SpriteID sprid)
Scale sprite size for GUI.
Definition widget.cpp:68
@ FS_NORMAL
Index of the normal font in the font tables.
Definition gfx_type.h:249
@ SA_HOR_CENTER
Horizontally center the text.
Definition gfx_type.h:389
uint32_t PaletteID
The number of the palette.
Definition gfx_type.h:18
constexpr NWidgetPart SetMatrixDataTip(uint32_t cols, uint32_t rows, StringID tip={})
Widget part function for setting the data and tooltip of WWT_MATRIX widgets.
constexpr NWidgetPart NWidgetFunction(NWidgetFunctionType *func_ptr)
Obtain a nested widget (sub)tree from an external source.
constexpr NWidgetPart SetFill(uint16_t fill_x, uint16_t fill_y)
Widget part function for setting filling.
constexpr NWidgetPart SetPIP(uint8_t pre, uint8_t inter, uint8_t post)
Widget part function for setting a pre/inter/post spaces.
constexpr NWidgetPart SetScrollbar(WidgetID index)
Attach a scrollbar to a widget.
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 SetStringTip(StringID string, StringID tip={})
Widget part function for setting the string and tooltip.
constexpr NWidgetPart SetMinimalSize(int16_t x, int16_t y)
Widget part function for setting the minimal size.
constexpr NWidgetPart SetResizeWidgetTypeTip(ResizeWidgetValues widget_type, StringID tip)
Widget part function for setting the resize widget type and tooltip.
std::unique_ptr< NWidgetBase > MakeNWidgets(std::span< const NWidgetPart > nwid_parts, std::unique_ptr< NWidgetBase > &&container)
Construct a nested widget tree from an array of parts.
Definition widget.cpp:3374
constexpr NWidgetPart SetToolTip(StringID tip)
Widget part function for setting tooltip and clearing the widget data.
constexpr NWidgetPart EndContainer()
Widget part function for denoting the end of a container (horizontal, vertical, WWT_FRAME,...
constexpr NWidgetPart SetArrowWidgetTypeTip(ArrowWidgetValues widget_type, StringID tip={})
Widget part function for setting the arrow widget type and tooltip.
constexpr NWidgetPart NWidget(WidgetType tp, Colours col, WidgetID idx=INVALID_WIDGET)
Widget part function for starting a new 'real' widget.
constexpr NWidgetPart SetResize(int16_t dx, int16_t dy)
Widget part function for setting the resize step.
void SetDirty() const
Mark entire window as dirty (in need of re-paint)
Definition window.cpp:968
constexpr bool IsInsideBS(const T x, const size_t base, const size_t size)
Checks if a value is between a window started at some base point.
constexpr bool IsInsideMM(const size_t x, const size_t min, const size_t max) noexcept
Checks if a value is in an interval.
constexpr uint ClampU(const uint a, const uint min, const uint max)
Clamp an unsigned integer between an interval.
void ShowQuery(EncodedString &&caption, EncodedString &&message, Window *parent, QueryCallbackProc *callback, bool focus)
Show a confirmation window with standard 'yes' and 'no' buttons The window is aligned to the centre o...
void ShowQueryString(std::string_view str, StringID caption, uint maxsize, Window *parent, CharSetFilter afilter, QueryStringFlags flags)
Show a query popup window with a textbox in it.
Types related to the misc widgets.
@ WID_TF_CAPTION
The caption of the window.
Definition misc_widget.h:53
bool _network_available
is network mode available?
Definition network.cpp:68
Basic functions/variables used all over the place.
Part of the network protocol handling content distribution.
void ShowNetworkContentListWindow(ContentVector *cv=nullptr, ContentType type1=CONTENT_TYPE_END, ContentType type2=CONTENT_TYPE_END)
Show the content list window with a given set of content.
std::vector< std::unique_ptr< ContentInfo > > ContentVector
Vector with content info.
Base for the NewGRF implementation.
void ReloadNewGRFData()
Reload all NewGRF files during a running game.
GRFConfigList _grfconfig
First item in list of current GRF set up.
void CopyGRFConfigList(GRFConfigList &dst, const GRFConfigList &src, bool init_only)
Copy a GRF Config list.
std::string GRFBuildParamList(const GRFConfig &c)
Build a string containing space separated parameter values, and terminate.
const GRFConfig * FindGRFConfig(uint32_t grfid, FindGRFConfigMode mode, const MD5Hash *md5sum, uint32_t desired_version)
Find a NewGRF in the scanned list.
void ResetGRFConfig(bool defaults)
Reset the current GRF Config to either blank or newgame settings.
GRFConfigList _all_grfs
First item in list of all scanned NewGRFs.
void ClearGRFConfigList(GRFConfigList &config)
Clear a GRF Config list, freeing all nodes.
@ GCS_DISABLED
GRF file is disabled.
@ GCS_NOT_FOUND
GRF file was not found in the local cache.
@ GCS_ACTIVATED
GRF file has been activated.
@ Invalid
GRF is unusable with this version of OpenTTD.
@ Static
GRF file is used statically (can be used in any MP game)
@ Compatible
GRF file does not exactly match the requested GRF (different MD5SUM), but grfid matches)
@ PTYPE_UINT_ENUM
The parameter allows a range of numbers, each of which can have a special name.
@ PTYPE_BOOL
The parameter is either 0 or 1.
@ FGCM_NEWEST
Find newest Grf.
@ FGCM_NEWEST_VALID
Find newest Grf, ignoring Grfs with GRFConfigFlag::Invalid set.
@ FGCM_EXACT
Only find Grfs matching md5sum.
@ GRFP_GRF_UNSET
The NewGRF provided no information.
@ GRFP_USE_WINDOWS
The palette state is set to use the Windows palette.
@ GRFP_BLT_32BPP
The NewGRF prefers a 32 bpp blitter.
@ GRFP_USE_MASK
Bitmask to get only the use palette use states.
@ GRFP_GRF_MASK
Bitmask to get only the NewGRF supplied information.
static void ShowSavePresetWindow(std::string_view initial_text)
Open the window for saving a preset.
std::map< uint32_t, const GRFConfig * > GrfIdMap
Map of grfid to the grf config.
static WindowDesc _save_preset_desc(WDP_CENTER, "save_preset", 140, 110, WC_SAVE_PRESET, WC_GAME_OPTIONS, WindowDefaultFlag::Modal, _nested_save_preset_widgets)
Window description of the preset save window.
static WindowDesc _scan_progress_desc(WDP_CENTER, {}, 0, 0, WC_MODAL_PROGRESS, WC_NONE, {}, _nested_scan_progress_widgets)
Description of the widgets and other settings of the window.
static void NewGRFConfirmationCallback(Window *w, bool confirmed)
Callback function for the newgrf 'apply changes' confirmation window.
static void FillGrfidMap(const GRFConfigList &lst, GrfIdMap &grfid_map)
Add all grf configs from c into the map.
void UpdateNewGRFScanStatus(uint num, std::string &&name)
Update the NewGRF scan status.
void ShowNewGRFSettings(bool editable, bool show_params, bool exec_changes, GRFConfigList &config)
Setup the NewGRF gui.
static constexpr std::initializer_list< NWidgetPart > _nested_scan_progress_widgets
Widgets for the progress window.
std::unique_ptr< NWidgetBase > NewGRFDisplay()
Construct nested container widget for managing the lists and the info panel of the NewGRF GUI.
static constexpr std::initializer_list< NWidgetPart > _nested_save_preset_widgets
Widget parts of the save preset window.
static WindowDesc _newgrf_parameters_desc(WDP_CENTER, "settings_newgrf_config", 500, 208, WC_GRF_PARAMETERS, WC_NONE, {}, _nested_newgrf_parameter_widgets)
Window definition for the change grf parameters window.
void ShowNewGRFError()
Show the first NewGRF error we can find.
void ShowMissingContentWindow(const GRFConfigList &list)
Show the content list window with all missing grfs from the given list.
std::optional< std::string_view > GetGRFStringFromGRFText(const GRFTextList &text_list)
Get a C-string from a GRFText-list.
Header of Action 04 "universal holder" structure and functions.
Types related to the newgrf widgets.
@ WID_NS_VIEW_PARAMETERS
Open Parameters Window for selected NewGRF for viewing parameters.
@ WID_NS_APPLY_CHANGES
Apply changes to NewGRF config.
@ WID_NS_OPEN_URL
Open URL of NewGRF.
@ WID_NS_CONTENT_DOWNLOAD2
Open content download (active NewGRFs).
@ WID_NS_RESCAN_FILES
Rescan files (available NewGRFs).
@ WID_NS_CONTENT_DOWNLOAD
Open content download (available NewGRFs).
@ WID_NS_SHOW_EDIT
Select display of the buttons below the 'details'.
@ WID_NS_MOVE_UP
Move NewGRF up in active list.
@ WID_NS_SHOW_REMOVE
Select active list buttons (0 = normal, 1 = simple layout).
@ WID_NS_NEWGRF_INFO
Panel for Info on selected NewGRF.
@ WID_NS_NEWGRF_TEXTFILE
Open NewGRF readme, changelog (+1) or license (+2).
@ WID_NS_SET_PARAMETERS
Open Parameters Window for selected NewGRF for editing parameters.
@ WID_NS_ADD
Add NewGRF to active list.
@ WID_NS_MOVE_DOWN
Move NewGRF down in active list.
@ WID_NS_SCROLL2BAR
Scrollbar for available NewGRF list.
@ WID_NS_UPGRADE
Upgrade NewGRFs that have a newer version available.
@ WID_NS_PRESET_SAVE
Save list of active NewGRFs as presets.
@ WID_NS_FILTER
Filter list of available NewGRFs.
@ WID_NS_FILE_LIST
List window of active NewGRFs.
@ WID_NS_SHOW_APPLY
Select display of the apply button.
@ WID_NS_SCROLLBAR
Scrollbar for active NewGRF list.
@ WID_NS_PRESET_DELETE
Delete active preset.
@ WID_NS_TOGGLE_PALETTE
Toggle Palette of selected, active NewGRF.
@ WID_NS_RESCAN_FILES2
Rescan files (active NewGRFs).
@ WID_NS_NEWGRF_INFO_TITLE
Title for Info on selected NewGRF.
@ WID_NS_REMOVE
Remove NewGRF from active list.
@ WID_NS_PRESET_LIST
Active NewGRF preset.
@ WID_NS_AVAIL_LIST
List window of available NewGRFs.
@ WID_SP_PROGRESS_BAR
Simple progress bar.
@ WID_SP_PROGRESS_TEXT
Text explaining what is happening.
@ WID_NP_NUMPAR_INC
Button to increase number of parameters.
@ WID_NP_RESET
Reset button.
@ WID_NP_NUMPAR_DEC
Button to decrease number of parameters.
@ WID_NP_SCROLLBAR
Scrollbar to scroll through all settings.
@ WID_NP_CAPTION
Caption of the window.
@ WID_NP_SETTING_DROPDOWN
Dynamically created dropdown for changing setting value.
@ WID_NP_BACKGROUND
Panel to draw the settings on.
@ WID_NP_NUMPAR
Optional number of parameters.
@ WID_NP_SHOW_NUMPAR
NWID_SELECTION to optionally display WID_NP_NUMPAR.
@ WID_NP_SHOW_DESCRIPTION
NWID_SELECTION to optionally display parameter descriptions.
@ WID_NP_DESCRIPTION
Multi-line description of a parameter.
@ WID_SVP_SAVE
Button to save the preset.
@ WID_SVP_PRESET_LIST
List with available preset names.
@ WID_SVP_SCROLLBAR
Scrollbar for the list available preset names.
@ WID_SVP_EDITBOX
Edit box for changing the preset name.
bool RequestNewGRFScan(NewGRFScanCallback *callback)
Request a new NewGRF scan.
Definition openttd.cpp:1327
PixelColour GetColourGradient(Colours colour, ColourShade shade)
Get colour gradient palette index.
Definition palette.cpp:388
static constexpr PixelColour PC_DARK_BLUE
Dark blue palette colour.
static constexpr PixelColour PC_GREY
Grey palette colour.
static constexpr PixelColour PC_DARK_GREY
Dark grey palette colour.
static constexpr PixelColour PC_BLACK
Black palette colour.
Base for the GUIs that have an edit box in them.
A number of safeguards to prevent using unsafe methods.
void SaveGRFPresetToConfig(std::string_view config_name, GRFConfigList &config)
Save a NewGRF configuration with a preset name.
StringList GetGRFPresetList()
Get the list of known NewGrf presets.
void DeleteGRFPresetFromConfig(std::string_view config_name)
Delete a NewGRF configuration by preset name.
GRFConfigList LoadGRFPresetFromConfig(std::string_view config_name)
Load a NewGRF configuration by preset-name.
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:60
Functions related to setting/changing the settings.
void DrawArrowButtons(int x, int y, Colours button_colour, uint8_t state, bool clickable_left, bool clickable_right)
Draw [<][>] boxes.
void DrawBoolButton(int x, int y, Colours button_colour, Colours background, bool state, bool clickable)
Draw a toggle button.
void DrawDropDownButton(int x, int y, Colours button_colour, bool state, bool clickable)
Draw a dropdown button.
Functions for setting GUIs.
#define SETTING_BUTTON_WIDTH
Width of setting buttons.
#define SETTING_BUTTON_HEIGHT
Height of setting buttons.
Types related to global configuration settings.
Base types for having sorted lists in GUIs.
This file contains all sprite-related enums and defines.
static const CursorID SPR_CURSOR_MOUSE
Cursor sprite numbers.
Definition sprites.h:1404
Definition of base types and functions in a cross-platform compatible way.
std::string FormatArrayAsHex(std::span< const uint8_t > data)
Format a byte array into a continuous hex string.
Definition string.cpp:77
int StrNaturalCompare(std::string_view s1, std::string_view s2, bool ignore_garbage_at_front)
Compares two strings using case insensitive natural sort.
Definition string.cpp:427
static void StrMakeValid(Builder &builder, StringConsumer &consumer, StringValidationSettings settings)
Copies the valid (UTF-8) characters from consumer to the builder.
Definition string.cpp:119
Parse strings.
@ CS_NUMERAL
Only numeric ones.
Definition string_type.h:26
std::vector< std::string > StringList
Type for a list of strings.
Definition string_type.h:60
Searching and filtering using a stringterm.
uint64_t GetParamMaxValue(uint64_t max_value, uint min_count, FontSize size)
Get some number that is suitable for string size computations.
Definition strings.cpp:236
EncodedString GetEncodedStringWithArgs(StringID str, std::span< const StringParameter > params)
Encode a string with its parameters into an encoded string.
Definition strings.cpp:102
void GetStringWithArgs(StringBuilder &builder, StringID string, StringParameters &args, uint case_index, bool game_script)
Get a parsed string with most special stringcodes replaced by the string parameters.
Definition strings.cpp:336
EncodedString GetEncodedString(StringID str)
Encode a string with no parameters into an encoded string.
Definition strings.cpp:90
std::string GetString(StringID string)
Resolve the given StringID into a std::string with formatting but no parameters.
Definition strings.cpp:424
TextDirection _current_text_dir
Text direction of the currently selected language.
Definition strings.cpp:56
uint64_t GetParamMaxDigits(uint count, FontSize size)
Get some number that is suitable for string size computations.
Definition strings.cpp:218
Functions related to OTTD's strings.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
@ TD_RTL
Text is written right-to-left by default.
GUISettings gui
settings related to the GUI
@ DoesNotExist
The content does not exist in the content system.
T y
Y coordinate.
Dimensions (a width and height) of a rectangle in 2D.
Data structure describing what to show in the list (filter criteria).
Information about GRF, used in the game and (part of it) in savegames.
MD5Hash original_md5sum
MD5 checksum of original file if only a 'compatible' file was loaded.
void SetParameterDefaults()
Set the default value for all parameters as specified by action14.
uint8_t palette
GRFPalette, bitset.
std::vector< std::optional< GRFParameterInfo > > param_info
NOSAVE: extra information about the parameters.
std::vector< GRFError > errors
NOSAVE: Error/Warning during GRF loading (Action 0x0B)
uint32_t version
NOSAVE: Version a NewGRF can set so only the newest NewGRF is shown.
std::vector< uint32_t > param
GRF parameters.
std::optional< std::string > GetURL() const
Get the grf url.
GRFStatus status
NOSAVE: GRFStatus, enum.
void SetValue(const GRFParameterInfo &info, uint32_t value)
Set the value of the given user-changeable parameter.
std::optional< std::string > GetTextfile(TextfileType type) const
Search a textfile file next to this NewGRF.
GRFConfigFlags flags
NOSAVE: GCF_Flags, bitset.
uint8_t num_valid_params
NOSAVE: Number of valid parameters (action 0x14)
std::string filename
Filename - either with or without full path.
GRFIdentifier ident
grfid and md5sum to uniquely identify newgrfs
uint32_t min_loadable_version
NOSAVE: Minimum compatible version a NewGRF can define.
std::string GetName() const
Get the name of this grf.
std::optional< std::string > GetDescription() const
Get the grf info.
uint32_t GetValue(const GRFParameterInfo &info) const
Get the value of the given user-changeable parameter.
Information about why GRF had problems during initialisation.
StringID severity
Info / Warning / Error / Fatal.
std::array< uint32_t, 2 > param_value
Values of GRF parameters to show for message and custom_message.
StringID message
Default message.
std::string custom_message
Custom message (if present)
std::string data
Additional data for message and custom_message.
bool HasGrfIdentifier(uint32_t grfid, const MD5Hash *md5sum) const
Does the identification match the provided values?
uint32_t grfid
GRF ID (defined by Action 0x08)
MD5Hash md5sum
MD5 checksum of file to distinguish files with the same GRF ID (eg. newer version of GRF)
Information about one grf parameter.
bool complete_labels
True if all values have a label.
uint8_t param_nr
GRF parameter to store content in.
uint32_t min_value
The minimal value this parameter can have.
uint32_t max_value
The maximal value of this parameter.
GRFParameterType type
The type of this parameter.
std::vector< ValueName > value_names
Names for each value.
GRFTextList name
The name of this parameter.
GRFTextList desc
The description of this parameter.
uint32_t last_newgrf_count
the numbers of NewGRFs we found during the last scan
bool newgrf_show_old_versions
whether to show old versions in the NewGRF list
bool scenario_developer
activate scenario developer: allow modifying NewGRFs in an existing game
bool newgrf_developer_tools
activate NewGRF developer tools and allow modifying NewGRFs in an existing game
Data structure describing how to show the list (what sort direction and criteria).
Window for setting the parameters of a NewGRF.
bool clicked_dropdown
Whether the dropdown is open.
std::string GetWidgetString(WidgetID widget, StringID stringid) const override
Get the raw string for a widget.
void OnQueryTextFinished(std::optional< std::string > str) override
The query window opened from this window has closed.
void OnDropdownClose(Point, WidgetID widget, int, int, bool) override
A dropdown window associated to this window has been closed.
void OnPaint() override
The window must be repainted.
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
bool editable
Allow editing parameters.
bool HasParameterInfo(uint nr) const
Test if GRF Parameter Info exists for a given parameter index.
static GRFParameterInfo dummy_parameter_info
Dummy info in case a newgrf didn't provide info about some parameter.
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
void OnDropdownSelect(WidgetID widget, int index, int) override
A dropdown option associated to this window has been selected.
TimeoutTimer< TimerWindow > unclick_timeout
When reset, unclick the button after a small timeout.
bool action14present
True if action14 information is present.
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.
int line_height
Height of a row in the matrix widget.
bool clicked_increase
True if the increase button was clicked, false for the decrease button.
int32_t clicked_row
The selected parameter, or INT32_MAX when none is selected.
bool closing_dropdown
True, if the dropdown list is currently closing.
GRFConfig & grf_config
Set the parameters of this GRFConfig.
int32_t clicked_button
The row in which a button was clicked or INT32_MAX when none is selected.
static GRFParameterInfo & GetDummyParameterInfo(uint nr)
Get a dummy parameter-info object with default information.
GRFParameterInfo & GetParameterInfo(uint nr) const
Get GRF Parameter Info exists for a given parameter index.
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
void OnResize() override
Called after the window got resized.
Callback for NewGRF scanning.
Window for displaying the textfile of a NewGRF.
std::string GetWidgetString(WidgetID widget, StringID stringid) const override
Get the raw string for a widget.
const GRFConfig * grf_config
View the textfile of this GRFConfig.
Window for showing NewGRF files.
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.
bool editable
Is the window editable?
void OnEditboxChanged(WidgetID widget) override
The text in an editbox has been edited.
bool CanUpgradeCurrent()
Test whether the currently active set of NewGRFs can be upgraded with the available NewGRFs.
GRFConfig * active_sel
Selected active grf item.
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
bool show_params
Are the grf-parameters shown in the info-panel?
void UpgradeCurrent()
Upgrade the currently active set of NewGRFs.
void OnResize() override
Called after the window got resized.
std::string GetWidgetString(WidgetID widget, StringID stringid) const override
Get the raw string for a widget.
bool AddGRFToActive(int ins_pos=-1)
Insert a GRF into the active list.
GRFConfigList actives
Temporary active grf list to which changes are made.
int avail_pos
Index of avail_sel if existing, else -1.
int active_over
Active GRF item over which another one is dragged, -1 if none.
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
void OnMouseDrag(Point pt, WidgetID widget) override
An 'object' is being dragged at the provided position, highlight the target if possible.
bool modified
The list of active NewGRFs has been modified since the last time they got saved.
bool execute
On pressing 'apply changes' are grf changes applied immediately, or only list is updated.
GRFConfigList & orig_list
List active grfs in the game. Used as initial value, may be updated by the window.
EventState OnKeyPress(char32_t key, uint16_t keycode) override
A key has been pressed.
int preset
Selected preset or -1 if none selected.
PaletteID GetPalette(const GRFConfig &c) const
Pick the palette for the sprite of the grf to display.
StringList grf_presets
List of known NewGRF presets.
const GRFConfig * avail_sel
Currently selected available grf. nullptr is none is selected.
void UpdateScrollBars()
Updates the scroll bars for the active and inactive NewGRF lists.
QueryString filter_editbox
Filter editbox;.
static bool TagNameFilter(const GRFConfig *const *a, StringFilter &filter)
Filter grfs by tags/name.
void OnDropdownSelect(WidgetID widget, int index, int) override
A dropdown option associated to this window has been selected.
void OnDragDrop(Point pt, WidgetID widget) override
A dragged 'object' has been released.
static Filtering last_filtering
Default filtering of GUIGRFConfigList.
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
static const std::initializer_list< GUIGRFConfigList::SortFunction *const > sorter_funcs
Sort functions of the GUIGRFConfigList.
void OnQueryTextFinished(std::optional< std::string > str) override
The query window opened from this window has closed.
static const std::initializer_list< GUIGRFConfigList::FilterFunction *const > filter_funcs
Filter functions of the GUIGRFConfigList.
void OnNewGRFsScanned() override
Called whenever the NewGRF scan completed.
GUIGRFConfigList avails
Available (non-active) grfs.
StringFilter string_filter
Filter for available grf.
static Listing last_sorting
Default sorting of GUIGRFConfigList.
static bool NameSorter(const GRFConfig *const &a, const GRFConfig *const &b)
Sort grfs by name.
void Close(int data=0) override
Hide the window and all its child windows, and mark them for a later deletion.
Data stored about a string that can be modified in the GUI.
int ok_button
Widget button of parent window to simulate when pressing OK in OSK.
int cancel_button
Widget button of parent window to simulate when pressing CANCEL in OSK.
static const int ACTION_CLEAR
Clear editbox.
constexpr uint Horizontal() const
Get total horizontal padding of RectPadding.
constexpr uint Vertical() const
Get total vertical padding of RectPadding.
Specification of a rectangle with absolute coordinates of all edges.
int Width() const
Get width of Rect.
Rect Shrink(int s) const
Copy and shrink Rect by s pixels.
Rect Indent(int indent, bool end) const
Copy Rect and indent it from its position.
Class for the save preset window.
QueryString presetname_editbox
Edit box of the save preset.
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.
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
void OnResize() override
Called after the window got resized.
int selected
Selected entry in the preset list, or -1 if none selected.
Scrollbar * vscroll
Pointer to the scrollbar widget.
StringList presets
Available presets.
SavePresetWindow(std::string_view initial_text)
Constructor of the save preset window.
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
Window for showing the progress of NewGRF scanning.
std::string last_name
The name of the last 'seen' NewGRF.
int scanned
The number of NewGRFs that we have seen.
void UpdateNewGRFScanStatus(uint num, std::string &&name)
Update the NewGRF scan status.
ScanProgressWindow()
Create the window.
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested 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.
String filter and state.
bool IsEmpty() const
Check whether any filter words were entered.
void SetFilterTerm(std::string_view str)
Set the term to filter on.
void ResetState()
Reset the matching state to process a new item.
bool GetState() const
Get the matching state of the current item.
The data required to format and validate a single parameter of a string.
std::string_view GetText() const
Get the current text.
Definition textbuf.cpp:284
void Assign(std::string_view text)
Copy a string into the textbuffer.
Definition textbuf.cpp:420
Window for displaying a textfile.
TextfileType file_type
Type of textfile to view.
virtual void LoadTextfile(const std::string &textfile, Subdirectory dir)
Loads the textfile text from file and setup lines.
High level window description.
Definition window_gui.h:168
Data structure for an opened window.
Definition window_gui.h:274
virtual void Close(int data=0)
Hide the window and all its child windows, and mark them for a later deletion.
Definition window.cpp:1104
void FinishInitNested(WindowNumber window_number=0)
Perform the second part of the initialization of a nested widget tree.
Definition window.cpp:1809
std::map< WidgetID, QueryString * > querystrings
QueryString associated to WWT_EDITBOX widgets.
Definition window_gui.h:321
void DrawWidgets() const
Paint all widgets of a window.
Definition widget.cpp:766
void InvalidateData(int data=0, bool gui_scope=true)
Mark this window's data as invalid (in need of re-computing)
Definition window.cpp:3240
Window * parent
Parent window.
Definition window_gui.h:329
void SetWidgetDirty(WidgetID widget_index) const
Invalidate a widget, i.e.
Definition window.cpp:558
virtual std::string GetWidgetString(WidgetID widget, StringID stringid) const
Get the raw string for a widget.
Definition window.cpp:506
void CloseChildWindows(WindowClass wc=WC_INVALID) const
Close all children a window might have in a head-recursive manner.
Definition window.cpp:1077
ResizeInfo resize
Resize information.
Definition window_gui.h:315
void DisableWidget(WidgetID widget_index)
Sets a widget to disabled.
Definition window_gui.h:392
void SetWidgetsDisabledState(bool disab_stat, Args... widgets)
Sets the enabled/disabled status of a list of widgets.
Definition window_gui.h:516
void CreateNestedTree()
Perform the first part of the initialization of a nested widget tree.
Definition window.cpp:1799
void CloseChildWindowById(WindowClass wc, WindowNumber number) const
Close all children a window might have in a head-recursive manner.
Definition window.cpp:1092
virtual void OnQueryTextFinished(std::optional< std::string > str)
The query window opened from this window has closed.
Definition window_gui.h:787
bool SetFocusedWidget(WidgetID widget_index)
Set focus within this window to the given widget.
Definition window.cpp:487
int top
y position of top edge of the window
Definition window_gui.h:311
void InitNested(WindowNumber number=0)
Perform complete initialization of the Window with nested widgets, to allow use.
Definition window.cpp:1822
const Scrollbar * GetScrollbar(WidgetID widnum) const
Return the Scrollbar to a widget index.
Definition window.cpp:315
void SetWidgetDisabledState(WidgetID widget_index, bool disab_stat)
Sets the enabled/disabled status of a widget.
Definition window_gui.h:382
int width
width of the window (number of pixels to the right in x direction)
Definition window_gui.h:312
@ CONTENT_TYPE_NEWGRF
The content consists of a NewGRF.
GUI functions related to textfiles.
TextfileType
Additional text files accompanying Tar archives.
@ TFT_LICENSE
Content license.
@ TFT_README
Content readme.
@ TFT_CHANGELOG
Content changelog.
Functions related to tile highlights.
void ResetObjectToPlace()
Reset the cursor and mouse mode handling back to default (normal cursor, only clicking in windows).
void SetObjectToPlaceWnd(CursorID icon, PaletteID pal, HighLightStyle mode, Window *w)
Change the cursor and mouse click/drag handling to a mode for performing special operations like tile...
@ HT_DRAG
dragging items in the depot windows
Definition of Interval and OneShot timers.
Definition of the Window system.
void DrawFrameRect(int left, int top, int right, int bottom, Colours colour, FrameFlags flags)
Draw frame rectangle.
Definition widget.cpp:289
static RectPadding ScaleGUITrad(const RectPadding &r)
Scale a RectPadding to GUI zoom level.
Definition widget.cpp:49
@ WWT_PUSHTXTBTN
Normal push-button (no toggle button) with text caption.
@ WWT_INSET
Pressed (inset) panel, most commonly used as combo box text area.
Definition widget_type.h:40
@ NWID_CUSTOM
General Custom widget.
Definition widget_type.h:77
@ WWT_PUSHARROWBTN
Normal push-button (no toggle button) with arrow caption.
@ WWT_LABEL
Centered label.
Definition widget_type.h:48
@ NWID_SPACER
Invisible widget that takes some space.
Definition widget_type.h:70
@ WWT_EDITBOX
a textbox for typing
Definition widget_type.h:62
@ NWID_HORIZONTAL
Horizontal container.
Definition widget_type.h:66
@ WWT_PANEL
Simple depressed panel.
Definition widget_type.h:39
@ WWT_MATRIX
Grid of rows and columns.
Definition widget_type.h:50
@ WWT_CAPTION
Window caption (window title between closebox and stickybox)
Definition widget_type.h:52
@ NWID_VSCROLLBAR
Vertical scrollbar.
Definition widget_type.h:76
@ NWID_VERTICAL
Vertical container.
Definition widget_type.h:68
@ WWT_CLOSEBOX
Close box (at top-left of a window)
Definition widget_type.h:60
@ WWT_FRAME
Frame.
Definition widget_type.h:51
@ WWT_EMPTY
Empty widget, place holder to reserve space in widget tree.
Definition widget_type.h:37
@ WWT_RESIZEBOX
Resize box (normally at bottom-right of a window)
Definition widget_type.h:59
@ WWT_DEFSIZEBOX
Default window size box (at top-right of a window, between WWT_SHADEBOX and WWT_STICKYBOX)
Definition widget_type.h:56
@ WWT_DROPDOWN
Drop down list.
Definition widget_type.h:61
@ WWT_TEXT
Pure simple text.
Definition widget_type.h:49
@ NWID_SELECTION
Stacked widgets, only one visible at a time (eg in a panel with tabs).
Definition widget_type.h:71
uint ComputeMaxSize(uint base, uint max_space, uint step)
Return the biggest possible size of a nested widget.
@ SZSP_HORIZONTAL
Display plane with zero size vertically, and filling and resizing horizontally.
@ SZSP_VERTICAL
Display plane with zero size horizontally, and filling and resizing vertically.
@ EqualSize
Containers should keep all their (resizing) children equally large.
SizingType
Different forms of sizing nested widgets, using NWidgetBase::AssignSizePosition()
@ AWV_DECREASE
Arrow to the left or in case of RTL to the right.
Definition widget_type.h:20
@ AWV_INCREASE
Arrow to the right or in case of RTL to the left.
Definition widget_type.h:21
std::map< WidgetID, class NWidgetBase * > WidgetLookup
Lookup between widget IDs and NWidget objects.
@ RWV_HIDE_BEVEL
Bevel of resize box is hidden.
Definition widget_type.h:29
Window * FindWindowByClass(WindowClass cls)
Find any window by its class.
Definition window.cpp:1168
void ReInitAllWindows(bool zoom_changed)
Re-initialize all windows.
Definition window.cpp:3417
void CloseWindowByClass(WindowClass cls, int data)
Close all windows of a given class.
Definition window.cpp:1207
Window * FindWindowById(WindowClass cls, WindowNumber number)
Find a window by its class and window number.
Definition window.cpp:1153
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting)
Definition window.cpp:3178
Window functions not directly related to making/drawing windows.
@ Modal
The window is a modal child of some other window, meaning the parent is 'inactive'.
@ BorderOnly
Draw border only, no background.
@ Lowered
If set the frame is lowered and the background colour brighter (ie. buttons when pressed)
@ WDP_CENTER
Center the window.
Definition window_gui.h:145
int WidgetID
Widget ID.
Definition window_type.h:20
@ WN_GAME_OPTIONS_NEWGRF_STATE
NewGRF settings.
Definition window_type.h:30
EventState
State of handling an event.
@ ES_HANDLED
The passed event is handled.
@ ES_NOT_HANDLED
The passed event is not handled.
@ GOID_NEWGRF_CURRENT_LOADED
The current list of active NewGRF has been loaded.
@ GOID_NEWGRF_RESCANNED
NewGRFs were just rescanned.
@ GOID_NEWGRF_CHANGES_MADE
Changes have been made to a given NewGRF either through the palette or its parameters.
@ GOID_NEWGRF_LIST_EDITED
List of active NewGRFs is being edited.
@ WC_BUILD_OBJECT
Build object; Window numbers:
@ WC_NONE
No window, redirects to WC_MAIN_WINDOW.
Definition window_type.h:50
@ WC_SAVE_PRESET
Save preset; Window numbers:
@ WC_GAME_OPTIONS
Game options window; Window numbers:
@ WC_TEXTFILE
textfile; Window numbers:
@ WC_DROPDOWN_MENU
Drop down menu; Window numbers:
@ WC_QUERY_STRING
Query string window; Window numbers:
@ WC_GRF_PARAMETERS
NewGRF parameters; Window numbers:
@ WC_MODAL_PROGRESS
Progress report of landscape generation; Window numbers:
Functions related to zooming.