OpenTTD Source  20241120-master-g6d3adc6169
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"
33 #include "core/geometry_func.hpp"
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 
57 bool _ignore_restrictions;
58 std::bitset<NUM_INDUSTRYTYPES> _displayed_industries;
59 
65 };
66 
73 };
74 
76 struct CargoSuffix {
78  std::string text;
79 };
80 
81 extern void GenerateIndustries();
82 static void ShowIndustryCargoesWindow(IndustryType id);
83 
93 static void GetCargoSuffix(uint cargo, CargoSuffixType cst, const Industry *ind, IndustryType ind_type, const IndustrySpec *indspec, CargoSuffix &suffix)
94 {
95  suffix.text.clear();
96  suffix.display = CSD_CARGO_AMOUNT;
97 
98  if (HasBit(indspec->callback_mask, CBM_IND_CARGO_SUFFIX)) {
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.grffile->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.grffile->grfid, 0xD000 + callback));
126  return;
127  }
128  if (callback >= 0x800 && callback < 0xC00) {
130  suffix.text = GetString(GetGRFStringID(indspec->grf_prop.grffile->grfid, 0xD000 - 0x800 + callback));
132  suffix.display = CSD_CARGO_TEXT;
133  return;
134  }
136  return;
137  }
138  }
139 }
140 
141 enum CargoSuffixInOut {
142  CARGOSUFFIX_OUT = 0,
143  CARGOSUFFIX_IN = 1,
144 };
145 
156 template <typename TC, typename TS>
157 static 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 
208 void 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 
224 std::array<IndustryType, NUM_INDUSTRYTYPES> _sorted_industry_types;
225 
227 static 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++) {
242  _sorted_industry_types[i] = i;
243  }
244 
245  /* Sort industry types by name. */
247 }
248 
255 void 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 
268 static 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),
280  NWidget(WWT_TEXTBTN, COLOUR_DARK_GREEN, WID_DPI_REMOVE_ALL_INDUSTRIES_WIDGET), SetMinimalSize(0, 12), SetFill(1, 0), SetResize(1, 0),
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 
307 class BuildIndustryWindow : public Window {
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) {
331  const IndustrySpec *indsp = GetIndustrySpec(ind);
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 
355  void SetButtons()
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);
390  AppendStringInPlace(cargostring, STR_INDUSTRY_VIEW_CARGO_LIST_EXTENSION);
391  }
392 
393  if (numcargo > 0) {
394  SetDParam(0, CargoSpec::Get(cargolist[firstcargo])->name);
395  SetDParamStr(1, cargo_suffix[firstcargo].text);
396  cargostring = GetString(prefixstr) + cargostring;
397  } else {
398  SetDParam(0, STR_JUST_NOTHING);
399  SetDParamStr(1, "");
400  cargostring = GetString(prefixstr);
401  }
402 
403  return cargostring;
404  }
405 
406 public:
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) {
415  this->GetWidget<NWidgetStacked>(WID_DPI_SCENARIO_EDITOR_PANE)->SetDisplayedPlane(SZSP_HORIZONTAL);
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 
431  void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
432  {
433  switch (widget) {
434  case WID_DPI_MATRIX_WIDGET: {
435  SetDParamMaxDigits(0, 4);
436  Dimension count = GetStringBoundingBox(STR_JUST_COMMA, FS_SMALL);
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);
462  std::string cargostring = this->MakeCargoListString(indsp->accepts_cargo, cargo_suffix, STR_INDUSTRY_VIEW_REQUIRES_N_CARGO);
463  Dimension strdim = GetStringBoundingBox(cargostring);
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);
472  cargostring = this->MakeCargoListString(indsp->produced_cargo, cargo_suffix, STR_INDUSTRY_VIEW_PRODUCES_N_CARGO);
473  strdim = GetStringBoundingBox(cargostring);
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.grffile != nullptr) {
481  /* Reserve a few extra lines for text from an industry NewGRF. */
482  extra_lines_newgrf = 4;
483  }
484  }
485 
486  /* Set it to something more sane :) */
487  height += extra_lines_prd + extra_lines_req + extra_lines_newgrf;
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: {
494  Dimension d = GetStringBoundingBox(STR_FUND_INDUSTRY_BUILD_NEW_INDUSTRY);
495  d = maxdim(d, GetStringBoundingBox(STR_FUND_INDUSTRY_PROSPECT_NEW_INDUSTRY));
496  d = maxdim(d, GetStringBoundingBox(STR_FUND_INDUSTRY_FUND_NEW_INDUSTRY));
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) {
508  case WID_DPI_FUND_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 */
513  SetDParam(0, STR_FUND_INDUSTRY_BUILD_NEW_INDUSTRY);
514  } else {
515  if (this->selected_type != INVALID_INDUSTRYTYPE) {
516  const IndustrySpec *indsp = GetIndustrySpec(this->selected_type);
517  SetDParam(0, (_settings_game.construction.raw_industry_construction == 2 && indsp->IsRawIndustry()) ? STR_FUND_INDUSTRY_PROSPECT_NEW_INDUSTRY : STR_FUND_INDUSTRY_FUND_NEW_INDUSTRY);
518  } else {
519  SetDParam(0, STR_FUND_INDUSTRY_FUND_NEW_INDUSTRY);
520  }
521  }
522  break;
523  }
524  }
525 
526  void DrawWidget(const Rect &r, WidgetID widget) const override
527  {
528  switch (widget) {
529  case WID_DPI_MATRIX_WIDGET: {
530  bool rtl = _current_text_dir == TD_RTL;
531  Rect text = r.WithHeight(this->resize.step_height).Shrink(WidgetDimensions::scaled.matrix);
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) {
562  DrawStringMultiLine(ir, STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_TOOLTIP);
563  break;
564  }
565 
566  const IndustrySpec *indsp = GetIndustrySpec(this->selected_type);
567 
568  if (_game_mode != GM_EDITOR) {
569  SetDParam(0, indsp->GetConstructionCost());
570  DrawString(ir, STR_FUND_INDUSTRY_INDUSTRY_BUILD_COST);
571  ir.top += GetCharacterHeight(FS_NORMAL);
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);
578  std::string cargostring = this->MakeCargoListString(indsp->accepts_cargo, cargo_suffix, STR_INDUSTRY_VIEW_REQUIRES_N_CARGO);
579  ir.top = DrawStringMultiLine(ir, cargostring);
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);
583  cargostring = this->MakeCargoListString(indsp->produced_cargo, cargo_suffix, STR_INDUSTRY_VIEW_PRODUCES_N_CARGO);
584  ir.top = DrawStringMultiLine(ir, cargostring);
585 
586  /* Get the additional purchase info text, if it has not already been queried. */
588  uint16_t callback_res = GetIndustryCallback(CBID_INDUSTRY_FUND_MORE_TEXT, 0, 0, nullptr, this->selected_type, INVALID_TILE);
589  if (callback_res != CALLBACK_FAILED && callback_res != 0x400) {
590  if (callback_res > 0x400) {
592  } else {
593  StringID str = GetGRFStringID(indsp->grf_prop.grffile->grfid, 0xD000 + callback_res); // No. here's the new string
594  if (str != STR_UNDEFINED) {
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) {
612  ShowErrorMessage(STR_ERROR_CAN_T_GENERATE_INDUSTRIES, STR_ERROR_MUST_FOUND_TOWN_FIRST, WL_INFO);
613  } else {
614  Backup<bool> old_generating_world(_generating_world, true);
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 (TileIndex tile = 0; tile < Map::Size(); tile++) {
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);
644  ShowQuery(STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_CAPTION, STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_QUERY, nullptr, AskManyRandomIndustriesCallback);
645  break;
646  }
647 
649  assert(_game_mode == GM_EDITOR);
651  ShowQuery(STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES_CAPTION, STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES_QUERY, nullptr, AskRemoveAllIndustriesCallback);
652  break;
653  }
654 
655  case WID_DPI_MATRIX_WIDGET: {
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);
714  ShowErrorMessage(STR_ERROR_CAN_T_BUILD_HERE, STR_ERROR_MUST_FOUND_TOWN_FIRST, WL_INFO, pt.x, pt.y);
715  return;
716  }
717 
719  Backup<bool> old_generating_world(_generating_world, true);
720  _ignore_restrictions = true;
721 
722  Command<CMD_BUILD_INDUSTRY>::Post(STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY, &CcBuildIndustry, tile, this->selected_type, layout_index, false, seed);
723 
724  cur_company.Restore();
725  old_generating_world.Restore();
726  _ignore_restrictions = false;
727  } else {
728  success = Command<CMD_BUILD_INDUSTRY>::Post(STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY, tile, this->selected_type, layout_index, false, seed);
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 
771 void ShowBuildIndustryWindow()
772 {
773  if (_game_mode != GM_EDITOR && !Company::IsValidID(_local_company)) return;
775  new BuildIndustryWindow();
776 }
777 
778 static void UpdateIndustryProduction(Industry *i);
779 
780 static 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 {
792  enum Editability {
796  };
797 
799  enum InfoLine {
804  };
805 
810  uint8_t clicked_button;
814 
815 public:
817  {
818  this->flags |= WF_DISABLE_VP_SCROLL;
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);
825  NWidgetViewport *nvp = this->GetWidget<NWidgetViewport>(WID_IV_VIEWPORT);
826  nvp->InitializeViewport(this, Industry::Get(window_number)->location.GetCenterTile(), ScaleZoomGUI(ZOOM_LVL_INDUSTRY));
827 
829  if (!i->IsCargoProduced()) this->DisableWidget(WID_IV_GRAPH);
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;
864  SpriteID icon = CargoSpec::Get(cid)->GetCargoIcon();
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;
879  const IndustrySpec *ind = GetIndustrySpec(i->type);
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) {
885  DrawString(ir, STR_INDUSTRY_VIEW_INDUSTRY_ANNOUNCED_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) {
896  DrawString(ir, STR_INDUSTRY_VIEW_REQUIRES);
897  ir.top += GetCharacterHeight(FS_NORMAL);
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:
916  str = stockpiling ? STR_INDUSTRY_VIEW_ACCEPT_CARGO_AMOUNT : STR_INDUSTRY_VIEW_ACCEPT_CARGO;
917  break;
918 
919  case CSD_CARGO_TEXT:
920  SetDParamStr(3, suffix.text);
921  [[fallthrough]];
922  case CSD_CARGO:
923  str = STR_INDUSTRY_VIEW_ACCEPT_CARGO;
924  break;
925 
926  default:
927  NOT_REACHED();
928  }
929  DrawString(ir.Indent(label_indent, rtl), str);
930  ir.top += GetCharacterHeight(FS_NORMAL);
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) {
940  if (has_accept) ir.top += WidgetDimensions::scaled.vsep_wide;
941  DrawString(ir, TimerGameEconomy::UsingWallclockUnits() ? STR_INDUSTRY_VIEW_PRODUCTION_LAST_MINUTE_TITLE : STR_INDUSTRY_VIEW_PRODUCTION_LAST_MONTH_TITLE);
942  ir.top += GetCharacterHeight(FS_NORMAL);
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()));
956  DrawString(ir.Indent(label_indent + (this->editable == EA_RATE ? SETTING_BUTTON_WIDTH + WidgetDimensions::scaled.hsep_normal : 0), rtl).Translate(0, text_y_offset), STR_INDUSTRY_VIEW_TRANSPORTED);
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;
973  DrawString(ir.Indent(label_indent + SETTING_BUTTON_WIDTH + WidgetDimensions::scaled.hsep_normal, rtl).Translate(0, text_y_offset), STR_INDUSTRY_VIEW_PRODUCTION_LEVEL);
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 */
981  uint16_t callback_res = GetIndustryCallback(CBID_INDUSTRY_WINDOW_MORE_TEXT, 0, 0, i, i->type, i->location.tile);
982  if (callback_res != CALLBACK_FAILED && callback_res != 0x400) {
983  if (callback_res > 0x400) {
985  } else {
986  StringID message = GetGRFStringID(ind->grf_prop.grffile->grfid, 0xD000 + callback_res);
987  if (message != STR_NULL && message != STR_UNDEFINED) {
989 
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 
1016  void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
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: {
1025  Industry *i = Industry::Get(this->window_number);
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 
1054  if (r.WithWidth(SETTING_BUTTON_WIDTH, rtl).Contains(pt)) {
1055  /* Clicked buttons, decrease or increase production */
1056  bool decrease = r.WithWidth(SETTING_BUTTON_WIDTH / 2, rtl).Contains(pt);
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:
1094  ShowQueryString(STR_JUST_INT, STR_CONFIG_GAME_PRODUCTION_LEVEL, 10, this, CS_ALPHANUMERAL, QSF_NONE);
1095  break;
1096 
1097  case EA_RATE:
1098  SetDParam(0, i->produced[line - IL_RATE1].rate * 8);
1099  ShowQueryString(STR_JUST_INT, STR_CONFIG_GAME_PRODUCTION, 10, this, CS_ALPHANUMERAL, QSF_NONE);
1100  break;
1101 
1102  default: NOT_REACHED();
1103  }
1104  }
1105  break;
1106  }
1107 
1108  case WID_IV_GOTO: {
1109  Industry *i = Industry::Get(this->window_number);
1110  if (_ctrl_pressed) {
1112  } else {
1114  }
1115  break;
1116  }
1117 
1118  case WID_IV_DISPLAY: {
1119  Industry *i = Industry::Get(this->window_number);
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) {
1140  NWidgetViewport *nvp = this->GetWidget<NWidgetViewport>(WID_IV_VIEWPORT);
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 
1158  Industry *i = Industry::Get(this->window_number);
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 
1203 static 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 
1248 void 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(),
1277  NWidget(NWID_VSCROLLBAR, COLOUR_BROWN, WID_ID_VSCROLLBAR),
1278  EndContainer(),
1280  NWidget(NWID_HSCROLLBAR, COLOUR_BROWN, WID_ID_HSCROLLBAR),
1281  NWidget(WWT_RESIZEBOX, COLOUR_BROWN),
1282  EndContainer(),
1283 };
1284 
1286 
1294 static 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 
1334 static GUIIndustryList::FilterFunction * const _industry_filter_funcs[] = { &CargoFilter };
1335 
1339 };
1344 protected:
1345  /* Runtime saved values */
1346  static Listing last_sorting;
1347 
1348  /* Constants for sorting industries */
1349  static inline const StringID sorter_names[] = {
1350  STR_SORT_BY_NAME,
1351  STR_SORT_BY_TYPE,
1352  STR_SORT_BY_PRODUCTION,
1353  STR_SORT_BY_TRANSPORTED,
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,
1373  ByTransported,
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) {
1413  case CargoFilterCriteria::CF_ANY: return STR_INDUSTRY_DIRECTORY_FILTER_ALL_TYPES;
1414  case CargoFilterCriteria::CF_NONE: return STR_INDUSTRY_DIRECTORY_FILTER_NONE;
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 = 0;
1441  for (const Industry *i : this->industries) {
1442  width = std::max(width, GetStringBoundingBox(this->GetIndustryString(i)).width);
1443  }
1445  }
1446 
1449  {
1450  if (this->industries.NeedRebuild()) {
1451  this->industries.clear();
1452  this->industries.reserve(Industry::GetNumItems());
1453 
1454  for (const Industry *i : Industry::Iterate()) {
1455  if (this->string_filter.IsEmpty()) {
1456  this->industries.push_back(i);
1457  continue;
1458  }
1459  this->string_filter.ResetState();
1460  this->string_filter.AddLine(i->GetCachedName());
1461  if (this->string_filter.GetState()) this->industries.push_back(i);
1462  }
1463 
1464  this->industries.RebuildDone();
1465 
1466  auto filter = std::make_pair(this->accepted_cargo_filter_criteria, this->produced_cargo_filter_criteria);
1467 
1468  this->industries.Filter(filter);
1469 
1470  this->hscroll->SetCount(this->GetIndustryListWidth());
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) {
1509  produced_cargo_count++;
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;
1521  return percentage / produced_cargo_count;
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;
1536  while (it_a != NUM_INDUSTRYTYPES && a->type != _sorted_industry_types[it_a]) it_a++;
1537  int it_b = 0;
1538  while (it_b != NUM_INDUSTRYTYPES && b->type != _sorted_industry_types[it_b]) it_b++;
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  {
1579  const IndustrySpec *indsp = GetIndustrySpec(i->type);
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 {
1587  CargoID cargo_id;
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;
1598  CargoSuffix cargo_suffix;
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 */
1625  const CargoID cid = this->produced_cargo_filter_criteria;
1627  auto filtered_ci = std::find_if(cargos.begin(), cargos.end(), [cid](const CargoInfo &ci) -> bool {
1628  return ci.cargo_id == cid;
1629  });
1630  if (filtered_ci != cargos.end()) {
1631  std::rotate(cargos.begin(), filtered_ci, filtered_ci + 1);
1632  }
1633  }
1634 
1635  /* Display first 3 cargos */
1636  for (size_t j = 0; j < std::min<size_t>(3, cargos.size()); j++) {
1637  CargoInfo &ci = cargos[j];
1638  SetDParam(p++, STR_INDUSTRY_DIRECTORY_ITEM_INFO);
1639  SetDParam(p++, ci.cargo_id);
1640  SetDParam(p++, ci.production);
1641  SetDParamStr(p++, std::move(ci.suffix));
1642  SetDParam(p++, ci.transported);
1643  }
1644 
1645  /* Undisplayed cargos if any */
1646  SetDParam(p++, cargos.size() - 3);
1647 
1648  /* Drawing the right string */
1649  switch (cargos.size()) {
1650  case 0: return STR_INDUSTRY_DIRECTORY_ITEM_NOPROD;
1651  case 1: return STR_INDUSTRY_DIRECTORY_ITEM_PROD1;
1652  case 2: return STR_INDUSTRY_DIRECTORY_ITEM_PROD2;
1653  case 3: return STR_INDUSTRY_DIRECTORY_ITEM_PROD3;
1654  default: return STR_INDUSTRY_DIRECTORY_ITEM_PRODMORE;
1655  }
1656  }
1657 
1658 public:
1660  {
1661  this->CreateNestedTree();
1662  this->vscroll = this->GetScrollbar(WID_ID_VSCROLLBAR);
1663  this->hscroll = this->GetScrollbar(WID_ID_HSCROLLBAR);
1664 
1665  this->industries.SetListing(this->last_sorting);
1666  this->industries.SetSortFuncs(IndustryDirectoryWindow::sorter_funcs);
1667  this->industries.ForceRebuild();
1668 
1669  this->FinishInitNested(0);
1670 
1671  this->BuildSortIndustriesList();
1672 
1674  this->industry_editbox.cancel_button = QueryString::ACTION_CLEAR;
1675  }
1676 
1678  {
1679  this->last_sorting = this->industries.GetListing();
1680  }
1681 
1682  void OnInit() override
1683  {
1684  this->SetCargoFilterArray();
1685  }
1686 
1687  void SetStringParameters(WidgetID widget) const override
1688  {
1689  switch (widget) {
1690  case WID_ID_CAPTION:
1691  SetDParam(0, this->vscroll->GetCount());
1693  break;
1694 
1696  SetDParam(0, IndustryDirectoryWindow::sorter_names[this->industries.SortType()]);
1697  break;
1698 
1700  SetDParam(0, this->GetCargoFilterLabel(this->accepted_cargo_filter_criteria));
1701  break;
1702 
1704  SetDParam(0, this->GetCargoFilterLabel(this->produced_cargo_filter_criteria));
1705  break;
1706  }
1707  }
1708 
1709  void DrawWidget(const Rect &r, WidgetID widget) const override
1710  {
1711  switch (widget) {
1712  case WID_ID_DROPDOWN_ORDER:
1713  this->DrawSortButtonState(widget, this->industries.IsDescSortOrder() ? SBS_DOWN : SBS_UP);
1714  break;
1715 
1716  case WID_ID_INDUSTRY_LIST: {
1717  Rect ir = r.Shrink(WidgetDimensions::scaled.framerect);
1718 
1719  /* Setup a clipping rectangle... */
1720  DrawPixelInfo tmp_dpi;
1721  if (!FillDrawPixelInfo(&tmp_dpi, ir)) return;
1722  /* ...but keep coordinates relative to the window. */
1723  tmp_dpi.left += ir.left;
1724  tmp_dpi.top += ir.top;
1725 
1726  AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi);
1727 
1728  ir = ScrollRect(ir, *this->hscroll, 1);
1729 
1730  if (this->industries.empty()) {
1731  DrawString(ir, STR_INDUSTRY_DIRECTORY_NONE);
1732  break;
1733  }
1734  const CargoID acf_cid = this->accepted_cargo_filter_criteria;
1735  auto [first, last] = this->vscroll->GetVisibleRangeIterators(this->industries);
1736  for (auto it = first; it != last; ++it) {
1737  TextColour tc = TC_FROMSTRING;
1738  if (acf_cid != CargoFilterCriteria::CF_ANY && acf_cid != CargoFilterCriteria::CF_NONE) {
1739  Industry *ind = const_cast<Industry *>(*it);
1740  if (IndustryTemporarilyRefusesCargo(ind, acf_cid)) {
1741  tc = TC_GREY | TC_FORCED;
1742  }
1743  }
1744  DrawString(ir, this->GetIndustryString(*it), tc);
1745 
1746  ir.top += this->resize.step_height;
1747  }
1748  break;
1749  }
1750  }
1751  }
1752 
1753  void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
1754  {
1755  switch (widget) {
1756  case WID_ID_DROPDOWN_ORDER: {
1757  Dimension d = GetStringBoundingBox(this->GetWidget<NWidgetCore>(widget)->widget_data);
1758  d.width += padding.width + Window::SortButtonWidth() * 2; // Doubled since the string is centred and it also looks better.
1759  d.height += padding.height;
1760  size = maxdim(size, d);
1761  break;
1762  }
1763 
1764  case WID_ID_DROPDOWN_CRITERIA: {
1765  Dimension d = GetStringListBoundingBox(IndustryDirectoryWindow::sorter_names);
1766  d.width += padding.width;
1767  d.height += padding.height;
1768  size = maxdim(size, d);
1769  break;
1770  }
1771 
1772  case WID_ID_INDUSTRY_LIST: {
1773  Dimension d = GetStringBoundingBox(STR_INDUSTRY_DIRECTORY_NONE);
1774  resize.height = d.height;
1775  d.height *= 5;
1776  d.width += padding.width;
1777  d.height += padding.height;
1778  size = maxdim(size, d);
1779  break;
1780  }
1781  }
1782  }
1783 
1784  DropDownList BuildCargoDropDownList() const
1785  {
1786  DropDownList list;
1787 
1788  /* Add item for disabling filtering. */
1789  list.push_back(MakeDropDownListStringItem(this->GetCargoFilterLabel(CargoFilterCriteria::CF_ANY), CargoFilterCriteria::CF_ANY));
1790  /* Add item for industries not producing anything, e.g. power plants */
1791  list.push_back(MakeDropDownListStringItem(this->GetCargoFilterLabel(CargoFilterCriteria::CF_NONE), CargoFilterCriteria::CF_NONE));
1792 
1793  /* Add cargos */
1795  for (const CargoSpec *cs : _sorted_standard_cargo_specs) {
1796  list.push_back(MakeDropDownListIconItem(d, cs->GetCargoIcon(), PAL_NONE, cs->name, cs->Index()));
1797  }
1798 
1799  return list;
1800  }
1801 
1802  void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
1803  {
1804  switch (widget) {
1805  case WID_ID_DROPDOWN_ORDER:
1806  this->industries.ToggleSortOrder();
1807  this->SetDirty();
1808  break;
1809 
1811  ShowDropDownMenu(this, IndustryDirectoryWindow::sorter_names, this->industries.SortType(), WID_ID_DROPDOWN_CRITERIA, 0, 0);
1812  break;
1813 
1814  case WID_ID_FILTER_BY_ACC_CARGO: // Cargo filter dropdown
1815  ShowDropDownList(this, this->BuildCargoDropDownList(), this->accepted_cargo_filter_criteria, widget);
1816  break;
1817 
1818  case WID_ID_FILTER_BY_PROD_CARGO: // Cargo filter dropdown
1819  ShowDropDownList(this, this->BuildCargoDropDownList(), this->produced_cargo_filter_criteria, widget);
1820  break;
1821 
1822  case WID_ID_INDUSTRY_LIST: {
1823  auto it = this->vscroll->GetScrolledItemFromWidget(this->industries, pt.y, this, WID_ID_INDUSTRY_LIST, WidgetDimensions::scaled.framerect.top);
1824  if (it != this->industries.end()) {
1825  if (_ctrl_pressed) {
1826  ShowExtraViewportWindow((*it)->location.tile);
1827  } else {
1828  ScrollMainWindowToTile((*it)->location.tile);
1829  }
1830  }
1831  break;
1832  }
1833  }
1834  }
1835 
1836  void OnDropdownSelect(WidgetID widget, int index) override
1837  {
1838  switch (widget) {
1839  case WID_ID_DROPDOWN_CRITERIA: {
1840  if (this->industries.SortType() != index) {
1841  this->industries.SetSortType(index);
1842  this->BuildSortIndustriesList();
1843  }
1844  break;
1845  }
1846 
1848  this->SetAcceptedCargoFilter(index);
1849  this->BuildSortIndustriesList();
1850  break;
1851  }
1852 
1854  this->SetProducedCargoFilter(index);
1855  this->BuildSortIndustriesList();
1856  break;
1857  }
1858  }
1859  }
1860 
1861  void OnResize() override
1862  {
1863  this->vscroll->SetCapacityFromWidget(this, WID_ID_INDUSTRY_LIST, WidgetDimensions::scaled.framerect.Vertical());
1864  this->hscroll->SetCapacityFromWidget(this, WID_ID_INDUSTRY_LIST, WidgetDimensions::scaled.framerect.Horizontal());
1865  }
1866 
1867  void OnEditboxChanged(WidgetID wid) override
1868  {
1869  if (wid == WID_ID_FILTER) {
1870  this->string_filter.SetFilterTerm(this->industry_editbox.text.buf);
1871  this->InvalidateData(IDIWD_FORCE_REBUILD);
1872  }
1873  }
1874 
1875  void OnPaint() override
1876  {
1877  if (this->industries.NeedRebuild()) this->BuildSortIndustriesList();
1878  this->DrawWidgets();
1879  }
1880 
1882  IntervalTimer<TimerWindow> rebuild_interval = {std::chrono::seconds(3), [this](auto) {
1883  this->industries.ForceResort();
1884  this->BuildSortIndustriesList();
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 
1910  EventState OnHotkey(int hotkey) override
1911  {
1912  switch (hotkey) {
1913  case IDHK_FOCUS_FILTER_BOX:
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 
1928 Listing IndustryDirectoryWindow::last_sorting = {false, 0};
1929 
1930 /* Available station sorting functions. */
1931 const std::initializer_list<GUIIndustryList::SortFunction * const> IndustryDirectoryWindow::sorter_funcs = {
1932  &IndustryNameSorter,
1933  &IndustryTypeSorter,
1934  &IndustryProductionSorter,
1935  &IndustryTransportedCargoSorter
1936 };
1937 
1938 CargoID IndustryDirectoryWindow::produced_cargo_filter = CargoFilterCriteria::CF_ANY;
1939 
1940 
1943  WDP_AUTO, "list_industries", 428, 190,
1945  0,
1947  &IndustryDirectoryWindow::hotkeys
1948 );
1949 
1950 void 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(),
1966  NWidget(NWID_VSCROLLBAR, COLOUR_BROWN, WID_IC_SCROLLBAR),
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 
1996 };
1997 
1998 static 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 
2015  static int cargo_field_width;
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 {
2031  Cargoes supp_cargoes;
2032  Cargoes cust_cargoes;
2033  uint8_t num_cargoes;
2034  uint8_t top_end;
2035  uint8_t bottom_end;
2036  } cargo;
2037  struct {
2039  bool left_align;
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 
2146  void MakeHeader(StringID textid)
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);
2187  ypos += (normal_height - GetCharacterHeight(FS_NORMAL)) / 2;
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 
2375  int vpos = vert_inter_industry_space / 2 + CargoesField::cargo_border.height;
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 
2386 private:
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 
2402 static_assert(MAX_CARGOES >= std::tuple_size_v<decltype(IndustrySpec::produced_cargo)>);
2403 static_assert(MAX_CARGOES >= std::tuple_size_v<decltype(IndustrySpec::accepts_cargo)>);
2404 
2410 
2417 
2419 
2422 
2424 struct CargoesRow {
2426 
2431  void ConnectIndustryProduced(int column)
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 
2490  void ConnectIndustryAccepted(int column)
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  }
2526 next_cargo: ;
2527  }
2528  }
2529  }
2530 };
2531 
2532 
2561  typedef std::vector<CargoesRow> Fields;
2562 
2563  Fields fields;
2564  uint ind_cargo;
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. */
2581  Dimension d = GetStringBoundingBox(STR_INDUSTRY_CARGOES_PRODUCERS);
2582  d = maxdim(d, GetStringBoundingBox(STR_INDUSTRY_CARGOES_CUSTOMERS));
2585  CargoesField::small_height = d.height;
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::count_if(std::begin(indsp->accepts_cargo), std::end(indsp->accepts_cargo), IsValidCargoID));
2619  CargoesField::max_cargoes = std::max<uint>(CargoesField::max_cargoes, std::count_if(std::begin(indsp->produced_cargo), std::end(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 
2640  CargoesField::industry_width = d.width;
2642 
2643  /* Width of a #CFT_CARGO field. */
2645  }
2646 
2647  void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
2648  {
2649  switch (widget) {
2650  case WID_IC_PANEL:
2654  break;
2655 
2656  case WID_IC_IND_DROPDOWN:
2657  size.width = std::max(size.width, this->ind_textsize.width + padding.width);
2658  break;
2659 
2660  case WID_IC_CARGO_DROPDOWN:
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 
2828  void ComputeIndustryDisplay(IndustryType displayed_it)
2829  {
2830  this->GetWidget<NWidgetCore>(WID_IC_CAPTION)->widget_data = STR_INDUSTRY_CARGOES_INDUSTRY_CAPTION;
2831  this->ind_cargo = displayed_it;
2832  _displayed_industries.reset();
2833  _displayed_industries.set(displayed_it);
2834 
2835  this->fields.clear();
2836  CargoesRow &first_row = this->fields.emplace_back();
2837  first_row.columns[0].MakeHeader(STR_INDUSTRY_CARGOES_PRODUCERS);
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 
2843  const IndustrySpec *central_sp = GetIndustrySpec(displayed_it);
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. */
2847  int num_supp = CountMatchingProducingIndustries(central_sp->accepts_cargo) + houses_supply;
2848  int num_cust = CountMatchingAcceptingIndustries(central_sp->produced_cargo) + houses_accept;
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();
2852  row.columns[0].MakeEmpty(CFT_EMPTY);
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  {
2908  this->GetWidget<NWidgetCore>(WID_IC_CAPTION)->widget_data = STR_INDUSTRY_CARGOES_CARGO_CAPTION;
2909  this->ind_cargo = cid + NUM_INDUSTRYTYPES;
2910  _displayed_industries.reset();
2911 
2912  this->fields.clear();
2913  CargoesRow &first_row = this->fields.emplace_back();
2914  first_row.columns[0].MakeHeader(STR_INDUSTRY_CARGOES_PRODUCERS);
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.
2924  int num_cust = CountMatchingAcceptingIndustries(cargoes) + houses_accept;
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();
2928  row.columns[0].MakeEmpty(CFT_EMPTY);
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 
2993  Rect ir = r.Shrink(WidgetDimensions::scaled.bevel);
2994  DrawPixelInfo tmp_dpi;
2995  if (!FillDrawPixelInfo(&tmp_dpi, ir)) return;
2996  AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi);
2997 
2999  if (this->ind_cargo >= NUM_INDUSTRYTYPES) left_pos += (CargoesField::industry_width + CargoesField::cargo_field_width) / 2;
3000  int last_column = (this->ind_cargo < NUM_INDUSTRYTYPES) ? 4 : 2;
3001 
3002  const NWidgetBase *nwp = this->GetWidget<NWidgetBase>(WID_IC_PANEL);
3003  int vpos = WidgetDimensions::scaled.frametext.top - WidgetDimensions::scaled.bevel.top - this->vscroll->GetPosition() * nwp->resize_y;
3004  int row_height = CargoesField::small_height;
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;
3024  row_height = CargoesField::normal_height;
3025  }
3026  }
3027 
3035  bool CalculatePositionInWidget(Point pt, Point *fieldxy, Point *xy)
3036  {
3037  const NWidgetBase *nw = this->GetWidget<NWidgetBase>(WID_IC_PANEL);
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;
3066  xy->x = ((column & 1) ? CargoesField::cargo_field_width : CargoesField::industry_width) - xpos;
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:
3110  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
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: {
3119  DropDownList lst;
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: {
3132  DropDownList lst;
3133  for (IndustryType ind : _sorted_industry_types) {
3134  const IndustrySpec *indsp = GetIndustrySpec(ind);
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) {
3152  case WID_IC_CARGO_DROPDOWN:
3153  this->ComputeCargoDisplay(index);
3154  break;
3155 
3156  case WID_IC_IND_DROPDOWN:
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)) {
3186  GuiShowTooltips(this, STR_INDUSTRY_CARGOES_INDUSTRY_TOOLTIP, close_cond);
3187  }
3188  return true;
3189 
3190  default:
3191  break;
3192  }
3193  if (IsValidCargoID(cid) && (this->ind_cargo < NUM_INDUSTRYTYPES || cid != this->ind_cargo - NUM_INDUSTRYTYPES)) {
3194  const CargoSpec *csp = CargoSpec::Get(cid);
3195  SetDParam(0, csp->name);
3196  GuiShowTooltips(this, STR_INDUSTRY_CARGOES_CARGO_TOOLTIP, close_cond, 1);
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 
3213 static 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 
3236 {
3238 }
Class for backupping variables and making sure they are restored later.
constexpr debug_inline 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.
constexpr static debug_inline 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.
Definition: cargotype.cpp:156
std::span< const CargoSpec * > _sorted_standard_cargo_specs
Standard cargo specifications sorted alphabetically by name.
Definition: cargotype.cpp:184
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
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 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([[maybe_unused]] int data=0, [[maybe_unused]] 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.
std::vector< IndustryType > list
List of industries.
bool enabled
Availability state of the selected industry.
Common return value for all commands.
Definition: command_type.h:23
bool Succeeded() const
Did this command succeed?
Definition: command_type.h:162
StringID GetErrorMessage() const
Returns the error message of a command.
Definition: command_type.h:142
List template of 'things' T to sort in a GUI.
Definition: sortlist_type.h:47
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.
Definition: sortlist_type.h:50
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 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.
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.
static int GetCargoTransportedPercentsIfValid(const Industry::ProducedCargo &p)
Returns percents of cargo transported if industry produces this cargo, else -1.
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.
CargoID produced_cargo_filter_criteria
Selected produced cargo filter index.
void OnInvalidateData([[maybe_unused]] int data=0, [[maybe_unused]] 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 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 OnInvalidateData([[maybe_unused]] int data=0, [[maybe_unused]] 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.
bool IsNewGRFInspectable() const override
Is the data related to this window NewGRF inspectable?
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.
uint8_t clicked_button
The button that has been clicked (to raise)
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.
Baseclass for nested widgets.
Definition: widget_type.h:144
int pos_y
Vertical position of top-left corner of the widget in the window.
Definition: widget_type.h:249
int pos_x
Horizontal position of top-left corner of the widget in the window.
Definition: widget_type.h:248
uint resize_y
Vertical resize step (0 means not resizable).
Definition: widget_type.h:236
Nested widget to display a viewport in a window.
Definition: widget_type.h:680
void UpdateViewportCoordinates(Window *w)
Update the position and size of the viewport (after eg a resize).
Definition: widget.cpp:2297
void InitializeViewport(Window *w, std::variant< TileIndex, VehicleID > focus, ZoomLevel zoom)
Initialize the viewport of the window.
Definition: widget.cpp:2288
Scrollbar data structure.
Definition: widget_type.h:694
void SetCount(size_t num)
Sets the number of elements in the list.
Definition: widget_type.h:780
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.
Definition: widget_type.h:879
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:2394
size_type GetCount() const
Gets the number of elements in the list.
Definition: widget_type.h:722
auto GetVisibleRangeIterators(Tcontainer &container) const
Get a pair of iterators for the range of visible elements in a container.
Definition: widget_type.h:860
size_type GetPosition() const
Gets the position of the first visible element in the list.
Definition: widget_type.h:740
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:68
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:67
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: command_type.h:187
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.
Definition: company_cmd.cpp:52
CompanyID _current_company
Company currently doing an action.
Definition: company_cmd.cpp:53
Functions related to companies.
@ OWNER_NONE
The tile has no ownership.
Definition: company_type.h:25
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.
Definition: dropdown_type.h:50
uint ScaleByCargoScale(uint num, bool town)
Scale a number by the cargo scale setting.
Definition: economy_func.h:77
Functions related to errors.
void ShowErrorMessage(StringID summary_msg, int x, int y, CommandCost cc)
Display an error message in a window.
Definition: error_gui.cpp:367
@ 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.
Definition: widget_type.h:1181
constexpr NWidgetPart SetScrollbar(WidgetID index)
Attach a scrollbar to a widget.
Definition: widget_type.h:1284
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.
Definition: widget_type.h:1228
constexpr NWidgetPart SetDataTip(uint32_t data, StringID tip)
Widget part function for setting the data and tooltip.
Definition: widget_type.h:1202
constexpr NWidgetPart SetMinimalSize(int16_t x, int16_t y)
Widget part function for setting the minimal size.
Definition: widget_type.h:1137
constexpr NWidgetPart NWidget(WidgetType tp, Colours col, WidgetID idx=-1)
Widget part function for starting a new 'real' widget.
Definition: widget_type.h:1309
constexpr NWidgetPart SetMatrixDataTip(uint8_t cols, uint8_t rows, StringID tip)
Widget part function for setting the data and tooltip of WWT_MATRIX widgets.
Definition: widget_type.h:1214
constexpr NWidgetPart EndContainer()
Widget part function for denoting the end of a container (horizontal, vertical, WWT_FRAME,...
Definition: widget_type.h:1191
constexpr NWidgetPart SetMinimalTextLines(uint8_t lines, uint8_t spacing, FontSize size=FS_NORMAL)
Widget part function for setting the minimal text lines.
Definition: widget_type.h:1149
constexpr NWidgetPart SetAspect(float ratio, AspectFlags flags=AspectFlags::ResizeX)
Widget part function for setting the aspect ratio.
Definition: widget_type.h:1295
constexpr NWidgetPart SetResize(int16_t dx, int16_t dy)
Widget part function for setting the resize step.
Definition: widget_type.h:1126
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.
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.
static void ShowIndustryCargoesWindow(IndustryType id)
Open the industry and cargoes window.
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.
Definition: industry_type.h:40
static const IndustryType NUM_INDUSTRYTYPES
total number of industry types, new and old; limited to 240 because we need some special ids like INV...
Definition: industry_type.h:26
static const IndustryType INVALID_INDUSTRYTYPE
one above amount is considered invalid
Definition: industry_type.h:27
static const int INDUSTRY_ORIGINAL_NUM_OUTPUTS
Original number of produced cargo types.
Definition: industry_type.h:41
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.
Definition: industrytype.h:76
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.
Definition: math_func.hpp:252
constexpr int RoundDivSU(int a, uint b)
Computes round(a / b) for signed a and unsigned b.
Definition: math_func.hpp:342
constexpr uint ToPercent8(uint i)
Converts a "fract" value 0..255 to "percent" value 0..100.
Definition: math_func.hpp:295
constexpr uint ClampU(const uint a, const uint min, const uint max)
Clamp an unsigned integer between an interval.
Definition: math_func.hpp:150
void GuiShowTooltips(Window *parent, StringID str, TooltipCloseCondition close_tooltip, uint paramcount)
Shows a tooltip.
Definition: misc_gui.cpp:760
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...
Definition: misc_gui.cpp:1223
void ShowQueryString(StringID str, StringID caption, uint maxsize, Window *parent, CharSetFilter afilter, QueryStringFlags flags)
Show a query popup window with a textbox in it.
Definition: misc_gui.cpp:1079
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 IsNewGRFInspectable(GrfSpecFeature feature, uint index)
Can we inspect the data given a certain feature and index.
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.
Definition: palette_func.h:70
static const uint8_t PC_BLACK
Black palette colour.
Definition: palette_func.h:67
static const uint8_t PC_YELLOW
Yellow palette colour.
Definition: palette_func.h:80
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.
Definition: settings_gui.h:17
#define SETTING_BUTTON_HEIGHT
Height of setting buttons.
Definition: settings_gui.h:19
@ 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:58
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:331
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:319
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:357
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.
Definition: strings_type.h:24
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
Definition: strings_type.h:16
static const int MAX_CHAR_LENGTH
Max. length of UTF-8 encoded unicode character.
Definition: strings_type.h:18
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.
Definition: backup_type.hpp:21
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:240
Specification of a cargo type.
Definition: cargotype.h:71
SpriteID GetCargoIcon() const
Get sprite for showing cargo of this type.
Definition: cargotype.cpp:169
static IterateWrapper Iterate(size_t from=0)
Returns an iterable ensemble of all valid CargoSpec.
Definition: cargotype.h:190
static CargoSpec * Get(size_t index)
Retrieve cargo details for the given cargo ID.
Definition: cargotype.h:134
StringID name
Name of this type of cargo.
Definition: cargotype.h:88
TownProductionEffect town_production_effect
The effect on town cargo production.
Definition: cargotype.h:84
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
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:131
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.
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 OnInvalidateData([[maybe_unused]] int data=0, [[maybe_unused]] bool gui_scope=true) override
Some data on this window has become invalid.
Dimension ind_textsize
Size to hold any industry type text, as well as STR_INDUSTRY_CARGOES_SELECT_INDUSTRY.
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.
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.
Defines the data structure for constructing industry.
Definition: industrytype.h:101
uint16_t callback_mask
Bitmask of industry callbacks that have to be called.
Definition: industrytype.h:130
std::array< CargoID, INDUSTRY_NUM_INPUTS > accepts_cargo
16 accepted cargoes.
Definition: industrytype.h:115
bool UsesOriginalEconomy() const
Determines whether this industrytype uses standard/newgrf production changes.
StringID name
Displayed name of the industry.
Definition: industrytype.h:121
IndustryBehaviour behaviour
How this industry will behave, and how others entities can use it.
Definition: industrytype.h:119
bool IsRawIndustry() const
Is an industry with the spec a raw industry?
GRFFileProps grf_prop
properties related to the grf file
Definition: industrytype.h:132
std::vector< IndustryTileLayout > layouts
List of possible tile layouts for the industry.
Definition: industrytype.h:102
bool enabled
entity still available (by default true).newgrf can disable it, though
Definition: industrytype.h:131
Money GetConstructionCost() const
Get the cost for constructing this industry.
uint8_t map_colour
colour used for the small map
Definition: industrytype.h:120
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:273
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).
Definition: sortlist_type.h:30
static debug_inline uint Size()
Get the size of the map.
Definition: map_func.h:288
Partial widget specification to allow NWidgets to be written nested.
Definition: widget_type.h:1075
TileIndex GetCenterTile() const
Get the center tile.
Definition: tilearea_type.h:59
TileIndex tile
The base tile of the area.
Definition: tilearea_type.h:19
Coordinates of a point in 2D.
Tindex index
Index of this pool item.
Definition: pool_type.hpp:238
static Titem * Get(size_t index)
Returns Titem with given index.
Definition: pool_type.hpp:339
static size_t GetNumItems()
Returns number of valid items in the pool.
Definition: pool_type.hpp:369
static bool IsValidID(size_t index)
Tests whether given index can be used to get valid (non-nullptr) Titem.
Definition: pool_type.hpp:328
static Pool::IterateWrapper< Titem > Iterate(size_t from=0)
Returns an iterable ensemble of all valid Titem.
Definition: pool_type.hpp:388
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.
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
Definition: textbuf_type.h:32
Window * GetCallbackWnd()
Get the window that started the current highlighting.
Definition: viewport.cpp:2588
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:780
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:731
void InvalidateData(int data=0, bool gui_scope=true)
Mark this window's data as invalid (in need of re-computing)
Definition: window.cpp:3151
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:763
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
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
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).
Definition: viewport.cpp:3498
@ 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.
Definition: viewport.cpp:2504
bool ScrollMainWindowToTile(TileIndex tile, bool instant)
Scrolls the viewport of the main window to a given location.
Definition: viewport.cpp:2515
Functions related to (drawing on) viewports.
@ ZOOM_IN
Zoom in (get more detailed view).
Definition: viewport_type.h:77
@ ZOOM_OUT
Zoom out (get helicopter view).
Definition: viewport_type.h:78
Rect ScrollRect(Rect r, const Scrollbar &sb, int resize_step)
Apply 'scroll' to a rect to be drawn in.
Definition: widget.cpp:2411
@ SZSP_HORIZONTAL
Display plane with zero size vertically, and filling and resizing horizontally.
Definition: widget_type.h:482
@ WWT_PUSHTXTBTN
Normal push-button (no toggle button) with text caption.
Definition: widget_type.h:112
@ 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.
Definition: widget_type.h:113
@ 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:84
@ 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:83
@ 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:81
@ 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
Window * BringWindowToFrontById(WindowClass cls, WindowNumber number)
Find a window and make it the relative top-window on the screen.
Definition: window.cpp:1223
Window * FindWindowByClass(WindowClass cls)
Find any window by its class.
Definition: window.cpp:1113
void SetFocusedWindow(Window *w)
Set the window that has the focus.
Definition: window.cpp:422
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:3228
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.
Definition: window_type.h:737
EventState
State of handling an event.
Definition: window_type.h:743
@ ES_HANDLED
The passed event is handled.
Definition: window_type.h:744
@ ES_NOT_HANDLED
The passed event is not handled.
Definition: window_type.h:745
@ WC_INDUSTRY_DIRECTORY
Industry directory; Window numbers:
Definition: window_type.h:266
@ WC_NONE
No window, redirects to WC_MAIN_WINDOW.
Definition: window_type.h:45
@ WC_INDUSTRY_CARGOES
Industry cargoes chain; Window numbers:
Definition: window_type.h:515
@ WC_INDUSTRY_PRODUCTION
Industry production history graph; Window numbers:
Definition: window_type.h:581
@ WC_INDUSTRY_VIEW
Industry view; Window numbers:
Definition: window_type.h:363
@ WC_SMALLMAP
Small map; Window numbers:
Definition: window_type.h:104
@ WC_BUILD_INDUSTRY
Build industry; Window numbers:
Definition: window_type.h:441
Functions related to zooming.
int ScaleGUITrad(int value)
Scale traditional pixel dimensions to GUI zoom level.
Definition: zoom_func.h:117
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