OpenTTD Source 15.0-beta2
industry_gui.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6 */
7
10#include "stdafx.h"
11#include <ranges>
12#include "error.h"
13#include "gui.h"
14#include "settings_gui.h"
15#include "sound_func.h"
16#include "window_func.h"
17#include "textbuf_gui.h"
18#include "command_func.h"
19#include "viewport_func.h"
20#include "industry.h"
21#include "town.h"
22#include "cheat_type.h"
23#include "newgrf_badge.h"
24#include "newgrf_industries.h"
25#include "newgrf_text.h"
26#include "newgrf_debug.h"
27#include "network/network.h"
28#include "strings_func.h"
29#include "company_func.h"
30#include "tilehighlight_func.h"
31#include "string_func.h"
32#include "sortlist_type.h"
33#include "dropdown_func.h"
34#include "company_base.h"
36#include "core/random_func.hpp"
37#include "core/backup_type.hpp"
38#include "genworld.h"
39#include "smallmap_gui.h"
40#include "dropdown_type.h"
41#include "clear_map.h"
42#include "zoom_func.h"
43#include "industry_cmd.h"
44#include "graph_gui.h"
45#include "querystring_gui.h"
46#include "stringfilter_type.h"
47#include "timer/timer.h"
48#include "timer/timer_window.h"
49#include "hotkeys.h"
50
52
53#include "table/strings.h"
54
55#include <bitset>
56
57#include "safeguards.h"
58
59bool _ignore_industry_restrictions;
60std::bitset<NUM_INDUSTRYTYPES> _displayed_industries;
61
68
76
82
83extern void GenerateIndustries();
84static void ShowIndustryCargoesWindow(IndustryType id);
85
95static void GetCargoSuffix(uint cargo, CargoSuffixType cst, const Industry *ind, IndustryType ind_type, const IndustrySpec *indspec, CargoSuffix &suffix)
96{
97 suffix.text.clear();
99
101 TileIndex t = (cst != CST_FUND) ? ind->location.tile : INVALID_TILE;
102 uint16_t callback = GetIndustryCallback(CBID_INDUSTRY_CARGO_SUFFIX, 0, (cst << 8) | cargo, const_cast<Industry *>(ind), ind_type, t);
103 if (callback == CALLBACK_FAILED) return;
104
105 if (indspec->grf_prop.grffile->grf_version < 8) {
106 if (GB(callback, 0, 8) == 0xFF) return;
107 if (callback < 0x400) {
108 suffix.text = GetGRFStringWithTextStack(indspec->grf_prop.grffile, GRFSTR_MISC_GRF_TEXT + callback, 6);
110 return;
111 }
113 return;
114
115 } else { // GRF version 8 or higher.
116 if (callback == 0x400) return;
117 if (callback == 0x401) {
118 suffix.display = CSD_CARGO;
119 return;
120 }
121 if (callback < 0x400) {
122 suffix.text = GetGRFStringWithTextStack(indspec->grf_prop.grffile, GRFSTR_MISC_GRF_TEXT + callback, 6);
124 return;
125 }
126 if (callback >= 0x800 && callback < 0xC00) {
127 suffix.text = GetGRFStringWithTextStack(indspec->grf_prop.grffile, GRFSTR_MISC_GRF_TEXT + callback - 0x800, 6);
128 suffix.display = CSD_CARGO_TEXT;
129 return;
130 }
132 return;
133 }
134 }
135}
136
137enum CargoSuffixInOut : uint8_t {
138 CARGOSUFFIX_OUT = 0,
139 CARGOSUFFIX_IN = 1,
140};
141
152template <typename TC, typename TS>
153static inline void GetAllCargoSuffixes(CargoSuffixInOut use_input, CargoSuffixType cst, const Industry *ind, IndustryType ind_type, const IndustrySpec *indspec, const TC &cargoes, TS &suffixes)
154{
155 static_assert(std::tuple_size_v<std::remove_reference_t<decltype(cargoes)>> <= lengthof(suffixes));
156
158 /* Reworked behaviour with new many-in-many-out scheme */
159 for (uint j = 0; j < lengthof(suffixes); j++) {
160 if (IsValidCargoType(cargoes[j])) {
161 uint8_t local_id = indspec->grf_prop.grffile->cargo_map[cargoes[j]]; // should we check the value for valid?
162 uint cargotype = local_id << 16 | use_input;
163 GetCargoSuffix(cargotype, cst, ind, ind_type, indspec, suffixes[j]);
164 } else {
165 suffixes[j].text.clear();
166 suffixes[j].display = CSD_CARGO;
167 }
168 }
169 } else {
170 /* Compatible behaviour with old 3-in-2-out scheme */
171 for (uint j = 0; j < lengthof(suffixes); j++) {
172 suffixes[j].text.clear();
173 suffixes[j].display = CSD_CARGO;
174 }
175 switch (use_input) {
176 case CARGOSUFFIX_OUT:
177 // Handle INDUSTRY_ORIGINAL_NUM_OUTPUTS cargoes
178 if (IsValidCargoType(cargoes[0])) GetCargoSuffix(3, cst, ind, ind_type, indspec, suffixes[0]);
179 if (IsValidCargoType(cargoes[1])) GetCargoSuffix(4, cst, ind, ind_type, indspec, suffixes[1]);
180 break;
181 case CARGOSUFFIX_IN:
182 // Handle INDUSTRY_ORIGINAL_NUM_INPUTS cargoes
183 if (IsValidCargoType(cargoes[0])) GetCargoSuffix(0, cst, ind, ind_type, indspec, suffixes[0]);
184 if (IsValidCargoType(cargoes[1])) GetCargoSuffix(1, cst, ind, ind_type, indspec, suffixes[1]);
185 if (IsValidCargoType(cargoes[2])) GetCargoSuffix(2, cst, ind, ind_type, indspec, suffixes[2]);
186 break;
187 default:
188 NOT_REACHED();
189 }
190 }
191}
192
204void GetCargoSuffix(CargoSuffixInOut use_input, CargoSuffixType cst, const Industry *ind, IndustryType ind_type, const IndustrySpec *indspec, CargoType cargo, uint8_t slot, CargoSuffix &suffix)
205{
206 suffix.text.clear();
207 suffix.display = CSD_CARGO;
208 if (!IsValidCargoType(cargo)) return;
210 uint8_t local_id = indspec->grf_prop.grffile->cargo_map[cargo]; // should we check the value for valid?
211 uint cargotype = local_id << 16 | use_input;
212 GetCargoSuffix(cargotype, cst, ind, ind_type, indspec, suffix);
213 } else if (use_input == CARGOSUFFIX_IN) {
214 if (slot < INDUSTRY_ORIGINAL_NUM_INPUTS) GetCargoSuffix(slot, cst, ind, ind_type, indspec, suffix);
215 } else if (use_input == CARGOSUFFIX_OUT) {
216 if (slot < INDUSTRY_ORIGINAL_NUM_OUTPUTS) GetCargoSuffix(slot + INDUSTRY_ORIGINAL_NUM_INPUTS, cst, ind, ind_type, indspec, suffix);
217 }
218}
219
220std::array<IndustryType, NUM_INDUSTRYTYPES> _sorted_industry_types;
221
223static bool IndustryTypeNameSorter(const IndustryType &a, const IndustryType &b)
224{
225 int r = StrNaturalCompare(GetString(GetIndustrySpec(a)->name), GetString(GetIndustrySpec(b)->name)); // Sort by name (natural sorting).
226
227 /* If the names are equal, sort by industry type. */
228 return (r != 0) ? r < 0 : (a < b);
229}
230
235{
236 /* Add each industry type to the list. */
237 for (IndustryType i = 0; i < NUM_INDUSTRYTYPES; i++) {
239 }
240
241 /* Sort industry types by name. */
243}
244
251void CcBuildIndustry(Commands, const CommandCost &result, TileIndex tile, IndustryType indtype, uint32_t, bool, uint32_t)
252{
253 if (result.Succeeded()) return;
254
255 if (indtype < NUM_INDUSTRYTYPES) {
256 const IndustrySpec *indsp = GetIndustrySpec(indtype);
257 if (indsp->enabled) {
258 ShowErrorMessage(GetEncodedString(STR_ERROR_CAN_T_BUILD_HERE, indsp->name),
260 }
261 }
262}
263
264static constexpr NWidgetPart _nested_build_industry_widgets[] = {
266 NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
267 NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetStringTip(STR_FUND_INDUSTRY_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
268 NWidget(WWT_SHADEBOX, COLOUR_DARK_GREEN),
269 NWidget(WWT_DEFSIZEBOX, COLOUR_DARK_GREEN),
270 NWidget(WWT_STICKYBOX, COLOUR_DARK_GREEN),
271 EndContainer(),
275 SetStringTip(STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES, STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_TOOLTIP),
277 SetStringTip(STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES, STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES_TOOLTIP),
278 EndContainer(),
279 EndContainer(),
281 NWidget(WWT_MATRIX, COLOUR_DARK_GREEN, WID_DPI_MATRIX_WIDGET), SetMatrixDataTip(1, 0, STR_FUND_INDUSTRY_SELECTION_TOOLTIP), SetFill(1, 0), SetResize(1, 1), SetScrollbar(WID_DPI_SCROLLBAR),
282 NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, WID_DPI_SCROLLBAR),
283 EndContainer(),
284 NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_DPI_INFOPANEL), SetResize(1, 0),
285 EndContainer(),
287 NWidget(WWT_TEXTBTN, COLOUR_DARK_GREEN, WID_DPI_DISPLAY_WIDGET), SetFill(1, 0), SetResize(1, 0),
288 SetStringTip(STR_INDUSTRY_DISPLAY_CHAIN, STR_INDUSTRY_DISPLAY_CHAIN_TOOLTIP),
289 NWidget(WWT_TEXTBTN, COLOUR_DARK_GREEN, WID_DPI_FUND_WIDGET), SetFill(1, 0), SetResize(1, 0),
290 NWidget(WWT_RESIZEBOX, COLOUR_DARK_GREEN),
291 EndContainer(),
292};
293
296 WDP_AUTO, "build_industry", 170, 212,
299 _nested_build_industry_widgets
300);
301
304 IndustryType selected_type = IT_INVALID;
305 std::vector<IndustryType> list{};
306 bool enabled = false;
307 Scrollbar *vscroll = nullptr;
309 GUIBadgeClasses badge_classes{};
310
312 static const int MAX_MINWIDTH_LINEHEIGHTS = 20;
313
314 void UpdateAvailability()
315 {
316 this->enabled = this->selected_type != IT_INVALID && (_game_mode == GM_EDITOR || GetIndustryProbabilityCallback(this->selected_type, IACT_USERCREATION, 1) > 0);
317 }
318
319 void SetupArrays()
320 {
321 this->list.clear();
322
323 /* Fill the arrays with industries.
324 * The tests performed after the enabled allow to load the industries
325 * In the same way they are inserted by grf (if any)
326 */
327 for (IndustryType ind : _sorted_industry_types) {
328 const IndustrySpec *indsp = GetIndustrySpec(ind);
329 if (indsp->enabled) {
330 /* Rule is that editor mode loads all industries.
331 * In game mode, all non raw industries are loaded too
332 * and raw ones are loaded only when setting allows it */
333 if (_game_mode != GM_EDITOR && indsp->IsRawIndustry() && _settings_game.construction.raw_industry_construction == 0) {
334 /* Unselect if the industry is no longer in the list */
335 if (this->selected_type == ind) this->selected_type = IT_INVALID;
336 continue;
337 }
338
339 this->list.push_back(ind);
340 }
341 }
342
343 /* First industry type is selected if the current selection is invalid. */
344 if (this->selected_type == IT_INVALID && !this->list.empty()) this->selected_type = this->list[0];
345
346 this->UpdateAvailability();
347
348 this->vscroll->SetCount(this->list.size());
349 }
350
353 {
354 this->SetWidgetDisabledState(WID_DPI_FUND_WIDGET, this->selected_type != IT_INVALID && !this->enabled);
355 this->SetWidgetDisabledState(WID_DPI_DISPLAY_WIDGET, this->selected_type == IT_INVALID && this->enabled);
356 }
357
370 std::string MakeCargoListString(const std::span<const CargoType> cargolist, const std::span<const CargoSuffix> cargo_suffix, StringID prefixstr) const
371 {
372 assert(cargolist.size() == cargo_suffix.size());
373
374 std::string cargostring;
375 size_t numcargo = 0;
376 size_t firstcargo = 0;
377
378 for (size_t j = 0; j < cargolist.size(); j++) {
379 if (!IsValidCargoType(cargolist[j])) continue;
380 numcargo++;
381 if (numcargo == 1) {
382 firstcargo = j;
383 continue;
384 }
385 auto params = MakeParameters(CargoSpec::Get(cargolist[j])->name, cargo_suffix[j].text);
386 AppendStringWithArgsInPlace(cargostring, STR_INDUSTRY_VIEW_CARGO_LIST_EXTENSION, params);
387 }
388
389 if (numcargo > 0) {
390 cargostring = GetString(prefixstr, CargoSpec::Get(cargolist[firstcargo])->name, cargo_suffix[firstcargo].text) + cargostring;
391 } else {
392 cargostring = GetString(prefixstr, STR_JUST_NOTHING, "");
393 }
394
395 return cargostring;
396 }
397
398public:
400 {
401 this->CreateNestedTree();
402 this->vscroll = this->GetScrollbar(WID_DPI_SCROLLBAR);
403 /* Show scenario editor tools in editor. */
404 if (_game_mode != GM_EDITOR) {
405 this->GetWidget<NWidgetStacked>(WID_DPI_SCENARIO_EDITOR_PANE)->SetDisplayedPlane(SZSP_HORIZONTAL);
406 }
407 this->FinishInitNested(0);
408
409 this->SetButtons();
410 }
411
412 void OnInit() override
413 {
414 this->badge_classes = GUIBadgeClasses{GSF_INDUSTRIES};
415
416 /* Width of the legend blob -- slightly larger than the smallmap legend blob. */
417 this->legend.height = GetCharacterHeight(FS_SMALL);
418 this->legend.width = this->legend.height * 9 / 6;
419
420 this->SetupArrays();
421 }
422
423 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
424 {
425 switch (widget) {
428 Dimension d{};
429 for (const auto &indtype : this->list) {
430 d = maxdim(d, GetStringBoundingBox(GetIndustrySpec(indtype)->name));
431 }
432 resize.height = std::max<uint>({this->legend.height, d.height, count.height}) + padding.height;
433 d.width += this->badge_classes.GetTotalColumnsWidth() + this->legend.width + WidgetDimensions::scaled.hsep_wide + WidgetDimensions::scaled.hsep_normal + count.width + padding.width;
434 d.height = 5 * resize.height;
435 size = maxdim(size, d);
436 break;
437 }
438
439 case WID_DPI_INFOPANEL: {
440 /* Extra line for cost outside of editor. */
441 int height = 2 + (_game_mode == GM_EDITOR ? 0 : 1);
442 uint extra_lines_req = 0;
443 uint extra_lines_prd = 0;
444 uint extra_lines_newgrf = 0;
446 Dimension d = {0, 0};
447 for (const auto &indtype : this->list) {
448 const IndustrySpec *indsp = GetIndustrySpec(indtype);
449 CargoSuffix cargo_suffix[std::tuple_size_v<decltype(indsp->accepts_cargo)>];
450
451 /* Measure the accepted cargoes, if any. */
452 GetAllCargoSuffixes(CARGOSUFFIX_IN, CST_FUND, nullptr, indtype, indsp, indsp->accepts_cargo, cargo_suffix);
453 std::string cargostring = this->MakeCargoListString(indsp->accepts_cargo, cargo_suffix, STR_INDUSTRY_VIEW_REQUIRES_N_CARGO);
454 Dimension strdim = GetStringBoundingBox(cargostring);
455 if (strdim.width > max_minwidth) {
456 extra_lines_req = std::max(extra_lines_req, strdim.width / max_minwidth + 1);
457 strdim.width = max_minwidth;
458 }
459 d = maxdim(d, strdim);
460
461 /* Measure the produced cargoes, if any. */
462 GetAllCargoSuffixes(CARGOSUFFIX_OUT, CST_FUND, nullptr, indtype, indsp, indsp->produced_cargo, cargo_suffix);
463 cargostring = this->MakeCargoListString(indsp->produced_cargo, cargo_suffix, STR_INDUSTRY_VIEW_PRODUCES_N_CARGO);
464 strdim = GetStringBoundingBox(cargostring);
465 if (strdim.width > max_minwidth) {
466 extra_lines_prd = std::max(extra_lines_prd, strdim.width / max_minwidth + 1);
467 strdim.width = max_minwidth;
468 }
469 d = maxdim(d, strdim);
470
471 if (indsp->grf_prop.HasGrfFile()) {
472 /* Reserve a few extra lines for text from an industry NewGRF. */
473 extra_lines_newgrf = 4;
474 }
475 }
476
477 /* Set it to something more sane :) */
478 height += extra_lines_prd + extra_lines_req + extra_lines_newgrf;
479 size.height = height * GetCharacterHeight(FS_NORMAL) + padding.height;
480 size.width = d.width + padding.width;
481 break;
482 }
483
484 case WID_DPI_FUND_WIDGET: {
485 Dimension d = GetStringBoundingBox(STR_FUND_INDUSTRY_BUILD_NEW_INDUSTRY);
486 d = maxdim(d, GetStringBoundingBox(STR_FUND_INDUSTRY_PROSPECT_NEW_INDUSTRY));
487 d = maxdim(d, GetStringBoundingBox(STR_FUND_INDUSTRY_FUND_NEW_INDUSTRY));
488 d.width += padding.width;
489 d.height += padding.height;
490 size = maxdim(size, d);
491 break;
492 }
493 }
494 }
495
496 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
497 {
498 switch (widget) {
500 /* Raw industries might be prospected. Show this fact by changing the string
501 * In Editor, you just build, while ingame, or you fund or you prospect */
502 if (_game_mode == GM_EDITOR) {
503 /* We've chosen many random industries but no industries have been specified */
504 return GetString(STR_FUND_INDUSTRY_BUILD_NEW_INDUSTRY);
505 }
506 if (this->selected_type != IT_INVALID) {
507 const IndustrySpec *indsp = GetIndustrySpec(this->selected_type);
508 return GetString((_settings_game.construction.raw_industry_construction == 2 && indsp->IsRawIndustry()) ? STR_FUND_INDUSTRY_PROSPECT_NEW_INDUSTRY : STR_FUND_INDUSTRY_FUND_NEW_INDUSTRY);
509 }
510 return GetString(STR_FUND_INDUSTRY_FUND_NEW_INDUSTRY);
511
512 default:
513 return this->Window::GetWidgetString(widget, stringid);
514 }
515 }
516
517 void DrawWidget(const Rect &r, WidgetID widget) const override
518 {
519 switch (widget) {
521 bool rtl = _current_text_dir == TD_RTL;
523 Rect icon = text.WithWidth(this->legend.width, rtl);
524 text = text.Indent(this->legend.width + WidgetDimensions::scaled.hsep_wide, rtl);
525
526 /* Vertical offset for legend icon. */
527 icon.top = r.top + (this->resize.step_height - this->legend.height + 1) / 2;
528 icon.bottom = icon.top + this->legend.height - 1;
529
530 auto badge_column_widths = badge_classes.GetColumnWidths();
531
532 auto [first, last] = this->vscroll->GetVisibleRangeIterators(this->list);
533 for (auto it = first; it != last; ++it) {
534 IndustryType type = *it;
535 bool selected = this->selected_type == type;
536 const IndustrySpec *indsp = GetIndustrySpec(type);
537
538 Rect tr = text;
539 if (badge_column_widths.size() >= 1 && badge_column_widths[0] > 0) {
540 DrawBadgeColumn(tr.WithWidth(badge_column_widths[0], rtl), 0, this->badge_classes, indsp->badges, GSF_INDUSTRIES, std::nullopt, PAL_NONE);
541 tr = tr.Indent(badge_column_widths[0], rtl);
542 }
543 if (badge_column_widths.size() >= 2 && badge_column_widths[1] > 0) {
544 DrawBadgeColumn(tr.WithWidth(badge_column_widths[1], !rtl), 0, this->badge_classes, indsp->badges, GSF_INDUSTRIES, std::nullopt, PAL_NONE);
545 tr = tr.Indent(badge_column_widths[1], !rtl);
546 }
547
548 /* Draw the name of the industry in white is selected, otherwise, in orange */
549 DrawString(tr, indsp->name, selected ? TC_WHITE : TC_ORANGE);
550 GfxFillRect(icon, selected ? PC_WHITE : PC_BLACK);
552 DrawString(tr, GetString(STR_JUST_COMMA, Industry::GetIndustryTypeCount(type)), TC_BLACK, SA_RIGHT, false, FS_SMALL);
553
554 text = text.Translate(0, this->resize.step_height);
555 icon = icon.Translate(0, this->resize.step_height);
556 }
557 break;
558 }
559
560 case WID_DPI_INFOPANEL: {
561 Rect ir = r.Shrink(WidgetDimensions::scaled.framerect);
562
563 if (this->selected_type == IT_INVALID) {
564 DrawStringMultiLine(ir, STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_TOOLTIP);
565 break;
566 }
567
568 const IndustrySpec *indsp = GetIndustrySpec(this->selected_type);
569
570 if (_game_mode != GM_EDITOR) {
571 DrawString(ir, GetString(STR_FUND_INDUSTRY_INDUSTRY_BUILD_COST, indsp->GetConstructionCost()));
572 ir.top += GetCharacterHeight(FS_NORMAL);
573 }
574
575 CargoSuffix cargo_suffix[std::tuple_size_v<decltype(indsp->accepts_cargo)>];
576
577 /* Draw the accepted cargoes, if any. Otherwise, will print "Nothing". */
578 GetAllCargoSuffixes(CARGOSUFFIX_IN, CST_FUND, nullptr, this->selected_type, indsp, indsp->accepts_cargo, cargo_suffix);
579 std::string cargostring = this->MakeCargoListString(indsp->accepts_cargo, cargo_suffix, STR_INDUSTRY_VIEW_REQUIRES_N_CARGO);
580 ir.top = DrawStringMultiLine(ir, cargostring);
581
582 /* Draw the produced cargoes, if any. Otherwise, will print "Nothing". */
583 GetAllCargoSuffixes(CARGOSUFFIX_OUT, CST_FUND, nullptr, this->selected_type, indsp, indsp->produced_cargo, cargo_suffix);
584 cargostring = this->MakeCargoListString(indsp->produced_cargo, cargo_suffix, STR_INDUSTRY_VIEW_PRODUCES_N_CARGO);
585 ir.top = DrawStringMultiLine(ir, cargostring);
586
587 ir.top = DrawBadgeNameList(ir, indsp->badges, GSF_INDUSTRIES);
588
589 /* Get the additional purchase info text, if it has not already been queried. */
591 uint16_t callback_res = GetIndustryCallback(CBID_INDUSTRY_FUND_MORE_TEXT, 0, 0, nullptr, this->selected_type, INVALID_TILE);
592 if (callback_res != CALLBACK_FAILED && callback_res != 0x400) {
593 if (callback_res > 0x400) {
595 } else {
596 std::string str = GetGRFStringWithTextStack(indsp->grf_prop.grffile, GRFSTR_MISC_GRF_TEXT + callback_res, 6);
597 if (!str.empty()) {
598 DrawStringMultiLine(ir, str, TC_YELLOW);
599 }
600 }
601 }
602 }
603 break;
604 }
605 }
606 }
607
608 static void AskManyRandomIndustriesCallback(Window *, bool confirmed)
609 {
610 if (!confirmed) return;
611
612 if (Town::GetNumItems() == 0) {
613 ShowErrorMessage(GetEncodedString(STR_ERROR_CAN_T_GENERATE_INDUSTRIES), GetEncodedString(STR_ERROR_MUST_FOUND_TOWN_FIRST), WL_INFO);
614 } else {
615 Backup<bool> old_generating_world(_generating_world, true);
619 old_generating_world.Restore();
620 }
621 }
622
623 static void AskRemoveAllIndustriesCallback(Window *, bool confirmed)
624 {
625 if (!confirmed) return;
626
627 for (Industry *industry : Industry::Iterate()) delete industry;
628
629 /* Clear farmland. */
630 for (const auto tile : Map::Iterate()) {
631 if (IsTileType(tile, MP_CLEAR) && GetClearGround(tile) == CLEAR_FIELDS) {
632 MakeClear(tile, CLEAR_GRASS, 3);
633 }
634 }
635
637 }
638
639 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
640 {
641 switch (widget) {
643 assert(_game_mode == GM_EDITOR);
645 ShowQuery(
646 GetEncodedString(STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_CAPTION),
647 GetEncodedString(STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_QUERY),
648 nullptr, AskManyRandomIndustriesCallback);
649 break;
650 }
651
653 assert(_game_mode == GM_EDITOR);
655 ShowQuery(
656 GetEncodedString(STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES_CAPTION),
657 GetEncodedString(STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES_QUERY),
658 nullptr, AskRemoveAllIndustriesCallback);
659 break;
660 }
661
663 auto it = this->vscroll->GetScrolledItemFromWidget(this->list, pt.y, this, WID_DPI_MATRIX_WIDGET);
664 if (it != this->list.end()) { // Is it within the boundaries of available data?
665 this->selected_type = *it;
666 this->UpdateAvailability();
667
668 const IndustrySpec *indsp = GetIndustrySpec(this->selected_type);
669
670 this->SetDirty();
671
672 if (_thd.GetCallbackWnd() == this &&
673 ((_game_mode != GM_EDITOR && _settings_game.construction.raw_industry_construction == 2 && indsp != nullptr && indsp->IsRawIndustry()) || !this->enabled)) {
674 /* Reset the button state if going to prospecting or "build many industries" */
675 this->RaiseButtons();
677 }
678
679 this->SetButtons();
680 if (this->enabled && click_count > 1) this->OnClick(pt, WID_DPI_FUND_WIDGET, 1);
681 }
682 break;
683 }
684
686 if (this->selected_type != IT_INVALID) ShowIndustryCargoesWindow(this->selected_type);
687 break;
688
689 case WID_DPI_FUND_WIDGET: {
690 if (this->selected_type != IT_INVALID) {
691 if (_game_mode != GM_EDITOR && _settings_game.construction.raw_industry_construction == 2 && GetIndustrySpec(this->selected_type)->IsRawIndustry()) {
692 Command<CMD_BUILD_INDUSTRY>::Post(STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY, TileIndex{}, this->selected_type, 0, false, InteractiveRandom());
694 } else {
695 HandlePlacePushButton(this, WID_DPI_FUND_WIDGET, SPR_CURSOR_INDUSTRY, HT_RECT);
696 }
697 }
698 break;
699 }
700 }
701 }
702
703 void OnResize() override
704 {
705 /* Adjust the number of items in the matrix depending of the resize */
706 this->vscroll->SetCapacityFromWidget(this, WID_DPI_MATRIX_WIDGET);
707 }
708
709 void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile) override
710 {
711 bool success = true;
712 /* We do not need to protect ourselves against "Random Many Industries" in this mode */
713 const IndustrySpec *indsp = GetIndustrySpec(this->selected_type);
714 uint32_t seed = InteractiveRandom();
715 uint32_t layout_index = InteractiveRandomRange((uint32_t)indsp->layouts.size());
716
717 if (_game_mode == GM_EDITOR) {
718 /* Show error if no town exists at all */
719 if (Town::GetNumItems() == 0) {
720 ShowErrorMessage(GetEncodedString(STR_ERROR_CAN_T_BUILD_HERE, indsp->name),
721 GetEncodedString(STR_ERROR_MUST_FOUND_TOWN_FIRST), WL_INFO, pt.x, pt.y);
722 return;
723 }
724
725 AutoRestoreBackup backup_cur_company(_current_company, OWNER_NONE);
726 AutoRestoreBackup backup_generating_world(_generating_world, true);
727 AutoRestoreBackup backup_ignore_industry_restritions(_ignore_industry_restrictions, true);
728
729 Command<CMD_BUILD_INDUSTRY>::Post(STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY, &CcBuildIndustry, tile, this->selected_type, layout_index, false, seed);
730 } else {
731 success = Command<CMD_BUILD_INDUSTRY>::Post(STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY, tile, this->selected_type, layout_index, false, seed);
732 }
733
734 /* If an industry has been built, just reset the cursor and the system */
736 }
737
738 IntervalTimer<TimerWindow> update_interval = {std::chrono::seconds(3), [this](auto) {
739 if (_game_mode == GM_EDITOR) return;
740 if (this->selected_type == IT_INVALID) return;
741
742 bool enabled = this->enabled;
743 this->UpdateAvailability();
744 if (enabled != this->enabled) {
745 this->SetButtons();
746 this->SetDirty();
747 }
748 }};
749
750 void OnTimeout() override
751 {
752 this->RaiseButtons();
753 }
754
755 void OnPlaceObjectAbort() override
756 {
757 this->RaiseButtons();
758 }
759
765 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
766 {
767 if (!gui_scope) return;
768 this->SetupArrays();
769 this->SetButtons();
770 this->SetDirty();
771 }
772};
773
774void ShowBuildIndustryWindow()
775{
776 if (_game_mode != GM_EDITOR && !Company::IsValidID(_local_company)) return;
779}
780
781static void UpdateIndustryProduction(Industry *i);
782
783static inline bool IsProductionAlterable(const Industry *i)
784{
785 const IndustrySpec *is = GetIndustrySpec(i->type);
786 bool has_prod = std::any_of(std::begin(is->production_rate), std::end(is->production_rate), [](auto rate) { return rate != 0; });
787 return ((_game_mode == GM_EDITOR || _cheats.setup_prod.value) &&
788 (has_prod || is->IsRawIndustry()) &&
789 !_networking);
790}
791
793{
800
808
813 uint8_t clicked_button = 0;
815 int info_height = 0;
817
818public:
820 {
822 this->info_height = WidgetDimensions::scaled.framerect.Vertical() + 2 * GetCharacterHeight(FS_NORMAL); // Info panel has at least two lines text.
823
824 this->InitNested(window_number);
825 NWidgetViewport *nvp = this->GetWidget<NWidgetViewport>(WID_IV_VIEWPORT);
826 nvp->InitializeViewport(this, Industry::Get(window_number)->location.GetCenterTile(), ScaleZoomGUI(ZOOM_LVL_INDUSTRY));
827
830
831 this->InvalidateData();
832 }
833
835 {
837 }
838
839 void OnInit() override
840 {
841 /* This only used when the cheat to alter industry production is enabled */
842 this->cheat_line_height = std::max(SETTING_BUTTON_HEIGHT + WidgetDimensions::scaled.vsep_normal, GetCharacterHeight(FS_NORMAL));
844 }
845
846 void OnPaint() override
847 {
848 this->DrawWidgets();
849
850 if (this->IsShaded()) return; // Don't draw anything when the window is shaded.
851
852 const Rect r = this->GetWidget<NWidgetBase>(WID_IV_INFO)->GetCurrentRect();
853 int expected = this->DrawInfo(r);
854 if (expected != r.bottom) {
855 this->info_height = expected - r.top + 1;
856 this->ReInit();
857 return;
858 }
859 }
860
861 void DrawCargoIcon(const Rect &r, CargoType cargo_type) const
862 {
863 bool rtl = _current_text_dir == TD_RTL;
864 SpriteID icon = CargoSpec::Get(cargo_type)->GetCargoIcon();
865 Dimension d = GetSpriteSize(icon);
867 DrawSprite(icon, PAL_NONE, CenterBounds(ir.left, ir.right, d.width), CenterBounds(ir.top, ir.bottom, this->cargo_icon_size.height));
868 }
869
870 std::string GetAcceptedCargoString(const Industry::AcceptedCargo &ac, const CargoSuffix &suffix) const
871 {
872 auto params = MakeParameters(CargoSpec::Get(ac.cargo)->name, ac.cargo, ac.waiting, suffix.text);
873 switch (suffix.display) {
874 case CSD_CARGO_AMOUNT_TEXT: return GetStringWithArgs(STR_INDUSTRY_VIEW_ACCEPT_CARGO_AMOUNT_SUFFIX, params);
875 case CSD_CARGO_TEXT: return GetStringWithArgs(STR_INDUSTRY_VIEW_ACCEPT_CARGO_SUFFIX, params);
876 case CSD_CARGO_AMOUNT: return GetStringWithArgs(STR_INDUSTRY_VIEW_ACCEPT_CARGO_AMOUNT_NOSUFFIX, params);
877 case CSD_CARGO: return GetStringWithArgs(STR_INDUSTRY_VIEW_ACCEPT_CARGO_NOSUFFIX, params);
878 default: NOT_REACHED();
879 }
880 }
881
887 int DrawInfo(const Rect &r)
888 {
889 bool rtl = _current_text_dir == TD_RTL;
891 const IndustrySpec *ind = GetIndustrySpec(i->type);
892 Rect ir = r.Shrink(WidgetDimensions::scaled.framerect);
893 bool first = true;
894 bool has_accept = false;
895
896 /* Use all the available space past the rect, so that we can enlarge the window if needed. */
897 ir.bottom = INT_MAX;
898
899 if (i->prod_level == PRODLEVEL_CLOSURE) {
900 DrawString(ir, STR_INDUSTRY_VIEW_INDUSTRY_ANNOUNCED_CLOSURE);
902 }
903
904 const int label_indent = WidgetDimensions::scaled.hsep_normal + this->cargo_icon_size.width;
906
907 for (const auto &a : i->accepted) {
908 if (!IsValidCargoType(a.cargo)) continue;
909 has_accept = true;
910 if (first) {
911 DrawString(ir, STR_INDUSTRY_VIEW_REQUIRES);
912 ir.top += GetCharacterHeight(FS_NORMAL);
913 first = false;
914 }
915
916 DrawCargoIcon(ir, a.cargo);
917
918 CargoSuffix suffix;
919 GetCargoSuffix(CARGOSUFFIX_IN, CST_VIEW, i, i->type, ind, a.cargo, &a - i->accepted.data(), suffix);
920 /* if the industry is not stockpiling then don't show amount in the acceptance display. */
921 if (!stockpiling && suffix.display == CSD_CARGO_AMOUNT_TEXT) suffix.display = CSD_CARGO_TEXT;
922 if (!stockpiling && suffix.display == CSD_CARGO_AMOUNT) suffix.display = CSD_CARGO;
923
924 DrawString(ir.Indent(label_indent, rtl), this->GetAcceptedCargoString(a, suffix));
925 ir.top += GetCharacterHeight(FS_NORMAL);
926 }
927
928 int line_height = this->editable == EA_RATE ? this->cheat_line_height : GetCharacterHeight(FS_NORMAL);
929 int text_y_offset = (line_height - GetCharacterHeight(FS_NORMAL)) / 2;
930 int button_y_offset = (line_height - SETTING_BUTTON_HEIGHT) / 2;
931 first = true;
932 for (const auto &p : i->produced) {
933 if (!IsValidCargoType(p.cargo)) continue;
934 if (first) {
935 if (has_accept) ir.top += WidgetDimensions::scaled.vsep_wide;
936 DrawString(ir, TimerGameEconomy::UsingWallclockUnits() ? STR_INDUSTRY_VIEW_PRODUCTION_LAST_MINUTE_TITLE : STR_INDUSTRY_VIEW_PRODUCTION_LAST_MONTH_TITLE);
937 ir.top += GetCharacterHeight(FS_NORMAL);
938 if (this->editable == EA_RATE) this->production_offset_y = ir.top;
939 first = false;
940 }
941
942 DrawCargoIcon(ir, p.cargo);
943
944 CargoSuffix suffix;
945 GetCargoSuffix(CARGOSUFFIX_OUT, CST_VIEW, i, i->type, ind, p.cargo, &p - i->produced.data(), suffix);
946
947 DrawString(ir.Indent(label_indent + (this->editable == EA_RATE ? SETTING_BUTTON_WIDTH + WidgetDimensions::scaled.hsep_normal : 0), rtl).Translate(0, text_y_offset),
948 GetString(STR_INDUSTRY_VIEW_TRANSPORTED, p.cargo, p.history[LAST_MONTH].production, suffix.text, ToPercent8(p.history[LAST_MONTH].PctTransported())));
949 /* Let's put out those buttons.. */
950 if (this->editable == EA_RATE) {
951 DrawArrowButtons(ir.Indent(label_indent, rtl).WithWidth(SETTING_BUTTON_WIDTH, rtl).left, ir.top + button_y_offset, COLOUR_YELLOW, (this->clicked_line == IL_RATE1 + (&p - i->produced.data())) ? this->clicked_button : 0,
952 p.rate > 0, p.rate < 255);
953 }
954 ir.top += line_height;
955 }
956
957 /* Display production multiplier if editable */
958 if (this->editable == EA_MULTIPLIER) {
959 line_height = this->cheat_line_height;
960 text_y_offset = (line_height - GetCharacterHeight(FS_NORMAL)) / 2;
961 button_y_offset = (line_height - SETTING_BUTTON_HEIGHT) / 2;
963 this->production_offset_y = ir.top;
964 DrawString(ir.Indent(label_indent + SETTING_BUTTON_WIDTH + WidgetDimensions::scaled.hsep_normal, rtl).Translate(0, text_y_offset),
965 GetString(STR_INDUSTRY_VIEW_PRODUCTION_LEVEL, RoundDivSU(i->prod_level * 100, PRODLEVEL_DEFAULT)));
966 DrawArrowButtons(ir.Indent(label_indent, rtl).WithWidth(SETTING_BUTTON_WIDTH, rtl).left, ir.top + button_y_offset, COLOUR_YELLOW, (this->clicked_line == IL_MULTIPLIER) ? this->clicked_button : 0,
968 ir.top += line_height;
969 }
970
971 /* Get the extra message for the GUI */
973 uint16_t callback_res = GetIndustryCallback(CBID_INDUSTRY_WINDOW_MORE_TEXT, 0, 0, i, i->type, i->location.tile);
974 if (callback_res != CALLBACK_FAILED && callback_res != 0x400) {
975 if (callback_res > 0x400) {
977 } else {
978 std::string str = GetGRFStringWithTextStack(ind->grf_prop.grffile, GRFSTR_MISC_GRF_TEXT + callback_res, 6);
979 if (!str.empty()) {
981 ir.top = DrawStringMultiLine(ir, str, TC_YELLOW);
982 }
983 }
984 }
985 }
986
987 if (!i->text.empty()) {
989 ir.top = DrawStringMultiLine(ir, i->text.GetDecodedString(), TC_BLACK);
990 }
991
992 /* Return required bottom position, the last pixel row plus some padding. */
993 return ir.top - 1 + WidgetDimensions::scaled.framerect.bottom;
994 }
995
996 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
997 {
998 if (widget == WID_IV_CAPTION) return GetString(STR_INDUSTRY_VIEW_CAPTION, this->window_number);
999
1000 return this->Window::GetWidgetString(widget, stringid);
1001 }
1002
1003 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
1004 {
1005 if (widget == WID_IV_INFO) size.height = this->info_height;
1006 }
1007
1008 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
1009 {
1010 switch (widget) {
1011 case WID_IV_INFO: {
1013 InfoLine line = IL_NONE;
1014
1015 switch (this->editable) {
1016 case EA_NONE: break;
1017
1018 case EA_MULTIPLIER:
1019 if (IsInsideBS(pt.y, this->production_offset_y, this->cheat_line_height)) line = IL_MULTIPLIER;
1020 break;
1021
1022 case EA_RATE:
1023 if (pt.y >= this->production_offset_y) {
1024 int row = (pt.y - this->production_offset_y) / this->cheat_line_height;
1025 for (auto itp = std::begin(i->produced); itp != std::end(i->produced); ++itp) {
1026 if (!IsValidCargoType(itp->cargo)) continue;
1027 row--;
1028 if (row < 0) {
1029 line = (InfoLine)(IL_RATE1 + (itp - std::begin(i->produced)));
1030 break;
1031 }
1032 }
1033 }
1034 break;
1035 }
1036 if (line == IL_NONE) return;
1037
1038 bool rtl = _current_text_dir == TD_RTL;
1039 Rect r = this->GetWidget<NWidgetBase>(widget)->GetCurrentRect().Shrink(WidgetDimensions::scaled.framerect).Indent(this->cargo_icon_size.width + WidgetDimensions::scaled.hsep_normal, rtl);
1040
1041 if (r.WithWidth(SETTING_BUTTON_WIDTH, rtl).Contains(pt)) {
1042 /* Clicked buttons, decrease or increase production */
1043 bool decrease = r.WithWidth(SETTING_BUTTON_WIDTH / 2, rtl).Contains(pt);
1044 switch (this->editable) {
1045 case EA_MULTIPLIER:
1046 if (decrease) {
1047 if (i->prod_level <= PRODLEVEL_MINIMUM) return;
1048 i->prod_level = static_cast<uint8_t>(std::max<uint>(i->prod_level / 2, PRODLEVEL_MINIMUM));
1049 } else {
1050 if (i->prod_level >= PRODLEVEL_MAXIMUM) return;
1051 i->prod_level = static_cast<uint8_t>(std::min<uint>(i->prod_level * 2, PRODLEVEL_MAXIMUM));
1052 }
1053 break;
1054
1055 case EA_RATE:
1056 if (decrease) {
1057 if (i->produced[line - IL_RATE1].rate <= 0) return;
1058 i->produced[line - IL_RATE1].rate = std::max(i->produced[line - IL_RATE1].rate / 2, 0);
1059 } else {
1060 if (i->produced[line - IL_RATE1].rate >= 255) return;
1061 /* a zero production industry is unlikely to give anything but zero, so push it a little bit */
1062 int new_prod = i->produced[line - IL_RATE1].rate == 0 ? 1 : i->produced[line - IL_RATE1].rate * 2;
1063 i->produced[line - IL_RATE1].rate = ClampTo<uint8_t>(new_prod);
1064 }
1065 break;
1066
1067 default: NOT_REACHED();
1068 }
1069
1070 UpdateIndustryProduction(i);
1071 this->SetDirty();
1072 this->SetTimeout();
1073 this->clicked_line = line;
1074 this->clicked_button = (decrease ^ rtl) ? 1 : 2;
1076 /* clicked the text */
1077 this->editbox_line = line;
1078 switch (this->editable) {
1079 case EA_MULTIPLIER:
1080 ShowQueryString(GetString(STR_JUST_INT, RoundDivSU(i->prod_level * 100, PRODLEVEL_DEFAULT)), STR_CONFIG_GAME_PRODUCTION_LEVEL, 10, this, CS_ALPHANUMERAL, {});
1081 break;
1082
1083 case EA_RATE:
1084 ShowQueryString(GetString(STR_JUST_INT, i->produced[line - IL_RATE1].rate * 8), STR_CONFIG_GAME_PRODUCTION, 10, this, CS_ALPHANUMERAL, {});
1085 break;
1086
1087 default: NOT_REACHED();
1088 }
1089 }
1090 break;
1091 }
1092
1093 case WID_IV_GOTO: {
1095 if (_ctrl_pressed) {
1097 } else {
1099 }
1100 break;
1101 }
1102
1103 case WID_IV_DISPLAY: {
1106 break;
1107 }
1108
1109 case WID_IV_GRAPH:
1110 ShowIndustryProductionGraph(this->window_number);
1111 break;
1112 }
1113 }
1114
1115 void OnTimeout() override
1116 {
1117 this->clicked_line = IL_NONE;
1118 this->clicked_button = 0;
1119 this->SetDirty();
1120 }
1121
1122 void OnResize() override
1123 {
1124 if (this->viewport != nullptr) {
1125 NWidgetViewport *nvp = this->GetWidget<NWidgetViewport>(WID_IV_VIEWPORT);
1126 nvp->UpdateViewportCoordinates(this);
1127
1128 ScrollWindowToTile(Industry::Get(this->window_number)->location.GetCenterTile(), this, true); // Re-center viewport.
1129 }
1130 }
1131
1132 void OnMouseWheel(int wheel) override
1133 {
1135 DoZoomInOutWindow(wheel < 0 ? ZOOM_IN : ZOOM_OUT, this);
1136 }
1137 }
1138
1139 void OnQueryTextFinished(std::optional<std::string> str) override
1140 {
1141 if (!str.has_value() || str->empty()) return;
1142
1144 uint value = atoi(str->c_str());
1145 switch (this->editbox_line) {
1146 case IL_NONE: NOT_REACHED();
1147
1148 case IL_MULTIPLIER:
1150 break;
1151
1152 default:
1153 i->produced[this->editbox_line - IL_RATE1].rate = ClampU(RoundDivSU(value, 8), 0, 255);
1154 break;
1155 }
1156 UpdateIndustryProduction(i);
1157 this->SetDirty();
1158 }
1159
1165 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
1166 {
1167 if (!gui_scope) return;
1168 const Industry *i = Industry::Get(this->window_number);
1169 if (IsProductionAlterable(i)) {
1170 const IndustrySpec *ind = GetIndustrySpec(i->type);
1172 } else {
1173 this->editable = EA_NONE;
1174 }
1175 }
1176
1177 bool IsNewGRFInspectable() const override
1178 {
1179 return ::IsNewGRFInspectable(GSF_INDUSTRIES, this->window_number);
1180 }
1181
1182 void ShowNewGRFInspectWindow() const override
1183 {
1184 ::ShowNewGRFInspectWindow(GSF_INDUSTRIES, this->window_number);
1185 }
1186};
1187
1188static void UpdateIndustryProduction(Industry *i)
1189{
1190 const IndustrySpec *indspec = GetIndustrySpec(i->type);
1192
1193 for (auto &p : i->produced) {
1194 if (IsValidCargoType(p.cargo)) {
1195 p.history[LAST_MONTH].production = ScaleByCargoScale(8 * p.rate, false);
1196 }
1197 }
1198}
1199
1203 NWidget(WWT_CLOSEBOX, COLOUR_CREAM),
1204 NWidget(WWT_CAPTION, COLOUR_CREAM, WID_IV_CAPTION),
1205 NWidget(WWT_PUSHIMGBTN, COLOUR_CREAM, WID_IV_GOTO), SetAspect(WidgetDimensions::ASPECT_LOCATION), SetSpriteTip(SPR_GOTO_LOCATION, STR_INDUSTRY_VIEW_LOCATION_TOOLTIP),
1206 NWidget(WWT_DEBUGBOX, COLOUR_CREAM),
1207 NWidget(WWT_SHADEBOX, COLOUR_CREAM),
1208 NWidget(WWT_DEFSIZEBOX, COLOUR_CREAM),
1209 NWidget(WWT_STICKYBOX, COLOUR_CREAM),
1210 EndContainer(),
1211 NWidget(WWT_PANEL, COLOUR_CREAM),
1212 NWidget(WWT_INSET, COLOUR_CREAM), SetPadding(2, 2, 2, 2),
1213 NWidget(NWID_VIEWPORT, INVALID_COLOUR, WID_IV_VIEWPORT), SetMinimalSize(254, 86), SetFill(1, 0), SetResize(1, 1),
1214 EndContainer(),
1215 EndContainer(),
1216 NWidget(WWT_PANEL, COLOUR_CREAM, WID_IV_INFO), SetMinimalSize(260, 0), SetMinimalTextLines(2, WidgetDimensions::unscaled.framerect.Vertical()), SetResize(1, 0),
1217 EndContainer(),
1219 NWidget(WWT_PUSHTXTBTN, COLOUR_CREAM, WID_IV_DISPLAY), SetFill(1, 0), SetResize(1, 0), SetStringTip(STR_INDUSTRY_DISPLAY_CHAIN, STR_INDUSTRY_DISPLAY_CHAIN_TOOLTIP),
1220 NWidget(WWT_PUSHTXTBTN, COLOUR_CREAM, WID_IV_GRAPH), SetFill(1, 0), SetResize(1, 0), SetStringTip(STR_INDUSTRY_VIEW_PRODUCTION_GRAPH, STR_INDUSTRY_VIEW_PRODUCTION_GRAPH_TOOLTIP),
1221 NWidget(WWT_RESIZEBOX, COLOUR_CREAM),
1222 EndContainer(),
1223};
1224
1227 WDP_AUTO, "view_industry", 260, 120,
1229 {},
1231);
1232
1233void ShowIndustryViewWindow(IndustryID industry)
1234{
1235 AllocateWindowDescFront<IndustryViewWindow>(_industry_view_desc, industry);
1236}
1237
1241 NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
1242 NWidget(WWT_CAPTION, COLOUR_BROWN, WID_ID_CAPTION),
1243 NWidget(WWT_SHADEBOX, COLOUR_BROWN),
1244 NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
1245 NWidget(WWT_STICKYBOX, COLOUR_BROWN),
1246 EndContainer(),
1250 NWidget(WWT_TEXTBTN, COLOUR_BROWN, WID_ID_DROPDOWN_ORDER), SetStringTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER),
1251 NWidget(WWT_DROPDOWN, COLOUR_BROWN, WID_ID_DROPDOWN_CRITERIA), SetToolTip(STR_TOOLTIP_SORT_CRITERIA),
1252 NWidget(WWT_EDITBOX, COLOUR_BROWN, WID_ID_FILTER), SetFill(1, 0), SetResize(1, 0), SetStringTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP),
1253 EndContainer(),
1255 NWidget(WWT_DROPDOWN, COLOUR_BROWN, WID_ID_FILTER_BY_ACC_CARGO), SetMinimalSize(225, 12), SetFill(0, 1), SetToolTip(STR_TOOLTIP_FILTER_CRITERIA),
1256 NWidget(WWT_DROPDOWN, COLOUR_BROWN, WID_ID_FILTER_BY_PROD_CARGO), SetMinimalSize(225, 12), SetFill(0, 1), SetToolTip(STR_TOOLTIP_FILTER_CRITERIA),
1257 NWidget(WWT_PANEL, COLOUR_BROWN), SetResize(1, 0), EndContainer(),
1258 EndContainer(),
1259 NWidget(WWT_PANEL, COLOUR_BROWN, WID_ID_INDUSTRY_LIST), SetToolTip(STR_INDUSTRY_DIRECTORY_LIST_TOOLTIP), SetResize(1, 1), SetScrollbar(WID_ID_VSCROLLBAR),
1260 EndContainer(),
1261 EndContainer(),
1263 EndContainer(),
1266 NWidget(WWT_RESIZEBOX, COLOUR_BROWN),
1267 EndContainer(),
1268};
1269
1271
1279static bool CargoFilter(const Industry * const *industry, const std::pair<CargoType, CargoType> &cargoes)
1280{
1281 auto accepted_cargo = cargoes.first;
1282 auto produced_cargo = cargoes.second;
1283
1284 bool accepted_cargo_matches;
1285
1286 switch (accepted_cargo) {
1288 accepted_cargo_matches = true;
1289 break;
1290
1292 accepted_cargo_matches = !(*industry)->IsCargoAccepted();
1293 break;
1294
1295 default:
1296 accepted_cargo_matches = (*industry)->IsCargoAccepted(accepted_cargo);
1297 break;
1298 }
1299
1300 bool produced_cargo_matches;
1301
1302 switch (produced_cargo) {
1304 produced_cargo_matches = true;
1305 break;
1306
1308 produced_cargo_matches = !(*industry)->IsCargoProduced();
1309 break;
1310
1311 default:
1312 produced_cargo_matches = (*industry)->IsCargoProduced(produced_cargo);
1313 break;
1314 }
1315
1316 return accepted_cargo_matches && produced_cargo_matches;
1317}
1318
1319static GUIIndustryList::FilterFunction * const _industry_filter_funcs[] = { &CargoFilter };
1320
1329protected:
1330 /* Runtime saved values */
1331 static Listing last_sorting;
1332
1333 /* Constants for sorting industries */
1334 static inline const StringID sorter_names[] = {
1335 STR_SORT_BY_NAME,
1336 STR_SORT_BY_TYPE,
1337 STR_SORT_BY_PRODUCTION,
1338 STR_SORT_BY_TRANSPORTED,
1339 };
1340 static const std::initializer_list<GUIIndustryList::SortFunction * const> sorter_funcs;
1341
1342 GUIIndustryList industries{IndustryDirectoryWindow::produced_cargo_filter};
1343 Scrollbar *vscroll{};
1344 Scrollbar *hscroll{};
1345
1348 static CargoType produced_cargo_filter;
1349
1350 const int MAX_FILTER_LENGTH = 16;
1353
1354 enum class SorterType : uint8_t {
1355 ByName,
1356 ByType,
1357 ByProduction,
1359 };
1360
1366 {
1367 if (this->produced_cargo_filter_criteria != cargo_type) {
1368 this->produced_cargo_filter_criteria = cargo_type;
1369 /* deactivate filter if criteria is 'Show All', activate it otherwise */
1371
1372 this->industries.SetFilterState(is_filtering_necessary);
1373 this->industries.SetFilterType(0);
1374 this->industries.ForceRebuild();
1375 }
1376 }
1377
1383 {
1384 if (this->accepted_cargo_filter_criteria != cargo_type) {
1385 this->accepted_cargo_filter_criteria = cargo_type;
1386 /* deactivate filter if criteria is 'Show All', activate it otherwise */
1388
1389 this->industries.SetFilterState(is_filtering_necessary);
1390 this->industries.SetFilterType(0);
1391 this->industries.ForceRebuild();
1392 }
1393 }
1394
1395 StringID GetCargoFilterLabel(CargoType cargo_type) const
1396 {
1397 switch (cargo_type) {
1398 case CargoFilterCriteria::CF_ANY: return STR_INDUSTRY_DIRECTORY_FILTER_ALL_TYPES;
1399 case CargoFilterCriteria::CF_NONE: return STR_INDUSTRY_DIRECTORY_FILTER_NONE;
1400 default: return CargoSpec::Get(cargo_type)->name;
1401 }
1402 }
1403
1408 {
1411
1412 this->industries.SetFilterFuncs(_industry_filter_funcs);
1413
1415
1416 this->industries.SetFilterState(is_filtering_necessary);
1417 }
1418
1424 {
1425 uint width = this->hscroll->GetCount();
1426 auto [first, last] = this->vscroll->GetVisibleRangeIterators(this->industries);
1427 for (auto it = first; it != last; ++it) {
1428 width = std::max(width, GetStringBoundingBox(this->GetIndustryString(*it)).width);
1429 }
1430 return width;
1431 }
1432
1435 {
1436 if (this->industries.NeedRebuild()) {
1437 this->industries.clear();
1438 this->industries.reserve(Industry::GetNumItems());
1439
1440 for (const Industry *i : Industry::Iterate()) {
1441 if (this->string_filter.IsEmpty()) {
1442 this->industries.push_back(i);
1443 continue;
1444 }
1445 this->string_filter.ResetState();
1446 this->string_filter.AddLine(i->GetCachedName());
1447 if (this->string_filter.GetState()) this->industries.push_back(i);
1448 }
1449
1450 this->industries.RebuildDone();
1451
1452 auto filter = std::make_pair(this->accepted_cargo_filter_criteria, this->produced_cargo_filter_criteria);
1453
1454 this->industries.Filter(filter);
1455
1456 this->vscroll->SetCount(this->industries.size()); // Update scrollbar as well.
1457 }
1458
1459 IndustryDirectoryWindow::produced_cargo_filter = this->produced_cargo_filter_criteria;
1460 this->industries.Sort();
1461
1462 this->SetDirty();
1463 }
1464
1472 {
1473 if (!IsValidCargoType(p.cargo)) return -1;
1474 return ToPercent8(p.history[LAST_MONTH].PctTransported());
1475 }
1476
1485 {
1486 CargoType filter = IndustryDirectoryWindow::produced_cargo_filter;
1487 if (filter == CargoFilterCriteria::CF_NONE) return 0;
1488
1489 int percentage = 0, produced_cargo_count = 0;
1490 for (const auto &p : i->produced) {
1491 if (filter == CargoFilterCriteria::CF_ANY) {
1492 int transported = GetCargoTransportedPercentsIfValid(p);
1493 if (transported != -1) {
1494 produced_cargo_count++;
1495 percentage += transported;
1496 }
1497 if (produced_cargo_count == 0 && &p == &i->produced.back() && percentage == 0) {
1498 return transported;
1499 }
1500 } else if (filter == p.cargo) {
1502 }
1503 }
1504
1505 if (produced_cargo_count == 0) return percentage;
1506 return percentage / produced_cargo_count;
1507 }
1508
1510 static bool IndustryNameSorter(const Industry * const &a, const Industry * const &b, const CargoType &)
1511 {
1512 int r = StrNaturalCompare(a->GetCachedName(), b->GetCachedName()); // Sort by name (natural sorting).
1513 if (r == 0) return a->index < b->index;
1514 return r < 0;
1515 }
1516
1518 static bool IndustryTypeSorter(const Industry * const &a, const Industry * const &b, const CargoType &filter)
1519 {
1520 int it_a = 0;
1521 while (it_a != NUM_INDUSTRYTYPES && a->type != _sorted_industry_types[it_a]) it_a++;
1522 int it_b = 0;
1523 while (it_b != NUM_INDUSTRYTYPES && b->type != _sorted_industry_types[it_b]) it_b++;
1524 int r = it_a - it_b;
1525 return (r == 0) ? IndustryNameSorter(a, b, filter) : r < 0;
1526 }
1527
1529 static bool IndustryProductionSorter(const Industry * const &a, const Industry * const &b, const CargoType &filter)
1530 {
1531 if (filter == CargoFilterCriteria::CF_NONE) return IndustryTypeSorter(a, b, filter);
1532
1533 uint prod_a = 0, prod_b = 0;
1534 if (filter == CargoFilterCriteria::CF_ANY) {
1535 for (const auto &pa : a->produced) {
1536 if (IsValidCargoType(pa.cargo)) prod_a += pa.history[LAST_MONTH].production;
1537 }
1538 for (const auto &pb : b->produced) {
1539 if (IsValidCargoType(pb.cargo)) prod_b += pb.history[LAST_MONTH].production;
1540 }
1541 } else {
1542 if (auto ita = a->GetCargoProduced(filter); ita != std::end(a->produced)) prod_a = ita->history[LAST_MONTH].production;
1543 if (auto itb = b->GetCargoProduced(filter); itb != std::end(b->produced)) prod_b = itb->history[LAST_MONTH].production;
1544 }
1545 int r = prod_a - prod_b;
1546
1547 return (r == 0) ? IndustryTypeSorter(a, b, filter) : r < 0;
1548 }
1549
1551 static bool IndustryTransportedCargoSorter(const Industry * const &a, const Industry * const &b, const CargoType &filter)
1552 {
1554 return (r == 0) ? IndustryNameSorter(a, b, filter) : r < 0;
1555 }
1556
1557 StringID GetStringForNumCargo(size_t count) const
1558 {
1559 switch (count) {
1560 case 0: return STR_INDUSTRY_DIRECTORY_ITEM_NOPROD;
1561 case 1: return STR_INDUSTRY_DIRECTORY_ITEM_PROD1;
1562 case 2: return STR_INDUSTRY_DIRECTORY_ITEM_PROD2;
1563 case 3: return STR_INDUSTRY_DIRECTORY_ITEM_PROD3;
1564 default: return STR_INDUSTRY_DIRECTORY_ITEM_PRODMORE;
1565 }
1566 }
1567
1573 std::string GetIndustryString(const Industry *i) const
1574 {
1575 const IndustrySpec *indsp = GetIndustrySpec(i->type);
1576
1577 /* Get industry productions (CargoType, production, suffix, transported) */
1578 struct CargoInfo {
1579 CargoType cargo_type;
1580 uint16_t production;
1581 uint transported;
1582 std::string suffix;
1583
1584 CargoInfo(CargoType cargo_type, uint16_t production, uint transported, std::string &&suffix) : cargo_type(cargo_type), production(production), transported(transported), suffix(std::move(suffix)) {}
1585 };
1586 std::vector<CargoInfo> cargos;
1587
1588 for (auto itp = std::begin(i->produced); itp != std::end(i->produced); ++itp) {
1589 if (!IsValidCargoType(itp->cargo)) continue;
1590 CargoSuffix cargo_suffix;
1591 GetCargoSuffix(CARGOSUFFIX_OUT, CST_DIR, i, i->type, indsp, itp->cargo, itp - std::begin(i->produced), cargo_suffix);
1592 cargos.emplace_back(itp->cargo, itp->history[LAST_MONTH].production, ToPercent8(itp->history[LAST_MONTH].PctTransported()), std::move(cargo_suffix.text));
1593 }
1594
1595 switch (static_cast<IndustryDirectoryWindow::SorterType>(this->industries.SortType())) {
1599 /* Sort by descending production, then descending transported */
1600 std::sort(cargos.begin(), cargos.end(), [](const CargoInfo &a, const CargoInfo &b) {
1601 if (a.production != b.production) return a.production > b.production;
1602 return a.transported > b.transported;
1603 });
1604 break;
1605
1607 /* Sort by descending transported, then descending production */
1608 std::sort(cargos.begin(), cargos.end(), [](const CargoInfo &a, const CargoInfo &b) {
1609 if (a.transported != b.transported) return a.transported > b.transported;
1610 return a.production > b.production;
1611 });
1612 break;
1613 }
1614
1615 /* If the produced cargo filter is active then move the filtered cargo to the beginning of the list,
1616 * because this is the one the player interested in, and that way it is not hidden in the 'n' more cargos */
1617 const CargoType cargo_type = this->produced_cargo_filter_criteria;
1618 if (cargo_type != CargoFilterCriteria::CF_ANY && cargo_type != CargoFilterCriteria::CF_NONE) {
1619 auto filtered_ci = std::ranges::find(cargos, cargo_type, &CargoInfo::cargo_type);
1620 if (filtered_ci != cargos.end()) {
1621 std::rotate(cargos.begin(), filtered_ci, filtered_ci + 1);
1622 }
1623 }
1624
1625 static constexpr size_t MAX_DISPLAYED_CARGOES = 3;
1626 std::array<StringParameter, 2 + 5 * MAX_DISPLAYED_CARGOES> params{};
1627 auto it = params.begin();
1628
1629 /* Industry name */
1630 *it++ = i->index;
1631
1632 /* Display first MAX_DISPLAYED_CARGOES cargoes */
1633 for (CargoInfo &ci : cargos | std::views::take(MAX_DISPLAYED_CARGOES)) {
1634 *it++ = STR_INDUSTRY_DIRECTORY_ITEM_INFO;
1635 *it++ = ci.cargo_type;
1636 *it++ = ci.production;
1637 *it++ = std::move(ci.suffix);
1638 *it++ = ci.transported;
1639 }
1640
1641 /* Undisplayed cargos if any */
1642 if (std::size(cargos) > MAX_DISPLAYED_CARGOES) *it++ = std::size(cargos) - MAX_DISPLAYED_CARGOES;
1643
1644 return GetStringWithArgs(GetStringForNumCargo(std::size(cargos)), {params.begin(), it});
1645 }
1646
1647public:
1649 {
1650 this->CreateNestedTree();
1651 this->vscroll = this->GetScrollbar(WID_ID_VSCROLLBAR);
1652 this->hscroll = this->GetScrollbar(WID_ID_HSCROLLBAR);
1653
1654 this->industries.SetListing(this->last_sorting);
1655 this->industries.SetSortFuncs(IndustryDirectoryWindow::sorter_funcs);
1656 this->industries.ForceRebuild();
1657
1658 this->FinishInitNested(0);
1659
1661
1663 this->industry_editbox.cancel_button = QueryString::ACTION_CLEAR;
1664 }
1665
1667 {
1668 this->last_sorting = this->industries.GetListing();
1669 }
1670
1671 void OnInit() override
1672 {
1673 this->SetCargoFilterArray();
1674 this->hscroll->SetCount(0);
1675 }
1676
1677 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
1678 {
1679 switch (widget) {
1680 case WID_ID_CAPTION:
1681 return GetString(STR_INDUSTRY_DIRECTORY_CAPTION, this->vscroll->GetCount(), Industry::GetNumItems());
1682
1684 return GetString(IndustryDirectoryWindow::sorter_names[this->industries.SortType()]);
1685
1687 return GetString(STR_INDUSTRY_DIRECTORY_ACCEPTED_CARGO_FILTER, this->GetCargoFilterLabel(this->accepted_cargo_filter_criteria));
1688
1690 return GetString(STR_INDUSTRY_DIRECTORY_PRODUCED_CARGO_FILTER, this->GetCargoFilterLabel(this->produced_cargo_filter_criteria));
1691
1692 default:
1693 return this->Window::GetWidgetString(widget, stringid);
1694 }
1695 }
1696
1697 void DrawWidget(const Rect &r, WidgetID widget) const override
1698 {
1699 switch (widget) {
1701 this->DrawSortButtonState(widget, this->industries.IsDescSortOrder() ? SBS_DOWN : SBS_UP);
1702 break;
1703
1704 case WID_ID_INDUSTRY_LIST: {
1705 Rect ir = r.Shrink(WidgetDimensions::scaled.framerect);
1706
1707 /* Setup a clipping rectangle... */
1708 DrawPixelInfo tmp_dpi;
1709 if (!FillDrawPixelInfo(&tmp_dpi, ir)) return;
1710 /* ...but keep coordinates relative to the window. */
1711 tmp_dpi.left += ir.left;
1712 tmp_dpi.top += ir.top;
1713
1714 AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi);
1715
1716 ir = ScrollRect(ir, *this->hscroll, 1);
1717
1718 if (this->industries.empty()) {
1719 DrawString(ir, STR_INDUSTRY_DIRECTORY_NONE);
1720 break;
1721 }
1722 const CargoType acf_cargo_type = this->accepted_cargo_filter_criteria;
1723 auto [first, last] = this->vscroll->GetVisibleRangeIterators(this->industries);
1724 for (auto it = first; it != last; ++it) {
1725 TextColour tc = TC_FROMSTRING;
1726 if (acf_cargo_type != CargoFilterCriteria::CF_ANY && acf_cargo_type != CargoFilterCriteria::CF_NONE) {
1727 Industry *ind = const_cast<Industry *>(*it);
1728 if (IndustryTemporarilyRefusesCargo(ind, acf_cargo_type)) {
1729 tc = TC_GREY | TC_FORCED;
1730 }
1731 }
1732 DrawString(ir, this->GetIndustryString(*it), tc);
1733
1734 ir.top += this->resize.step_height;
1735 }
1736 break;
1737 }
1738 }
1739 }
1740
1741 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
1742 {
1743 switch (widget) {
1744 case WID_ID_DROPDOWN_ORDER: {
1745 Dimension d = GetStringBoundingBox(this->GetWidget<NWidgetCore>(widget)->GetString());
1746 d.width += padding.width + Window::SortButtonWidth() * 2; // Doubled since the string is centred and it also looks better.
1747 d.height += padding.height;
1748 size = maxdim(size, d);
1749 break;
1750 }
1751
1753 Dimension d = GetStringListBoundingBox(IndustryDirectoryWindow::sorter_names);
1754 d.width += padding.width;
1755 d.height += padding.height;
1756 size = maxdim(size, d);
1757 break;
1758 }
1759
1760 case WID_ID_INDUSTRY_LIST: {
1761 Dimension d = GetStringBoundingBox(STR_INDUSTRY_DIRECTORY_NONE);
1762 resize.height = d.height;
1763 d.height *= 5;
1764 d.width += padding.width;
1765 d.height += padding.height;
1766 size = maxdim(size, d);
1767 break;
1768 }
1769 }
1770 }
1771
1772 DropDownList BuildCargoDropDownList() const
1773 {
1774 DropDownList list;
1775
1776 /* Add item for disabling filtering. */
1777 list.push_back(MakeDropDownListStringItem(this->GetCargoFilterLabel(CargoFilterCriteria::CF_ANY), CargoFilterCriteria::CF_ANY));
1778 /* Add item for industries not producing anything, e.g. power plants */
1779 list.push_back(MakeDropDownListStringItem(this->GetCargoFilterLabel(CargoFilterCriteria::CF_NONE), CargoFilterCriteria::CF_NONE));
1780
1781 /* Add cargos */
1783 for (const CargoSpec *cs : _sorted_standard_cargo_specs) {
1784 list.push_back(MakeDropDownListIconItem(d, cs->GetCargoIcon(), PAL_NONE, cs->name, cs->Index()));
1785 }
1786
1787 return list;
1788 }
1789
1790 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
1791 {
1792 switch (widget) {
1794 this->industries.ToggleSortOrder();
1795 this->SetDirty();
1796 break;
1797
1799 ShowDropDownMenu(this, IndustryDirectoryWindow::sorter_names, this->industries.SortType(), WID_ID_DROPDOWN_CRITERIA, 0, 0);
1800 break;
1801
1802 case WID_ID_FILTER_BY_ACC_CARGO: // Cargo filter dropdown
1803 ShowDropDownList(this, this->BuildCargoDropDownList(), this->accepted_cargo_filter_criteria, widget);
1804 break;
1805
1806 case WID_ID_FILTER_BY_PROD_CARGO: // Cargo filter dropdown
1807 ShowDropDownList(this, this->BuildCargoDropDownList(), this->produced_cargo_filter_criteria, widget);
1808 break;
1809
1810 case WID_ID_INDUSTRY_LIST: {
1811 auto it = this->vscroll->GetScrolledItemFromWidget(this->industries, pt.y, this, WID_ID_INDUSTRY_LIST, WidgetDimensions::scaled.framerect.top);
1812 if (it != this->industries.end()) {
1813 if (_ctrl_pressed) {
1814 ShowExtraViewportWindow((*it)->location.tile);
1815 } else {
1816 ScrollMainWindowToTile((*it)->location.tile);
1817 }
1818 }
1819 break;
1820 }
1821 }
1822 }
1823
1824 void OnDropdownSelect(WidgetID widget, int index) override
1825 {
1826 switch (widget) {
1828 if (this->industries.SortType() != index) {
1829 this->industries.SetSortType(index);
1831 }
1832 break;
1833 }
1834
1836 this->SetAcceptedCargoFilter(index);
1838 break;
1839 }
1840
1842 this->SetProducedCargoFilter(index);
1844 break;
1845 }
1846 }
1847 }
1848
1849 void OnResize() override
1850 {
1851 this->vscroll->SetCapacityFromWidget(this, WID_ID_INDUSTRY_LIST, WidgetDimensions::scaled.framerect.Vertical());
1852 this->hscroll->SetCapacityFromWidget(this, WID_ID_INDUSTRY_LIST, WidgetDimensions::scaled.framerect.Horizontal());
1853 }
1854
1855 void OnEditboxChanged(WidgetID wid) override
1856 {
1857 if (wid == WID_ID_FILTER) {
1858 this->string_filter.SetFilterTerm(this->industry_editbox.text.GetText());
1859 this->InvalidateData(IDIWD_FORCE_REBUILD);
1860 }
1861 }
1862
1863 void OnPaint() override
1864 {
1865 if (this->industries.NeedRebuild()) this->BuildSortIndustriesList();
1866 this->hscroll->SetCount(this->GetIndustryListWidth());
1867 this->DrawWidgets();
1868 }
1869
1871 IntervalTimer<TimerWindow> rebuild_interval = {std::chrono::seconds(3), [this](auto) {
1872 this->industries.ForceResort();
1874 }};
1875
1881 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
1882 {
1883 switch (data) {
1884 case IDIWD_FORCE_REBUILD:
1885 /* This needs to be done in command-scope to enforce rebuilding before resorting invalid data */
1886 this->industries.ForceRebuild();
1887 break;
1888
1889 case IDIWD_PRODUCTION_CHANGE:
1890 if (this->industries.SortType() == 2) this->industries.ForceResort();
1891 break;
1892
1893 default:
1894 this->industries.ForceResort();
1895 break;
1896 }
1897 }
1898
1899 EventState OnHotkey(int hotkey) override
1900 {
1901 switch (hotkey) {
1904 SetFocusedWindow(this); // The user has asked to give focus to the text box, so make sure this window is focused.
1905 break;
1906 default:
1907 return ES_NOT_HANDLED;
1908 }
1909 return ES_HANDLED;
1910 }
1911
1912 static inline HotkeyList hotkeys {"industrydirectory", {
1913 Hotkey('F', "focus_filter_box", IDHK_FOCUS_FILTER_BOX),
1914 }};
1915};
1916
1917Listing IndustryDirectoryWindow::last_sorting = {false, 0};
1918
1919/* Available station sorting functions. */
1920const std::initializer_list<GUIIndustryList::SortFunction * const> IndustryDirectoryWindow::sorter_funcs = {
1921 &IndustryNameSorter,
1922 &IndustryTypeSorter,
1923 &IndustryProductionSorter,
1924 &IndustryTransportedCargoSorter
1925};
1926
1927CargoType IndustryDirectoryWindow::produced_cargo_filter = CargoFilterCriteria::CF_ANY;
1928
1929
1932 WDP_AUTO, "list_industries", 428, 190,
1934 {},
1936 &IndustryDirectoryWindow::hotkeys
1937);
1938
1939void ShowIndustryDirectory()
1940{
1941 AllocateWindowDescFront<IndustryDirectoryWindow>(_industry_directory_desc, 0);
1942}
1943
1947 NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
1948 NWidget(WWT_CAPTION, COLOUR_BROWN, WID_IC_CAPTION),
1949 NWidget(WWT_SHADEBOX, COLOUR_BROWN),
1950 NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
1951 NWidget(WWT_STICKYBOX, COLOUR_BROWN),
1952 EndContainer(),
1956 EndContainer(),
1958 NWidget(WWT_TEXTBTN, COLOUR_BROWN, WID_IC_NOTIFY),
1959 SetStringTip(STR_INDUSTRY_CARGOES_NOTIFY_SMALLMAP, STR_INDUSTRY_CARGOES_NOTIFY_SMALLMAP_TOOLTIP),
1960 NWidget(WWT_PANEL, COLOUR_BROWN), SetFill(1, 0), SetResize(0, 0), EndContainer(),
1961 NWidget(WWT_DROPDOWN, COLOUR_BROWN, WID_IC_IND_DROPDOWN), SetFill(0, 0), SetResize(0, 0),
1962 SetStringTip(STR_INDUSTRY_CARGOES_SELECT_INDUSTRY, STR_INDUSTRY_CARGOES_SELECT_INDUSTRY_TOOLTIP),
1963 NWidget(WWT_DROPDOWN, COLOUR_BROWN, WID_IC_CARGO_DROPDOWN), SetFill(0, 0), SetResize(0, 0),
1964 SetStringTip(STR_INDUSTRY_CARGOES_SELECT_CARGO, STR_INDUSTRY_CARGOES_SELECT_CARGO_TOOLTIP),
1965 NWidget(WWT_RESIZEBOX, COLOUR_BROWN),
1966 EndContainer(),
1967};
1968
1971 WDP_AUTO, "industry_cargoes", 300, 210,
1973 {},
1975);
1976
1986
1987static const uint MAX_CARGOES = 16;
1988
1992 static int blob_distance;
1993
1999
2000 static const int INDUSTRY_LINE_COLOUR;
2001 static const int CARGO_LINE_COLOUR;
2002
2005 static int industry_width;
2006 static uint max_cargoes;
2007
2008 using Cargoes = uint16_t;
2009 static_assert(std::numeric_limits<Cargoes>::digits >= MAX_CARGOES);
2010
2012 union {
2013 struct {
2014 IndustryType ind_type;
2015 std::array<CargoType, MAX_CARGOES> other_produced;
2016 std::array<CargoType, MAX_CARGOES> other_accepted;
2018 struct {
2019 std::array<CargoType, MAX_CARGOES> vertical_cargoes;
2022 uint8_t num_cargoes;
2023 uint8_t top_end;
2024 uint8_t bottom_end;
2026 struct {
2027 std::array<CargoType, MAX_CARGOES> cargoes;
2031 } u{}; // Data for each type.
2032
2038 {
2039 this->type = type;
2040 }
2041
2047 void MakeIndustry(IndustryType ind_type)
2048 {
2049 this->type = CFT_INDUSTRY;
2050 this->u.industry.ind_type = ind_type;
2051 std::fill(std::begin(this->u.industry.other_accepted), std::end(this->u.industry.other_accepted), INVALID_CARGO);
2052 std::fill(std::begin(this->u.industry.other_produced), std::end(this->u.industry.other_produced), INVALID_CARGO);
2053 }
2054
2061 int ConnectCargo(CargoType cargo, bool producer)
2062 {
2063 assert(this->type == CFT_CARGO);
2064 if (!IsValidCargoType(cargo)) return -1;
2065
2066 /* Find the vertical cargo column carrying the cargo. */
2067 int column = -1;
2068 for (int i = 0; i < this->u.cargo.num_cargoes; i++) {
2069 if (cargo == this->u.cargo.vertical_cargoes[i]) {
2070 column = i;
2071 break;
2072 }
2073 }
2074 if (column < 0) return -1;
2075
2076 if (producer) {
2077 assert(!HasBit(this->u.cargo.supp_cargoes, column));
2078 SetBit(this->u.cargo.supp_cargoes, column);
2079 } else {
2080 assert(!HasBit(this->u.cargo.cust_cargoes, column));
2081 SetBit(this->u.cargo.cust_cargoes, column);
2082 }
2083 return column;
2084 }
2085
2091 {
2092 assert(this->type == CFT_CARGO);
2093
2094 return this->u.cargo.supp_cargoes != 0 || this->u.cargo.cust_cargoes != 0;
2095 }
2096
2102 void MakeCargo(const std::span<const CargoType> cargoes)
2103 {
2104 this->type = CFT_CARGO;
2105 assert(std::size(cargoes) <= std::size(this->u.cargo.vertical_cargoes));
2106 auto insert = std::copy_if(std::begin(cargoes), std::end(cargoes), std::begin(this->u.cargo.vertical_cargoes), IsValidCargoType);
2107 this->u.cargo.num_cargoes = static_cast<uint8_t>(std::distance(std::begin(this->u.cargo.vertical_cargoes), insert));
2108 CargoTypeComparator comparator;
2109 std::sort(std::begin(this->u.cargo.vertical_cargoes), insert, comparator);
2110 std::fill(insert, std::end(this->u.cargo.vertical_cargoes), INVALID_CARGO);
2111 this->u.cargo.top_end = false;
2112 this->u.cargo.bottom_end = false;
2113 this->u.cargo.supp_cargoes = 0;
2114 this->u.cargo.cust_cargoes = 0;
2115 }
2116
2122 void MakeCargoLabel(const std::span<const CargoType> cargoes, bool left_align)
2123 {
2124 this->type = CFT_CARGO_LABEL;
2125 assert(std::size(cargoes) <= std::size(this->u.cargo_label.cargoes));
2126 auto insert = std::copy(std::begin(cargoes), std::end(cargoes), std::begin(this->u.cargo_label.cargoes));
2127 std::fill(insert, std::end(this->u.cargo_label.cargoes), INVALID_CARGO);
2128 this->u.cargo_label.left_align = left_align;
2129 }
2130
2136 {
2137 this->type = CFT_HEADER;
2138 this->u.header = textid;
2139 }
2140
2146 int GetCargoBase(int xpos) const
2147 {
2148 assert(this->type == CFT_CARGO);
2149 int n = this->u.cargo.num_cargoes;
2150
2151 return xpos + cargo_field_width / 2 - (CargoesField::cargo_line.width * n + CargoesField::cargo_space.width * (n - 1)) / 2;
2152 }
2153
2159 void Draw(int xpos, int ypos) const
2160 {
2161 switch (this->type) {
2162 case CFT_EMPTY:
2163 case CFT_SMALL_EMPTY:
2164 break;
2165
2166 case CFT_HEADER:
2167 ypos += (small_height - GetCharacterHeight(FS_NORMAL)) / 2;
2168 DrawString(xpos, xpos + industry_width, ypos, this->u.header, TC_WHITE, SA_HOR_CENTER);
2169 break;
2170
2171 case CFT_INDUSTRY: {
2172 int ypos1 = ypos + vert_inter_industry_space / 2;
2173 int ypos2 = ypos + normal_height - 1 - vert_inter_industry_space / 2;
2174 int xpos2 = xpos + industry_width - 1;
2175 DrawRectOutline({xpos, ypos1, xpos2, ypos2}, INDUSTRY_LINE_COLOUR);
2177 if (this->u.industry.ind_type < NUM_INDUSTRYTYPES) {
2178 const IndustrySpec *indsp = GetIndustrySpec(this->u.industry.ind_type);
2179 DrawString(xpos, xpos2, ypos, indsp->name, TC_WHITE, SA_HOR_CENTER);
2180
2181 /* Draw the industry legend. */
2182 int blob_left, blob_right;
2183 if (_current_text_dir == TD_RTL) {
2184 blob_right = xpos2 - blob_distance;
2185 blob_left = blob_right - CargoesField::legend.width;
2186 } else {
2187 blob_left = xpos + blob_distance;
2188 blob_right = blob_left + CargoesField::legend.width;
2189 }
2190 GfxFillRect(blob_left, ypos2 - blob_distance - CargoesField::legend.height, blob_right, ypos2 - blob_distance, PC_BLACK); // Border
2191 GfxFillRect(blob_left + 1, ypos2 - blob_distance - CargoesField::legend.height + 1, blob_right - 1, ypos2 - blob_distance - 1, indsp->map_colour);
2192 } else {
2193 DrawString(xpos, xpos2, ypos, STR_INDUSTRY_CARGOES_HOUSES, TC_FROMSTRING, SA_HOR_CENTER);
2194 }
2195
2196 /* Draw the other_produced/other_accepted cargoes. */
2197 std::span<const CargoType> other_right, other_left;
2198 if (_current_text_dir == TD_RTL) {
2199 other_right = this->u.industry.other_accepted;
2200 other_left = this->u.industry.other_produced;
2201 } else {
2202 other_right = this->u.industry.other_produced;
2203 other_left = this->u.industry.other_accepted;
2204 }
2206 for (uint i = 0; i < CargoesField::max_cargoes; i++) {
2207 if (IsValidCargoType(other_right[i])) {
2208 const CargoSpec *csp = CargoSpec::Get(other_right[i]);
2209 int xp = xpos + industry_width + CargoesField::cargo_stub.width;
2210 DrawHorConnection(xpos + industry_width, xp - 1, ypos1, csp);
2211 GfxDrawLine(xp, ypos1, xp, ypos1 + CargoesField::cargo_line.height - 1, CARGO_LINE_COLOUR);
2212 }
2213 if (IsValidCargoType(other_left[i])) {
2214 const CargoSpec *csp = CargoSpec::Get(other_left[i]);
2215 int xp = xpos - CargoesField::cargo_stub.width;
2216 DrawHorConnection(xp + 1, xpos - 1, ypos1, csp);
2217 GfxDrawLine(xp, ypos1, xp, ypos1 + CargoesField::cargo_line.height - 1, CARGO_LINE_COLOUR);
2218 }
2220 }
2221 break;
2222 }
2223
2224 case CFT_CARGO: {
2225 int cargo_base = this->GetCargoBase(xpos);
2226 int top = ypos + (this->u.cargo.top_end ? vert_inter_industry_space / 2 + 1 : 0);
2227 int bot = ypos - (this->u.cargo.bottom_end ? vert_inter_industry_space / 2 + 1 : 0) + normal_height - 1;
2228 int colpos = cargo_base;
2229 for (int i = 0; i < this->u.cargo.num_cargoes; i++) {
2230 if (this->u.cargo.top_end) GfxDrawLine(colpos, top - 1, colpos + CargoesField::cargo_line.width - 1, top - 1, CARGO_LINE_COLOUR);
2231 if (this->u.cargo.bottom_end) GfxDrawLine(colpos, bot + 1, colpos + CargoesField::cargo_line.width - 1, bot + 1, CARGO_LINE_COLOUR);
2232 GfxDrawLine(colpos, top, colpos, bot, CARGO_LINE_COLOUR);
2233 colpos++;
2234 const CargoSpec *csp = CargoSpec::Get(this->u.cargo.vertical_cargoes[i]);
2235 GfxFillRect(colpos, top, colpos + CargoesField::cargo_line.width - 2, bot, csp->legend_colour, FILLRECT_OPAQUE);
2236 colpos += CargoesField::cargo_line.width - 2;
2237 GfxDrawLine(colpos, top, colpos, bot, CARGO_LINE_COLOUR);
2238 colpos += 1 + CargoesField::cargo_space.width;
2239 }
2240
2241 Cargoes hor_left, hor_right;
2242 if (_current_text_dir == TD_RTL) {
2243 hor_left = this->u.cargo.cust_cargoes;
2244 hor_right = this->u.cargo.supp_cargoes;
2245 } else {
2246 hor_left = this->u.cargo.supp_cargoes;
2247 hor_right = this->u.cargo.cust_cargoes;
2248 }
2250 for (uint i = 0; i < MAX_CARGOES; i++) {
2251 if (HasBit(hor_left, i)) {
2252 int col = i;
2253 int dx = 0;
2254 const CargoSpec *csp = CargoSpec::Get(this->u.cargo.vertical_cargoes[col]);
2255 for (; col > 0; col--) {
2256 int lf = cargo_base + col * CargoesField::cargo_line.width + (col - 1) * CargoesField::cargo_space.width;
2257 DrawHorConnection(lf, lf + CargoesField::cargo_space.width - dx, ypos, csp);
2258 dx = 1;
2259 }
2260 DrawHorConnection(xpos, cargo_base - dx, ypos, csp);
2261 }
2262 if (HasBit(hor_right, i)) {
2263 int col = i;
2264 int dx = 0;
2265 const CargoSpec *csp = CargoSpec::Get(this->u.cargo.vertical_cargoes[col]);
2266 for (; col < this->u.cargo.num_cargoes - 1; col++) {
2267 int lf = cargo_base + (col + 1) * CargoesField::cargo_line.width + col * CargoesField::cargo_space.width;
2268 DrawHorConnection(lf + dx - 1, lf + CargoesField::cargo_space.width - 1, ypos, csp);
2269 dx = 1;
2270 }
2271 DrawHorConnection(cargo_base + col * CargoesField::cargo_space.width + (col + 1) * CargoesField::cargo_line.width - 1 + dx, xpos + CargoesField::cargo_field_width - 1, ypos, csp);
2272 }
2274 }
2275 break;
2276 }
2277
2278 case CFT_CARGO_LABEL:
2280 for (uint i = 0; i < MAX_CARGOES; i++) {
2281 if (IsValidCargoType(this->u.cargo_label.cargoes[i])) {
2282 const CargoSpec *csp = CargoSpec::Get(this->u.cargo_label.cargoes[i]);
2283 DrawString(xpos + WidgetDimensions::scaled.framerect.left, xpos + industry_width - 1 - WidgetDimensions::scaled.framerect.right, ypos, csp->name, TC_WHITE,
2284 (this->u.cargo_label.left_align) ? SA_LEFT : SA_RIGHT);
2285 }
2287 }
2288 break;
2289
2290 default:
2291 NOT_REACHED();
2292 }
2293 }
2294
2302 CargoType CargoClickedAt(const CargoesField *left, const CargoesField *right, Point pt) const
2303 {
2304 assert(this->type == CFT_CARGO);
2305
2306 /* Vertical matching. */
2307 int cpos = this->GetCargoBase(0);
2308 uint col;
2309 for (col = 0; col < this->u.cargo.num_cargoes; col++) {
2310 if (pt.x < cpos) break;
2311 if (pt.x < cpos + (int)CargoesField::cargo_line.width) return this->u.cargo.vertical_cargoes[col];
2313 }
2314 /* col = 0 -> left of first col, 1 -> left of 2nd col, ... this->u.cargo.num_cargoes right of last-col. */
2315
2317 uint row;
2318 for (row = 0; row < MAX_CARGOES; row++) {
2319 if (pt.y < vpos) return INVALID_CARGO;
2320 if (pt.y < vpos + GetCharacterHeight(FS_NORMAL)) break;
2322 }
2323 if (row == MAX_CARGOES) return INVALID_CARGO;
2324
2325 /* row = 0 -> at first horizontal row, row = 1 -> second horizontal row, 2 = 3rd horizontal row. */
2326 if (col == 0) {
2327 if (HasBit(this->u.cargo.supp_cargoes, row)) return this->u.cargo.vertical_cargoes[row];
2328 if (left != nullptr) {
2329 if (left->type == CFT_INDUSTRY) return left->u.industry.other_produced[row];
2330 if (left->type == CFT_CARGO_LABEL && !left->u.cargo_label.left_align) return left->u.cargo_label.cargoes[row];
2331 }
2332 return INVALID_CARGO;
2333 }
2334 if (col == this->u.cargo.num_cargoes) {
2335 if (HasBit(this->u.cargo.cust_cargoes, row)) return this->u.cargo.vertical_cargoes[row];
2336 if (right != nullptr) {
2337 if (right->type == CFT_INDUSTRY) return right->u.industry.other_accepted[row];
2338 if (right->type == CFT_CARGO_LABEL && right->u.cargo_label.left_align) return right->u.cargo_label.cargoes[row];
2339 }
2340 return INVALID_CARGO;
2341 }
2342 if (row >= col) {
2343 /* Clicked somewhere in-between vertical cargo connection.
2344 * Since the horizontal connection is made in the same order as the vertical list, the above condition
2345 * ensures we are left-below the main diagonal, thus at the supplying side.
2346 */
2347 if (HasBit(this->u.cargo.supp_cargoes, row)) return this->u.cargo.vertical_cargoes[row];
2348 return INVALID_CARGO;
2349 }
2350 /* Clicked at a customer connection. */
2351 if (HasBit(this->u.cargo.cust_cargoes, row)) return this->u.cargo.vertical_cargoes[row];
2352 return INVALID_CARGO;
2353 }
2354
2361 {
2362 assert(this->type == CFT_CARGO_LABEL);
2363
2365 uint row;
2366 for (row = 0; row < MAX_CARGOES; row++) {
2367 if (pt.y < vpos) return INVALID_CARGO;
2368 if (pt.y < vpos + GetCharacterHeight(FS_NORMAL)) break;
2370 }
2371 if (row == MAX_CARGOES) return INVALID_CARGO;
2372 return this->u.cargo_label.cargoes[row];
2373 }
2374
2375private:
2383 static void DrawHorConnection(int left, int right, int top, const CargoSpec *csp)
2384 {
2385 GfxDrawLine(left, top, right, top, CARGO_LINE_COLOUR);
2386 GfxFillRect(left, top + 1, right, top + CargoesField::cargo_line.height - 2, csp->legend_colour, FILLRECT_OPAQUE);
2387 GfxDrawLine(left, top + CargoesField::cargo_line.height - 1, right, top + CargoesField::cargo_line.height - 1, CARGO_LINE_COLOUR);
2388 }
2389};
2390
2391static_assert(MAX_CARGOES >= std::tuple_size_v<decltype(IndustrySpec::produced_cargo)>);
2392static_assert(MAX_CARGOES >= std::tuple_size_v<decltype(IndustrySpec::accepts_cargo)>);
2393
2399
2406
2408
2411
2415
2421 {
2422 CargoesField *ind_fld = this->columns + column;
2423 CargoesField *cargo_fld = this->columns + column + 1;
2424 assert(ind_fld->type == CFT_INDUSTRY && cargo_fld->type == CFT_CARGO);
2425
2426 std::fill(std::begin(ind_fld->u.industry.other_produced), std::end(ind_fld->u.industry.other_produced), INVALID_CARGO);
2427
2428 if (ind_fld->u.industry.ind_type < NUM_INDUSTRYTYPES) {
2429 CargoType others[MAX_CARGOES]; // Produced cargoes not carried in the cargo column.
2430 int other_count = 0;
2431
2432 const IndustrySpec *indsp = GetIndustrySpec(ind_fld->u.industry.ind_type);
2433 assert(CargoesField::max_cargoes <= std::size(indsp->produced_cargo));
2434 for (uint i = 0; i < CargoesField::max_cargoes; i++) {
2435 int col = cargo_fld->ConnectCargo(indsp->produced_cargo[i], true);
2436 if (col < 0) others[other_count++] = indsp->produced_cargo[i];
2437 }
2438
2439 /* Allocate other cargoes in the empty holes of the horizontal cargo connections. */
2440 for (uint i = 0; i < CargoesField::max_cargoes && other_count > 0; i++) {
2441 if (HasBit(cargo_fld->u.cargo.supp_cargoes, i)) ind_fld->u.industry.other_produced[i] = others[--other_count];
2442 }
2443 } else {
2444 /* Houses only display cargo that towns produce. */
2445 for (uint i = 0; i < cargo_fld->u.cargo.num_cargoes; i++) {
2446 CargoType cargo_type = cargo_fld->u.cargo.vertical_cargoes[i];
2448 if (tpe == TPE_PASSENGERS || tpe == TPE_MAIL) cargo_fld->ConnectCargo(cargo_type, true);
2449 }
2450 }
2451 }
2452
2458 void MakeCargoLabel(int column, bool accepting)
2459 {
2460 CargoType cargoes[MAX_CARGOES];
2461 std::fill(std::begin(cargoes), std::end(cargoes), INVALID_CARGO);
2462
2463 CargoesField *label_fld = this->columns + column;
2464 CargoesField *cargo_fld = this->columns + (accepting ? column - 1 : column + 1);
2465
2466 assert(cargo_fld->type == CFT_CARGO && label_fld->type == CFT_EMPTY);
2467 for (uint i = 0; i < cargo_fld->u.cargo.num_cargoes; i++) {
2468 int col = cargo_fld->ConnectCargo(cargo_fld->u.cargo.vertical_cargoes[i], !accepting);
2469 if (col >= 0) cargoes[col] = cargo_fld->u.cargo.vertical_cargoes[i];
2470 }
2471 label_fld->MakeCargoLabel(cargoes, accepting);
2472 }
2473
2474
2480 {
2481 CargoesField *ind_fld = this->columns + column;
2482 CargoesField *cargo_fld = this->columns + column - 1;
2483 assert(ind_fld->type == CFT_INDUSTRY && cargo_fld->type == CFT_CARGO);
2484
2485 std::fill(std::begin(ind_fld->u.industry.other_accepted), std::end(ind_fld->u.industry.other_accepted), INVALID_CARGO);
2486
2487 if (ind_fld->u.industry.ind_type < NUM_INDUSTRYTYPES) {
2488 CargoType others[MAX_CARGOES]; // Accepted cargoes not carried in the cargo column.
2489 int other_count = 0;
2490
2491 const IndustrySpec *indsp = GetIndustrySpec(ind_fld->u.industry.ind_type);
2492 assert(CargoesField::max_cargoes <= std::size(indsp->accepts_cargo));
2493 for (uint i = 0; i < CargoesField::max_cargoes; i++) {
2494 int col = cargo_fld->ConnectCargo(indsp->accepts_cargo[i], false);
2495 if (col < 0) others[other_count++] = indsp->accepts_cargo[i];
2496 }
2497
2498 /* Allocate other cargoes in the empty holes of the horizontal cargo connections. */
2499 for (uint i = 0; i < CargoesField::max_cargoes && other_count > 0; i++) {
2500 if (!HasBit(cargo_fld->u.cargo.cust_cargoes, i)) ind_fld->u.industry.other_accepted[i] = others[--other_count];
2501 }
2502 } else {
2503 /* Houses only display what is demanded. */
2504 for (uint i = 0; i < cargo_fld->u.cargo.num_cargoes; i++) {
2505 for (const auto &hs : HouseSpec::Specs()) {
2506 if (!hs.enabled) continue;
2507
2508 for (uint j = 0; j < lengthof(hs.accepts_cargo); j++) {
2509 if (hs.cargo_acceptance[j] > 0 && cargo_fld->u.cargo.vertical_cargoes[i] == hs.accepts_cargo[j]) {
2510 cargo_fld->ConnectCargo(cargo_fld->u.cargo.vertical_cargoes[i], false);
2511 goto next_cargo;
2512 }
2513 }
2514 }
2515next_cargo: ;
2516 }
2517 }
2518 }
2519};
2520
2521
2550 typedef std::vector<CargoesRow> Fields;
2551
2552 Fields fields{};
2553 uint ind_cargo = 0;
2556 Scrollbar *vscroll = nullptr;
2557
2559 {
2560 this->OnInit();
2561 this->CreateNestedTree();
2562 this->vscroll = this->GetScrollbar(WID_IC_SCROLLBAR);
2563 this->FinishInitNested(0);
2564 this->OnInvalidateData(id);
2565 }
2566
2567 void OnInit() override
2568 {
2569 /* Initialize static CargoesField size variables. */
2570 Dimension d = GetStringBoundingBox(STR_INDUSTRY_CARGOES_PRODUCERS);
2571 d = maxdim(d, GetStringBoundingBox(STR_INDUSTRY_CARGOES_CUSTOMERS));
2574 CargoesField::small_height = d.height;
2575
2576 /* Size of the legend blob -- slightly larger than the smallmap legend blob. */
2578 CargoesField::legend.width = CargoesField::legend.height * 9 / 6;
2579
2580 /* Size of cargo lines. */
2583
2584 /* Size of border between cargo lines and industry boxes. */
2587
2588 /* Size of space between cargo lines. */
2591
2592 /* Size of cargo stub (unconnected cargo line.) */
2594 CargoesField::cargo_stub.height = CargoesField::cargo_line.height; /* Unused */
2595
2598
2599 /* Decide about the size of the box holding the text of an industry type. */
2600 this->ind_textsize.width = 0;
2601 this->ind_textsize.height = 0;
2602 CargoesField::max_cargoes = 0;
2603 for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
2604 const IndustrySpec *indsp = GetIndustrySpec(it);
2605 if (!indsp->enabled) continue;
2607 CargoesField::max_cargoes = std::max<uint>(CargoesField::max_cargoes, std::ranges::count_if(indsp->accepts_cargo, IsValidCargoType));
2608 CargoesField::max_cargoes = std::max<uint>(CargoesField::max_cargoes, std::ranges::count_if(indsp->produced_cargo, IsValidCargoType));
2609 }
2610 d.width = std::max(d.width, this->ind_textsize.width);
2611 d.height = this->ind_textsize.height;
2612 this->ind_textsize = maxdim(this->ind_textsize, GetStringBoundingBox(STR_INDUSTRY_CARGOES_SELECT_INDUSTRY));
2613
2614 /* Compute max size of the cargo texts. */
2615 this->cargo_textsize.width = 0;
2616 this->cargo_textsize.height = 0;
2617 for (const CargoSpec *csp : CargoSpec::Iterate()) {
2618 this->cargo_textsize = maxdim(this->cargo_textsize, GetStringBoundingBox(csp->name));
2619 }
2620 d = maxdim(d, this->cargo_textsize); // Box must also be wide enough to hold any cargo label.
2621 this->cargo_textsize = maxdim(this->cargo_textsize, GetStringBoundingBox(STR_INDUSTRY_CARGOES_SELECT_CARGO));
2622
2624 /* Ensure the height is enough for the industry type text, for the horizontal connections, and for the cargo labels. */
2626 d.height = std::max(d.height + WidgetDimensions::scaled.frametext.Vertical(), min_ind_height);
2627
2630
2631 /* Width of a #CFT_CARGO field. */
2633 }
2634
2635 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
2636 {
2637 switch (widget) {
2638 case WID_IC_PANEL:
2642 break;
2643
2645 size.width = std::max(size.width, this->ind_textsize.width + padding.width);
2646 break;
2647
2649 size.width = std::max(size.width, this->cargo_textsize.width + padding.width);
2650 break;
2651 }
2652 }
2653
2654 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
2655 {
2656 if (widget != WID_IC_CAPTION) return this->Window::GetWidgetString(widget, stringid);
2657
2658 if (this->ind_cargo < NUM_INDUSTRYTYPES) {
2659 const IndustrySpec *indsp = GetIndustrySpec(this->ind_cargo);
2660 return GetString(STR_INDUSTRY_CARGOES_INDUSTRY_CAPTION, indsp->name);
2661 } else {
2662 const CargoSpec *csp = CargoSpec::Get(this->ind_cargo - NUM_INDUSTRYTYPES);
2663 return GetString(STR_INDUSTRY_CARGOES_CARGO_CAPTION, csp->name);
2664 }
2665 }
2666
2673 static bool HasCommonValidCargo(const std::span<const CargoType> cargoes1, const std::span<const CargoType> cargoes2)
2674 {
2675 for (const CargoType cargo_type1 : cargoes1) {
2676 if (!IsValidCargoType(cargo_type1)) continue;
2677 for (const CargoType cargo_type2 : cargoes2) {
2678 if (cargo_type1 == cargo_type2) return true;
2679 }
2680 }
2681 return false;
2682 }
2683
2689 static bool HousesCanSupply(const std::span<const CargoType> cargoes)
2690 {
2691 for (const CargoType cargo_type : cargoes) {
2692 if (!IsValidCargoType(cargo_type)) continue;
2694 if (tpe == TPE_PASSENGERS || tpe == TPE_MAIL) return true;
2695 }
2696 return false;
2697 }
2698
2704 static bool HousesCanAccept(const std::span<const CargoType> cargoes)
2705 {
2706 HouseZones climate_mask;
2708 case LandscapeType::Temperate: climate_mask = HZ_TEMP; break;
2709 case LandscapeType::Arctic: climate_mask = HZ_SUBARTC_ABOVE | HZ_SUBARTC_BELOW; break;
2710 case LandscapeType::Tropic: climate_mask = HZ_SUBTROPIC; break;
2711 case LandscapeType::Toyland: climate_mask = HZ_TOYLND; break;
2712 default: NOT_REACHED();
2713 }
2714 for (const CargoType cargo_type : cargoes) {
2715 if (!IsValidCargoType(cargo_type)) continue;
2716
2717 for (const auto &hs : HouseSpec::Specs()) {
2718 if (!hs.enabled || !(hs.building_availability & climate_mask)) continue;
2719
2720 for (uint j = 0; j < lengthof(hs.accepts_cargo); j++) {
2721 if (hs.cargo_acceptance[j] > 0 && cargo_type == hs.accepts_cargo[j]) return true;
2722 }
2723 }
2724 }
2725 return false;
2726 }
2727
2733 static int CountMatchingAcceptingIndustries(const std::span<const CargoType> cargoes)
2734 {
2735 int count = 0;
2736 for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
2737 const IndustrySpec *indsp = GetIndustrySpec(it);
2738 if (!indsp->enabled) continue;
2739
2740 if (HasCommonValidCargo(cargoes, indsp->accepts_cargo)) count++;
2741 }
2742 return count;
2743 }
2744
2750 static int CountMatchingProducingIndustries(const std::span<const CargoType> cargoes)
2751 {
2752 int count = 0;
2753 for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
2754 const IndustrySpec *indsp = GetIndustrySpec(it);
2755 if (!indsp->enabled) continue;
2756
2757 if (HasCommonValidCargo(cargoes, indsp->produced_cargo)) count++;
2758 }
2759 return count;
2760 }
2761
2768 void ShortenCargoColumn(int column, int top, int bottom)
2769 {
2770 while (top < bottom && !this->fields[top].columns[column].HasConnection()) {
2771 this->fields[top].columns[column].MakeEmpty(CFT_EMPTY);
2772 top++;
2773 }
2774 this->fields[top].columns[column].u.cargo.top_end = true;
2775
2776 while (bottom > top && !this->fields[bottom].columns[column].HasConnection()) {
2777 this->fields[bottom].columns[column].MakeEmpty(CFT_EMPTY);
2778 bottom--;
2779 }
2780 this->fields[bottom].columns[column].u.cargo.bottom_end = true;
2781 }
2782
2789 void PlaceIndustry(int row, int col, IndustryType it)
2790 {
2791 assert(this->fields[row].columns[col].type == CFT_EMPTY);
2792 this->fields[row].columns[col].MakeIndustry(it);
2793 if (col == 0) {
2794 this->fields[row].ConnectIndustryProduced(col);
2795 } else {
2796 this->fields[row].ConnectIndustryAccepted(col);
2797 }
2798 }
2799
2804 {
2805 if (!this->IsWidgetLowered(WID_IC_NOTIFY)) return;
2806
2807 /* Only notify the smallmap window if it exists. In particular, do not
2808 * bring it to the front to prevent messing up any nice layout of the user. */
2810 }
2811
2816 void ComputeIndustryDisplay(IndustryType displayed_it)
2817 {
2818 this->ind_cargo = displayed_it;
2819 _displayed_industries.reset();
2820 _displayed_industries.set(displayed_it);
2821
2822 this->fields.clear();
2823 CargoesRow &first_row = this->fields.emplace_back();
2824 first_row.columns[0].MakeHeader(STR_INDUSTRY_CARGOES_PRODUCERS);
2825 first_row.columns[1].MakeEmpty(CFT_SMALL_EMPTY);
2826 first_row.columns[2].MakeEmpty(CFT_SMALL_EMPTY);
2827 first_row.columns[3].MakeEmpty(CFT_SMALL_EMPTY);
2828 first_row.columns[4].MakeHeader(STR_INDUSTRY_CARGOES_CUSTOMERS);
2829
2830 const IndustrySpec *central_sp = GetIndustrySpec(displayed_it);
2831 bool houses_supply = HousesCanSupply(central_sp->accepts_cargo);
2832 bool houses_accept = HousesCanAccept(central_sp->produced_cargo);
2833 /* Make a field consisting of two cargo columns. */
2834 int num_supp = CountMatchingProducingIndustries(central_sp->accepts_cargo) + houses_supply;
2835 int num_cust = CountMatchingAcceptingIndustries(central_sp->produced_cargo) + houses_accept;
2836 int num_indrows = std::max(3, std::max(num_supp, num_cust)); // One is needed for the 'it' industry, and 2 for the cargo labels.
2837 for (int i = 0; i < num_indrows; i++) {
2838 CargoesRow &row = this->fields.emplace_back();
2839 row.columns[0].MakeEmpty(CFT_EMPTY);
2840 row.columns[1].MakeCargo(central_sp->accepts_cargo);
2841 row.columns[2].MakeEmpty(CFT_EMPTY);
2842 row.columns[3].MakeCargo(central_sp->produced_cargo);
2843 row.columns[4].MakeEmpty(CFT_EMPTY);
2844 }
2845 /* Add central industry. */
2846 int central_row = 1 + num_indrows / 2;
2847 this->fields[central_row].columns[2].MakeIndustry(displayed_it);
2848 this->fields[central_row].ConnectIndustryProduced(2);
2849 this->fields[central_row].ConnectIndustryAccepted(2);
2850
2851 /* Add cargo labels. */
2852 this->fields[central_row - 1].MakeCargoLabel(2, true);
2853 this->fields[central_row + 1].MakeCargoLabel(2, false);
2854
2855 /* Add suppliers and customers of the 'it' industry. */
2856 int supp_count = 0;
2857 int cust_count = 0;
2858 for (IndustryType it : _sorted_industry_types) {
2859 const IndustrySpec *indsp = GetIndustrySpec(it);
2860 if (!indsp->enabled) continue;
2861
2862 if (HasCommonValidCargo(central_sp->accepts_cargo, indsp->produced_cargo)) {
2863 this->PlaceIndustry(1 + supp_count * num_indrows / num_supp, 0, it);
2864 _displayed_industries.set(it);
2865 supp_count++;
2866 }
2867 if (HasCommonValidCargo(central_sp->produced_cargo, indsp->accepts_cargo)) {
2868 this->PlaceIndustry(1 + cust_count * num_indrows / num_cust, 4, it);
2869 _displayed_industries.set(it);
2870 cust_count++;
2871 }
2872 }
2873 if (houses_supply) {
2874 this->PlaceIndustry(1 + supp_count * num_indrows / num_supp, 0, NUM_INDUSTRYTYPES);
2875 supp_count++;
2876 }
2877 if (houses_accept) {
2878 this->PlaceIndustry(1 + cust_count * num_indrows / num_cust, 4, NUM_INDUSTRYTYPES);
2879 cust_count++;
2880 }
2881
2882 this->ShortenCargoColumn(1, 1, num_indrows);
2883 this->ShortenCargoColumn(3, 1, num_indrows);
2884 this->vscroll->SetCount(num_indrows);
2885 this->SetDirty();
2886 this->NotifySmallmap();
2887 }
2888
2894 {
2895 this->ind_cargo = cargo_type + NUM_INDUSTRYTYPES;
2896 _displayed_industries.reset();
2897
2898 this->fields.clear();
2899 CargoesRow &first_row = this->fields.emplace_back();
2900 first_row.columns[0].MakeHeader(STR_INDUSTRY_CARGOES_PRODUCERS);
2901 first_row.columns[1].MakeEmpty(CFT_SMALL_EMPTY);
2902 first_row.columns[2].MakeHeader(STR_INDUSTRY_CARGOES_CUSTOMERS);
2903 first_row.columns[3].MakeEmpty(CFT_SMALL_EMPTY);
2904 first_row.columns[4].MakeEmpty(CFT_SMALL_EMPTY);
2905
2906 auto cargoes = std::span(&cargo_type, 1);
2907 bool houses_supply = HousesCanSupply(cargoes);
2908 bool houses_accept = HousesCanAccept(cargoes);
2909 int num_supp = CountMatchingProducingIndustries(cargoes) + houses_supply + 1; // Ensure room for the cargo label.
2910 int num_cust = CountMatchingAcceptingIndustries(cargoes) + houses_accept;
2911 int num_indrows = std::max(num_supp, num_cust);
2912 for (int i = 0; i < num_indrows; i++) {
2913 CargoesRow &row = this->fields.emplace_back();
2914 row.columns[0].MakeEmpty(CFT_EMPTY);
2915 row.columns[1].MakeCargo(cargoes);
2916 row.columns[2].MakeEmpty(CFT_EMPTY);
2917 row.columns[3].MakeEmpty(CFT_EMPTY);
2918 row.columns[4].MakeEmpty(CFT_EMPTY);
2919 }
2920
2921 this->fields[num_indrows].MakeCargoLabel(0, false); // Add cargo labels at the left bottom.
2922
2923 /* Add suppliers and customers of the cargo. */
2924 int supp_count = 0;
2925 int cust_count = 0;
2926 for (IndustryType it : _sorted_industry_types) {
2927 const IndustrySpec *indsp = GetIndustrySpec(it);
2928 if (!indsp->enabled) continue;
2929
2930 if (HasCommonValidCargo(cargoes, indsp->produced_cargo)) {
2931 this->PlaceIndustry(1 + supp_count * num_indrows / num_supp, 0, it);
2932 _displayed_industries.set(it);
2933 supp_count++;
2934 }
2935 if (HasCommonValidCargo(cargoes, indsp->accepts_cargo)) {
2936 this->PlaceIndustry(1 + cust_count * num_indrows / num_cust, 2, it);
2937 _displayed_industries.set(it);
2938 cust_count++;
2939 }
2940 }
2941 if (houses_supply) {
2942 this->PlaceIndustry(1 + supp_count * num_indrows / num_supp, 0, NUM_INDUSTRYTYPES);
2943 supp_count++;
2944 }
2945 if (houses_accept) {
2946 this->PlaceIndustry(1 + cust_count * num_indrows / num_cust, 2, NUM_INDUSTRYTYPES);
2947 cust_count++;
2948 }
2949
2950 this->ShortenCargoColumn(1, 1, num_indrows);
2951 this->vscroll->SetCount(num_indrows);
2952 this->SetDirty();
2953 this->NotifySmallmap();
2954 }
2955
2963 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
2964 {
2965 if (!gui_scope) return;
2966 if (data == NUM_INDUSTRYTYPES) {
2968 return;
2969 }
2970
2971 assert(data >= 0 && data < NUM_INDUSTRYTYPES);
2972 this->ComputeIndustryDisplay(data);
2973 }
2974
2975 void DrawWidget(const Rect &r, WidgetID widget) const override
2976 {
2977 if (widget != WID_IC_PANEL) return;
2978
2979 Rect ir = r.Shrink(WidgetDimensions::scaled.bevel);
2980 DrawPixelInfo tmp_dpi;
2981 if (!FillDrawPixelInfo(&tmp_dpi, ir)) return;
2982 AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi);
2983
2985 if (this->ind_cargo >= NUM_INDUSTRYTYPES) left_pos += (CargoesField::industry_width + CargoesField::cargo_field_width) / 2;
2986 int last_column = (this->ind_cargo < NUM_INDUSTRYTYPES) ? 4 : 2;
2987
2988 const NWidgetBase *nwp = this->GetWidget<NWidgetBase>(WID_IC_PANEL);
2989 int vpos = WidgetDimensions::scaled.frametext.top - WidgetDimensions::scaled.bevel.top - this->vscroll->GetPosition() * nwp->resize_y;
2990 int row_height = CargoesField::small_height;
2991 for (const auto &field : this->fields) {
2992 if (vpos + row_height >= 0) {
2993 int xpos = left_pos;
2994 int col, dir;
2995 if (_current_text_dir == TD_RTL) {
2996 col = last_column;
2997 dir = -1;
2998 } else {
2999 col = 0;
3000 dir = 1;
3001 }
3002 while (col >= 0 && col <= last_column) {
3003 field.columns[col].Draw(xpos, vpos);
3005 col += dir;
3006 }
3007 }
3008 vpos += row_height;
3009 if (vpos >= height) break;
3010 row_height = CargoesField::normal_height;
3011 }
3012 }
3013
3022 {
3023 const NWidgetBase *nw = this->GetWidget<NWidgetBase>(WID_IC_PANEL);
3024 pt.x -= nw->pos_x;
3025 pt.y -= nw->pos_y;
3026
3027 int vpos = WidgetDimensions::scaled.frametext.top + CargoesField::small_height - this->vscroll->GetPosition() * nw->resize_y;
3028 if (pt.y < vpos) return false;
3029
3030 int row = (pt.y - vpos) / CargoesField::normal_height; // row is relative to row 1.
3031 if (row + 1 >= (int)this->fields.size()) return false;
3032 vpos = pt.y - vpos - row * CargoesField::normal_height; // Position in the row + 1 field
3033 row++; // rebase row to match index of this->fields.
3034
3036 if (pt.x < xpos) return false;
3037 int column;
3038 for (column = 0; column <= 5; column++) {
3040 if (pt.x < xpos + width) break;
3041 xpos += width;
3042 }
3043 int num_columns = (this->ind_cargo < NUM_INDUSTRYTYPES) ? 4 : 2;
3044 if (column > num_columns) return false;
3045 xpos = pt.x - xpos;
3046
3047 /* Return both positions, compensating for RTL languages (which works due to the equal symmetry in both displays). */
3048 fieldxy->y = row;
3049 xy->y = vpos;
3050 if (_current_text_dir == TD_RTL) {
3051 fieldxy->x = num_columns - column;
3052 xy->x = ((column & 1) ? CargoesField::cargo_field_width : CargoesField::industry_width) - xpos;
3053 } else {
3054 fieldxy->x = column;
3055 xy->x = xpos;
3056 }
3057 return true;
3058 }
3059
3060 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
3061 {
3062 switch (widget) {
3063 case WID_IC_PANEL: {
3064 Point fieldxy, xy;
3065 if (!CalculatePositionInWidget(pt, &fieldxy, &xy)) return;
3066
3067 const CargoesField *fld = this->fields[fieldxy.y].columns + fieldxy.x;
3068 switch (fld->type) {
3069 case CFT_INDUSTRY:
3071 break;
3072
3073 case CFT_CARGO: {
3074 CargoesField *lft = (fieldxy.x > 0) ? this->fields[fieldxy.y].columns + fieldxy.x - 1 : nullptr;
3075 CargoesField *rgt = (fieldxy.x < 4) ? this->fields[fieldxy.y].columns + fieldxy.x + 1 : nullptr;
3076 CargoType cargo_type = fld->CargoClickedAt(lft, rgt, xy);
3077 if (IsValidCargoType(cargo_type)) this->ComputeCargoDisplay(cargo_type);
3078 break;
3079 }
3080
3081 case CFT_CARGO_LABEL: {
3082 CargoType cargo_type = fld->CargoLabelClickedAt(xy);
3083 if (IsValidCargoType(cargo_type)) this->ComputeCargoDisplay(cargo_type);
3084 break;
3085 }
3086
3087 default:
3088 break;
3089 }
3090 break;
3091 }
3092
3093 case WID_IC_NOTIFY:
3097
3098 if (this->IsWidgetLowered(WID_IC_NOTIFY)) {
3099 if (FindWindowByClass(WC_SMALLMAP) == nullptr) ShowSmallMap();
3100 this->NotifySmallmap();
3101 }
3102 break;
3103
3104 case WID_IC_CARGO_DROPDOWN: {
3105 DropDownList lst;
3107 for (const CargoSpec *cs : _sorted_standard_cargo_specs) {
3108 lst.push_back(MakeDropDownListIconItem(d, cs->GetCargoIcon(), PAL_NONE, cs->name, cs->Index()));
3109 }
3110 if (!lst.empty()) {
3111 int selected = (this->ind_cargo >= NUM_INDUSTRYTYPES) ? (int)(this->ind_cargo - NUM_INDUSTRYTYPES) : -1;
3112 ShowDropDownList(this, std::move(lst), selected, WID_IC_CARGO_DROPDOWN);
3113 }
3114 break;
3115 }
3116
3117 case WID_IC_IND_DROPDOWN: {
3118 DropDownList lst;
3119 for (IndustryType ind : _sorted_industry_types) {
3120 const IndustrySpec *indsp = GetIndustrySpec(ind);
3121 if (!indsp->enabled) continue;
3122 lst.push_back(MakeDropDownListStringItem(indsp->name, ind));
3123 }
3124 if (!lst.empty()) {
3125 int selected = (this->ind_cargo < NUM_INDUSTRYTYPES) ? (int)this->ind_cargo : -1;
3126 ShowDropDownList(this, std::move(lst), selected, WID_IC_IND_DROPDOWN);
3127 }
3128 break;
3129 }
3130 }
3131 }
3132
3133 void OnDropdownSelect(WidgetID widget, int index) override
3134 {
3135 if (index < 0) return;
3136
3137 switch (widget) {
3139 this->ComputeCargoDisplay(index);
3140 break;
3141
3143 this->ComputeIndustryDisplay(index);
3144 break;
3145 }
3146 }
3147
3148 bool OnTooltip([[maybe_unused]] Point pt, WidgetID widget, TooltipCloseCondition close_cond) override
3149 {
3150 if (widget != WID_IC_PANEL) return false;
3151
3152 Point fieldxy, xy;
3153 if (!CalculatePositionInWidget(pt, &fieldxy, &xy)) return false;
3154
3155 const CargoesField *fld = this->fields[fieldxy.y].columns + fieldxy.x;
3156 CargoType cargo_type = INVALID_CARGO;
3157 switch (fld->type) {
3158 case CFT_CARGO: {
3159 CargoesField *lft = (fieldxy.x > 0) ? this->fields[fieldxy.y].columns + fieldxy.x - 1 : nullptr;
3160 CargoesField *rgt = (fieldxy.x < 4) ? this->fields[fieldxy.y].columns + fieldxy.x + 1 : nullptr;
3161 cargo_type = fld->CargoClickedAt(lft, rgt, xy);
3162 break;
3163 }
3164
3165 case CFT_CARGO_LABEL: {
3166 cargo_type = fld->CargoLabelClickedAt(xy);
3167 break;
3168 }
3169
3170 case CFT_INDUSTRY:
3171 if (fld->u.industry.ind_type < NUM_INDUSTRYTYPES && (this->ind_cargo >= NUM_INDUSTRYTYPES || fieldxy.x != 2)) {
3172 GuiShowTooltips(this, GetEncodedString(STR_INDUSTRY_CARGOES_INDUSTRY_TOOLTIP), close_cond);
3173 }
3174 return true;
3175
3176 default:
3177 break;
3178 }
3179 if (IsValidCargoType(cargo_type) && (this->ind_cargo < NUM_INDUSTRYTYPES || cargo_type != this->ind_cargo - NUM_INDUSTRYTYPES)) {
3180 const CargoSpec *csp = CargoSpec::Get(cargo_type);
3181 GuiShowTooltips(this, GetEncodedString(STR_INDUSTRY_CARGOES_CARGO_TOOLTIP, csp->name), close_cond);
3182 return true;
3183 }
3184
3185 return false;
3186 }
3187
3188 void OnResize() override
3189 {
3190 this->vscroll->SetCapacityFromWidget(this, WID_IC_PANEL, WidgetDimensions::scaled.framerect.Vertical() + CargoesField::small_height);
3191 }
3192};
3193
3198static void ShowIndustryCargoesWindow(IndustryType id)
3199{
3200 if (id >= NUM_INDUSTRYTYPES) {
3201 for (IndustryType ind : _sorted_industry_types) {
3202 const IndustrySpec *indsp = GetIndustrySpec(ind);
3203 if (indsp->enabled) {
3204 id = ind;
3205 break;
3206 }
3207 }
3208 if (id >= NUM_INDUSTRYTYPES) return;
3209 }
3210
3212 if (w != nullptr) {
3213 w->InvalidateData(id);
3214 return;
3215 }
3216 new IndustryCargoesWindow(id);
3217}
3218
Class for backupping variables and making sure they are restored later.
debug_inline constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
debug_inline static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
uint8_t CargoType
Cargo slots to indicate a cargo type within a game.
Definition cargo_type.h:23
bool IsValidCargoType(CargoType cargo)
Test whether cargo type is not INVALID_CARGO.
Definition cargo_type.h:106
Dimension GetLargestCargoIconSize()
Get dimensions of largest cargo icon.
std::span< const CargoSpec * > _sorted_standard_cargo_specs
Standard cargo specifications sorted alphabetically by name.
TownProductionEffect
Town effect when producing cargo.
Definition cargotype.h:35
@ TPE_PASSENGERS
Cargo behaves passenger-like for production.
Definition cargotype.h:37
@ TPE_MAIL
Cargo behaves mail-like for production.
Definition cargotype.h:38
Cheats _cheats
All the cheats.
Definition cheat.cpp:16
Types related to cheating.
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr Timpl & Set()
Set all bits.
constexpr bool Any(const Timpl &other) const
Test if any of the given values are set.
Build (fund or prospect) a new industry,.
IndustryType selected_type
industry corresponding to the above index
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
Dimension legend
Dimension of the legend 'blob'.
void OnInit() override
Notification that the nested widget tree gets initialized.
void SetButtons()
Update status of the fund and display-chain widgets.
static const int MAX_MINWIDTH_LINEHEIGHTS
The largest allowed minimum-width of the window, given in line heights.
std::string GetWidgetString(WidgetID widget, StringID stringid) const override
Get the raw string for a widget.
void OnPlaceObject(Point pt, TileIndex tile) override
The user clicked some place on the map when a tile highlight mode has been set.
void OnPlaceObjectAbort() override
The user cancelled a tile highlight mode that has been set.
std::string MakeCargoListString(const std::span< const CargoType > cargolist, const std::span< const CargoSuffix > cargo_suffix, StringID prefixstr) const
Build a string of cargo names with suffixes attached.
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
void OnTimeout() override
Called when this window's timeout has been reached.
void OnResize() override
Called after the window got resized.
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.
std::vector< IndustryType > list
List of industries.
bool enabled
Availability state of the selected industry.
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
Common return value for all commands.
bool Succeeded() const
Did this command succeed?
StringID GetErrorMessage() const
Returns the error message of a command.
std::string GetDecodedString() const
Decode the encoded string.
Definition strings.cpp:217
uint GetTotalColumnsWidth() const
Get total width of all columns.
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 SetListing(Listing l)
Import sort conditions.
void SetFilterState(bool state)
Enable or disable the filter.
bool IsDescSortOrder() const
Check if the sort order is descending.
void ToggleSortOrder()
Toggle the sort order Since that is the worst condition for the sort function reverse the list here.
bool(const const Industry * *, const std::pair< CargoType, CargoType > &) FilterFunction
Signature of filter function.
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 SetFilterType(uint8_t n_type)
Set the filtertype of the list.
void ForceRebuild()
Force that a rebuild is needed.
bool Sort(Comp compare)
Sort the list.
void ForceResort()
Force a resort next Sort call Reset the resort timer if used too.
uint8_t SortType() const
Get the sorttype of the list.
Listing GetListing() const
Export current sort conditions.
void SetSortFuncs(std::span< SortFunction *const > n_funcs)
Hand the sort function pointers to the GUIList.
void SetSortType(uint8_t n_type)
Set the sorttype of the list.
The list of industries.
IntervalTimer< TimerWindow > rebuild_interval
Rebuild the industry list on a regular interval.
void OnEditboxChanged(WidgetID wid) override
The text in an editbox has been edited.
std::string GetWidgetString(WidgetID widget, StringID stringid) const override
Get the raw string for a widget.
static bool IndustryTypeSorter(const Industry *const &a, const Industry *const &b, const CargoType &filter)
Sort industries by type and name.
static int GetCargoTransportedSortValue(const Industry *i)
Returns value representing industry's transported cargo percentage for industry sorting.
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
const int MAX_FILTER_LENGTH
The max length of the filter, in chars.
EventState OnHotkey(int hotkey) override
A hotkey has been pressed.
void SetCargoFilterArray()
Populate the filter list and set the cargo filter criteria.
void OnResize() override
Called after the window got resized.
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
void SetProducedCargoFilter(CargoType cargo_type)
Set produced cargo filter for the industry list.
static int GetCargoTransportedPercentsIfValid(const Industry::ProducedCargo &p)
Returns percents of cargo transported if industry produces this cargo, else -1.
@ ByName
Sorter type to sort by name.
@ ByTransported
Sorter type to sort by transported percentage.
@ ByProduction
Sorter type to sort by production amount.
@ ByType
Sorter type to sort by type.
std::string GetIndustryString(const Industry *i) const
Get the StringID to draw and set the appropriate DParams.
uint GetIndustryListWidth() const
Get the width needed to draw the longest industry line.
static bool IndustryTransportedCargoSorter(const Industry *const &a, const Industry *const &b, const CargoType &filter)
Sort industries by transported cargo and name.
QueryString industry_editbox
Filter editbox.
StringFilter string_filter
Filter for industries.
static bool IndustryProductionSorter(const Industry *const &a, const Industry *const &b, const CargoType &filter)
Sort industries by production and name.
void BuildSortIndustriesList()
(Re)Build industries list
CargoType accepted_cargo_filter_criteria
Selected accepted cargo filter index.
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.
static bool IndustryNameSorter(const Industry *const &a, const Industry *const &b, const CargoType &)
Sort industries by name.
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
void SetAcceptedCargoFilter(CargoType cargo_type)
Set accepted cargo filter for the industry list.
CargoType produced_cargo_filter_criteria
Selected produced cargo filter index.
void OnDropdownSelect(WidgetID widget, int index) override
A dropdown option associated to this window has been selected.
void OnPaint() override
The window must be repainted.
void OnInit() override
Notification that the nested widget tree gets initialized.
void OnResize() override
Called after the window got resized.
int cheat_line_height
Height of each line for the WID_IV_INFO panel.
int production_offset_y
The offset of the production texts/buttons.
void OnPaint() override
The window must be repainted.
Editability editable
Mode for changing production.
InfoLine clicked_line
The line of the button that has been clicked.
void OnMouseWheel(int wheel) override
The mouse wheel has been turned.
void OnTimeout() override
Called when this window's timeout has been reached.
bool IsNewGRFInspectable() const override
Is the data related to this window NewGRF inspectable?
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
int info_height
Height needed for the WID_IV_INFO panel.
Dimension cargo_icon_size
Largest cargo icon dimension.
void ShowNewGRFInspectWindow() const override
Show the NewGRF inspection window.
std::string GetWidgetString(WidgetID widget, StringID stringid) const override
Get the raw string for a widget.
void OnInit() override
Notification that the nested widget tree gets initialized.
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
uint8_t clicked_button
The button that has been clicked (to raise)
void OnQueryTextFinished(std::optional< std::string > str) override
The query window opened from this window has closed.
InfoLine
Specific lines in the info panel.
@ IL_RATE2
Production rate of cargo 2.
@ IL_MULTIPLIER
Production multiplier.
@ IL_RATE1
Production rate of cargo 1.
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 DrawInfo(const Rect &r)
Draw the text in the WID_IV_INFO panel.
InfoLine editbox_line
The line clicked to open the edit box.
Editability
Modes for changing production.
@ EA_RATE
Allow changing the production rates.
@ EA_MULTIPLIER
Allow changing the production multiplier.
@ EA_NONE
Not alterable.
An interval timer will fire every interval, and will continue to fire until it is deleted.
Definition timer.h:76
Baseclass for nested widgets.
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 resize_y
Vertical resize step (0 means not resizable).
Nested widget to display a viewport in a window.
void UpdateViewportCoordinates(Window *w)
Update the position and size of the viewport (after eg a resize).
Definition widget.cpp:2426
void InitializeViewport(Window *w, std::variant< TileIndex, VehicleID > focus, ZoomLevel zoom)
Initialize the viewport of the window.
Definition widget.cpp:2417
Scrollbar data structure.
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.
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:2523
size_type GetCount() const
Gets the number of elements in the list.
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.
static bool UsingWallclockUnits(bool newgame=false)
Check if we are using wallclock units.
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:29
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:94
int hsep_normal
Normal horizontal spacing.
Definition window_gui.h:61
RectPadding bevel
Bevel thickness, affected by "scaled bevels" game option.
Definition window_gui.h:38
Map accessors for 'clear' tiles.
@ CLEAR_GRASS
0-3
Definition clear_map.h:20
@ CLEAR_FIELDS
3
Definition clear_map.h:23
void MakeClear(Tile t, ClearGround g, uint density)
Make a clear tile.
Definition clear_map.h:247
ClearGround GetClearGround(Tile t)
Get the type of clear tile.
Definition clear_map.h:47
Functions related to commands.
Commands
List of commands.
Definition of stuff that is very close to a company, like the company struct itself.
CompanyID _local_company
Company controlled by the human player at this client. Can also be COMPANY_SPECTATOR.
CompanyID _current_company
Company currently doing an action.
Functions related to companies.
static constexpr Owner OWNER_NONE
The tile has no ownership.
void ShowDropDownMenu(Window *w, std::span< const StringID > strings, int selected, WidgetID button, uint32_t disabled_mask, uint32_t hidden_mask, uint width)
Show a dropdown menu window near a widget of the parent window.
Definition dropdown.cpp:444
void ShowDropDownList(Window *w, DropDownList &&list, int selected, WidgetID button, uint width, bool instant_close, bool persist)
Show a drop down list.
Definition dropdown.cpp:407
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.
uint ScaleByCargoScale(uint num, bool town)
Scale a number by the cargo scale setting.
Functions related to errors.
@ 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.
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
Definition fontcache.cpp:77
bool _generating_world
Whether we are generating the map or not.
Definition genworld.cpp:74
Functions related to world/map generation.
Dimension maxdim(const Dimension &d1, const Dimension &d2)
Compute bounding box of both dimensions.
Geometry functions.
Dimension GetSpriteSize(SpriteID sprid, Point *offset, ZoomLevel zoom)
Get the size of a sprite.
Definition gfx.cpp:923
Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
Return the string dimension in pixels.
Definition gfx.cpp:852
void DrawRectOutline(const Rect &r, int colour, int width, int dash)
Draw the outline of a Rect.
Definition gfx.cpp:457
Dimension GetStringListBoundingBox(std::span< const StringID > list, FontSize fontsize)
Get maximum dimension of a list of strings.
Definition gfx.cpp:890
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:658
bool _ctrl_pressed
Is Ctrl pressed?
Definition gfx.cpp:38
void GfxFillRect(int left, int top, int right, int bottom, int colour, FillRectMode mode)
Applies a certain FillRectMode-operation to a rectangle [left, right] x [top, bottom] on the screen.
Definition gfx.cpp:115
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:989
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:775
bool FillDrawPixelInfo(DrawPixelInfo *n, int left, int top, int width, int height)
Set up a clipping area for only drawing into a certain area.
Definition gfx.cpp:1519
int CenterBounds(int min, int max, int size)
Determine where to draw a centred object inside a widget.
Definition gfx_func.h:166
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
Definition gfx_type.h:17
@ FS_SMALL
Index of the small font in the font tables.
Definition gfx_type.h:246
@ FS_NORMAL
Index of the normal font in the font tables.
Definition gfx_type.h:245
@ SA_LEFT
Left align the text.
Definition gfx_type.h:377
@ SA_RIGHT
Right align the text (must be a single bit).
Definition gfx_type.h:379
@ SA_HOR_CENTER
Horizontally center the text.
Definition gfx_type.h:378
TextColour
Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palett...
Definition gfx_type.h:296
@ TC_FORCED
Ignore colour changes from strings.
Definition gfx_type.h:321
@ FILLRECT_OPAQUE
Fill rectangle with a single colour.
Definition gfx_type.h:334
Graph GUI functions.
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 SetFill(uint16_t fill_x, uint16_t fill_y)
Widget part function for setting filling.
constexpr NWidgetPart SetSpriteTip(SpriteID sprite, StringID tip={})
Widget part function for setting the sprite and tooltip.
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 SetAspect(float ratio, AspectFlags flags=AspectFlag::ResizeX)
Widget part function for setting the aspect ratio.
constexpr NWidgetPart SetMinimalSize(int16_t x, int16_t y)
Widget part function for setting the minimal size.
constexpr NWidgetPart NWidget(WidgetType tp, Colours col, WidgetID idx=-1)
Widget part function for starting a new 'real' widget.
constexpr NWidgetPart 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 SetMinimalTextLines(uint8_t lines, uint8_t spacing, FontSize size=FS_NORMAL)
Widget part function for setting the minimal text lines.
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:945
void MarkWholeScreenDirty()
This function mark the whole screen as dirty.
Definition gfx.cpp:1500
GUI functions that shouldn't be here.
void ShowExtraViewportWindow(TileIndex tile=INVALID_TILE)
Show a new Extra Viewport window.
Hotkey related functions.
HouseZones
Definition house.h:68
@ HZ_SUBARTC_BELOW
13 2000 can appear in sub-arctic climate below the snow line
Definition house.h:78
@ HZ_TEMP
12 1000 can appear in temperate climate
Definition house.h:77
@ HZ_TOYLND
15 8000 can appear in toyland climate
Definition house.h:80
@ HZ_SUBTROPIC
14 4000 can appear in subtropical climate
Definition house.h:79
@ HZ_SUBARTC_ABOVE
11 800 can appear in sub-arctic climate above the snow line
Definition house.h:76
Base of all industries.
static constexpr uint8_t PRODLEVEL_MAXIMUM
the industry is running at full speed
Definition industry.h:36
static constexpr uint8_t PRODLEVEL_DEFAULT
default level set when the industry is created
Definition industry.h:35
static constexpr uint8_t PRODLEVEL_MINIMUM
below this level, the industry is set to be closing
Definition industry.h:34
static constexpr uint8_t PRODLEVEL_CLOSURE
signal set to actually close the industry
Definition industry.h:33
const IndustrySpec * GetIndustrySpec(IndustryType thistype)
Accessor for array _industry_specs.
Command definitions related to industries.
static WindowDesc _build_industry_desc(WDP_AUTO, "build_industry", 170, 212, WC_BUILD_INDUSTRY, WC_NONE, WindowDefaultFlag::Construction, _nested_build_industry_widgets)
Window definition of the dynamic place industries gui.
void ShowIndustryCargoesWindow()
Open the industry and cargoes window with an industry.
static void GetCargoSuffix(uint cargo, CargoSuffixType cst, const Industry *ind, IndustryType ind_type, const IndustrySpec *indspec, CargoSuffix &suffix)
Gets the string to display after the cargo name (using callback 37)
CargoSuffixType
Cargo suffix type (for which window is it requested)
@ CST_DIR
Industry-directory window.
@ CST_FUND
Fund-industry window.
@ CST_VIEW
View-industry window.
static const uint MAX_CARGOES
Maximum number of cargoes carried in a CFT_CARGO field in CargoesField.
static WindowDesc _industry_directory_desc(WDP_AUTO, "list_industries", 428, 190, WC_INDUSTRY_DIRECTORY, WC_NONE, {}, _nested_industry_directory_widgets, &IndustryDirectoryWindow::hotkeys)
Window definition of the industry directory gui.
static WindowDesc _industry_cargoes_desc(WDP_AUTO, "industry_cargoes", 300, 210, WC_INDUSTRY_CARGOES, WC_NONE, {}, _nested_industry_cargoes_widgets)
Window description for the industry cargoes window.
static bool CargoFilter(const Industry *const *industry, const std::pair< CargoType, CargoType > &cargoes)
Cargo filter functions.
static void GetAllCargoSuffixes(CargoSuffixInOut use_input, CargoSuffixType cst, const Industry *ind, IndustryType ind_type, const IndustrySpec *indspec, const TC &cargoes, TS &suffixes)
Gets all strings to display after the cargoes of industries (using callback 37)
static WindowDesc _industry_view_desc(WDP_AUTO, "view_industry", 260, 120, WC_INDUSTRY_VIEW, WC_NONE, {}, _nested_industry_view_widgets)
Window definition of the view industry gui.
void GenerateIndustries()
This function will create random industries during game creation.
static bool IndustryTypeNameSorter(const IndustryType &a, const IndustryType &b)
Sort industry types by their name.
CargoSuffixDisplay
Ways of displaying the cargo.
@ CSD_CARGO_TEXT
Display then cargo and supplied string (cb37 result 800-BFF).
@ CSD_CARGO
Display the cargo without sub-type (cb37 result 401).
@ CSD_CARGO_AMOUNT_TEXT
Display then cargo, amount, and string (cb37 result 000-3FF).
@ CSD_CARGO_AMOUNT
Display the cargo and amount (if useful), but no sub-type (cb37 result 400 or fail).
std::array< IndustryType, NUM_INDUSTRYTYPES > _sorted_industry_types
Industry types sorted by name.
static constexpr NWidgetPart _nested_industry_view_widgets[]
Widget definition of the view industry gui.
std::bitset< NUM_INDUSTRYTYPES > _displayed_industries
Communication from the industry chain window to the smallmap window about what industries to display.
void CcBuildIndustry(Commands, const CommandCost &result, TileIndex tile, IndustryType indtype, uint32_t, bool, uint32_t)
Command callback.
CargoesFieldType
Available types of field.
@ CFT_SMALL_EMPTY
Empty small field (for the header).
@ CFT_EMPTY
Empty field.
@ CFT_INDUSTRY
Display industry.
@ CFT_CARGO
Display cargo connections.
@ CFT_HEADER
Header text.
@ CFT_CARGO_LABEL
Display cargo labels.
static constexpr NWidgetPart _nested_industry_directory_widgets[]
Widget definition of the industry directory gui.
static constexpr NWidgetPart _nested_industry_cargoes_widgets[]
Widgets of the industry cargoes window.
IndustryDirectoryHotkeys
Enum referring to the Hotkeys in the industry directory window.
@ IDHK_FOCUS_FILTER_BOX
Focus the filter box.
void SortIndustryTypes()
Initialize the list of sorted industry types.
static const int INDUSTRY_ORIGINAL_NUM_INPUTS
Original number of accepted cargo types.
static const IndustryType NUM_INDUSTRYTYPES
total number of industry types, new and old; limited to 240 because we need some special ids like IT_...
static const int INDUSTRY_ORIGINAL_NUM_OUTPUTS
Original number of produced cargo types.
Types related to the industry widgets.
@ WID_DPI_MATRIX_WIDGET
Matrix of the industries.
@ WID_DPI_REMOVE_ALL_INDUSTRIES_WIDGET
Remove all industries button.
@ WID_DPI_INFOPANEL
Info panel about the industry.
@ WID_DPI_SCENARIO_EDITOR_PANE
Pane containing SE-only widgets.
@ WID_DPI_SCROLLBAR
Scrollbar of the matrix.
@ WID_DPI_CREATE_RANDOM_INDUSTRIES_WIDGET
Create random industries button.
@ WID_DPI_FUND_WIDGET
Fund button.
@ WID_DPI_DISPLAY_WIDGET
Display chain button.
@ WID_ID_HSCROLLBAR
Horizontal scrollbar of the list.
@ WID_ID_INDUSTRY_LIST
Industry list.
@ WID_ID_DROPDOWN_ORDER
Dropdown for the order of the sort.
@ WID_ID_FILTER_BY_PROD_CARGO
Produced cargo filter dropdown list.
@ WID_ID_DROPDOWN_CRITERIA
Dropdown for the criteria of the sort.
@ WID_ID_CAPTION
Caption of the window.
@ WID_ID_FILTER
Textbox to filter industry name.
@ WID_ID_FILTER_BY_ACC_CARGO
Accepted cargo filter dropdown list.
@ WID_ID_VSCROLLBAR
Vertical scrollbar of the list.
@ WID_IC_SCROLLBAR
Scrollbar of the panel.
@ WID_IC_NOTIFY
Row of buttons at the bottom.
@ WID_IC_IND_DROPDOWN
Select industry dropdown.
@ WID_IC_CAPTION
Caption of the window.
@ WID_IC_PANEL
Panel that shows the chain.
@ WID_IC_CARGO_DROPDOWN
Select cargo dropdown.
@ WID_IV_INFO
Info of the industry.
@ WID_IV_CAPTION
Caption of the window.
@ WID_IV_GRAPH
Production history button.
@ WID_IV_DISPLAY
Display chain button.
@ WID_IV_GOTO
Goto button.
@ WID_IV_VIEWPORT
Viewport of the industry.
@ CargoTypesUnlimited
Allow produced/accepted cargoes callbacks to supply more than 2 and 3 types.
bool DoZoomInOutWindow(ZoomStateChange how, Window *w)
Zooms a viewport in a window in or out.
Definition main_gui.cpp:93
bool HandlePlacePushButton(Window *w, WidgetID widget, CursorID cursor, HighLightStyle mode)
This code is shared for the majority of the pushbuttons.
Definition main_gui.cpp:63
static debug_inline uint TileY(TileIndex tile)
Get the Y component of a tile.
Definition map_func.h:424
static debug_inline uint TileX(TileIndex tile)
Get the X component of a tile.
Definition map_func.h:414
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 int RoundDivSU(int a, uint b)
Computes round(a / b) for signed a and unsigned b.
constexpr uint ToPercent8(uint i)
Converts a "fract" value 0..255 to "percent" value 0..100.
constexpr uint ClampU(const uint a, const uint min, const uint max)
Clamp an unsigned integer between an interval.
void GuiShowTooltips(Window *parent, EncodedString &&text, TooltipCloseCondition close_tooltip)
Shows a tooltip.
Definition misc_gui.cpp:688
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.
static constexpr CargoType CF_NONE
Show only items which do not carry cargo (e.g. train engines)
Definition cargo_type.h:96
static constexpr CargoType CF_ANY
Show all items independent of carried cargo (i.e. no filtering)
Definition cargo_type.h:95
bool _networking
are we in networking mode?
Definition network.cpp:66
Basic functions/variables used all over the place.
int DrawBadgeNameList(Rect r, std::span< const BadgeID > badges, GrfSpecFeature)
Draw names for a list of badge labels.
void DrawBadgeColumn(Rect r, int column_group, const GUIBadgeClasses &badge_classes, std::span< const BadgeID > badges, GrfSpecFeature feature, std::optional< TimerGameCalendar::Date > introduction_date, PaletteID remap)
Draw a badge column group.
Functions related to NewGRF badges.
@ WindowMoreText
additional text in industry window
@ FundMoreText
additional text in fund window
@ Production256Ticks
call production callback every 256 ticks
@ ProductionCargoArrival
call production callback when cargo arrives at the industry
@ CargoSuffix
cargo sub-type display
@ CBID_INDUSTRY_WINDOW_MORE_TEXT
Called to determine more text in the industry window.
@ CBID_INDUSTRY_CARGO_SUFFIX
Called to determine text to display after cargo name.
@ CBID_INDUSTRY_FUND_MORE_TEXT
Called to determine more text in the fund industry window.
static const uint CALLBACK_FAILED
Different values for Callback result evaluations.
void ErrorUnknownCallbackResult(uint32_t grfid, uint16_t cbid, uint16_t cb_res)
Record that a NewGRF returned an unknown/invalid callback result.
Functions/types related to NewGRF debugging.
uint32_t GetIndustryProbabilityCallback(IndustryType type, IndustryAvailabilityCallType creation_type, uint32_t default_prob)
Check with callback CBID_INDUSTRY_PROBABILITY whether the industry can be built.
uint16_t GetIndustryCallback(CallbackID callback, uint32_t param1, uint32_t param2, Industry *industry, IndustryType type, TileIndex tile)
Perform an industry callback.
bool IndustryTemporarilyRefusesCargo(Industry *ind, CargoType cargo_type)
Check whether an industry temporarily refuses to accept a certain cargo.
Functions for NewGRF industries.
@ IACT_USERCREATION
from the Fund/build window
@ PSM_ENTER_GAMELOOP
Enter the gameloop, changes will be permanent.
@ PSM_LEAVE_GAMELOOP
Leave the gameloop, changes will be temporary.
std::string GetGRFStringWithTextStack(const struct GRFFile *grffile, GRFStringID grfstringid, uint8_t num_entries)
Format a GRF string using the text ref stack for parameters.
Header of Action 04 "universal holder" structure and functions.
static constexpr GRFStringID GRFSTR_MISC_GRF_TEXT
Miscellaneous GRF text range.
static const uint8_t PC_WHITE
White palette colour.
static const uint8_t PC_BLACK
Black palette colour.
static const uint8_t PC_YELLOW
Yellow palette colour.
Base for the GUIs that have an edit box in them.
Pseudo random number generator.
A number of safeguards to prevent using unsafe methods.
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition settings.cpp:59
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:58
void DrawArrowButtons(int x, int y, Colours button_colour, uint8_t state, bool clickable_left, bool clickable_right)
Draw [<][>] boxes.
Functions for setting GUIs.
#define SETTING_BUTTON_WIDTH
Width of setting buttons.
#define SETTING_BUTTON_HEIGHT
Height of setting buttons.
@ SWS_OFF
Scroll wheel has no effect.
void ShowSmallMap()
Show the smallmap window.
Smallmap GUI functions.
Base types for having sorted lists in GUIs.
Functions related to sound.
@ SND_15_BEEP
19 == 0x13 GUI button click
Definition sound_type.h:66
Definition of base types and functions in a cross-platform compatible way.
#define lengthof(array)
Return the length of an fixed size array.
Definition stdafx.h:271
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:514
Functions related to low-level strings.
@ CS_ALPHANUMERAL
Both numeric and alphabetic and spaces and stuff.
Definition string_type.h:25
Searching and filtering using a stringterm.
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:91
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:57
uint64_t GetParamMaxDigits(uint count, FontSize size)
Get some number that is suitable for string size computations.
Definition strings.cpp:228
Functions related to OTTD's strings.
auto MakeParameters(Args &&... args)
Helper to create the StringParameters with its own buffer with the given parameter values.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
@ TD_RTL
Text is written right-to-left by default.
static const int MAX_CHAR_LENGTH
Max. length of UTF-8 encoded unicode character.
Class to backup a specific variable and restore it upon destruction of this object to prevent stack v...
Class to backup a specific variable and restore it later.
static void SwitchMode(PersistentStorageMode mode, bool ignore_prev_mode=false)
Clear temporary changes made since the last call to SwitchMode, and set whether subsequent changes sh...
Specification of a cargo type.
Definition cargotype.h:74
static CargoSpec * Get(size_t index)
Retrieve cargo details for the given cargo type.
Definition cargotype.h:137
SpriteID GetCargoIcon() const
Get sprite for showing cargo of this type.
static IterateWrapper Iterate(size_t from=0)
Returns an iterable ensemble of all valid CargoSpec.
Definition cargotype.h:192
StringID name
Name of this type of cargo.
Definition cargotype.h:91
TownProductionEffect town_production_effect
The effect on town cargo production.
Definition cargotype.h:87
Transfer storage of cargo suffix information.
std::string text
Cargo suffix text.
CargoSuffixDisplay display
How to display the cargo and text.
Comparator to sort CargoType by according to desired order.
Definition cargotype.h:244
Data about a single field in the IndustryCargoesWindow panel.
StringID header
Header text (for CFT_HEADER).
std::array< CargoType, MAX_CARGOES > cargoes
Cargoes to display (or INVALID_CARGO).
CargoesFieldType type
Type of field.
static int blob_distance
Distance of the industry legend colour from the edge of the industry box.
static Dimension cargo_border
Dimensions of border between cargo lines and industry boxes.
static const int INDUSTRY_LINE_COLOUR
Line colour of the industry type box.
uint8_t top_end
Stop at the top of the vertical cargoes.
Cargoes cust_cargoes
Cargoes in vertical_cargoes leaving to the right.
int GetCargoBase(int xpos) const
For a CFT_CARGO, compute the left position of the left-most vertical cargo connection.
static const int CARGO_LINE_COLOUR
Line colour around the cargo.
std::array< CargoType, MAX_CARGOES > other_accepted
Cargoes accepted but not used in this figure.
int ConnectCargo(CargoType cargo, bool producer)
Connect a cargo from an industry to the CFT_CARGO column.
static int small_height
Height of the header row.
CargoType CargoLabelClickedAt(Point pt) const
Decide what cargo the user clicked in the cargo label field.
static Dimension cargo_space
Dimensions of space between cargo lines.
static int normal_height
Height of the non-header rows.
static int cargo_field_width
Width of a cargo field.
bool left_align
Align all cargo texts to the left (else align to the right).
static uint max_cargoes
Largest number of cargoes actually on any industry.
bool HasConnection()
Does this CFT_CARGO field have a horizontal connection?
static Dimension cargo_line
Dimensions of cargo lines.
void MakeIndustry(IndustryType ind_type)
Make an industry type field.
static void DrawHorConnection(int left, int right, int top, const CargoSpec *csp)
Draw a horizontal cargo connection.
struct CargoesField::@9::@12 cargo_label
Label data (for CFT_CARGO_LABEL).
IndustryType ind_type
Industry type (NUM_INDUSTRYTYPES means 'houses').
static int vert_inter_industry_space
Amount of space between two industries in a column.
static Dimension cargo_stub
Dimensions of cargo stub (unconnected cargo line.)
static Dimension legend
Dimension of the legend blob.
void MakeCargo(const std::span< const CargoType > cargoes)
Make a piece of cargo column.
Cargoes supp_cargoes
Cargoes in vertical_cargoes entering from the left.
CargoType CargoClickedAt(const CargoesField *left, const CargoesField *right, Point pt) const
Decide which cargo was clicked at in a CFT_CARGO field.
void Draw(int xpos, int ypos) const
Draw the field.
std::array< CargoType, MAX_CARGOES > other_produced
Cargoes produced but not used in this figure.
struct CargoesField::@9::@11 cargo
Cargo data (for CFT_CARGO).
static int industry_width
Width of an industry field.
std::array< CargoType, MAX_CARGOES > vertical_cargoes
Cargoes running from top to bottom (cargo type or INVALID_CARGO).
struct CargoesField::@9::@10 industry
Industry data (for CFT_INDUSTRY).
uint8_t num_cargoes
Number of cargoes.
void MakeHeader(StringID textid)
Make a header above an industry column.
uint8_t bottom_end
Stop at the bottom of the vertical cargoes.
void MakeCargoLabel(const std::span< const CargoType > cargoes, bool left_align)
Make a field displaying cargo type names.
void MakeEmpty(CargoesFieldType type)
Make one of the empty fields (CFT_EMPTY or CFT_SMALL_EMPTY).
A single row of CargoesField.
CargoesField columns[5]
One row of fields.
void ConnectIndustryProduced(int column)
Connect industry production cargoes to the cargo column after it.
void ConnectIndustryAccepted(int column)
Connect industry accepted cargoes to the cargo column before it.
void MakeCargoLabel(int column, bool accepting)
Construct a CFT_CARGO_LABEL field.
bool value
tells if the bool cheat is active or not
Definition cheat_type.h:18
Cheat setup_prod
setup raw-material production in game
Definition cheat_type.h:33
SoundSettings sound
sound effect settings
GUISettings gui
settings related to the GUI
uint8_t raw_industry_construction
type of (raw) industry construction (none, "normal", prospecting)
Dimensions (a width and height) of a rectangle in 2D.
Data about how and where to blit pixels.
Definition gfx_type.h:158
const struct GRFFile * grffile
grf file that introduced this entity
uint32_t grfid
grfid that introduced this entity.
bool HasGrfFile() const
Test if this entity was introduced by NewGRF.
std::array< uint8_t, NUM_CARGO > cargo_map
Inverse cargo translation table (CargoType -> local ID)
Definition newgrf.h:135
bool persistent_buildingtools
keep the building tools active after usage
uint8_t scrollwheel_scrolling
scrolling using the scroll wheel?
LandscapeType landscape
the landscape we're currently in
ConstructionSettings construction
construction of things in-game
GameCreationSettings game_creation
settings used during the creation of a game (map)
List of hotkeys for a window.
Definition hotkeys.h:37
All data for a single hotkey.
Definition hotkeys.h:21
static std::vector< HouseSpec > & Specs()
Get a reference to all HouseSpecs.
Window displaying the cargo connections around an industry (or cargo).
Dimension cargo_textsize
Size to hold any cargo text, as well as STR_INDUSTRY_CARGOES_SELECT_CARGO.
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
std::string GetWidgetString(WidgetID widget, StringID stringid) const override
Get the raw string for a widget.
void OnInit() override
Notification that the nested widget tree gets initialized.
static bool HousesCanSupply(const std::span< const CargoType > cargoes)
Can houses be used to supply one of the cargoes?
void OnDropdownSelect(WidgetID widget, int index) override
A dropdown option associated to this window has been selected.
bool OnTooltip(Point pt, WidgetID widget, TooltipCloseCondition close_cond) override
Event to display a custom tooltip.
Dimension ind_textsize
Size to hold any industry type text, as well as STR_INDUSTRY_CARGOES_SELECT_INDUSTRY.
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
void ShortenCargoColumn(int column, int top, int bottom)
Shorten the cargo column to just the part between industries.
static int CountMatchingAcceptingIndustries(const std::span< const CargoType > cargoes)
Count how many industries have accepted cargoes in common with one of the supplied set.
void PlaceIndustry(int row, int col, IndustryType it)
Place an industry in the fields.
void OnResize() override
Called after the window got resized.
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
Fields fields
Fields to display in the WID_IC_PANEL.
static int CountMatchingProducingIndustries(const std::span< const CargoType > cargoes)
Count how many industries have produced cargoes in common with one of the supplied set.
void ComputeIndustryDisplay(IndustryType displayed_it)
Compute what and where to display for industry type it.
void NotifySmallmap()
Notify smallmap that new displayed industries have been selected (in _displayed_industries).
bool CalculatePositionInWidget(Point pt, Point *fieldxy, Point *xy)
Calculate in which field was clicked, and within the field, at what position.
void ComputeCargoDisplay(CargoType cargo_type)
Compute what and where to display for cargo type cargo_type.
uint ind_cargo
If less than NUM_INDUSTRYTYPES, an industry type, else a cargo type + NUM_INDUSTRYTYPES.
static bool HasCommonValidCargo(const std::span< const CargoType > cargoes1, const std::span< const CargoType > cargoes2)
Do the two sets of cargoes have a valid cargo in common?
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.
static bool HousesCanAccept(const std::span< const CargoType > cargoes)
Can houses be used as customers of the produced cargoes?
Defines the data structure for constructing industry.
IndustryCallbackMasks callback_mask
Bitmask of industry callbacks that have to be called.
std::array< CargoType, INDUSTRY_NUM_INPUTS > accepts_cargo
16 accepted cargoes.
bool UsesOriginalEconomy() const
Determines whether this industrytype uses standard/newgrf production changes.
StringID name
Displayed name of the industry.
bool IsRawIndustry() const
Is an industry with the spec a raw industry?
GRFFileProps grf_prop
properties related to the grf file
IndustryBehaviours behaviour
How this industry will behave, and how others entities can use it.
std::vector< IndustryTileLayout > layouts
List of possible tile layouts for the industry.
bool enabled
entity still available (by default true).newgrf can disable it, though
Money GetConstructionCost() const
Get the cost for constructing this industry.
uint8_t map_colour
colour used for the small map
CargoType cargo
Cargo type.
Definition industry.h:83
uint16_t waiting
Amount of cargo waiting to processed.
Definition industry.h:84
std::array< ProducedHistory, 25 > history
History of cargo produced and transported for this month and 24 previous months.
Definition industry.h:79
CargoType cargo
Cargo type.
Definition industry.h:76
Defines the internal data of a functional industry.
Definition industry.h:63
IndustryType type
type of industry.
Definition industry.h:99
uint8_t prod_level
general production level
Definition industry.h:96
void RecomputeProductionMultipliers()
Recompute #production_rate for current prod_level.
EncodedString text
General text with additional information.
Definition industry.h:116
ProducedCargoes::iterator GetCargoProduced(CargoType cargo)
Get produced cargo slot for a specific cargo type.
Definition industry.h:164
ProducedCargoes produced
produced cargo slots
Definition industry.h:94
AcceptedCargoes accepted
accepted cargo slots
Definition industry.h:95
static uint16_t GetIndustryTypeCount(IndustryType type)
Get the count of industries for this type.
Definition industry.h:248
TileArea location
Location of the industry.
Definition industry.h:91
bool IsCargoProduced() const
Test if this industry produces any cargo.
Definition industry.h:213
Data structure describing how to show the list (what sort direction and criteria).
Size related data of the map.
Definition map_func.h:206
Partial widget specification to allow NWidgets to be written nested.
TileIndex GetCenterTile() const
Get the center tile.
TileIndex tile
The base tile of the area.
Coordinates of a point in 2D.
Templated helper to make a PoolID a single POD value.
Definition pool_type.hpp:43
static Pool::IterateWrapper< Titem > Iterate(size_t from=0)
Returns an iterable ensemble of all valid Titem.
static Titem * Get(auto index)
Returns Titem with given index.
static size_t GetNumItems()
Returns number of valid items in the pool.
Tindex index
Index of this pool item.
static bool IsValidID(auto index)
Tests whether given index can be used to get valid (non-nullptr) Titem.
Data stored about a string that can be modified in the GUI.
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.
Rect WithWidth(int width, bool end) const
Copy Rect and set its width.
Rect Shrink(int s) const
Copy and shrink Rect by s pixels.
Rect WithHeight(int height, bool end=false) const
Copy Rect and set its height.
Rect Indent(int indent, bool end) const
Copy Rect and indent it from its position.
Rect Translate(int x, int y) const
Copy and translate Rect by x,y pixels.
bool Contains(const Point &pt) const
Test if a point falls inside this Rect.
uint step_height
Step-size of height resize changes.
Definition window_gui.h:213
bool click_beep
Beep on a random selection of buttons.
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.
const char * GetText() const
Get the current text.
Definition textbuf.cpp:284
Window * GetCallbackWnd()
Get the window that started the current highlighting.
High level window description.
Definition window_gui.h:168
Number to differentiate different windows of the same class.
Data structure for an opened window.
Definition window_gui.h:274
void ReInit(int rx=0, int ry=0, bool reposition=false)
Re-initialize a window, and optionally change its size.
Definition window.cpp:957
static int SortButtonWidth()
Get width of up/down arrow of sort button state.
Definition widget.cpp:793
void FinishInitNested(WindowNumber window_number=0)
Perform the second part of the initialization of a nested widget tree.
Definition window.cpp:1736
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:744
void InvalidateData(int data=0, bool gui_scope=true)
Mark this window's data as invalid (in need of re-computing)
Definition window.cpp:3164
void SetWidgetDirty(WidgetID widget_index) const
Invalidate a widget, i.e.
Definition window.cpp:556
std::unique_ptr< ViewportData > viewport
Pointer to viewport data, if present.
Definition window_gui.h:319
virtual std::string GetWidgetString(WidgetID widget, StringID stringid) const
Get the raw string for a widget.
Definition window.cpp:504
void RaiseWidgetWhenLowered(WidgetID widget_index)
Marks a widget as raised and dirty (redraw), when it is marked as lowered.
Definition window_gui.h:479
void DrawSortButtonState(WidgetID widget, SortButtonState state) const
Draw a sort button's up or down arrow symbol.
Definition widget.cpp:776
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 CreateNestedTree()
Perform the first part of the initialization of a nested widget tree.
Definition window.cpp:1726
bool SetFocusedWidget(WidgetID widget_index)
Set focus within this window to the given widget.
Definition window.cpp:485
bool IsWidgetLowered(WidgetID widget_index) const
Gets the lowered state of a widget.
Definition window_gui.h:492
void RaiseButtons(bool autoraise=false)
Raise the buttons of the window.
Definition window.cpp:530
bool IsShaded() const
Is window shaded currently?
Definition window_gui.h:558
void SetTimeout()
Set the timeout flag of the window and initiate the timer.
Definition window_gui.h:356
int top
y position of top edge of the window
Definition window_gui.h:311
void HandleButtonClick(WidgetID widget)
Do all things to make a button look clicked and mark it to be unclicked in a few ticks.
Definition window.cpp:595
void InitNested(WindowNumber number=0)
Perform complete initialization of the Window with nested widgets, to allow use.
Definition window.cpp:1749
WindowFlags flags
Window flags.
Definition window_gui.h:301
const Scrollbar * GetScrollbar(WidgetID widnum) const
Return the Scrollbar to a widget index.
Definition window.cpp:313
void SetWidgetDisabledState(WidgetID widget_index, bool disab_stat)
Sets the enabled/disabled status of a widget.
Definition window_gui.h:382
AllWindows< false > Iterate
Iterate all windows in whatever order is easiest.
Definition window_gui.h:923
int height
Height of the window (number of pixels down in y direction)
Definition window_gui.h:313
int width
width of the window (number of pixels to the right in x direction)
Definition window_gui.h:312
void ToggleWidgetLoweredState(WidgetID widget_index)
Invert the lowered/raised status of a widget.
Definition window_gui.h:451
WindowNumber window_number
Window number within the window class.
Definition window_gui.h:303
Stuff related to the text buffer GUI.
static debug_inline bool IsTileType(Tile tile, TileType type)
Checks if a tile is a given tiletype.
Definition tile_map.h:150
static const uint TILE_SIZE
Tile size in world coordinates.
Definition tile_type.h:15
constexpr TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition tile_type.h:95
@ MP_CLEAR
A tile without any structures, i.e. grass, rocks, farm fields etc.
Definition tile_type.h:48
Functions related to tile highlights.
void ResetObjectToPlace()
Reset the cursor and mouse mode handling back to default (normal cursor, only clicking in windows).
@ HT_RECT
rectangle (stations, depots, ...)
Definition of Interval and OneShot timers.
Definition of the Window system.
Base of the town class.
bool ScrollWindowToTile(TileIndex tile, Window *w, bool instant)
Scrolls the viewport in a window to a given location.
bool ScrollMainWindowToTile(TileIndex tile, bool instant)
Scrolls the viewport of the main window to a given location.
Functions related to (drawing on) viewports.
@ ZOOM_IN
Zoom in (get more detailed view).
@ ZOOM_OUT
Zoom out (get helicopter view).
Rect ScrollRect(Rect r, const Scrollbar &sb, int resize_step)
Apply 'scroll' to a rect to be drawn in.
Definition widget.cpp:2540
static RectPadding ScaleGUITrad(const RectPadding &r)
Scale a RectPadding to GUI zoom level.
Definition widget.cpp:48
@ 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:41
@ WWT_PUSHIMGBTN
Normal push-button (no toggle button) with image caption.
@ WWT_EDITBOX
a textbox for typing
Definition widget_type.h:61
@ NWID_HORIZONTAL
Horizontal container.
Definition widget_type.h:65
@ WWT_TEXTBTN
(Toggle) Button with text
Definition widget_type.h:45
@ WWT_PANEL
Simple depressed panel.
Definition widget_type.h:40
@ WWT_STICKYBOX
Sticky box (at top-right of a window, after WWT_DEFSIZEBOX)
Definition widget_type.h:56
@ WWT_MATRIX
Grid of rows and columns.
Definition widget_type.h:49
@ WWT_SHADEBOX
Shade box (at top-right of a window, between WWT_DEBUGBOX and WWT_DEFSIZEBOX)
Definition widget_type.h:54
@ WWT_CAPTION
Window caption (window title between closebox and stickybox)
Definition widget_type.h:51
@ NWID_VSCROLLBAR
Vertical scrollbar.
Definition widget_type.h:75
@ NWID_VERTICAL
Vertical container.
Definition widget_type.h:67
@ WWT_CLOSEBOX
Close box (at top-left of a window)
Definition widget_type.h:59
@ NWID_HSCROLLBAR
Horizontal scrollbar.
Definition widget_type.h:74
@ WWT_RESIZEBOX
Resize box (normally at bottom-right of a window)
Definition widget_type.h:58
@ WWT_DEFSIZEBOX
Default window size box (at top-right of a window, between WWT_SHADEBOX and WWT_STICKYBOX)
Definition widget_type.h:55
@ NWID_VIEWPORT
Nested widget containing a viewport.
Definition widget_type.h:72
@ WWT_DROPDOWN
Drop down list.
Definition widget_type.h:60
@ WWT_DEBUGBOX
NewGRF debug box (at top-right of a window, between WWT_CAPTION and WWT_SHADEBOX)
Definition widget_type.h:53
@ NWID_SELECTION
Stacked widgets, only one visible at a time (eg in a panel with tabs).
Definition widget_type.h:70
@ SZSP_HORIZONTAL
Display plane with zero size vertically, and filling and resizing horizontally.
void CloseWindowById(WindowClass cls, WindowNumber number, bool force, int data)
Close a window by its class and window number (if it is open).
Definition window.cpp:1143
void SetFocusedWindow(Window *w)
Set the window that has the focus.
Definition window.cpp:421
Window * FindWindowByClass(WindowClass cls)
Find any window by its class.
Definition window.cpp:1116
Window * BringWindowToFrontById(WindowClass cls, WindowNumber number)
Find a window and make it the relative top-window on the screen.
Definition window.cpp:1226
void InvalidateWindowClassesData(WindowClass cls, int data, bool gui_scope)
Mark window data of all windows of a given class as invalid (in need of re-computing) Note that by de...
Definition window.cpp:3241
Window functions not directly related to making/drawing windows.
@ Construction
This window is used for construction; close it whenever changing company.
@ DisableVpScroll
Window does not do autoscroll,.
@ SBS_DOWN
Sort ascending.
Definition window_gui.h:219
@ SBS_UP
Sort descending.
Definition window_gui.h:220
@ WDP_AUTO
Find a place automatically.
Definition window_gui.h:145
int WidgetID
Widget ID.
Definition window_type.h:20
EventState
State of handling an event.
@ ES_HANDLED
The passed event is handled.
@ ES_NOT_HANDLED
The passed event is not handled.
@ WC_INDUSTRY_DIRECTORY
Industry directory; Window numbers:
@ WC_NONE
No window, redirects to WC_MAIN_WINDOW.
Definition window_type.h:47
@ WC_INDUSTRY_CARGOES
Industry cargoes chain; Window numbers:
@ WC_INDUSTRY_PRODUCTION
Industry production history graph; Window numbers:
@ WC_INDUSTRY_VIEW
Industry view; Window numbers:
@ WC_SMALLMAP
Small map; Window numbers:
@ WC_BUILD_INDUSTRY
Build industry; Window numbers:
Functions related to zooming.
ZoomLevel ScaleZoomGUI(ZoomLevel value)
Scale zoom level relative to GUI zoom.
Definition zoom_func.h:87
@ ZOOM_LVL_INDUSTRY
Default zoom level for the industry view.
Definition zoom_type.h:30