OpenTTD
industry_gui.cpp
Go to the documentation of this file.
1 /* $Id$ */
2 
3 /*
4  * This file is part of OpenTTD.
5  * 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.
6  * 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.
7  * 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/>.
8  */
9 
12 #include "stdafx.h"
13 #include "error.h"
14 #include "gui.h"
15 #include "settings_gui.h"
16 #include "sound_func.h"
17 #include "window_func.h"
18 #include "textbuf_gui.h"
19 #include "command_func.h"
20 #include "viewport_func.h"
21 #include "industry.h"
22 #include "town.h"
23 #include "cheat_type.h"
24 #include "newgrf_industries.h"
25 #include "newgrf_text.h"
26 #include "newgrf_debug.h"
27 #include "network/network.h"
28 #include "strings_func.h"
29 #include "company_func.h"
30 #include "tilehighlight_func.h"
31 #include "string_func.h"
32 #include "sortlist_type.h"
33 #include "widgets/dropdown_func.h"
34 #include "company_base.h"
35 #include "core/geometry_func.hpp"
36 #include "core/random_func.hpp"
37 #include "core/backup_type.hpp"
38 #include "genworld.h"
39 #include "smallmap_gui.h"
40 #include "widgets/dropdown_type.h"
42 
43 #include "table/strings.h"
44 
45 #include <bitset>
46 
47 #include "safeguards.h"
48 
49 bool _ignore_restrictions;
50 std::bitset<NUM_INDUSTRYTYPES> _displayed_industries;
51 
57 };
58 
65 };
66 
68 struct CargoSuffix {
70  char text[512];
71 };
72 
73 static void ShowIndustryCargoesWindow(IndustryType id);
74 
84 static void GetCargoSuffix(uint cargo, CargoSuffixType cst, const Industry *ind, IndustryType ind_type, const IndustrySpec *indspec, CargoSuffix &suffix)
85 {
86  suffix.text[0] = '\0';
87  suffix.display = CSD_CARGO_AMOUNT;
88 
89  if (HasBit(indspec->callback_mask, CBM_IND_CARGO_SUFFIX)) {
90  TileIndex t = (cst != CST_FUND) ? ind->location.tile : INVALID_TILE;
91  uint16 callback = GetIndustryCallback(CBID_INDUSTRY_CARGO_SUFFIX, 0, (cst << 8) | cargo, const_cast<Industry *>(ind), ind_type, t);
92  if (callback == CALLBACK_FAILED) return;
93 
94  if (indspec->grf_prop.grffile->grf_version < 8) {
95  if (GB(callback, 0, 8) == 0xFF) return;
96  if (callback < 0x400) {
98  GetString(suffix.text, GetGRFStringID(indspec->grf_prop.grffile->grfid, 0xD000 + callback), lastof(suffix.text));
101  return;
102  }
104  return;
105 
106  } else { // GRF version 8 or higher.
107  if (callback == 0x400) return;
108  if (callback == 0x401) {
109  suffix.display = CSD_CARGO;
110  return;
111  }
112  if (callback < 0x400) {
114  GetString(suffix.text, GetGRFStringID(indspec->grf_prop.grffile->grfid, 0xD000 + callback), lastof(suffix.text));
117  return;
118  }
119  if (callback >= 0x800 && callback < 0xC00) {
121  GetString(suffix.text, GetGRFStringID(indspec->grf_prop.grffile->grfid, 0xD000 - 0x800 + callback), lastof(suffix.text));
123  suffix.display = CSD_CARGO_TEXT;
124  return;
125  }
127  return;
128  }
129  }
130 }
131 
132 enum CargoSuffixInOut {
133  CARGOSUFFIX_OUT = 0,
134  CARGOSUFFIX_IN = 1,
135 };
136 
147 template <typename TC, typename TS>
148 static inline void GetAllCargoSuffixes(CargoSuffixInOut use_input, CargoSuffixType cst, const Industry *ind, IndustryType ind_type, const IndustrySpec *indspec, const TC &cargoes, TS &suffixes)
149 {
150  assert_compile(lengthof(cargoes) <= lengthof(suffixes));
151 
153  /* Reworked behaviour with new many-in-many-out scheme */
154  for (uint j = 0; j < lengthof(suffixes); j++) {
155  if (cargoes[j] != CT_INVALID) {
156  byte local_id = indspec->grf_prop.grffile->cargo_map[cargoes[j]]; // should we check the value for valid?
157  uint cargotype = local_id << 16 | use_input;
158  GetCargoSuffix(cargotype, cst, ind, ind_type, indspec, suffixes[j]);
159  } else {
160  suffixes[j].text[0] = '\0';
161  suffixes[j].display = CSD_CARGO;
162  }
163  }
164  } else {
165  /* Compatible behaviour with old 3-in-2-out scheme */
166  for (uint j = 0; j < lengthof(suffixes); j++) {
167  suffixes[j].text[0] = '\0';
168  suffixes[j].display = CSD_CARGO;
169  }
170  switch (use_input) {
171  case CARGOSUFFIX_OUT:
172  if (cargoes[0] != CT_INVALID) GetCargoSuffix(3, cst, ind, ind_type, indspec, suffixes[0]);
173  if (cargoes[1] != CT_INVALID) GetCargoSuffix(4, cst, ind, ind_type, indspec, suffixes[1]);
174  break;
175  case CARGOSUFFIX_IN:
176  if (cargoes[0] != CT_INVALID) GetCargoSuffix(0, cst, ind, ind_type, indspec, suffixes[0]);
177  if (cargoes[1] != CT_INVALID) GetCargoSuffix(1, cst, ind, ind_type, indspec, suffixes[1]);
178  if (cargoes[2] != CT_INVALID) GetCargoSuffix(2, cst, ind, ind_type, indspec, suffixes[2]);
179  break;
180  default:
181  NOT_REACHED();
182  }
183  }
184 }
185 
186 std::array<IndustryType, NUM_INDUSTRYTYPES> _sorted_industry_types;
187 
189 static bool IndustryTypeNameSorter(const IndustryType &a, const IndustryType &b)
190 {
191  static char industry_name[2][64];
192 
193  const IndustrySpec *indsp1 = GetIndustrySpec(a);
194  GetString(industry_name[0], indsp1->name, lastof(industry_name[0]));
195 
196  const IndustrySpec *indsp2 = GetIndustrySpec(b);
197  GetString(industry_name[1], indsp2->name, lastof(industry_name[1]));
198 
199  int r = strnatcmp(industry_name[0], industry_name[1]); // Sort by name (natural sorting).
200 
201  /* If the names are equal, sort by industry type. */
202  return (r != 0) ? r < 0 : (a < b);
203 }
204 
209 {
210  /* Add each industry type to the list. */
211  for (IndustryType i = 0; i < NUM_INDUSTRYTYPES; i++) {
212  _sorted_industry_types[i] = i;
213  }
214 
215  /* Sort industry types by name. */
217 }
218 
227 void CcBuildIndustry(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2, uint32 cmd)
228 {
229  if (result.Succeeded()) return;
230 
231  uint8 indtype = GB(p1, 0, 8);
232  if (indtype < NUM_INDUSTRYTYPES) {
233  const IndustrySpec *indsp = GetIndustrySpec(indtype);
234  if (indsp->enabled) {
235  SetDParam(0, indsp->name);
236  ShowErrorMessage(STR_ERROR_CAN_T_BUILD_HERE, result.GetErrorMessage(), WL_INFO, TileX(tile) * TILE_SIZE, TileY(tile) * TILE_SIZE);
237  }
238  }
239 }
240 
241 static const NWidgetPart _nested_build_industry_widgets[] = {
243  NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
244  NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetDataTip(STR_FUND_INDUSTRY_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
245  NWidget(WWT_SHADEBOX, COLOUR_DARK_GREEN),
246  NWidget(WWT_DEFSIZEBOX, COLOUR_DARK_GREEN),
247  NWidget(WWT_STICKYBOX, COLOUR_DARK_GREEN),
248  EndContainer(),
250  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),
251  NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, WID_DPI_SCROLLBAR),
252  EndContainer(),
253  NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_DPI_INFOPANEL), SetResize(1, 0),
254  EndContainer(),
256  NWidget(WWT_TEXTBTN, COLOUR_DARK_GREEN, WID_DPI_DISPLAY_WIDGET), SetFill(1, 0), SetResize(1, 0),
257  SetDataTip(STR_INDUSTRY_DISPLAY_CHAIN, STR_INDUSTRY_DISPLAY_CHAIN_TOOLTIP),
258  NWidget(WWT_TEXTBTN, COLOUR_DARK_GREEN, WID_DPI_FUND_WIDGET), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_JUST_STRING, STR_NULL),
259  NWidget(WWT_RESIZEBOX, COLOUR_DARK_GREEN),
260  EndContainer(),
261 };
262 
265  WDP_AUTO, "build_industry", 170, 212,
268  _nested_build_industry_widgets, lengthof(_nested_build_industry_widgets)
269 );
270 
272 class BuildIndustryWindow : public Window {
274  IndustryType selected_type;
275  uint16 callback_timer;
277  uint16 count;
278  IndustryType index[NUM_INDUSTRYTYPES + 1];
279  bool enabled[NUM_INDUSTRYTYPES + 1];
280  Scrollbar *vscroll;
281 
283  static const int MATRIX_TEXT_OFFSET = 17;
285  static const int MAX_MINWIDTH_LINEHEIGHTS = 20;
286 
287  void SetupArrays()
288  {
289  this->count = 0;
290 
291  for (uint i = 0; i < lengthof(this->index); i++) {
292  this->index[i] = INVALID_INDUSTRYTYPE;
293  this->enabled[i] = false;
294  }
295 
296  if (_game_mode == GM_EDITOR) { // give room for the Many Random "button"
297  this->index[this->count] = INVALID_INDUSTRYTYPE;
298  this->enabled[this->count] = true;
299  this->count++;
300  this->timer_enabled = false;
301  }
302  /* Fill the arrays with industries.
303  * The tests performed after the enabled allow to load the industries
304  * In the same way they are inserted by grf (if any)
305  */
306  for (IndustryType ind : _sorted_industry_types) {
307  const IndustrySpec *indsp = GetIndustrySpec(ind);
308  if (indsp->enabled) {
309  /* Rule is that editor mode loads all industries.
310  * In game mode, all non raw industries are loaded too
311  * and raw ones are loaded only when setting allows it */
312  if (_game_mode != GM_EDITOR && indsp->IsRawIndustry() && _settings_game.construction.raw_industry_construction == 0) {
313  /* Unselect if the industry is no longer in the list */
314  if (this->selected_type == ind) this->selected_index = -1;
315  continue;
316  }
317  this->index[this->count] = ind;
318  this->enabled[this->count] = (_game_mode == GM_EDITOR) || GetIndustryProbabilityCallback(ind, IACT_USERCREATION, 1) > 0;
319  /* Keep the selection to the correct line */
320  if (this->selected_type == ind) this->selected_index = this->count;
321  this->count++;
322  }
323  }
324 
325  /* first industry type is selected if the current selection is invalid.
326  * I'll be damned if there are none available ;) */
327  if (this->selected_index == -1) {
328  this->selected_index = 0;
329  this->selected_type = this->index[0];
330  }
331 
332  this->vscroll->SetCount(this->count);
333  }
334 
336  void SetButtons()
337  {
338  this->SetWidgetDisabledState(WID_DPI_FUND_WIDGET, this->selected_type != INVALID_INDUSTRYTYPE && !this->enabled[this->selected_index]);
339  this->SetWidgetDisabledState(WID_DPI_DISPLAY_WIDGET, this->selected_type == INVALID_INDUSTRYTYPE && this->enabled[this->selected_index]);
340  }
341 
354  std::string MakeCargoListString(const CargoID *cargolist, const CargoSuffix *cargo_suffix, int cargolistlen, StringID prefixstr) const
355  {
356  std::string cargostring;
357  char buf[1024];
358  int numcargo = 0;
359  int firstcargo = -1;
360 
361  for (byte j = 0; j < cargolistlen; j++) {
362  if (cargolist[j] == CT_INVALID) continue;
363  numcargo++;
364  if (firstcargo < 0) {
365  firstcargo = j;
366  continue;
367  }
368  SetDParam(0, CargoSpec::Get(cargolist[j])->name);
369  SetDParamStr(1, cargo_suffix[j].text);
370  GetString(buf, STR_INDUSTRY_VIEW_CARGO_LIST_EXTENSION, lastof(buf));
371  cargostring += buf;
372  }
373 
374  if (numcargo > 0) {
375  SetDParam(0, CargoSpec::Get(cargolist[firstcargo])->name);
376  SetDParamStr(1, cargo_suffix[firstcargo].text);
377  GetString(buf, prefixstr, lastof(buf));
378  cargostring = std::string(buf) + cargostring;
379  } else {
380  SetDParam(0, STR_JUST_NOTHING);
381  SetDParamStr(1, "");
382  GetString(buf, prefixstr, lastof(buf));
383  cargostring = std::string(buf);
384  }
385 
386  return cargostring;
387  }
388 
389 public:
390  BuildIndustryWindow() : Window(&_build_industry_desc)
391  {
392  this->timer_enabled = _loaded_newgrf_features.has_newindustries;
393 
394  this->selected_index = -1;
395  this->selected_type = INVALID_INDUSTRYTYPE;
396 
397  this->callback_timer = DAY_TICKS;
398 
399  this->CreateNestedTree();
400  this->vscroll = this->GetScrollbar(WID_DPI_SCROLLBAR);
401  this->FinishInitNested(0);
402 
403  this->SetButtons();
404  }
405 
406  void OnInit() override
407  {
408  this->SetupArrays();
409  }
410 
411  void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
412  {
413  switch (widget) {
414  case WID_DPI_MATRIX_WIDGET: {
415  Dimension d = GetStringBoundingBox(STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES);
416  for (byte i = 0; i < this->count; i++) {
417  if (this->index[i] == INVALID_INDUSTRYTYPE) continue;
418  d = maxdim(d, GetStringBoundingBox(GetIndustrySpec(this->index[i])->name));
419  }
421  d.width += MATRIX_TEXT_OFFSET + padding.width;
422  d.height = 5 * resize->height;
423  *size = maxdim(*size, d);
424  break;
425  }
426 
427  case WID_DPI_INFOPANEL: {
428  /* Extra line for cost outside of editor + extra lines for 'extra' information for NewGRFs. */
429  int height = 2 + (_game_mode == GM_EDITOR ? 0 : 1) + (_loaded_newgrf_features.has_newindustries ? 4 : 0);
430  uint extra_lines_req = 0;
431  uint extra_lines_prd = 0;
432  uint max_minwidth = FONT_HEIGHT_NORMAL * MAX_MINWIDTH_LINEHEIGHTS;
433  Dimension d = {0, 0};
434  for (byte i = 0; i < this->count; i++) {
435  if (this->index[i] == INVALID_INDUSTRYTYPE) continue;
436 
437  const IndustrySpec *indsp = GetIndustrySpec(this->index[i]);
438  CargoSuffix cargo_suffix[lengthof(indsp->accepts_cargo)];
439 
440  /* Measure the accepted cargoes, if any. */
441  GetAllCargoSuffixes(CARGOSUFFIX_IN, CST_FUND, nullptr, this->index[i], indsp, indsp->accepts_cargo, cargo_suffix);
442  std::string cargostring = this->MakeCargoListString(indsp->accepts_cargo, cargo_suffix, lengthof(indsp->accepts_cargo), STR_INDUSTRY_VIEW_REQUIRES_N_CARGO);
443  Dimension strdim = GetStringBoundingBox(cargostring.c_str());
444  if (strdim.width > max_minwidth) {
445  extra_lines_req = max(extra_lines_req, strdim.width / max_minwidth + 1);
446  strdim.width = max_minwidth;
447  }
448  d = maxdim(d, strdim);
449 
450  /* Measure the produced cargoes, if any. */
451  GetAllCargoSuffixes(CARGOSUFFIX_OUT, CST_FUND, nullptr, this->index[i], indsp, indsp->produced_cargo, cargo_suffix);
452  cargostring = this->MakeCargoListString(indsp->produced_cargo, cargo_suffix, lengthof(indsp->produced_cargo), STR_INDUSTRY_VIEW_PRODUCES_N_CARGO);
453  strdim = GetStringBoundingBox(cargostring.c_str());
454  if (strdim.width > max_minwidth) {
455  extra_lines_prd = max(extra_lines_prd, strdim.width / max_minwidth + 1);
456  strdim.width = max_minwidth;
457  }
458  d = maxdim(d, strdim);
459  }
460 
461  /* Set it to something more sane :) */
462  height += extra_lines_prd + extra_lines_req;
463  size->height = height * FONT_HEIGHT_NORMAL + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
464  size->width = d.width + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
465  break;
466  }
467 
468  case WID_DPI_FUND_WIDGET: {
469  Dimension d = GetStringBoundingBox(STR_FUND_INDUSTRY_BUILD_NEW_INDUSTRY);
470  d = maxdim(d, GetStringBoundingBox(STR_FUND_INDUSTRY_PROSPECT_NEW_INDUSTRY));
471  d = maxdim(d, GetStringBoundingBox(STR_FUND_INDUSTRY_FUND_NEW_INDUSTRY));
472  d.width += padding.width;
473  d.height += padding.height;
474  *size = maxdim(*size, d);
475  break;
476  }
477  }
478  }
479 
480  void SetStringParameters(int widget) const override
481  {
482  switch (widget) {
483  case WID_DPI_FUND_WIDGET:
484  /* Raw industries might be prospected. Show this fact by changing the string
485  * In Editor, you just build, while ingame, or you fund or you prospect */
486  if (_game_mode == GM_EDITOR) {
487  /* We've chosen many random industries but no industries have been specified */
488  SetDParam(0, STR_FUND_INDUSTRY_BUILD_NEW_INDUSTRY);
489  } else {
490  const IndustrySpec *indsp = GetIndustrySpec(this->index[this->selected_index]);
491  SetDParam(0, (_settings_game.construction.raw_industry_construction == 2 && indsp->IsRawIndustry()) ? STR_FUND_INDUSTRY_PROSPECT_NEW_INDUSTRY : STR_FUND_INDUSTRY_FUND_NEW_INDUSTRY);
492  }
493  break;
494  }
495  }
496 
497  void DrawWidget(const Rect &r, int widget) const override
498  {
499  switch (widget) {
500  case WID_DPI_MATRIX_WIDGET: {
501  uint text_left, text_right, icon_left, icon_right;
502  if (_current_text_dir == TD_RTL) {
503  icon_right = r.right - WD_MATRIX_RIGHT;
504  icon_left = icon_right - 10;
505  text_right = icon_right - BuildIndustryWindow::MATRIX_TEXT_OFFSET;
506  text_left = r.left + WD_MATRIX_LEFT;
507  } else {
508  icon_left = r.left + WD_MATRIX_LEFT;
509  icon_right = icon_left + 10;
510  text_left = icon_left + BuildIndustryWindow::MATRIX_TEXT_OFFSET;
511  text_right = r.right - WD_MATRIX_RIGHT;
512  }
513 
514  for (byte i = 0; i < this->vscroll->GetCapacity() && i + this->vscroll->GetPosition() < this->count; i++) {
515  int y = r.top + WD_MATRIX_TOP + i * this->resize.step_height;
516  bool selected = this->selected_index == i + this->vscroll->GetPosition();
517 
518  if (this->index[i + this->vscroll->GetPosition()] == INVALID_INDUSTRYTYPE) {
519  DrawString(text_left, text_right, y, STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES, selected ? TC_WHITE : TC_ORANGE);
520  continue;
521  }
522  const IndustrySpec *indsp = GetIndustrySpec(this->index[i + this->vscroll->GetPosition()]);
523 
524  /* Draw the name of the industry in white is selected, otherwise, in orange */
525  DrawString(text_left, text_right, y, indsp->name, selected ? TC_WHITE : TC_ORANGE);
526  GfxFillRect(icon_left, y + 1, icon_right, y + 7, selected ? PC_WHITE : PC_BLACK);
527  GfxFillRect(icon_left + 1, y + 2, icon_right - 1, y + 6, indsp->map_colour);
528  }
529  break;
530  }
531 
532  case WID_DPI_INFOPANEL: {
533  int y = r.top + WD_FRAMERECT_TOP;
534  int bottom = r.bottom - WD_FRAMERECT_BOTTOM;
535  int left = r.left + WD_FRAMERECT_LEFT;
536  int right = r.right - WD_FRAMERECT_RIGHT;
537 
538  if (this->selected_type == INVALID_INDUSTRYTYPE) {
539  DrawStringMultiLine(left, right, y, bottom, STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_TOOLTIP);
540  break;
541  }
542 
543  const IndustrySpec *indsp = GetIndustrySpec(this->selected_type);
544 
545  if (_game_mode != GM_EDITOR) {
546  SetDParam(0, indsp->GetConstructionCost());
547  DrawString(left, right, y, STR_FUND_INDUSTRY_INDUSTRY_BUILD_COST);
548  y += FONT_HEIGHT_NORMAL;
549  }
550 
551  CargoSuffix cargo_suffix[lengthof(indsp->accepts_cargo)];
552 
553  /* Draw the accepted cargoes, if any. Otherwise, will print "Nothing". */
554  GetAllCargoSuffixes(CARGOSUFFIX_IN, CST_FUND, nullptr, this->selected_type, indsp, indsp->accepts_cargo, cargo_suffix);
555  std::string cargostring = this->MakeCargoListString(indsp->accepts_cargo, cargo_suffix, lengthof(indsp->accepts_cargo), STR_INDUSTRY_VIEW_REQUIRES_N_CARGO);
556  y = DrawStringMultiLine(left, right, y, bottom, cargostring.c_str());
557 
558  /* Draw the produced cargoes, if any. Otherwise, will print "Nothing". */
559  GetAllCargoSuffixes(CARGOSUFFIX_OUT, CST_FUND, nullptr, this->selected_type, indsp, indsp->produced_cargo, cargo_suffix);
560  cargostring = this->MakeCargoListString(indsp->produced_cargo, cargo_suffix, lengthof(indsp->produced_cargo), STR_INDUSTRY_VIEW_PRODUCES_N_CARGO);
561  y = DrawStringMultiLine(left, right, y, bottom, cargostring.c_str());
562 
563  /* Get the additional purchase info text, if it has not already been queried. */
565  uint16 callback_res = GetIndustryCallback(CBID_INDUSTRY_FUND_MORE_TEXT, 0, 0, nullptr, this->selected_type, INVALID_TILE);
566  if (callback_res != CALLBACK_FAILED && callback_res != 0x400) {
567  if (callback_res > 0x400) {
569  } else {
570  StringID str = GetGRFStringID(indsp->grf_prop.grffile->grfid, 0xD000 + callback_res); // No. here's the new string
571  if (str != STR_UNDEFINED) {
573  DrawStringMultiLine(left, right, y, bottom, str, TC_YELLOW);
575  }
576  }
577  }
578  }
579  break;
580  }
581  }
582  }
583 
584  void OnClick(Point pt, int widget, int click_count) override
585  {
586  switch (widget) {
587  case WID_DPI_MATRIX_WIDGET: {
588  int y = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_DPI_MATRIX_WIDGET);
589  if (y < this->count) { // Is it within the boundaries of available data?
590  this->selected_index = y;
591  this->selected_type = this->index[y];
592  const IndustrySpec *indsp = (this->selected_type == INVALID_INDUSTRYTYPE) ? nullptr : GetIndustrySpec(this->selected_type);
593 
594  this->SetDirty();
595 
596  if (_thd.GetCallbackWnd() == this &&
597  ((_game_mode != GM_EDITOR && _settings_game.construction.raw_industry_construction == 2 && indsp != nullptr && indsp->IsRawIndustry()) ||
598  this->selected_type == INVALID_INDUSTRYTYPE ||
599  !this->enabled[this->selected_index])) {
600  /* Reset the button state if going to prospecting or "build many industries" */
601  this->RaiseButtons();
603  }
604 
605  this->SetButtons();
606  if (this->enabled[this->selected_index] && click_count > 1) this->OnClick(pt, WID_DPI_FUND_WIDGET, 1);
607  }
608  break;
609  }
610 
612  if (this->selected_type != INVALID_INDUSTRYTYPE) ShowIndustryCargoesWindow(this->selected_type);
613  break;
614 
615  case WID_DPI_FUND_WIDGET: {
616  if (this->selected_type == INVALID_INDUSTRYTYPE) {
617  this->HandleButtonClick(WID_DPI_FUND_WIDGET);
618 
619  if (Town::GetNumItems() == 0) {
620  ShowErrorMessage(STR_ERROR_CAN_T_GENERATE_INDUSTRIES, STR_ERROR_MUST_FOUND_TOWN_FIRST, WL_INFO);
621  } else {
622  extern void GenerateIndustries();
623  _generating_world = true;
625  _generating_world = false;
626  }
627  } else if (_game_mode != GM_EDITOR && _settings_game.construction.raw_industry_construction == 2 && GetIndustrySpec(this->selected_type)->IsRawIndustry()) {
628  DoCommandP(0, this->selected_type, InteractiveRandom(), CMD_BUILD_INDUSTRY | CMD_MSG(STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY));
629  this->HandleButtonClick(WID_DPI_FUND_WIDGET);
630  } else {
631  HandlePlacePushButton(this, WID_DPI_FUND_WIDGET, SPR_CURSOR_INDUSTRY, HT_RECT);
632  }
633  break;
634  }
635  }
636  }
637 
638  void OnResize() override
639  {
640  /* Adjust the number of items in the matrix depending of the resize */
641  this->vscroll->SetCapacityFromWidget(this, WID_DPI_MATRIX_WIDGET);
642  }
643 
644  void OnPlaceObject(Point pt, TileIndex tile) override
645  {
646  bool success = true;
647  /* We do not need to protect ourselves against "Random Many Industries" in this mode */
648  const IndustrySpec *indsp = GetIndustrySpec(this->selected_type);
649  uint32 seed = InteractiveRandom();
650  uint32 layout_index = InteractiveRandomRange((uint32)indsp->layouts.size());
651 
652  if (_game_mode == GM_EDITOR) {
653  /* Show error if no town exists at all */
654  if (Town::GetNumItems() == 0) {
655  SetDParam(0, indsp->name);
656  ShowErrorMessage(STR_ERROR_CAN_T_BUILD_HERE, STR_ERROR_MUST_FOUND_TOWN_FIRST, WL_INFO, pt.x, pt.y);
657  return;
658  }
659 
660  Backup<CompanyID> cur_company(_current_company, OWNER_NONE, FILE_LINE);
661  _generating_world = true;
662  _ignore_restrictions = true;
663 
664  DoCommandP(tile, (layout_index << 8) | this->selected_type, seed,
665  CMD_BUILD_INDUSTRY | CMD_MSG(STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY), &CcBuildIndustry);
666 
667  cur_company.Restore();
668  _ignore_restrictions = false;
669  _generating_world = false;
670  } else {
671  success = DoCommandP(tile, (layout_index << 8) | this->selected_type, seed, CMD_BUILD_INDUSTRY | CMD_MSG(STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY));
672  }
673 
674  /* If an industry has been built, just reset the cursor and the system */
676  }
677 
678  void OnGameTick() override
679  {
680  if (!this->timer_enabled) return;
681  if (--this->callback_timer == 0) {
682  /* We have just passed another day.
683  * See if we need to update availability of currently selected industry */
684  this->callback_timer = DAY_TICKS; // restart counter
685 
686  const IndustrySpec *indsp = GetIndustrySpec(this->selected_type);
687 
688  if (indsp->enabled) {
689  bool call_back_result = GetIndustryProbabilityCallback(this->selected_type, IACT_USERCREATION, 1) > 0;
690 
691  /* Only if result does match the previous state would it require a redraw. */
692  if (call_back_result != this->enabled[this->selected_index]) {
693  this->enabled[this->selected_index] = call_back_result;
694  this->SetButtons();
695  this->SetDirty();
696  }
697  }
698  }
699  }
700 
701  void OnTimeout() override
702  {
703  this->RaiseButtons();
704  }
705 
706  void OnPlaceObjectAbort() override
707  {
708  this->RaiseButtons();
709  }
710 
716  void OnInvalidateData(int data = 0, bool gui_scope = true) override
717  {
718  if (!gui_scope) return;
719  this->SetupArrays();
720 
721  const IndustrySpec *indsp = (this->selected_type == INVALID_INDUSTRYTYPE) ? nullptr : GetIndustrySpec(this->selected_type);
722  if (indsp == nullptr) this->enabled[this->selected_index] = _settings_game.difficulty.industry_density != ID_FUND_ONLY;
723  this->SetButtons();
724  }
725 };
726 
727 void ShowBuildIndustryWindow()
728 {
729  if (_game_mode != GM_EDITOR && !Company::IsValidID(_local_company)) return;
731  new BuildIndustryWindow();
732 }
733 
734 static void UpdateIndustryProduction(Industry *i);
735 
736 static inline bool IsProductionAlterable(const Industry *i)
737 {
738  const IndustrySpec *is = GetIndustrySpec(i->type);
739  bool has_prod = false;
740  for (size_t j = 0; j < lengthof(is->production_rate); j++) {
741  if (is->production_rate[j] != 0) {
742  has_prod = true;
743  break;
744  }
745  }
746  return ((_game_mode == GM_EDITOR || _cheats.setup_prod.value) &&
747  (has_prod || is->IsRawIndustry()) &&
748  !_networking);
749 }
750 
752 {
754  enum Editability {
758  };
759 
761  enum InfoLine {
766  };
767 
774 
775 public:
776  IndustryViewWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc)
777  {
778  this->flags |= WF_DISABLE_VP_SCROLL;
779  this->editbox_line = IL_NONE;
780  this->clicked_line = IL_NONE;
781  this->clicked_button = 0;
782  this->info_height = WD_FRAMERECT_TOP + 2 * FONT_HEIGHT_NORMAL + WD_FRAMERECT_BOTTOM + 1; // Info panel has at least two lines text.
783 
784  this->InitNested(window_number);
785  NWidgetViewport *nvp = this->GetWidget<NWidgetViewport>(WID_IV_VIEWPORT);
786  nvp->InitializeViewport(this, Industry::Get(window_number)->location.GetCenterTile(), ZOOM_LVL_INDUSTRY);
787 
788  this->InvalidateData();
789  }
790 
791  void OnPaint() override
792  {
793  this->DrawWidgets();
794 
795  if (this->IsShaded()) return; // Don't draw anything when the window is shaded.
796 
797  NWidgetBase *nwi = this->GetWidget<NWidgetBase>(WID_IV_INFO);
798  uint expected = this->DrawInfo(nwi->pos_x, nwi->pos_x + nwi->current_x - 1, nwi->pos_y) - nwi->pos_y;
799  if (expected > nwi->current_y - 1) {
800  this->info_height = expected + 1;
801  this->ReInit();
802  return;
803  }
804  }
805 
813  int DrawInfo(uint left, uint right, uint top)
814  {
815  Industry *i = Industry::Get(this->window_number);
816  const IndustrySpec *ind = GetIndustrySpec(i->type);
817  int y = top + WD_FRAMERECT_TOP;
818  bool first = true;
819  bool has_accept = false;
820 
821  if (i->prod_level == PRODLEVEL_CLOSURE) {
822  DrawString(left + WD_FRAMERECT_LEFT, right - WD_FRAMERECT_RIGHT, y, STR_INDUSTRY_VIEW_INDUSTRY_ANNOUNCED_CLOSURE);
823  y += 2 * FONT_HEIGHT_NORMAL;
824  }
825 
826  CargoSuffix cargo_suffix[lengthof(i->accepts_cargo)];
827  GetAllCargoSuffixes(CARGOSUFFIX_IN, CST_VIEW, i, i->type, ind, i->accepts_cargo, cargo_suffix);
829 
830  uint left_side = left + WD_FRAMERECT_LEFT * 4; // Indent accepted cargoes.
831  for (byte j = 0; j < lengthof(i->accepts_cargo); j++) {
832  if (i->accepts_cargo[j] == CT_INVALID) continue;
833  has_accept = true;
834  if (first) {
835  DrawString(left + WD_FRAMERECT_LEFT, right - WD_FRAMERECT_RIGHT, y, STR_INDUSTRY_VIEW_REQUIRES);
836  y += FONT_HEIGHT_NORMAL;
837  first = false;
838  }
840  SetDParam(1, i->accepts_cargo[j]);
842  SetDParamStr(3, "");
843  StringID str = STR_NULL;
844  switch (cargo_suffix[j].display) {
846  SetDParamStr(3, cargo_suffix[j].text);
847  FALLTHROUGH;
848  case CSD_CARGO_AMOUNT:
849  str = stockpiling ? STR_INDUSTRY_VIEW_ACCEPT_CARGO_AMOUNT : STR_INDUSTRY_VIEW_ACCEPT_CARGO;
850  break;
851 
852  case CSD_CARGO_TEXT:
853  SetDParamStr(3, cargo_suffix[j].text);
854  FALLTHROUGH;
855  case CSD_CARGO:
856  str = STR_INDUSTRY_VIEW_ACCEPT_CARGO;
857  break;
858 
859  default:
860  NOT_REACHED();
861  }
862  DrawString(left_side, right - WD_FRAMERECT_RIGHT, y, str);
863  y += FONT_HEIGHT_NORMAL;
864  }
865 
866  GetAllCargoSuffixes(CARGOSUFFIX_OUT, CST_VIEW, i, i->type, ind, i->produced_cargo, cargo_suffix);
867  first = true;
868  for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
869  if (i->produced_cargo[j] == CT_INVALID) continue;
870  if (first) {
871  if (has_accept) y += WD_PAR_VSEP_WIDE;
872  DrawString(left + WD_FRAMERECT_LEFT, right - WD_FRAMERECT_RIGHT, y, STR_INDUSTRY_VIEW_PRODUCTION_LAST_MONTH_TITLE);
873  y += FONT_HEIGHT_NORMAL;
874  if (this->editable == EA_RATE) this->production_offset_y = y;
875  first = false;
876  }
877 
878  SetDParam(0, i->produced_cargo[j]);
880  SetDParamStr(2, cargo_suffix[j].text);
882  uint x = left + WD_FRAMETEXT_LEFT + (this->editable == EA_RATE ? SETTING_BUTTON_WIDTH + 10 : 0);
883  DrawString(x, right - WD_FRAMERECT_RIGHT, y, STR_INDUSTRY_VIEW_TRANSPORTED);
884  /* Let's put out those buttons.. */
885  if (this->editable == EA_RATE) {
886  DrawArrowButtons(left + WD_FRAMETEXT_LEFT, y, COLOUR_YELLOW, (this->clicked_line == IL_RATE1 + j) ? this->clicked_button : 0,
887  i->production_rate[j] > 0, i->production_rate[j] < 255);
888  }
889  y += FONT_HEIGHT_NORMAL;
890  }
891 
892  /* Display production multiplier if editable */
893  if (this->editable == EA_MULTIPLIER) {
894  y += WD_PAR_VSEP_WIDE;
895  this->production_offset_y = y;
897  uint x = left + WD_FRAMETEXT_LEFT + SETTING_BUTTON_WIDTH + 10;
898  DrawString(x, right - WD_FRAMERECT_RIGHT, y, STR_INDUSTRY_VIEW_PRODUCTION_LEVEL);
899  DrawArrowButtons(left + WD_FRAMETEXT_LEFT, y, COLOUR_YELLOW, (this->clicked_line == IL_MULTIPLIER) ? this->clicked_button : 0,
901  y += FONT_HEIGHT_NORMAL;
902  }
903 
904  /* Get the extra message for the GUI */
906  uint16 callback_res = GetIndustryCallback(CBID_INDUSTRY_WINDOW_MORE_TEXT, 0, 0, i, i->type, i->location.tile);
907  if (callback_res != CALLBACK_FAILED && callback_res != 0x400) {
908  if (callback_res > 0x400) {
910  } else {
911  StringID message = GetGRFStringID(ind->grf_prop.grffile->grfid, 0xD000 + callback_res);
912  if (message != STR_NULL && message != STR_UNDEFINED) {
913  y += WD_PAR_VSEP_WIDE;
914 
916  /* Use all the available space left from where we stand up to the
917  * end of the window. We ALSO enlarge the window if needed, so we
918  * can 'go' wild with the bottom of the window. */
919  y = DrawStringMultiLine(left + WD_FRAMERECT_LEFT, right - WD_FRAMERECT_RIGHT, y, UINT16_MAX, message, TC_BLACK);
921  }
922  }
923  }
924  }
925  return y + WD_FRAMERECT_BOTTOM;
926  }
927 
928  void SetStringParameters(int widget) const override
929  {
930  if (widget == WID_IV_CAPTION) SetDParam(0, this->window_number);
931  }
932 
933  void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
934  {
935  if (widget == WID_IV_INFO) size->height = this->info_height;
936  }
937 
938  void OnClick(Point pt, int widget, int click_count) override
939  {
940  switch (widget) {
941  case WID_IV_INFO: {
942  Industry *i = Industry::Get(this->window_number);
943  InfoLine line = IL_NONE;
944 
945  switch (this->editable) {
946  case EA_NONE: break;
947 
948  case EA_MULTIPLIER:
949  if (IsInsideBS(pt.y, this->production_offset_y, FONT_HEIGHT_NORMAL)) line = IL_MULTIPLIER;
950  break;
951 
952  case EA_RATE:
953  if (pt.y >= this->production_offset_y) {
954  int row = (pt.y - this->production_offset_y) / FONT_HEIGHT_NORMAL;
955  for (uint j = 0; j < lengthof(i->produced_cargo); j++) {
956  if (i->produced_cargo[j] == CT_INVALID) continue;
957  row--;
958  if (row < 0) {
959  line = (InfoLine)(IL_RATE1 + j);
960  break;
961  }
962  }
963  }
964  break;
965  }
966  if (line == IL_NONE) return;
967 
968  NWidgetBase *nwi = this->GetWidget<NWidgetBase>(widget);
969  int left = nwi->pos_x + WD_FRAMETEXT_LEFT;
970  int right = nwi->pos_x + nwi->current_x - 1 - WD_FRAMERECT_RIGHT;
971  if (IsInsideMM(pt.x, left, left + SETTING_BUTTON_WIDTH)) {
972  /* Clicked buttons, decrease or increase production */
973  byte button = (pt.x < left + SETTING_BUTTON_WIDTH / 2) ? 1 : 2;
974  switch (this->editable) {
975  case EA_MULTIPLIER:
976  if (button == 1) {
977  if (i->prod_level <= PRODLEVEL_MINIMUM) return;
978  i->prod_level = max<uint>(i->prod_level / 2, PRODLEVEL_MINIMUM);
979  } else {
980  if (i->prod_level >= PRODLEVEL_MAXIMUM) return;
982  }
983  break;
984 
985  case EA_RATE:
986  if (button == 1) {
987  if (i->production_rate[line - IL_RATE1] <= 0) return;
988  i->production_rate[line - IL_RATE1] = max(i->production_rate[line - IL_RATE1] / 2, 0);
989  } else {
990  if (i->production_rate[line - IL_RATE1] >= 255) return;
991  /* a zero production industry is unlikely to give anything but zero, so push it a little bit */
992  int new_prod = i->production_rate[line - IL_RATE1] == 0 ? 1 : i->production_rate[line - IL_RATE1] * 2;
993  i->production_rate[line - IL_RATE1] = minu(new_prod, 255);
994  }
995  break;
996 
997  default: NOT_REACHED();
998  }
999 
1000  UpdateIndustryProduction(i);
1001  this->SetDirty();
1002  this->SetTimeout();
1003  this->clicked_line = line;
1004  this->clicked_button = button;
1005  } else if (IsInsideMM(pt.x, left + SETTING_BUTTON_WIDTH + 10, right)) {
1006  /* clicked the text */
1007  this->editbox_line = line;
1008  switch (this->editable) {
1009  case EA_MULTIPLIER:
1011  ShowQueryString(STR_JUST_INT, STR_CONFIG_GAME_PRODUCTION_LEVEL, 10, this, CS_ALPHANUMERAL, QSF_NONE);
1012  break;
1013 
1014  case EA_RATE:
1015  SetDParam(0, i->production_rate[line - IL_RATE1] * 8);
1016  ShowQueryString(STR_JUST_INT, STR_CONFIG_GAME_PRODUCTION, 10, this, CS_ALPHANUMERAL, QSF_NONE);
1017  break;
1018 
1019  default: NOT_REACHED();
1020  }
1021  }
1022  break;
1023  }
1024 
1025  case WID_IV_GOTO: {
1026  Industry *i = Industry::Get(this->window_number);
1027  if (_ctrl_pressed) {
1029  } else {
1031  }
1032  break;
1033  }
1034 
1035  case WID_IV_DISPLAY: {
1036  Industry *i = Industry::Get(this->window_number);
1038  break;
1039  }
1040  }
1041  }
1042 
1043  void OnTimeout() override
1044  {
1045  this->clicked_line = IL_NONE;
1046  this->clicked_button = 0;
1047  this->SetDirty();
1048  }
1049 
1050  void OnResize() override
1051  {
1052  if (this->viewport != nullptr) {
1053  NWidgetViewport *nvp = this->GetWidget<NWidgetViewport>(WID_IV_VIEWPORT);
1054  nvp->UpdateViewportCoordinates(this);
1055 
1056  ScrollWindowToTile(Industry::Get(this->window_number)->location.GetCenterTile(), this, true); // Re-center viewport.
1057  }
1058  }
1059 
1060  void OnQueryTextFinished(char *str) override
1061  {
1062  if (StrEmpty(str)) return;
1063 
1064  Industry *i = Industry::Get(this->window_number);
1065  uint value = atoi(str);
1066  switch (this->editbox_line) {
1067  case IL_NONE: NOT_REACHED();
1068 
1069  case IL_MULTIPLIER:
1071  break;
1072 
1073  default:
1074  i->production_rate[this->editbox_line - IL_RATE1] = ClampU(RoundDivSU(value, 8), 0, 255);
1075  break;
1076  }
1077  UpdateIndustryProduction(i);
1078  this->SetDirty();
1079  }
1080 
1086  void OnInvalidateData(int data = 0, bool gui_scope = true) override
1087  {
1088  if (!gui_scope) return;
1089  const Industry *i = Industry::Get(this->window_number);
1090  if (IsProductionAlterable(i)) {
1091  const IndustrySpec *ind = GetIndustrySpec(i->type);
1092  this->editable = ind->UsesSmoothEconomy() ? EA_RATE : EA_MULTIPLIER;
1093  } else {
1094  this->editable = EA_NONE;
1095  }
1096  }
1097 
1098  bool IsNewGRFInspectable() const override
1099  {
1100  return ::IsNewGRFInspectable(GSF_INDUSTRIES, this->window_number);
1101  }
1102 
1103  void ShowNewGRFInspectWindow() const override
1104  {
1105  ::ShowNewGRFInspectWindow(GSF_INDUSTRIES, this->window_number);
1106  }
1107 };
1108 
1109 static void UpdateIndustryProduction(Industry *i)
1110 {
1111  const IndustrySpec *indspec = GetIndustrySpec(i->type);
1112  if (!indspec->UsesSmoothEconomy()) i->RecomputeProductionMultipliers();
1113 
1114  for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
1115  if (i->produced_cargo[j] != CT_INVALID) {
1116  i->last_month_production[j] = 8 * i->production_rate[j];
1117  }
1118  }
1119 }
1120 
1124  NWidget(WWT_CLOSEBOX, COLOUR_CREAM),
1125  NWidget(WWT_CAPTION, COLOUR_CREAM, WID_IV_CAPTION), SetDataTip(STR_INDUSTRY_VIEW_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1126  NWidget(WWT_DEBUGBOX, COLOUR_CREAM),
1127  NWidget(WWT_SHADEBOX, COLOUR_CREAM),
1128  NWidget(WWT_DEFSIZEBOX, COLOUR_CREAM),
1129  NWidget(WWT_STICKYBOX, COLOUR_CREAM),
1130  EndContainer(),
1131  NWidget(WWT_PANEL, COLOUR_CREAM),
1132  NWidget(WWT_INSET, COLOUR_CREAM), SetPadding(2, 2, 2, 2),
1133  NWidget(NWID_VIEWPORT, INVALID_COLOUR, WID_IV_VIEWPORT), SetMinimalSize(254, 86), SetFill(1, 0), SetPadding(1, 1, 1, 1), SetResize(1, 1),
1134  EndContainer(),
1135  EndContainer(),
1136  NWidget(WWT_PANEL, COLOUR_CREAM, WID_IV_INFO), SetMinimalSize(260, 2), SetResize(1, 0),
1137  EndContainer(),
1139  NWidget(WWT_PUSHTXTBTN, COLOUR_CREAM, WID_IV_GOTO), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_BUTTON_LOCATION, STR_INDUSTRY_VIEW_LOCATION_TOOLTIP),
1140  NWidget(WWT_PUSHTXTBTN, COLOUR_CREAM, WID_IV_DISPLAY), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_INDUSTRY_DISPLAY_CHAIN, STR_INDUSTRY_DISPLAY_CHAIN_TOOLTIP),
1141  NWidget(WWT_RESIZEBOX, COLOUR_CREAM),
1142  EndContainer(),
1143 };
1144 
1147  WDP_AUTO, "view_industry", 260, 120,
1149  0,
1150  _nested_industry_view_widgets, lengthof(_nested_industry_view_widgets)
1151 );
1152 
1153 void ShowIndustryViewWindow(int industry)
1154 {
1155  AllocateWindowDescFront<IndustryViewWindow>(&_industry_view_desc, industry);
1156 }
1157 
1161  NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
1162  NWidget(WWT_CAPTION, COLOUR_BROWN), SetDataTip(STR_INDUSTRY_DIRECTORY_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1163  NWidget(WWT_SHADEBOX, COLOUR_BROWN),
1164  NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
1165  NWidget(WWT_STICKYBOX, COLOUR_BROWN),
1166  EndContainer(),
1170  NWidget(WWT_TEXTBTN, COLOUR_BROWN, WID_ID_DROPDOWN_ORDER), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER),
1171  NWidget(WWT_DROPDOWN, COLOUR_BROWN, WID_ID_DROPDOWN_CRITERIA), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_SORT_CRITERIA),
1172  NWidget(WWT_PANEL, COLOUR_BROWN), SetResize(1, 0), EndContainer(),
1173  EndContainer(),
1174  NWidget(WWT_PANEL, COLOUR_BROWN, WID_ID_INDUSTRY_LIST), SetDataTip(0x0, STR_INDUSTRY_DIRECTORY_LIST_CAPTION), SetResize(1, 1), SetScrollbar(WID_ID_SCROLLBAR), EndContainer(),
1175  EndContainer(),
1177  NWidget(NWID_VSCROLLBAR, COLOUR_BROWN, WID_ID_SCROLLBAR),
1178  NWidget(WWT_RESIZEBOX, COLOUR_BROWN),
1179  EndContainer(),
1180  EndContainer(),
1181 };
1182 
1184 
1185 
1190 protected:
1191  /* Runtime saved values */
1192  static Listing last_sorting;
1193  static const Industry *last_industry;
1194 
1195  /* Constants for sorting stations */
1196  static const StringID sorter_names[];
1197  static GUIIndustryList::SortFunction * const sorter_funcs[];
1198 
1199  GUIIndustryList industries;
1200  Scrollbar *vscroll;
1201 
1204  {
1205  if (this->industries.NeedRebuild()) {
1206  this->industries.clear();
1207 
1208  const Industry *i;
1209  FOR_ALL_INDUSTRIES(i) {
1210  this->industries.push_back(i);
1211  }
1212 
1213  this->industries.shrink_to_fit();
1214  this->industries.RebuildDone();
1215  this->vscroll->SetCount((uint)this->industries.size()); // Update scrollbar as well.
1216  }
1217 
1218  if (!this->industries.Sort()) return;
1219  IndustryDirectoryWindow::last_industry = nullptr; // Reset name sorter sort cache
1220  this->SetWidgetDirty(WID_ID_INDUSTRY_LIST); // Set the modified widget dirty
1221  }
1222 
1230  static inline int GetCargoTransportedPercentsIfValid(const Industry *i, uint id)
1231  {
1232  assert(id < lengthof(i->produced_cargo));
1233 
1234  if (i->produced_cargo[id] == CT_INVALID) return 101;
1235  return ToPercent8(i->last_month_pct_transported[id]);
1236  }
1237 
1246  {
1247  int p1 = GetCargoTransportedPercentsIfValid(i, 0);
1248  int p2 = GetCargoTransportedPercentsIfValid(i, 1);
1249 
1250  if (p1 > p2) Swap(p1, p2); // lower value has higher priority
1251 
1252  return (p1 << 8) + p2;
1253  }
1254 
1256  static bool IndustryNameSorter(const Industry * const &a, const Industry * const &b)
1257  {
1258  static char buf_cache[96];
1259  static char buf[96];
1260 
1261  SetDParam(0, a->index);
1262  GetString(buf, STR_INDUSTRY_NAME, lastof(buf));
1263 
1264  if (b != last_industry) {
1265  last_industry = b;
1266  SetDParam(0, b->index);
1267  GetString(buf_cache, STR_INDUSTRY_NAME, lastof(buf_cache));
1268  }
1269 
1270  return strnatcmp(buf, buf_cache) < 0; // Sort by name (natural sorting).
1271  }
1272 
1274  static bool IndustryTypeSorter(const Industry * const &a, const Industry * const &b)
1275  {
1276  int it_a = 0;
1277  while (it_a != NUM_INDUSTRYTYPES && a->type != _sorted_industry_types[it_a]) it_a++;
1278  int it_b = 0;
1279  while (it_b != NUM_INDUSTRYTYPES && b->type != _sorted_industry_types[it_b]) it_b++;
1280  int r = it_a - it_b;
1281  return (r == 0) ? IndustryNameSorter(a, b) : r < 0;
1282  }
1283 
1285  static bool IndustryProductionSorter(const Industry * const &a, const Industry * const &b)
1286  {
1287  uint prod_a = 0, prod_b = 0;
1288  for (uint i = 0; i < lengthof(a->produced_cargo); i++) {
1289  if (a->produced_cargo[i] != CT_INVALID) prod_a += a->last_month_production[i];
1290  if (b->produced_cargo[i] != CT_INVALID) prod_b += b->last_month_production[i];
1291  }
1292  int r = prod_a - prod_b;
1293 
1294  return (r == 0) ? IndustryTypeSorter(a, b) : r < 0;
1295  }
1296 
1298  static bool IndustryTransportedCargoSorter(const Industry * const &a, const Industry * const &b)
1299  {
1300  int r = GetCargoTransportedSortValue(a) - GetCargoTransportedSortValue(b);
1301  return (r == 0) ? IndustryNameSorter(a, b) : r < 0;
1302  }
1303 
1310  {
1311  const IndustrySpec *indsp = GetIndustrySpec(i->type);
1312  byte p = 0;
1313 
1314  /* Industry name */
1315  SetDParam(p++, i->index);
1316 
1317  static CargoSuffix cargo_suffix[lengthof(i->produced_cargo)];
1318  GetAllCargoSuffixes(CARGOSUFFIX_OUT, CST_DIR, i, i->type, indsp, i->produced_cargo, cargo_suffix);
1319 
1320  /* Industry productions */
1321  for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
1322  if (i->produced_cargo[j] == CT_INVALID) continue;
1323  SetDParam(p++, i->produced_cargo[j]);
1324  SetDParam(p++, i->last_month_production[j]);
1325  SetDParamStr(p++, cargo_suffix[j].text);
1326  }
1327 
1328  /* Transported productions */
1329  for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
1330  if (i->produced_cargo[j] == CT_INVALID) continue;
1332  }
1333 
1334  /* Drawing the right string */
1335  switch (p) {
1336  case 1: return STR_INDUSTRY_DIRECTORY_ITEM_NOPROD;
1337  case 5: return STR_INDUSTRY_DIRECTORY_ITEM;
1338  default: return STR_INDUSTRY_DIRECTORY_ITEM_TWO;
1339  }
1340  }
1341 
1342 public:
1343  IndustryDirectoryWindow(WindowDesc *desc, WindowNumber number) : Window(desc)
1344  {
1345  this->CreateNestedTree();
1346  this->vscroll = this->GetScrollbar(WID_ID_SCROLLBAR);
1347 
1348  this->industries.SetListing(this->last_sorting);
1349  this->industries.SetSortFuncs(IndustryDirectoryWindow::sorter_funcs);
1350  this->industries.ForceRebuild();
1351  this->BuildSortIndustriesList();
1352 
1353  this->FinishInitNested(0);
1354  }
1355 
1357  {
1358  this->last_sorting = this->industries.GetListing();
1359  }
1360 
1361  void SetStringParameters(int widget) const override
1362  {
1363  if (widget == WID_ID_DROPDOWN_CRITERIA) SetDParam(0, IndustryDirectoryWindow::sorter_names[this->industries.SortType()]);
1364  }
1365 
1366  void DrawWidget(const Rect &r, int widget) const override
1367  {
1368  switch (widget) {
1369  case WID_ID_DROPDOWN_ORDER:
1370  this->DrawSortButtonState(widget, this->industries.IsDescSortOrder() ? SBS_DOWN : SBS_UP);
1371  break;
1372 
1373  case WID_ID_INDUSTRY_LIST: {
1374  int n = 0;
1375  int y = r.top + WD_FRAMERECT_TOP;
1376  if (this->industries.size() == 0) {
1377  DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_INDUSTRY_DIRECTORY_NONE);
1378  break;
1379  }
1380  for (uint i = this->vscroll->GetPosition(); i < this->industries.size(); i++) {
1381  DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, this->GetIndustryString(this->industries[i]));
1382 
1383  y += this->resize.step_height;
1384  if (++n == this->vscroll->GetCapacity()) break; // max number of industries in 1 window
1385  }
1386  break;
1387  }
1388  }
1389  }
1390 
1391  void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
1392  {
1393  switch (widget) {
1394  case WID_ID_DROPDOWN_ORDER: {
1395  Dimension d = GetStringBoundingBox(this->GetWidget<NWidgetCore>(widget)->widget_data);
1396  d.width += padding.width + Window::SortButtonWidth() * 2; // Doubled since the string is centred and it also looks better.
1397  d.height += padding.height;
1398  *size = maxdim(*size, d);
1399  break;
1400  }
1401 
1402  case WID_ID_DROPDOWN_CRITERIA: {
1403  Dimension d = {0, 0};
1404  for (uint i = 0; IndustryDirectoryWindow::sorter_names[i] != INVALID_STRING_ID; i++) {
1405  d = maxdim(d, GetStringBoundingBox(IndustryDirectoryWindow::sorter_names[i]));
1406  }
1407  d.width += padding.width;
1408  d.height += padding.height;
1409  *size = maxdim(*size, d);
1410  break;
1411  }
1412 
1413  case WID_ID_INDUSTRY_LIST: {
1414  Dimension d = GetStringBoundingBox(STR_INDUSTRY_DIRECTORY_NONE);
1415  for (uint i = 0; i < this->industries.size(); i++) {
1416  d = maxdim(d, GetStringBoundingBox(this->GetIndustryString(this->industries[i])));
1417  }
1418  resize->height = d.height;
1419  d.height *= 5;
1420  d.width += padding.width + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
1421  d.height += padding.height + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
1422  *size = maxdim(*size, d);
1423  break;
1424  }
1425  }
1426  }
1427 
1428 
1429  void OnClick(Point pt, int widget, int click_count) override
1430  {
1431  switch (widget) {
1432  case WID_ID_DROPDOWN_ORDER:
1433  this->industries.ToggleSortOrder();
1434  this->SetDirty();
1435  break;
1436 
1438  ShowDropDownMenu(this, IndustryDirectoryWindow::sorter_names, this->industries.SortType(), WID_ID_DROPDOWN_CRITERIA, 0, 0);
1439  break;
1440 
1441  case WID_ID_INDUSTRY_LIST: {
1442  uint p = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_ID_INDUSTRY_LIST, WD_FRAMERECT_TOP);
1443  if (p < this->industries.size()) {
1444  if (_ctrl_pressed) {
1445  ShowExtraViewPortWindow(this->industries[p]->location.tile);
1446  } else {
1447  ScrollMainWindowToTile(this->industries[p]->location.tile);
1448  }
1449  }
1450  break;
1451  }
1452  }
1453  }
1454 
1455  void OnDropdownSelect(int widget, int index) override
1456  {
1457  if (this->industries.SortType() != index) {
1458  this->industries.SetSortType(index);
1459  this->BuildSortIndustriesList();
1460  }
1461  }
1462 
1463  void OnResize() override
1464  {
1465  this->vscroll->SetCapacityFromWidget(this, WID_ID_INDUSTRY_LIST);
1466  }
1467 
1468  void OnPaint() override
1469  {
1470  if (this->industries.NeedRebuild()) this->BuildSortIndustriesList();
1471  this->DrawWidgets();
1472  }
1473 
1474  void OnHundredthTick() override
1475  {
1476  this->industries.ForceResort();
1477  this->BuildSortIndustriesList();
1478  }
1479 
1485  void OnInvalidateData(int data = 0, bool gui_scope = true) override
1486  {
1487  if (data == 0) {
1488  /* This needs to be done in command-scope to enforce rebuilding before resorting invalid data */
1489  this->industries.ForceRebuild();
1490  } else {
1491  this->industries.ForceResort();
1492  }
1493  }
1494 };
1495 
1496 Listing IndustryDirectoryWindow::last_sorting = {false, 0};
1497 const Industry *IndustryDirectoryWindow::last_industry = nullptr;
1498 
1499 /* Available station sorting functions. */
1500 GUIIndustryList::SortFunction * const IndustryDirectoryWindow::sorter_funcs[] = {
1501  &IndustryNameSorter,
1502  &IndustryTypeSorter,
1503  &IndustryProductionSorter,
1504  &IndustryTransportedCargoSorter
1505 };
1506 
1507 /* Names of the sorting functions */
1508 const StringID IndustryDirectoryWindow::sorter_names[] = {
1509  STR_SORT_BY_NAME,
1510  STR_SORT_BY_TYPE,
1511  STR_SORT_BY_PRODUCTION,
1512  STR_SORT_BY_TRANSPORTED,
1514 };
1515 
1516 
1519  WDP_AUTO, "list_industries", 428, 190,
1521  0,
1522  _nested_industry_directory_widgets, lengthof(_nested_industry_directory_widgets)
1523 );
1524 
1525 void ShowIndustryDirectory()
1526 {
1527  AllocateWindowDescFront<IndustryDirectoryWindow>(&_industry_directory_desc, 0);
1528 }
1529 
1533  NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
1534  NWidget(WWT_CAPTION, COLOUR_BROWN, WID_IC_CAPTION), SetDataTip(STR_INDUSTRY_CARGOES_INDUSTRY_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1535  NWidget(WWT_SHADEBOX, COLOUR_BROWN),
1536  NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
1537  NWidget(WWT_STICKYBOX, COLOUR_BROWN),
1538  EndContainer(),
1543  NWidget(WWT_TEXTBTN, COLOUR_BROWN, WID_IC_NOTIFY),
1544  SetDataTip(STR_INDUSTRY_CARGOES_NOTIFY_SMALLMAP, STR_INDUSTRY_CARGOES_NOTIFY_SMALLMAP_TOOLTIP),
1545  NWidget(WWT_PANEL, COLOUR_BROWN), SetFill(1, 0), SetResize(0, 0), EndContainer(),
1546  NWidget(WWT_DROPDOWN, COLOUR_BROWN, WID_IC_IND_DROPDOWN), SetFill(0, 0), SetResize(0, 0),
1547  SetDataTip(STR_INDUSTRY_CARGOES_SELECT_INDUSTRY, STR_INDUSTRY_CARGOES_SELECT_INDUSTRY_TOOLTIP),
1548  NWidget(WWT_DROPDOWN, COLOUR_BROWN, WID_IC_CARGO_DROPDOWN), SetFill(0, 0), SetResize(0, 0),
1549  SetDataTip(STR_INDUSTRY_CARGOES_SELECT_CARGO, STR_INDUSTRY_CARGOES_SELECT_CARGO_TOOLTIP),
1550  EndContainer(),
1551  EndContainer(),
1553  NWidget(NWID_VSCROLLBAR, COLOUR_BROWN, WID_IC_SCROLLBAR),
1554  NWidget(WWT_RESIZEBOX, COLOUR_BROWN),
1555  EndContainer(),
1556  EndContainer(),
1557 };
1558 
1561  WDP_AUTO, "industry_cargoes", 300, 210,
1563  0,
1564  _nested_industry_cargoes_widgets, lengthof(_nested_industry_cargoes_widgets)
1565 );
1566 
1575 };
1576 
1577 static const uint MAX_CARGOES = 16;
1578 
1581  static const int VERT_INTER_INDUSTRY_SPACE;
1582  static const int HOR_CARGO_BORDER_SPACE;
1583  static const int CARGO_STUB_WIDTH;
1584  static const int HOR_CARGO_WIDTH, HOR_CARGO_SPACE;
1585  static const int VERT_CARGO_SPACE, VERT_CARGO_EDGE;
1586  static const int BLOB_DISTANCE, BLOB_WIDTH, BLOB_HEIGHT;
1587 
1588  static const int INDUSTRY_LINE_COLOUR;
1589  static const int CARGO_LINE_COLOUR;
1590 
1591  static int small_height, normal_height;
1592  static int cargo_field_width;
1593  static int industry_width;
1594  static uint max_cargoes;
1595 
1597  union {
1598  struct {
1599  IndustryType ind_type;
1600  CargoID other_produced[MAX_CARGOES];
1601  CargoID other_accepted[MAX_CARGOES];
1602  } industry;
1603  struct {
1604  CargoID vertical_cargoes[MAX_CARGOES];
1606  CargoID supp_cargoes[MAX_CARGOES];
1607  byte top_end;
1608  CargoID cust_cargoes[MAX_CARGOES];
1609  byte bottom_end;
1610  } cargo;
1611  struct {
1613  bool left_align;
1614  } cargo_label;
1616  } u; // Data for each type.
1617 
1623  {
1624  this->type = type;
1625  }
1626 
1632  void MakeIndustry(IndustryType ind_type)
1633  {
1634  this->type = CFT_INDUSTRY;
1635  this->u.industry.ind_type = ind_type;
1636  MemSetT(this->u.industry.other_accepted, INVALID_CARGO, MAX_CARGOES);
1637  MemSetT(this->u.industry.other_produced, INVALID_CARGO, MAX_CARGOES);
1638  }
1639 
1646  int ConnectCargo(CargoID cargo, bool producer)
1647  {
1648  assert(this->type == CFT_CARGO);
1649  if (cargo == INVALID_CARGO) return -1;
1650 
1651  /* Find the vertical cargo column carrying the cargo. */
1652  int column = -1;
1653  for (int i = 0; i < this->u.cargo.num_cargoes; i++) {
1654  if (cargo == this->u.cargo.vertical_cargoes[i]) {
1655  column = i;
1656  break;
1657  }
1658  }
1659  if (column < 0) return -1;
1660 
1661  if (producer) {
1662  assert(this->u.cargo.supp_cargoes[column] == INVALID_CARGO);
1663  this->u.cargo.supp_cargoes[column] = column;
1664  } else {
1665  assert(this->u.cargo.cust_cargoes[column] == INVALID_CARGO);
1666  this->u.cargo.cust_cargoes[column] = column;
1667  }
1668  return column;
1669  }
1670 
1676  {
1677  assert(this->type == CFT_CARGO);
1678 
1679  for (uint i = 0; i < MAX_CARGOES; i++) {
1680  if (this->u.cargo.supp_cargoes[i] != INVALID_CARGO) return true;
1681  if (this->u.cargo.cust_cargoes[i] != INVALID_CARGO) return true;
1682  }
1683  return false;
1684  }
1685 
1695  void MakeCargo(const CargoID *cargoes, uint length, int count = -1, bool top_end = false, bool bottom_end = false)
1696  {
1697  this->type = CFT_CARGO;
1698  uint i;
1699  uint num = 0;
1700  for (i = 0; i < MAX_CARGOES && i < length; i++) {
1701  if (cargoes[i] != INVALID_CARGO) {
1702  this->u.cargo.vertical_cargoes[num] = cargoes[i];
1703  num++;
1704  }
1705  }
1706  this->u.cargo.num_cargoes = (count < 0) ? num : count;
1707  for (; num < MAX_CARGOES; num++) this->u.cargo.vertical_cargoes[num] = INVALID_CARGO;
1708  this->u.cargo.top_end = top_end;
1709  this->u.cargo.bottom_end = bottom_end;
1710  MemSetT(this->u.cargo.supp_cargoes, INVALID_CARGO, MAX_CARGOES);
1711  MemSetT(this->u.cargo.cust_cargoes, INVALID_CARGO, MAX_CARGOES);
1712  }
1713 
1720  void MakeCargoLabel(const CargoID *cargoes, uint length, bool left_align)
1721  {
1722  this->type = CFT_CARGO_LABEL;
1723  uint i;
1724  for (i = 0; i < MAX_CARGOES && i < length; i++) this->u.cargo_label.cargoes[i] = cargoes[i];
1725  for (; i < MAX_CARGOES; i++) this->u.cargo_label.cargoes[i] = INVALID_CARGO;
1726  this->u.cargo_label.left_align = left_align;
1727  }
1728 
1733  void MakeHeader(StringID textid)
1734  {
1735  this->type = CFT_HEADER;
1736  this->u.header = textid;
1737  }
1738 
1744  int GetCargoBase(int xpos) const
1745  {
1746  assert(this->type == CFT_CARGO);
1747  int n = this->u.cargo.num_cargoes;
1748 
1749  if (n % 2 == 0) {
1750  return xpos + cargo_field_width / 2 - (HOR_CARGO_WIDTH + HOR_CARGO_SPACE / 2) * (n / 2);
1751  } else {
1752  return xpos + cargo_field_width / 2 - HOR_CARGO_WIDTH / 2 - (HOR_CARGO_WIDTH + HOR_CARGO_SPACE) * (n / 2);
1753  }
1754  }
1755 
1761  void Draw(int xpos, int ypos) const
1762  {
1763  switch (this->type) {
1764  case CFT_EMPTY:
1765  case CFT_SMALL_EMPTY:
1766  break;
1767 
1768  case CFT_HEADER:
1769  ypos += (small_height - FONT_HEIGHT_NORMAL) / 2;
1770  DrawString(xpos, xpos + industry_width, ypos, this->u.header, TC_WHITE, SA_HOR_CENTER);
1771  break;
1772 
1773  case CFT_INDUSTRY: {
1774  int ypos1 = ypos + VERT_INTER_INDUSTRY_SPACE / 2;
1775  int ypos2 = ypos + normal_height - 1 - VERT_INTER_INDUSTRY_SPACE / 2;
1776  int xpos2 = xpos + industry_width - 1;
1777  GfxDrawLine(xpos, ypos1, xpos2, ypos1, INDUSTRY_LINE_COLOUR);
1778  GfxDrawLine(xpos, ypos1, xpos, ypos2, INDUSTRY_LINE_COLOUR);
1779  GfxDrawLine(xpos, ypos2, xpos2, ypos2, INDUSTRY_LINE_COLOUR);
1780  GfxDrawLine(xpos2, ypos1, xpos2, ypos2, INDUSTRY_LINE_COLOUR);
1781  ypos += (normal_height - FONT_HEIGHT_NORMAL) / 2;
1782  if (this->u.industry.ind_type < NUM_INDUSTRYTYPES) {
1783  const IndustrySpec *indsp = GetIndustrySpec(this->u.industry.ind_type);
1784  DrawString(xpos, xpos2, ypos, indsp->name, TC_WHITE, SA_HOR_CENTER);
1785 
1786  /* Draw the industry legend. */
1787  int blob_left, blob_right;
1788  if (_current_text_dir == TD_RTL) {
1789  blob_right = xpos2 - BLOB_DISTANCE;
1790  blob_left = blob_right - BLOB_WIDTH;
1791  } else {
1792  blob_left = xpos + BLOB_DISTANCE;
1793  blob_right = blob_left + BLOB_WIDTH;
1794  }
1795  GfxFillRect(blob_left, ypos2 - BLOB_DISTANCE - BLOB_HEIGHT, blob_right, ypos2 - BLOB_DISTANCE, PC_BLACK); // Border
1796  GfxFillRect(blob_left + 1, ypos2 - BLOB_DISTANCE - BLOB_HEIGHT + 1, blob_right - 1, ypos2 - BLOB_DISTANCE - 1, indsp->map_colour);
1797  } else {
1798  DrawString(xpos, xpos2, ypos, STR_INDUSTRY_CARGOES_HOUSES, TC_FROMSTRING, SA_HOR_CENTER);
1799  }
1800 
1801  /* Draw the other_produced/other_accepted cargoes. */
1802  const CargoID *other_right, *other_left;
1803  if (_current_text_dir == TD_RTL) {
1804  other_right = this->u.industry.other_accepted;
1805  other_left = this->u.industry.other_produced;
1806  } else {
1807  other_right = this->u.industry.other_produced;
1808  other_left = this->u.industry.other_accepted;
1809  }
1810  ypos1 += VERT_CARGO_EDGE;
1811  for (uint i = 0; i < CargoesField::max_cargoes; i++) {
1812  if (other_right[i] != INVALID_CARGO) {
1813  const CargoSpec *csp = CargoSpec::Get(other_right[i]);
1814  int xp = xpos + industry_width + CARGO_STUB_WIDTH;
1815  DrawHorConnection(xpos + industry_width, xp - 1, ypos1, csp);
1816  GfxDrawLine(xp, ypos1, xp, ypos1 + FONT_HEIGHT_NORMAL - 1, CARGO_LINE_COLOUR);
1817  }
1818  if (other_left[i] != INVALID_CARGO) {
1819  const CargoSpec *csp = CargoSpec::Get(other_left[i]);
1820  int xp = xpos - CARGO_STUB_WIDTH;
1821  DrawHorConnection(xp + 1, xpos - 1, ypos1, csp);
1822  GfxDrawLine(xp, ypos1, xp, ypos1 + FONT_HEIGHT_NORMAL - 1, CARGO_LINE_COLOUR);
1823  }
1824  ypos1 += FONT_HEIGHT_NORMAL + VERT_CARGO_SPACE;
1825  }
1826  break;
1827  }
1828 
1829  case CFT_CARGO: {
1830  int cargo_base = this->GetCargoBase(xpos);
1831  int top = ypos + (this->u.cargo.top_end ? VERT_INTER_INDUSTRY_SPACE / 2 + 1 : 0);
1832  int bot = ypos - (this->u.cargo.bottom_end ? VERT_INTER_INDUSTRY_SPACE / 2 + 1 : 0) + normal_height - 1;
1833  int colpos = cargo_base;
1834  for (int i = 0; i < this->u.cargo.num_cargoes; i++) {
1835  if (this->u.cargo.top_end) GfxDrawLine(colpos, top - 1, colpos + HOR_CARGO_WIDTH - 1, top - 1, CARGO_LINE_COLOUR);
1836  if (this->u.cargo.bottom_end) GfxDrawLine(colpos, bot + 1, colpos + HOR_CARGO_WIDTH - 1, bot + 1, CARGO_LINE_COLOUR);
1837  GfxDrawLine(colpos, top, colpos, bot, CARGO_LINE_COLOUR);
1838  colpos++;
1839  const CargoSpec *csp = CargoSpec::Get(this->u.cargo.vertical_cargoes[i]);
1840  GfxFillRect(colpos, top, colpos + HOR_CARGO_WIDTH - 2, bot, csp->legend_colour, FILLRECT_OPAQUE);
1841  colpos += HOR_CARGO_WIDTH - 2;
1842  GfxDrawLine(colpos, top, colpos, bot, CARGO_LINE_COLOUR);
1843  colpos += 1 + HOR_CARGO_SPACE;
1844  }
1845 
1846  const CargoID *hor_left, *hor_right;
1847  if (_current_text_dir == TD_RTL) {
1848  hor_left = this->u.cargo.cust_cargoes;
1849  hor_right = this->u.cargo.supp_cargoes;
1850  } else {
1851  hor_left = this->u.cargo.supp_cargoes;
1852  hor_right = this->u.cargo.cust_cargoes;
1853  }
1854  ypos += VERT_CARGO_EDGE + VERT_INTER_INDUSTRY_SPACE / 2;
1855  for (uint i = 0; i < MAX_CARGOES; i++) {
1856  if (hor_left[i] != INVALID_CARGO) {
1857  int col = hor_left[i];
1858  int dx = 0;
1859  const CargoSpec *csp = CargoSpec::Get(this->u.cargo.vertical_cargoes[col]);
1860  for (; col > 0; col--) {
1861  int lf = cargo_base + col * HOR_CARGO_WIDTH + (col - 1) * HOR_CARGO_SPACE;
1862  DrawHorConnection(lf, lf + HOR_CARGO_SPACE - dx, ypos, csp);
1863  dx = 1;
1864  }
1865  DrawHorConnection(xpos, cargo_base - dx, ypos, csp);
1866  }
1867  if (hor_right[i] != INVALID_CARGO) {
1868  int col = hor_right[i];
1869  int dx = 0;
1870  const CargoSpec *csp = CargoSpec::Get(this->u.cargo.vertical_cargoes[col]);
1871  for (; col < this->u.cargo.num_cargoes - 1; col++) {
1872  int lf = cargo_base + (col + 1) * HOR_CARGO_WIDTH + col * HOR_CARGO_SPACE;
1873  DrawHorConnection(lf + dx - 1, lf + HOR_CARGO_SPACE - 1, ypos, csp);
1874  dx = 1;
1875  }
1876  DrawHorConnection(cargo_base + col * HOR_CARGO_SPACE + (col + 1) * HOR_CARGO_WIDTH - 1 + dx, xpos + CargoesField::cargo_field_width - 1, ypos, csp);
1877  }
1878  ypos += FONT_HEIGHT_NORMAL + VERT_CARGO_SPACE;
1879  }
1880  break;
1881  }
1882 
1883  case CFT_CARGO_LABEL:
1884  ypos += VERT_CARGO_EDGE + VERT_INTER_INDUSTRY_SPACE / 2;
1885  for (uint i = 0; i < MAX_CARGOES; i++) {
1886  if (this->u.cargo_label.cargoes[i] != INVALID_CARGO) {
1887  const CargoSpec *csp = CargoSpec::Get(this->u.cargo_label.cargoes[i]);
1888  DrawString(xpos + WD_FRAMERECT_LEFT, xpos + industry_width - 1 - WD_FRAMERECT_RIGHT, ypos, csp->name, TC_WHITE,
1889  (this->u.cargo_label.left_align) ? SA_LEFT : SA_RIGHT);
1890  }
1891  ypos += FONT_HEIGHT_NORMAL + VERT_CARGO_SPACE;
1892  }
1893  break;
1894 
1895  default:
1896  NOT_REACHED();
1897  }
1898  }
1899 
1907  CargoID CargoClickedAt(const CargoesField *left, const CargoesField *right, Point pt) const
1908  {
1909  assert(this->type == CFT_CARGO);
1910 
1911  /* Vertical matching. */
1912  int cpos = this->GetCargoBase(0);
1913  uint col;
1914  for (col = 0; col < this->u.cargo.num_cargoes; col++) {
1915  if (pt.x < cpos) break;
1916  if (pt.x < cpos + CargoesField::HOR_CARGO_WIDTH) return this->u.cargo.vertical_cargoes[col];
1918  }
1919  /* col = 0 -> left of first col, 1 -> left of 2nd col, ... this->u.cargo.num_cargoes right of last-col. */
1920 
1921  int vpos = VERT_INTER_INDUSTRY_SPACE / 2 + VERT_CARGO_EDGE;
1922  uint row;
1923  for (row = 0; row < MAX_CARGOES; row++) {
1924  if (pt.y < vpos) return INVALID_CARGO;
1925  if (pt.y < vpos + FONT_HEIGHT_NORMAL) break;
1926  vpos += FONT_HEIGHT_NORMAL + VERT_CARGO_SPACE;
1927  }
1928  if (row == MAX_CARGOES) return INVALID_CARGO;
1929 
1930  /* row = 0 -> at first horizontal row, row = 1 -> second horizontal row, 2 = 3rd horizontal row. */
1931  if (col == 0) {
1932  if (this->u.cargo.supp_cargoes[row] != INVALID_CARGO) return this->u.cargo.vertical_cargoes[this->u.cargo.supp_cargoes[row]];
1933  if (left != nullptr) {
1934  if (left->type == CFT_INDUSTRY) return left->u.industry.other_produced[row];
1935  if (left->type == CFT_CARGO_LABEL && !left->u.cargo_label.left_align) return left->u.cargo_label.cargoes[row];
1936  }
1937  return INVALID_CARGO;
1938  }
1939  if (col == this->u.cargo.num_cargoes) {
1940  if (this->u.cargo.cust_cargoes[row] != INVALID_CARGO) return this->u.cargo.vertical_cargoes[this->u.cargo.cust_cargoes[row]];
1941  if (right != nullptr) {
1942  if (right->type == CFT_INDUSTRY) return right->u.industry.other_accepted[row];
1943  if (right->type == CFT_CARGO_LABEL && right->u.cargo_label.left_align) return right->u.cargo_label.cargoes[row];
1944  }
1945  return INVALID_CARGO;
1946  }
1947  if (row >= col) {
1948  /* Clicked somewhere in-between vertical cargo connection.
1949  * Since the horizontal connection is made in the same order as the vertical list, the above condition
1950  * ensures we are left-below the main diagonal, thus at the supplying side.
1951  */
1952  return (this->u.cargo.supp_cargoes[row] != INVALID_CARGO) ? this->u.cargo.vertical_cargoes[this->u.cargo.supp_cargoes[row]] : INVALID_CARGO;
1953  } else {
1954  /* Clicked at a customer connection. */
1955  return (this->u.cargo.cust_cargoes[row] != INVALID_CARGO) ? this->u.cargo.vertical_cargoes[this->u.cargo.cust_cargoes[row]] : INVALID_CARGO;
1956  }
1957  }
1958 
1965  {
1966  assert(this->type == CFT_CARGO_LABEL);
1967 
1968  int vpos = VERT_INTER_INDUSTRY_SPACE / 2 + VERT_CARGO_EDGE;
1969  uint row;
1970  for (row = 0; row < MAX_CARGOES; row++) {
1971  if (pt.y < vpos) return INVALID_CARGO;
1972  if (pt.y < vpos + FONT_HEIGHT_NORMAL) break;
1973  vpos += FONT_HEIGHT_NORMAL + VERT_CARGO_SPACE;
1974  }
1975  if (row == MAX_CARGOES) return INVALID_CARGO;
1976  return this->u.cargo_label.cargoes[row];
1977  }
1978 
1979 private:
1987  static void DrawHorConnection(int left, int right, int top, const CargoSpec *csp)
1988  {
1989  GfxDrawLine(left, top, right, top, CARGO_LINE_COLOUR);
1990  GfxFillRect(left, top + 1, right, top + FONT_HEIGHT_NORMAL - 2, csp->legend_colour, FILLRECT_OPAQUE);
1991  GfxDrawLine(left, top + FONT_HEIGHT_NORMAL - 1, right, top + FONT_HEIGHT_NORMAL - 1, CARGO_LINE_COLOUR);
1992  }
1993 };
1994 
1995 assert_compile(MAX_CARGOES >= cpp_lengthof(IndustrySpec, produced_cargo));
1996 assert_compile(MAX_CARGOES >= cpp_lengthof(IndustrySpec, accepts_cargo));
1997 
2004 
2005 const int CargoesField::HOR_CARGO_BORDER_SPACE = 15;
2006 const int CargoesField::CARGO_STUB_WIDTH = 10;
2007 const int CargoesField::HOR_CARGO_WIDTH = 15;
2008 const int CargoesField::HOR_CARGO_SPACE = 5;
2009 const int CargoesField::VERT_CARGO_EDGE = 4;
2010 const int CargoesField::VERT_CARGO_SPACE = 4;
2011 
2012 const int CargoesField::BLOB_DISTANCE = 5;
2013 const int CargoesField::BLOB_WIDTH = 12;
2014 const int CargoesField::BLOB_HEIGHT = 9;
2015 
2018 
2020 struct CargoesRow {
2021  CargoesField columns[5];
2022 
2027  void ConnectIndustryProduced(int column)
2028  {
2029  CargoesField *ind_fld = this->columns + column;
2030  CargoesField *cargo_fld = this->columns + column + 1;
2031  assert(ind_fld->type == CFT_INDUSTRY && cargo_fld->type == CFT_CARGO);
2032 
2033  MemSetT(ind_fld->u.industry.other_produced, INVALID_CARGO, MAX_CARGOES);
2034 
2035  if (ind_fld->u.industry.ind_type < NUM_INDUSTRYTYPES) {
2036  CargoID others[MAX_CARGOES]; // Produced cargoes not carried in the cargo column.
2037  int other_count = 0;
2038 
2039  const IndustrySpec *indsp = GetIndustrySpec(ind_fld->u.industry.ind_type);
2040  assert(CargoesField::max_cargoes <= lengthof(indsp->produced_cargo));
2041  for (uint i = 0; i < CargoesField::max_cargoes; i++) {
2042  int col = cargo_fld->ConnectCargo(indsp->produced_cargo[i], true);
2043  if (col < 0) others[other_count++] = indsp->produced_cargo[i];
2044  }
2045 
2046  /* Allocate other cargoes in the empty holes of the horizontal cargo connections. */
2047  for (uint i = 0; i < CargoesField::max_cargoes && other_count > 0; i++) {
2048  if (cargo_fld->u.cargo.supp_cargoes[i] == INVALID_CARGO) ind_fld->u.industry.other_produced[i] = others[--other_count];
2049  }
2050  } else {
2051  /* Houses only display what is demanded. */
2052  for (uint i = 0; i < cargo_fld->u.cargo.num_cargoes; i++) {
2053  CargoID cid = cargo_fld->u.cargo.vertical_cargoes[i];
2054  if (cid == CT_PASSENGERS || cid == CT_MAIL) cargo_fld->ConnectCargo(cid, true);
2055  }
2056  }
2057  }
2058 
2064  void MakeCargoLabel(int column, bool accepting)
2065  {
2066  CargoID cargoes[MAX_CARGOES];
2067  MemSetT(cargoes, INVALID_CARGO, lengthof(cargoes));
2068 
2069  CargoesField *label_fld = this->columns + column;
2070  CargoesField *cargo_fld = this->columns + (accepting ? column - 1 : column + 1);
2071 
2072  assert(cargo_fld->type == CFT_CARGO && label_fld->type == CFT_EMPTY);
2073  for (uint i = 0; i < cargo_fld->u.cargo.num_cargoes; i++) {
2074  int col = cargo_fld->ConnectCargo(cargo_fld->u.cargo.vertical_cargoes[i], !accepting);
2075  if (col >= 0) cargoes[col] = cargo_fld->u.cargo.vertical_cargoes[i];
2076  }
2077  label_fld->MakeCargoLabel(cargoes, lengthof(cargoes), accepting);
2078  }
2079 
2080 
2085  void ConnectIndustryAccepted(int column)
2086  {
2087  CargoesField *ind_fld = this->columns + column;
2088  CargoesField *cargo_fld = this->columns + column - 1;
2089  assert(ind_fld->type == CFT_INDUSTRY && cargo_fld->type == CFT_CARGO);
2090 
2091  MemSetT(ind_fld->u.industry.other_accepted, INVALID_CARGO, MAX_CARGOES);
2092 
2093  if (ind_fld->u.industry.ind_type < NUM_INDUSTRYTYPES) {
2094  CargoID others[MAX_CARGOES]; // Accepted cargoes not carried in the cargo column.
2095  int other_count = 0;
2096 
2097  const IndustrySpec *indsp = GetIndustrySpec(ind_fld->u.industry.ind_type);
2099  for (uint i = 0; i < CargoesField::max_cargoes; i++) {
2100  int col = cargo_fld->ConnectCargo(indsp->accepts_cargo[i], false);
2101  if (col < 0) others[other_count++] = indsp->accepts_cargo[i];
2102  }
2103 
2104  /* Allocate other cargoes in the empty holes of the horizontal cargo connections. */
2105  for (uint i = 0; i < CargoesField::max_cargoes && other_count > 0; i++) {
2106  if (cargo_fld->u.cargo.cust_cargoes[i] == INVALID_CARGO) ind_fld->u.industry.other_accepted[i] = others[--other_count];
2107  }
2108  } else {
2109  /* Houses only display what is demanded. */
2110  for (uint i = 0; i < cargo_fld->u.cargo.num_cargoes; i++) {
2111  for (uint h = 0; h < NUM_HOUSES; h++) {
2112  HouseSpec *hs = HouseSpec::Get(h);
2113  if (!hs->enabled) continue;
2114 
2115  for (uint j = 0; j < lengthof(hs->accepts_cargo); j++) {
2116  if (hs->cargo_acceptance[j] > 0 && cargo_fld->u.cargo.vertical_cargoes[i] == hs->accepts_cargo[j]) {
2117  cargo_fld->ConnectCargo(cargo_fld->u.cargo.vertical_cargoes[i], false);
2118  goto next_cargo;
2119  }
2120  }
2121  }
2122 next_cargo: ;
2123  }
2124  }
2125  }
2126 };
2127 
2128 
2157  static const int HOR_TEXT_PADDING, VERT_TEXT_PADDING;
2158 
2159  typedef std::vector<CargoesRow> Fields;
2160 
2161  Fields fields;
2162  uint ind_cargo;
2165  Scrollbar *vscroll;
2166 
2167  IndustryCargoesWindow(int id) : Window(&_industry_cargoes_desc)
2168  {
2169  this->OnInit();
2170  this->CreateNestedTree();
2171  this->vscroll = this->GetScrollbar(WID_IC_SCROLLBAR);
2172  this->FinishInitNested(0);
2173  this->OnInvalidateData(id);
2174  }
2175 
2176  void OnInit() override
2177  {
2178  /* Initialize static CargoesField size variables. */
2179  Dimension d = GetStringBoundingBox(STR_INDUSTRY_CARGOES_PRODUCERS);
2180  d = maxdim(d, GetStringBoundingBox(STR_INDUSTRY_CARGOES_CUSTOMERS));
2182  d.height += WD_FRAMETEXT_TOP + WD_FRAMETEXT_BOTTOM;
2183  CargoesField::small_height = d.height;
2184 
2185  /* Decide about the size of the box holding the text of an industry type. */
2186  this->ind_textsize.width = 0;
2187  this->ind_textsize.height = 0;
2189  for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
2190  const IndustrySpec *indsp = GetIndustrySpec(it);
2191  if (!indsp->enabled) continue;
2192  this->ind_textsize = maxdim(this->ind_textsize, GetStringBoundingBox(indsp->name));
2194  CargoesField::max_cargoes = max<uint>(CargoesField::max_cargoes, std::count_if(indsp->produced_cargo, endof(indsp->produced_cargo), IsCargoIDValid));
2195  }
2196  d.width = max(d.width, this->ind_textsize.width);
2197  d.height = this->ind_textsize.height;
2198  this->ind_textsize = maxdim(this->ind_textsize, GetStringBoundingBox(STR_INDUSTRY_CARGOES_SELECT_INDUSTRY));
2199 
2200  /* Compute max size of the cargo texts. */
2201  this->cargo_textsize.width = 0;
2202  this->cargo_textsize.height = 0;
2203  for (uint i = 0; i < NUM_CARGO; i++) {
2204  const CargoSpec *csp = CargoSpec::Get(i);
2205  if (!csp->IsValid()) continue;
2206  this->cargo_textsize = maxdim(this->cargo_textsize, GetStringBoundingBox(csp->name));
2207  }
2208  d = maxdim(d, this->cargo_textsize); // Box must also be wide enough to hold any cargo label.
2209  this->cargo_textsize = maxdim(this->cargo_textsize, GetStringBoundingBox(STR_INDUSTRY_CARGOES_SELECT_CARGO));
2210 
2211  d.width += 2 * HOR_TEXT_PADDING;
2212  /* Ensure the height is enough for the industry type text, for the horizontal connections, and for the cargo labels. */
2214  d.height = max(d.height + 2 * VERT_TEXT_PADDING, min_ind_height);
2215 
2216  CargoesField::industry_width = d.width;
2218 
2219  /* Width of a #CFT_CARGO field. */
2221  }
2222 
2223  void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
2224  {
2225  switch (widget) {
2226  case WID_IC_PANEL:
2228  break;
2229 
2230  case WID_IC_IND_DROPDOWN:
2231  size->width = max(size->width, this->ind_textsize.width + padding.width);
2232  break;
2233 
2234  case WID_IC_CARGO_DROPDOWN:
2235  size->width = max(size->width, this->cargo_textsize.width + padding.width);
2236  break;
2237  }
2238  }
2239 
2240 
2242  void SetStringParameters (int widget) const override
2243  {
2244  if (widget != WID_IC_CAPTION) return;
2245 
2246  if (this->ind_cargo < NUM_INDUSTRYTYPES) {
2247  const IndustrySpec *indsp = GetIndustrySpec(this->ind_cargo);
2248  SetDParam(0, indsp->name);
2249  } else {
2250  const CargoSpec *csp = CargoSpec::Get(this->ind_cargo - NUM_INDUSTRYTYPES);
2251  SetDParam(0, csp->name);
2252  }
2253  }
2254 
2263  static bool HasCommonValidCargo(const CargoID *cargoes1, uint length1, const CargoID *cargoes2, uint length2)
2264  {
2265  while (length1 > 0) {
2266  if (*cargoes1 != INVALID_CARGO) {
2267  for (uint i = 0; i < length2; i++) if (*cargoes1 == cargoes2[i]) return true;
2268  }
2269  cargoes1++;
2270  length1--;
2271  }
2272  return false;
2273  }
2274 
2281  static bool HousesCanSupply(const CargoID *cargoes, uint length)
2282  {
2283  for (uint i = 0; i < length; i++) {
2284  if (cargoes[i] == INVALID_CARGO) continue;
2285  if (cargoes[i] == CT_PASSENGERS || cargoes[i] == CT_MAIL) return true;
2286  }
2287  return false;
2288  }
2289 
2296  static bool HousesCanAccept(const CargoID *cargoes, uint length)
2297  {
2298  HouseZones climate_mask;
2300  case LT_TEMPERATE: climate_mask = HZ_TEMP; break;
2301  case LT_ARCTIC: climate_mask = HZ_SUBARTC_ABOVE | HZ_SUBARTC_BELOW; break;
2302  case LT_TROPIC: climate_mask = HZ_SUBTROPIC; break;
2303  case LT_TOYLAND: climate_mask = HZ_TOYLND; break;
2304  default: NOT_REACHED();
2305  }
2306  for (uint i = 0; i < length; i++) {
2307  if (cargoes[i] == INVALID_CARGO) continue;
2308 
2309  for (uint h = 0; h < NUM_HOUSES; h++) {
2310  HouseSpec *hs = HouseSpec::Get(h);
2311  if (!hs->enabled || !(hs->building_availability & climate_mask)) continue;
2312 
2313  for (uint j = 0; j < lengthof(hs->accepts_cargo); j++) {
2314  if (hs->cargo_acceptance[j] > 0 && cargoes[i] == hs->accepts_cargo[j]) return true;
2315  }
2316  }
2317  }
2318  return false;
2319  }
2320 
2327  static int CountMatchingAcceptingIndustries(const CargoID *cargoes, uint length)
2328  {
2329  int count = 0;
2330  for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
2331  const IndustrySpec *indsp = GetIndustrySpec(it);
2332  if (!indsp->enabled) continue;
2333 
2334  if (HasCommonValidCargo(cargoes, length, indsp->accepts_cargo, lengthof(indsp->accepts_cargo))) count++;
2335  }
2336  return count;
2337  }
2338 
2345  static int CountMatchingProducingIndustries(const CargoID *cargoes, uint length)
2346  {
2347  int count = 0;
2348  for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
2349  const IndustrySpec *indsp = GetIndustrySpec(it);
2350  if (!indsp->enabled) continue;
2351 
2352  if (HasCommonValidCargo(cargoes, length, indsp->produced_cargo, lengthof(indsp->produced_cargo))) count++;
2353  }
2354  return count;
2355  }
2356 
2363  void ShortenCargoColumn(int column, int top, int bottom)
2364  {
2365  while (top < bottom && !this->fields[top].columns[column].HasConnection()) {
2366  this->fields[top].columns[column].MakeEmpty(CFT_EMPTY);
2367  top++;
2368  }
2369  this->fields[top].columns[column].u.cargo.top_end = true;
2370 
2371  while (bottom > top && !this->fields[bottom].columns[column].HasConnection()) {
2372  this->fields[bottom].columns[column].MakeEmpty(CFT_EMPTY);
2373  bottom--;
2374  }
2375  this->fields[bottom].columns[column].u.cargo.bottom_end = true;
2376  }
2377 
2384  void PlaceIndustry(int row, int col, IndustryType it)
2385  {
2386  assert(this->fields[row].columns[col].type == CFT_EMPTY);
2387  this->fields[row].columns[col].MakeIndustry(it);
2388  if (col == 0) {
2389  this->fields[row].ConnectIndustryProduced(col);
2390  } else {
2391  this->fields[row].ConnectIndustryAccepted(col);
2392  }
2393  }
2394 
2399  {
2400  if (!this->IsWidgetLowered(WID_IC_NOTIFY)) return;
2401 
2402  /* Only notify the smallmap window if it exists. In particular, do not
2403  * bring it to the front to prevent messing up any nice layout of the user. */
2405  }
2406 
2411  void ComputeIndustryDisplay(IndustryType it)
2412  {
2413  this->GetWidget<NWidgetCore>(WID_IC_CAPTION)->widget_data = STR_INDUSTRY_CARGOES_INDUSTRY_CAPTION;
2414  this->ind_cargo = it;
2415  _displayed_industries.reset();
2416  _displayed_industries.set(it);
2417 
2418  this->fields.clear();
2419  /*C++17: CargoesRow &row = */ this->fields.emplace_back();
2420  CargoesRow &row = this->fields.back();
2421  row.columns[0].MakeHeader(STR_INDUSTRY_CARGOES_PRODUCERS);
2425  row.columns[4].MakeHeader(STR_INDUSTRY_CARGOES_CUSTOMERS);
2426 
2427  const IndustrySpec *central_sp = GetIndustrySpec(it);
2428  bool houses_supply = HousesCanSupply(central_sp->accepts_cargo, lengthof(central_sp->accepts_cargo));
2429  bool houses_accept = HousesCanAccept(central_sp->produced_cargo, lengthof(central_sp->produced_cargo));
2430  /* Make a field consisting of two cargo columns. */
2431  int num_supp = CountMatchingProducingIndustries(central_sp->accepts_cargo, lengthof(central_sp->accepts_cargo)) + houses_supply;
2432  int num_cust = CountMatchingAcceptingIndustries(central_sp->produced_cargo, lengthof(central_sp->produced_cargo)) + houses_accept;
2433  int num_indrows = max(3, max(num_supp, num_cust)); // One is needed for the 'it' industry, and 2 for the cargo labels.
2434  for (int i = 0; i < num_indrows; i++) {
2435  /*C++17: CargoesRow &row = */ this->fields.emplace_back();
2436  CargoesRow &row = this->fields.back();
2437  row.columns[0].MakeEmpty(CFT_EMPTY);
2438  row.columns[1].MakeCargo(central_sp->accepts_cargo, lengthof(central_sp->accepts_cargo));
2439  row.columns[2].MakeEmpty(CFT_EMPTY);
2440  row.columns[3].MakeCargo(central_sp->produced_cargo, lengthof(central_sp->produced_cargo));
2441  row.columns[4].MakeEmpty(CFT_EMPTY);
2442  }
2443  /* Add central industry. */
2444  int central_row = 1 + num_indrows / 2;
2445  this->fields[central_row].columns[2].MakeIndustry(it);
2446  this->fields[central_row].ConnectIndustryProduced(2);
2447  this->fields[central_row].ConnectIndustryAccepted(2);
2448 
2449  /* Add cargo labels. */
2450  this->fields[central_row - 1].MakeCargoLabel(2, true);
2451  this->fields[central_row + 1].MakeCargoLabel(2, false);
2452 
2453  /* Add suppliers and customers of the 'it' industry. */
2454  int supp_count = 0;
2455  int cust_count = 0;
2456  for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
2457  const IndustrySpec *indsp = GetIndustrySpec(it);
2458  if (!indsp->enabled) continue;
2459 
2460  if (HasCommonValidCargo(central_sp->accepts_cargo, lengthof(central_sp->accepts_cargo), indsp->produced_cargo, lengthof(indsp->produced_cargo))) {
2461  this->PlaceIndustry(1 + supp_count * num_indrows / num_supp, 0, it);
2462  _displayed_industries.set(it);
2463  supp_count++;
2464  }
2465  if (HasCommonValidCargo(central_sp->produced_cargo, lengthof(central_sp->produced_cargo), indsp->accepts_cargo, lengthof(indsp->accepts_cargo))) {
2466  this->PlaceIndustry(1 + cust_count * num_indrows / num_cust, 4, it);
2467  _displayed_industries.set(it);
2468  cust_count++;
2469  }
2470  }
2471  if (houses_supply) {
2472  this->PlaceIndustry(1 + supp_count * num_indrows / num_supp, 0, NUM_INDUSTRYTYPES);
2473  supp_count++;
2474  }
2475  if (houses_accept) {
2476  this->PlaceIndustry(1 + cust_count * num_indrows / num_cust, 4, NUM_INDUSTRYTYPES);
2477  cust_count++;
2478  }
2479 
2480  this->ShortenCargoColumn(1, 1, num_indrows);
2481  this->ShortenCargoColumn(3, 1, num_indrows);
2482  const NWidgetBase *nwp = this->GetWidget<NWidgetBase>(WID_IC_PANEL);
2484  this->SetDirty();
2485  this->NotifySmallmap();
2486  }
2487 
2493  {
2494  this->GetWidget<NWidgetCore>(WID_IC_CAPTION)->widget_data = STR_INDUSTRY_CARGOES_CARGO_CAPTION;
2495  this->ind_cargo = cid + NUM_INDUSTRYTYPES;
2496  _displayed_industries.reset();
2497 
2498  this->fields.clear();
2499  /*C++17: CargoesRow &row = */ this->fields.emplace_back();
2500  CargoesRow &row = this->fields.back();
2501  row.columns[0].MakeHeader(STR_INDUSTRY_CARGOES_PRODUCERS);
2503  row.columns[2].MakeHeader(STR_INDUSTRY_CARGOES_CUSTOMERS);
2506 
2507  bool houses_supply = HousesCanSupply(&cid, 1);
2508  bool houses_accept = HousesCanAccept(&cid, 1);
2509  int num_supp = CountMatchingProducingIndustries(&cid, 1) + houses_supply + 1; // Ensure room for the cargo label.
2510  int num_cust = CountMatchingAcceptingIndustries(&cid, 1) + houses_accept;
2511  int num_indrows = max(num_supp, num_cust);
2512  for (int i = 0; i < num_indrows; i++) {
2513  /*C++17: CargoesRow &row = */ this->fields.emplace_back();
2514  CargoesRow &row = this->fields.back();
2515  row.columns[0].MakeEmpty(CFT_EMPTY);
2516  row.columns[1].MakeCargo(&cid, 1);
2517  row.columns[2].MakeEmpty(CFT_EMPTY);
2518  row.columns[3].MakeEmpty(CFT_EMPTY);
2519  row.columns[4].MakeEmpty(CFT_EMPTY);
2520  }
2521 
2522  this->fields[num_indrows].MakeCargoLabel(0, false); // Add cargo labels at the left bottom.
2523 
2524  /* Add suppliers and customers of the cargo. */
2525  int supp_count = 0;
2526  int cust_count = 0;
2527  for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
2528  const IndustrySpec *indsp = GetIndustrySpec(it);
2529  if (!indsp->enabled) continue;
2530 
2531  if (HasCommonValidCargo(&cid, 1, indsp->produced_cargo, lengthof(indsp->produced_cargo))) {
2532  this->PlaceIndustry(1 + supp_count * num_indrows / num_supp, 0, it);
2533  _displayed_industries.set(it);
2534  supp_count++;
2535  }
2536  if (HasCommonValidCargo(&cid, 1, indsp->accepts_cargo, lengthof(indsp->accepts_cargo))) {
2537  this->PlaceIndustry(1 + cust_count * num_indrows / num_cust, 2, it);
2538  _displayed_industries.set(it);
2539  cust_count++;
2540  }
2541  }
2542  if (houses_supply) {
2543  this->PlaceIndustry(1 + supp_count * num_indrows / num_supp, 0, NUM_INDUSTRYTYPES);
2544  supp_count++;
2545  }
2546  if (houses_accept) {
2547  this->PlaceIndustry(1 + cust_count * num_indrows / num_cust, 2, NUM_INDUSTRYTYPES);
2548  cust_count++;
2549  }
2550 
2551  this->ShortenCargoColumn(1, 1, num_indrows);
2552  const NWidgetBase *nwp = this->GetWidget<NWidgetBase>(WID_IC_PANEL);
2554  this->SetDirty();
2555  this->NotifySmallmap();
2556  }
2557 
2565  void OnInvalidateData(int data = 0, bool gui_scope = true) override
2566  {
2567  if (!gui_scope) return;
2568  if (data == NUM_INDUSTRYTYPES) {
2569  if (this->IsWidgetLowered(WID_IC_NOTIFY)) {
2570  this->RaiseWidget(WID_IC_NOTIFY);
2571  this->SetWidgetDirty(WID_IC_NOTIFY);
2572  }
2573  return;
2574  }
2575 
2576  assert(data >= 0 && data < NUM_INDUSTRYTYPES);
2577  this->ComputeIndustryDisplay(data);
2578  }
2579 
2580  void DrawWidget(const Rect &r, int widget) const override
2581  {
2582  if (widget != WID_IC_PANEL) return;
2583 
2584  DrawPixelInfo tmp_dpi, *old_dpi;
2585  int width = r.right - r.left + 1;
2586  int height = r.bottom - r.top + 1 - WD_FRAMERECT_TOP - WD_FRAMERECT_BOTTOM;
2587  if (!FillDrawPixelInfo(&tmp_dpi, r.left + WD_FRAMERECT_LEFT, r.top + WD_FRAMERECT_TOP, width, height)) return;
2588  old_dpi = _cur_dpi;
2589  _cur_dpi = &tmp_dpi;
2590 
2591  int left_pos = WD_FRAMERECT_LEFT;
2592  if (this->ind_cargo >= NUM_INDUSTRYTYPES) left_pos += (CargoesField::industry_width + CargoesField::cargo_field_width) / 2;
2593  int last_column = (this->ind_cargo < NUM_INDUSTRYTYPES) ? 4 : 2;
2594 
2595  const NWidgetBase *nwp = this->GetWidget<NWidgetBase>(WID_IC_PANEL);
2596  int vpos = -this->vscroll->GetPosition() * nwp->resize_y;
2597  for (uint i = 0; i < this->fields.size(); i++) {
2598  int row_height = (i == 0) ? CargoesField::small_height : CargoesField::normal_height;
2599  if (vpos + row_height >= 0) {
2600  int xpos = left_pos;
2601  int col, dir;
2602  if (_current_text_dir == TD_RTL) {
2603  col = last_column;
2604  dir = -1;
2605  } else {
2606  col = 0;
2607  dir = 1;
2608  }
2609  while (col >= 0 && col <= last_column) {
2610  this->fields[i].columns[col].Draw(xpos, vpos);
2612  col += dir;
2613  }
2614  }
2615  vpos += row_height;
2616  if (vpos >= height) break;
2617  }
2618 
2619  _cur_dpi = old_dpi;
2620  }
2621 
2629  bool CalculatePositionInWidget(Point pt, Point *fieldxy, Point *xy)
2630  {
2631  const NWidgetBase *nw = this->GetWidget<NWidgetBase>(WID_IC_PANEL);
2632  pt.x -= nw->pos_x;
2633  pt.y -= nw->pos_y;
2634 
2635  int vpos = WD_FRAMERECT_TOP + CargoesField::small_height - this->vscroll->GetPosition() * nw->resize_y;
2636  if (pt.y < vpos) return false;
2637 
2638  int row = (pt.y - vpos) / CargoesField::normal_height; // row is relative to row 1.
2639  if (row + 1 >= (int)this->fields.size()) return false;
2640  vpos = pt.y - vpos - row * CargoesField::normal_height; // Position in the row + 1 field
2641  row++; // rebase row to match index of this->fields.
2642 
2643  int xpos = 2 * WD_FRAMERECT_LEFT + ((this->ind_cargo < NUM_INDUSTRYTYPES) ? 0 : (CargoesField::industry_width + CargoesField::cargo_field_width) / 2);
2644  if (pt.x < xpos) return false;
2645  int column;
2646  for (column = 0; column <= 5; column++) {
2648  if (pt.x < xpos + width) break;
2649  xpos += width;
2650  }
2651  int num_columns = (this->ind_cargo < NUM_INDUSTRYTYPES) ? 4 : 2;
2652  if (column > num_columns) return false;
2653  xpos = pt.x - xpos;
2654 
2655  /* Return both positions, compensating for RTL languages (which works due to the equal symmetry in both displays). */
2656  fieldxy->y = row;
2657  xy->y = vpos;
2658  if (_current_text_dir == TD_RTL) {
2659  fieldxy->x = num_columns - column;
2660  xy->x = ((column & 1) ? CargoesField::cargo_field_width : CargoesField::industry_width) - xpos;
2661  } else {
2662  fieldxy->x = column;
2663  xy->x = xpos;
2664  }
2665  return true;
2666  }
2667 
2668  void OnClick(Point pt, int widget, int click_count) override
2669  {
2670  switch (widget) {
2671  case WID_IC_PANEL: {
2672  Point fieldxy, xy;
2673  if (!CalculatePositionInWidget(pt, &fieldxy, &xy)) return;
2674 
2675  const CargoesField *fld = this->fields[fieldxy.y].columns + fieldxy.x;
2676  switch (fld->type) {
2677  case CFT_INDUSTRY:
2678  if (fld->u.industry.ind_type < NUM_INDUSTRYTYPES) this->ComputeIndustryDisplay(fld->u.industry.ind_type);
2679  break;
2680 
2681  case CFT_CARGO: {
2682  CargoesField *lft = (fieldxy.x > 0) ? this->fields[fieldxy.y].columns + fieldxy.x - 1 : nullptr;
2683  CargoesField *rgt = (fieldxy.x < 4) ? this->fields[fieldxy.y].columns + fieldxy.x + 1 : nullptr;
2684  CargoID cid = fld->CargoClickedAt(lft, rgt, xy);
2685  if (cid != INVALID_CARGO) this->ComputeCargoDisplay(cid);
2686  break;
2687  }
2688 
2689  case CFT_CARGO_LABEL: {
2690  CargoID cid = fld->CargoLabelClickedAt(xy);
2691  if (cid != INVALID_CARGO) this->ComputeCargoDisplay(cid);
2692  break;
2693  }
2694 
2695  default:
2696  break;
2697  }
2698  break;
2699  }
2700 
2701  case WID_IC_NOTIFY:
2702  this->ToggleWidgetLoweredState(WID_IC_NOTIFY);
2703  this->SetWidgetDirty(WID_IC_NOTIFY);
2704  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
2705 
2706  if (this->IsWidgetLowered(WID_IC_NOTIFY)) {
2707  if (FindWindowByClass(WC_SMALLMAP) == nullptr) ShowSmallMap();
2708  this->NotifySmallmap();
2709  }
2710  break;
2711 
2712  case WID_IC_CARGO_DROPDOWN: {
2713  DropDownList lst;
2714  const CargoSpec *cs;
2716  lst.emplace_back(new DropDownListStringItem(cs->name, cs->Index(), false));
2717  }
2718  if (!lst.empty()) {
2719  int selected = (this->ind_cargo >= NUM_INDUSTRYTYPES) ? (int)(this->ind_cargo - NUM_INDUSTRYTYPES) : -1;
2720  ShowDropDownList(this, std::move(lst), selected, WID_IC_CARGO_DROPDOWN, 0, true);
2721  }
2722  break;
2723  }
2724 
2725  case WID_IC_IND_DROPDOWN: {
2726  DropDownList lst;
2727  for (IndustryType ind : _sorted_industry_types) {
2728  const IndustrySpec *indsp = GetIndustrySpec(ind);
2729  if (!indsp->enabled) continue;
2730  lst.emplace_back(new DropDownListStringItem(indsp->name, ind, false));
2731  }
2732  if (!lst.empty()) {
2733  int selected = (this->ind_cargo < NUM_INDUSTRYTYPES) ? (int)this->ind_cargo : -1;
2734  ShowDropDownList(this, std::move(lst), selected, WID_IC_IND_DROPDOWN, 0, true);
2735  }
2736  break;
2737  }
2738  }
2739  }
2740 
2741  void OnDropdownSelect(int widget, int index) override
2742  {
2743  if (index < 0) return;
2744 
2745  switch (widget) {
2746  case WID_IC_CARGO_DROPDOWN:
2747  this->ComputeCargoDisplay(index);
2748  break;
2749 
2750  case WID_IC_IND_DROPDOWN:
2751  this->ComputeIndustryDisplay(index);
2752  break;
2753  }
2754  }
2755 
2756  bool OnTooltip(Point pt, int widget, TooltipCloseCondition close_cond) override
2757  {
2758  if (widget != WID_IC_PANEL) return false;
2759 
2760  Point fieldxy, xy;
2761  if (!CalculatePositionInWidget(pt, &fieldxy, &xy)) return false;
2762 
2763  const CargoesField *fld = this->fields[fieldxy.y].columns + fieldxy.x;
2764  CargoID cid = INVALID_CARGO;
2765  switch (fld->type) {
2766  case CFT_CARGO: {
2767  CargoesField *lft = (fieldxy.x > 0) ? this->fields[fieldxy.y].columns + fieldxy.x - 1 : nullptr;
2768  CargoesField *rgt = (fieldxy.x < 4) ? this->fields[fieldxy.y].columns + fieldxy.x + 1 : nullptr;
2769  cid = fld->CargoClickedAt(lft, rgt, xy);
2770  break;
2771  }
2772 
2773  case CFT_CARGO_LABEL: {
2774  cid = fld->CargoLabelClickedAt(xy);
2775  break;
2776  }
2777 
2778  case CFT_INDUSTRY:
2779  if (fld->u.industry.ind_type < NUM_INDUSTRYTYPES && (this->ind_cargo >= NUM_INDUSTRYTYPES || fieldxy.x != 2)) {
2780  GuiShowTooltips(this, STR_INDUSTRY_CARGOES_INDUSTRY_TOOLTIP, 0, nullptr, close_cond);
2781  }
2782  return true;
2783 
2784  default:
2785  break;
2786  }
2787  if (cid != INVALID_CARGO && (this->ind_cargo < NUM_INDUSTRYTYPES || cid != this->ind_cargo - NUM_INDUSTRYTYPES)) {
2788  const CargoSpec *csp = CargoSpec::Get(cid);
2789  uint64 params[5];
2790  params[0] = csp->name;
2791  GuiShowTooltips(this, STR_INDUSTRY_CARGOES_CARGO_TOOLTIP, 1, params, close_cond);
2792  return true;
2793  }
2794 
2795  return false;
2796  }
2797 
2798  void OnResize() override
2799  {
2800  this->vscroll->SetCapacityFromWidget(this, WID_IC_PANEL);
2801  }
2802 };
2803 
2806 
2811 static void ShowIndustryCargoesWindow(IndustryType id)
2812 {
2813  if (id >= NUM_INDUSTRYTYPES) {
2814  for (IndustryType ind : _sorted_industry_types) {
2815  const IndustrySpec *indsp = GetIndustrySpec(ind);
2816  if (indsp->enabled) {
2817  id = ind;
2818  break;
2819  }
2820  }
2821  if (id >= NUM_INDUSTRYTYPES) return;
2822  }
2823 
2825  if (w != nullptr) {
2826  w->InvalidateData(id);
2827  return;
2828  }
2829  new IndustryCargoesWindow(id);
2830 }
2831 
2834 {
2836 }
CargoID accepts_cargo[INDUSTRY_NUM_INPUTS]
16 accepted cargoes.
Definition: industrytype.h:122
Nested widget containing a viewport.
Definition: widget_type.h:81
Display chain button.
bool enabled
the house is available to build (true by default, but can be disabled by newgrf)
Definition: house.h:113
Functions related to OTTD&#39;s strings.
void NotifySmallmap()
Notify smallmap that new displayed industries have been selected (in _displayed_industries).
void GenerateIndustries()
This function will create random industries during game creation.
static const int HOR_CARGO_BORDER_SPACE
Amount of space between the left/right edge of a CFT_CARGO field, and the left/right most vertical ca...
void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
Update size and resize step of a widget in the window.
Functions/types related to NewGRF debugging.
static void Swap(T &a, T &b)
Type safe swap operation.
Definition: math_func.hpp:277
Transfer storage of cargo suffix information.
Base types for having sorted lists in GUIs.
void RebuildDone()
Notify the sortlist that the rebuild is done.
Matrix of the industries.
static const uint MAX_CARGOES
Maximum number of cargoes carried in a CFT_CARGO field in CargoesField.
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition: settings.cpp:81
Definition of stuff that is very close to a company, like the company struct itself.
bool _networking
are we in networking mode?
Definition: network.cpp:54
static int GetCargoTransportedPercentsIfValid(const Industry *i, uint id)
Returns percents of cargo transported if industry produces this cargo, else -1.
static uint minu(const uint a, const uint b)
Returns the minimum of two unsigned integers.
Definition: math_func.hpp:70
void DrawWidget(const Rect &r, int widget) const override
Draw the contents of a nested widget.
bool enabled
entity still available (by default true).newgrf can disable it, though
Definition: industrytype.h:141
Build (fund or prospect) a new industry,.
byte production_rate[INDUSTRY_NUM_OUTPUTS]
production rate for each cargo
Definition: industry.h:49
void SortIndustryTypes()
Initialize the list of sorted industry types.
static const uint CALLBACK_FAILED
Different values for Callback result evaluations.
Data about how and where to blit pixels.
Definition: gfx_type.h:156
static const uint8 PC_WHITE
White palette colour.
Definition: gfx_func.h:208
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
Dropdown for the criteria of the sort.
Horizontally center the text.
Definition: gfx_func.h:97
void OnInit() override
Notification that the nested widget tree gets initialized.
static int CountMatchingAcceptingIndustries(const CargoID *cargoes, uint length)
Count how many industries have accepted cargoes in common with one of the supplied set...
static NWidgetPart SetResize(int16 dx, int16 dy)
Widget part function for setting the resize step.
Definition: widget_type.h:930
uint8 raw_industry_construction
type of (raw) industry construction (none, "normal", prospecting)
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:112
Offset at right of a matrix cell.
Definition: window_gui.h:79
byte landscape
the landscape we&#39;re currently in
Viewport of the industry.
High level window description.
Definition: window_gui.h:168
byte map_colour
colour used for the small map
Definition: industrytype.h:127
static int industry_width
Width of an industry field.
uint16 count
How many industries are loaded.
Goto button.
void MakeIndustry(IndustryType ind_type)
Make an industry type field.
below this level, the industry is set to be closing
Definition: industry.h:34
static Titem * Get(size_t index)
Returns Titem with given index.
Definition: pool_type.hpp:246
static const int VERT_CARGO_SPACE
Amount of vertical space between two connected cargoes at an industry.
int info_height
Height needed for the WID_IV_INFO panel.
Scrollbar data structure.
Definition: widget_type.h:589
Functions for NewGRF industries.
static const int INDUSTRY_LINE_COLOUR
Line colour of the industry type box.
void MakeEmpty(CargoesFieldType type)
Make one of the empty fields (CFT_EMPTY or CFT_SMALL_EMPTY).
void PlaceIndustry(int row, int col, IndustryType it)
Place an industry in the fields.
Offset at top to draw the frame rectangular area.
Definition: window_gui.h:64
void OnGameTick() override
Called once per (game) tick.
void UpdateViewportCoordinates(Window *w)
Update the position and size of the viewport (after eg a resize).
Definition: widget.cpp:1936
Horizontal container.
Definition: widget_type.h:75
void ShowSmallMap()
Show the smallmap window.
NewGRF debug box (at top-right of a window, between WWT_CAPTION and WWT_SHADEBOX) ...
Definition: widget_type.h:63
void SetSortFuncs(SortFunction *const *n_funcs)
Hand the array of sort function pointers to the sort list.
void DrawWidget(const Rect &r, int widget) const override
Draw the contents of a nested widget.
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:1121
uint32 GetIndustryProbabilityCallback(IndustryType type, IndustryAvailabilityCallType creation_type, uint32 default_prob)
Check with callback CBID_INDUSTRY_PROBABILITY whether the industry can be built.
Maximal number of cargo types in a game.
Definition: cargo_type.h:66
CargoSuffixDisplay
Ways of displaying the cargo.
byte cargo_acceptance[HOUSE_NUM_ACCEPTS]
acceptance level for the cargo slots
Definition: house.h:109
void OnClick(Point pt, int widget, int click_count) override
A click with the left mouse button has been made on the window.
void BuildSortIndustriesList()
(Re)Build industries list
Specification of a cargo type.
Definition: cargotype.h:57
void OnClick(Point pt, int widget, int click_count) override
A click with the left mouse button has been made on the window.
void GuiShowTooltips(Window *parent, StringID str, uint paramcount, const uint64 params[], TooltipCloseCondition close_tooltip)
Shows a tooltip.
Definition: misc_gui.cpp:766
Display the cargo without sub-type (cb37 result 401).
from the Fund/build window
default level set when the industry is created
Definition: industry.h:35
Row of buttons at the bottom.
static bool IndustryTypeSorter(const Industry *const &a, const Industry *const &b)
Sort industries by type and name.
static int small_height
Height of the header row.
Editability editable
Mode for changing production.
CargoID accepts_cargo[HOUSE_NUM_ACCEPTS]
input cargo slots
Definition: house.h:110
Resize box (normally at bottom-right of a window)
Definition: widget_type.h:68
Pressed (inset) panel, most commonly used as combo box text area.
Definition: widget_type.h:51
uint16 callback_mask
Bitmask of industry callbacks that have to be called.
Definition: industrytype.h:139
struct CargoesField::@17::@19 cargo
Cargo data (for CFT_CARGO).
static uint TileX(TileIndex tile)
Get the X component of a tile.
Definition: map_func.h:207
static const int CARGO_LINE_COLOUR
Line colour around the cargo.
static bool HousesCanSupply(const CargoID *cargoes, uint length)
Can houses be used to supply one of the cargoes?
signal set to actually close the industry
Definition: industry.h:33
Defines the internal data of a functional industry.
Definition: industry.h:42
static const int BLOB_HEIGHT
Height of the industry legend colour, including border.
void CcBuildIndustry(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2, uint32 cmd)
Command callback.
DifficultySettings difficulty
settings related to the difficulty
Industry-directory window.
Tindex index
Index of this pool item.
Definition: pool_type.hpp:147
void ShowErrorMessage(StringID summary_msg, StringID detailed_msg, WarningLevel wl, int x=0, int y=0, const GRFFile *textref_stack_grffile=nullptr, uint textref_stack_size=0, const uint32 *textref_stack=nullptr)
Display an error message in a window.
Definition: error_gui.cpp:382
static const int DAY_TICKS
1 day is 74 ticks; _date_fract used to be uint16 and incremented by 885.
Definition: date_type.h:30
Close box (at top-left of a window)
Definition: widget_type.h:69
Offset at top of a matrix cell.
Definition: window_gui.h:80
InfoLine
Specific lines in the info panel.
View-industry window.
Display cargo labels.
#define lastof(x)
Get the last element of an fixed size array.
Definition: depend.cpp:50
StringID GetGRFStringID(uint32 grfid, StringID stringid)
Returns the index for this stringid associated with its grfID.
Functions related to world/map generation.
static void ShowIndustryCargoesWindow(IndustryType id)
Open the industry and cargoes window.
Stuff related to the text buffer GUI.
static const int MATRIX_TEXT_OFFSET
The offset for the text in the matrix.
void InitializeViewport(Window *w, uint32 follow_flags, ZoomLevel zoom)
Initialize the viewport of the window.
Definition: widget.cpp:1927
static int CountMatchingProducingIndustries(const CargoID *cargoes, uint length)
Count how many industries have produced cargoes in common with one of the supplied set...
void OnDropdownSelect(int widget, int index) override
A dropdown option associated to this window has been selected.
bool persistent_buildingtools
keep the building tools active after usage
void ComputeCargoDisplay(CargoID cid)
Compute what and where to display for cargo id cid.
Common return value for all commands.
Definition: command_type.h:25
Dimension ind_textsize
Size to hold any industry type text, as well as STR_INDUSTRY_CARGOES_SELECT_INDUSTRY.
bool IsCargoIDValid(CargoID t)
Test whether cargo type is not CT_INVALID.
Definition: cargo_type.h:76
void MakeHeader(StringID textid)
Make a header above an industry column.
static T max(const T a, const T b)
Returns the maximum of two values.
Definition: math_func.hpp:26
std::vector< IndustryTileLayout > layouts
List of possible tile layouts for the industry.
Definition: industrytype.h:109
the industry is running at full speed
Definition: industry.h:36
Nested widget to display a viewport in a window.
Definition: widget_type.h:575
Common string list item.
Definition: dropdown_type.h:41
void OnDropdownSelect(int widget, int index) override
A dropdown option associated to this window has been selected.
void SetListing(Listing l)
Import sort conditions.
Display industry.
Large amount of vertical space between two paragraphs of text.
Definition: window_gui.h:140
int DrawInfo(uint left, uint right, uint top)
Draw the text in the WID_IV_INFO panel.
Allow changing the production rates.
Window * GetCallbackWnd()
Get the window that started the current highlighting.
Definition: viewport.cpp:2490
static const uint TILE_SIZE
Tile size in world coordinates.
Definition: tile_type.h:15
StringID name
Name of this type of cargo.
Definition: cargotype.h:72
Industry directory; Window numbers:
Definition: window_type.h:261
static const HouseID NUM_HOUSES
Total number of houses.
Definition: house.h:31
void OnPaint() override
The window must be repainted.
StringID name
Displayed name of the industry.
Definition: industrytype.h:128
static int RoundDivSU(int a, uint b)
Computes round(a / b) for signed a and unsigned b.
Definition: math_func.hpp:338
HouseZones
Definition: house.h:73
bool NeedRebuild() const
Check if a rebuild is needed.
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
static int cargo_field_width
Width of a cargo field.
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:1478
Called to determine more text in the fund industry window.
Display the cargo and amount (if useful), but no sub-type (cb37 result 400 or fail).
static 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:250
static uint ClampU(const uint a, const uint min, const uint max)
Clamp an unsigned integer between an interval.
Definition: math_func.hpp:184
StringID GetIndustryString(const Industry *i) const
Get the StringID to draw and set the appropriate DParams.
The list of industries.
HouseZones building_availability
where can it be built (climates, zones)
Definition: house.h:112
Class to backup a specific variable and restore it later.
Definition: backup_type.hpp:23
Info panel about the industry.
void SetCount(int num)
Sets the number of elements in the list.
Definition: widget_type.h:670
Partial widget specification to allow NWidgets to be written nested.
Definition: widget_type.h:910
Functions related to (drawing on) viewports.
Pseudo random number generator.
void ForceRebuild()
Force that a rebuild is needed.
static bool IndustryTransportedCargoSorter(const Industry *const &a, const Industry *const &b)
Sort industries by transported cargo and name.
Data structure for an opened window.
Definition: window_gui.h:278
Invalid cargo type.
Definition: cargo_type.h:70
bool _ctrl_pressed
Is Ctrl pressed?
Definition: gfx.cpp:37
static NWidgetPart SetMatrixDataTip(uint8 cols, uint8 rows, StringID tip)
Widget part function for setting the data and tooltip of WWT_MATRIX widgets.
Definition: widget_type.h:1032
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
static NWidgetPart SetPadding(uint8 top, uint8 right, uint8 bottom, uint8 left)
Widget part function for setting additional space around a widget.
Definition: widget_type.h:1046
static bool IsInsideMM(const T x, const size_t min, const size_t max)
Checks if a value is in an interval.
Definition: math_func.hpp:266
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:3318
Bottom offset of the text of the frame.
Definition: window_gui.h:75
Header of Action 04 "universal holder" structure and functions.
void SetDParamStr(uint n, const char *str)
This function is used to "bind" a C string to a OpenTTD dparam slot.
Definition: strings.cpp:281
Header text.
void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
Update size and resize step of a widget in the window.
Functions related to low-level strings.
The tile has no ownership.
Definition: company_type.h:27
void OnResize() override
Called after the window got resized.
Types related to cheating.
StringID header
Header text (for CFT_HEADER).
void OnTimeout() override
Called when this window&#39;s timeout has been reached.
Functions related to errors.
void OnResize() override
Called after the window got resized.
Offset at bottom of a matrix cell.
Definition: window_gui.h:81
Default window size box (at top-right of a window, between WWT_SHADEBOX and WWT_STICKYBOX) ...
Definition: widget_type.h:65
void SetStringParameters(int widget) const override
Initialize string parameters for a widget.
bool IsRawIndustry() const
Is an industry with the spec a raw industry?
void ShowNewGRFInspectWindow() const override
Show the NewGRF inspection window.
int GetScrolledRowFromWidget(int clickpos, const Window *const w, int widget, int padding=0, int line_height=-1) const
Compute the row of a scrolled widget that a user clicked in.
Definition: widget.cpp:1959
Caption of the window.
void OnQueryTextFinished(char *str) override
The query window opened from this window has closed.
This window is used for construction; close it whenever changing company.
Definition: window_gui.h:210
Fill rectangle with a single colour.
Definition: gfx_type.h:283
SoundSettings sound
sound effect settings
std::array< IndustryType, NUM_INDUSTRYTYPES > _sorted_industry_types
Industry types sorted by name.
void SetStringParameters(int widget) const override
Initialize string parameters for a widget.
Listing GetListing() const
Export current sort conditions.
static const NWidgetPart _nested_industry_cargoes_widgets[]
Widgets of the industry cargoes window.
uint current_y
Current vertical size (after resizing).
Definition: widget_type.h:175
Sort descending.
Definition: window_gui.h:227
void MakeCargoLabel(int column, bool accepting)
Construct a CFT_CARGO_LABEL field.
StringID GetErrorMessage() const
Returns the error message of a command.
Definition: command_type.h:142
Caption of the window.
Small map; Window numbers:
Definition: window_type.h:99
#define FONT_HEIGHT_NORMAL
Height of characters in the normal (FS_NORMAL) font.
Definition: gfx_func.h:178
static NWidgetPart SetDataTip(uint32 data, StringID tip)
Widget part function for setting the data and tooltip.
Definition: widget_type.h:1014
void OnResize() override
Called after the window got resized.
CargoSuffixDisplay display
How to display the cargo and text.
ClientSettings _settings_client
The current settings for this game.
Definition: settings.cpp:80
void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
Update size and resize step of a widget in the window.
static NWidgetPart SetMinimalSize(int16 x, int16 y)
Widget part function for setting the minimal size.
Definition: widget_type.h:947
bool Succeeded() const
Did this command succeed?
Definition: command_type.h:152
const IndustrySpec * GetIndustrySpec(IndustryType thistype)
Accessor for array _industry_specs.
Data about a single field in the IndustryCargoesWindow panel.
void OnPlaceObject(Point pt, TileIndex tile) override
The user clicked some place on the map when a tile highlight mode has been set.
Definition of base types and functions in a cross-platform compatible way.
TileIndex GetCenterTile() const
Get the center tile.
Definition: tilearea_type.h:59
char text[512]
Cargo suffix text.
bool UsesSmoothEconomy() const
Determines whether this industrytype uses smooth economy or whether it uses standard/newgrf productio...
A number of safeguards to prevent using unsafe methods.
CargoesFieldType
Available types of field.
bool value
tells if the bool cheat is active or not
Definition: cheat_type.h:20
static void DrawHorConnection(int left, int right, int top, const CargoSpec *csp)
Draw a horizontal cargo connection.
IndustryType type
type of industry.
Definition: industry.h:59
Normal push-button (no toggle button) with text caption.
Definition: widget_type.h:104
Geometry functions.
rectangle (stations, depots, ...)
Simple depressed panel.
Definition: widget_type.h:50
static const int VERT_TEXT_PADDING
Vertical padding around the industry type text.
static uint CeilDiv(uint a, uint b)
Computes ceil(a / b) for non-negative a and b.
Definition: math_func.hpp:316
Fields fields
Fields to display in the WID_IC_PANEL.
Cheat setup_prod
setup raw-material production in game
Definition: cheat_type.h:37
void MakeCargoLabel(const CargoID *cargoes, uint length, bool left_align)
Make a field displaying cargo type names.
TileArea location
Location of the industry.
Definition: industry.h:43
additional text in industry window
static const int BLOB_DISTANCE
Distance of the industry legend colour from the edge of the industry box.
uint16 last_month_production[INDUSTRY_NUM_OUTPUTS]
total units produced per cargo in the last full month
Definition: industry.h:55
void Draw(int xpos, int ypos) const
Draw the field.
static const int HOR_CARGO_WIDTH
Width of a vertical cargo column (inclusive the border line).
static const int VERT_INTER_INDUSTRY_SPACE
Amount of space between two industries in a column.
CargoID produced_cargo[INDUSTRY_NUM_OUTPUTS]
16 production cargo slots
Definition: industry.h:46
int pos_x
Horizontal position of top-left corner of the widget in the window.
Definition: widget_type.h:177
Offset at left of a matrix cell.
Definition: window_gui.h:78
static const NWidgetPart _nested_industry_view_widgets[]
Widget definition of the view industry gui.
static WindowDesc _build_industry_desc(WDP_AUTO, "build_industry", 170, 212, WC_BUILD_INDUSTRY, WC_NONE, WDF_CONSTRUCTION, _nested_build_industry_widgets, lengthof(_nested_build_industry_widgets))
Window definition of the dynamic place industries gui.
byte bottom_end
Stop at the bottom of the vertical cargoes.
Default zoom level for the industry view.
Definition: zoom_type.h:37
Defines the data structure for constructing industry.
Definition: industrytype.h:108
InfoLine clicked_line
The line of the button that has been clicked.
static WindowDesc _industry_directory_desc(WDP_AUTO, "list_industries", 428, 190, WC_INDUSTRY_DIRECTORY, WC_NONE, 0, _nested_industry_directory_widgets, lengthof(_nested_industry_directory_widgets))
Window definition of the industry directory gui.
static NWidgetPart NWidget(WidgetType tp, Colours col, int16 idx=-1)
Widget part function for starting a new &#39;real&#39; widget.
Definition: widget_type.h:1114
CargoesFieldType type
Type of field.
Offset at bottom to draw the frame rectangular area.
Definition: window_gui.h:65
std::string MakeCargoListString(const CargoID *cargolist, const CargoSuffix *cargo_suffix, int cargolistlen, StringID prefixstr) const
Build a string of cargo names with suffixes attached.
Baseclass for nested widgets.
Definition: widget_type.h:126
Money GetConstructionCost() const
Get the cost for constructing this industry.
int DrawString(int left, int right, int top, const char *str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly truncated to make it fit in its allocated space.
Definition: gfx.cpp:499
additional text in fund window
Basic functions/variables used all over the place.
static bool HasCommonValidCargo(const CargoID *cargoes1, uint length1, const CargoID *cargoes2, uint length2)
Do the two sets of cargoes have a valid cargo in common?
CargoID accepts_cargo[INDUSTRY_NUM_INPUTS]
16 input cargo slots
Definition: industry.h:51
void OnClick(Point pt, int widget, int click_count) override
A click with the left mouse button has been made on the window.
Empty small field (for the header).
Right offset of the text of the frame.
Definition: window_gui.h:73
bool DoCommandP(const CommandContainer *container, bool my_cmd)
Shortcut for the long DoCommandP when having a container with the data.
Definition: command.cpp:534
Industry view; Window numbers:
Definition: window_type.h:358
uint8 cargo_map[NUM_CARGO]
Inverse cargo translation table (CargoID -> local ID)
Definition: newgrf.h:129
uint16 incoming_cargo_waiting[INDUSTRY_NUM_INPUTS]
incoming cargo waiting to be processed
Definition: industry.h:48
static const int CARGO_STUB_WIDTH
Width of a cargo not carried in the column (should be less than HOR_CARGO_BORDER_SPACE).
void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
Update size and resize step of a widget in the window.
#define lengthof(x)
Return the length of an fixed size array.
Definition: depend.cpp:42
IndustryBehaviour behaviour
How this industry will behave, and how others entities can use it.
Definition: industrytype.h:126
byte prod_level
general production level
Definition: industry.h:50
Select cargo dropdown.
uint resize_y
Vertical resize step (0 means not resizable).
Definition: widget_type.h:167
GRFFileProps grf_prop
properties related to the grf file
Definition: industrytype.h:142
Grid of rows and columns.
Definition: widget_type.h:59
#define FOR_ALL_SORTED_STANDARD_CARGOSPECS(var)
Loop header for iterating over &#39;real&#39; cargoes, sorted by name.
Definition: cargotype.h:173
Dimension cargo_textsize
Size to hold any cargo text, as well as STR_INDUSTRY_CARGOES_SELECT_CARGO.
Top offset of the text of the frame.
Definition: window_gui.h:74
Left offset of the text of the frame.
Definition: window_gui.h:72
bool ScrollWindowToTile(TileIndex tile, Window *w, bool instant)
Scrolls the viewport in a window to a given location.
Definition: viewport.cpp:2406
Functions related to sound.
void DrawArrowButtons(int x, int y, Colours button_colour, byte state, bool clickable_left, bool clickable_right)
Draw [<][>] boxes.
void SetSortType(uint8 n_type)
Set the sorttype of the list.
bool Sort(SortFunction *compare)
Sort the list.
void StartTextRefStackUsage(const GRFFile *grffile, byte numEntries, const uint32 *values)
Start using the TTDP compatible string code parsing.
The game does not build industries.
Definition: settings_type.h:44
static int SortButtonWidth()
Get width of up/down arrow of sort button state.
Definition: widget.cpp:658
bool CalculatePositionInWidget(Point pt, Point *fieldxy, Point *xy)
Calculate in which field was clicked, and within the field, at what position.
uint32 StringID
Numeric value that represents a string, independent of the selected language.
Definition: strings_type.h:18
void OnTimeout() override
Called when this window&#39;s timeout has been reached.
IndustryType selected_type
industry corresponding to the above index
static const uint8 PC_BLACK
Black palette colour.
Definition: gfx_func.h:205
#define SETTING_BUTTON_WIDTH
Width of setting buttons.
Definition: settings_gui.h:19
12 1000 can appear in temperate climate
Definition: house.h:82
IndustryType ind_type
Industry type (NUM_INDUSTRYTYPES means &#39;houses&#39;).
Empty field.
static const int HOR_CARGO_SPACE
Amount of horizontal space between two vertical cargoes.
static bool IndustryNameSorter(const Industry *const &a, const Industry *const &b)
Sort industries by name.
Display then cargo, amount, and string (cb37 result 000-3FF).
int GetCargoBase(int xpos) const
For a CFT_CARGO, compute the left position of the left-most vertical cargo connection.
static int normal_height
Height of the non-header rows.
bool IsNewGRFInspectable() const override
Is the data related to this window NewGRF inspectable?
Build industry; Window numbers:
Definition: window_type.h:430
void ShowExtraViewPortWindow(TileIndex tile=INVALID_TILE)
Show a new Extra Viewport window.
Allow changing the production multiplier.
Dimension GetStringBoundingBox(const char *str, FontSize start_fontsize)
Return the string dimension in pixels.
Definition: gfx.cpp:698
bool timer_enabled
timer can be used
TileIndex tile
The base tile of the area.
Definition: tilearea_type.h:19
14 4000 can appear in subtropical climate
Definition: house.h:84
void ForceResort()
Force a resort next Sort call Reset the resort timer if used too.
Dimension maxdim(const Dimension &d1, const Dimension &d2)
Compute bounding box of both dimensions.
No window, redirects to WC_MAIN_WINDOW.
Definition: window_type.h:40
std::bitset< NUM_INDUSTRYTYPES > _displayed_industries
Communication from the industry chain window to the smallmap window about what industries to display...
13 2000 can appear in sub-arctic climate below the snow line
Definition: house.h:83
void ConnectIndustryProduced(int column)
Connect industry production cargoes to the cargo column after it.
static bool IndustryTypeNameSorter(const IndustryType &a, const IndustryType &b)
Sort industry types by their name.
static bool HousesCanAccept(const CargoID *cargoes, uint length)
Can houses be used as customers of the produced cargoes?
static const uint8 PC_YELLOW
Yellow palette colour.
Definition: gfx_func.h:218
Smallmap GUI functions.
Functions related to companies.
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
static WindowDesc _industry_cargoes_desc(WDP_AUTO, "industry_cargoes", 300, 210, WC_INDUSTRY_CARGOES, WC_NONE, 0, _nested_industry_cargoes_widgets, lengthof(_nested_industry_cargoes_widgets))
Window description for the industry cargoes window.
bool IsNewGRFInspectable(GrfSpecFeature feature, uint index)
Can we inspect the data given a certain feature and index.
void ErrorUnknownCallbackResult(uint32 grfid, uint16 cbid, uint16 cb_res)
Record that a NewGRF returned an unknown/invalid callback result.
bool _generating_world
Whether we are generating the map or not.
Definition: genworld.cpp:62
15 8000 can appear in toyland climate
Definition: house.h:85
Both numeric and alphabetic and spaces and stuff.
Definition: string_type.h:29
static const IndustryType INVALID_INDUSTRYTYPE
one above amount is considered invalid
Definition: industry_type.h:29
void SetStringParameters(int widget) const override
Initialize string parameters for a widget.
GUISettings gui
settings related to the GUI
void SetStringParameters(int widget) const override
Initialize string parameters for a widget.
Window caption (window title between closebox and stickybox)
Definition: widget_type.h:61
Scrollbar of the panel.
int strnatcmp(const char *s1, const char *s2, bool ignore_garbage_at_front)
Compares two strings using case insensitive natural sort.
Definition: string.cpp:580
Display cargo connections.
static bool StrEmpty(const char *s)
Check if a string buffer is empty.
Definition: string_func.h:59
static CargoSpec * Get(size_t index)
Retrieve cargo details for the given cargo ID.
Definition: cargotype.h:119
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)
byte clicked_button
The button that has been clicked (to raise)
Types related to the industry widgets.
Editability
Modes for changing production.
void ShowNewGRFInspectWindow(GrfSpecFeature feature, uint index, const uint32 grfid=0)
Show the inspect window for a given feature and index.
uint32 TileIndex
The index/ID of a Tile.
Definition: tile_type.h:80
bool HandlePlacePushButton(Window *w, int widget, CursorID cursor, HighLightStyle mode)
This code is shared for the majority of the pushbuttons.
Definition: main_gui.cpp:100
#define cpp_lengthof(base, variable)
Gets the length of an array variable within a class.
Definition: stdafx.h:415
static int GetCargoTransportedSortValue(const Industry *i)
Returns value representing industry&#39;s transported cargo percentage for industry sorting.
static uint ToPercent8(uint i)
Converts a "fract" value 0..255 to "percent" value 0..100.
Definition: math_func.hpp:289
Production rate of cargo 1.
static size_t GetNumItems()
Returns number of valid items in the pool.
Definition: pool_type.hpp:276
uint ind_cargo
If less than NUM_INDUSTRYTYPES, an industry type, else a cargo id + NUM_INDUSTRYTYPES.
CargoesField columns[5]
One row of fields.
void RecomputeProductionMultipliers()
Recompute production_rate for current prod_level.
TextDirection _current_text_dir
Text direction of the currently selected language.
Definition: strings.cpp:50
void ToggleSortOrder()
Toggle the sort order Since that is the worst condition for the sort function reverse the list here...
A single row of CargoesField.
void ConnectIndustryAccepted(int column)
Connect industry accepted cargoes to the cargo column before it.
byte num_cargoes
Number of cargoes.
Industry list.
bool has_newindustries
Set if there are any newindustries loaded.
Definition: newgrf.h:180
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:28
Sort ascending.
Definition: window_gui.h:226
static uint TileY(TileIndex tile)
Get the Y component of a tile.
Definition: map_func.h:217
Display then cargo and supplied string (cb37 result 800-BFF).
Vertical container.
Definition: widget_type.h:77
Scrollbar of the matrix.
Allow produced/accepted cargoes callbacks to supply more than 2 and 3 types.
Definition: industrytype.h:84
Functions for setting GUIs.
static NWidgetPart EndContainer()
Widget part function for denoting the end of a container (horizontal, vertical, WWT_FRAME, WWT_INSET, or WWT_PANEL).
Definition: widget_type.h:999
static uint GB(const T x, const uint8 s, const uint8 n)
Fetch n bits from x, started at bit s.
Select industry dropdown.
static const byte INVALID_CARGO
Constant representing invalid cargo.
Definition: cargotype.h:54
uint current_x
Current horizontal size (after resizing).
Definition: widget_type.h:174
static Industry * PlaceIndustry(IndustryType type, IndustryAvailabilityCallType creation_type, bool try_hard)
Try to place the industry in the game.
#define endof(x)
Get the end element of an fixed size array.
Definition: stdafx.h:386
Window displaying the cargo connections around an industry (or cargo).
call production callback when cargo arrives at the industry
void OnInit() override
Notification that the nested widget tree gets initialized.
Window * FindWindowByClass(WindowClass cls)
Find any window by its class.
Definition: window.cpp:1130
cargo sub-type display
Panel that shows the chain.
CargoSuffixType
Cargo suffix type (for which window is it requested)
bool ScrollMainWindowToTile(TileIndex tile, bool instant)
Scrolls the viewport of the main window to a given location.
Definition: viewport.cpp:2417
Scrollbar of the list.
Functions related to commands.
Fund-industry window.
Coordinates of a point in 2D.
byte top_end
Stop at the top of the vertical cargoes.
CompanyID _current_company
Company currently doing an action.
Definition: company_cmd.cpp:47
CargoID Index() const
Determines index of this cargospec.
Definition: cargotype.h:90
static bool IsValidID(size_t index)
Tests whether given index can be used to get valid (non-nullptr) Titem.
Definition: pool_type.hpp:235
Data structure describing how to show the list (what sort direction and criteria).
Definition: sortlist_type.h:33
Drop down list.
Definition: widget_type.h:70
static const int BLOB_WIDTH
Width of the industry legend colour, including border.
uint16 GetCapacity() const
Gets the number of visible elements of the scrollbar.
Definition: widget_type.h:622
bool OnTooltip(Point pt, int widget, TooltipCloseCondition close_cond) override
Event to display a custom tooltip.
Window does not do autoscroll,.
Definition: window_gui.h:241
ConstructionSettings construction
construction of things in-game
void OnHundredthTick() override
Called once every 100 (game) ticks.
static const StringID INVALID_STRING_ID
Constant representing an invalid string (16bit in case it is used in savegames)
Definition: strings_type.h:19
Base of all industries.
Called to determine text to display after cargo name.
bool left_align
Align all cargo texts to the left (else align to the right).
bool HasConnection()
Does this CFT_CARGO field have a horizontal connection?
Offset at right to draw the frame rectangular area.
Definition: window_gui.h:63
void OnPaint() override
The window must be repainted.
const struct GRFFile * grffile
grf file that introduced this entity
Sticky box (at top-right of a window, after WWT_DEFSIZEBOX)
Definition: widget_type.h:66
Used for DoCommand-like (and some non-fatal AI GUI) errors/information.
Definition: error.h:23
static NWidgetPart SetFill(uint fill_x, uint fill_y)
Widget part function for setting filling.
Definition: widget_type.h:983
static bool HasBit(const T x, const uint8 y)
Checks if a bit in a value is set.
bool IsValid() const
Tests for validity of this cargospec.
Definition: cargotype.h:100
Dropdown for the order of the sort.
CargoesFieldType type
Type of field.
CargoID CargoClickedAt(const CargoesField *left, const CargoesField *right, Point pt) const
Decide which cargo was clicked at in a CFT_CARGO field.
void Restore()
Restore the variable.
void OnClick(Point pt, int widget, int click_count) override
A click with the left mouse button has been made on the window.
static const TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition: tile_type.h:85
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)
Base of the town class.
bool IsDescSortOrder() const
Check if the sort order is descending.
#define CMD_MSG(x)
Used to combine a StringID with the command.
Definition: command_type.h:370
void OnPlaceObjectAbort() override
The user cancelled a tile highlight mode that has been set.
int32 WindowNumber
Number to differentiate different windows of the same class.
Definition: window_type.h:707
GameCreationSettings game_creation
settings used during the creation of a game (map)
void SetCapacityFromWidget(Window *w, int widget, int padding=0)
Set capacity of visible elements from the size and resize properties of a widget. ...
Definition: widget.cpp:1973
void ResetObjectToPlace()
Reset the cursor and mouse mode handling back to default (normal cursor, only clicking in windows)...
Definition: viewport.cpp:3374
Specification of a rectangle with absolute coordinates of all edges.
Vertical scrollbar.
Definition: widget_type.h:84
byte CargoID
Cargo slots to indicate a cargo type within a game.
Definition: cargo_type.h:22
InfoLine editbox_line
The line clicked to open the edit box.
Text is written right-to-left by default.
Definition: strings_type.h:26
Right align the text (must be a single bit).
Definition: gfx_func.h:98
Called to determine more text in the industry window.
Info of the industry.
Left align the text.
Definition: gfx_func.h:96
Functions related to tile highlights.
Window functions not directly related to making/drawing windows.
int ConnectCargo(CargoID cargo, bool producer)
Connect a cargo from an industry to the CFT_CARGO column.
GRFLoadedFeatures _loaded_newgrf_features
Indicates which are the newgrf features currently loaded ingame.
Definition: newgrf.cpp:78
Find a place automatically.
Definition: window_gui.h:156
byte industry_density
The industry density.
Definition: settings_type.h:58
void MakeCargo(const CargoID *cargoes, uint length, int count=-1, bool top_end=false, bool bottom_end=false)
Make a piece of cargo column.
byte last_month_pct_transported[INDUSTRY_NUM_OUTPUTS]
percentage transported per cargo in the last full month
Definition: industry.h:54
static bool IndustryProductionSorter(const Industry *const &a, const Industry *const &b)
Sort industries by production and name.
GUI functions that shouldn&#39;t be here.
void OnResize() override
Called after the window got resized.
CargoID CargoLabelClickedAt(Point pt) const
Decide what cargo the user clicked in the cargo label field.
bool SortFunction(const T &, const T &)
Signature of sort function.
Definition: sortlist_type.h:51
void SetButtons()
Update status of the fund and display-chain widgets.
call production callback every 256 ticks
struct CargoesField::@17::@18 industry
Industry data (for CFT_INDUSTRY).
int production_offset_y
The offset of the production texts/buttons.
Industry cargoes chain; Window numbers:
Definition: window_type.h:506
void ComputeIndustryDisplay(IndustryType it)
Compute what and where to display for industry type it.
static NWidgetPart SetScrollbar(int index)
Attach a scrollbar to a widget.
Definition: widget_type.h:1095
CompanyID _local_company
Company controlled by the human player at this client. Can also be COMPANY_SPECTATOR.
Definition: company_cmd.cpp:46
static WindowDesc _industry_view_desc(WDP_AUTO, "view_industry", 260, 120, WC_INDUSTRY_VIEW, WC_NONE, 0, _nested_industry_view_widgets, lengthof(_nested_industry_view_widgets))
Window definition of the view industry gui.
Dimensions (a width and height) of a rectangle in 2D.
bool click_beep
Beep on a random selection of buttons.
Offset at left to draw the frame rectangular area.
Definition: window_gui.h:62
Class for backupping variables and making sure they are restored later.
void ShortenCargoColumn(int column, int top, int bottom)
Shorten the cargo column to just the part between industries.
Window * BringWindowToFrontById(WindowClass cls, WindowNumber number)
Find a window and make it the relative top-window on the screen.
Definition: window.cpp:1243
static const int HOR_TEXT_PADDING
Horizontal padding around the industry type text.
Shade box (at top-right of a window, between WWT_DEBUGBOX and WWT_DEFSIZEBOX)
Definition: widget_type.h:64
Display chain button.
void DrawWidget(const Rect &r, int widget) const override
Draw the contents of a nested widget.
build a new industry
Definition: command_type.h:234
int pos_y
Vertical position of top-left corner of the widget in the window.
Definition: widget_type.h:178
void InvalidateData(int data=0, bool gui_scope=true)
Mark this window&#39;s data as invalid (in need of re-computing)
Definition: window.cpp:3240
struct CargoesField::@17::@20 cargo_label
Label data (for CFT_CARGO_LABEL).
void StopTextRefStackUsage()
Stop using the TTDP compatible string code parsing.
int DrawStringMultiLine(int left, int right, int top, int bottom, const char *str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly over multiple lines.
Definition: gfx.cpp:620
(Toggle) Button with text
Definition: widget_type.h:55
static const int VERT_CARGO_EDGE
Amount of vertical space between top/bottom and the top/bottom connected cargo at an industry...
static void MemSetT(T *ptr, byte value, size_t num=1)
Type-safe version of memset().
Definition: mem_func.hpp:51
Cheats _cheats
All the cheats.
Definition: cheat.cpp:18
uint16 GetIndustryCallback(CallbackID callback, uint32 param1, uint32 param2, Industry *industry, IndustryType type, TileIndex tile)
Perform an industry callback.
int selected_index
index of the element in the matrix
11 800 can appear in sub-arctic climate above the snow line
Definition: house.h:81
uint16 GetPosition() const
Gets the position of the first visible element in the list.
Definition: widget_type.h:631
static uint max_cargoes
Largest number of cargoes actually on any industry.
static const NWidgetPart _nested_industry_directory_widgets[]
Widget definition of the industry directory gui.
Production rate of cargo 2.
uint8 SortType() const
Get the sorttype of the list.
Definition: sortlist_type.h:96
static void SetDParam(uint n, uint64 v)
Set a string parameter v at index n in the global string parameter array.
Definition: strings_func.h:201
uint16 callback_timer
timer counter for callback eventual verification