OpenTTD Source 20260531-master-g0e951f3528
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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
9
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 "cargo_type.h"
23#include "cheat_type.h"
24#include "newgrf_badge.h"
25#include "newgrf_badge_gui.h"
26#include "newgrf_industries.h"
27#include "newgrf_text.h"
28#include "newgrf_debug.h"
29#include "network/network.h"
30#include "strings_func.h"
31#include "company_func.h"
32#include "tilehighlight_func.h"
33#include "string_func.h"
34#include "sortlist_type.h"
35#include "dropdown_func.h"
36#include "company_base.h"
38#include "core/random_func.hpp"
39#include "core/backup_type.hpp"
40#include "genworld.h"
41#include "smallmap_gui.h"
42#include "dropdown_type.h"
43#include "clear_map.h"
44#include "zoom_func.h"
45#include "industry_cmd.h"
46#include "graph_gui.h"
47#include "querystring_gui.h"
48#include "stringfilter_type.h"
49#include "timer/timer.h"
50#include "timer/timer_window.h"
51#include "hotkeys.h"
53
55
56#include "table/strings.h"
57
58#include <bitset>
59
60#include "safeguards.h"
61
62bool _ignore_industry_restrictions;
63std::bitset<NUM_INDUSTRYTYPES> _displayed_industries;
64
66enum class CargoSuffixType : uint8_t {
67 Fund = 0,
68 View = 1,
70};
71
79
85
86extern void GenerateIndustries();
87static void ShowIndustryCargoesWindow(IndustryType id);
88
98static void GetCargoSuffix(uint cargo, CargoSuffixType cst, const Industry *ind, IndustryType ind_type, const IndustrySpec *indspec, CargoSuffix &suffix)
99{
100 suffix.text.clear();
102
105 std::array<int32_t, 16> regs100;
106 uint16_t callback = GetIndustryCallback(CBID_INDUSTRY_CARGO_SUFFIX, 0, (to_underlying(cst) << 8) | cargo, const_cast<Industry *>(ind), ind_type, t, regs100);
107 if (callback == CALLBACK_FAILED) return;
108
109 if (indspec->grf_prop.grffile->grf_version < 8) {
110 if (GB(callback, 0, 8) == 0xFF) return;
111 if (callback < 0x400) {
112 suffix.text = GetGRFStringWithTextStack(indspec->grf_prop.grffile, GRFSTR_MISC_GRF_TEXT + callback, regs100);
114 return;
115 }
117 return;
118
119 } else { // GRF version 8 or higher.
120 switch (callback) {
121 case 0x400:
122 return;
123 case 0x401:
125 return;
126 case 0x40E:
128 suffix.text = GetGRFStringWithTextStack(indspec->grf_prop.grffile, static_cast<GRFStringID>(regs100[0]), std::span{regs100}.subspan(1));
129 return;
130 case 0x40F:
132 suffix.text = GetGRFStringWithTextStack(indspec->grf_prop.grffile, static_cast<GRFStringID>(regs100[0]), std::span{regs100}.subspan(1));
133 return;
134 default:
135 break;
136 }
137 if (callback < 0x400) {
138 suffix.text = GetGRFStringWithTextStack(indspec->grf_prop.grffile, GRFSTR_MISC_GRF_TEXT + callback, regs100);
140 return;
141 }
142 if (callback >= 0x800 && callback < 0xC00) {
143 suffix.text = GetGRFStringWithTextStack(indspec->grf_prop.grffile, GRFSTR_MISC_GRF_TEXT + callback - 0x800, regs100);
145 return;
146 }
148 return;
149 }
150 }
151}
152
154enum class CargoSuffixDirection : uint8_t {
155 Out = 0,
156 In = 1,
157};
158
169template <typename TC, typename TS>
170static inline void GetAllCargoSuffixes(CargoSuffixDirection use_input, CargoSuffixType cst, const Industry *ind, IndustryType ind_type, const IndustrySpec *indspec, const TC &cargoes, TS &suffixes)
171{
172 static_assert(std::tuple_size_v<std::remove_reference_t<decltype(cargoes)>> <= lengthof(suffixes));
173
175 /* Reworked behaviour with new many-in-many-out scheme */
176 for (uint j = 0; j < lengthof(suffixes); j++) {
177 if (IsValidCargoType(cargoes[j])) {
178 uint8_t local_id = indspec->grf_prop.grffile->cargo_map[cargoes[j]]; // should we check the value for valid?
179 uint cargotype = local_id << 16 | to_underlying(use_input);
180 GetCargoSuffix(cargotype, cst, ind, ind_type, indspec, suffixes[j]);
181 } else {
182 suffixes[j].text.clear();
183 suffixes[j].display = CargoSuffixDisplay::None;
184 }
185 }
186 } else {
187 /* Compatible behaviour with old 3-in-2-out scheme */
188 for (uint j = 0; j < lengthof(suffixes); j++) {
189 suffixes[j].text.clear();
190 suffixes[j].display = CargoSuffixDisplay::None;
191 }
192 switch (use_input) {
194 /* Handle INDUSTRY_ORIGINAL_NUM_OUTPUTS cargoes */
195 if (IsValidCargoType(cargoes[0])) GetCargoSuffix(3, cst, ind, ind_type, indspec, suffixes[0]);
196 if (IsValidCargoType(cargoes[1])) GetCargoSuffix(4, cst, ind, ind_type, indspec, suffixes[1]);
197 break;
199 /* Handle INDUSTRY_ORIGINAL_NUM_INPUTS cargoes */
200 if (IsValidCargoType(cargoes[0])) GetCargoSuffix(0, cst, ind, ind_type, indspec, suffixes[0]);
201 if (IsValidCargoType(cargoes[1])) GetCargoSuffix(1, cst, ind, ind_type, indspec, suffixes[1]);
202 if (IsValidCargoType(cargoes[2])) GetCargoSuffix(2, cst, ind, ind_type, indspec, suffixes[2]);
203 break;
204 default:
205 NOT_REACHED();
206 }
207 }
208}
209
221void GetCargoSuffix(CargoSuffixDirection use_input, CargoSuffixType cst, const Industry *ind, IndustryType ind_type, const IndustrySpec *indspec, CargoType cargo, uint8_t slot, CargoSuffix &suffix)
222{
223 suffix.text.clear();
225 if (!IsValidCargoType(cargo)) return;
227 uint8_t local_id = indspec->grf_prop.grffile->cargo_map[cargo]; // should we check the value for valid?
228 uint cargotype = local_id << 16 | to_underlying(use_input);
229 GetCargoSuffix(cargotype, cst, ind, ind_type, indspec, suffix);
230 } else if (use_input == CargoSuffixDirection::In) {
231 if (slot < INDUSTRY_ORIGINAL_NUM_INPUTS) GetCargoSuffix(slot, cst, ind, ind_type, indspec, suffix);
232 } else if (use_input == CargoSuffixDirection::Out) {
233 if (slot < INDUSTRY_ORIGINAL_NUM_OUTPUTS) GetCargoSuffix(slot + INDUSTRY_ORIGINAL_NUM_INPUTS, cst, ind, ind_type, indspec, suffix);
234 }
235}
236
237std::array<IndustryType, NUM_INDUSTRYTYPES> _sorted_industry_types;
238
240static bool IndustryTypeNameSorter(const IndustryType &a, const IndustryType &b)
241{
242 int r = StrNaturalCompare(GetString(GetIndustrySpec(a)->name), GetString(GetIndustrySpec(b)->name)); // Sort by name (natural sorting).
243
244 /* If the names are equal, sort by industry type. */
245 return (r != 0) ? r < 0 : (a < b);
246}
247
252{
253 /* Add each industry type to the list. */
254 for (IndustryType i = 0; i < NUM_INDUSTRYTYPES; i++) {
256 }
257
258 /* Sort industry types by name. */
260}
261
262static constexpr std::initializer_list<NWidgetPart> _nested_build_industry_widgets = {
265 NWidget(WWT_CAPTION, Colours::DarkGreen), SetStringTip(STR_FUND_INDUSTRY_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
269 EndContainer(),
273 SetStringTip(STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES, STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_TOOLTIP),
275 SetStringTip(STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES, STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES_TOOLTIP),
276 EndContainer(),
277 EndContainer(),
281 EndContainer(),
283 EndContainer(),
286 SetStringTip(STR_INDUSTRY_DISPLAY_CHAIN, STR_INDUSTRY_DISPLAY_CHAIN_TOOLTIP),
289 EndContainer(),
290};
291
294 WindowPosition::Automatic, "build_industry", 170, 212,
295 WindowClass::BuildIndustry, WindowClass::None,
297 _nested_build_industry_widgets
298);
299
301class BuildIndustryWindow : public Window {
302 IndustryType selected_type = IT_INVALID;
303 std::vector<IndustryType> list{};
304 bool enabled = false;
305 Scrollbar *vscroll = nullptr;
307 GUIBadgeClasses badge_classes{};
308
310 static const int MAX_MINWIDTH_LINEHEIGHTS = 20;
311
312 void UpdateAvailability()
313 {
314 this->enabled = this->selected_type != IT_INVALID && (_game_mode == GameMode::Editor || GetIndustryProbabilityCallback(this->selected_type, IndustryAvailabilityCallType::UserCreation, 1) > 0);
315 }
316
317 void SetupArrays()
318 {
319 this->list.clear();
320
321 /* Fill the arrays with industries.
322 * The tests performed after the enabled allow to load the industries
323 * In the same way they are inserted by grf (if any)
324 */
325 for (IndustryType ind : _sorted_industry_types) {
326 const IndustrySpec *indsp = GetIndustrySpec(ind);
327 if (indsp->enabled) {
328 /* Rule is that editor mode loads all industries.
329 * In game mode, all non raw industries are loaded too
330 * and raw ones are loaded only when setting allows it */
331 if (_game_mode != GameMode::Editor && indsp->IsRawIndustry() && _settings_game.construction.raw_industry_construction == 0) {
332 /* Unselect if the industry is no longer in the list */
333 if (this->selected_type == ind) this->selected_type = IT_INVALID;
334 continue;
335 }
336
337 this->list.push_back(ind);
338 }
339 }
340
341 /* First industry type is selected if the current selection is invalid. */
342 if (this->selected_type == IT_INVALID && !this->list.empty()) this->selected_type = this->list[0];
343
344 this->UpdateAvailability();
345
346 this->vscroll->SetCount(this->list.size());
347 }
348
351 {
352 this->SetWidgetDisabledState(WID_DPI_FUND_WIDGET, this->selected_type != IT_INVALID && !this->enabled);
353 this->SetWidgetDisabledState(WID_DPI_DISPLAY_WIDGET, this->selected_type == IT_INVALID && this->enabled);
354 }
355
367 std::string MakeCargoListString(const std::span<const CargoType> cargolist, const std::span<const CargoSuffix> cargo_suffix, StringID prefixstr) const
368 {
369 assert(cargolist.size() == cargo_suffix.size());
370
371 std::string cargostring;
372 std::string_view list_separator = GetListSeparator();
373
374 for (size_t j = 0; j < cargolist.size(); j++) {
375 if (!IsValidCargoType(cargolist[j])) continue;
376
377 if (!cargostring.empty()) cargostring += list_separator;
378 auto params = MakeParameters(CargoSpec::Get(cargolist[j])->name, cargo_suffix[j].text);
379 AppendStringWithArgsInPlace(cargostring, STR_INDUSTRY_VIEW_CARGO_LIST_EXTENSION, params);
380 }
381
382 if (cargostring.empty()) AppendStringInPlace(cargostring, STR_JUST_NOTHING);
383 return GetString(prefixstr, cargostring);
384 }
385
386public:
388 {
389 this->CreateNestedTree();
390 this->vscroll = this->GetScrollbar(WID_DPI_SCROLLBAR);
391 /* Show scenario editor tools in editor. */
392 if (_game_mode != GameMode::Editor) {
394 }
395 this->FinishInitNested(0);
396
397 this->SetButtons();
398 }
399
400 void OnInit() override
401 {
402 this->badge_classes = GUIBadgeClasses{GrfSpecFeature::Industries};
403
404 /* Width of the legend blob -- slightly larger than the smallmap legend blob. */
405 this->legend.height = GetCharacterHeight(FontSize::Small);
406 this->legend.width = this->legend.height * 9 / 6;
407
408 this->SetupArrays();
409 }
410
411 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
412 {
413 switch (widget) {
416 Dimension d{};
417 for (const auto &indtype : this->list) {
418 d = maxdim(d, GetStringBoundingBox(GetIndustrySpec(indtype)->name));
419 }
420 fill.height = resize.height = std::max<uint>({this->legend.height, d.height, count.height}) + padding.height;
421 d.width += this->badge_classes.GetTotalColumnsWidth() + this->legend.width + WidgetDimensions::scaled.hsep_wide + WidgetDimensions::scaled.hsep_normal + count.width + padding.width;
422 d.height = 5 * resize.height;
423 size = maxdim(size, d);
424 break;
425 }
426
427 case WID_DPI_INFOPANEL: {
428 /* Extra line for cost outside of editor. */
429 int height = 2 + (_game_mode == GameMode::Editor ? 0 : 1);
430 uint extra_lines_req = 0;
431 uint extra_lines_prd = 0;
432 uint extra_lines_newgrf = 0;
434 Dimension d = {0, 0};
435 for (const auto &indtype : this->list) {
436 const IndustrySpec *indsp = GetIndustrySpec(indtype);
437 CargoSuffix cargo_suffix[std::tuple_size_v<decltype(indsp->accepts_cargo)>];
438
439 /* Measure the accepted cargoes, if any. */
440 GetAllCargoSuffixes(CargoSuffixDirection::In, CargoSuffixType::Fund, nullptr, indtype, indsp, indsp->accepts_cargo, cargo_suffix);
441 std::string cargostring = this->MakeCargoListString(indsp->accepts_cargo, cargo_suffix, STR_INDUSTRY_VIEW_REQUIRES_N_CARGO);
442 Dimension strdim = GetStringBoundingBox(cargostring);
443 if (strdim.width > max_minwidth) {
444 extra_lines_req = std::max(extra_lines_req, strdim.width / max_minwidth + 1);
445 strdim.width = max_minwidth;
446 }
447 d = maxdim(d, strdim);
448
449 /* Measure the produced cargoes, if any. */
450 GetAllCargoSuffixes(CargoSuffixDirection::Out, CargoSuffixType::Fund, nullptr, indtype, indsp, indsp->produced_cargo, cargo_suffix);
451 cargostring = this->MakeCargoListString(indsp->produced_cargo, cargo_suffix, STR_INDUSTRY_VIEW_PRODUCES_N_CARGO);
452 strdim = GetStringBoundingBox(cargostring);
453 if (strdim.width > max_minwidth) {
454 extra_lines_prd = std::max(extra_lines_prd, strdim.width / max_minwidth + 1);
455 strdim.width = max_minwidth;
456 }
457 d = maxdim(d, strdim);
458
459 if (indsp->grf_prop.HasGrfFile()) {
460 /* Reserve a few extra lines for text from an industry NewGRF. */
461 extra_lines_newgrf = 4;
462 }
463 }
464
465 /* Set it to something more sane :) */
466 height += extra_lines_prd + extra_lines_req + extra_lines_newgrf;
467 size.height = height * GetCharacterHeight(FontSize::Normal) + padding.height;
468 size.width = d.width + padding.width;
469 break;
470 }
471
472 case WID_DPI_FUND_WIDGET: {
473 Dimension d = GetStringBoundingBox(STR_FUND_INDUSTRY_BUILD_NEW_INDUSTRY);
474 d = maxdim(d, GetStringBoundingBox(STR_FUND_INDUSTRY_PROSPECT_NEW_INDUSTRY));
475 d = maxdim(d, GetStringBoundingBox(STR_FUND_INDUSTRY_FUND_NEW_INDUSTRY));
476 d.width += padding.width;
477 d.height += padding.height;
478 size = maxdim(size, d);
479 break;
480 }
481 }
482 }
483
484 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
485 {
486 switch (widget) {
488 /* Raw industries might be prospected. Show this fact by changing the string
489 * In Editor, you just build, while ingame, or you fund or you prospect */
490 if (_game_mode == GameMode::Editor) {
491 /* We've chosen many random industries but no industries have been specified */
492 return GetString(STR_FUND_INDUSTRY_BUILD_NEW_INDUSTRY);
493 }
494 if (this->selected_type != IT_INVALID) {
495 const IndustrySpec *indsp = GetIndustrySpec(this->selected_type);
496 return GetString((_settings_game.construction.raw_industry_construction == 2 && indsp->IsRawIndustry()) ? STR_FUND_INDUSTRY_PROSPECT_NEW_INDUSTRY : STR_FUND_INDUSTRY_FUND_NEW_INDUSTRY);
497 }
498 return GetString(STR_FUND_INDUSTRY_FUND_NEW_INDUSTRY);
499
500 default:
501 return this->Window::GetWidgetString(widget, stringid);
502 }
503 }
504
505 void DrawWidget(const Rect &r, WidgetID widget) const override
506 {
507 switch (widget) {
509 bool rtl = _current_text_dir == TD_RTL;
510 Rect text = r.WithHeight(this->resize.step_height).Shrink(WidgetDimensions::scaled.matrix);
511 Rect icon = text.WithWidth(this->legend.width, rtl);
512 text = text.Indent(this->legend.width + WidgetDimensions::scaled.hsep_wide, rtl);
513
514 /* Vertical offset for legend icon. */
515 icon.top = r.top + (this->resize.step_height - this->legend.height + 1) / 2;
516 icon.bottom = icon.top + this->legend.height - 1;
517
518 auto badge_column_widths = badge_classes.GetColumnWidths();
519
520 auto [first, last] = this->vscroll->GetVisibleRangeIterators(this->list);
521 for (auto it = first; it != last; ++it) {
522 IndustryType type = *it;
523 bool selected = this->selected_type == type;
524 const IndustrySpec *indsp = GetIndustrySpec(type);
525
526 Rect tr = text;
527 if (badge_column_widths.size() >= 1 && badge_column_widths[0] > 0) {
528 DrawBadgeColumn(tr.WithWidth(badge_column_widths[0], rtl), 0, this->badge_classes, indsp->badges, GrfSpecFeature::Industries, std::nullopt, PAL_NONE);
529 tr = tr.Indent(badge_column_widths[0], rtl);
530 }
531 if (badge_column_widths.size() >= 2 && badge_column_widths[1] > 0) {
532 DrawBadgeColumn(tr.WithWidth(badge_column_widths[1], !rtl), 0, this->badge_classes, indsp->badges, GrfSpecFeature::Industries, std::nullopt, PAL_NONE);
533 tr = tr.Indent(badge_column_widths[1], !rtl);
534 }
535
536 /* Draw the name of the industry in white is selected, otherwise, in orange */
537 DrawString(tr, indsp->name, selected ? TextColour::White : TextColour::Orange);
538 GfxFillRect(icon, selected ? PC_WHITE : PC_BLACK);
541
542 text = text.Translate(0, this->resize.step_height);
543 icon = icon.Translate(0, this->resize.step_height);
544 }
545 break;
546 }
547
548 case WID_DPI_INFOPANEL: {
549 Rect ir = r.Shrink(WidgetDimensions::scaled.framerect);
550
551 if (this->selected_type == IT_INVALID) {
552 DrawStringMultiLine(ir, STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_TOOLTIP);
553 break;
554 }
555
556 const IndustrySpec *indsp = GetIndustrySpec(this->selected_type);
557
558 if (_game_mode != GameMode::Editor) {
559 DrawString(ir, GetString(STR_FUND_INDUSTRY_INDUSTRY_BUILD_COST, indsp->GetConstructionCost()));
561 }
562
563 CargoSuffix cargo_suffix[std::tuple_size_v<decltype(indsp->accepts_cargo)>];
564
565 /* Draw the accepted cargoes, if any. Otherwise, will print "Nothing". */
566 GetAllCargoSuffixes(CargoSuffixDirection::In, CargoSuffixType::Fund, nullptr, this->selected_type, indsp, indsp->accepts_cargo, cargo_suffix);
567 std::string cargostring = this->MakeCargoListString(indsp->accepts_cargo, cargo_suffix, STR_INDUSTRY_VIEW_REQUIRES_N_CARGO);
568 ir.top = DrawStringMultiLine(ir, cargostring);
569
570 /* Draw the produced cargoes, if any. Otherwise, will print "Nothing". */
571 GetAllCargoSuffixes(CargoSuffixDirection::Out, CargoSuffixType::Fund, nullptr, this->selected_type, indsp, indsp->produced_cargo, cargo_suffix);
572 cargostring = this->MakeCargoListString(indsp->produced_cargo, cargo_suffix, STR_INDUSTRY_VIEW_PRODUCES_N_CARGO);
573 ir.top = DrawStringMultiLine(ir, cargostring);
574
575 ir.top = DrawBadgeNameList(ir, indsp->badges, GrfSpecFeature::Industries);
576
577 /* Get the additional purchase info text, if it has not already been queried. */
579 std::array<int32_t, 16> regs100;
580 uint16_t callback_res = GetIndustryCallback(CBID_INDUSTRY_FUND_MORE_TEXT, 0, 0, nullptr, this->selected_type, INVALID_TILE, regs100);
581 if (callback_res != CALLBACK_FAILED && callback_res != 0x400) {
582 std::string str;
583 if (callback_res == 0x40F) {
584 str = GetGRFStringWithTextStack(indsp->grf_prop.grffile, static_cast<GRFStringID>(regs100[0]), std::span{regs100}.subspan(1));
585 } else if (callback_res > 0x400) {
587 } else {
588 str = GetGRFStringWithTextStack(indsp->grf_prop.grffile, GRFSTR_MISC_GRF_TEXT + callback_res, regs100);
589 }
590 if (!str.empty()) {
592 }
593 }
594 }
595 break;
596 }
597 }
598 }
599
600 static void AskManyRandomIndustriesCallback(Window *, bool confirmed)
601 {
602 if (!confirmed) return;
603
604 if (Town::GetNumItems() == 0) {
605 ShowErrorMessage(GetEncodedString(STR_ERROR_CAN_T_GENERATE_INDUSTRIES), GetEncodedString(STR_ERROR_MUST_FOUND_TOWN_FIRST), WarningLevel::Info);
606 } else {
607 Map::CountLandTiles();
608 AutoRestoreBackup old_generating_world(_generating_world, true);
612 }
613 }
614
615 static void AskRemoveAllIndustriesCallback(Window *, bool confirmed)
616 {
617 if (!confirmed) return;
618
619 for (Industry *industry : Industry::Iterate()) delete industry;
620
621 /* Clear farmland. */
622 for (const auto tile : Map::Iterate()) {
625 }
626 }
627
629 }
630
631 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
632 {
633 switch (widget) {
635 assert(_game_mode == GameMode::Editor);
637 ShowQuery(
638 GetEncodedString(STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_CAPTION),
639 GetEncodedString(STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_QUERY),
640 nullptr, AskManyRandomIndustriesCallback);
641 break;
642 }
643
645 assert(_game_mode == GameMode::Editor);
647 ShowQuery(
648 GetEncodedString(STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES_CAPTION),
649 GetEncodedString(STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES_QUERY),
650 nullptr, AskRemoveAllIndustriesCallback);
651 break;
652 }
653
655 auto it = this->vscroll->GetScrolledItemFromWidget(this->list, pt.y, this, WID_DPI_MATRIX_WIDGET);
656 if (it != this->list.end()) { // Is it within the boundaries of available data?
657 this->selected_type = *it;
658 this->UpdateAvailability();
659
660 const IndustrySpec *indsp = GetIndustrySpec(this->selected_type);
661
662 this->SetDirty();
663
664 if (_thd.GetCallbackWnd() == this &&
665 ((_game_mode != GameMode::Editor && _settings_game.construction.raw_industry_construction == 2 && indsp != nullptr && indsp->IsRawIndustry()) || !this->enabled)) {
666 /* Reset the button state if going to prospecting or "build many industries" */
667 this->RaiseButtons();
669 }
670
671 this->SetButtons();
672 if (this->enabled && click_count > 1) this->OnClick(pt, WID_DPI_FUND_WIDGET, 1);
673 }
674 break;
675 }
676
678 if (this->selected_type != IT_INVALID) ShowIndustryCargoesWindow(this->selected_type);
679 break;
680
681 case WID_DPI_FUND_WIDGET: {
682 if (this->selected_type != IT_INVALID) {
683 if (_game_mode != GameMode::Editor && _settings_game.construction.raw_industry_construction == 2 && GetIndustrySpec(this->selected_type)->IsRawIndustry()) {
684 Command<Commands::BuildIndustry>::Post(STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY, TileIndex{}, this->selected_type, 0, false, InteractiveRandom());
686 } else {
687 HandlePlacePushButton(this, WID_DPI_FUND_WIDGET, SPR_CURSOR_INDUSTRY, HT_RECT);
688 }
689 }
690 break;
691 }
692 }
693 }
694
695 void OnResize() override
696 {
697 /* Adjust the number of items in the matrix depending of the resize */
698 this->vscroll->SetCapacityFromWidget(this, WID_DPI_MATRIX_WIDGET);
699 }
700
701 void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile) override
702 {
703 bool success = true;
704 /* We do not need to protect ourselves against "Random Many Industries" in this mode */
705 const IndustrySpec *indsp = GetIndustrySpec(this->selected_type);
706 uint32_t seed = InteractiveRandom();
707 uint32_t layout_index = InteractiveRandomRange((uint32_t)indsp->layouts.size());
708
709 if (_game_mode == GameMode::Editor) {
710 /* Show error if no town exists at all */
711 if (Town::GetNumItems() == 0) {
712 ShowErrorMessage(GetEncodedString(STR_ERROR_CAN_T_BUILD_HERE, indsp->name),
713 GetEncodedString(STR_ERROR_MUST_FOUND_TOWN_FIRST), WarningLevel::Info, pt.x, pt.y);
714 return;
715 }
716
717 AutoRestoreBackup backup_cur_company(_current_company, OWNER_NONE);
718 AutoRestoreBackup backup_generating_world(_generating_world, true);
719 AutoRestoreBackup backup_ignore_industry_restritions(_ignore_industry_restrictions, true);
720
721 Command<Commands::BuildIndustry>::Post(STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY, tile, this->selected_type, layout_index, false, seed);
722 } else {
723 success = Command<Commands::BuildIndustry>::Post(STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY, tile, this->selected_type, layout_index, false, seed);
724 }
725
726 /* If an industry has been built, just reset the cursor and the system */
727 if (success && !_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
728 }
729
730 const IntervalTimer<TimerWindow> update_interval = {std::chrono::seconds(3), [this](auto) {
731 if (_game_mode == GameMode::Editor) return;
732 if (this->selected_type == IT_INVALID) return;
733
734 bool enabled = this->enabled;
735 this->UpdateAvailability();
736 if (enabled != this->enabled) {
737 this->SetButtons();
738 this->SetDirty();
739 }
740 }};
741
742 void OnTimeout() override
743 {
744 this->RaiseButtons();
745 }
746
747 void OnPlaceObjectAbort() override
748 {
749 this->RaiseButtons();
750 }
751
757 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
758 {
759 if (!gui_scope) return;
760 this->SetupArrays();
761 this->SetButtons();
762 this->SetDirty();
763 }
764};
765
766void ShowBuildIndustryWindow()
767{
768 if (_game_mode != GameMode::Editor && !Company::IsValidID(_local_company)) return;
769 if (BringWindowToFrontById(WindowClass::BuildIndustry, 0)) return;
771}
772
773static void UpdateIndustryProduction(Industry *i);
774
775static inline bool IsProductionAlterable(const Industry *i)
776{
777 const IndustrySpec *is = GetIndustrySpec(i->type);
778 bool has_prod = std::any_of(std::begin(is->production_rate), std::end(is->production_rate), [](auto rate) { return rate != 0; });
779 return ((_game_mode == GameMode::Editor || _cheats.setup_prod.value) &&
780 (has_prod || is->IsRawIndustry()) &&
781 !_networking);
782}
783
784class IndustryViewWindow : public Window
785{
787 enum class Editability : uint8_t {
791 };
792
794 using ClickedCargoLine = int8_t;
795
796 /* Special clicked cargo line values. */
797 static constexpr ClickedCargoLine CCL_NONE = -1;
798 static constexpr ClickedCargoLine CCL_MULTIPLIER = -2;
799
804 uint8_t clicked_button = 0;
806 int info_height = 0;
808
809public:
810 IndustryViewWindow(WindowDesc &desc, WindowNumber window_number) : Window(desc)
811 {
813 this->info_height = WidgetDimensions::scaled.framerect.Vertical() + 2 * GetCharacterHeight(FontSize::Normal); // Info panel has at least two lines text.
814
815 this->InitNested(window_number);
817 nvp->InitializeViewport(this, Industry::Get(window_number)->location.GetCenterTile(), ScaleZoomGUI(ZoomLevel::Industry));
818
821
822 this->InvalidateData();
823 }
824
827 {
828 CloseWindowById(WindowClass::IndustryProductionGraph, this->window_number, false);
829 }
830
831 void OnInit() override
832 {
833 /* This only used when the cheat to alter industry production is enabled */
834 this->cheat_line_height = std::max(SETTING_BUTTON_HEIGHT + WidgetDimensions::scaled.vsep_normal, GetCharacterHeight(FontSize::Normal));
835 this->cargo_icon_size = GetLargestCargoIconSize();
836 }
837
838 void OnPaint() override
839 {
840 this->DrawWidgets();
841
842 if (this->IsShaded()) return; // Don't draw anything when the window is shaded.
843
844 const Rect r = this->GetWidget<NWidgetBase>(WID_IV_INFO)->GetCurrentRect();
845 int expected = this->DrawInfo(r);
846 if (expected != r.bottom) {
847 this->info_height = expected - r.top + 1;
848 this->ReInit();
849 return;
850 }
851 }
852
853 void DrawCargoIcon(const Rect &r, CargoType cargo_type) const
854 {
855 bool rtl = _current_text_dir == TD_RTL;
856 SpriteID icon = CargoSpec::Get(cargo_type)->GetCargoIcon();
857 Dimension d = GetSpriteSize(icon);
858 Rect ir = r.WithWidth(this->cargo_icon_size.width, rtl).WithHeight(GetCharacterHeight(FontSize::Normal));
859 DrawSprite(icon, PAL_NONE, CentreBounds(ir.left, ir.right, d.width), CentreBounds(ir.top, ir.bottom, this->cargo_icon_size.height));
860 }
861
862 std::string GetAcceptedCargoString(const Industry::AcceptedCargo &ac, const CargoSuffix &suffix) const
863 {
864 auto params = MakeParameters(CargoSpec::Get(ac.cargo)->name, ac.cargo, ac.waiting, suffix.text);
865 switch (suffix.display) {
866 case CargoSuffixDisplay::AmountAndText: return GetStringWithArgs(STR_INDUSTRY_VIEW_ACCEPT_CARGO_AMOUNT_SUFFIX, params);
867 case CargoSuffixDisplay::Text: return GetStringWithArgs(STR_INDUSTRY_VIEW_ACCEPT_CARGO_SUFFIX, params);
868 case CargoSuffixDisplay::Amount: return GetStringWithArgs(STR_INDUSTRY_VIEW_ACCEPT_CARGO_AMOUNT_NOSUFFIX, params);
869 case CargoSuffixDisplay::None: return GetStringWithArgs(STR_INDUSTRY_VIEW_ACCEPT_CARGO_NOSUFFIX, params);
870 default: NOT_REACHED();
871 }
872 }
873
879 int DrawInfo(const Rect &r)
880 {
881 bool rtl = _current_text_dir == TD_RTL;
883 const IndustrySpec *ind = GetIndustrySpec(i->type);
884 Rect ir = r.Shrink(WidgetDimensions::scaled.framerect);
885 bool first = true;
886 bool has_accept = false;
887
888 /* Use all the available space past the rect, so that we can enlarge the window if needed. */
889 ir.bottom = INT_MAX;
890
891 if (i->prod_level == PRODLEVEL_CLOSURE) {
892 DrawString(ir, STR_INDUSTRY_VIEW_INDUSTRY_ANNOUNCED_CLOSURE);
894 }
895
896 const int label_indent = WidgetDimensions::scaled.hsep_normal + this->cargo_icon_size.width;
898
899 for (const auto &a : i->accepted) {
900 if (!IsValidCargoType(a.cargo)) continue;
901 has_accept = true;
902 if (first) {
903 DrawString(ir, STR_INDUSTRY_VIEW_REQUIRES);
905 first = false;
906 }
907
908 DrawCargoIcon(ir, a.cargo);
909
910 CargoSuffix suffix;
911 GetCargoSuffix(CargoSuffixDirection::In, CargoSuffixType::View, i, i->type, ind, a.cargo, &a - i->accepted.data(), suffix);
912 /* if the industry is not stockpiling then don't show amount in the acceptance display. */
913 if (!stockpiling && suffix.display == CargoSuffixDisplay::AmountAndText) suffix.display = CargoSuffixDisplay::Text;
914 if (!stockpiling && suffix.display == CargoSuffixDisplay::Amount) suffix.display = CargoSuffixDisplay::None;
915
916 DrawString(ir.Indent(label_indent, rtl), this->GetAcceptedCargoString(a, suffix));
918 }
919
920 int line_height = this->editable == Editability::Rate ? this->cheat_line_height : GetCharacterHeight(FontSize::Normal);
921 int text_y_offset = (line_height - GetCharacterHeight(FontSize::Normal)) / 2;
922 int button_y_offset = (line_height - SETTING_BUTTON_HEIGHT) / 2;
923 first = true;
924 for (const auto &p : i->produced) {
925 if (!IsValidCargoType(p.cargo)) continue;
926 if (first) {
927 if (has_accept) ir.top += WidgetDimensions::scaled.vsep_wide;
928 DrawString(ir, TimerGameEconomy::UsingWallclockUnits() ? STR_INDUSTRY_VIEW_PRODUCTION_LAST_MINUTE_TITLE : STR_INDUSTRY_VIEW_PRODUCTION_LAST_MONTH_TITLE);
930 if (this->editable == Editability::Rate) this->production_offset_y = ir.top;
931 first = false;
932 }
933
934 DrawCargoIcon(ir, p.cargo);
935
936 CargoSuffix suffix;
937 GetCargoSuffix(CargoSuffixDirection::Out, CargoSuffixType::View, i, i->type, ind, p.cargo, &p - i->produced.data(), suffix);
938
939 DrawString(ir.Indent(label_indent + (this->editable == Editability::Rate ? SETTING_BUTTON_WIDTH + WidgetDimensions::scaled.hsep_normal : 0), rtl).Translate(0, text_y_offset),
940 GetString(STR_INDUSTRY_VIEW_TRANSPORTED, p.cargo, p.history[LAST_MONTH].production, suffix.text, ToPercent8(p.history[LAST_MONTH].PctTransported())));
941 /* Let's put out those buttons.. */
942 if (this->editable == Editability::Rate) {
943 DrawArrowButtons(ir.Indent(label_indent, rtl).WithWidth(SETTING_BUTTON_WIDTH, rtl).left, ir.top + button_y_offset, Colours::Yellow, (this->clicked_line == &p - i->produced.data()) ? this->clicked_button : 0,
944 p.rate > 0, p.rate < 255);
945 }
946 ir.top += line_height;
947 }
948
949 /* Display production multiplier if editable */
950 if (this->editable == Editability::Multiplier) {
951 line_height = this->cheat_line_height;
952 text_y_offset = (line_height - GetCharacterHeight(FontSize::Normal)) / 2;
953 button_y_offset = (line_height - SETTING_BUTTON_HEIGHT) / 2;
954 ir.top += WidgetDimensions::scaled.vsep_wide;
955 this->production_offset_y = ir.top;
956 DrawString(ir.Indent(label_indent + SETTING_BUTTON_WIDTH + WidgetDimensions::scaled.hsep_normal, rtl).Translate(0, text_y_offset),
957 GetString(STR_INDUSTRY_VIEW_PRODUCTION_LEVEL, RoundDivSU(i->prod_level * 100, PRODLEVEL_DEFAULT)));
958 DrawArrowButtons(ir.Indent(label_indent, rtl).WithWidth(SETTING_BUTTON_WIDTH, rtl).left, ir.top + button_y_offset, Colours::Yellow, (this->clicked_line == CCL_MULTIPLIER) ? this->clicked_button : 0,
960 ir.top += line_height;
961 }
962
963 /* Get the extra message for the GUI */
965 std::array<int32_t, 16> regs100;
966 uint16_t callback_res = GetIndustryCallback(CBID_INDUSTRY_WINDOW_MORE_TEXT, 0, 0, i, i->type, i->location.tile, regs100);
967 if (callback_res != CALLBACK_FAILED && callback_res != 0x400) {
968 std::string str;
969 if (callback_res == 0x40F) {
970 str = GetGRFStringWithTextStack(ind->grf_prop.grffile, static_cast<GRFStringID>(regs100[0]), std::span{regs100}.subspan(1));
971 } else if (callback_res > 0x400) {
973 } else {
974 str = GetGRFStringWithTextStack(ind->grf_prop.grffile, GRFSTR_MISC_GRF_TEXT + callback_res, regs100);
975 }
976 if (!str.empty()) {
977 ir.top += WidgetDimensions::scaled.vsep_wide;
978 ir.top = DrawStringMultiLine(ir, str, TextColour::Yellow);
979 }
980 }
981 }
982
983 if (!i->text.empty()) {
984 ir.top += WidgetDimensions::scaled.vsep_wide;
986 }
987
988 /* Return required bottom position, the last pixel row plus some padding. */
989 return ir.top - 1 + WidgetDimensions::scaled.framerect.bottom;
990 }
991
992 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
993 {
994 if (widget == WID_IV_CAPTION) return GetString(STR_INDUSTRY_VIEW_CAPTION, this->window_number);
995
996 return this->Window::GetWidgetString(widget, stringid);
997 }
998
999 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
1000 {
1001 if (widget == WID_IV_INFO) size.height = this->info_height;
1002 }
1003
1004 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
1005 {
1006 switch (widget) {
1007 case WID_IV_INFO: {
1010
1011 switch (this->editable) {
1012 case Editability::None: break;
1013
1015 if (IsInsideBS(pt.y, this->production_offset_y, this->cheat_line_height)) line = CCL_MULTIPLIER;
1016 break;
1017
1018 case Editability::Rate:
1019 if (pt.y >= this->production_offset_y) {
1020 int row = (pt.y - this->production_offset_y) / this->cheat_line_height;
1021 for (auto itp = std::begin(i->produced); itp != std::end(i->produced); ++itp) {
1022 if (!IsValidCargoType(itp->cargo)) continue;
1023 row--;
1024 if (row < 0) {
1025 line = itp - std::begin(i->produced);
1026 break;
1027 }
1028 }
1029 }
1030 break;
1031 }
1032 if (line == CCL_NONE) return;
1033
1034 bool rtl = _current_text_dir == TD_RTL;
1035 Rect r = this->GetWidget<NWidgetBase>(widget)->GetCurrentRect().Shrink(WidgetDimensions::scaled.framerect).Indent(this->cargo_icon_size.width + WidgetDimensions::scaled.hsep_normal, rtl);
1036
1037 if (r.WithWidth(SETTING_BUTTON_WIDTH, rtl).Contains(pt)) {
1038 /* Clicked buttons, decrease or increase production */
1039 bool decrease = r.WithWidth(SETTING_BUTTON_WIDTH / 2, rtl).Contains(pt);
1040 switch (this->editable) {
1042 if (decrease) {
1043 if (i->prod_level <= PRODLEVEL_MINIMUM) return;
1044 i->prod_level = static_cast<uint8_t>(std::max<uint>(i->prod_level / 2, PRODLEVEL_MINIMUM));
1045 } else {
1046 if (i->prod_level >= PRODLEVEL_MAXIMUM) return;
1047 i->prod_level = static_cast<uint8_t>(std::min<uint>(i->prod_level * 2, PRODLEVEL_MAXIMUM));
1048 }
1049 break;
1050
1051 case Editability::Rate:
1052 if (decrease) {
1053 if (i->produced[line].rate <= 0) return;
1054 i->produced[line].rate = std::max(i->produced[line].rate / 2, 0);
1055 } else {
1056 if (i->produced[line].rate >= 255) return;
1057 /* a zero production industry is unlikely to give anything but zero, so push it a little bit */
1058 int new_prod = i->produced[line].rate == 0 ? 1 : i->produced[line].rate * 2;
1059 i->produced[line].rate = ClampTo<uint8_t>(new_prod);
1060 }
1061 break;
1062
1063 default: NOT_REACHED();
1064 }
1065
1066 UpdateIndustryProduction(i);
1067 this->SetDirty();
1068 this->SetTimeout();
1069 this->clicked_line = line;
1070 this->clicked_button = (decrease ^ rtl) ? 1 : 2;
1071 } else if (r.Indent(SETTING_BUTTON_WIDTH + WidgetDimensions::scaled.hsep_normal, rtl).Contains(pt)) {
1072 /* clicked the text */
1073 this->editbox_line = line;
1074 switch (this->editable) {
1076 ShowQueryString(GetString(STR_JUST_INT, RoundDivSU(i->prod_level * 100, PRODLEVEL_DEFAULT)), STR_CONFIG_GAME_PRODUCTION_LEVEL, 10, this, CS_ALPHANUMERAL, {});
1077 break;
1078
1079 case Editability::Rate:
1080 ShowQueryString(GetString(STR_JUST_INT, i->produced[line].rate * 8), STR_CONFIG_GAME_PRODUCTION, 10, this, CS_ALPHANUMERAL, {});
1081 break;
1082
1083 default: NOT_REACHED();
1084 }
1085 }
1086 break;
1087 }
1088
1089 case WID_IV_GOTO: {
1091 if (_ctrl_pressed) {
1093 } else {
1095 }
1096 break;
1097 }
1098
1099 case WID_IV_DISPLAY: {
1102 break;
1103 }
1104
1105 case WID_IV_GRAPH:
1106 ShowIndustryProductionGraph(this->window_number);
1107 break;
1108 }
1109 }
1110
1111 void OnTimeout() override
1112 {
1113 this->clicked_line = CCL_NONE;
1114 this->clicked_button = 0;
1115 this->SetDirty();
1116 }
1117
1118 void OnResize() override
1119 {
1120 if (this->viewport != nullptr) {
1122 nvp->UpdateViewportCoordinates(this);
1123
1124 ScrollWindowToTile(Industry::Get(this->window_number)->location.GetCenterTile(), this, true); // Re-center viewport.
1125 }
1126 }
1127
1128 void OnMouseWheel(int wheel, WidgetID widget) override
1129 {
1130 if (widget != WID_IV_VIEWPORT) return;
1131 if (_settings_client.gui.scrollwheel_scrolling != ScrollWheelScrolling::Off) {
1132 DoZoomInOutWindow(wheel < 0 ? ZOOM_IN : ZOOM_OUT, this);
1133 }
1134 }
1135
1136 void OnQueryTextFinished(std::optional<std::string> str) override
1137 {
1138 if (!str.has_value() || str->empty()) return;
1139
1141 auto value = ParseInteger(*str, 10, true);
1142 if (!value.has_value()) return;
1143 switch (this->editbox_line) {
1144 case CCL_NONE: NOT_REACHED();
1145
1146 case CCL_MULTIPLIER:
1148 break;
1149
1150 default:
1151 i->produced[this->editbox_line].rate = ClampU(RoundDivSU(*value, 8), 0, 255);
1152 break;
1153 }
1154 UpdateIndustryProduction(i);
1155 this->SetDirty();
1156 }
1157
1163 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
1164 {
1165 if (!gui_scope) return;
1166 const Industry *i = Industry::Get(this->window_number);
1167 if (IsProductionAlterable(i)) {
1168 const IndustrySpec *ind = GetIndustrySpec(i->type);
1170 } else {
1171 this->editable = Editability::None;
1172 }
1173 }
1174
1175 bool IsNewGRFInspectable() const override
1176 {
1177 return ::IsNewGRFInspectable(GrfSpecFeature::Industries, this->window_number);
1178 }
1179
1184};
1185
1186static void UpdateIndustryProduction(Industry *i)
1187{
1188 const IndustrySpec *indspec = GetIndustrySpec(i->type);
1190
1191 for (auto &p : i->produced) {
1192 if (IsValidCargoType(p.cargo)) {
1193 p.history[LAST_MONTH].production = ScaleByCargoScale(8 * p.rate, false);
1194 }
1195 }
1196}
1197
1199static constexpr std::initializer_list<NWidgetPart> _nested_industry_view_widgets = {
1203 NWidget(WWT_PUSHIMGBTN, Colours::Cream, WID_IV_GOTO), SetAspect(WidgetDimensions::ASPECT_LOCATION), SetSpriteTip(SPR_GOTO_LOCATION, STR_INDUSTRY_VIEW_LOCATION_TOOLTIP),
1208 EndContainer(),
1212 EndContainer(),
1213 EndContainer(),
1215 EndContainer(),
1217 NWidget(WWT_PUSHTXTBTN, Colours::Cream, WID_IV_DISPLAY), SetFill(1, 0), SetResize(1, 0), SetStringTip(STR_INDUSTRY_DISPLAY_CHAIN, STR_INDUSTRY_DISPLAY_CHAIN_TOOLTIP),
1218 NWidget(WWT_PUSHTXTBTN, Colours::Cream, WID_IV_GRAPH), SetFill(1, 0), SetResize(1, 0), SetStringTip(STR_INDUSTRY_VIEW_CARGO_GRAPH, STR_INDUSTRY_VIEW_CARGO_GRAPH_TOOLTIP),
1220 EndContainer(),
1221};
1222
1225 WindowPosition::Automatic, "view_industry", 260, 120,
1226 WindowClass::IndustryView, WindowClass::None,
1227 {},
1229);
1230
1231void ShowIndustryViewWindow(IndustryID industry)
1232{
1234}
1235
1237static constexpr std::initializer_list<NWidgetPart> _nested_industry_directory_widgets = {
1244 EndContainer(),
1248 NWidget(WWT_TEXTBTN, Colours::Brown, WID_ID_DROPDOWN_ORDER), SetStringTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER),
1250 NWidget(WWT_EDITBOX, Colours::Brown, WID_ID_FILTER), SetFill(1, 0), SetResize(1, 0), SetStringTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP),
1251 EndContainer(),
1253 NWidget(WWT_DROPDOWN, Colours::Brown, WID_ID_FILTER_BY_ACC_CARGO), SetMinimalSize(225, 12), SetFill(0, 1), SetToolTip(STR_TOOLTIP_FILTER_CRITERIA),
1254 NWidget(WWT_DROPDOWN, Colours::Brown, WID_ID_FILTER_BY_PROD_CARGO), SetMinimalSize(225, 12), SetFill(0, 1), SetToolTip(STR_TOOLTIP_FILTER_CRITERIA),
1256 EndContainer(),
1258 EndContainer(),
1259 EndContainer(),
1261 EndContainer(),
1265 EndContainer(),
1266};
1267
1269
1277static bool CargoFilter(const Industry * const *industry, const std::pair<CargoType, CargoType> &cargoes)
1278{
1279 auto accepted_cargo = cargoes.first;
1280 auto produced_cargo = cargoes.second;
1281
1282 bool accepted_cargo_matches;
1283
1284 switch (accepted_cargo) {
1286 accepted_cargo_matches = true;
1287 break;
1288
1290 accepted_cargo_matches = !(*industry)->IsCargoAccepted();
1291 break;
1292
1293 default:
1294 accepted_cargo_matches = (*industry)->IsCargoAccepted(accepted_cargo);
1295 break;
1296 }
1297
1298 bool produced_cargo_matches;
1299
1300 switch (produced_cargo) {
1302 produced_cargo_matches = true;
1303 break;
1304
1306 produced_cargo_matches = !(*industry)->IsCargoProduced();
1307 break;
1308
1309 default:
1310 produced_cargo_matches = (*industry)->IsCargoProduced(produced_cargo);
1311 break;
1312 }
1313
1314 return accepted_cargo_matches && produced_cargo_matches;
1315}
1316
1317static GUIIndustryList::FilterFunction * const _industry_filter_funcs[] = { &CargoFilter };
1318
1322class IndustryDirectoryWindow : public Window {
1323protected:
1324 /* Runtime saved values */
1325 static Listing last_sorting;
1326
1328 static inline const StringID sorter_names[] = {
1329 STR_SORT_BY_NAME,
1330 STR_SORT_BY_TYPE,
1331 STR_SORT_BY_PRODUCTION,
1332 STR_SORT_BY_TRANSPORTED,
1333 };
1334 static const std::initializer_list<GUIIndustryList::SortFunction * const> sorter_funcs;
1335
1336 GUIIndustryList industries{IndustryDirectoryWindow::produced_cargo_filter};
1337 Scrollbar *vscroll{};
1338 Scrollbar *hscroll{};
1339
1342 static CargoType produced_cargo_filter;
1343
1344 const int MAX_FILTER_LENGTH = 16;
1347
1355
1361 {
1362 if (this->produced_cargo_filter_criteria != cargo_type) {
1363 this->produced_cargo_filter_criteria = cargo_type;
1364 /* deactivate filter if criteria is 'Show All', activate it otherwise */
1365 bool is_filtering_necessary = this->produced_cargo_filter_criteria != CargoFilterCriteria::CF_ANY || this->accepted_cargo_filter_criteria != CargoFilterCriteria::CF_ANY;
1366
1367 this->industries.SetFilterState(is_filtering_necessary);
1368 this->industries.SetFilterType(0);
1369 this->industries.ForceRebuild();
1370 }
1371 }
1372
1378 {
1379 if (this->accepted_cargo_filter_criteria != cargo_type) {
1380 this->accepted_cargo_filter_criteria = cargo_type;
1381 /* deactivate filter if criteria is 'Show All', activate it otherwise */
1382 bool is_filtering_necessary = this->produced_cargo_filter_criteria != CargoFilterCriteria::CF_ANY || this->accepted_cargo_filter_criteria != CargoFilterCriteria::CF_ANY;
1383
1384 this->industries.SetFilterState(is_filtering_necessary);
1385 this->industries.SetFilterType(0);
1386 this->industries.ForceRebuild();
1387 }
1388 }
1389
1390 StringID GetCargoFilterLabel(CargoType cargo_type) const
1391 {
1392 switch (cargo_type) {
1393 case CargoFilterCriteria::CF_ANY: return STR_INDUSTRY_DIRECTORY_FILTER_ALL_TYPES;
1394 case CargoFilterCriteria::CF_NONE: return STR_INDUSTRY_DIRECTORY_FILTER_NONE;
1395 default: return CargoSpec::Get(cargo_type)->name;
1396 }
1397 }
1398
1403 {
1404 this->produced_cargo_filter_criteria = CargoFilterCriteria::CF_ANY;
1405 this->accepted_cargo_filter_criteria = CargoFilterCriteria::CF_ANY;
1406
1407 this->industries.SetFilterFuncs(_industry_filter_funcs);
1408
1409 bool is_filtering_necessary = this->produced_cargo_filter_criteria != CargoFilterCriteria::CF_ANY || this->accepted_cargo_filter_criteria != CargoFilterCriteria::CF_ANY;
1410
1411 this->industries.SetFilterState(is_filtering_necessary);
1412 }
1413
1419 {
1420 uint width = this->hscroll->GetCount();
1421 auto [first, last] = this->vscroll->GetVisibleRangeIterators(this->industries);
1422 for (auto it = first; it != last; ++it) {
1423 width = std::max(width, GetStringBoundingBox(this->GetIndustryString(*it)).width);
1424 }
1425 return width;
1426 }
1427
1430 {
1431 if (this->industries.NeedRebuild()) {
1432 this->industries.clear();
1433 this->industries.reserve(Industry::GetNumItems());
1434
1435 for (const Industry *i : Industry::Iterate()) {
1436 if (this->string_filter.IsEmpty()) {
1437 this->industries.push_back(i);
1438 continue;
1439 }
1440 this->string_filter.ResetState();
1441 this->string_filter.AddLine(i->GetCachedName());
1442 if (this->string_filter.GetState()) this->industries.push_back(i);
1443 }
1444
1445 this->industries.RebuildDone();
1446
1447 auto filter = std::make_pair(this->accepted_cargo_filter_criteria, this->produced_cargo_filter_criteria);
1448
1449 this->industries.Filter(filter);
1450
1451 this->vscroll->SetCount(this->industries.size()); // Update scrollbar as well.
1452 }
1453
1454 IndustryDirectoryWindow::produced_cargo_filter = this->produced_cargo_filter_criteria;
1455 this->industries.Sort();
1456
1457 this->SetDirty();
1458 }
1459
1467 {
1468 if (!IsValidCargoType(p.cargo)) return -1;
1469 return ToPercent8(p.history[LAST_MONTH].PctTransported());
1470 }
1471
1480 {
1481 CargoType filter = IndustryDirectoryWindow::produced_cargo_filter;
1482 if (filter == CargoFilterCriteria::CF_NONE) return 0;
1483
1484 int percentage = 0, produced_cargo_count = 0;
1485 for (const auto &p : i->produced) {
1486 if (filter == CargoFilterCriteria::CF_ANY) {
1487 int transported = GetCargoTransportedPercentsIfValid(p);
1488 if (transported != -1) {
1489 produced_cargo_count++;
1490 percentage += transported;
1491 }
1492 if (produced_cargo_count == 0 && &p == &i->produced.back() && percentage == 0) {
1493 return transported;
1494 }
1495 } else if (filter == p.cargo) {
1497 }
1498 }
1499
1500 if (produced_cargo_count == 0) return percentage;
1501 return percentage / produced_cargo_count;
1502 }
1503
1505 static bool IndustryNameSorter(const Industry * const &a, const Industry * const &b, [[maybe_unused]] const CargoType &filter)
1506 {
1507 int r = StrNaturalCompare(a->GetCachedName(), b->GetCachedName()); // Sort by name (natural sorting).
1508 if (r == 0) return a->index < b->index;
1509 return r < 0;
1510 }
1511
1513 static bool IndustryTypeSorter(const Industry * const &a, const Industry * const &b, const CargoType &filter)
1514 {
1515 int it_a = 0;
1516 while (it_a != NUM_INDUSTRYTYPES && a->type != _sorted_industry_types[it_a]) it_a++;
1517 int it_b = 0;
1518 while (it_b != NUM_INDUSTRYTYPES && b->type != _sorted_industry_types[it_b]) it_b++;
1519 int r = it_a - it_b;
1520 return (r == 0) ? IndustryNameSorter(a, b, filter) : r < 0;
1521 }
1522
1524 static bool IndustryProductionSorter(const Industry * const &a, const Industry * const &b, const CargoType &filter)
1525 {
1526 if (filter == CargoFilterCriteria::CF_NONE) return IndustryTypeSorter(a, b, filter);
1527
1528 uint prod_a = 0, prod_b = 0;
1529 if (filter == CargoFilterCriteria::CF_ANY) {
1530 for (const auto &pa : a->produced) {
1531 if (IsValidCargoType(pa.cargo)) prod_a += pa.history[LAST_MONTH].production;
1532 }
1533 for (const auto &pb : b->produced) {
1534 if (IsValidCargoType(pb.cargo)) prod_b += pb.history[LAST_MONTH].production;
1535 }
1536 } else {
1537 if (auto ita = a->GetCargoProduced(filter); ita != std::end(a->produced)) prod_a = ita->history[LAST_MONTH].production;
1538 if (auto itb = b->GetCargoProduced(filter); itb != std::end(b->produced)) prod_b = itb->history[LAST_MONTH].production;
1539 }
1540 int r = prod_a - prod_b;
1541
1542 return (r == 0) ? IndustryTypeSorter(a, b, filter) : r < 0;
1543 }
1544
1546 static bool IndustryTransportedCargoSorter(const Industry * const &a, const Industry * const &b, const CargoType &filter)
1547 {
1549 return (r == 0) ? IndustryNameSorter(a, b, filter) : r < 0;
1550 }
1551
1552 StringID GetStringForNumCargo(size_t count) const
1553 {
1554 switch (count) {
1555 case 0: return STR_INDUSTRY_DIRECTORY_ITEM_NOPROD;
1556 case 1: return STR_INDUSTRY_DIRECTORY_ITEM_PROD1;
1557 case 2: return STR_INDUSTRY_DIRECTORY_ITEM_PROD2;
1558 case 3: return STR_INDUSTRY_DIRECTORY_ITEM_PROD3;
1559 default: return STR_INDUSTRY_DIRECTORY_ITEM_PRODMORE;
1560 }
1561 }
1562
1568 std::string GetIndustryString(const Industry *i) const
1569 {
1570 const IndustrySpec *indsp = GetIndustrySpec(i->type);
1571
1572 /* Get industry productions (CargoType, production, suffix, transported) */
1573 struct CargoInfo {
1574 CargoType cargo_type;
1575 uint16_t production;
1576 uint transported;
1577 std::string suffix;
1578
1579 CargoInfo(CargoType cargo_type, uint16_t production, uint transported, std::string &&suffix) : cargo_type(cargo_type), production(production), transported(transported), suffix(std::move(suffix)) {}
1580 };
1581 std::vector<CargoInfo> cargos;
1582
1583 for (auto itp = std::begin(i->produced); itp != std::end(i->produced); ++itp) {
1584 if (!IsValidCargoType(itp->cargo)) continue;
1585 CargoSuffix cargo_suffix;
1586 GetCargoSuffix(CargoSuffixDirection::Out, CargoSuffixType::Directory, i, i->type, indsp, itp->cargo, itp - std::begin(i->produced), cargo_suffix);
1587 cargos.emplace_back(itp->cargo, itp->history[LAST_MONTH].production, ToPercent8(itp->history[LAST_MONTH].PctTransported()), std::move(cargo_suffix.text));
1588 }
1589
1590 switch (static_cast<IndustryDirectoryWindow::SorterType>(this->industries.SortType())) {
1594 /* Sort by descending production, then descending transported */
1595 std::sort(cargos.begin(), cargos.end(), [](const CargoInfo &a, const CargoInfo &b) {
1596 if (a.production != b.production) return a.production > b.production;
1597 return a.transported > b.transported;
1598 });
1599 break;
1600
1602 /* Sort by descending transported, then descending production */
1603 std::sort(cargos.begin(), cargos.end(), [](const CargoInfo &a, const CargoInfo &b) {
1604 if (a.transported != b.transported) return a.transported > b.transported;
1605 return a.production > b.production;
1606 });
1607 break;
1608 }
1609
1610 /* If the produced cargo filter is active then move the filtered cargo to the beginning of the list,
1611 * because this is the one the player interested in, and that way it is not hidden in the 'n' more cargos */
1612 const CargoType cargo_type = this->produced_cargo_filter_criteria;
1613 if (cargo_type != CargoFilterCriteria::CF_ANY && cargo_type != CargoFilterCriteria::CF_NONE) {
1614 auto filtered_ci = std::ranges::find(cargos, cargo_type, &CargoInfo::cargo_type);
1615 if (filtered_ci != cargos.end()) {
1616 std::rotate(cargos.begin(), filtered_ci, filtered_ci + 1);
1617 }
1618 }
1619
1620 static constexpr size_t MAX_DISPLAYED_CARGOES = 3;
1621 std::array<StringParameter, 2 + 5 * MAX_DISPLAYED_CARGOES> params{};
1622 auto it = params.begin();
1623
1624 /* Industry name */
1625 *it++ = i->index;
1626
1627 /* Display first MAX_DISPLAYED_CARGOES cargoes */
1628 for (CargoInfo &ci : cargos | std::views::take(MAX_DISPLAYED_CARGOES)) {
1629 *it++ = STR_INDUSTRY_DIRECTORY_ITEM_INFO;
1630 *it++ = ci.cargo_type;
1631 *it++ = ci.production;
1632 *it++ = std::move(ci.suffix);
1633 *it++ = ci.transported;
1634 }
1635
1636 /* Undisplayed cargos if any */
1637 if (std::size(cargos) > MAX_DISPLAYED_CARGOES) *it++ = std::size(cargos) - MAX_DISPLAYED_CARGOES;
1638
1639 return GetStringWithArgs(GetStringForNumCargo(std::size(cargos)), {params.begin(), it});
1640 }
1641
1642public:
1644 {
1645 this->CreateNestedTree();
1646 this->vscroll = this->GetScrollbar(WID_ID_VSCROLLBAR);
1647 this->hscroll = this->GetScrollbar(WID_ID_HSCROLLBAR);
1648
1649 this->industries.SetListing(this->last_sorting);
1651 this->industries.ForceRebuild();
1652
1653 this->FinishInitNested(0);
1654
1656
1657 this->querystrings[WID_ID_FILTER] = &this->industry_editbox;
1658 this->industry_editbox.cancel_button = QueryString::ACTION_CLEAR;
1659 }
1660
1663 {
1664 this->last_sorting = this->industries.GetListing();
1665 }
1666
1667 void OnInit() override
1668 {
1669 this->SetCargoFilterArray();
1670 this->hscroll->SetCount(0);
1671 }
1672
1673 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
1674 {
1675 switch (widget) {
1676 case WID_ID_CAPTION:
1677 return GetString(STR_INDUSTRY_DIRECTORY_CAPTION, this->vscroll->GetCount(), Industry::GetNumItems());
1678
1680 return GetString(IndustryDirectoryWindow::sorter_names[this->industries.SortType()]);
1681
1683 return GetString(STR_INDUSTRY_DIRECTORY_ACCEPTED_CARGO_FILTER, this->GetCargoFilterLabel(this->accepted_cargo_filter_criteria));
1684
1686 return GetString(STR_INDUSTRY_DIRECTORY_PRODUCED_CARGO_FILTER, this->GetCargoFilterLabel(this->produced_cargo_filter_criteria));
1687
1688 default:
1689 return this->Window::GetWidgetString(widget, stringid);
1690 }
1691 }
1692
1693 void DrawWidget(const Rect &r, WidgetID widget) const override
1694 {
1695 switch (widget) {
1697 this->DrawSortButtonState(widget, this->industries.IsDescSortOrder() ? SBS_DOWN : SBS_UP);
1698 break;
1699
1700 case WID_ID_INDUSTRY_LIST: {
1701 Rect ir = r.Shrink(WidgetDimensions::scaled.framerect);
1702
1703 /* Setup a clipping rectangle... */
1704 DrawPixelInfo tmp_dpi;
1705 if (!FillDrawPixelInfo(&tmp_dpi, ir)) return;
1706 /* ...but keep coordinates relative to the window. */
1707 tmp_dpi.left += ir.left;
1708 tmp_dpi.top += ir.top;
1709
1710 AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi);
1711
1712 ir = ScrollRect(ir, *this->hscroll, 1);
1713
1714 if (this->industries.empty()) {
1715 DrawString(ir, STR_INDUSTRY_DIRECTORY_NONE);
1716 break;
1717 }
1718 const CargoType acf_cargo_type = this->accepted_cargo_filter_criteria;
1719 auto [first, last] = this->vscroll->GetVisibleRangeIterators(this->industries);
1720 for (auto it = first; it != last; ++it) {
1722 if (acf_cargo_type != CargoFilterCriteria::CF_ANY && acf_cargo_type != CargoFilterCriteria::CF_NONE) {
1723 Industry *ind = const_cast<Industry *>(*it);
1724 if (IndustryTemporarilyRefusesCargo(ind, acf_cargo_type)) {
1726 }
1727 }
1728 DrawString(ir, this->GetIndustryString(*it), tc);
1729
1730 ir.top += this->resize.step_height;
1731 }
1732 break;
1733 }
1734 }
1735 }
1736
1737 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
1738 {
1739 switch (widget) {
1740 case WID_ID_DROPDOWN_ORDER: {
1742 d.width += padding.width + Window::SortButtonWidth() * 2; // Doubled since the string is centred and it also looks better.
1743 d.height += padding.height;
1744 size = maxdim(size, d);
1745 break;
1746 }
1747
1750 d.width += padding.width;
1751 d.height += padding.height;
1752 size = maxdim(size, d);
1753 break;
1754 }
1755
1756 case WID_ID_INDUSTRY_LIST: {
1757 Dimension d = GetStringBoundingBox(STR_INDUSTRY_DIRECTORY_NONE);
1758 fill.height = resize.height = d.height;
1759 d.height *= 5;
1760 d.width += padding.width;
1761 d.height += padding.height;
1762 size = maxdim(size, d);
1763 break;
1764 }
1765 }
1766 }
1767
1768 DropDownList BuildCargoDropDownList() const
1769 {
1770 DropDownList list;
1771
1772 /* Add item for disabling filtering. */
1773 list.push_back(MakeDropDownListStringItem(this->GetCargoFilterLabel(CargoFilterCriteria::CF_ANY), CargoFilterCriteria::CF_ANY));
1774 /* Add item for industries not producing anything, e.g. power plants */
1776
1777 /* Add cargos */
1779 for (const CargoSpec *cs : _sorted_standard_cargo_specs) {
1780 list.push_back(MakeDropDownListIconItem(d, cs->GetCargoIcon(), PAL_NONE, cs->name, cs->Index()));
1781 }
1782
1783 return list;
1784 }
1785
1786 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
1787 {
1788 switch (widget) {
1790 this->industries.ToggleSortOrder();
1791 this->SetDirty();
1792 break;
1793
1796 break;
1797
1798 case WID_ID_FILTER_BY_ACC_CARGO: { // Cargo filter dropdown
1799 static std::string acc_cargo_filter;
1800 ShowDropDownList(this, this->BuildCargoDropDownList(), this->accepted_cargo_filter_criteria, widget, 0, DropDownOption::Filterable, &acc_cargo_filter);
1801 break;
1802 }
1803
1804 case WID_ID_FILTER_BY_PROD_CARGO: { // Cargo filter dropdown
1805 static std::string prod_cargo_filter;
1806 ShowDropDownList(this, this->BuildCargoDropDownList(), this->produced_cargo_filter_criteria, widget, 0, DropDownOption::Filterable, &prod_cargo_filter);
1807 break;
1808 }
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, int) override
1825 {
1826 switch (widget) {
1828 if (this->industries.SortType() != index) {
1829 this->industries.SetSortType(index);
1831 }
1832 break;
1833 }
1834
1836 this->SetAcceptedCargoFilter(static_cast<CargoType>(index));
1838 break;
1839 }
1840
1842 this->SetProducedCargoFilter(static_cast<CargoType>(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 const 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 static inline HotkeyList hotkeys {"industrydirectory", {
1900 Hotkey('F', "focus_filter_box", WID_ID_FILTER),
1901 }};
1902};
1903
1904Listing IndustryDirectoryWindow::last_sorting = {false, 0};
1905
1906/* Available station sorting functions. */
1907const std::initializer_list<GUIIndustryList::SortFunction * const> IndustryDirectoryWindow::sorter_funcs = {
1912};
1913
1914CargoType IndustryDirectoryWindow::produced_cargo_filter = CargoFilterCriteria::CF_ANY;
1915
1916
1919 WindowPosition::Automatic, "list_industries", 428, 190,
1920 WindowClass::IndustryDirectory, WindowClass::None,
1921 {},
1923 &IndustryDirectoryWindow::hotkeys
1924);
1925
1926void ShowIndustryDirectory()
1927{
1929}
1930
1932static constexpr std::initializer_list<NWidgetPart> _nested_industry_cargoes_widgets = {
1939 EndContainer(),
1943 EndContainer(),
1946 SetStringTip(STR_INDUSTRY_CARGOES_NOTIFY_SMALLMAP, STR_INDUSTRY_CARGOES_NOTIFY_SMALLMAP_TOOLTIP),
1949 SetStringTip(STR_INDUSTRY_CARGOES_SELECT_INDUSTRY, STR_INDUSTRY_CARGOES_SELECT_INDUSTRY_TOOLTIP),
1951 SetStringTip(STR_INDUSTRY_CARGOES_SELECT_CARGO, STR_INDUSTRY_CARGOES_SELECT_CARGO_TOOLTIP),
1953 EndContainer(),
1954};
1955
1958 WindowPosition::Automatic, "industry_cargoes", 300, 210,
1959 WindowClass::IndustryCargoes, WindowClass::None,
1960 {},
1962);
1963
1973
1974static const uint MAX_CARGOES = 16;
1975
1979 static int blob_distance;
1980
1986
1989
1992 static int industry_width;
1993 static uint max_cargoes;
1994
1995 using Cargoes = uint16_t;
1996 static_assert(std::numeric_limits<Cargoes>::digits >= MAX_CARGOES);
1997
1999 union {
2000 struct {
2001 IndustryType ind_type;
2002 std::array<CargoType, MAX_CARGOES> other_produced;
2003 std::array<CargoType, MAX_CARGOES> other_accepted;
2005 struct {
2006 std::array<CargoType, MAX_CARGOES> vertical_cargoes;
2009 uint8_t num_cargoes;
2010 uint8_t top_end;
2011 uint8_t bottom_end;
2013 struct {
2014 std::array<CargoType, MAX_CARGOES> cargoes;
2018 } u{};
2019
2025 {
2026 this->type = type;
2027 }
2028
2034 void MakeIndustry(IndustryType ind_type)
2035 {
2036 this->type = CargoesFieldType::Industry;
2037 this->u.industry.ind_type = ind_type;
2038 std::fill(std::begin(this->u.industry.other_accepted), std::end(this->u.industry.other_accepted), INVALID_CARGO);
2039 std::fill(std::begin(this->u.industry.other_produced), std::end(this->u.industry.other_produced), INVALID_CARGO);
2040 }
2041
2048 int ConnectCargo(CargoType cargo, bool producer)
2049 {
2050 assert(this->type == CargoesFieldType::Cargo);
2051 if (!IsValidCargoType(cargo)) return -1;
2052
2053 /* Find the vertical cargo column carrying the cargo. */
2054 int column = -1;
2055 for (int i = 0; i < this->u.cargo.num_cargoes; i++) {
2056 if (cargo == this->u.cargo.vertical_cargoes[i]) {
2057 column = i;
2058 break;
2059 }
2060 }
2061 if (column < 0) return -1;
2062
2063 if (producer) {
2064 assert(!HasBit(this->u.cargo.supp_cargoes, column));
2065 SetBit(this->u.cargo.supp_cargoes, column);
2066 } else {
2067 assert(!HasBit(this->u.cargo.cust_cargoes, column));
2068 SetBit(this->u.cargo.cust_cargoes, column);
2069 }
2070 return column;
2071 }
2072
2078 {
2079 assert(this->type == CargoesFieldType::Cargo);
2080
2081 return this->u.cargo.supp_cargoes != 0 || this->u.cargo.cust_cargoes != 0;
2082 }
2083
2089 void MakeCargo(const std::span<const CargoType> cargoes)
2090 {
2091 this->type = CargoesFieldType::Cargo;
2092 assert(std::size(cargoes) <= std::size(this->u.cargo.vertical_cargoes));
2093 auto insert = std::copy_if(std::begin(cargoes), std::end(cargoes), std::begin(this->u.cargo.vertical_cargoes), IsValidCargoType);
2094 this->u.cargo.num_cargoes = static_cast<uint8_t>(std::distance(std::begin(this->u.cargo.vertical_cargoes), insert));
2095 CargoTypeComparator comparator;
2096 std::sort(std::begin(this->u.cargo.vertical_cargoes), insert, comparator);
2097 std::fill(insert, std::end(this->u.cargo.vertical_cargoes), INVALID_CARGO);
2098 this->u.cargo.top_end = false;
2099 this->u.cargo.bottom_end = false;
2100 this->u.cargo.supp_cargoes = 0;
2101 this->u.cargo.cust_cargoes = 0;
2102 }
2103
2109 void MakeCargoLabel(const std::span<const CargoType> cargoes, bool left_align)
2110 {
2111 this->type = CargoesFieldType::CargoLabel;
2112 assert(std::size(cargoes) <= std::size(this->u.cargo_label.cargoes));
2113 auto insert = std::copy(std::begin(cargoes), std::end(cargoes), std::begin(this->u.cargo_label.cargoes));
2114 std::fill(insert, std::end(this->u.cargo_label.cargoes), INVALID_CARGO);
2115 this->u.cargo_label.left_align = left_align;
2116 }
2117
2123 {
2124 this->type = CargoesFieldType::Header;
2125 this->u.header = textid;
2126 }
2127
2133 int GetCargoBase(int xpos) const
2134 {
2135 assert(this->type == CargoesFieldType::Cargo);
2136 int n = this->u.cargo.num_cargoes;
2137
2138 return xpos + cargo_field_width / 2 - (CargoesField::cargo_line.width * n + CargoesField::cargo_space.width * (n - 1)) / 2;
2139 }
2140
2146 void Draw(int xpos, int ypos) const
2147 {
2148 switch (this->type) {
2151 break;
2152
2155 DrawString(xpos, xpos + industry_width, ypos, this->u.header, TextColour::White, SA_HOR_CENTER);
2156 break;
2157
2159 int ypos1 = ypos + vert_inter_industry_space / 2;
2160 int ypos2 = ypos + normal_height - 1 - vert_inter_industry_space / 2;
2161 int xpos2 = xpos + industry_width - 1;
2162 DrawRectOutline({xpos, ypos1, xpos2, ypos2}, INDUSTRY_LINE_COLOUR);
2164 if (this->u.industry.ind_type < NUM_INDUSTRYTYPES) {
2165 const IndustrySpec *indsp = GetIndustrySpec(this->u.industry.ind_type);
2166 DrawString(xpos, xpos2, ypos, indsp->name, TextColour::White, SA_HOR_CENTER);
2167
2168 /* Draw the industry legend. */
2169 int blob_left, blob_right;
2170 if (_current_text_dir == TD_RTL) {
2171 blob_right = xpos2 - blob_distance;
2172 blob_left = blob_right - CargoesField::legend.width;
2173 } else {
2174 blob_left = xpos + blob_distance;
2175 blob_right = blob_left + CargoesField::legend.width;
2176 }
2177 GfxFillRect(blob_left, ypos2 - blob_distance - CargoesField::legend.height, blob_right, ypos2 - blob_distance, PC_BLACK); // Border
2178 GfxFillRect(blob_left + 1, ypos2 - blob_distance - CargoesField::legend.height + 1, blob_right - 1, ypos2 - blob_distance - 1, indsp->map_colour);
2179 } else {
2180 DrawString(xpos, xpos2, ypos, STR_INDUSTRY_CARGOES_HOUSES, TextColour::FromString, SA_HOR_CENTER);
2181 }
2182
2183 /* Draw the other_produced/other_accepted cargoes. */
2184 std::span<const CargoType> other_right, other_left;
2185 if (_current_text_dir == TD_RTL) {
2186 other_right = this->u.industry.other_accepted;
2187 other_left = this->u.industry.other_produced;
2188 } else {
2189 other_right = this->u.industry.other_produced;
2190 other_left = this->u.industry.other_accepted;
2191 }
2193 for (uint i = 0; i < CargoesField::max_cargoes; i++) {
2194 if (IsValidCargoType(other_right[i])) {
2195 const CargoSpec *csp = CargoSpec::Get(other_right[i]);
2196 int xp = xpos + industry_width + CargoesField::cargo_stub.width;
2197 DrawHorConnection(xpos + industry_width, xp - 1, ypos1, csp);
2198 GfxDrawLine(xp, ypos1, xp, ypos1 + CargoesField::cargo_line.height - 1, CARGO_LINE_COLOUR);
2199 }
2200 if (IsValidCargoType(other_left[i])) {
2201 const CargoSpec *csp = CargoSpec::Get(other_left[i]);
2202 int xp = xpos - CargoesField::cargo_stub.width;
2203 DrawHorConnection(xp + 1, xpos - 1, ypos1, csp);
2204 GfxDrawLine(xp, ypos1, xp, ypos1 + CargoesField::cargo_line.height - 1, CARGO_LINE_COLOUR);
2205 }
2207 }
2208 break;
2209 }
2210
2212 int cargo_base = this->GetCargoBase(xpos);
2213 int top = ypos + (this->u.cargo.top_end ? vert_inter_industry_space / 2 + 1 : 0);
2214 int bot = ypos - (this->u.cargo.bottom_end ? vert_inter_industry_space / 2 + 1 : 0) + normal_height - 1;
2215 int colpos = cargo_base;
2216 for (int i = 0; i < this->u.cargo.num_cargoes; i++) {
2217 if (this->u.cargo.top_end) GfxDrawLine(colpos, top - 1, colpos + CargoesField::cargo_line.width - 1, top - 1, CARGO_LINE_COLOUR);
2218 if (this->u.cargo.bottom_end) GfxDrawLine(colpos, bot + 1, colpos + CargoesField::cargo_line.width - 1, bot + 1, CARGO_LINE_COLOUR);
2219 GfxDrawLine(colpos, top, colpos, bot, CARGO_LINE_COLOUR);
2220 colpos++;
2221 const CargoSpec *csp = CargoSpec::Get(this->u.cargo.vertical_cargoes[i]);
2222 GfxFillRect(colpos, top, colpos + CargoesField::cargo_line.width - 2, bot, csp->legend_colour, FillRectMode::Opaque);
2223 colpos += CargoesField::cargo_line.width - 2;
2224 GfxDrawLine(colpos, top, colpos, bot, CARGO_LINE_COLOUR);
2225 colpos += 1 + CargoesField::cargo_space.width;
2226 }
2227
2228 Cargoes hor_left, hor_right;
2229 if (_current_text_dir == TD_RTL) {
2230 hor_left = this->u.cargo.cust_cargoes;
2231 hor_right = this->u.cargo.supp_cargoes;
2232 } else {
2233 hor_left = this->u.cargo.supp_cargoes;
2234 hor_right = this->u.cargo.cust_cargoes;
2235 }
2237 for (uint i = 0; i < MAX_CARGOES; i++) {
2238 if (HasBit(hor_left, i)) {
2239 int col = i;
2240 int dx = 0;
2241 const CargoSpec *csp = CargoSpec::Get(this->u.cargo.vertical_cargoes[col]);
2242 for (; col > 0; col--) {
2243 int lf = cargo_base + col * CargoesField::cargo_line.width + (col - 1) * CargoesField::cargo_space.width;
2244 DrawHorConnection(lf, lf + CargoesField::cargo_space.width - dx, ypos, csp);
2245 dx = 1;
2246 }
2247 DrawHorConnection(xpos, cargo_base - dx, ypos, csp);
2248 }
2249 if (HasBit(hor_right, i)) {
2250 int col = i;
2251 int dx = 0;
2252 const CargoSpec *csp = CargoSpec::Get(this->u.cargo.vertical_cargoes[col]);
2253 for (; col < this->u.cargo.num_cargoes - 1; col++) {
2254 int lf = cargo_base + (col + 1) * CargoesField::cargo_line.width + col * CargoesField::cargo_space.width;
2255 DrawHorConnection(lf + dx - 1, lf + CargoesField::cargo_space.width - 1, ypos, csp);
2256 dx = 1;
2257 }
2258 DrawHorConnection(cargo_base + col * CargoesField::cargo_space.width + (col + 1) * CargoesField::cargo_line.width - 1 + dx, xpos + CargoesField::cargo_field_width - 1, ypos, csp);
2259 }
2261 }
2262 break;
2263 }
2264
2267 for (uint i = 0; i < MAX_CARGOES; i++) {
2268 if (IsValidCargoType(this->u.cargo_label.cargoes[i])) {
2269 const CargoSpec *csp = CargoSpec::Get(this->u.cargo_label.cargoes[i]);
2270 DrawString(xpos + WidgetDimensions::scaled.framerect.left, xpos + industry_width - 1 - WidgetDimensions::scaled.framerect.right, ypos, csp->name, TextColour::White,
2271 (this->u.cargo_label.left_align) ? SA_LEFT : SA_RIGHT);
2272 }
2274 }
2275 break;
2276
2277 default:
2278 NOT_REACHED();
2279 }
2280 }
2281
2289 CargoType CargoClickedAt(const CargoesField *left, const CargoesField *right, Point pt) const
2290 {
2291 assert(this->type == CargoesFieldType::Cargo);
2292
2293 /* Vertical matching. */
2294 int cpos = this->GetCargoBase(0);
2295 uint col;
2296 for (col = 0; col < this->u.cargo.num_cargoes; col++) {
2297 if (pt.x < cpos) break;
2298 if (pt.x < cpos + static_cast<int>(CargoesField::cargo_line.width)) return this->u.cargo.vertical_cargoes[col];
2300 }
2301 /* col = 0 -> left of first col, 1 -> left of 2nd col, ... this->u.cargo.num_cargoes right of last-col. */
2302
2304 uint row;
2305 for (row = 0; row < MAX_CARGOES; row++) {
2306 if (pt.y < vpos) return INVALID_CARGO;
2307 if (pt.y < vpos + static_cast<int>(CargoesField::cargo_line.height)) break;
2309 }
2310 if (row == MAX_CARGOES) return INVALID_CARGO;
2311
2312 /* row = 0 -> at first horizontal row, row = 1 -> second horizontal row, 2 = 3rd horizontal row. */
2313 if (col == 0) {
2314 if (HasBit(this->u.cargo.supp_cargoes, row)) return this->u.cargo.vertical_cargoes[row];
2315 if (left != nullptr) {
2316 if (left->type == CargoesFieldType::Industry) return left->u.industry.other_produced[row];
2317 if (left->type == CargoesFieldType::CargoLabel && !left->u.cargo_label.left_align) return left->u.cargo_label.cargoes[row];
2318 }
2319 return INVALID_CARGO;
2320 }
2321 if (col == this->u.cargo.num_cargoes) {
2322 if (HasBit(this->u.cargo.cust_cargoes, row)) return this->u.cargo.vertical_cargoes[row];
2323 if (right != nullptr) {
2324 if (right->type == CargoesFieldType::Industry) return right->u.industry.other_accepted[row];
2325 if (right->type == CargoesFieldType::CargoLabel && right->u.cargo_label.left_align) return right->u.cargo_label.cargoes[row];
2326 }
2327 return INVALID_CARGO;
2328 }
2329 if (row >= col) {
2330 /* Clicked somewhere in-between vertical cargo connection.
2331 * Since the horizontal connection is made in the same order as the vertical list, the above condition
2332 * ensures we are left-below the main diagonal, thus at the supplying side.
2333 */
2334 if (HasBit(this->u.cargo.supp_cargoes, row)) return this->u.cargo.vertical_cargoes[row];
2335 return INVALID_CARGO;
2336 }
2337 /* Clicked at a customer connection. */
2338 if (HasBit(this->u.cargo.cust_cargoes, row)) return this->u.cargo.vertical_cargoes[row];
2339 return INVALID_CARGO;
2340 }
2341
2348 {
2349 assert(this->type == CargoesFieldType::CargoLabel);
2350
2352 uint row;
2353 for (row = 0; row < MAX_CARGOES; row++) {
2354 if (pt.y < vpos) return INVALID_CARGO;
2355 if (pt.y < vpos + GetCharacterHeight(FontSize::Normal)) break;
2357 }
2358 if (row == MAX_CARGOES) return INVALID_CARGO;
2359 return this->u.cargo_label.cargoes[row];
2360 }
2361
2362private:
2370 static void DrawHorConnection(int left, int right, int top, const CargoSpec *csp)
2371 {
2372 GfxDrawLine(left, top, right, top, CARGO_LINE_COLOUR);
2373 GfxFillRect(left, top + 1, right, top + CargoesField::cargo_line.height - 2, csp->legend_colour, FillRectMode::Opaque);
2374 GfxDrawLine(left, top + CargoesField::cargo_line.height - 1, right, top + CargoesField::cargo_line.height - 1, CARGO_LINE_COLOUR);
2375 }
2376};
2377
2378static_assert(MAX_CARGOES >= std::tuple_size_v<decltype(IndustrySpec::produced_cargo)>);
2379static_assert(MAX_CARGOES >= std::tuple_size_v<decltype(IndustrySpec::accepts_cargo)>);
2380
2386
2393
2395
2398
2402
2408 {
2409 CargoesField *ind_fld = this->columns + column;
2410 CargoesField *cargo_fld = this->columns + column + 1;
2411 assert(ind_fld->type == CargoesFieldType::Industry && cargo_fld->type == CargoesFieldType::Cargo);
2412
2413 std::fill(std::begin(ind_fld->u.industry.other_produced), std::end(ind_fld->u.industry.other_produced), INVALID_CARGO);
2414
2415 if (ind_fld->u.industry.ind_type < NUM_INDUSTRYTYPES) {
2416 CargoType others[MAX_CARGOES]; // Produced cargoes not carried in the cargo column.
2417 int other_count = 0;
2418
2419 const IndustrySpec *indsp = GetIndustrySpec(ind_fld->u.industry.ind_type);
2420 assert(CargoesField::max_cargoes <= std::size(indsp->produced_cargo));
2421 for (uint i = 0; i < CargoesField::max_cargoes; i++) {
2422 int col = cargo_fld->ConnectCargo(indsp->produced_cargo[i], true);
2423 if (col < 0) others[other_count++] = indsp->produced_cargo[i];
2424 }
2425
2426 /* Allocate other cargoes in the empty holes of the horizontal cargo connections. */
2427 for (uint i = 0; i < CargoesField::max_cargoes && other_count > 0; i++) {
2428 if (HasBit(cargo_fld->u.cargo.supp_cargoes, i)) ind_fld->u.industry.other_produced[i] = others[--other_count];
2429 }
2430 } else {
2431 /* Houses only display cargo that towns produce. */
2432 for (uint i = 0; i < cargo_fld->u.cargo.num_cargoes; i++) {
2433 CargoType cargo_type = cargo_fld->u.cargo.vertical_cargoes[i];
2435 if (tpe == TownProductionEffect::Passengers || tpe == TownProductionEffect::Mail) cargo_fld->ConnectCargo(cargo_type, true);
2436 }
2437 }
2438 }
2439
2445 void MakeCargoLabel(int column, bool accepting)
2446 {
2447 CargoType cargoes[MAX_CARGOES];
2448 std::fill(std::begin(cargoes), std::end(cargoes), INVALID_CARGO);
2449
2450 CargoesField *label_fld = this->columns + column;
2451 CargoesField *cargo_fld = this->columns + (accepting ? column - 1 : column + 1);
2452
2453 assert(cargo_fld->type == CargoesFieldType::Cargo && label_fld->type == CargoesFieldType::Empty);
2454 for (uint i = 0; i < cargo_fld->u.cargo.num_cargoes; i++) {
2455 int col = cargo_fld->ConnectCargo(cargo_fld->u.cargo.vertical_cargoes[i], !accepting);
2456 if (col >= 0) cargoes[col] = cargo_fld->u.cargo.vertical_cargoes[i];
2457 }
2458 label_fld->MakeCargoLabel(cargoes, accepting);
2459 }
2460
2461
2467 {
2468 CargoesField *ind_fld = this->columns + column;
2469 CargoesField *cargo_fld = this->columns + column - 1;
2470 assert(ind_fld->type == CargoesFieldType::Industry && cargo_fld->type == CargoesFieldType::Cargo);
2471
2472 std::fill(std::begin(ind_fld->u.industry.other_accepted), std::end(ind_fld->u.industry.other_accepted), INVALID_CARGO);
2473
2474 if (ind_fld->u.industry.ind_type < NUM_INDUSTRYTYPES) {
2475 CargoType others[MAX_CARGOES]; // Accepted cargoes not carried in the cargo column.
2476 int other_count = 0;
2477
2478 const IndustrySpec *indsp = GetIndustrySpec(ind_fld->u.industry.ind_type);
2479 assert(CargoesField::max_cargoes <= std::size(indsp->accepts_cargo));
2480 for (uint i = 0; i < CargoesField::max_cargoes; i++) {
2481 int col = cargo_fld->ConnectCargo(indsp->accepts_cargo[i], false);
2482 if (col < 0) others[other_count++] = indsp->accepts_cargo[i];
2483 }
2484
2485 /* Allocate other cargoes in the empty holes of the horizontal cargo connections. */
2486 for (uint i = 0; i < CargoesField::max_cargoes && other_count > 0; i++) {
2487 if (!HasBit(cargo_fld->u.cargo.cust_cargoes, i)) ind_fld->u.industry.other_accepted[i] = others[--other_count];
2488 }
2489 } else {
2490 /* Houses only display what is demanded. */
2491 for (uint i = 0; i < cargo_fld->u.cargo.num_cargoes; i++) {
2492 for (const auto &hs : HouseSpec::Specs()) {
2493 if (!hs.enabled) continue;
2494
2495 for (uint j = 0; j < lengthof(hs.accepts_cargo); j++) {
2496 if (hs.cargo_acceptance[j] > 0 && cargo_fld->u.cargo.vertical_cargoes[i] == hs.accepts_cargo[j]) {
2497 cargo_fld->ConnectCargo(cargo_fld->u.cargo.vertical_cargoes[i], false);
2498 goto next_cargo;
2499 }
2500 }
2501 }
2502next_cargo: ;
2503 }
2504 }
2505 }
2506};
2507
2508
2536struct IndustryCargoesWindow : public Window {
2537 typedef std::vector<CargoesRow> Fields;
2538
2539 Fields fields{};
2540 uint ind_cargo = 0;
2543 Scrollbar *vscroll = nullptr;
2544
2546 {
2547 this->OnInit();
2548 this->CreateNestedTree();
2549 this->vscroll = this->GetScrollbar(WID_IC_SCROLLBAR);
2550 this->FinishInitNested(0);
2551 this->OnInvalidateData(id);
2552 }
2553
2554 void OnInit() override
2555 {
2556 /* Initialize static CargoesField size variables. */
2557 Dimension d = GetStringBoundingBox(STR_INDUSTRY_CARGOES_PRODUCERS);
2558 d = maxdim(d, GetStringBoundingBox(STR_INDUSTRY_CARGOES_CUSTOMERS));
2559 d.width += WidgetDimensions::scaled.frametext.Horizontal();
2560 d.height += WidgetDimensions::scaled.frametext.Vertical();
2561 CargoesField::small_height = d.height;
2562
2563 /* Size of the legend blob -- slightly larger than the smallmap legend blob. */
2565 CargoesField::legend.width = CargoesField::legend.height * 9 / 6;
2566
2567 /* Size of cargo lines. */
2570
2571 /* Size of border between cargo lines and industry boxes. */
2574
2575 /* Size of space between cargo lines. */
2578
2579 /* Size of cargo stub (unconnected cargo line.) */
2581 CargoesField::cargo_stub.height = CargoesField::cargo_line.height; /* Unused */
2582
2585
2586 /* Decide about the size of the box holding the text of an industry type. */
2587 this->ind_textsize.width = 0;
2588 this->ind_textsize.height = 0;
2590 for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
2591 const IndustrySpec *indsp = GetIndustrySpec(it);
2592 if (!indsp->enabled) continue;
2593 this->ind_textsize = maxdim(this->ind_textsize, GetStringBoundingBox(indsp->name));
2594 CargoesField::max_cargoes = std::max<uint>(CargoesField::max_cargoes, std::ranges::count_if(indsp->accepts_cargo, IsValidCargoType));
2595 CargoesField::max_cargoes = std::max<uint>(CargoesField::max_cargoes, std::ranges::count_if(indsp->produced_cargo, IsValidCargoType));
2596 }
2597 d.width = std::max(d.width, this->ind_textsize.width);
2598 d.height = this->ind_textsize.height;
2599 this->ind_textsize = maxdim(this->ind_textsize, GetStringBoundingBox(STR_INDUSTRY_CARGOES_SELECT_INDUSTRY));
2600
2601 /* Compute max size of the cargo texts. */
2602 this->cargo_textsize.width = 0;
2603 this->cargo_textsize.height = 0;
2604 for (const CargoSpec *csp : CargoSpec::Iterate()) {
2605 this->cargo_textsize = maxdim(this->cargo_textsize, GetStringBoundingBox(csp->name));
2606 }
2607 d = maxdim(d, this->cargo_textsize); // Box must also be wide enough to hold any cargo label.
2608 this->cargo_textsize = maxdim(this->cargo_textsize, GetStringBoundingBox(STR_INDUSTRY_CARGOES_SELECT_CARGO));
2609
2610 d.width += WidgetDimensions::scaled.frametext.Horizontal();
2611 /* Ensure the height is enough for the industry type text, for the horizontal connections, and for the cargo labels. */
2613 d.height = std::max(d.height + WidgetDimensions::scaled.frametext.Vertical(), min_ind_height);
2614
2617
2618 /* Width of a #CargoesFieldType::Cargo field. */
2620 }
2621
2622 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
2623 {
2624 switch (widget) {
2625 case WID_IC_PANEL:
2626 fill.height = resize.height = CargoesField::normal_height;
2627 size.width = CargoesField::industry_width * 3 + CargoesField::cargo_field_width * 2 + WidgetDimensions::scaled.frametext.Horizontal();
2628 size.height = CargoesField::small_height + 2 * resize.height + WidgetDimensions::scaled.frametext.Vertical();
2629 break;
2630
2632 size.width = std::max(size.width, this->ind_textsize.width + padding.width);
2633 break;
2634
2636 size.width = std::max(size.width, this->cargo_textsize.width + padding.width);
2637 break;
2638 }
2639 }
2640
2641 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
2642 {
2643 if (widget != WID_IC_CAPTION) return this->Window::GetWidgetString(widget, stringid);
2644
2645 if (this->ind_cargo < NUM_INDUSTRYTYPES) {
2646 const IndustrySpec *indsp = GetIndustrySpec(this->ind_cargo);
2647 return GetString(STR_INDUSTRY_CARGOES_INDUSTRY_CAPTION, indsp->name);
2648 } else {
2649 const CargoSpec *csp = CargoSpec::Get(this->ind_cargo - NUM_INDUSTRYTYPES);
2650 return GetString(STR_INDUSTRY_CARGOES_CARGO_CAPTION, csp->name);
2651 }
2652 }
2653
2660 static bool HasCommonValidCargo(const std::span<const CargoType> cargoes1, const std::span<const CargoType> cargoes2)
2661 {
2662 for (const CargoType cargo_type1 : cargoes1) {
2663 if (!IsValidCargoType(cargo_type1)) continue;
2664 for (const CargoType cargo_type2 : cargoes2) {
2665 if (cargo_type1 == cargo_type2) return true;
2666 }
2667 }
2668 return false;
2669 }
2670
2676 static bool HousesCanSupply(const std::span<const CargoType> cargoes)
2677 {
2678 for (const CargoType cargo_type : cargoes) {
2679 if (!IsValidCargoType(cargo_type)) continue;
2681 if (tpe == TownProductionEffect::Passengers || tpe == TownProductionEffect::Mail) return true;
2682 }
2683 return false;
2684 }
2685
2691 static bool HousesCanAccept(const std::span<const CargoType> cargoes)
2692 {
2693 HouseZones climate_mask = GetClimateMaskForLandscape();
2694
2695 for (const CargoType cargo_type : cargoes) {
2696 if (!IsValidCargoType(cargo_type)) continue;
2697
2698 for (const auto &hs : HouseSpec::Specs()) {
2699 if (!hs.enabled || !hs.building_availability.Any(climate_mask)) continue;
2700
2701 for (uint j = 0; j < lengthof(hs.accepts_cargo); j++) {
2702 if (hs.cargo_acceptance[j] > 0 && cargo_type == hs.accepts_cargo[j]) return true;
2703 }
2704 }
2705 }
2706 return false;
2707 }
2708
2714 static int CountMatchingAcceptingIndustries(const std::span<const CargoType> cargoes)
2715 {
2716 int count = 0;
2717 for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
2718 const IndustrySpec *indsp = GetIndustrySpec(it);
2719 if (!indsp->enabled) continue;
2720
2721 if (HasCommonValidCargo(cargoes, indsp->accepts_cargo)) count++;
2722 }
2723 return count;
2724 }
2725
2731 static int CountMatchingProducingIndustries(const std::span<const CargoType> cargoes)
2732 {
2733 int count = 0;
2734 for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
2735 const IndustrySpec *indsp = GetIndustrySpec(it);
2736 if (!indsp->enabled) continue;
2737
2738 if (HasCommonValidCargo(cargoes, indsp->produced_cargo)) count++;
2739 }
2740 return count;
2741 }
2742
2749 void ShortenCargoColumn(int column, int top, int bottom)
2750 {
2751 while (top < bottom && !this->fields[top].columns[column].HasConnection()) {
2752 this->fields[top].columns[column].MakeEmpty(CargoesFieldType::Empty);
2753 top++;
2754 }
2755 this->fields[top].columns[column].u.cargo.top_end = true;
2756
2757 while (bottom > top && !this->fields[bottom].columns[column].HasConnection()) {
2758 this->fields[bottom].columns[column].MakeEmpty(CargoesFieldType::Empty);
2759 bottom--;
2760 }
2761 this->fields[bottom].columns[column].u.cargo.bottom_end = true;
2762 }
2763
2770 void PlaceIndustry(int row, int col, IndustryType it)
2771 {
2772 assert(this->fields[row].columns[col].type == CargoesFieldType::Empty);
2773 this->fields[row].columns[col].MakeIndustry(it);
2774 if (col == 0) {
2775 this->fields[row].ConnectIndustryProduced(col);
2776 } else {
2777 this->fields[row].ConnectIndustryAccepted(col);
2778 }
2779 }
2780
2785 {
2786 if (!this->IsWidgetLowered(WID_IC_NOTIFY)) return;
2787
2788 /* Only notify the smallmap window if it exists. In particular, do not
2789 * bring it to the front to prevent messing up any nice layout of the user. */
2790 InvalidateWindowClassesData(WindowClass::SmallMap, 0);
2791 }
2792
2797 void ComputeIndustryDisplay(IndustryType displayed_it)
2798 {
2799 this->ind_cargo = displayed_it;
2800 _displayed_industries.reset();
2801 _displayed_industries.set(displayed_it);
2802
2803 this->fields.clear();
2804 CargoesRow &first_row = this->fields.emplace_back();
2805 first_row.columns[0].MakeHeader(STR_INDUSTRY_CARGOES_PRODUCERS);
2809 first_row.columns[4].MakeHeader(STR_INDUSTRY_CARGOES_CUSTOMERS);
2810
2811 const IndustrySpec *central_sp = GetIndustrySpec(displayed_it);
2812 bool houses_supply = HousesCanSupply(central_sp->accepts_cargo);
2813 bool houses_accept = HousesCanAccept(central_sp->produced_cargo);
2814 /* Make a field consisting of two cargo columns. */
2815 int num_supp = CountMatchingProducingIndustries(central_sp->accepts_cargo) + houses_supply;
2816 int num_cust = CountMatchingAcceptingIndustries(central_sp->produced_cargo) + houses_accept;
2817 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.
2818 for (int i = 0; i < num_indrows; i++) {
2819 CargoesRow &row = this->fields.emplace_back();
2821 row.columns[1].MakeCargo(central_sp->accepts_cargo);
2823 row.columns[3].MakeCargo(central_sp->produced_cargo);
2825 }
2826 /* Add central industry. */
2827 int central_row = 1 + num_indrows / 2;
2828 this->fields[central_row].columns[2].MakeIndustry(displayed_it);
2829 this->fields[central_row].ConnectIndustryProduced(2);
2830 this->fields[central_row].ConnectIndustryAccepted(2);
2831
2832 /* Add cargo labels. */
2833 this->fields[central_row - 1].MakeCargoLabel(2, true);
2834 this->fields[central_row + 1].MakeCargoLabel(2, false);
2835
2836 /* Add suppliers and customers of the 'it' industry. */
2837 int supp_count = 0;
2838 int cust_count = 0;
2839 for (IndustryType it : _sorted_industry_types) {
2840 const IndustrySpec *indsp = GetIndustrySpec(it);
2841 if (!indsp->enabled) continue;
2842
2843 if (HasCommonValidCargo(central_sp->accepts_cargo, indsp->produced_cargo)) {
2844 this->PlaceIndustry(1 + supp_count * num_indrows / num_supp, 0, it);
2845 _displayed_industries.set(it);
2846 supp_count++;
2847 }
2848 if (HasCommonValidCargo(central_sp->produced_cargo, indsp->accepts_cargo)) {
2849 this->PlaceIndustry(1 + cust_count * num_indrows / num_cust, 4, it);
2850 _displayed_industries.set(it);
2851 cust_count++;
2852 }
2853 }
2854 if (houses_supply) {
2855 this->PlaceIndustry(1 + supp_count * num_indrows / num_supp, 0, NUM_INDUSTRYTYPES);
2856 supp_count++;
2857 }
2858 if (houses_accept) {
2859 this->PlaceIndustry(1 + cust_count * num_indrows / num_cust, 4, NUM_INDUSTRYTYPES);
2860 cust_count++;
2861 }
2862
2863 this->ShortenCargoColumn(1, 1, num_indrows);
2864 this->ShortenCargoColumn(3, 1, num_indrows);
2865 this->vscroll->SetCount(num_indrows);
2866 this->SetDirty();
2867 this->NotifySmallmap();
2868 }
2869
2875 {
2876 this->ind_cargo = cargo_type + NUM_INDUSTRYTYPES;
2877 _displayed_industries.reset();
2878
2879 this->fields.clear();
2880 CargoesRow &first_row = this->fields.emplace_back();
2881 first_row.columns[0].MakeHeader(STR_INDUSTRY_CARGOES_PRODUCERS);
2883 first_row.columns[2].MakeHeader(STR_INDUSTRY_CARGOES_CUSTOMERS);
2886
2887 auto cargoes = std::span(&cargo_type, 1);
2888 bool houses_supply = HousesCanSupply(cargoes);
2889 bool houses_accept = HousesCanAccept(cargoes);
2890 int num_supp = CountMatchingProducingIndustries(cargoes) + houses_supply + 1; // Ensure room for the cargo label.
2891 int num_cust = CountMatchingAcceptingIndustries(cargoes) + houses_accept;
2892 int num_indrows = std::max(num_supp, num_cust);
2893 for (int i = 0; i < num_indrows; i++) {
2894 CargoesRow &row = this->fields.emplace_back();
2896 row.columns[1].MakeCargo(cargoes);
2900 }
2901
2902 this->fields[num_indrows].MakeCargoLabel(0, false); // Add cargo labels at the left bottom.
2903
2904 /* Add suppliers and customers of the cargo. */
2905 int supp_count = 0;
2906 int cust_count = 0;
2907 for (IndustryType it : _sorted_industry_types) {
2908 const IndustrySpec *indsp = GetIndustrySpec(it);
2909 if (!indsp->enabled) continue;
2910
2911 if (HasCommonValidCargo(cargoes, indsp->produced_cargo)) {
2912 this->PlaceIndustry(1 + supp_count * num_indrows / num_supp, 0, it);
2913 _displayed_industries.set(it);
2914 supp_count++;
2915 }
2916 if (HasCommonValidCargo(cargoes, indsp->accepts_cargo)) {
2917 this->PlaceIndustry(1 + cust_count * num_indrows / num_cust, 2, it);
2918 _displayed_industries.set(it);
2919 cust_count++;
2920 }
2921 }
2922 if (houses_supply) {
2923 this->PlaceIndustry(1 + supp_count * num_indrows / num_supp, 0, NUM_INDUSTRYTYPES);
2924 supp_count++;
2925 }
2926 if (houses_accept) {
2927 this->PlaceIndustry(1 + cust_count * num_indrows / num_cust, 2, NUM_INDUSTRYTYPES);
2928 cust_count++;
2929 }
2930
2931 this->ShortenCargoColumn(1, 1, num_indrows);
2932 this->vscroll->SetCount(num_indrows);
2933 this->SetDirty();
2934 this->NotifySmallmap();
2935 }
2936
2944 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
2945 {
2946 if (!gui_scope) return;
2947 if (data == NUM_INDUSTRYTYPES) {
2949 return;
2950 }
2951
2952 assert(data >= 0 && data < NUM_INDUSTRYTYPES);
2953 this->ComputeIndustryDisplay(data);
2954 }
2955
2956 void DrawWidget(const Rect &r, WidgetID widget) const override
2957 {
2958 if (widget != WID_IC_PANEL) return;
2959
2960 Rect ir = r.Shrink(WidgetDimensions::scaled.bevel);
2961 DrawPixelInfo tmp_dpi;
2962 if (!FillDrawPixelInfo(&tmp_dpi, ir)) return;
2963 AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi);
2964
2965 int left_pos = WidgetDimensions::scaled.frametext.left - WidgetDimensions::scaled.bevel.left;
2966 if (this->ind_cargo >= NUM_INDUSTRYTYPES) left_pos += (CargoesField::industry_width + CargoesField::cargo_field_width) / 2;
2967 int last_column = (this->ind_cargo < NUM_INDUSTRYTYPES) ? 4 : 2;
2968
2970 int vpos = WidgetDimensions::scaled.frametext.top - WidgetDimensions::scaled.bevel.top - this->vscroll->GetPosition() * nwp->resize_y;
2971 int row_height = CargoesField::small_height;
2972 for (const auto &field : this->fields) {
2973 if (vpos + row_height >= 0) {
2974 int xpos = left_pos;
2975 int col, dir;
2976 if (_current_text_dir == TD_RTL) {
2977 col = last_column;
2978 dir = -1;
2979 } else {
2980 col = 0;
2981 dir = 1;
2982 }
2983 while (col >= 0 && col <= last_column) {
2984 field.columns[col].Draw(xpos, vpos);
2986 col += dir;
2987 }
2988 }
2989 vpos += row_height;
2990 if (vpos >= height) break;
2991 row_height = CargoesField::normal_height;
2992 }
2993 }
2994
3003 {
3005 pt.x -= nw->pos_x;
3006 pt.y -= nw->pos_y;
3007
3008 int vpos = WidgetDimensions::scaled.frametext.top + CargoesField::small_height - this->vscroll->GetPosition() * nw->resize_y;
3009 if (pt.y < vpos) return false;
3010
3011 int row = (pt.y - vpos) / CargoesField::normal_height; // row is relative to row 1.
3012 if (row + 1 >= (int)this->fields.size()) return false;
3013 vpos = pt.y - vpos - row * CargoesField::normal_height; // Position in the row + 1 field
3014 row++; // rebase row to match index of this->fields.
3015
3016 int xpos = 2 * WidgetDimensions::scaled.frametext.left + ((this->ind_cargo < NUM_INDUSTRYTYPES) ? 0 : (CargoesField::industry_width + CargoesField::cargo_field_width) / 2);
3017 if (pt.x < xpos) return false;
3018 int column;
3019 for (column = 0; column <= 5; column++) {
3021 if (pt.x < xpos + width) break;
3022 xpos += width;
3023 }
3024 int num_columns = (this->ind_cargo < NUM_INDUSTRYTYPES) ? 4 : 2;
3025 if (column > num_columns) return false;
3026 xpos = pt.x - xpos;
3027
3028 /* Return both positions, compensating for RTL languages (which works due to the equal symmetry in both displays). */
3029 fieldxy->y = row;
3030 xy->y = vpos;
3031 if (_current_text_dir == TD_RTL) {
3032 fieldxy->x = num_columns - column;
3033 xy->x = ((column & 1) ? CargoesField::cargo_field_width : CargoesField::industry_width) - xpos;
3034 } else {
3035 fieldxy->x = column;
3036 xy->x = xpos;
3037 }
3038 return true;
3039 }
3040
3041 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
3042 {
3043 switch (widget) {
3044 case WID_IC_PANEL: {
3045 Point fieldxy, xy;
3046 if (!CalculatePositionInWidget(pt, &fieldxy, &xy)) return;
3047
3048 const CargoesField *fld = this->fields[fieldxy.y].columns + fieldxy.x;
3049 switch (fld->type) {
3052 break;
3053
3055 CargoesField *lft = (fieldxy.x > 0) ? this->fields[fieldxy.y].columns + fieldxy.x - 1 : nullptr;
3056 CargoesField *rgt = (fieldxy.x < 4) ? this->fields[fieldxy.y].columns + fieldxy.x + 1 : nullptr;
3057 CargoType cargo_type = fld->CargoClickedAt(lft, rgt, xy);
3058 if (IsValidCargoType(cargo_type)) this->ComputeCargoDisplay(cargo_type);
3059 break;
3060 }
3061
3063 CargoType cargo_type = fld->CargoLabelClickedAt(xy);
3064 if (IsValidCargoType(cargo_type)) this->ComputeCargoDisplay(cargo_type);
3065 break;
3066 }
3067
3068 default:
3069 break;
3070 }
3071 break;
3072 }
3073
3074 case WID_IC_NOTIFY:
3077 SndClickBeep();
3078
3079 if (this->IsWidgetLowered(WID_IC_NOTIFY)) {
3080 if (FindWindowByClass(WindowClass::SmallMap) == nullptr) ShowSmallMap();
3081 this->NotifySmallmap();
3082 }
3083 break;
3084
3085 case WID_IC_CARGO_DROPDOWN: {
3086 DropDownList lst;
3088 for (const CargoSpec *cs : _sorted_standard_cargo_specs) {
3089 lst.push_back(MakeDropDownListIconItem(d, cs->GetCargoIcon(), PAL_NONE, cs->name, cs->Index()));
3090 }
3091 if (!lst.empty()) {
3092 static std::string cargo_filter;
3093 int selected = (this->ind_cargo >= NUM_INDUSTRYTYPES) ? (int)(this->ind_cargo - NUM_INDUSTRYTYPES) : -1;
3094 ShowDropDownList(this, std::move(lst), selected, WID_IC_CARGO_DROPDOWN, 0, DropDownOption::Filterable, &cargo_filter);
3095 }
3096 break;
3097 }
3098
3099 case WID_IC_IND_DROPDOWN: {
3100 DropDownList lst;
3101 for (IndustryType ind : _sorted_industry_types) {
3102 const IndustrySpec *indsp = GetIndustrySpec(ind);
3103 if (!indsp->enabled) continue;
3104 lst.push_back(MakeDropDownListStringItem(indsp->name, ind));
3105 }
3106 if (!lst.empty()) {
3107 static std::string cargo_filter;
3108 int selected = (this->ind_cargo < NUM_INDUSTRYTYPES) ? (int)this->ind_cargo : -1;
3109 ShowDropDownList(this, std::move(lst), selected, WID_IC_IND_DROPDOWN, 0, DropDownOption::Filterable, &cargo_filter);
3110 }
3111 break;
3112 }
3113 }
3114 }
3115
3116 void OnDropdownSelect(WidgetID widget, int index, int) override
3117 {
3118 if (index < 0) return;
3119
3120 switch (widget) {
3122 this->ComputeCargoDisplay(static_cast<CargoType>(index));
3123 break;
3124
3126 this->ComputeIndustryDisplay(index);
3127 break;
3128 }
3129 }
3130
3131 bool OnTooltip([[maybe_unused]] Point pt, WidgetID widget, TooltipCloseCondition close_cond) override
3132 {
3133 if (widget != WID_IC_PANEL) return false;
3134
3135 Point fieldxy, xy;
3136 if (!CalculatePositionInWidget(pt, &fieldxy, &xy)) return false;
3137
3138 const CargoesField *fld = this->fields[fieldxy.y].columns + fieldxy.x;
3139 CargoType cargo_type = INVALID_CARGO;
3140 switch (fld->type) {
3142 CargoesField *lft = (fieldxy.x > 0) ? this->fields[fieldxy.y].columns + fieldxy.x - 1 : nullptr;
3143 CargoesField *rgt = (fieldxy.x < 4) ? this->fields[fieldxy.y].columns + fieldxy.x + 1 : nullptr;
3144 cargo_type = fld->CargoClickedAt(lft, rgt, xy);
3145 break;
3146 }
3147
3149 cargo_type = fld->CargoLabelClickedAt(xy);
3150 break;
3151 }
3152
3154 if (fld->u.industry.ind_type < NUM_INDUSTRYTYPES && (this->ind_cargo >= NUM_INDUSTRYTYPES || fieldxy.x != 2)) {
3155 GuiShowTooltips(this, GetEncodedString(STR_INDUSTRY_CARGOES_INDUSTRY_TOOLTIP), close_cond);
3156 }
3157 return true;
3158
3159 default:
3160 break;
3161 }
3163 const CargoSpec *csp = CargoSpec::Get(cargo_type);
3164 GuiShowTooltips(this, GetEncodedString(STR_INDUSTRY_CARGOES_CARGO_TOOLTIP, csp->name), close_cond);
3165 return true;
3166 }
3167
3168 return false;
3169 }
3170
3171 void OnResize() override
3172 {
3173 this->vscroll->SetCapacityFromWidget(this, WID_IC_PANEL, WidgetDimensions::scaled.framerect.Vertical() + CargoesField::small_height);
3174 }
3175};
3176
3181static void ShowIndustryCargoesWindow(IndustryType id)
3182{
3183 if (id >= NUM_INDUSTRYTYPES) {
3184 for (IndustryType ind : _sorted_industry_types) {
3185 const IndustrySpec *indsp = GetIndustrySpec(ind);
3186 if (indsp->enabled) {
3187 id = ind;
3188 break;
3189 }
3190 }
3191 if (id >= NUM_INDUSTRYTYPES) return;
3192 }
3193
3194 Window *w = BringWindowToFrontById(WindowClass::IndustryCargoes, 0);
3195 if (w != nullptr) {
3196 w->InvalidateData(id);
3197 return;
3198 }
3199 new IndustryCargoesWindow(id);
3200}
3201
Class for backupping variables and making sure they are restored later.
static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
Types related to cargoes...
bool IsValidCargoType(CargoType cargo)
Test whether cargo type is not INVALID_CARGO.
Definition cargo_type.h:110
StrongType::Typedef< uint32_t, struct CargoLabelTag, StrongType::Compare > CargoLabel
Globally unique label of a cargo type.
Definition cargo_type.h:17
CargoType
Cargo slots to indicate a cargo type within a game.
Definition cargo_type.h:22
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:36
@ Mail
Cargo behaves mail-like for production.
Definition cargotype.h:39
@ Passengers
Cargo behaves passenger-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 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.
std::string GetDecodedString() const
Decode the encoded string.
Definition strings.cpp:207
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.
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.
bool(const const Industry **item, const std::pair< CargoType, CargoType > &filter) FilterFunction
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.
~IndustryDirectoryWindow() override
Save the last sorting state.
const 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.
static const StringID sorter_names[]
Strings describing how industries are sorted.
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.
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.
SorterType
Ways to sort industries.
@ 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 const std::initializer_list< GUIIndustryList::SortFunction *const > sorter_funcs
Functions to sort industries.
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.
static bool IndustryNameSorter(const Industry *const &a, const Industry *const &b, const CargoType &filter)
Sort industries by 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.
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.
void OnDropdownSelect(WidgetID widget, int index, int) override
A dropdown option associated to this window has been selected.
CargoType produced_cargo_filter_criteria
Selected produced cargo filter index.
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.
ClickedCargoLine editbox_line
The line clicked to open the edit box.
void OnTimeout() override
Called when this window's timeout has been reached.
~IndustryViewWindow() override
Close the industry production window.
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 OnMouseWheel(int wheel, WidgetID widget) override
The mouse wheel has been turned.
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.
static constexpr ClickedCargoLine CCL_NONE
No cargo line has been clicked.
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.
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.
int8_t ClickedCargoLine
Clicked cargo line in the info panel.
int DrawInfo(const Rect &r)
Draw the text in the WID_IV_INFO panel.
ClickedCargoLine clicked_line
The line of the button that has been clicked.
static constexpr ClickedCargoLine CCL_MULTIPLIER
Product multiplier line is clicked.
Editability
Modes for changing production.
@ Rate
Allow changing the production rates.
@ Multiplier
Allow changing the production multiplier.
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:2434
void InitializeViewport(Window *w, std::variant< TileIndex, VehicleID > focus, ZoomLevel zoom)
Initialize the viewport of the window.
Definition widget.cpp:2425
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:2531
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.
static WidgetDimensions scaled
Widget dimensions scaled for current zoom level.
Definition window_gui.h:30
static const WidgetDimensions unscaled
Unscaled widget dimensions.
Definition window_gui.h:93
Map accessors for 'clear' tiles.
@ Fields
Farm fields (3).
Definition clear_map.h:25
@ Grass
Plain grass with dirt transition (0-3).
Definition clear_map.h:22
void MakeClear(Tile t, ClearGround g, uint density)
Make a clear tile.
Definition clear_map.h:253
ClearGround GetClearGround(Tile t)
Get the type of clear tile.
Definition clear_map.h:52
Functions related to 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, DropDownOptions options, std::string *const persistent_filter_text)
Show a dropdown menu window near a widget of the parent window.
Definition dropdown.cpp:629
std::unique_ptr< DropDownListItem > MakeDropDownListIconItem(SpriteID sprite, PaletteID palette, StringID str, int value, bool masked, bool shaded)
Creates new DropDownListIconItem.
Definition dropdown.cpp:70
std::unique_ptr< DropDownListItem > MakeDropDownListStringItem(StringID str, int value, bool masked, bool shaded)
Creates new DropDownListStringItem.
Definition dropdown.cpp:49
void ShowDropDownList(Window *w, DropDownList &&list, int selected, WidgetID button, uint width, DropDownOptions options, std::string *const persistent_filter_text)
Show a drop down list.
Definition dropdown.cpp:587
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.
@ Filterable
Set if the dropdown is filterable.
uint ScaleByCargoScale(uint num, bool town)
Scale a number by the cargo scale setting.
constexpr std::underlying_type_t< enum_type > to_underlying(enum_type e)
Implementation of std::to_underlying (from C++23).
Definition enum_type.hpp:21
Functions related to errors.
@ 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:88
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.
int CentreBounds(int min, int max, int size)
Determine where to position a centred object.
Dimension GetSpriteSize(SpriteID sprid, Point *offset, ZoomLevel zoom)
Get the size of a sprite.
Definition gfx.cpp:971
Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
Return the string dimension in pixels.
Definition gfx.cpp:899
Dimension GetStringListBoundingBox(std::span< const StringID > list, FontSize fontsize)
Get maximum dimension of a list of strings.
Definition gfx.cpp:938
bool _ctrl_pressed
Is Ctrl pressed?
Definition gfx.cpp:39
int DrawStringMultiLine(int left, int right, int top, int bottom, std::string_view str, ExtendedTextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly over multiple lines.
Definition gfx.cpp:787
int DrawString(int left, int right, int top, std::string_view str, ExtendedTextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly truncated to make it fit in its allocated space.
Definition gfx.cpp:668
void DrawRectOutline(const Rect &r, PixelColour colour, int width, int dash)
Draw the outline of a Rect.
Definition gfx.cpp:464
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:1037
void GfxFillRect(int left, int top, int right, int bottom, const std::variant< PixelColour, PaletteID > &colour, FillRectMode mode)
Applies a certain FillRectMode-operation to a rectangle [left, right] x [top, bottom] on the screen.
Definition gfx.cpp:116
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:1572
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
Definition gfx_type.h:17
@ Small
Index of the small font in the font tables.
Definition gfx_type.h:250
@ Normal
Index of the normal font in the font tables.
Definition gfx_type.h:249
@ SA_LEFT
Left align the text.
Definition gfx_type.h:434
@ SA_RIGHT
Right align the text (must be a single bit).
Definition gfx_type.h:436
@ SA_HOR_CENTER
Horizontally center the text.
Definition gfx_type.h:435
@ Invalid
Invalid marker.
Definition gfx_type.h:302
@ Yellow
Yellow.
Definition gfx_type.h:288
@ Cream
Cream.
Definition gfx_type.h:294
@ Brown
Brown.
Definition gfx_type.h:298
@ DarkGreen
Dark green.
Definition gfx_type.h:292
@ Forced
Ignore colour changes from strings.
Definition gfx_type.h:343
@ White
White colour.
Definition gfx_type.h:330
@ Yellow
Yellow colour.
Definition gfx_type.h:326
@ FromString
Marker for telling to use the colour from the string.
Definition gfx_type.h:317
@ Orange
Orange colour.
Definition gfx_type.h:324
@ Grey
Grey colour.
Definition gfx_type.h:332
@ Black
Black colour.
Definition gfx_type.h:334
@ Opaque
Fill rectangle with a single colour.
Definition gfx_type.h:391
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 SetMinimalTextLines(uint8_t lines, uint8_t spacing, FontSize size=FontSize::Normal)
Widget part function for setting the minimal text lines.
constexpr NWidgetPart SetMinimalSize(int16_t x, int16_t y)
Widget part function for setting the minimal size.
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 NWidget(WidgetType tp, Colours col, WidgetID idx=INVALID_WIDGET)
Widget part function for starting a new 'real' widget.
constexpr NWidgetPart SetResize(int16_t dx, int16_t dy)
Widget part function for setting the resize step.
void SetDirty() const
Mark entire window as dirty (in need of re-paint).
Definition window.cpp:980
void MarkWholeScreenDirty()
This function mark the whole screen as dirty.
Definition gfx.cpp:1553
GUI functions that shouldn't be here.
void ShowExtraViewportWindow(TileIndex tile=INVALID_TILE)
Show a new Extra Viewport window.
Hotkey related functions.
HouseZones GetClimateMaskForLandscape()
Get the HouseZones climate mask for the current landscape type.
Base of all industries.
static constexpr uint8_t PRODLEVEL_MAXIMUM
the industry is running at full speed
Definition industry.h:38
static constexpr uint8_t PRODLEVEL_DEFAULT
default level set when the industry is created
Definition industry.h:37
static constexpr uint8_t PRODLEVEL_MINIMUM
below this level, the industry is set to be closing
Definition industry.h:36
static constexpr uint8_t PRODLEVEL_CLOSURE
signal set to actually close the industry
Definition industry.h:35
const IndustrySpec * GetIndustrySpec(IndustryType thistype)
Accessor for array _industry_specs.
Command definitions related to industries.
static constexpr std::initializer_list< NWidgetPart > _nested_industry_directory_widgets
Widget definition of the industry directory 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).
@ View
View-industry window.
@ Fund
Fund-industry window.
@ Directory
Industry-directory window.
static const uint MAX_CARGOES
Maximum number of cargoes carried in a CargoesFieldType::Cargo field in CargoesField.
static WindowDesc _industry_directory_desc(WindowPosition::Automatic, "list_industries", 428, 190, WindowClass::IndustryDirectory, WindowClass::None, {}, _nested_industry_directory_widgets, &IndustryDirectoryWindow::hotkeys)
Window definition of the industry directory gui.
static bool CargoFilter(const Industry *const *industry, const std::pair< CargoType, CargoType > &cargoes)
Cargo filter functions.
CargoSuffixDirection
Cargo direction for cargo suffixes.
@ Out
Get cargo suffix for output cargoes.
@ In
Get cargo suffix for input cargoes.
void GenerateIndustries()
This function will create random industries during game creation.
static WindowDesc _build_industry_desc(WindowPosition::Automatic, "build_industry", 170, 212, WindowClass::BuildIndustry, WindowClass::None, WindowDefaultFlag::Construction, _nested_build_industry_widgets)
Window definition of the dynamic place industries gui.
static bool IndustryTypeNameSorter(const IndustryType &a, const IndustryType &b)
Sort industry types by their name.
CargoSuffixDisplay
Ways of displaying the cargo.
@ AmountAndText
Display the cargo, amount, and string (cb37 result 000-3FF).
@ None
Display the cargo without sub-type (cb37 result 401).
@ Text
Display the cargo and supplied string (cb37 result 800-BFF).
@ Amount
Display the cargo and amount (if useful), but no sub-type (cb37 result 400 or fail).
static WindowDesc _industry_view_desc(WindowPosition::Automatic, "view_industry", 260, 120, WindowClass::IndustryView, WindowClass::None, {}, _nested_industry_view_widgets)
Window definition of the view industry gui.
std::array< IndustryType, NUM_INDUSTRYTYPES > _sorted_industry_types
Industry types sorted by name.
static WindowDesc _industry_cargoes_desc(WindowPosition::Automatic, "industry_cargoes", 300, 210, WindowClass::IndustryCargoes, WindowClass::None, {}, _nested_industry_cargoes_widgets)
Window description for the industry cargoes window.
static void GetAllCargoSuffixes(CargoSuffixDirection 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).
std::bitset< NUM_INDUSTRYTYPES > _displayed_industries
Communication from the industry chain window to the smallmap window about what industries to display.
static constexpr std::initializer_list< NWidgetPart > _nested_industry_view_widgets
Widget definition of the view industry gui.
CargoesFieldType
Available types of field.
@ SmallEmpty
Empty small field (for the header).
@ CargoLabel
Display cargo labels.
@ Industry
Display industry.
@ Header
Header text.
@ Empty
Empty field.
@ Cargo
Display cargo connections.
void SortIndustryTypes()
Initialize the list of sorted industry types.
static constexpr std::initializer_list< NWidgetPart > _nested_industry_cargoes_widgets
Widgets of the industry cargoes window.
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.
#define Rect
Macro that prevents name conflicts between included headers.
#define Point
Macro that prevents name conflicts between included headers.
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
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.
constexpr To ClampTo(From value)
Clamp the given value down to lie within the requested type.
void GuiShowTooltips(Window *parent, EncodedString &&text, TooltipCloseCondition close_tooltip)
Shows a tooltip.
Definition misc_gui.cpp:690
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:67
Basic functions/variables used all over the place.
@ Industries
Industries feature.
Definition newgrf.h:84
Functions related to NewGRF badges.
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 &gui_classes, std::span< const BadgeID > badges, GrfSpecFeature feature, std::optional< TimerGameCalendar::Date > introduction_date, PaletteID remap)
Draw a badge column group.
GUI functions related to NewGRF badges.
@ CargoSuffix
Show suffix after cargo name.
@ 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.
uint16_t GetIndustryCallback(CallbackID callback, uint32_t param1, uint32_t param2, Industry *industry, IndustryType type, TileIndex tile, std::span< int32_t > regs100)
Perform an industry callback.
uint32_t GetIndustryProbabilityCallback(IndustryType type, IndustryAvailabilityCallType creation_type, uint32_t default_prob)
Check with callback CBID_INDUSTRY_PROBABILITY whether the industry can be built.
bool IndustryTemporarilyRefusesCargo(Industry *ind, CargoType cargo_type)
Check whether an industry temporarily refuses to accept a certain cargo.
Functions for NewGRF industries.
@ 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, std::span< const int32_t > textstack)
Format a GRF string using the text ref stack for parameters.
Header of Action 04 "universal holder" structure and functions.
StrongType::Typedef< uint32_t, struct GRFStringIDTag, StrongType::Compare, StrongType::Integer > GRFStringID
Type for GRF-internal string IDs.
static constexpr GRFStringID GRFSTR_MISC_GRF_TEXT
Miscellaneous GRF text range.
@ Editor
In the scenario editor.
Definition openttd.h:21
static constexpr PixelColour PC_YELLOW
Yellow palette colour.
static constexpr PixelColour PC_BLACK
Black palette colour.
static constexpr PixelColour PC_WHITE
White 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:61
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:60
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.
@ Off
Scroll wheel has no effect.
void ShowSmallMap()
Show the smallmap window.
Smallmap GUI functions.
Base types for having sorted lists in GUIs.
void SndClickBeep()
Play a beep sound for a click event if enabled in settings.
Definition sound.cpp:254
Functions related to sound.
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:429
Parse strings.
static std::optional< T > ParseInteger(std::string_view arg, int base=10, bool clamp=false)
Change a string into its number representation.
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
std::string_view GetListSeparator()
Get the list separator string for the current language.
Definition strings.cpp:299
void AppendStringInPlace(std::string &result, StringID string)
Resolve the given StringID and append in place into an existing std::string with formatting but no pa...
Definition strings.cpp:434
EncodedString GetEncodedString(StringID str)
Encode a string with no parameters into an encoded string.
Definition strings.cpp:90
std::string GetString(StringID string)
Resolve the given StringID into a std::string with formatting but no parameters.
Definition strings.cpp:424
TextDirection _current_text_dir
Text direction of the currently selected language.
Definition strings.cpp:56
uint64_t GetParamMaxDigits(uint count, FontSize size)
Get some number that is suitable for string size computations.
Definition strings.cpp:218
Functions related to OTTD's strings.
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...
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:75
static CargoSpec * Get(size_t index)
Retrieve cargo details for the given cargo type.
Definition cargotype.h:139
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:92
TownProductionEffect town_production_effect
The effect on town cargo production.
Definition cargotype.h:88
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:242
Data about a single field in the IndustryCargoesWindow panel.
static const PixelColour CARGO_LINE_COLOUR
Line colour around the cargo.
StringID header
Header text (for CargoesFieldType::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.
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 CargoesFieldType::Cargo, compute the left position of the left-most vertical cargo connection.
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 CargoesFieldType::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 CargoesFieldType::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.
IndustryType ind_type
Industry type (NUM_INDUSTRYTYPES means 'houses').
struct CargoesField::@163200273102357245024106251007063276336245070176::@243103260313115260077253264317120110047327036000 cargo_label
Label data (for CargoesFieldType::CargoLabel).
struct CargoesField::@163200273102357245024106251007063276336245070176::@267143254045067360107006147207152052155243026163 cargo
Cargo data (for CargoesFieldType::Cargo).
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.
static const PixelColour INDUSTRY_LINE_COLOUR
Line colour of the industry type box.
CargoType CargoClickedAt(const CargoesField *left, const CargoesField *right, Point pt) const
Decide which cargo was clicked at in a CargoesFieldType::Cargo field.
union CargoesField::@163200273102357245024106251007063276336245070176 u
Data for each type.
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.
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).
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 (CargoesFieldType::Empty or CargoesFieldType::SmallEmpty).
struct CargoesField::@163200273102357245024106251007063276336245070176::@002015101225166035271011323266367366265233313323 industry
Industry data (for CargoesFieldType::Industry).
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 CargoesFieldType::CargoLabel field.
T y
Y coordinate.
T x
X coordinate.
Dimensions (a width and height) of a rectangle in 2D.
Data about how and where to blit pixels.
Definition gfx_type.h:157
Container for the text colour and some text colour related flags for drawing.
Definition gfx_type.h:348
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:141
List of hotkeys for a window.
Definition hotkeys.h:46
All data for a single hotkey.
Definition hotkeys.h:22
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?
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?
void OnDropdownSelect(WidgetID widget, int index, int) override
A dropdown option associated to this window has been selected.
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.
SubstituteGRFFileProps grf_prop
properties related to the grf file
StringID name
Displayed name of the industry.
bool IsRawIndustry() const
Is an industry with the spec a raw industry?
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.
PixelColour map_colour
colour used for the small map
CargoType cargo
Cargo type.
Definition industry.h:86
uint16_t waiting
Amount of cargo waiting to processed.
Definition industry.h:87
CargoType cargo
Cargo type.
Definition industry.h:74
HistoryData< ProducedHistory > history
History of cargo produced and transported for this month and 24 previous months.
Definition industry.h:77
Defines the internal data of a functional industry.
Definition industry.h:62
IndustryType type
type of industry.
Definition industry.h:115
bool IsCargoAccepted() const
Test if this industry accepts any cargo.
Definition industry.h:223
uint8_t prod_level
general production level
Definition industry.h:112
void RecomputeProductionMultipliers()
Recompute production_rate for current prod_level.
EncodedString text
General text with additional information.
Definition industry.h:132
ProducedCargoes::iterator GetCargoProduced(CargoType cargo)
Get produced cargo slot for a specific cargo type.
Definition industry.h:180
ProducedCargoes produced
produced cargo slots
Definition industry.h:110
AcceptedCargoes accepted
accepted cargo slots
Definition industry.h:111
static uint16_t GetIndustryTypeCount(IndustryType type)
Get the count of industries for this type.
Definition industry.h:265
TileArea location
Location of the industry.
Definition industry.h:106
bool IsCargoProduced() const
Test if this industry produces any cargo.
Definition industry.h:229
Data structure describing how to show the list (what sort direction and criteria).
static IterateWrapper Iterate()
Returns an iterable ensemble of all Tiles.
Definition map_func.h:366
TileIndex GetCenterTile() const
Get the center tile.
TileIndex tile
The base tile of the area.
Colour for pixel/line drawing.
Definition gfx_type.h:307
static Pool::IterateWrapper< Industry > Iterate(size_t from=0)
static Industry * Get(auto index)
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.
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.
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.
std::string_view GetText() const
Get the current text.
Definition textbuf.cpp:284
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:992
static int SortButtonWidth()
Get width of up/down arrow of sort button state.
Definition widget.cpp:835
void FinishInitNested(WindowNumber window_number=0)
Perform the second part of the initialization of a nested widget tree.
Definition window.cpp:1822
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:786
void InvalidateData(int data=0, bool gui_scope=true)
Mark this window's data as invalid (in need of re-computing).
Definition window.cpp:3262
void SetWidgetDirty(WidgetID widget_index) const
Invalidate a widget, i.e.
Definition window.cpp:570
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:518
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:818
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:1812
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:544
bool IsShaded() const
Is window shaded currently?
Definition window_gui.h:563
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
Window(WindowDesc &desc)
Empty constructor, initialization has been moved to InitNested() called from the constructor of the d...
Definition window.cpp:1846
const NWID * GetWidget(WidgetID widnum) const
Get the nested widget with number widnum from the nested widget tree.
Definition window_gui.h:990
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:609
void InitNested(WindowNumber number=0)
Perform complete initialization of the Window with nested widgets, to allow use.
Definition window.cpp:1836
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:327
void SetWidgetDisabledState(WidgetID widget_index, bool disab_stat)
Sets the enabled/disabled status of a widget.
Definition window_gui.h:382
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 bool IsTileType(Tile tile, TileType type)
Checks if a tile is a given tiletype.
Definition tile_map.h:150
StrongType::Typedef< uint32_t, struct TileIndexTag, StrongType::Compare, StrongType::Integer, StrongType::Compatible< int32_t >, StrongType::Compatible< int64_t > > TileIndex
The index/ID of a Tile.
Definition tile_type.h:92
constexpr TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition tile_type.h:100
@ Clear
A tile without any structures, i.e. grass, rocks, farm fields etc.
Definition tile_type.h:49
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.
@ None
No visual effect.
Definition vehicle.cpp:2824
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:2548
static RectPadding ScaleGUITrad(const RectPadding &r)
Scale a RectPadding to GUI zoom level.
Definition widget.cpp:49
@ WWT_PUSHTXTBTN
Normal push-button (no toggle button) with text caption.
@ WWT_INSET
Pressed (inset) panel, most commonly used as combo box text area.
Definition widget_type.h:40
@ WWT_PUSHIMGBTN
Normal push-button (no toggle button) with image caption.
@ WWT_EDITBOX
a textbox for typing
Definition widget_type.h:62
@ NWID_HORIZONTAL
Horizontal container.
Definition widget_type.h:66
@ WWT_TEXTBTN
(Toggle) Button with text
Definition widget_type.h:44
@ WWT_PANEL
Simple depressed panel.
Definition widget_type.h:39
@ WWT_STICKYBOX
Sticky box (at top-right of a window, after WWT_DEFSIZEBOX).
Definition widget_type.h:57
@ WWT_MATRIX
Grid of rows and columns.
Definition widget_type.h:50
@ WWT_SHADEBOX
Shade box (at top-right of a window, between WWT_DEBUGBOX and WWT_DEFSIZEBOX).
Definition widget_type.h:55
@ WWT_CAPTION
Window caption (window title between closebox and stickybox).
Definition widget_type.h:52
@ NWID_VSCROLLBAR
Vertical scrollbar.
Definition widget_type.h:76
@ NWID_VERTICAL
Vertical container.
Definition widget_type.h:68
@ WWT_CLOSEBOX
Close box (at top-left of a window).
Definition widget_type.h:60
@ NWID_HSCROLLBAR
Horizontal scrollbar.
Definition widget_type.h:75
@ WWT_RESIZEBOX
Resize box (normally at bottom-right of a window).
Definition widget_type.h:59
@ WWT_DEFSIZEBOX
Default window size box (at top-right of a window, between WWT_SHADEBOX and WWT_STICKYBOX).
Definition widget_type.h:56
@ NWID_VIEWPORT
Nested widget containing a viewport.
Definition widget_type.h:73
@ WWT_DROPDOWN
Drop down list.
Definition widget_type.h:61
@ WWT_DEBUGBOX
NewGRF debug box (at top-right of a window, between WWT_CAPTION and WWT_SHADEBOX).
Definition widget_type.h:54
@ NWID_SELECTION
Stacked widgets, only one visible at a time (eg in a panel with tabs).
Definition widget_type.h:71
@ 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:1209
Window * FindWindowByClass(WindowClass cls)
Find any window by its class.
Definition window.cpp:1181
Window * BringWindowToFrontById(WindowClass cls, WindowNumber number)
Find a window and make it the relative top-window on the screen.
Definition window.cpp:1293
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:3340
Window functions not directly related to making/drawing windows.
@ Construction
This window is used for construction; close it whenever changing company.
Definition window_gui.h:153
Twindow * AllocateWindowDescFront(WindowDesc &desc, WindowNumber window_number, Targs... extra_arguments)
Open a new window.
@ DisableVpScroll
Window does not do autoscroll,.
Definition window_gui.h:234
@ SBS_DOWN
Sort ascending.
Definition window_gui.h:219
@ SBS_UP
Sort descending.
Definition window_gui.h:220
@ Automatic
Find a place automatically.
Definition window_gui.h:144
int WidgetID
Widget ID.
Definition window_type.h:21
Functions related to zooming.
ZoomLevel ScaleZoomGUI(ZoomLevel value)
Scale zoom level relative to GUI zoom.
Definition zoom_func.h:87
@ Industry
Default zoom level for the industry view.
Definition zoom_type.h:36