OpenTTD Source 20241222-master-gc72542431a
industry_gui.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6 */
7
10#include "stdafx.h"
11#include "error.h"
12#include "gui.h"
13#include "settings_gui.h"
14#include "sound_func.h"
15#include "window_func.h"
16#include "textbuf_gui.h"
17#include "command_func.h"
18#include "viewport_func.h"
19#include "industry.h"
20#include "town.h"
21#include "cheat_type.h"
22#include "newgrf_industries.h"
23#include "newgrf_text.h"
24#include "newgrf_debug.h"
25#include "network/network.h"
26#include "strings_func.h"
27#include "company_func.h"
28#include "tilehighlight_func.h"
29#include "string_func.h"
30#include "sortlist_type.h"
31#include "dropdown_func.h"
32#include "company_base.h"
34#include "core/random_func.hpp"
35#include "core/backup_type.hpp"
36#include "genworld.h"
37#include "smallmap_gui.h"
38#include "dropdown_type.h"
39#include "clear_map.h"
40#include "zoom_func.h"
41#include "industry_cmd.h"
42#include "graph_gui.h"
43#include "querystring_gui.h"
44#include "stringfilter_type.h"
45#include "timer/timer.h"
46#include "timer/timer_window.h"
47#include "hotkeys.h"
48
50
51#include "table/strings.h"
52
53#include <bitset>
54
55#include "safeguards.h"
56
57bool _ignore_restrictions;
58std::bitset<NUM_INDUSTRYTYPES> _displayed_industries;
59
66
74
80
81extern void GenerateIndustries();
82static void ShowIndustryCargoesWindow(IndustryType id);
83
93static void GetCargoSuffix(uint cargo, CargoSuffixType cst, const Industry *ind, IndustryType ind_type, const IndustrySpec *indspec, CargoSuffix &suffix)
94{
95 suffix.text.clear();
97
99 TileIndex t = (cst != CST_FUND) ? ind->location.tile : INVALID_TILE;
100 uint16_t callback = GetIndustryCallback(CBID_INDUSTRY_CARGO_SUFFIX, 0, (cst << 8) | cargo, const_cast<Industry *>(ind), ind_type, t);
101 if (callback == CALLBACK_FAILED) return;
102
103 if (indspec->grf_prop.grffile->grf_version < 8) {
104 if (GB(callback, 0, 8) == 0xFF) return;
105 if (callback < 0x400) {
107 suffix.text = GetString(GetGRFStringID(indspec->grf_prop.grfid, 0xD000 + callback));
110 return;
111 }
113 return;
114
115 } else { // GRF version 8 or higher.
116 if (callback == 0x400) return;
117 if (callback == 0x401) {
118 suffix.display = CSD_CARGO;
119 return;
120 }
121 if (callback < 0x400) {
123 suffix.text = GetString(GetGRFStringID(indspec->grf_prop.grfid, 0xD000 + callback));
126 return;
127 }
128 if (callback >= 0x800 && callback < 0xC00) {
130 suffix.text = GetString(GetGRFStringID(indspec->grf_prop.grfid, 0xD000 - 0x800 + callback));
132 suffix.display = CSD_CARGO_TEXT;
133 return;
134 }
136 return;
137 }
138 }
139}
140
141enum CargoSuffixInOut {
142 CARGOSUFFIX_OUT = 0,
143 CARGOSUFFIX_IN = 1,
144};
145
156template <typename TC, typename TS>
157static inline void GetAllCargoSuffixes(CargoSuffixInOut use_input, CargoSuffixType cst, const Industry *ind, IndustryType ind_type, const IndustrySpec *indspec, const TC &cargoes, TS &suffixes)
158{
159 static_assert(std::tuple_size_v<std::remove_reference_t<decltype(cargoes)>> <= lengthof(suffixes));
160
162 /* Reworked behaviour with new many-in-many-out scheme */
163 for (uint j = 0; j < lengthof(suffixes); j++) {
164 if (IsValidCargoID(cargoes[j])) {
165 uint8_t local_id = indspec->grf_prop.grffile->cargo_map[cargoes[j]]; // should we check the value for valid?
166 uint cargotype = local_id << 16 | use_input;
167 GetCargoSuffix(cargotype, cst, ind, ind_type, indspec, suffixes[j]);
168 } else {
169 suffixes[j].text.clear();
170 suffixes[j].display = CSD_CARGO;
171 }
172 }
173 } else {
174 /* Compatible behaviour with old 3-in-2-out scheme */
175 for (uint j = 0; j < lengthof(suffixes); j++) {
176 suffixes[j].text.clear();
177 suffixes[j].display = CSD_CARGO;
178 }
179 switch (use_input) {
180 case CARGOSUFFIX_OUT:
181 // Handle INDUSTRY_ORIGINAL_NUM_OUTPUTS cargoes
182 if (IsValidCargoID(cargoes[0])) GetCargoSuffix(3, cst, ind, ind_type, indspec, suffixes[0]);
183 if (IsValidCargoID(cargoes[1])) GetCargoSuffix(4, cst, ind, ind_type, indspec, suffixes[1]);
184 break;
185 case CARGOSUFFIX_IN:
186 // Handle INDUSTRY_ORIGINAL_NUM_INPUTS cargoes
187 if (IsValidCargoID(cargoes[0])) GetCargoSuffix(0, cst, ind, ind_type, indspec, suffixes[0]);
188 if (IsValidCargoID(cargoes[1])) GetCargoSuffix(1, cst, ind, ind_type, indspec, suffixes[1]);
189 if (IsValidCargoID(cargoes[2])) GetCargoSuffix(2, cst, ind, ind_type, indspec, suffixes[2]);
190 break;
191 default:
192 NOT_REACHED();
193 }
194 }
195}
196
208void GetCargoSuffix(CargoSuffixInOut use_input, CargoSuffixType cst, const Industry *ind, IndustryType ind_type, const IndustrySpec *indspec, CargoID cargo, uint8_t slot, CargoSuffix &suffix)
209{
210 suffix.text.clear();
211 suffix.display = CSD_CARGO;
212 if (!IsValidCargoID(cargo)) return;
214 uint8_t local_id = indspec->grf_prop.grffile->cargo_map[cargo]; // should we check the value for valid?
215 uint cargotype = local_id << 16 | use_input;
216 GetCargoSuffix(cargotype, cst, ind, ind_type, indspec, suffix);
217 } else if (use_input == CARGOSUFFIX_IN) {
218 if (slot < INDUSTRY_ORIGINAL_NUM_INPUTS) GetCargoSuffix(slot, cst, ind, ind_type, indspec, suffix);
219 } else if (use_input == CARGOSUFFIX_OUT) {
220 if (slot < INDUSTRY_ORIGINAL_NUM_OUTPUTS) GetCargoSuffix(slot + INDUSTRY_ORIGINAL_NUM_INPUTS, cst, ind, ind_type, indspec, suffix);
221 }
222}
223
224std::array<IndustryType, NUM_INDUSTRYTYPES> _sorted_industry_types;
225
227static bool IndustryTypeNameSorter(const IndustryType &a, const IndustryType &b)
228{
229 int r = StrNaturalCompare(GetString(GetIndustrySpec(a)->name), GetString(GetIndustrySpec(b)->name)); // Sort by name (natural sorting).
230
231 /* If the names are equal, sort by industry type. */
232 return (r != 0) ? r < 0 : (a < b);
233}
234
239{
240 /* Add each industry type to the list. */
241 for (IndustryType i = 0; i < NUM_INDUSTRYTYPES; i++) {
243 }
244
245 /* Sort industry types by name. */
247}
248
255void CcBuildIndustry(Commands, const CommandCost &result, TileIndex tile, IndustryType indtype, uint32_t, bool, uint32_t)
256{
257 if (result.Succeeded()) return;
258
259 if (indtype < NUM_INDUSTRYTYPES) {
260 const IndustrySpec *indsp = GetIndustrySpec(indtype);
261 if (indsp->enabled) {
262 SetDParam(0, indsp->name);
263 ShowErrorMessage(STR_ERROR_CAN_T_BUILD_HERE, result.GetErrorMessage(), WL_INFO, TileX(tile) * TILE_SIZE, TileY(tile) * TILE_SIZE);
264 }
265 }
266}
267
268static constexpr NWidgetPart _nested_build_industry_widgets[] = {
270 NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
271 NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetDataTip(STR_FUND_INDUSTRY_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
272 NWidget(WWT_SHADEBOX, COLOUR_DARK_GREEN),
273 NWidget(WWT_DEFSIZEBOX, COLOUR_DARK_GREEN),
274 NWidget(WWT_STICKYBOX, COLOUR_DARK_GREEN),
275 EndContainer(),
279 SetDataTip(STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES, STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_TOOLTIP),
281 SetDataTip(STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES, STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES_TOOLTIP),
282 EndContainer(),
283 EndContainer(),
285 NWidget(WWT_MATRIX, COLOUR_DARK_GREEN, WID_DPI_MATRIX_WIDGET), SetMatrixDataTip(1, 0, STR_FUND_INDUSTRY_SELECTION_TOOLTIP), SetFill(1, 0), SetResize(1, 1), SetScrollbar(WID_DPI_SCROLLBAR),
286 NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, WID_DPI_SCROLLBAR),
287 EndContainer(),
288 NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_DPI_INFOPANEL), SetResize(1, 0),
289 EndContainer(),
291 NWidget(WWT_TEXTBTN, COLOUR_DARK_GREEN, WID_DPI_DISPLAY_WIDGET), SetFill(1, 0), SetResize(1, 0),
292 SetDataTip(STR_INDUSTRY_DISPLAY_CHAIN, STR_INDUSTRY_DISPLAY_CHAIN_TOOLTIP),
293 NWidget(WWT_TEXTBTN, COLOUR_DARK_GREEN, WID_DPI_FUND_WIDGET), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_JUST_STRING, STR_NULL),
294 NWidget(WWT_RESIZEBOX, COLOUR_DARK_GREEN),
295 EndContainer(),
296};
297
300 WDP_AUTO, "build_industry", 170, 212,
303 _nested_build_industry_widgets
304);
305
308 IndustryType selected_type;
309 std::vector<IndustryType> list;
310 bool enabled;
311 Scrollbar *vscroll;
313
315 static const int MAX_MINWIDTH_LINEHEIGHTS = 20;
316
317 void UpdateAvailability()
318 {
319 this->enabled = this->selected_type != INVALID_INDUSTRYTYPE && (_game_mode == GM_EDITOR || GetIndustryProbabilityCallback(this->selected_type, IACT_USERCREATION, 1) > 0);
320 }
321
322 void SetupArrays()
323 {
324 this->list.clear();
325
326 /* Fill the arrays with industries.
327 * The tests performed after the enabled allow to load the industries
328 * In the same way they are inserted by grf (if any)
329 */
330 for (IndustryType ind : _sorted_industry_types) {
332 if (indsp->enabled) {
333 /* Rule is that editor mode loads all industries.
334 * In game mode, all non raw industries are loaded too
335 * and raw ones are loaded only when setting allows it */
336 if (_game_mode != GM_EDITOR && indsp->IsRawIndustry() && _settings_game.construction.raw_industry_construction == 0) {
337 /* Unselect if the industry is no longer in the list */
338 if (this->selected_type == ind) this->selected_type = INVALID_INDUSTRYTYPE;
339 continue;
340 }
341
342 this->list.push_back(ind);
343 }
344 }
345
346 /* First industry type is selected if the current selection is invalid. */
347 if (this->selected_type == INVALID_INDUSTRYTYPE && !this->list.empty()) this->selected_type = this->list[0];
348
349 this->UpdateAvailability();
350
351 this->vscroll->SetCount(this->list.size());
352 }
353
356 {
357 this->SetWidgetDisabledState(WID_DPI_FUND_WIDGET, this->selected_type != INVALID_INDUSTRYTYPE && !this->enabled);
358 this->SetWidgetDisabledState(WID_DPI_DISPLAY_WIDGET, this->selected_type == INVALID_INDUSTRYTYPE && this->enabled);
359 }
360
373 std::string MakeCargoListString(const std::span<const CargoID> cargolist, const std::span<const CargoSuffix> cargo_suffix, StringID prefixstr) const
374 {
375 assert(cargolist.size() == cargo_suffix.size());
376
377 std::string cargostring;
378 size_t numcargo = 0;
379 size_t firstcargo = 0;
380
381 for (size_t j = 0; j < cargolist.size(); j++) {
382 if (!IsValidCargoID(cargolist[j])) continue;
383 numcargo++;
384 if (numcargo == 1) {
385 firstcargo = j;
386 continue;
387 }
388 SetDParam(0, CargoSpec::Get(cargolist[j])->name);
389 SetDParamStr(1, cargo_suffix[j].text);
391 }
392
393 if (numcargo > 0) {
397 } else {
399 SetDParamStr(1, "");
401 }
402
403 return cargostring;
404 }
405
406public:
408 {
409 this->selected_type = INVALID_INDUSTRYTYPE;
410
411 this->CreateNestedTree();
412 this->vscroll = this->GetScrollbar(WID_DPI_SCROLLBAR);
413 /* Show scenario editor tools in editor. */
414 if (_game_mode != GM_EDITOR) {
416 }
417 this->FinishInitNested(0);
418
419 this->SetButtons();
420 }
421
422 void OnInit() override
423 {
424 /* Width of the legend blob -- slightly larger than the smallmap legend blob. */
425 this->legend.height = GetCharacterHeight(FS_SMALL);
426 this->legend.width = this->legend.height * 9 / 6;
427
428 this->SetupArrays();
429 }
430
432 {
433 switch (widget) {
435 SetDParamMaxDigits(0, 4);
437 Dimension d{};
438 for (const auto &indtype : this->list) {
439 d = maxdim(d, GetStringBoundingBox(GetIndustrySpec(indtype)->name));
440 }
441 resize.height = std::max<uint>({this->legend.height, d.height, count.height}) + padding.height;
442 d.width += this->legend.width + WidgetDimensions::scaled.hsep_wide + WidgetDimensions::scaled.hsep_normal + count.width + padding.width;
443 d.height = 5 * resize.height;
444 size = maxdim(size, d);
445 break;
446 }
447
448 case WID_DPI_INFOPANEL: {
449 /* Extra line for cost outside of editor. */
450 int height = 2 + (_game_mode == GM_EDITOR ? 0 : 1);
451 uint extra_lines_req = 0;
452 uint extra_lines_prd = 0;
453 uint extra_lines_newgrf = 0;
455 Dimension d = {0, 0};
456 for (const auto &indtype : this->list) {
457 const IndustrySpec *indsp = GetIndustrySpec(indtype);
458 CargoSuffix cargo_suffix[std::tuple_size_v<decltype(indsp->accepts_cargo)>];
459
460 /* Measure the accepted cargoes, if any. */
461 GetAllCargoSuffixes(CARGOSUFFIX_IN, CST_FUND, nullptr, indtype, indsp, indsp->accepts_cargo, cargo_suffix);
464 if (strdim.width > max_minwidth) {
465 extra_lines_req = std::max(extra_lines_req, strdim.width / max_minwidth + 1);
466 strdim.width = max_minwidth;
467 }
468 d = maxdim(d, strdim);
469
470 /* Measure the produced cargoes, if any. */
471 GetAllCargoSuffixes(CARGOSUFFIX_OUT, CST_FUND, nullptr, indtype, indsp, indsp->produced_cargo, cargo_suffix);
474 if (strdim.width > max_minwidth) {
475 extra_lines_prd = std::max(extra_lines_prd, strdim.width / max_minwidth + 1);
476 strdim.width = max_minwidth;
477 }
478 d = maxdim(d, strdim);
479
480 if (indsp->grf_prop.HasGrfFile()) {
481 /* Reserve a few extra lines for text from an industry NewGRF. */
483 }
484 }
485
486 /* Set it to something more sane :) */
488 size.height = height * GetCharacterHeight(FS_NORMAL) + padding.height;
489 size.width = d.width + padding.width;
490 break;
491 }
492
493 case WID_DPI_FUND_WIDGET: {
497 d.width += padding.width;
498 d.height += padding.height;
499 size = maxdim(size, d);
500 break;
501 }
502 }
503 }
504
505 void SetStringParameters(WidgetID widget) const override
506 {
507 switch (widget) {
509 /* Raw industries might be prospected. Show this fact by changing the string
510 * In Editor, you just build, while ingame, or you fund or you prospect */
511 if (_game_mode == GM_EDITOR) {
512 /* We've chosen many random industries but no industries have been specified */
514 } else {
515 if (this->selected_type != INVALID_INDUSTRYTYPE) {
516 const IndustrySpec *indsp = GetIndustrySpec(this->selected_type);
518 } else {
520 }
521 }
522 break;
523 }
524 }
525
526 void DrawWidget(const Rect &r, WidgetID widget) const override
527 {
528 switch (widget) {
530 bool rtl = _current_text_dir == TD_RTL;
532 Rect icon = text.WithWidth(this->legend.width, rtl);
533 text = text.Indent(this->legend.width + WidgetDimensions::scaled.hsep_wide, rtl);
534
535 /* Vertical offset for legend icon. */
536 icon.top = r.top + (this->resize.step_height - this->legend.height + 1) / 2;
537 icon.bottom = icon.top + this->legend.height - 1;
538
539 auto [first, last] = this->vscroll->GetVisibleRangeIterators(this->list);
540 for (auto it = first; it != last; ++it) {
541 IndustryType type = *it;
542 bool selected = this->selected_type == type;
543 const IndustrySpec *indsp = GetIndustrySpec(type);
544
545 /* Draw the name of the industry in white is selected, otherwise, in orange */
546 DrawString(text, indsp->name, selected ? TC_WHITE : TC_ORANGE);
547 GfxFillRect(icon, selected ? PC_WHITE : PC_BLACK);
550 DrawString(text, STR_JUST_COMMA, TC_BLACK, SA_RIGHT, false, FS_SMALL);
551
552 text = text.Translate(0, this->resize.step_height);
553 icon = icon.Translate(0, this->resize.step_height);
554 }
555 break;
556 }
557
558 case WID_DPI_INFOPANEL: {
559 Rect ir = r.Shrink(WidgetDimensions::scaled.framerect);
560
561 if (this->selected_type == INVALID_INDUSTRYTYPE) {
563 break;
564 }
565
566 const IndustrySpec *indsp = GetIndustrySpec(this->selected_type);
567
568 if (_game_mode != GM_EDITOR) {
569 SetDParam(0, indsp->GetConstructionCost());
572 }
573
574 CargoSuffix cargo_suffix[std::tuple_size_v<decltype(indsp->accepts_cargo)>];
575
576 /* Draw the accepted cargoes, if any. Otherwise, will print "Nothing". */
577 GetAllCargoSuffixes(CARGOSUFFIX_IN, CST_FUND, nullptr, this->selected_type, indsp, indsp->accepts_cargo, cargo_suffix);
580
581 /* Draw the produced cargoes, if any. Otherwise, will print "Nothing". */
582 GetAllCargoSuffixes(CARGOSUFFIX_OUT, CST_FUND, nullptr, this->selected_type, indsp, indsp->produced_cargo, cargo_suffix);
585
586 /* Get the additional purchase info text, if it has not already been queried. */
587 if (HasBit(indsp->callback_mask, CBM_IND_FUND_MORE_TEXT)) {
589 if (callback_res != CALLBACK_FAILED && callback_res != 0x400) {
590 if (callback_res > 0x400) {
592 } else {
593 StringID str = GetGRFStringID(indsp->grf_prop.grfid, 0xD000 + callback_res); // No. here's the new string
594 if (str != STR_UNDEFINED) {
595 StartTextRefStackUsage(indsp->grf_prop.grffile, 6);
596 DrawStringMultiLine(ir, str, TC_YELLOW);
598 }
599 }
600 }
601 }
602 break;
603 }
604 }
605 }
606
607 static void AskManyRandomIndustriesCallback(Window *, bool confirmed)
608 {
609 if (!confirmed) return;
610
611 if (Town::GetNumItems() == 0) {
613 } else {
618 old_generating_world.Restore();
619 }
620 }
621
622 static void AskRemoveAllIndustriesCallback(Window *, bool confirmed)
623 {
624 if (!confirmed) return;
625
626 for (Industry *industry : Industry::Iterate()) delete industry;
627
628 /* Clear farmland. */
629 for (const auto tile : Map::Iterate()) {
630 if (IsTileType(tile, MP_CLEAR) && GetRawClearGround(tile) == CLEAR_FIELDS) {
631 MakeClear(tile, CLEAR_GRASS, 3);
632 }
633 }
634
636 }
637
638 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
639 {
640 switch (widget) {
642 assert(_game_mode == GM_EDITOR);
645 break;
646 }
647
649 assert(_game_mode == GM_EDITOR);
652 break;
653 }
654
656 auto it = this->vscroll->GetScrolledItemFromWidget(this->list, pt.y, this, WID_DPI_MATRIX_WIDGET);
657 if (it != this->list.end()) { // Is it within the boundaries of available data?
658 this->selected_type = *it;
659 this->UpdateAvailability();
660
661 const IndustrySpec *indsp = GetIndustrySpec(this->selected_type);
662
663 this->SetDirty();
664
665 if (_thd.GetCallbackWnd() == this &&
666 ((_game_mode != GM_EDITOR && _settings_game.construction.raw_industry_construction == 2 && indsp != nullptr && indsp->IsRawIndustry()) || !this->enabled)) {
667 /* Reset the button state if going to prospecting or "build many industries" */
668 this->RaiseButtons();
670 }
671
672 this->SetButtons();
673 if (this->enabled && click_count > 1) this->OnClick(pt, WID_DPI_FUND_WIDGET, 1);
674 }
675 break;
676 }
677
679 if (this->selected_type != INVALID_INDUSTRYTYPE) ShowIndustryCargoesWindow(this->selected_type);
680 break;
681
682 case WID_DPI_FUND_WIDGET: {
683 if (this->selected_type != INVALID_INDUSTRYTYPE) {
684 if (_game_mode != GM_EDITOR && _settings_game.construction.raw_industry_construction == 2 && GetIndustrySpec(this->selected_type)->IsRawIndustry()) {
685 Command<CMD_BUILD_INDUSTRY>::Post(STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY, 0, this->selected_type, 0, false, InteractiveRandom());
687 } else {
688 HandlePlacePushButton(this, WID_DPI_FUND_WIDGET, SPR_CURSOR_INDUSTRY, HT_RECT);
689 }
690 }
691 break;
692 }
693 }
694 }
695
696 void OnResize() override
697 {
698 /* Adjust the number of items in the matrix depending of the resize */
699 this->vscroll->SetCapacityFromWidget(this, WID_DPI_MATRIX_WIDGET);
700 }
701
702 void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile) override
703 {
704 bool success = true;
705 /* We do not need to protect ourselves against "Random Many Industries" in this mode */
706 const IndustrySpec *indsp = GetIndustrySpec(this->selected_type);
707 uint32_t seed = InteractiveRandom();
708 uint32_t layout_index = InteractiveRandomRange((uint32_t)indsp->layouts.size());
709
710 if (_game_mode == GM_EDITOR) {
711 /* Show error if no town exists at all */
712 if (Town::GetNumItems() == 0) {
713 SetDParam(0, indsp->name);
715 return;
716 }
717
720 _ignore_restrictions = true;
721
723
724 cur_company.Restore();
725 old_generating_world.Restore();
726 _ignore_restrictions = false;
727 } else {
729 }
730
731 /* If an industry has been built, just reset the cursor and the system */
733 }
734
735 IntervalTimer<TimerWindow> update_interval = {std::chrono::seconds(3), [this](auto) {
736 if (_game_mode == GM_EDITOR) return;
737 if (this->selected_type == INVALID_INDUSTRYTYPE) return;
738
739 bool enabled = this->enabled;
740 this->UpdateAvailability();
741 if (enabled != this->enabled) {
742 this->SetButtons();
743 this->SetDirty();
744 }
745 }};
746
747 void OnTimeout() override
748 {
749 this->RaiseButtons();
750 }
751
752 void OnPlaceObjectAbort() override
753 {
754 this->RaiseButtons();
755 }
756
762 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
763 {
764 if (!gui_scope) return;
765 this->SetupArrays();
766 this->SetButtons();
767 this->SetDirty();
768 }
769};
770
771void ShowBuildIndustryWindow()
772{
773 if (_game_mode != GM_EDITOR && !Company::IsValidID(_local_company)) return;
776}
777
778static void UpdateIndustryProduction(Industry *i);
779
780static inline bool IsProductionAlterable(const Industry *i)
781{
782 const IndustrySpec *is = GetIndustrySpec(i->type);
783 bool has_prod = std::any_of(std::begin(is->production_rate), std::end(is->production_rate), [](auto rate) { return rate != 0; });
784 return ((_game_mode == GM_EDITOR || _cheats.setup_prod.value) &&
785 (has_prod || is->IsRawIndustry()) &&
786 !_networking);
787}
788
790{
797
805
814
815public:
817 {
819 this->editbox_line = IL_NONE;
820 this->clicked_line = IL_NONE;
821 this->clicked_button = 0;
822 this->info_height = WidgetDimensions::scaled.framerect.Vertical() + 2 * GetCharacterHeight(FS_NORMAL); // Info panel has at least two lines text.
823
824 this->InitNested(window_number);
826 nvp->InitializeViewport(this, Industry::Get(window_number)->location.GetCenterTile(), ScaleZoomGUI(ZOOM_LVL_INDUSTRY));
827
830
831 this->InvalidateData();
832 }
833
835 {
837 }
838
839 void OnInit() override
840 {
841 /* This only used when the cheat to alter industry production is enabled */
842 this->cheat_line_height = std::max(SETTING_BUTTON_HEIGHT + WidgetDimensions::scaled.vsep_normal, GetCharacterHeight(FS_NORMAL));
843 this->cargo_icon_size = GetLargestCargoIconSize();
844 }
845
846 void OnPaint() override
847 {
848 this->DrawWidgets();
849
850 if (this->IsShaded()) return; // Don't draw anything when the window is shaded.
851
852 const Rect r = this->GetWidget<NWidgetBase>(WID_IV_INFO)->GetCurrentRect();
853 int expected = this->DrawInfo(r);
854 if (expected != r.bottom) {
855 this->info_height = expected - r.top + 1;
856 this->ReInit();
857 return;
858 }
859 }
860
861 void DrawCargoIcon(const Rect &r, CargoID cid) const
862 {
863 bool rtl = _current_text_dir == TD_RTL;
865 Dimension d = GetSpriteSize(icon);
866 Rect ir = r.WithWidth(this->cargo_icon_size.width, rtl).WithHeight(GetCharacterHeight(FS_NORMAL));
867 DrawSprite(icon, PAL_NONE, CenterBounds(ir.left, ir.right, d.width), CenterBounds(ir.top, ir.bottom, this->cargo_icon_size.height));
868 }
869
875 int DrawInfo(const Rect &r)
876 {
877 bool rtl = _current_text_dir == TD_RTL;
880 Rect ir = r.Shrink(WidgetDimensions::scaled.framerect);
881 bool first = true;
882 bool has_accept = false;
883
884 if (i->prod_level == PRODLEVEL_CLOSURE) {
887 }
888
889 const int label_indent = WidgetDimensions::scaled.hsep_normal + this->cargo_icon_size.width;
891
892 for (const auto &a : i->accepted) {
893 if (!IsValidCargoID(a.cargo)) continue;
894 has_accept = true;
895 if (first) {
898 first = false;
899 }
900
901 DrawCargoIcon(ir, a.cargo);
902
903 CargoSuffix suffix;
904 GetCargoSuffix(CARGOSUFFIX_IN, CST_VIEW, i, i->type, ind, a.cargo, &a - i->accepted.data(), suffix);
905
906 SetDParam(0, CargoSpec::Get(a.cargo)->name);
907 SetDParam(1, a.cargo);
908 SetDParam(2, a.waiting);
909 SetDParamStr(3, "");
910 StringID str = STR_NULL;
911 switch (suffix.display) {
913 SetDParamStr(3, suffix.text);
914 [[fallthrough]];
915 case CSD_CARGO_AMOUNT:
917 break;
918
919 case CSD_CARGO_TEXT:
920 SetDParamStr(3, suffix.text);
921 [[fallthrough]];
922 case CSD_CARGO:
924 break;
925
926 default:
927 NOT_REACHED();
928 }
929 DrawString(ir.Indent(label_indent, rtl), str);
931 }
932
933 int line_height = this->editable == EA_RATE ? this->cheat_line_height : GetCharacterHeight(FS_NORMAL);
934 int text_y_offset = (line_height - GetCharacterHeight(FS_NORMAL)) / 2;
935 int button_y_offset = (line_height - SETTING_BUTTON_HEIGHT) / 2;
936 first = true;
937 for (const auto &p : i->produced) {
938 if (!IsValidCargoID(p.cargo)) continue;
939 if (first) {
943 if (this->editable == EA_RATE) this->production_offset_y = ir.top;
944 first = false;
945 }
946
947 DrawCargoIcon(ir, p.cargo);
948
949 CargoSuffix suffix;
950 GetCargoSuffix(CARGOSUFFIX_OUT, CST_VIEW, i, i->type, ind, p.cargo, &p - i->produced.data(), suffix);
951
952 SetDParam(0, p.cargo);
953 SetDParam(1, p.history[LAST_MONTH].production);
954 SetDParamStr(2, suffix.text);
955 SetDParam(3, ToPercent8(p.history[LAST_MONTH].PctTransported()));
957 /* Let's put out those buttons.. */
958 if (this->editable == EA_RATE) {
959 DrawArrowButtons(ir.Indent(label_indent, rtl).WithWidth(SETTING_BUTTON_WIDTH, rtl).left, ir.top + button_y_offset, COLOUR_YELLOW, (this->clicked_line == IL_RATE1 + (&p - i->produced.data())) ? this->clicked_button : 0,
960 p.rate > 0, p.rate < 255);
961 }
962 ir.top += line_height;
963 }
964
965 /* Display production multiplier if editable */
966 if (this->editable == EA_MULTIPLIER) {
967 line_height = this->cheat_line_height;
968 text_y_offset = (line_height - GetCharacterHeight(FS_NORMAL)) / 2;
969 button_y_offset = (line_height - SETTING_BUTTON_HEIGHT) / 2;
971 this->production_offset_y = ir.top;
974 DrawArrowButtons(ir.Indent(label_indent, rtl).WithWidth(SETTING_BUTTON_WIDTH, rtl).left, ir.top + button_y_offset, COLOUR_YELLOW, (this->clicked_line == IL_MULTIPLIER) ? this->clicked_button : 0,
976 ir.top += line_height;
977 }
978
979 /* Get the extra message for the GUI */
980 if (HasBit(ind->callback_mask, CBM_IND_WINDOW_MORE_TEXT)) {
982 if (callback_res != CALLBACK_FAILED && callback_res != 0x400) {
983 if (callback_res > 0x400) {
985 } else {
986 StringID message = GetGRFStringID(ind->grf_prop.grfid, 0xD000 + callback_res);
987 if (message != STR_NULL && message != STR_UNDEFINED) {
989
990 StartTextRefStackUsage(ind->grf_prop.grffile, 6);
991 /* Use all the available space left from where we stand up to the
992 * end of the window. We ALSO enlarge the window if needed, so we
993 * can 'go' wild with the bottom of the window. */
994 ir.top = DrawStringMultiLine(ir.left, ir.right, ir.top, UINT16_MAX, message, TC_BLACK);
996 }
997 }
998 }
999 }
1000
1001 if (!i->text.empty()) {
1002 SetDParamStr(0, i->text);
1004 ir.top = DrawStringMultiLine(ir.left, ir.right, ir.top, UINT16_MAX, STR_JUST_RAW_STRING, TC_BLACK);
1005 }
1006
1007 /* Return required bottom position, the last pixel row plus some padding. */
1008 return ir.top - 1 + WidgetDimensions::scaled.framerect.bottom;
1009 }
1010
1011 void SetStringParameters(WidgetID widget) const override
1012 {
1013 if (widget == WID_IV_CAPTION) SetDParam(0, this->window_number);
1014 }
1015
1017 {
1018 if (widget == WID_IV_INFO) size.height = this->info_height;
1019 }
1020
1021 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
1022 {
1023 switch (widget) {
1024 case WID_IV_INFO: {
1026 InfoLine line = IL_NONE;
1027
1028 switch (this->editable) {
1029 case EA_NONE: break;
1030
1031 case EA_MULTIPLIER:
1032 if (IsInsideBS(pt.y, this->production_offset_y, this->cheat_line_height)) line = IL_MULTIPLIER;
1033 break;
1034
1035 case EA_RATE:
1036 if (pt.y >= this->production_offset_y) {
1037 int row = (pt.y - this->production_offset_y) / this->cheat_line_height;
1038 for (auto itp = std::begin(i->produced); itp != std::end(i->produced); ++itp) {
1039 if (!IsValidCargoID(itp->cargo)) continue;
1040 row--;
1041 if (row < 0) {
1042 line = (InfoLine)(IL_RATE1 + (itp - std::begin(i->produced)));
1043 break;
1044 }
1045 }
1046 }
1047 break;
1048 }
1049 if (line == IL_NONE) return;
1050
1051 bool rtl = _current_text_dir == TD_RTL;
1052 Rect r = this->GetWidget<NWidgetBase>(widget)->GetCurrentRect().Shrink(WidgetDimensions::scaled.framerect).Indent(this->cargo_icon_size.width + WidgetDimensions::scaled.hsep_normal, rtl);
1053
1055 /* Clicked buttons, decrease or increase production */
1057 switch (this->editable) {
1058 case EA_MULTIPLIER:
1059 if (decrease) {
1060 if (i->prod_level <= PRODLEVEL_MINIMUM) return;
1061 i->prod_level = static_cast<uint8_t>(std::max<uint>(i->prod_level / 2, PRODLEVEL_MINIMUM));
1062 } else {
1063 if (i->prod_level >= PRODLEVEL_MAXIMUM) return;
1064 i->prod_level = static_cast<uint8_t>(std::min<uint>(i->prod_level * 2, PRODLEVEL_MAXIMUM));
1065 }
1066 break;
1067
1068 case EA_RATE:
1069 if (decrease) {
1070 if (i->produced[line - IL_RATE1].rate <= 0) return;
1071 i->produced[line - IL_RATE1].rate = std::max(i->produced[line - IL_RATE1].rate / 2, 0);
1072 } else {
1073 if (i->produced[line - IL_RATE1].rate >= 255) return;
1074 /* a zero production industry is unlikely to give anything but zero, so push it a little bit */
1075 int new_prod = i->produced[line - IL_RATE1].rate == 0 ? 1 : i->produced[line - IL_RATE1].rate * 2;
1076 i->produced[line - IL_RATE1].rate = ClampTo<uint8_t>(new_prod);
1077 }
1078 break;
1079
1080 default: NOT_REACHED();
1081 }
1082
1083 UpdateIndustryProduction(i);
1084 this->SetDirty();
1085 this->SetTimeout();
1086 this->clicked_line = line;
1087 this->clicked_button = (decrease ^ rtl) ? 1 : 2;
1089 /* clicked the text */
1090 this->editbox_line = line;
1091 switch (this->editable) {
1092 case EA_MULTIPLIER:
1095 break;
1096
1097 case EA_RATE:
1098 SetDParam(0, i->produced[line - IL_RATE1].rate * 8);
1100 break;
1101
1102 default: NOT_REACHED();
1103 }
1104 }
1105 break;
1106 }
1107
1108 case WID_IV_GOTO: {
1110 if (_ctrl_pressed) {
1112 } else {
1114 }
1115 break;
1116 }
1117
1118 case WID_IV_DISPLAY: {
1121 break;
1122 }
1123
1124 case WID_IV_GRAPH:
1125 ShowIndustryProductionGraph(this->window_number);
1126 break;
1127 }
1128 }
1129
1130 void OnTimeout() override
1131 {
1132 this->clicked_line = IL_NONE;
1133 this->clicked_button = 0;
1134 this->SetDirty();
1135 }
1136
1137 void OnResize() override
1138 {
1139 if (this->viewport != nullptr) {
1141 nvp->UpdateViewportCoordinates(this);
1142
1143 ScrollWindowToTile(Industry::Get(this->window_number)->location.GetCenterTile(), this, true); // Re-center viewport.
1144 }
1145 }
1146
1147 void OnMouseWheel(int wheel) override
1148 {
1150 DoZoomInOutWindow(wheel < 0 ? ZOOM_IN : ZOOM_OUT, this);
1151 }
1152 }
1153
1154 void OnQueryTextFinished(std::optional<std::string> str) override
1155 {
1156 if (!str.has_value() || str->empty()) return;
1157
1159 uint value = atoi(str->c_str());
1160 switch (this->editbox_line) {
1161 case IL_NONE: NOT_REACHED();
1162
1163 case IL_MULTIPLIER:
1165 break;
1166
1167 default:
1168 i->produced[this->editbox_line - IL_RATE1].rate = ClampU(RoundDivSU(value, 8), 0, 255);
1169 break;
1170 }
1171 UpdateIndustryProduction(i);
1172 this->SetDirty();
1173 }
1174
1180 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
1181 {
1182 if (!gui_scope) return;
1183 const Industry *i = Industry::Get(this->window_number);
1184 if (IsProductionAlterable(i)) {
1185 const IndustrySpec *ind = GetIndustrySpec(i->type);
1186 this->editable = ind->UsesOriginalEconomy() ? EA_MULTIPLIER : EA_RATE;
1187 } else {
1188 this->editable = EA_NONE;
1189 }
1190 }
1191
1192 bool IsNewGRFInspectable() const override
1193 {
1194 return ::IsNewGRFInspectable(GSF_INDUSTRIES, this->window_number);
1195 }
1196
1197 void ShowNewGRFInspectWindow() const override
1198 {
1199 ::ShowNewGRFInspectWindow(GSF_INDUSTRIES, this->window_number);
1200 }
1201};
1202
1203static void UpdateIndustryProduction(Industry *i)
1204{
1205 const IndustrySpec *indspec = GetIndustrySpec(i->type);
1207
1208 for (auto &p : i->produced) {
1209 if (IsValidCargoID(p.cargo)) {
1210 p.history[LAST_MONTH].production = ScaleByCargoScale(8 * p.rate, false);
1211 }
1212 }
1213}
1214
1218 NWidget(WWT_CLOSEBOX, COLOUR_CREAM),
1219 NWidget(WWT_CAPTION, COLOUR_CREAM, WID_IV_CAPTION), SetDataTip(STR_INDUSTRY_VIEW_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1220 NWidget(WWT_PUSHIMGBTN, COLOUR_CREAM, WID_IV_GOTO), SetAspect(WidgetDimensions::ASPECT_LOCATION), SetDataTip(SPR_GOTO_LOCATION, STR_INDUSTRY_VIEW_LOCATION_TOOLTIP),
1221 NWidget(WWT_DEBUGBOX, COLOUR_CREAM),
1222 NWidget(WWT_SHADEBOX, COLOUR_CREAM),
1223 NWidget(WWT_DEFSIZEBOX, COLOUR_CREAM),
1224 NWidget(WWT_STICKYBOX, COLOUR_CREAM),
1225 EndContainer(),
1226 NWidget(WWT_PANEL, COLOUR_CREAM),
1227 NWidget(WWT_INSET, COLOUR_CREAM), SetPadding(2, 2, 2, 2),
1228 NWidget(NWID_VIEWPORT, INVALID_COLOUR, WID_IV_VIEWPORT), SetMinimalSize(254, 86), SetFill(1, 0), SetResize(1, 1),
1229 EndContainer(),
1230 EndContainer(),
1231 NWidget(WWT_PANEL, COLOUR_CREAM, WID_IV_INFO), SetMinimalSize(260, 0), SetMinimalTextLines(2, WidgetDimensions::unscaled.framerect.Vertical()), SetResize(1, 0),
1232 EndContainer(),
1234 NWidget(WWT_PUSHTXTBTN, COLOUR_CREAM, WID_IV_DISPLAY), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_INDUSTRY_DISPLAY_CHAIN, STR_INDUSTRY_DISPLAY_CHAIN_TOOLTIP),
1235 NWidget(WWT_PUSHTXTBTN, COLOUR_CREAM, WID_IV_GRAPH), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_INDUSTRY_VIEW_PRODUCTION_GRAPH, STR_INDUSTRY_VIEW_PRODUCTION_GRAPH_TOOLTIP),
1236 NWidget(WWT_RESIZEBOX, COLOUR_CREAM),
1237 EndContainer(),
1238};
1239
1242 WDP_AUTO, "view_industry", 260, 120,
1244 0,
1246);
1247
1248void ShowIndustryViewWindow(int industry)
1249{
1250 AllocateWindowDescFront<IndustryViewWindow>(_industry_view_desc, industry);
1251}
1252
1256 NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
1257 NWidget(WWT_CAPTION, COLOUR_BROWN, WID_ID_CAPTION), SetDataTip(STR_INDUSTRY_DIRECTORY_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1258 NWidget(WWT_SHADEBOX, COLOUR_BROWN),
1259 NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
1260 NWidget(WWT_STICKYBOX, COLOUR_BROWN),
1261 EndContainer(),
1265 NWidget(WWT_TEXTBTN, COLOUR_BROWN, WID_ID_DROPDOWN_ORDER), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER),
1266 NWidget(WWT_DROPDOWN, COLOUR_BROWN, WID_ID_DROPDOWN_CRITERIA), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_SORT_CRITERIA),
1267 NWidget(WWT_EDITBOX, COLOUR_BROWN, WID_ID_FILTER), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP),
1268 EndContainer(),
1270 NWidget(WWT_DROPDOWN, COLOUR_BROWN, WID_ID_FILTER_BY_ACC_CARGO), SetMinimalSize(225, 12), SetFill(0, 1), SetDataTip(STR_INDUSTRY_DIRECTORY_ACCEPTED_CARGO_FILTER, STR_TOOLTIP_FILTER_CRITERIA),
1271 NWidget(WWT_DROPDOWN, COLOUR_BROWN, WID_ID_FILTER_BY_PROD_CARGO), SetMinimalSize(225, 12), SetFill(0, 1), SetDataTip(STR_INDUSTRY_DIRECTORY_PRODUCED_CARGO_FILTER, STR_TOOLTIP_FILTER_CRITERIA),
1272 NWidget(WWT_PANEL, COLOUR_BROWN), SetResize(1, 0), EndContainer(),
1273 EndContainer(),
1274 NWidget(WWT_PANEL, COLOUR_BROWN, WID_ID_INDUSTRY_LIST), SetDataTip(0x0, STR_INDUSTRY_DIRECTORY_LIST_CAPTION), SetResize(1, 1), SetScrollbar(WID_ID_VSCROLLBAR),
1275 EndContainer(),
1276 EndContainer(),
1278 EndContainer(),
1281 NWidget(WWT_RESIZEBOX, COLOUR_BROWN),
1282 EndContainer(),
1283};
1284
1286
1294static bool CargoFilter(const Industry * const *industry, const std::pair<CargoID, CargoID> &cargoes)
1295{
1296 auto accepted_cargo = cargoes.first;
1297 auto produced_cargo = cargoes.second;
1298
1299 bool accepted_cargo_matches;
1300
1301 switch (accepted_cargo) {
1303 accepted_cargo_matches = true;
1304 break;
1305
1307 accepted_cargo_matches = !(*industry)->IsCargoAccepted();
1308 break;
1309
1310 default:
1311 accepted_cargo_matches = (*industry)->IsCargoAccepted(accepted_cargo);
1312 break;
1313 }
1314
1315 bool produced_cargo_matches;
1316
1317 switch (produced_cargo) {
1319 produced_cargo_matches = true;
1320 break;
1321
1323 produced_cargo_matches = !(*industry)->IsCargoProduced();
1324 break;
1325
1326 default:
1327 produced_cargo_matches = (*industry)->IsCargoProduced(produced_cargo);
1328 break;
1329 }
1330
1331 return accepted_cargo_matches && produced_cargo_matches;
1332}
1333
1334static GUIIndustryList::FilterFunction * const _industry_filter_funcs[] = { &CargoFilter };
1335
1344protected:
1345 /* Runtime saved values */
1346 static Listing last_sorting;
1347
1348 /* Constants for sorting industries */
1349 static inline const StringID sorter_names[] = {
1354 };
1355 static const std::initializer_list<GUIIndustryList::SortFunction * const> sorter_funcs;
1356
1357 GUIIndustryList industries{IndustryDirectoryWindow::produced_cargo_filter};
1358 Scrollbar *vscroll;
1359 Scrollbar *hscroll;
1360
1363 static CargoID produced_cargo_filter;
1364
1365 const int MAX_FILTER_LENGTH = 16;
1368
1369 enum class SorterType : uint8_t {
1370 ByName,
1371 ByType,
1372 ByProduction,
1374 };
1375
1381 {
1382 if (this->produced_cargo_filter_criteria != cid) {
1383 this->produced_cargo_filter_criteria = cid;
1384 /* deactivate filter if criteria is 'Show All', activate it otherwise */
1385 bool is_filtering_necessary = this->produced_cargo_filter_criteria != CargoFilterCriteria::CF_ANY || this->accepted_cargo_filter_criteria != CargoFilterCriteria::CF_ANY;
1386
1387 this->industries.SetFilterState(is_filtering_necessary);
1388 this->industries.SetFilterType(0);
1389 this->industries.ForceRebuild();
1390 }
1391 }
1392
1398 {
1399 if (this->accepted_cargo_filter_criteria != cid) {
1400 this->accepted_cargo_filter_criteria = cid;
1401 /* deactivate filter if criteria is 'Show All', activate it otherwise */
1402 bool is_filtering_necessary = this->produced_cargo_filter_criteria != CargoFilterCriteria::CF_ANY || this->accepted_cargo_filter_criteria != CargoFilterCriteria::CF_ANY;
1403
1404 this->industries.SetFilterState(is_filtering_necessary);
1405 this->industries.SetFilterType(0);
1406 this->industries.ForceRebuild();
1407 }
1408 }
1409
1410 StringID GetCargoFilterLabel(CargoID cid) const
1411 {
1412 switch (cid) {
1415 default: return CargoSpec::Get(cid)->name;
1416 }
1417 }
1418
1423 {
1424 this->produced_cargo_filter_criteria = CargoFilterCriteria::CF_ANY;
1425 this->accepted_cargo_filter_criteria = CargoFilterCriteria::CF_ANY;
1426
1427 this->industries.SetFilterFuncs(_industry_filter_funcs);
1428
1429 bool is_filtering_necessary = this->produced_cargo_filter_criteria != CargoFilterCriteria::CF_ANY || this->accepted_cargo_filter_criteria != CargoFilterCriteria::CF_ANY;
1430
1431 this->industries.SetFilterState(is_filtering_necessary);
1432 }
1433
1439 {
1440 uint width = this->hscroll->GetCount();
1441 auto [first, last] = this->vscroll->GetVisibleRangeIterators(this->industries);
1442 for (auto it = first; it != last; ++it) {
1443 width = std::max(width, GetStringBoundingBox(this->GetIndustryString(*it)).width);
1444 }
1445 return width;
1446 }
1447
1450 {
1451 if (this->industries.NeedRebuild()) {
1452 this->industries.clear();
1453 this->industries.reserve(Industry::GetNumItems());
1454
1455 for (const Industry *i : Industry::Iterate()) {
1456 if (this->string_filter.IsEmpty()) {
1457 this->industries.push_back(i);
1458 continue;
1459 }
1460 this->string_filter.ResetState();
1461 this->string_filter.AddLine(i->GetCachedName());
1462 if (this->string_filter.GetState()) this->industries.push_back(i);
1463 }
1464
1465 this->industries.RebuildDone();
1466
1467 auto filter = std::make_pair(this->accepted_cargo_filter_criteria, this->produced_cargo_filter_criteria);
1468
1469 this->industries.Filter(filter);
1470
1471 this->vscroll->SetCount(this->industries.size()); // Update scrollbar as well.
1472 }
1473
1474 IndustryDirectoryWindow::produced_cargo_filter = this->produced_cargo_filter_criteria;
1475 this->industries.Sort();
1476
1477 this->SetDirty();
1478 }
1479
1487 {
1488 if (!IsValidCargoID(p.cargo)) return -1;
1489 return ToPercent8(p.history[LAST_MONTH].PctTransported());
1490 }
1491
1500 {
1501 CargoID filter = IndustryDirectoryWindow::produced_cargo_filter;
1502 if (filter == CargoFilterCriteria::CF_NONE) return 0;
1503
1504 int percentage = 0, produced_cargo_count = 0;
1505 for (const auto &p : i->produced) {
1506 if (filter == CargoFilterCriteria::CF_ANY) {
1507 int transported = GetCargoTransportedPercentsIfValid(p);
1508 if (transported != -1) {
1510 percentage += transported;
1511 }
1512 if (produced_cargo_count == 0 && &p == &i->produced.back() && percentage == 0) {
1513 return transported;
1514 }
1515 } else if (filter == p.cargo) {
1517 }
1518 }
1519
1520 if (produced_cargo_count == 0) return percentage;
1522 }
1523
1525 static bool IndustryNameSorter(const Industry * const &a, const Industry * const &b, const CargoID &)
1526 {
1527 int r = StrNaturalCompare(a->GetCachedName(), b->GetCachedName()); // Sort by name (natural sorting).
1528 if (r == 0) return a->index < b->index;
1529 return r < 0;
1530 }
1531
1533 static bool IndustryTypeSorter(const Industry * const &a, const Industry * const &b, const CargoID &filter)
1534 {
1535 int it_a = 0;
1537 int it_b = 0;
1539 int r = it_a - it_b;
1540 return (r == 0) ? IndustryNameSorter(a, b, filter) : r < 0;
1541 }
1542
1544 static bool IndustryProductionSorter(const Industry * const &a, const Industry * const &b, const CargoID &filter)
1545 {
1546 if (filter == CargoFilterCriteria::CF_NONE) return IndustryTypeSorter(a, b, filter);
1547
1548 uint prod_a = 0, prod_b = 0;
1549 if (filter == CargoFilterCriteria::CF_ANY) {
1550 for (const auto &pa : a->produced) {
1551 if (IsValidCargoID(pa.cargo)) prod_a += pa.history[LAST_MONTH].production;
1552 }
1553 for (const auto &pb : b->produced) {
1554 if (IsValidCargoID(pb.cargo)) prod_b += pb.history[LAST_MONTH].production;
1555 }
1556 } else {
1557 if (auto ita = a->GetCargoProduced(filter); ita != std::end(a->produced)) prod_a = ita->history[LAST_MONTH].production;
1558 if (auto itb = b->GetCargoProduced(filter); itb != std::end(b->produced)) prod_b = itb->history[LAST_MONTH].production;
1559 }
1560 int r = prod_a - prod_b;
1561
1562 return (r == 0) ? IndustryTypeSorter(a, b, filter) : r < 0;
1563 }
1564
1566 static bool IndustryTransportedCargoSorter(const Industry * const &a, const Industry * const &b, const CargoID &filter)
1567 {
1569 return (r == 0) ? IndustryNameSorter(a, b, filter) : r < 0;
1570 }
1571
1578 {
1580 uint8_t p = 0;
1581
1582 /* Industry name */
1583 SetDParam(p++, i->index);
1584
1585 /* Get industry productions (CargoID, production, suffix, transported) */
1586 struct CargoInfo {
1588 uint16_t production;
1589 uint transported;
1590 std::string suffix;
1591
1592 CargoInfo(CargoID cargo_id, uint16_t production, uint transported, std::string &&suffix) : cargo_id(cargo_id), production(production), transported(transported), suffix(std::move(suffix)) {}
1593 };
1594 std::vector<CargoInfo> cargos;
1595
1596 for (auto itp = std::begin(i->produced); itp != std::end(i->produced); ++itp) {
1597 if (!IsValidCargoID(itp->cargo)) continue;
1599 GetCargoSuffix(CARGOSUFFIX_OUT, CST_DIR, i, i->type, indsp, itp->cargo, itp - std::begin(i->produced), cargo_suffix);
1600 cargos.emplace_back(itp->cargo, itp->history[LAST_MONTH].production, ToPercent8(itp->history[LAST_MONTH].PctTransported()), std::move(cargo_suffix.text));
1601 }
1602
1603 switch (static_cast<IndustryDirectoryWindow::SorterType>(this->industries.SortType())) {
1607 /* Sort by descending production, then descending transported */
1608 std::sort(cargos.begin(), cargos.end(), [](const CargoInfo &a, const CargoInfo &b) {
1609 if (a.production != b.production) return a.production > b.production;
1610 return a.transported > b.transported;
1611 });
1612 break;
1613
1615 /* Sort by descending transported, then descending production */
1616 std::sort(cargos.begin(), cargos.end(), [](const CargoInfo &a, const CargoInfo &b) {
1617 if (a.transported != b.transported) return a.transported > b.transported;
1618 return a.production > b.production;
1619 });
1620 break;
1621 }
1622
1623 /* If the produced cargo filter is active then move the filtered cargo to the beginning of the list,
1624 * because this is the one the player interested in, and that way it is not hidden in the 'n' more cargos */
1627 auto filtered_ci = std::ranges::find(cargos, cid, &CargoInfo::cargo_id);
1628 if (filtered_ci != cargos.end()) {
1629 std::rotate(cargos.begin(), filtered_ci, filtered_ci + 1);
1630 }
1631 }
1632
1633 /* Display first 3 cargos */
1634 for (size_t j = 0; j < std::min<size_t>(3, cargos.size()); j++) {
1635 CargoInfo &ci = cargos[j];
1637 SetDParam(p++, ci.cargo_id);
1638 SetDParam(p++, ci.production);
1639 SetDParamStr(p++, std::move(ci.suffix));
1640 SetDParam(p++, ci.transported);
1641 }
1642
1643 /* Undisplayed cargos if any */
1644 SetDParam(p++, cargos.size() - 3);
1645
1646 /* Drawing the right string */
1647 switch (cargos.size()) {
1649 case 1: return STR_INDUSTRY_DIRECTORY_ITEM_PROD1;
1650 case 2: return STR_INDUSTRY_DIRECTORY_ITEM_PROD2;
1651 case 3: return STR_INDUSTRY_DIRECTORY_ITEM_PROD3;
1653 }
1654 }
1655
1656public:
1658 {
1659 this->CreateNestedTree();
1660 this->vscroll = this->GetScrollbar(WID_ID_VSCROLLBAR);
1661 this->hscroll = this->GetScrollbar(WID_ID_HSCROLLBAR);
1662
1663 this->industries.SetListing(this->last_sorting);
1664 this->industries.SetSortFuncs(IndustryDirectoryWindow::sorter_funcs);
1665 this->industries.ForceRebuild();
1666
1667 this->FinishInitNested(0);
1668
1670
1672 this->industry_editbox.cancel_button = QueryString::ACTION_CLEAR;
1673 }
1674
1676 {
1677 this->last_sorting = this->industries.GetListing();
1678 }
1679
1680 void OnInit() override
1681 {
1682 this->SetCargoFilterArray();
1683 this->hscroll->SetCount(0);
1684 }
1685
1686 void SetStringParameters(WidgetID widget) const override
1687 {
1688 switch (widget) {
1689 case WID_ID_CAPTION:
1690 SetDParam(0, this->vscroll->GetCount());
1692 break;
1693
1695 SetDParam(0, IndustryDirectoryWindow::sorter_names[this->industries.SortType()]);
1696 break;
1697
1699 SetDParam(0, this->GetCargoFilterLabel(this->accepted_cargo_filter_criteria));
1700 break;
1701
1703 SetDParam(0, this->GetCargoFilterLabel(this->produced_cargo_filter_criteria));
1704 break;
1705 }
1706 }
1707
1708 void DrawWidget(const Rect &r, WidgetID widget) const override
1709 {
1710 switch (widget) {
1712 this->DrawSortButtonState(widget, this->industries.IsDescSortOrder() ? SBS_DOWN : SBS_UP);
1713 break;
1714
1715 case WID_ID_INDUSTRY_LIST: {
1716 Rect ir = r.Shrink(WidgetDimensions::scaled.framerect);
1717
1718 /* Setup a clipping rectangle... */
1720 if (!FillDrawPixelInfo(&tmp_dpi, ir)) return;
1721 /* ...but keep coordinates relative to the window. */
1722 tmp_dpi.left += ir.left;
1723 tmp_dpi.top += ir.top;
1724
1726
1727 ir = ScrollRect(ir, *this->hscroll, 1);
1728
1729 if (this->industries.empty()) {
1731 break;
1732 }
1734 auto [first, last] = this->vscroll->GetVisibleRangeIterators(this->industries);
1735 for (auto it = first; it != last; ++it) {
1736 TextColour tc = TC_FROMSTRING;
1738 Industry *ind = const_cast<Industry *>(*it);
1740 tc = TC_GREY | TC_FORCED;
1741 }
1742 }
1743 DrawString(ir, this->GetIndustryString(*it), tc);
1744
1745 ir.top += this->resize.step_height;
1746 }
1747 break;
1748 }
1749 }
1750 }
1751
1753 {
1754 switch (widget) {
1755 case WID_ID_DROPDOWN_ORDER: {
1756 Dimension d = GetStringBoundingBox(this->GetWidget<NWidgetCore>(widget)->widget_data);
1757 d.width += padding.width + Window::SortButtonWidth() * 2; // Doubled since the string is centred and it also looks better.
1758 d.height += padding.height;
1759 size = maxdim(size, d);
1760 break;
1761 }
1762
1764 Dimension d = GetStringListBoundingBox(IndustryDirectoryWindow::sorter_names);
1765 d.width += padding.width;
1766 d.height += padding.height;
1767 size = maxdim(size, d);
1768 break;
1769 }
1770
1771 case WID_ID_INDUSTRY_LIST: {
1773 resize.height = d.height;
1774 d.height *= 5;
1775 d.width += padding.width;
1776 d.height += padding.height;
1777 size = maxdim(size, d);
1778 break;
1779 }
1780 }
1781 }
1782
1783 DropDownList BuildCargoDropDownList() const
1784 {
1785 DropDownList list;
1786
1787 /* Add item for disabling filtering. */
1788 list.push_back(MakeDropDownListStringItem(this->GetCargoFilterLabel(CargoFilterCriteria::CF_ANY), CargoFilterCriteria::CF_ANY));
1789 /* Add item for industries not producing anything, e.g. power plants */
1790 list.push_back(MakeDropDownListStringItem(this->GetCargoFilterLabel(CargoFilterCriteria::CF_NONE), CargoFilterCriteria::CF_NONE));
1791
1792 /* Add cargos */
1794 for (const CargoSpec *cs : _sorted_standard_cargo_specs) {
1795 list.push_back(MakeDropDownListIconItem(d, cs->GetCargoIcon(), PAL_NONE, cs->name, cs->Index()));
1796 }
1797
1798 return list;
1799 }
1800
1801 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
1802 {
1803 switch (widget) {
1805 this->industries.ToggleSortOrder();
1806 this->SetDirty();
1807 break;
1808
1810 ShowDropDownMenu(this, IndustryDirectoryWindow::sorter_names, this->industries.SortType(), WID_ID_DROPDOWN_CRITERIA, 0, 0);
1811 break;
1812
1813 case WID_ID_FILTER_BY_ACC_CARGO: // Cargo filter dropdown
1814 ShowDropDownList(this, this->BuildCargoDropDownList(), this->accepted_cargo_filter_criteria, widget);
1815 break;
1816
1817 case WID_ID_FILTER_BY_PROD_CARGO: // Cargo filter dropdown
1818 ShowDropDownList(this, this->BuildCargoDropDownList(), this->produced_cargo_filter_criteria, widget);
1819 break;
1820
1821 case WID_ID_INDUSTRY_LIST: {
1822 auto it = this->vscroll->GetScrolledItemFromWidget(this->industries, pt.y, this, WID_ID_INDUSTRY_LIST, WidgetDimensions::scaled.framerect.top);
1823 if (it != this->industries.end()) {
1824 if (_ctrl_pressed) {
1825 ShowExtraViewportWindow((*it)->location.tile);
1826 } else {
1827 ScrollMainWindowToTile((*it)->location.tile);
1828 }
1829 }
1830 break;
1831 }
1832 }
1833 }
1834
1835 void OnDropdownSelect(WidgetID widget, int index) override
1836 {
1837 switch (widget) {
1839 if (this->industries.SortType() != index) {
1840 this->industries.SetSortType(index);
1842 }
1843 break;
1844 }
1845
1847 this->SetAcceptedCargoFilter(index);
1849 break;
1850 }
1851
1853 this->SetProducedCargoFilter(index);
1855 break;
1856 }
1857 }
1858 }
1859
1860 void OnResize() override
1861 {
1862 this->vscroll->SetCapacityFromWidget(this, WID_ID_INDUSTRY_LIST, WidgetDimensions::scaled.framerect.Vertical());
1863 this->hscroll->SetCapacityFromWidget(this, WID_ID_INDUSTRY_LIST, WidgetDimensions::scaled.framerect.Horizontal());
1864 }
1865
1867 {
1868 if (wid == WID_ID_FILTER) {
1869 this->string_filter.SetFilterTerm(this->industry_editbox.text.buf);
1870 this->InvalidateData(IDIWD_FORCE_REBUILD);
1871 }
1872 }
1873
1874 void OnPaint() override
1875 {
1876 if (this->industries.NeedRebuild()) this->BuildSortIndustriesList();
1877 this->hscroll->SetCount(this->GetIndustryListWidth());
1878 this->DrawWidgets();
1879 }
1880
1882 IntervalTimer<TimerWindow> rebuild_interval = {std::chrono::seconds(3), [this](auto) {
1883 this->industries.ForceResort();
1885 }};
1886
1892 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
1893 {
1894 switch (data) {
1895 case IDIWD_FORCE_REBUILD:
1896 /* This needs to be done in command-scope to enforce rebuilding before resorting invalid data */
1897 this->industries.ForceRebuild();
1898 break;
1899
1900 case IDIWD_PRODUCTION_CHANGE:
1901 if (this->industries.SortType() == 2) this->industries.ForceResort();
1902 break;
1903
1904 default:
1905 this->industries.ForceResort();
1906 break;
1907 }
1908 }
1909
1911 {
1912 switch (hotkey) {
1915 SetFocusedWindow(this); // The user has asked to give focus to the text box, so make sure this window is focused.
1916 break;
1917 default:
1918 return ES_NOT_HANDLED;
1919 }
1920 return ES_HANDLED;
1921 }
1922
1923 static inline HotkeyList hotkeys {"industrydirectory", {
1924 Hotkey('F', "focus_filter_box", IDHK_FOCUS_FILTER_BOX),
1925 }};
1926};
1927
1928Listing IndustryDirectoryWindow::last_sorting = {false, 0};
1929
1930/* Available station sorting functions. */
1931const std::initializer_list<GUIIndustryList::SortFunction * const> IndustryDirectoryWindow::sorter_funcs = {
1932 &IndustryNameSorter,
1933 &IndustryTypeSorter,
1934 &IndustryProductionSorter,
1935 &IndustryTransportedCargoSorter
1936};
1937
1938CargoID IndustryDirectoryWindow::produced_cargo_filter = CargoFilterCriteria::CF_ANY;
1939
1940
1943 WDP_AUTO, "list_industries", 428, 190,
1945 0,
1947 &IndustryDirectoryWindow::hotkeys
1948);
1949
1950void ShowIndustryDirectory()
1951{
1952 AllocateWindowDescFront<IndustryDirectoryWindow>(_industry_directory_desc, 0);
1953}
1954
1958 NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
1959 NWidget(WWT_CAPTION, COLOUR_BROWN, WID_IC_CAPTION), SetDataTip(STR_INDUSTRY_CARGOES_INDUSTRY_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1960 NWidget(WWT_SHADEBOX, COLOUR_BROWN),
1961 NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
1962 NWidget(WWT_STICKYBOX, COLOUR_BROWN),
1963 EndContainer(),
1967 EndContainer(),
1969 NWidget(WWT_TEXTBTN, COLOUR_BROWN, WID_IC_NOTIFY),
1970 SetDataTip(STR_INDUSTRY_CARGOES_NOTIFY_SMALLMAP, STR_INDUSTRY_CARGOES_NOTIFY_SMALLMAP_TOOLTIP),
1971 NWidget(WWT_PANEL, COLOUR_BROWN), SetFill(1, 0), SetResize(0, 0), EndContainer(),
1972 NWidget(WWT_DROPDOWN, COLOUR_BROWN, WID_IC_IND_DROPDOWN), SetFill(0, 0), SetResize(0, 0),
1973 SetDataTip(STR_INDUSTRY_CARGOES_SELECT_INDUSTRY, STR_INDUSTRY_CARGOES_SELECT_INDUSTRY_TOOLTIP),
1974 NWidget(WWT_DROPDOWN, COLOUR_BROWN, WID_IC_CARGO_DROPDOWN), SetFill(0, 0), SetResize(0, 0),
1975 SetDataTip(STR_INDUSTRY_CARGOES_SELECT_CARGO, STR_INDUSTRY_CARGOES_SELECT_CARGO_TOOLTIP),
1976 NWidget(WWT_RESIZEBOX, COLOUR_BROWN),
1977 EndContainer(),
1978};
1979
1982 WDP_AUTO, "industry_cargoes", 300, 210,
1984 0,
1986);
1987
1997
1998static const uint MAX_CARGOES = 16;
1999
2003 static int blob_distance;
2004
2010
2011 static const int INDUSTRY_LINE_COLOUR;
2012 static const int CARGO_LINE_COLOUR;
2013
2016 static int industry_width;
2017 static uint max_cargoes;
2018
2019 using Cargoes = uint16_t;
2020 static_assert(std::numeric_limits<Cargoes>::digits >= MAX_CARGOES);
2021
2023 union {
2024 struct {
2025 IndustryType ind_type;
2029 struct {
2033 uint8_t num_cargoes;
2034 uint8_t top_end;
2035 uint8_t bottom_end;
2037 struct {
2042 } u; // Data for each type.
2043
2049 {
2050 this->type = type;
2051 }
2052
2058 void MakeIndustry(IndustryType ind_type)
2059 {
2060 this->type = CFT_INDUSTRY;
2061 this->u.industry.ind_type = ind_type;
2062 std::fill(std::begin(this->u.industry.other_accepted), std::end(this->u.industry.other_accepted), INVALID_CARGO);
2063 std::fill(std::begin(this->u.industry.other_produced), std::end(this->u.industry.other_produced), INVALID_CARGO);
2064 }
2065
2072 int ConnectCargo(CargoID cargo, bool producer)
2073 {
2074 assert(this->type == CFT_CARGO);
2075 if (!IsValidCargoID(cargo)) return -1;
2076
2077 /* Find the vertical cargo column carrying the cargo. */
2078 int column = -1;
2079 for (int i = 0; i < this->u.cargo.num_cargoes; i++) {
2080 if (cargo == this->u.cargo.vertical_cargoes[i]) {
2081 column = i;
2082 break;
2083 }
2084 }
2085 if (column < 0) return -1;
2086
2087 if (producer) {
2088 assert(!HasBit(this->u.cargo.supp_cargoes, column));
2089 SetBit(this->u.cargo.supp_cargoes, column);
2090 } else {
2091 assert(!HasBit(this->u.cargo.cust_cargoes, column));
2092 SetBit(this->u.cargo.cust_cargoes, column);
2093 }
2094 return column;
2095 }
2096
2102 {
2103 assert(this->type == CFT_CARGO);
2104
2105 return this->u.cargo.supp_cargoes != 0 || this->u.cargo.cust_cargoes != 0;
2106 }
2107
2113 void MakeCargo(const std::span<const CargoID> cargoes)
2114 {
2115 this->type = CFT_CARGO;
2116 assert(std::size(cargoes) <= std::size(this->u.cargo.vertical_cargoes));
2117 auto insert = std::copy_if(std::begin(cargoes), std::end(cargoes), std::begin(this->u.cargo.vertical_cargoes), IsValidCargoID);
2118 this->u.cargo.num_cargoes = static_cast<uint8_t>(std::distance(std::begin(this->u.cargo.vertical_cargoes), insert));
2119 CargoIDComparator comparator;
2120 std::sort(std::begin(this->u.cargo.vertical_cargoes), insert, comparator);
2121 std::fill(insert, std::end(this->u.cargo.vertical_cargoes), INVALID_CARGO);
2122 this->u.cargo.top_end = false;
2123 this->u.cargo.bottom_end = false;
2124 this->u.cargo.supp_cargoes = 0;
2125 this->u.cargo.cust_cargoes = 0;
2126 }
2127
2133 void MakeCargoLabel(const std::span<const CargoID> cargoes, bool left_align)
2134 {
2135 this->type = CFT_CARGO_LABEL;
2136 assert(std::size(cargoes) <= std::size(this->u.cargo_label.cargoes));
2137 auto insert = std::copy(std::begin(cargoes), std::end(cargoes), std::begin(this->u.cargo_label.cargoes));
2138 std::fill(insert, std::end(this->u.cargo_label.cargoes), INVALID_CARGO);
2139 this->u.cargo_label.left_align = left_align;
2140 }
2141
2147 {
2148 this->type = CFT_HEADER;
2149 this->u.header = textid;
2150 }
2151
2157 int GetCargoBase(int xpos) const
2158 {
2159 assert(this->type == CFT_CARGO);
2160 int n = this->u.cargo.num_cargoes;
2161
2162 return xpos + cargo_field_width / 2 - (CargoesField::cargo_line.width * n + CargoesField::cargo_space.width * (n - 1)) / 2;
2163 }
2164
2170 void Draw(int xpos, int ypos) const
2171 {
2172 switch (this->type) {
2173 case CFT_EMPTY:
2174 case CFT_SMALL_EMPTY:
2175 break;
2176
2177 case CFT_HEADER:
2178 ypos += (small_height - GetCharacterHeight(FS_NORMAL)) / 2;
2179 DrawString(xpos, xpos + industry_width, ypos, this->u.header, TC_WHITE, SA_HOR_CENTER);
2180 break;
2181
2182 case CFT_INDUSTRY: {
2183 int ypos1 = ypos + vert_inter_industry_space / 2;
2184 int ypos2 = ypos + normal_height - 1 - vert_inter_industry_space / 2;
2185 int xpos2 = xpos + industry_width - 1;
2186 DrawRectOutline({xpos, ypos1, xpos2, ypos2}, INDUSTRY_LINE_COLOUR);
2188 if (this->u.industry.ind_type < NUM_INDUSTRYTYPES) {
2189 const IndustrySpec *indsp = GetIndustrySpec(this->u.industry.ind_type);
2190 DrawString(xpos, xpos2, ypos, indsp->name, TC_WHITE, SA_HOR_CENTER);
2191
2192 /* Draw the industry legend. */
2193 int blob_left, blob_right;
2194 if (_current_text_dir == TD_RTL) {
2195 blob_right = xpos2 - blob_distance;
2196 blob_left = blob_right - CargoesField::legend.width;
2197 } else {
2198 blob_left = xpos + blob_distance;
2199 blob_right = blob_left + CargoesField::legend.width;
2200 }
2201 GfxFillRect(blob_left, ypos2 - blob_distance - CargoesField::legend.height, blob_right, ypos2 - blob_distance, PC_BLACK); // Border
2202 GfxFillRect(blob_left + 1, ypos2 - blob_distance - CargoesField::legend.height + 1, blob_right - 1, ypos2 - blob_distance - 1, indsp->map_colour);
2203 } else {
2204 DrawString(xpos, xpos2, ypos, STR_INDUSTRY_CARGOES_HOUSES, TC_FROMSTRING, SA_HOR_CENTER);
2205 }
2206
2207 /* Draw the other_produced/other_accepted cargoes. */
2208 std::span<const CargoID> other_right, other_left;
2209 if (_current_text_dir == TD_RTL) {
2210 other_right = this->u.industry.other_accepted;
2211 other_left = this->u.industry.other_produced;
2212 } else {
2213 other_right = this->u.industry.other_produced;
2214 other_left = this->u.industry.other_accepted;
2215 }
2217 for (uint i = 0; i < CargoesField::max_cargoes; i++) {
2218 if (IsValidCargoID(other_right[i])) {
2219 const CargoSpec *csp = CargoSpec::Get(other_right[i]);
2220 int xp = xpos + industry_width + CargoesField::cargo_stub.width;
2221 DrawHorConnection(xpos + industry_width, xp - 1, ypos1, csp);
2222 GfxDrawLine(xp, ypos1, xp, ypos1 + CargoesField::cargo_line.height - 1, CARGO_LINE_COLOUR);
2223 }
2224 if (IsValidCargoID(other_left[i])) {
2225 const CargoSpec *csp = CargoSpec::Get(other_left[i]);
2226 int xp = xpos - CargoesField::cargo_stub.width;
2227 DrawHorConnection(xp + 1, xpos - 1, ypos1, csp);
2228 GfxDrawLine(xp, ypos1, xp, ypos1 + CargoesField::cargo_line.height - 1, CARGO_LINE_COLOUR);
2229 }
2231 }
2232 break;
2233 }
2234
2235 case CFT_CARGO: {
2236 int cargo_base = this->GetCargoBase(xpos);
2237 int top = ypos + (this->u.cargo.top_end ? vert_inter_industry_space / 2 + 1 : 0);
2238 int bot = ypos - (this->u.cargo.bottom_end ? vert_inter_industry_space / 2 + 1 : 0) + normal_height - 1;
2239 int colpos = cargo_base;
2240 for (int i = 0; i < this->u.cargo.num_cargoes; i++) {
2241 if (this->u.cargo.top_end) GfxDrawLine(colpos, top - 1, colpos + CargoesField::cargo_line.width - 1, top - 1, CARGO_LINE_COLOUR);
2242 if (this->u.cargo.bottom_end) GfxDrawLine(colpos, bot + 1, colpos + CargoesField::cargo_line.width - 1, bot + 1, CARGO_LINE_COLOUR);
2243 GfxDrawLine(colpos, top, colpos, bot, CARGO_LINE_COLOUR);
2244 colpos++;
2245 const CargoSpec *csp = CargoSpec::Get(this->u.cargo.vertical_cargoes[i]);
2246 GfxFillRect(colpos, top, colpos + CargoesField::cargo_line.width - 2, bot, csp->legend_colour, FILLRECT_OPAQUE);
2247 colpos += CargoesField::cargo_line.width - 2;
2248 GfxDrawLine(colpos, top, colpos, bot, CARGO_LINE_COLOUR);
2249 colpos += 1 + CargoesField::cargo_space.width;
2250 }
2251
2252 Cargoes hor_left, hor_right;
2253 if (_current_text_dir == TD_RTL) {
2254 hor_left = this->u.cargo.cust_cargoes;
2255 hor_right = this->u.cargo.supp_cargoes;
2256 } else {
2257 hor_left = this->u.cargo.supp_cargoes;
2258 hor_right = this->u.cargo.cust_cargoes;
2259 }
2261 for (uint i = 0; i < MAX_CARGOES; i++) {
2262 if (HasBit(hor_left, i)) {
2263 int col = i;
2264 int dx = 0;
2265 const CargoSpec *csp = CargoSpec::Get(this->u.cargo.vertical_cargoes[col]);
2266 for (; col > 0; col--) {
2267 int lf = cargo_base + col * CargoesField::cargo_line.width + (col - 1) * CargoesField::cargo_space.width;
2268 DrawHorConnection(lf, lf + CargoesField::cargo_space.width - dx, ypos, csp);
2269 dx = 1;
2270 }
2271 DrawHorConnection(xpos, cargo_base - dx, ypos, csp);
2272 }
2273 if (HasBit(hor_right, i)) {
2274 int col = i;
2275 int dx = 0;
2276 const CargoSpec *csp = CargoSpec::Get(this->u.cargo.vertical_cargoes[col]);
2277 for (; col < this->u.cargo.num_cargoes - 1; col++) {
2278 int lf = cargo_base + (col + 1) * CargoesField::cargo_line.width + col * CargoesField::cargo_space.width;
2279 DrawHorConnection(lf + dx - 1, lf + CargoesField::cargo_space.width - 1, ypos, csp);
2280 dx = 1;
2281 }
2282 DrawHorConnection(cargo_base + col * CargoesField::cargo_space.width + (col + 1) * CargoesField::cargo_line.width - 1 + dx, xpos + CargoesField::cargo_field_width - 1, ypos, csp);
2283 }
2285 }
2286 break;
2287 }
2288
2289 case CFT_CARGO_LABEL:
2291 for (uint i = 0; i < MAX_CARGOES; i++) {
2292 if (IsValidCargoID(this->u.cargo_label.cargoes[i])) {
2293 const CargoSpec *csp = CargoSpec::Get(this->u.cargo_label.cargoes[i]);
2294 DrawString(xpos + WidgetDimensions::scaled.framerect.left, xpos + industry_width - 1 - WidgetDimensions::scaled.framerect.right, ypos, csp->name, TC_WHITE,
2295 (this->u.cargo_label.left_align) ? SA_LEFT : SA_RIGHT);
2296 }
2298 }
2299 break;
2300
2301 default:
2302 NOT_REACHED();
2303 }
2304 }
2305
2313 CargoID CargoClickedAt(const CargoesField *left, const CargoesField *right, Point pt) const
2314 {
2315 assert(this->type == CFT_CARGO);
2316
2317 /* Vertical matching. */
2318 int cpos = this->GetCargoBase(0);
2319 uint col;
2320 for (col = 0; col < this->u.cargo.num_cargoes; col++) {
2321 if (pt.x < cpos) break;
2322 if (pt.x < cpos + (int)CargoesField::cargo_line.width) return this->u.cargo.vertical_cargoes[col];
2324 }
2325 /* col = 0 -> left of first col, 1 -> left of 2nd col, ... this->u.cargo.num_cargoes right of last-col. */
2326
2328 uint row;
2329 for (row = 0; row < MAX_CARGOES; row++) {
2330 if (pt.y < vpos) return INVALID_CARGO;
2331 if (pt.y < vpos + GetCharacterHeight(FS_NORMAL)) break;
2333 }
2334 if (row == MAX_CARGOES) return INVALID_CARGO;
2335
2336 /* row = 0 -> at first horizontal row, row = 1 -> second horizontal row, 2 = 3rd horizontal row. */
2337 if (col == 0) {
2338 if (HasBit(this->u.cargo.supp_cargoes, row)) return this->u.cargo.vertical_cargoes[row];
2339 if (left != nullptr) {
2340 if (left->type == CFT_INDUSTRY) return left->u.industry.other_produced[row];
2341 if (left->type == CFT_CARGO_LABEL && !left->u.cargo_label.left_align) return left->u.cargo_label.cargoes[row];
2342 }
2343 return INVALID_CARGO;
2344 }
2345 if (col == this->u.cargo.num_cargoes) {
2346 if (HasBit(this->u.cargo.cust_cargoes, row)) return this->u.cargo.vertical_cargoes[row];
2347 if (right != nullptr) {
2348 if (right->type == CFT_INDUSTRY) return right->u.industry.other_accepted[row];
2349 if (right->type == CFT_CARGO_LABEL && right->u.cargo_label.left_align) return right->u.cargo_label.cargoes[row];
2350 }
2351 return INVALID_CARGO;
2352 }
2353 if (row >= col) {
2354 /* Clicked somewhere in-between vertical cargo connection.
2355 * Since the horizontal connection is made in the same order as the vertical list, the above condition
2356 * ensures we are left-below the main diagonal, thus at the supplying side.
2357 */
2358 if (HasBit(this->u.cargo.supp_cargoes, row)) return this->u.cargo.vertical_cargoes[row];
2359 return INVALID_CARGO;
2360 }
2361 /* Clicked at a customer connection. */
2362 if (HasBit(this->u.cargo.cust_cargoes, row)) return this->u.cargo.vertical_cargoes[row];
2363 return INVALID_CARGO;
2364 }
2365
2372 {
2373 assert(this->type == CFT_CARGO_LABEL);
2374
2376 uint row;
2377 for (row = 0; row < MAX_CARGOES; row++) {
2378 if (pt.y < vpos) return INVALID_CARGO;
2379 if (pt.y < vpos + GetCharacterHeight(FS_NORMAL)) break;
2381 }
2382 if (row == MAX_CARGOES) return INVALID_CARGO;
2383 return this->u.cargo_label.cargoes[row];
2384 }
2385
2386private:
2394 static void DrawHorConnection(int left, int right, int top, const CargoSpec *csp)
2395 {
2396 GfxDrawLine(left, top, right, top, CARGO_LINE_COLOUR);
2397 GfxFillRect(left, top + 1, right, top + CargoesField::cargo_line.height - 2, csp->legend_colour, FILLRECT_OPAQUE);
2398 GfxDrawLine(left, top + CargoesField::cargo_line.height - 1, right, top + CargoesField::cargo_line.height - 1, CARGO_LINE_COLOUR);
2399 }
2400};
2401
2402static_assert(MAX_CARGOES >= std::tuple_size_v<decltype(IndustrySpec::produced_cargo)>);
2403static_assert(MAX_CARGOES >= std::tuple_size_v<decltype(IndustrySpec::accepts_cargo)>);
2404
2410
2417
2419
2422
2426
2432 {
2433 CargoesField *ind_fld = this->columns + column;
2434 CargoesField *cargo_fld = this->columns + column + 1;
2435 assert(ind_fld->type == CFT_INDUSTRY && cargo_fld->type == CFT_CARGO);
2436
2437 std::fill(std::begin(ind_fld->u.industry.other_produced), std::end(ind_fld->u.industry.other_produced), INVALID_CARGO);
2438
2439 if (ind_fld->u.industry.ind_type < NUM_INDUSTRYTYPES) {
2440 CargoID others[MAX_CARGOES]; // Produced cargoes not carried in the cargo column.
2441 int other_count = 0;
2442
2443 const IndustrySpec *indsp = GetIndustrySpec(ind_fld->u.industry.ind_type);
2444 assert(CargoesField::max_cargoes <= std::size(indsp->produced_cargo));
2445 for (uint i = 0; i < CargoesField::max_cargoes; i++) {
2446 int col = cargo_fld->ConnectCargo(indsp->produced_cargo[i], true);
2447 if (col < 0) others[other_count++] = indsp->produced_cargo[i];
2448 }
2449
2450 /* Allocate other cargoes in the empty holes of the horizontal cargo connections. */
2451 for (uint i = 0; i < CargoesField::max_cargoes && other_count > 0; i++) {
2452 if (HasBit(cargo_fld->u.cargo.supp_cargoes, i)) ind_fld->u.industry.other_produced[i] = others[--other_count];
2453 }
2454 } else {
2455 /* Houses only display cargo that towns produce. */
2456 for (uint i = 0; i < cargo_fld->u.cargo.num_cargoes; i++) {
2457 CargoID cid = cargo_fld->u.cargo.vertical_cargoes[i];
2459 if (tpe == TPE_PASSENGERS || tpe == TPE_MAIL) cargo_fld->ConnectCargo(cid, true);
2460 }
2461 }
2462 }
2463
2469 void MakeCargoLabel(int column, bool accepting)
2470 {
2471 CargoID cargoes[MAX_CARGOES];
2472 std::fill(std::begin(cargoes), std::end(cargoes), INVALID_CARGO);
2473
2474 CargoesField *label_fld = this->columns + column;
2475 CargoesField *cargo_fld = this->columns + (accepting ? column - 1 : column + 1);
2476
2477 assert(cargo_fld->type == CFT_CARGO && label_fld->type == CFT_EMPTY);
2478 for (uint i = 0; i < cargo_fld->u.cargo.num_cargoes; i++) {
2479 int col = cargo_fld->ConnectCargo(cargo_fld->u.cargo.vertical_cargoes[i], !accepting);
2480 if (col >= 0) cargoes[col] = cargo_fld->u.cargo.vertical_cargoes[i];
2481 }
2482 label_fld->MakeCargoLabel(cargoes, accepting);
2483 }
2484
2485
2491 {
2492 CargoesField *ind_fld = this->columns + column;
2493 CargoesField *cargo_fld = this->columns + column - 1;
2494 assert(ind_fld->type == CFT_INDUSTRY && cargo_fld->type == CFT_CARGO);
2495
2496 std::fill(std::begin(ind_fld->u.industry.other_accepted), std::end(ind_fld->u.industry.other_accepted), INVALID_CARGO);
2497
2498 if (ind_fld->u.industry.ind_type < NUM_INDUSTRYTYPES) {
2499 CargoID others[MAX_CARGOES]; // Accepted cargoes not carried in the cargo column.
2500 int other_count = 0;
2501
2502 const IndustrySpec *indsp = GetIndustrySpec(ind_fld->u.industry.ind_type);
2503 assert(CargoesField::max_cargoes <= std::size(indsp->accepts_cargo));
2504 for (uint i = 0; i < CargoesField::max_cargoes; i++) {
2505 int col = cargo_fld->ConnectCargo(indsp->accepts_cargo[i], false);
2506 if (col < 0) others[other_count++] = indsp->accepts_cargo[i];
2507 }
2508
2509 /* Allocate other cargoes in the empty holes of the horizontal cargo connections. */
2510 for (uint i = 0; i < CargoesField::max_cargoes && other_count > 0; i++) {
2511 if (!HasBit(cargo_fld->u.cargo.cust_cargoes, i)) ind_fld->u.industry.other_accepted[i] = others[--other_count];
2512 }
2513 } else {
2514 /* Houses only display what is demanded. */
2515 for (uint i = 0; i < cargo_fld->u.cargo.num_cargoes; i++) {
2516 for (const auto &hs : HouseSpec::Specs()) {
2517 if (!hs.enabled) continue;
2518
2519 for (uint j = 0; j < lengthof(hs.accepts_cargo); j++) {
2520 if (hs.cargo_acceptance[j] > 0 && cargo_fld->u.cargo.vertical_cargoes[i] == hs.accepts_cargo[j]) {
2521 cargo_fld->ConnectCargo(cargo_fld->u.cargo.vertical_cargoes[i], false);
2522 goto next_cargo;
2523 }
2524 }
2525 }
2526next_cargo: ;
2527 }
2528 }
2529 }
2530};
2531
2532
2561 typedef std::vector<CargoesRow> Fields;
2562
2563 Fields fields;
2567 Scrollbar *vscroll;
2568
2570 {
2571 this->OnInit();
2572 this->CreateNestedTree();
2573 this->vscroll = this->GetScrollbar(WID_IC_SCROLLBAR);
2574 this->FinishInitNested(0);
2575 this->OnInvalidateData(id);
2576 }
2577
2578 void OnInit() override
2579 {
2580 /* Initialize static CargoesField size variables. */
2586
2587 /* Size of the legend blob -- slightly larger than the smallmap legend blob. */
2589 CargoesField::legend.width = CargoesField::legend.height * 9 / 6;
2590
2591 /* Size of cargo lines. */
2594
2595 /* Size of border between cargo lines and industry boxes. */
2598
2599 /* Size of space between cargo lines. */
2602
2603 /* Size of cargo stub (unconnected cargo line.) */
2605 CargoesField::cargo_stub.height = CargoesField::cargo_line.height; /* Unused */
2606
2609
2610 /* Decide about the size of the box holding the text of an industry type. */
2611 this->ind_textsize.width = 0;
2612 this->ind_textsize.height = 0;
2614 for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
2615 const IndustrySpec *indsp = GetIndustrySpec(it);
2616 if (!indsp->enabled) continue;
2617 this->ind_textsize = maxdim(this->ind_textsize, GetStringBoundingBox(indsp->name));
2618 CargoesField::max_cargoes = std::max<uint>(CargoesField::max_cargoes, std::ranges::count_if(indsp->accepts_cargo, IsValidCargoID));
2619 CargoesField::max_cargoes = std::max<uint>(CargoesField::max_cargoes, std::ranges::count_if(indsp->produced_cargo, IsValidCargoID));
2620 }
2621 d.width = std::max(d.width, this->ind_textsize.width);
2622 d.height = this->ind_textsize.height;
2623 this->ind_textsize = maxdim(this->ind_textsize, GetStringBoundingBox(STR_INDUSTRY_CARGOES_SELECT_INDUSTRY));
2624
2625 /* Compute max size of the cargo texts. */
2626 this->cargo_textsize.width = 0;
2627 this->cargo_textsize.height = 0;
2628 for (const CargoSpec *csp : CargoSpec::Iterate()) {
2629 if (!csp->IsValid()) continue;
2630 this->cargo_textsize = maxdim(this->cargo_textsize, GetStringBoundingBox(csp->name));
2631 }
2632 d = maxdim(d, this->cargo_textsize); // Box must also be wide enough to hold any cargo label.
2633 this->cargo_textsize = maxdim(this->cargo_textsize, GetStringBoundingBox(STR_INDUSTRY_CARGOES_SELECT_CARGO));
2634
2636 /* Ensure the height is enough for the industry type text, for the horizontal connections, and for the cargo labels. */
2638 d.height = std::max(d.height + WidgetDimensions::scaled.frametext.Vertical(), min_ind_height);
2639
2642
2643 /* Width of a #CFT_CARGO field. */
2645 }
2646
2648 {
2649 switch (widget) {
2650 case WID_IC_PANEL:
2654 break;
2655
2657 size.width = std::max(size.width, this->ind_textsize.width + padding.width);
2658 break;
2659
2661 size.width = std::max(size.width, this->cargo_textsize.width + padding.width);
2662 break;
2663 }
2664 }
2665
2666 void SetStringParameters(WidgetID widget) const override
2667 {
2668 if (widget != WID_IC_CAPTION) return;
2669
2670 if (this->ind_cargo < NUM_INDUSTRYTYPES) {
2671 const IndustrySpec *indsp = GetIndustrySpec(this->ind_cargo);
2672 SetDParam(0, indsp->name);
2673 } else {
2674 const CargoSpec *csp = CargoSpec::Get(this->ind_cargo - NUM_INDUSTRYTYPES);
2675 SetDParam(0, csp->name);
2676 }
2677 }
2678
2685 static bool HasCommonValidCargo(const std::span<const CargoID> cargoes1, const std::span<const CargoID> cargoes2)
2686 {
2687 for (const CargoID cid1 : cargoes1) {
2688 if (!IsValidCargoID(cid1)) continue;
2689 for (const CargoID cid2 : cargoes2) {
2690 if (cid1 == cid2) return true;
2691 }
2692 }
2693 return false;
2694 }
2695
2701 static bool HousesCanSupply(const std::span<const CargoID> cargoes)
2702 {
2703 for (const CargoID cid : cargoes) {
2704 if (!IsValidCargoID(cid)) continue;
2706 if (tpe == TPE_PASSENGERS || tpe == TPE_MAIL) return true;
2707 }
2708 return false;
2709 }
2710
2716 static bool HousesCanAccept(const std::span<const CargoID> cargoes)
2717 {
2718 HouseZones climate_mask;
2720 case LT_TEMPERATE: climate_mask = HZ_TEMP; break;
2721 case LT_ARCTIC: climate_mask = HZ_SUBARTC_ABOVE | HZ_SUBARTC_BELOW; break;
2722 case LT_TROPIC: climate_mask = HZ_SUBTROPIC; break;
2723 case LT_TOYLAND: climate_mask = HZ_TOYLND; break;
2724 default: NOT_REACHED();
2725 }
2726 for (const CargoID cid : cargoes) {
2727 if (!IsValidCargoID(cid)) continue;
2728
2729 for (const auto &hs : HouseSpec::Specs()) {
2730 if (!hs.enabled || !(hs.building_availability & climate_mask)) continue;
2731
2732 for (uint j = 0; j < lengthof(hs.accepts_cargo); j++) {
2733 if (hs.cargo_acceptance[j] > 0 && cid == hs.accepts_cargo[j]) return true;
2734 }
2735 }
2736 }
2737 return false;
2738 }
2739
2745 static int CountMatchingAcceptingIndustries(const std::span<const CargoID> cargoes)
2746 {
2747 int count = 0;
2748 for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
2749 const IndustrySpec *indsp = GetIndustrySpec(it);
2750 if (!indsp->enabled) continue;
2751
2752 if (HasCommonValidCargo(cargoes, indsp->accepts_cargo)) count++;
2753 }
2754 return count;
2755 }
2756
2762 static int CountMatchingProducingIndustries(const std::span<const CargoID> cargoes)
2763 {
2764 int count = 0;
2765 for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
2766 const IndustrySpec *indsp = GetIndustrySpec(it);
2767 if (!indsp->enabled) continue;
2768
2769 if (HasCommonValidCargo(cargoes, indsp->produced_cargo)) count++;
2770 }
2771 return count;
2772 }
2773
2780 void ShortenCargoColumn(int column, int top, int bottom)
2781 {
2782 while (top < bottom && !this->fields[top].columns[column].HasConnection()) {
2783 this->fields[top].columns[column].MakeEmpty(CFT_EMPTY);
2784 top++;
2785 }
2786 this->fields[top].columns[column].u.cargo.top_end = true;
2787
2788 while (bottom > top && !this->fields[bottom].columns[column].HasConnection()) {
2789 this->fields[bottom].columns[column].MakeEmpty(CFT_EMPTY);
2790 bottom--;
2791 }
2792 this->fields[bottom].columns[column].u.cargo.bottom_end = true;
2793 }
2794
2801 void PlaceIndustry(int row, int col, IndustryType it)
2802 {
2803 assert(this->fields[row].columns[col].type == CFT_EMPTY);
2804 this->fields[row].columns[col].MakeIndustry(it);
2805 if (col == 0) {
2806 this->fields[row].ConnectIndustryProduced(col);
2807 } else {
2808 this->fields[row].ConnectIndustryAccepted(col);
2809 }
2810 }
2811
2816 {
2817 if (!this->IsWidgetLowered(WID_IC_NOTIFY)) return;
2818
2819 /* Only notify the smallmap window if it exists. In particular, do not
2820 * bring it to the front to prevent messing up any nice layout of the user. */
2822 }
2823
2829 {
2831 this->ind_cargo = displayed_it;
2832 _displayed_industries.reset();
2834
2835 this->fields.clear();
2836 CargoesRow &first_row = this->fields.emplace_back();
2838 first_row.columns[1].MakeEmpty(CFT_SMALL_EMPTY);
2839 first_row.columns[2].MakeEmpty(CFT_SMALL_EMPTY);
2840 first_row.columns[3].MakeEmpty(CFT_SMALL_EMPTY);
2841 first_row.columns[4].MakeHeader(STR_INDUSTRY_CARGOES_CUSTOMERS);
2842
2844 bool houses_supply = HousesCanSupply(central_sp->accepts_cargo);
2845 bool houses_accept = HousesCanAccept(central_sp->produced_cargo);
2846 /* Make a field consisting of two cargo columns. */
2849 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.
2850 for (int i = 0; i < num_indrows; i++) {
2851 CargoesRow &row = this->fields.emplace_back();
2853 row.columns[1].MakeCargo(central_sp->accepts_cargo);
2854 row.columns[2].MakeEmpty(CFT_EMPTY);
2855 row.columns[3].MakeCargo(central_sp->produced_cargo);
2856 row.columns[4].MakeEmpty(CFT_EMPTY);
2857 }
2858 /* Add central industry. */
2859 int central_row = 1 + num_indrows / 2;
2860 this->fields[central_row].columns[2].MakeIndustry(displayed_it);
2861 this->fields[central_row].ConnectIndustryProduced(2);
2862 this->fields[central_row].ConnectIndustryAccepted(2);
2863
2864 /* Add cargo labels. */
2865 this->fields[central_row - 1].MakeCargoLabel(2, true);
2866 this->fields[central_row + 1].MakeCargoLabel(2, false);
2867
2868 /* Add suppliers and customers of the 'it' industry. */
2869 int supp_count = 0;
2870 int cust_count = 0;
2871 for (IndustryType it : _sorted_industry_types) {
2872 const IndustrySpec *indsp = GetIndustrySpec(it);
2873 if (!indsp->enabled) continue;
2874
2875 if (HasCommonValidCargo(central_sp->accepts_cargo, indsp->produced_cargo)) {
2876 this->PlaceIndustry(1 + supp_count * num_indrows / num_supp, 0, it);
2877 _displayed_industries.set(it);
2878 supp_count++;
2879 }
2880 if (HasCommonValidCargo(central_sp->produced_cargo, indsp->accepts_cargo)) {
2881 this->PlaceIndustry(1 + cust_count * num_indrows / num_cust, 4, it);
2882 _displayed_industries.set(it);
2883 cust_count++;
2884 }
2885 }
2886 if (houses_supply) {
2887 this->PlaceIndustry(1 + supp_count * num_indrows / num_supp, 0, NUM_INDUSTRYTYPES);
2888 supp_count++;
2889 }
2890 if (houses_accept) {
2891 this->PlaceIndustry(1 + cust_count * num_indrows / num_cust, 4, NUM_INDUSTRYTYPES);
2892 cust_count++;
2893 }
2894
2895 this->ShortenCargoColumn(1, 1, num_indrows);
2896 this->ShortenCargoColumn(3, 1, num_indrows);
2897 this->vscroll->SetCount(num_indrows);
2898 this->SetDirty();
2899 this->NotifySmallmap();
2900 }
2901
2907 {
2909 this->ind_cargo = cid + NUM_INDUSTRYTYPES;
2910 _displayed_industries.reset();
2911
2912 this->fields.clear();
2913 CargoesRow &first_row = this->fields.emplace_back();
2915 first_row.columns[1].MakeEmpty(CFT_SMALL_EMPTY);
2916 first_row.columns[2].MakeHeader(STR_INDUSTRY_CARGOES_CUSTOMERS);
2917 first_row.columns[3].MakeEmpty(CFT_SMALL_EMPTY);
2918 first_row.columns[4].MakeEmpty(CFT_SMALL_EMPTY);
2919
2920 auto cargoes = std::span(&cid, 1);
2921 bool houses_supply = HousesCanSupply(cargoes);
2922 bool houses_accept = HousesCanAccept(cargoes);
2923 int num_supp = CountMatchingProducingIndustries(cargoes) + houses_supply + 1; // Ensure room for the cargo label.
2925 int num_indrows = std::max(num_supp, num_cust);
2926 for (int i = 0; i < num_indrows; i++) {
2927 CargoesRow &row = this->fields.emplace_back();
2929 row.columns[1].MakeCargo(cargoes);
2930 row.columns[2].MakeEmpty(CFT_EMPTY);
2931 row.columns[3].MakeEmpty(CFT_EMPTY);
2932 row.columns[4].MakeEmpty(CFT_EMPTY);
2933 }
2934
2935 this->fields[num_indrows].MakeCargoLabel(0, false); // Add cargo labels at the left bottom.
2936
2937 /* Add suppliers and customers of the cargo. */
2938 int supp_count = 0;
2939 int cust_count = 0;
2940 for (IndustryType it : _sorted_industry_types) {
2941 const IndustrySpec *indsp = GetIndustrySpec(it);
2942 if (!indsp->enabled) continue;
2943
2944 if (HasCommonValidCargo(cargoes, indsp->produced_cargo)) {
2945 this->PlaceIndustry(1 + supp_count * num_indrows / num_supp, 0, it);
2946 _displayed_industries.set(it);
2947 supp_count++;
2948 }
2949 if (HasCommonValidCargo(cargoes, indsp->accepts_cargo)) {
2950 this->PlaceIndustry(1 + cust_count * num_indrows / num_cust, 2, it);
2951 _displayed_industries.set(it);
2952 cust_count++;
2953 }
2954 }
2955 if (houses_supply) {
2956 this->PlaceIndustry(1 + supp_count * num_indrows / num_supp, 0, NUM_INDUSTRYTYPES);
2957 supp_count++;
2958 }
2959 if (houses_accept) {
2960 this->PlaceIndustry(1 + cust_count * num_indrows / num_cust, 2, NUM_INDUSTRYTYPES);
2961 cust_count++;
2962 }
2963
2964 this->ShortenCargoColumn(1, 1, num_indrows);
2965 this->vscroll->SetCount(num_indrows);
2966 this->SetDirty();
2967 this->NotifySmallmap();
2968 }
2969
2977 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
2978 {
2979 if (!gui_scope) return;
2980 if (data == NUM_INDUSTRYTYPES) {
2982 return;
2983 }
2984
2985 assert(data >= 0 && data < NUM_INDUSTRYTYPES);
2986 this->ComputeIndustryDisplay(data);
2987 }
2988
2989 void DrawWidget(const Rect &r, WidgetID widget) const override
2990 {
2991 if (widget != WID_IC_PANEL) return;
2992
2995 if (!FillDrawPixelInfo(&tmp_dpi, ir)) return;
2997
3000 int last_column = (this->ind_cargo < NUM_INDUSTRYTYPES) ? 4 : 2;
3001
3003 int vpos = WidgetDimensions::scaled.frametext.top - WidgetDimensions::scaled.bevel.top - this->vscroll->GetPosition() * nwp->resize_y;
3005 for (const auto &field : this->fields) {
3006 if (vpos + row_height >= 0) {
3007 int xpos = left_pos;
3008 int col, dir;
3009 if (_current_text_dir == TD_RTL) {
3010 col = last_column;
3011 dir = -1;
3012 } else {
3013 col = 0;
3014 dir = 1;
3015 }
3016 while (col >= 0 && col <= last_column) {
3017 field.columns[col].Draw(xpos, vpos);
3019 col += dir;
3020 }
3021 }
3022 vpos += row_height;
3023 if (vpos >= height) break;
3025 }
3026 }
3027
3036 {
3038 pt.x -= nw->pos_x;
3039 pt.y -= nw->pos_y;
3040
3041 int vpos = WidgetDimensions::scaled.frametext.top + CargoesField::small_height - this->vscroll->GetPosition() * nw->resize_y;
3042 if (pt.y < vpos) return false;
3043
3044 int row = (pt.y - vpos) / CargoesField::normal_height; // row is relative to row 1.
3045 if (row + 1 >= (int)this->fields.size()) return false;
3046 vpos = pt.y - vpos - row * CargoesField::normal_height; // Position in the row + 1 field
3047 row++; // rebase row to match index of this->fields.
3048
3050 if (pt.x < xpos) return false;
3051 int column;
3052 for (column = 0; column <= 5; column++) {
3054 if (pt.x < xpos + width) break;
3055 xpos += width;
3056 }
3057 int num_columns = (this->ind_cargo < NUM_INDUSTRYTYPES) ? 4 : 2;
3058 if (column > num_columns) return false;
3059 xpos = pt.x - xpos;
3060
3061 /* Return both positions, compensating for RTL languages (which works due to the equal symmetry in both displays). */
3062 fieldxy->y = row;
3063 xy->y = vpos;
3064 if (_current_text_dir == TD_RTL) {
3065 fieldxy->x = num_columns - column;
3067 } else {
3068 fieldxy->x = column;
3069 xy->x = xpos;
3070 }
3071 return true;
3072 }
3073
3074 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
3075 {
3076 switch (widget) {
3077 case WID_IC_PANEL: {
3078 Point fieldxy, xy;
3079 if (!CalculatePositionInWidget(pt, &fieldxy, &xy)) return;
3080
3081 const CargoesField *fld = this->fields[fieldxy.y].columns + fieldxy.x;
3082 switch (fld->type) {
3083 case CFT_INDUSTRY:
3084 if (fld->u.industry.ind_type < NUM_INDUSTRYTYPES) this->ComputeIndustryDisplay(fld->u.industry.ind_type);
3085 break;
3086
3087 case CFT_CARGO: {
3088 CargoesField *lft = (fieldxy.x > 0) ? this->fields[fieldxy.y].columns + fieldxy.x - 1 : nullptr;
3089 CargoesField *rgt = (fieldxy.x < 4) ? this->fields[fieldxy.y].columns + fieldxy.x + 1 : nullptr;
3090 CargoID cid = fld->CargoClickedAt(lft, rgt, xy);
3091 if (IsValidCargoID(cid)) this->ComputeCargoDisplay(cid);
3092 break;
3093 }
3094
3095 case CFT_CARGO_LABEL: {
3096 CargoID cid = fld->CargoLabelClickedAt(xy);
3097 if (IsValidCargoID(cid)) this->ComputeCargoDisplay(cid);
3098 break;
3099 }
3100
3101 default:
3102 break;
3103 }
3104 break;
3105 }
3106
3107 case WID_IC_NOTIFY:
3111
3112 if (this->IsWidgetLowered(WID_IC_NOTIFY)) {
3113 if (FindWindowByClass(WC_SMALLMAP) == nullptr) ShowSmallMap();
3114 this->NotifySmallmap();
3115 }
3116 break;
3117
3118 case WID_IC_CARGO_DROPDOWN: {
3121 for (const CargoSpec *cs : _sorted_standard_cargo_specs) {
3122 lst.push_back(MakeDropDownListIconItem(d, cs->GetCargoIcon(), PAL_NONE, cs->name, cs->Index()));
3123 }
3124 if (!lst.empty()) {
3125 int selected = (this->ind_cargo >= NUM_INDUSTRYTYPES) ? (int)(this->ind_cargo - NUM_INDUSTRYTYPES) : -1;
3126 ShowDropDownList(this, std::move(lst), selected, WID_IC_CARGO_DROPDOWN);
3127 }
3128 break;
3129 }
3130
3131 case WID_IC_IND_DROPDOWN: {
3133 for (IndustryType ind : _sorted_industry_types) {
3135 if (!indsp->enabled) continue;
3136 lst.push_back(MakeDropDownListStringItem(indsp->name, ind));
3137 }
3138 if (!lst.empty()) {
3139 int selected = (this->ind_cargo < NUM_INDUSTRYTYPES) ? (int)this->ind_cargo : -1;
3140 ShowDropDownList(this, std::move(lst), selected, WID_IC_IND_DROPDOWN);
3141 }
3142 break;
3143 }
3144 }
3145 }
3146
3147 void OnDropdownSelect(WidgetID widget, int index) override
3148 {
3149 if (index < 0) return;
3150
3151 switch (widget) {
3153 this->ComputeCargoDisplay(index);
3154 break;
3155
3157 this->ComputeIndustryDisplay(index);
3158 break;
3159 }
3160 }
3161
3162 bool OnTooltip([[maybe_unused]] Point pt, WidgetID widget, TooltipCloseCondition close_cond) override
3163 {
3164 if (widget != WID_IC_PANEL) return false;
3165
3166 Point fieldxy, xy;
3167 if (!CalculatePositionInWidget(pt, &fieldxy, &xy)) return false;
3168
3169 const CargoesField *fld = this->fields[fieldxy.y].columns + fieldxy.x;
3170 CargoID cid = INVALID_CARGO;
3171 switch (fld->type) {
3172 case CFT_CARGO: {
3173 CargoesField *lft = (fieldxy.x > 0) ? this->fields[fieldxy.y].columns + fieldxy.x - 1 : nullptr;
3174 CargoesField *rgt = (fieldxy.x < 4) ? this->fields[fieldxy.y].columns + fieldxy.x + 1 : nullptr;
3175 cid = fld->CargoClickedAt(lft, rgt, xy);
3176 break;
3177 }
3178
3179 case CFT_CARGO_LABEL: {
3180 cid = fld->CargoLabelClickedAt(xy);
3181 break;
3182 }
3183
3184 case CFT_INDUSTRY:
3185 if (fld->u.industry.ind_type < NUM_INDUSTRYTYPES && (this->ind_cargo >= NUM_INDUSTRYTYPES || fieldxy.x != 2)) {
3187 }
3188 return true;
3189
3190 default:
3191 break;
3192 }
3194 const CargoSpec *csp = CargoSpec::Get(cid);
3195 SetDParam(0, csp->name);
3197 return true;
3198 }
3199
3200 return false;
3201 }
3202
3203 void OnResize() override
3204 {
3205 this->vscroll->SetCapacityFromWidget(this, WID_IC_PANEL, WidgetDimensions::scaled.framerect.Vertical() + CargoesField::small_height);
3206 }
3207};
3208
3213static void ShowIndustryCargoesWindow(IndustryType id)
3214{
3215 if (id >= NUM_INDUSTRYTYPES) {
3216 for (IndustryType ind : _sorted_industry_types) {
3217 const IndustrySpec *indsp = GetIndustrySpec(ind);
3218 if (indsp->enabled) {
3219 id = ind;
3220 break;
3221 }
3222 }
3223 if (id >= NUM_INDUSTRYTYPES) return;
3224 }
3225
3227 if (w != nullptr) {
3228 w->InvalidateData(id);
3229 return;
3230 }
3231 new IndustryCargoesWindow(id);
3232}
3233
Class for backupping variables and making sure they are restored later.
debug_inline constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
debug_inline static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
uint8_t CargoID
Cargo slots to indicate a cargo type within a game.
Definition cargo_type.h:22
bool IsValidCargoID(CargoID t)
Test whether cargo type is not INVALID_CARGO.
Definition cargo_type.h:107
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:34
@ TPE_PASSENGERS
Cargo behaves passenger-like for production.
Definition cargotype.h:36
@ TPE_MAIL
Cargo behaves mail-like for production.
Definition cargotype.h:37
Cheats _cheats
All the cheats.
Definition cheat.cpp:16
Types related to cheating.
Build (fund or prospect) a new industry,.
IndustryType selected_type
industry corresponding to the above index
void SetStringParameters(WidgetID widget) const override
Initialize string parameters for a widget.
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.
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 CargoID > cargolist, const std::span< const CargoSuffix > cargo_suffix, StringID prefixstr) const
Build a string of cargo names with suffixes attached.
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
void OnTimeout() override
Called when this window's timeout has been reached.
void OnResize() override
Called after the window got resized.
void UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize) override
Update size and resize step of a widget in the window.
std::vector< IndustryType > list
List of industries.
bool enabled
Availability state of the selected industry.
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
Common return value for all commands.
bool Succeeded() const
Did this command succeed?
StringID GetErrorMessage() const
Returns the error message of a command.
List template of 'things' T to sort in a GUI.
bool Filter(FilterFunction *decide, F filter_data)
Filter the list.
void RebuildDone()
Notify the sortlist that the rebuild is done.
void SetListing(Listing l)
Import sort conditions.
void SetFilterState(bool state)
Enable or disable the filter.
bool IsDescSortOrder() const
Check if the sort order is descending.
void ToggleSortOrder()
Toggle the sort order Since that is the worst condition for the sort function reverse the list here.
bool(const const Industry * *, const std::pair< CargoID, CargoID > &) FilterFunction
Signature of filter function.
void SetFilterFuncs(std::span< FilterFunction *const > n_funcs)
Hand the filter function pointers to the GUIList.
bool NeedRebuild() const
Check if a rebuild is needed.
void SetFilterType(uint8_t n_type)
Set the filtertype of the list.
void ForceRebuild()
Force that a rebuild is needed.
bool Sort(Comp compare)
Sort the list.
void ForceResort()
Force a resort next Sort call Reset the resort timer if used too.
uint8_t SortType() const
Get the sorttype of the list.
Listing GetListing() const
Export current sort conditions.
void SetSortFuncs(std::span< SortFunction *const > n_funcs)
Hand the sort function pointers to the GUIList.
void SetSortType(uint8_t n_type)
Set the sorttype of the list.
The list of industries.
IntervalTimer< TimerWindow > rebuild_interval
Rebuild the industry list on a regular interval.
void OnEditboxChanged(WidgetID wid) override
The text in an editbox has been edited.
void SetProducedCargoFilter(CargoID cid)
Set produced cargo filter for the industry list.
static int GetCargoTransportedSortValue(const Industry *i)
Returns value representing industry's transported cargo percentage for industry sorting.
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
const int MAX_FILTER_LENGTH
The max length of the filter, in chars.
EventState OnHotkey(int hotkey) override
A hotkey has been pressed.
void SetCargoFilterArray()
Populate the filter list and set the cargo filter criteria.
void OnResize() override
Called after the window got resized.
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
static int GetCargoTransportedPercentsIfValid(const Industry::ProducedCargo &p)
Returns percents of cargo transported if industry produces this cargo, else -1.
void SetStringParameters(WidgetID widget) const override
Initialize string parameters for a widget.
CargoID accepted_cargo_filter_criteria
Selected accepted cargo filter index.
@ 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.
uint GetIndustryListWidth() const
Get the width needed to draw the longest industry line.
static bool IndustryProductionSorter(const Industry *const &a, const Industry *const &b, const CargoID &filter)
Sort industries by production and name.
QueryString industry_editbox
Filter editbox.
StringFilter string_filter
Filter for industries.
void BuildSortIndustriesList()
(Re)Build industries list
static bool IndustryTypeSorter(const Industry *const &a, const Industry *const &b, const CargoID &filter)
Sort industries by type and name.
static bool IndustryNameSorter(const Industry *const &a, const Industry *const &b, const CargoID &)
Sort industries by name.
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.
CargoID produced_cargo_filter_criteria
Selected produced cargo filter index.
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
StringID GetIndustryString(const Industry *i) const
Get the StringID to draw and set the appropriate DParams.
void OnDropdownSelect(WidgetID widget, int index) override
A dropdown option associated to this window has been selected.
void SetAcceptedCargoFilter(CargoID cid)
Set accepted cargo filter for the industry list.
void OnPaint() override
The window must be repainted.
static bool IndustryTransportedCargoSorter(const Industry *const &a, const Industry *const &b, const CargoID &filter)
Sort industries by transported cargo and name.
void OnInit() override
Notification that the nested widget tree gets initialized.
Editability
Modes for changing production.
@ EA_RATE
Allow changing the production rates.
@ EA_MULTIPLIER
Allow changing the production multiplier.
@ EA_NONE
Not alterable.
void OnResize() override
Called after the window got resized.
int cheat_line_height
Height of each line for the WID_IV_INFO panel.
int production_offset_y
The offset of the production texts/buttons.
void OnPaint() override
The window must be repainted.
Editability editable
Mode for changing production.
InfoLine clicked_line
The line of the button that has been clicked.
InfoLine
Specific lines in the info panel.
@ IL_RATE2
Production rate of cargo 2.
@ IL_MULTIPLIER
Production multiplier.
@ IL_RATE1
Production rate of cargo 1.
void OnMouseWheel(int wheel) override
The mouse wheel has been turned.
void OnTimeout() override
Called when this window's timeout has been reached.
void SetStringParameters(WidgetID widget) const override
Initialize string parameters for a widget.
bool IsNewGRFInspectable() const override
Is the data related to this window NewGRF inspectable?
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
int info_height
Height needed for the WID_IV_INFO panel.
Dimension cargo_icon_size
Largest cargo icon dimension.
void ShowNewGRFInspectWindow() const override
Show the NewGRF inspection window.
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.
int DrawInfo(const Rect &r)
Draw the text in the WID_IV_INFO panel.
InfoLine editbox_line
The line clicked to open the edit box.
An interval timer will fire every interval, and will continue to fire until it is deleted.
Definition timer.h:76
Baseclass for nested widgets.
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:2354
void InitializeViewport(Window *w, std::variant< TileIndex, VehicleID > focus, ZoomLevel zoom)
Initialize the viewport of the window.
Definition widget.cpp:2345
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:2451
size_type GetCount() const
Gets the number of elements in the list.
auto GetVisibleRangeIterators(Tcontainer &container) const
Get a pair of iterators for the range of visible elements in a container.
size_type GetPosition() const
Gets the position of the first visible element in the list.
static bool UsingWallclockUnits(bool newgame=false)
Check if we are using wallclock units.
RectPadding framerect
Standard padding inside many panels.
Definition window_gui.h:42
RectPadding frametext
Padding inside frame with text.
Definition window_gui.h:43
static WidgetDimensions scaled
Widget dimensions scaled for current zoom level.
Definition window_gui.h:28
int vsep_wide
Wide vertical spacing.
Definition window_gui.h:62
int hsep_wide
Wide horizontal spacing.
Definition window_gui.h:64
static const WidgetDimensions unscaled
Unscaled widget dimensions.
Definition window_gui.h:96
int hsep_normal
Normal horizontal spacing.
Definition window_gui.h:63
RectPadding bevel
Bevel thickness, affected by "scaled bevels" game option.
Definition window_gui.h:40
Map accessors for 'clear' tiles.
ClearGround GetRawClearGround(Tile t)
Get the type of clear tile but never return CLEAR_SNOW.
Definition clear_map.h:47
@ CLEAR_GRASS
0-3
Definition clear_map.h:20
@ CLEAR_FIELDS
3
Definition clear_map.h:23
void MakeClear(Tile t, ClearGround g, uint density)
Make a clear tile.
Definition clear_map.h:259
Functions related to commands.
Commands
List of commands.
Definition of stuff that is very close to a company, like the company struct itself.
CompanyID _local_company
Company controlled by the human player at this client. Can also be COMPANY_SPECTATOR.
CompanyID _current_company
Company currently doing an action.
Functions related to companies.
@ OWNER_NONE
The tile has no ownership.
void ShowDropDownMenu(Window *w, std::span< const StringID > strings, int selected, WidgetID button, uint32_t disabled_mask, uint32_t hidden_mask, uint width)
Show a dropdown menu window near a widget of the parent window.
Definition dropdown.cpp:441
void ShowDropDownList(Window *w, DropDownList &&list, int selected, WidgetID button, uint width, bool instant_close, bool persist)
Show a drop down list.
Definition dropdown.cpp:404
Functions related to the drop down widget.
Types related to the drop down widget.
std::vector< std::unique_ptr< const DropDownListItem > > DropDownList
A drop down list is a collection of drop down list items.
uint ScaleByCargoScale(uint num, bool town)
Scale a number by the cargo scale setting.
Functions related to errors.
void ShowErrorMessage(StringID summary_msg, int x, int y, CommandCost cc)
Display an error message in a window.
@ WL_INFO
Used for DoCommand-like (and some non-fatal AI GUI) errors/information.
Definition error.h:24
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
Definition fontcache.cpp:77
bool _generating_world
Whether we are generating the map or not.
Definition genworld.cpp:67
Functions related to world/map generation.
Dimension maxdim(const Dimension &d1, const Dimension &d2)
Compute bounding box of both dimensions.
Geometry functions.
Dimension GetSpriteSize(SpriteID sprid, Point *offset, ZoomLevel zoom)
Get the size of a sprite.
Definition gfx.cpp:922
Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
Return the string dimension in pixels.
Definition gfx.cpp:851
void DrawRectOutline(const Rect &r, int colour, int width, int dash)
Draw the outline of a Rect.
Definition gfx.cpp:456
Dimension GetStringListBoundingBox(std::span< const StringID > list, FontSize fontsize)
Get maximum dimension of a list of strings.
Definition gfx.cpp:889
int DrawString(int left, int right, int top, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly truncated to make it fit in its allocated space.
Definition gfx.cpp:657
bool _ctrl_pressed
Is Ctrl pressed?
Definition gfx.cpp:38
void GfxFillRect(int left, int top, int right, int bottom, int colour, FillRectMode mode)
Applies a certain FillRectMode-operation to a rectangle [left, right] x [top, bottom] on the screen.
Definition gfx.cpp:114
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:988
int DrawStringMultiLine(int left, int right, int top, int bottom, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly over multiple lines.
Definition gfx.cpp:774
bool 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:1548
int CenterBounds(int min, int max, int size)
Determine where to draw a centred object inside a widget.
Definition gfx_func.h:166
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
Definition gfx_type.h:18
@ SA_LEFT
Left align the text.
Definition gfx_type.h:343
@ SA_RIGHT
Right align the text (must be a single bit).
Definition gfx_type.h:345
@ SA_HOR_CENTER
Horizontally center the text.
Definition gfx_type.h:344
@ FS_SMALL
Index of the small font in the font tables.
Definition gfx_type.h:210
@ FS_NORMAL
Index of the normal font in the font tables.
Definition gfx_type.h:209
TextColour
Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palett...
Definition gfx_type.h:260
@ TC_FORCED
Ignore colour changes from strings.
Definition gfx_type.h:285
@ FILLRECT_OPAQUE
Fill rectangle with a single colour.
Definition gfx_type.h:298
Graph GUI functions.
constexpr NWidgetPart SetFill(uint16_t fill_x, uint16_t fill_y)
Widget part function for setting filling.
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 SetDataTip(uint32_t data, StringID tip)
Widget part function for setting the data and tooltip.
constexpr NWidgetPart SetMinimalSize(int16_t x, int16_t y)
Widget part function for setting the minimal size.
constexpr NWidgetPart NWidget(WidgetType tp, Colours col, WidgetID idx=-1)
Widget part function for starting a new 'real' widget.
constexpr NWidgetPart SetMatrixDataTip(uint8_t cols, uint8_t rows, StringID tip)
Widget part function for setting the data and tooltip of WWT_MATRIX widgets.
constexpr NWidgetPart EndContainer()
Widget part function for denoting the end of a container (horizontal, vertical, WWT_FRAME,...
constexpr NWidgetPart SetMinimalTextLines(uint8_t lines, uint8_t spacing, FontSize size=FS_NORMAL)
Widget part function for setting the minimal text lines.
constexpr NWidgetPart SetAspect(float ratio, AspectFlags flags=AspectFlags::ResizeX)
Widget part function for setting the aspect ratio.
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:940
void MarkWholeScreenDirty()
This function mark the whole screen as dirty.
Definition gfx.cpp:1529
GUI functions that shouldn't be here.
void ShowExtraViewportWindow(TileIndex tile=INVALID_TILE)
Show a new Extra Viewport window.
Hotkey related functions.
HouseZones
Definition house.h:66
@ HZ_SUBARTC_BELOW
13 2000 can appear in sub-arctic climate below the snow line
Definition house.h:76
@ HZ_TEMP
12 1000 can appear in temperate climate
Definition house.h:75
@ HZ_TOYLND
15 8000 can appear in toyland climate
Definition house.h:78
@ HZ_SUBTROPIC
14 4000 can appear in subtropical climate
Definition house.h:77
@ HZ_SUBARTC_ABOVE
11 800 can appear in sub-arctic climate above the snow line
Definition house.h:74
Base of all industries.
static constexpr uint8_t PRODLEVEL_MAXIMUM
the industry is running at full speed
Definition industry.h:36
static constexpr uint8_t PRODLEVEL_DEFAULT
default level set when the industry is created
Definition industry.h:35
static constexpr uint8_t PRODLEVEL_MINIMUM
below this level, the industry is set to be closing
Definition industry.h:34
static constexpr uint8_t PRODLEVEL_CLOSURE
signal set to actually close the industry
Definition industry.h:33
const IndustrySpec * GetIndustrySpec(IndustryType thistype)
Accessor for array _industry_specs.
Command definitions related to industries.
static WindowDesc _industry_directory_desc(WDP_AUTO, "list_industries", 428, 190, WC_INDUSTRY_DIRECTORY, WC_NONE, 0, _nested_industry_directory_widgets, &IndustryDirectoryWindow::hotkeys)
Window 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)
static const uint MAX_CARGOES
Maximum number of cargoes carried in a CFT_CARGO field in CargoesField.
CargoesFieldType
Available types of field.
@ CFT_SMALL_EMPTY
Empty small field (for the header).
@ CFT_EMPTY
Empty field.
@ CFT_INDUSTRY
Display industry.
@ CFT_CARGO
Display cargo connections.
@ CFT_HEADER
Header text.
@ CFT_CARGO_LABEL
Display cargo labels.
static WindowDesc _industry_view_desc(WDP_AUTO, "view_industry", 260, 120, WC_INDUSTRY_VIEW, WC_NONE, 0, _nested_industry_view_widgets)
Window definition of the view industry gui.
static void GetAllCargoSuffixes(CargoSuffixInOut use_input, CargoSuffixType cst, const Industry *ind, IndustryType ind_type, const IndustrySpec *indspec, const TC &cargoes, TS &suffixes)
Gets all strings to display after the cargoes of industries (using callback 37)
IndustryDirectoryHotkeys
Enum referring to the Hotkeys in the industry directory window.
@ IDHK_FOCUS_FILTER_BOX
Focus the filter box.
CargoSuffixType
Cargo suffix type (for which window is it requested)
@ CST_DIR
Industry-directory window.
@ CST_FUND
Fund-industry window.
@ CST_VIEW
View-industry window.
void GenerateIndustries()
This function will create random industries during game creation.
static bool IndustryTypeNameSorter(const IndustryType &a, const IndustryType &b)
Sort industry types by their name.
std::array< IndustryType, NUM_INDUSTRYTYPES > _sorted_industry_types
Industry types sorted by name.
static WindowDesc _build_industry_desc(WDP_AUTO, "build_industry", 170, 212, WC_BUILD_INDUSTRY, WC_NONE, WDF_CONSTRUCTION, _nested_build_industry_widgets)
Window definition of the dynamic place industries gui.
static constexpr NWidgetPart _nested_industry_view_widgets[]
Widget definition of the view industry gui.
std::bitset< NUM_INDUSTRYTYPES > _displayed_industries
Communication from the industry chain window to the smallmap window about what industries to display.
static bool CargoFilter(const Industry *const *industry, const std::pair< CargoID, CargoID > &cargoes)
Cargo filter functions.
void CcBuildIndustry(Commands, const CommandCost &result, TileIndex tile, IndustryType indtype, uint32_t, bool, uint32_t)
Command callback.
CargoSuffixDisplay
Ways of displaying the cargo.
@ CSD_CARGO_TEXT
Display then cargo and supplied string (cb37 result 800-BFF).
@ CSD_CARGO
Display the cargo without sub-type (cb37 result 401).
@ CSD_CARGO_AMOUNT_TEXT
Display then cargo, amount, and string (cb37 result 000-3FF).
@ CSD_CARGO_AMOUNT
Display the cargo and amount (if useful), but no sub-type (cb37 result 400 or fail).
static constexpr NWidgetPart _nested_industry_directory_widgets[]
Widget definition of the industry directory gui.
static WindowDesc _industry_cargoes_desc(WDP_AUTO, "industry_cargoes", 300, 210, WC_INDUSTRY_CARGOES, WC_NONE, 0, _nested_industry_cargoes_widgets)
Window description for the industry cargoes window.
static constexpr NWidgetPart _nested_industry_cargoes_widgets[]
Widgets of the industry cargoes window.
void SortIndustryTypes()
Initialize the list of sorted industry types.
static const int INDUSTRY_ORIGINAL_NUM_INPUTS
Original number of accepted cargo types.
static const IndustryType NUM_INDUSTRYTYPES
total number of industry types, new and old; limited to 240 because we need some special ids like INV...
static const IndustryType INVALID_INDUSTRYTYPE
one above amount is considered invalid
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.
@ INDUSTRYBEH_CARGOTYPES_UNLIMITED
Allow produced/accepted cargoes callbacks to supply more than 2 and 3 types.
bool DoZoomInOutWindow(ZoomStateChange how, Window *w)
Zooms a viewport in a window in or out.
Definition main_gui.cpp:93
bool HandlePlacePushButton(Window *w, WidgetID widget, CursorID cursor, HighLightStyle mode)
This code is shared for the majority of the pushbuttons.
Definition main_gui.cpp:63
static debug_inline uint TileY(TileIndex tile)
Get the Y component of a tile.
Definition map_func.h:425
static debug_inline uint TileX(TileIndex tile)
Get the X component of a tile.
Definition map_func.h:415
constexpr bool IsInsideBS(const T x, const size_t base, const size_t size)
Checks if a value is between a window started at some base point.
constexpr int RoundDivSU(int a, uint b)
Computes round(a / b) for signed a and unsigned b.
constexpr uint ToPercent8(uint i)
Converts a "fract" value 0..255 to "percent" value 0..100.
constexpr uint ClampU(const uint a, const uint min, const uint max)
Clamp an unsigned integer between an interval.
void GuiShowTooltips(Window *parent, StringID str, TooltipCloseCondition close_tooltip, uint paramcount)
Shows a tooltip.
Definition misc_gui.cpp:740
void ShowQuery(StringID caption, StringID 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(StringID str, StringID caption, uint maxsize, Window *parent, CharSetFilter afilter, QueryStringFlags flags)
Show a query popup window with a textbox in it.
static constexpr CargoID CF_NONE
Show only items which do not carry cargo (e.g. train engines)
Definition cargo_type.h:95
static constexpr CargoID CF_ANY
Show all items independent of carried cargo (i.e. no filtering)
Definition cargo_type.h:94
bool _networking
are we in networking mode?
Definition network.cpp:65
Basic functions/variables used all over the place.
@ CBM_IND_FUND_MORE_TEXT
additional text in fund window
@ CBM_IND_WINDOW_MORE_TEXT
additional text in industry window
@ CBM_IND_PRODUCTION_CARGO_ARRIVAL
call production callback when cargo arrives at the industry
@ CBM_IND_CARGO_SUFFIX
cargo sub-type display
@ CBM_IND_PRODUCTION_256_TICKS
call production callback every 256 ticks
@ 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.
bool IndustryTemporarilyRefusesCargo(Industry *ind, CargoID cargo_type)
Check whether an industry temporarily refuses to accept a certain cargo.
uint32_t GetIndustryProbabilityCallback(IndustryType type, IndustryAvailabilityCallType creation_type, uint32_t default_prob)
Check with callback CBID_INDUSTRY_PROBABILITY whether the industry can be built.
uint16_t GetIndustryCallback(CallbackID callback, uint32_t param1, uint32_t param2, Industry *industry, IndustryType type, TileIndex tile)
Perform an industry callback.
Functions for NewGRF industries.
@ IACT_USERCREATION
from the Fund/build window
@ PSM_ENTER_GAMELOOP
Enter the gameloop, changes will be permanent.
@ PSM_LEAVE_GAMELOOP
Leave the gameloop, changes will be temporary.
void StartTextRefStackUsage(const GRFFile *grffile, uint8_t numEntries, const uint32_t *values)
Start using the TTDP compatible string code parsing.
StringID GetGRFStringID(uint32_t grfid, StringID stringid)
Returns the index for this stringid associated with its grfID.
void StopTextRefStackUsage()
Stop using the TTDP compatible string code parsing.
Header of Action 04 "universal holder" structure and functions.
static const uint8_t PC_WHITE
White palette colour.
static const uint8_t PC_BLACK
Black palette colour.
static const uint8_t PC_YELLOW
Yellow palette colour.
Base for the GUIs that have an edit box in them.
Pseudo random number generator.
A number of safeguards to prevent using unsafe methods.
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition settings.cpp:57
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:56
void DrawArrowButtons(int x, int y, Colours button_colour, uint8_t state, bool clickable_left, bool clickable_right)
Draw [<][>] boxes.
Functions for setting GUIs.
#define SETTING_BUTTON_WIDTH
Width of setting buttons.
#define SETTING_BUTTON_HEIGHT
Height of setting buttons.
@ SWS_OFF
Scroll wheel has no effect.
void ShowSmallMap()
Show the smallmap window.
Smallmap GUI functions.
Base types for having sorted lists in GUIs.
Functions related to sound.
@ SND_15_BEEP
19 == 0x13 GUI button click
Definition sound_type.h:66
Definition of base types and functions in a cross-platform compatible way.
#define lengthof(array)
Return the length of an fixed size array.
Definition stdafx.h:280
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:589
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 AppendStringInPlace(std::string &result, StringID string)
Resolve the given StringID and append in place into an existing std::string with all the associated D...
Definition strings.cpp:345
void SetDParam(size_t n, uint64_t v)
Set a string parameter v at index n in the global string parameter array.
Definition strings.cpp:104
std::string GetString(StringID string)
Resolve the given StringID into a std::string with all the associated DParam lookups and formatting.
Definition strings.cpp:333
TextDirection _current_text_dir
Text direction of the currently selected language.
Definition strings.cpp:56
void SetDParamStr(size_t n, const char *str)
This function is used to "bind" a C string to a OpenTTD dparam slot.
Definition strings.cpp:371
void SetDParamMaxDigits(size_t n, uint count, FontSize size)
Set DParam n to some number that is suitable for string size computations.
Definition strings.cpp:143
Functions related to OTTD's strings.
@ TD_RTL
Text is written right-to-left by default.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
static const int MAX_CHAR_LENGTH
Max. length of UTF-8 encoded unicode character.
Class to backup a specific variable and restore it upon destruction of this object to prevent stack v...
Class to backup a specific variable and restore it later.
static void SwitchMode(PersistentStorageMode mode, bool ignore_prev_mode=false)
Clear temporary changes made since the last call to SwitchMode, and set whether subsequent changes sh...
Comparator to sort CargoID by according to desired order.
Definition cargotype.h:247
Specification of a cargo type.
Definition cargotype.h:76
static CargoSpec * Get(size_t index)
Retrieve cargo details for the given cargo ID.
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:195
StringID name
Name of this type of cargo.
Definition cargotype.h:93
TownProductionEffect town_production_effect
The effect on town cargo production.
Definition cargotype.h:89
Transfer storage of cargo suffix information.
std::string text
Cargo suffix text.
CargoSuffixDisplay display
How to display the cargo and text.
Data about a single field in the IndustryCargoesWindow panel.
StringID header
Header text (for CFT_HEADER).
CargoesFieldType type
Type of field.
static int blob_distance
Distance of the industry legend colour from the edge of the industry box.
static Dimension cargo_border
Dimensions of border between cargo lines and industry boxes.
static const int INDUSTRY_LINE_COLOUR
Line colour of the industry type box.
uint8_t top_end
Stop at the top of the vertical cargoes.
Cargoes cust_cargoes
Cargoes in vertical_cargoes leaving to the right.
struct CargoesField::@5::@8 cargo_label
Label data (for CFT_CARGO_LABEL).
struct CargoesField::@5::@7 cargo
Cargo data (for CFT_CARGO).
struct CargoesField::@5::@6 industry
Industry data (for CFT_INDUSTRY).
int GetCargoBase(int xpos) const
For a CFT_CARGO, compute the left position of the left-most vertical cargo connection.
CargoID CargoClickedAt(const CargoesField *left, const CargoesField *right, Point pt) const
Decide which cargo was clicked at in a CFT_CARGO field.
static const int CARGO_LINE_COLOUR
Line colour around the cargo.
CargoID CargoLabelClickedAt(Point pt) const
Decide what cargo the user clicked in the cargo label field.
static int small_height
Height of the header row.
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.
int ConnectCargo(CargoID cargo, bool producer)
Connect a cargo from an industry to the CFT_CARGO column.
bool left_align
Align all cargo texts to the left (else align to the right).
static uint max_cargoes
Largest number of cargoes actually on any industry.
bool HasConnection()
Does this CFT_CARGO field have a horizontal connection?
CargoID vertical_cargoes[MAX_CARGOES]
Cargoes running from top to bottom (cargo ID or INVALID_CARGO).
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.
CargoID cargoes[MAX_CARGOES]
Cargoes to display (or INVALID_CARGO).
IndustryType ind_type
Industry type (NUM_INDUSTRYTYPES means 'houses').
static int vert_inter_industry_space
Amount of space between two industries in a column.
static Dimension cargo_stub
Dimensions of cargo stub (unconnected cargo line.)
static Dimension legend
Dimension of the legend blob.
CargoID other_produced[MAX_CARGOES]
Cargoes produced but not used in this figure.
Cargoes supp_cargoes
Cargoes in vertical_cargoes entering from the left.
void MakeCargo(const std::span< const CargoID > cargoes)
Make a piece of cargo column.
void Draw(int xpos, int ypos) const
Draw the field.
void MakeCargoLabel(const std::span< const CargoID > cargoes, bool left_align)
Make a field displaying cargo type names.
CargoID other_accepted[MAX_CARGOES]
Cargoes accepted but not used in this figure.
static int industry_width
Width of an industry field.
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 MakeEmpty(CargoesFieldType type)
Make one of the empty fields (CFT_EMPTY or CFT_SMALL_EMPTY).
A single row of CargoesField.
CargoesField columns[5]
One row of fields.
void ConnectIndustryProduced(int column)
Connect industry production cargoes to the cargo column after it.
void ConnectIndustryAccepted(int column)
Connect industry accepted cargoes to the cargo column before it.
void MakeCargoLabel(int column, bool accepting)
Construct a CFT_CARGO_LABEL field.
bool value
tells if the bool cheat is active or not
Definition cheat_type.h:18
Cheat setup_prod
setup raw-material production in game
Definition cheat_type.h:33
SoundSettings sound
sound effect settings
GUISettings gui
settings related to the GUI
uint8_t raw_industry_construction
type of (raw) industry construction (none, "normal", prospecting)
Dimensions (a width and height) of a rectangle in 2D.
Data about how and where to blit pixels.
Definition gfx_type.h:157
uint32_t grfid
grfid that introduced this entity.
const struct GRFFile * grffile
grf file that introduced this entity
std::array< uint8_t, NUM_CARGO > cargo_map
Inverse cargo translation table (CargoID -> local ID)
Definition newgrf.h:130
bool persistent_buildingtools
keep the building tools active after usage
uint8_t scrollwheel_scrolling
scrolling using the scroll wheel?
uint8_t landscape
the landscape we're currently in
ConstructionSettings construction
construction of things in-game
GameCreationSettings game_creation
settings used during the creation of a game (map)
List of hotkeys for a window.
Definition hotkeys.h:37
All data for a single hotkey.
Definition hotkeys.h:21
static std::vector< HouseSpec > & Specs()
Get a reference to all HouseSpecs.
Window displaying the cargo connections around an industry (or cargo).
Dimension cargo_textsize
Size to hold any cargo text, as well as STR_INDUSTRY_CARGOES_SELECT_CARGO.
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
static bool HousesCanAccept(const std::span< const CargoID > cargoes)
Can houses be used as customers of the produced cargoes?
void OnInit() override
Notification that the nested widget tree gets initialized.
void OnDropdownSelect(WidgetID widget, int index) override
A dropdown option associated to this window has been selected.
bool OnTooltip(Point pt, WidgetID widget, TooltipCloseCondition close_cond) override
Event to display a custom tooltip.
Dimension ind_textsize
Size to hold any industry type text, as well as STR_INDUSTRY_CARGOES_SELECT_INDUSTRY.
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
void ShortenCargoColumn(int column, int top, int bottom)
Shorten the cargo column to just the part between industries.
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.
static bool HousesCanSupply(const std::span< const CargoID > cargoes)
Can houses be used to supply one of the cargoes?
Fields fields
Fields to display in the WID_IC_PANEL.
static bool HasCommonValidCargo(const std::span< const CargoID > cargoes1, const std::span< const CargoID > cargoes2)
Do the two sets of cargoes have a valid cargo in common?
static int CountMatchingProducingIndustries(const std::span< const CargoID > cargoes)
Count how many industries have produced cargoes in common with one of the supplied set.
void ComputeCargoDisplay(CargoID cid)
Compute what and where to display for cargo id cid.
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).
static int CountMatchingAcceptingIndustries(const std::span< const CargoID > cargoes)
Count how many industries have accepted cargoes in common with one of the supplied set.
bool CalculatePositionInWidget(Point pt, Point *fieldxy, Point *xy)
Calculate in which field was clicked, and within the field, at what position.
uint ind_cargo
If less than NUM_INDUSTRYTYPES, an industry type, else a cargo id + NUM_INDUSTRYTYPES.
void SetStringParameters(WidgetID widget) const override
Initialize string parameters for a widget.
void UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize) override
Update size and resize step of a widget in the window.
Defines the data structure for constructing industry.
uint16_t callback_mask
Bitmask of industry callbacks that have to be called.
std::array< CargoID, INDUSTRY_NUM_INPUTS > accepts_cargo
16 accepted cargoes.
bool UsesOriginalEconomy() const
Determines whether this industrytype uses standard/newgrf production changes.
StringID name
Displayed name of the industry.
IndustryBehaviour behaviour
How this industry will behave, and how others entities can use it.
bool IsRawIndustry() const
Is an industry with the spec a raw industry?
GRFFileProps grf_prop
properties related to the grf file
bool enabled
entity still available (by default true).newgrf can disable it, though
uint8_t map_colour
colour used for the small map
CargoID cargo
Cargo type.
Definition industry.h:79
std::array< ProducedHistory, 25 > history
History of cargo produced and transported for this month and 24 previous months.
Definition industry.h:82
Defines the internal data of a functional industry.
Definition industry.h:66
IndustryType type
type of industry.
Definition industry.h:102
uint8_t prod_level
general production level
Definition industry.h:99
void RecomputeProductionMultipliers()
Recompute #production_rate for current prod_level.
ProducedCargoes produced
produced cargo slots
Definition industry.h:97
AcceptedCargoes accepted
accepted cargo slots
Definition industry.h:98
static uint16_t GetIndustryTypeCount(IndustryType type)
Get the count of industries for this type.
Definition industry.h:251
std::string text
General text with additional information.
Definition industry.h:119
TileArea location
Location of the industry.
Definition industry.h:94
bool IsCargoProduced() const
Test if this industry produces any cargo.
Definition industry.h:216
ProducedCargoes::iterator GetCargoProduced(CargoID cargo)
Get produced cargo slot for a specific cargo type.
Definition industry.h:167
Data structure describing how to show the list (what sort direction and criteria).
Size related data of the map.
Definition map_func.h:206
Partial widget specification to allow NWidgets to be written nested.
TileIndex GetCenterTile() const
Get the center tile.
TileIndex tile
The base tile of the area.
Coordinates of a point in 2D.
Tindex index
Index of this pool item.
static size_t GetNumItems()
Returns number of valid items in the pool.
static bool IsValidID(size_t index)
Tests whether given index can be used to get valid (non-nullptr) Titem.
static Pool::IterateWrapper< Titem > Iterate(size_t from=0)
Returns an iterable ensemble of all valid Titem.
static Titem * Get(size_t index)
Returns Titem with given 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.
constexpr uint Horizontal() const
Get total horizontal padding of RectPadding.
constexpr uint Vertical() const
Get total vertical padding of RectPadding.
Specification of a rectangle with absolute coordinates of all edges.
Rect WithWidth(int width, bool end) const
Copy Rect and set its width.
Rect Shrink(int s) const
Copy and shrink Rect by s pixels.
Rect WithHeight(int height, bool end=false) const
Copy Rect and set its height.
Rect Indent(int indent, bool end) const
Copy Rect and indent it from its position.
Rect Translate(int x, int y) const
Copy and translate Rect by x,y pixels.
bool Contains(const Point &pt) const
Test if a point falls inside this Rect.
uint step_height
Step-size of height resize changes.
Definition window_gui.h:214
bool click_beep
Beep on a random selection of buttons.
String filter and state.
bool IsEmpty() const
Check whether any filter words were entered.
void SetFilterTerm(const char *str)
Set the term to filter on.
void ResetState()
Reset the matching state to process a new item.
void AddLine(const char *str)
Pass another text line from the current item to the filter.
bool GetState() const
Get the matching state of the current item.
char *const buf
buffer in which text is saved
Window * GetCallbackWnd()
Get the window that started the current highlighting.
High level window description.
Definition window_gui.h:159
Data structure for an opened window.
Definition window_gui.h:273
void ReInit(int rx=0, int ry=0, bool reposition=false)
Re-initialize a window, and optionally change its size.
Definition window.cpp:952
static int SortButtonWidth()
Get width of up/down arrow of sort button state.
Definition widget.cpp:781
void FinishInitNested(WindowNumber window_number=0)
Perform the second part of the initialization of a nested widget tree.
Definition window.cpp:1733
std::map< WidgetID, QueryString * > querystrings
QueryString associated to WWT_EDITBOX widgets.
Definition window_gui.h:320
void DrawWidgets() const
Paint all widgets of a window.
Definition widget.cpp:732
void InvalidateData(int data=0, bool gui_scope=true)
Mark this window's data as invalid (in need of re-computing)
Definition window.cpp:3159
void SetWidgetDirty(WidgetID widget_index) const
Invalidate a widget, i.e.
Definition window.cpp:551
void RaiseWidgetWhenLowered(WidgetID widget_index)
Marks a widget as raised and dirty (redraw), when it is marked as lowered.
Definition window_gui.h:484
void DrawSortButtonState(WidgetID widget, SortButtonState state) const
Draw a sort button's up or down arrow symbol.
Definition widget.cpp:764
ResizeInfo resize
Resize information.
Definition window_gui.h:314
void DisableWidget(WidgetID widget_index)
Sets a widget to disabled.
Definition window_gui.h:397
void CreateNestedTree()
Perform the first part of the initialization of a nested widget tree.
Definition window.cpp:1723
ViewportData * viewport
Pointer to viewport data, if present.
Definition window_gui.h:318
bool SetFocusedWidget(WidgetID widget_index)
Set focus within this window to the given widget.
Definition window.cpp:486
bool IsWidgetLowered(WidgetID widget_index) const
Gets the lowered state of a widget.
Definition window_gui.h:497
void RaiseButtons(bool autoraise=false)
Raise the buttons of the window.
Definition window.cpp:525
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:361
int top
y position of top edge of the window
Definition window_gui.h:310
const NWID * GetWidget(WidgetID widnum) const
Get the nested widget with number widnum from the nested widget tree.
Definition window_gui.h:977
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:590
void InitNested(WindowNumber number=0)
Perform complete initialization of the Window with nested widgets, to allow use.
Definition window.cpp:1746
WindowFlags flags
Window flags.
Definition window_gui.h:300
const Scrollbar * GetScrollbar(WidgetID widnum) const
Return the Scrollbar to a widget index.
Definition window.cpp:314
void SetWidgetDisabledState(WidgetID widget_index, bool disab_stat)
Sets the enabled/disabled status of a widget.
Definition window_gui.h:387
AllWindows< false > Iterate
Iterate all windows in whatever order is easiest.
Definition window_gui.h:927
int height
Height of the window (number of pixels down in y direction)
Definition window_gui.h:312
int width
width of the window (number of pixels to the right in x direction)
Definition window_gui.h:311
void ToggleWidgetLoweredState(WidgetID widget_index)
Invert the lowered/raised status of a widget.
Definition window_gui.h:456
WindowNumber window_number
Window number within the window class.
Definition window_gui.h:302
Stuff related to the text buffer GUI.
static debug_inline bool IsTileType(Tile tile, TileType type)
Checks if a tile is a given tiletype.
Definition tile_map.h:150
static const uint TILE_SIZE
Tile size in world coordinates.
Definition tile_type.h:15
constexpr TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition tile_type.h:95
@ MP_CLEAR
A tile without any structures, i.e. grass, rocks, farm fields etc.
Definition tile_type.h:48
Functions related to tile highlights.
void ResetObjectToPlace()
Reset the cursor and mouse mode handling back to default (normal cursor, only clicking in windows).
@ HT_RECT
rectangle (stations, depots, ...)
Definition of Interval and OneShot timers.
Definition of the Window system.
Base of the town class.
bool ScrollWindowToTile(TileIndex tile, Window *w, bool instant)
Scrolls the viewport in a window to a given location.
bool ScrollMainWindowToTile(TileIndex tile, bool instant)
Scrolls the viewport of the main window to a given location.
Functions related to (drawing on) viewports.
@ ZOOM_IN
Zoom in (get more detailed view).
@ ZOOM_OUT
Zoom out (get helicopter view).
Rect ScrollRect(Rect r, const Scrollbar &sb, int resize_step)
Apply 'scroll' to a rect to be drawn in.
Definition widget.cpp:2468
static RectPadding ScaleGUITrad(const RectPadding &r)
Scale a RectPadding to GUI zoom level.
Definition widget.cpp:35
@ SZSP_HORIZONTAL
Display plane with zero size vertically, and filling and resizing horizontally.
@ 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:51
@ WWT_PUSHIMGBTN
Normal push-button (no toggle button) with image caption.
@ WWT_EDITBOX
a textbox for typing
Definition widget_type.h:71
@ NWID_HORIZONTAL
Horizontal container.
Definition widget_type.h:75
@ WWT_TEXTBTN
(Toggle) Button with text
Definition widget_type.h:55
@ WWT_PANEL
Simple depressed panel.
Definition widget_type.h:50
@ WWT_STICKYBOX
Sticky box (at top-right of a window, after WWT_DEFSIZEBOX)
Definition widget_type.h:66
@ WWT_MATRIX
Grid of rows and columns.
Definition widget_type.h:59
@ WWT_SHADEBOX
Shade box (at top-right of a window, between WWT_DEBUGBOX and WWT_DEFSIZEBOX)
Definition widget_type.h:64
@ WWT_CAPTION
Window caption (window title between closebox and stickybox)
Definition widget_type.h:61
@ NWID_VSCROLLBAR
Vertical scrollbar.
Definition widget_type.h:85
@ NWID_VERTICAL
Vertical container.
Definition widget_type.h:77
@ WWT_CLOSEBOX
Close box (at top-left of a window)
Definition widget_type.h:69
@ NWID_HSCROLLBAR
Horizontal scrollbar.
Definition widget_type.h:84
@ WWT_RESIZEBOX
Resize box (normally at bottom-right of a window)
Definition widget_type.h:68
@ WWT_DEFSIZEBOX
Default window size box (at top-right of a window, between WWT_SHADEBOX and WWT_STICKYBOX)
Definition widget_type.h:65
@ NWID_VIEWPORT
Nested widget containing a viewport.
Definition widget_type.h:82
@ WWT_DROPDOWN
Drop down list.
Definition widget_type.h:70
@ WWT_DEBUGBOX
NewGRF debug box (at top-right of a window, between WWT_CAPTION and WWT_SHADEBOX)
Definition widget_type.h:63
@ NWID_SELECTION
Stacked widgets, only one visible at a time (eg in a panel with tabs).
Definition widget_type.h:80
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:1140
void SetFocusedWindow(Window *w)
Set the window that has the focus.
Definition window.cpp:422
Window * FindWindowByClass(WindowClass cls)
Find any window by its class.
Definition window.cpp:1113
Window * BringWindowToFrontById(WindowClass cls, WindowNumber number)
Find a window and make it the relative top-window on the screen.
Definition window.cpp:1223
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:3236
Window functions not directly related to making/drawing windows.
@ SBS_DOWN
Sort ascending.
Definition window_gui.h:220
@ SBS_UP
Sort descending.
Definition window_gui.h:221
@ WF_DISABLE_VP_SCROLL
Window does not do autoscroll,.
Definition window_gui.h:235
@ WDF_CONSTRUCTION
This window is used for construction; close it whenever changing company.
Definition window_gui.h:203
@ WDP_AUTO
Find a place automatically.
Definition window_gui.h:147
int WidgetID
Widget ID.
Definition window_type.h:18
int32_t WindowNumber
Number to differentiate different windows of the same class.
EventState
State of handling an event.
@ ES_HANDLED
The passed event is handled.
@ ES_NOT_HANDLED
The passed event is not handled.
@ WC_INDUSTRY_DIRECTORY
Industry directory; Window numbers:
@ WC_NONE
No window, redirects to WC_MAIN_WINDOW.
Definition window_type.h:45
@ WC_INDUSTRY_CARGOES
Industry cargoes chain; Window numbers:
@ WC_INDUSTRY_PRODUCTION
Industry production history graph; Window numbers:
@ WC_INDUSTRY_VIEW
Industry view; Window numbers:
@ WC_SMALLMAP
Small map; Window numbers:
@ WC_BUILD_INDUSTRY
Build industry; Window numbers:
Functions related to zooming.
ZoomLevel ScaleZoomGUI(ZoomLevel value)
Scale zoom level relative to GUI zoom.
Definition zoom_func.h:87
@ ZOOM_LVL_INDUSTRY
Default zoom level for the industry view.
Definition zoom_type.h:30