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 
187 
189 static int CDECL 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 : (*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 
226 void CcBuildIndustry(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2)
227 {
228  if (result.Succeeded()) return;
229 
230  uint8 indtype = GB(p1, 0, 8);
231  if (indtype < NUM_INDUSTRYTYPES) {
232  const IndustrySpec *indsp = GetIndustrySpec(indtype);
233  if (indsp->enabled) {
234  SetDParam(0, indsp->name);
235  ShowErrorMessage(STR_ERROR_CAN_T_BUILD_HERE, result.GetErrorMessage(), WL_INFO, TileX(tile) * TILE_SIZE, TileY(tile) * TILE_SIZE);
236  }
237  }
238 }
239 
240 static const NWidgetPart _nested_build_industry_widgets[] = {
242  NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
243  NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetDataTip(STR_FUND_INDUSTRY_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
244  NWidget(WWT_SHADEBOX, COLOUR_DARK_GREEN),
245  NWidget(WWT_DEFSIZEBOX, COLOUR_DARK_GREEN),
246  NWidget(WWT_STICKYBOX, COLOUR_DARK_GREEN),
247  EndContainer(),
249  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),
250  NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, WID_DPI_SCROLLBAR),
251  EndContainer(),
252  NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_DPI_INFOPANEL), SetResize(1, 0),
253  EndContainer(),
255  NWidget(WWT_TEXTBTN, COLOUR_DARK_GREEN, WID_DPI_DISPLAY_WIDGET), SetFill(1, 0), SetResize(1, 0),
256  SetDataTip(STR_INDUSTRY_DISPLAY_CHAIN, STR_INDUSTRY_DISPLAY_CHAIN_TOOLTIP),
257  NWidget(WWT_TEXTBTN, COLOUR_DARK_GREEN, WID_DPI_FUND_WIDGET), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_JUST_STRING, STR_NULL),
258  NWidget(WWT_RESIZEBOX, COLOUR_DARK_GREEN),
259  EndContainer(),
260 };
261 
264  WDP_AUTO, "build_industry", 170, 212,
267  _nested_build_industry_widgets, lengthof(_nested_build_industry_widgets)
268 );
269 
271 class BuildIndustryWindow : public Window {
273  IndustryType selected_type;
274  uint16 callback_timer;
276  uint16 count;
277  IndustryType index[NUM_INDUSTRYTYPES + 1];
278  bool enabled[NUM_INDUSTRYTYPES + 1];
279  Scrollbar *vscroll;
280 
282  static const int MATRIX_TEXT_OFFSET = 17;
284  static const int MAX_MINWIDTH_LINEHEIGHTS = 20;
285 
286  void SetupArrays()
287  {
288  this->count = 0;
289 
290  for (uint i = 0; i < lengthof(this->index); i++) {
291  this->index[i] = INVALID_INDUSTRYTYPE;
292  this->enabled[i] = false;
293  }
294 
295  if (_game_mode == GM_EDITOR) { // give room for the Many Random "button"
296  this->index[this->count] = INVALID_INDUSTRYTYPE;
297  this->enabled[this->count] = true;
298  this->count++;
299  this->timer_enabled = false;
300  }
301  /* Fill the arrays with industries.
302  * The tests performed after the enabled allow to load the industries
303  * In the same way they are inserted by grf (if any)
304  */
305  for (uint i = 0; i < NUM_INDUSTRYTYPES; i++) {
306  IndustryType ind = _sorted_industry_types[i];
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  virtual void OnInit()
407  {
408  this->SetupArrays();
409  }
410 
411  virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
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, NULL, 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, NULL, 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  virtual void SetStringParameters(int widget) const
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  virtual void DrawWidget(const Rect &r, int widget) const
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, NULL, 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, NULL, 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, NULL, 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  virtual void OnClick(Point pt, int widget, int click_count)
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) ? NULL : 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 != NULL && 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  virtual void OnResize()
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  virtual void OnPlaceObject(Point pt, TileIndex tile)
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 
651  if (_game_mode == GM_EDITOR) {
652  /* Show error if no town exists at all */
653  if (Town::GetNumItems() == 0) {
654  SetDParam(0, indsp->name);
655  ShowErrorMessage(STR_ERROR_CAN_T_BUILD_HERE, STR_ERROR_MUST_FOUND_TOWN_FIRST, WL_INFO, pt.x, pt.y);
656  return;
657  }
658 
659  Backup<CompanyByte> cur_company(_current_company, OWNER_NONE, FILE_LINE);
660  _generating_world = true;
661  _ignore_restrictions = true;
662 
663  DoCommandP(tile, (InteractiveRandomRange(indsp->num_table) << 8) | this->selected_type, seed,
664  CMD_BUILD_INDUSTRY | CMD_MSG(STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY), &CcBuildIndustry);
665 
666  cur_company.Restore();
667  _ignore_restrictions = false;
668  _generating_world = false;
669  } else {
670  success = DoCommandP(tile, (InteractiveRandomRange(indsp->num_table) << 8) | this->selected_type, seed, CMD_BUILD_INDUSTRY | CMD_MSG(STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY));
671  }
672 
673  /* If an industry has been built, just reset the cursor and the system */
675  }
676 
677  virtual void OnGameTick()
678  {
679  if (!this->timer_enabled) return;
680  if (--this->callback_timer == 0) {
681  /* We have just passed another day.
682  * See if we need to update availability of currently selected industry */
683  this->callback_timer = DAY_TICKS; // restart counter
684 
685  const IndustrySpec *indsp = GetIndustrySpec(this->selected_type);
686 
687  if (indsp->enabled) {
688  bool call_back_result = GetIndustryProbabilityCallback(this->selected_type, IACT_USERCREATION, 1) > 0;
689 
690  /* Only if result does match the previous state would it require a redraw. */
691  if (call_back_result != this->enabled[this->selected_index]) {
692  this->enabled[this->selected_index] = call_back_result;
693  this->SetButtons();
694  this->SetDirty();
695  }
696  }
697  }
698  }
699 
700  virtual void OnTimeout()
701  {
702  this->RaiseButtons();
703  }
704 
705  virtual void OnPlaceObjectAbort()
706  {
707  this->RaiseButtons();
708  }
709 
715  virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
716  {
717  if (!gui_scope) return;
718  this->SetupArrays();
719 
720  const IndustrySpec *indsp = (this->selected_type == INVALID_INDUSTRYTYPE) ? NULL : GetIndustrySpec(this->selected_type);
721  if (indsp == NULL) this->enabled[this->selected_index] = _settings_game.difficulty.industry_density != ID_FUND_ONLY;
722  this->SetButtons();
723  }
724 };
725 
726 void ShowBuildIndustryWindow()
727 {
728  if (_game_mode != GM_EDITOR && !Company::IsValidID(_local_company)) return;
730  new BuildIndustryWindow();
731 }
732 
733 static void UpdateIndustryProduction(Industry *i);
734 
735 static inline bool IsProductionAlterable(const Industry *i)
736 {
737  const IndustrySpec *is = GetIndustrySpec(i->type);
738  bool has_prod = false;
739  for (size_t j = 0; j < lengthof(is->production_rate); j++) {
740  if (is->production_rate[j] != 0) {
741  has_prod = true;
742  break;
743  }
744  }
745  return ((_game_mode == GM_EDITOR || _cheats.setup_prod.value) &&
746  (has_prod || is->IsRawIndustry()) &&
747  !_networking);
748 }
749 
751 {
753  enum Editability {
757  };
758 
760  enum InfoLine {
765  };
766 
773 
774 public:
775  IndustryViewWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc)
776  {
777  this->flags |= WF_DISABLE_VP_SCROLL;
778  this->editbox_line = IL_NONE;
779  this->clicked_line = IL_NONE;
780  this->clicked_button = 0;
781  this->info_height = WD_FRAMERECT_TOP + 2 * FONT_HEIGHT_NORMAL + WD_FRAMERECT_BOTTOM + 1; // Info panel has at least two lines text.
782 
783  this->InitNested(window_number);
784  NWidgetViewport *nvp = this->GetWidget<NWidgetViewport>(WID_IV_VIEWPORT);
785  nvp->InitializeViewport(this, Industry::Get(window_number)->location.GetCenterTile(), ZOOM_LVL_INDUSTRY);
786 
787  this->InvalidateData();
788  }
789 
790  virtual void OnPaint()
791  {
792  this->DrawWidgets();
793 
794  if (this->IsShaded()) return; // Don't draw anything when the window is shaded.
795 
796  NWidgetBase *nwi = this->GetWidget<NWidgetBase>(WID_IV_INFO);
797  uint expected = this->DrawInfo(nwi->pos_x, nwi->pos_x + nwi->current_x - 1, nwi->pos_y) - nwi->pos_y;
798  if (expected > nwi->current_y - 1) {
799  this->info_height = expected + 1;
800  this->ReInit();
801  return;
802  }
803  }
804 
812  int DrawInfo(uint left, uint right, uint top)
813  {
814  Industry *i = Industry::Get(this->window_number);
815  const IndustrySpec *ind = GetIndustrySpec(i->type);
816  int y = top + WD_FRAMERECT_TOP;
817  bool first = true;
818  bool has_accept = false;
819 
820  if (i->prod_level == PRODLEVEL_CLOSURE) {
821  DrawString(left + WD_FRAMERECT_LEFT, right - WD_FRAMERECT_RIGHT, y, STR_INDUSTRY_VIEW_INDUSTRY_ANNOUNCED_CLOSURE);
822  y += 2 * FONT_HEIGHT_NORMAL;
823  }
824 
825  CargoSuffix cargo_suffix[lengthof(i->accepts_cargo)];
826  GetAllCargoSuffixes(CARGOSUFFIX_IN, CST_VIEW, i, i->type, ind, i->accepts_cargo, cargo_suffix);
828 
829  uint left_side = left + WD_FRAMERECT_LEFT * 4; // Indent accepted cargoes.
830  for (byte j = 0; j < lengthof(i->accepts_cargo); j++) {
831  if (i->accepts_cargo[j] == CT_INVALID) continue;
832  has_accept = true;
833  if (first) {
834  DrawString(left + WD_FRAMERECT_LEFT, right - WD_FRAMERECT_RIGHT, y, STR_INDUSTRY_VIEW_REQUIRES);
835  y += FONT_HEIGHT_NORMAL;
836  first = false;
837  }
839  SetDParam(1, i->accepts_cargo[j]);
841  SetDParamStr(3, "");
842  StringID str = STR_NULL;
843  switch (cargo_suffix[j].display) {
845  SetDParamStr(3, cargo_suffix[j].text);
846  FALLTHROUGH;
847  case CSD_CARGO_AMOUNT:
848  str = stockpiling ? STR_INDUSTRY_VIEW_ACCEPT_CARGO_AMOUNT : STR_INDUSTRY_VIEW_ACCEPT_CARGO;
849  break;
850 
851  case CSD_CARGO_TEXT:
852  SetDParamStr(3, cargo_suffix[j].text);
853  FALLTHROUGH;
854  case CSD_CARGO:
855  str = STR_INDUSTRY_VIEW_ACCEPT_CARGO;
856  break;
857 
858  default:
859  NOT_REACHED();
860  }
861  DrawString(left_side, right - WD_FRAMERECT_RIGHT, y, str);
862  y += FONT_HEIGHT_NORMAL;
863  }
864 
865  GetAllCargoSuffixes(CARGOSUFFIX_OUT, CST_VIEW, i, i->type, ind, i->produced_cargo, cargo_suffix);
866  first = true;
867  for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
868  if (i->produced_cargo[j] == CT_INVALID) continue;
869  if (first) {
870  if (has_accept) y += WD_PAR_VSEP_WIDE;
871  DrawString(left + WD_FRAMERECT_LEFT, right - WD_FRAMERECT_RIGHT, y, STR_INDUSTRY_VIEW_PRODUCTION_LAST_MONTH_TITLE);
872  y += FONT_HEIGHT_NORMAL;
873  if (this->editable == EA_RATE) this->production_offset_y = y;
874  first = false;
875  }
876 
877  SetDParam(0, i->produced_cargo[j]);
879  SetDParamStr(2, cargo_suffix[j].text);
881  uint x = left + WD_FRAMETEXT_LEFT + (this->editable == EA_RATE ? SETTING_BUTTON_WIDTH + 10 : 0);
882  DrawString(x, right - WD_FRAMERECT_RIGHT, y, STR_INDUSTRY_VIEW_TRANSPORTED);
883  /* Let's put out those buttons.. */
884  if (this->editable == EA_RATE) {
885  DrawArrowButtons(left + WD_FRAMETEXT_LEFT, y, COLOUR_YELLOW, (this->clicked_line == IL_RATE1 + j) ? this->clicked_button : 0,
886  i->production_rate[j] > 0, i->production_rate[j] < 255);
887  }
888  y += FONT_HEIGHT_NORMAL;
889  }
890 
891  /* Display production multiplier if editable */
892  if (this->editable == EA_MULTIPLIER) {
893  y += WD_PAR_VSEP_WIDE;
894  this->production_offset_y = y;
896  uint x = left + WD_FRAMETEXT_LEFT + SETTING_BUTTON_WIDTH + 10;
897  DrawString(x, right - WD_FRAMERECT_RIGHT, y, STR_INDUSTRY_VIEW_PRODUCTION_LEVEL);
898  DrawArrowButtons(left + WD_FRAMETEXT_LEFT, y, COLOUR_YELLOW, (this->clicked_line == IL_MULTIPLIER) ? this->clicked_button : 0,
900  y += FONT_HEIGHT_NORMAL;
901  }
902 
903  /* Get the extra message for the GUI */
905  uint16 callback_res = GetIndustryCallback(CBID_INDUSTRY_WINDOW_MORE_TEXT, 0, 0, i, i->type, i->location.tile);
906  if (callback_res != CALLBACK_FAILED && callback_res != 0x400) {
907  if (callback_res > 0x400) {
909  } else {
910  StringID message = GetGRFStringID(ind->grf_prop.grffile->grfid, 0xD000 + callback_res);
911  if (message != STR_NULL && message != STR_UNDEFINED) {
912  y += WD_PAR_VSEP_WIDE;
913 
915  /* Use all the available space left from where we stand up to the
916  * end of the window. We ALSO enlarge the window if needed, so we
917  * can 'go' wild with the bottom of the window. */
918  y = DrawStringMultiLine(left + WD_FRAMERECT_LEFT, right - WD_FRAMERECT_RIGHT, y, UINT16_MAX, message, TC_BLACK);
920  }
921  }
922  }
923  }
924  return y + WD_FRAMERECT_BOTTOM;
925  }
926 
927  virtual void SetStringParameters(int widget) const
928  {
929  if (widget == WID_IV_CAPTION) SetDParam(0, this->window_number);
930  }
931 
932  virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
933  {
934  if (widget == WID_IV_INFO) size->height = this->info_height;
935  }
936 
937  virtual void OnClick(Point pt, int widget, int click_count)
938  {
939  switch (widget) {
940  case WID_IV_INFO: {
941  Industry *i = Industry::Get(this->window_number);
942  InfoLine line = IL_NONE;
943 
944  switch (this->editable) {
945  case EA_NONE: break;
946 
947  case EA_MULTIPLIER:
948  if (IsInsideBS(pt.y, this->production_offset_y, FONT_HEIGHT_NORMAL)) line = IL_MULTIPLIER;
949  break;
950 
951  case EA_RATE:
952  if (pt.y >= this->production_offset_y) {
953  int row = (pt.y - this->production_offset_y) / FONT_HEIGHT_NORMAL;
954  for (uint j = 0; j < lengthof(i->produced_cargo); j++) {
955  if (i->produced_cargo[j] == CT_INVALID) continue;
956  row--;
957  if (row < 0) {
958  line = (InfoLine)(IL_RATE1 + j);
959  break;
960  }
961  }
962  }
963  break;
964  }
965  if (line == IL_NONE) return;
966 
967  NWidgetBase *nwi = this->GetWidget<NWidgetBase>(widget);
968  int left = nwi->pos_x + WD_FRAMETEXT_LEFT;
969  int right = nwi->pos_x + nwi->current_x - 1 - WD_FRAMERECT_RIGHT;
970  if (IsInsideMM(pt.x, left, left + SETTING_BUTTON_WIDTH)) {
971  /* Clicked buttons, decrease or increase production */
972  byte button = (pt.x < left + SETTING_BUTTON_WIDTH / 2) ? 1 : 2;
973  switch (this->editable) {
974  case EA_MULTIPLIER:
975  if (button == 1) {
976  if (i->prod_level <= PRODLEVEL_MINIMUM) return;
977  i->prod_level = max<uint>(i->prod_level / 2, PRODLEVEL_MINIMUM);
978  } else {
979  if (i->prod_level >= PRODLEVEL_MAXIMUM) return;
981  }
982  break;
983 
984  case EA_RATE:
985  if (button == 1) {
986  if (i->production_rate[line - IL_RATE1] <= 0) return;
987  i->production_rate[line - IL_RATE1] = max(i->production_rate[line - IL_RATE1] / 2, 0);
988  } else {
989  if (i->production_rate[line - IL_RATE1] >= 255) return;
990  /* a zero production industry is unlikely to give anything but zero, so push it a little bit */
991  int new_prod = i->production_rate[line - IL_RATE1] == 0 ? 1 : i->production_rate[line - IL_RATE1] * 2;
992  i->production_rate[line - IL_RATE1] = minu(new_prod, 255);
993  }
994  break;
995 
996  default: NOT_REACHED();
997  }
998 
999  UpdateIndustryProduction(i);
1000  this->SetDirty();
1001  this->SetTimeout();
1002  this->clicked_line = line;
1003  this->clicked_button = button;
1004  } else if (IsInsideMM(pt.x, left + SETTING_BUTTON_WIDTH + 10, right)) {
1005  /* clicked the text */
1006  this->editbox_line = line;
1007  switch (this->editable) {
1008  case EA_MULTIPLIER:
1010  ShowQueryString(STR_JUST_INT, STR_CONFIG_GAME_PRODUCTION_LEVEL, 10, this, CS_ALPHANUMERAL, QSF_NONE);
1011  break;
1012 
1013  case EA_RATE:
1014  SetDParam(0, i->production_rate[line - IL_RATE1] * 8);
1015  ShowQueryString(STR_JUST_INT, STR_CONFIG_GAME_PRODUCTION, 10, this, CS_ALPHANUMERAL, QSF_NONE);
1016  break;
1017 
1018  default: NOT_REACHED();
1019  }
1020  }
1021  break;
1022  }
1023 
1024  case WID_IV_GOTO: {
1025  Industry *i = Industry::Get(this->window_number);
1026  if (_ctrl_pressed) {
1028  } else {
1030  }
1031  break;
1032  }
1033 
1034  case WID_IV_DISPLAY: {
1035  Industry *i = Industry::Get(this->window_number);
1037  break;
1038  }
1039  }
1040  }
1041 
1042  virtual void OnTimeout()
1043  {
1044  this->clicked_line = IL_NONE;
1045  this->clicked_button = 0;
1046  this->SetDirty();
1047  }
1048 
1049  virtual void OnResize()
1050  {
1051  if (this->viewport != NULL) {
1052  NWidgetViewport *nvp = this->GetWidget<NWidgetViewport>(WID_IV_VIEWPORT);
1053  nvp->UpdateViewportCoordinates(this);
1054 
1055  ScrollWindowToTile(Industry::Get(this->window_number)->location.GetCenterTile(), this, true); // Re-center viewport.
1056  }
1057  }
1058 
1059  virtual void OnQueryTextFinished(char *str)
1060  {
1061  if (StrEmpty(str)) return;
1062 
1063  Industry *i = Industry::Get(this->window_number);
1064  uint value = atoi(str);
1065  switch (this->editbox_line) {
1066  case IL_NONE: NOT_REACHED();
1067 
1068  case IL_MULTIPLIER:
1070  break;
1071 
1072  default:
1073  i->production_rate[this->editbox_line - IL_RATE1] = ClampU(RoundDivSU(value, 8), 0, 255);
1074  break;
1075  }
1076  UpdateIndustryProduction(i);
1077  this->SetDirty();
1078  }
1079 
1085  virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
1086  {
1087  if (!gui_scope) return;
1088  const Industry *i = Industry::Get(this->window_number);
1089  if (IsProductionAlterable(i)) {
1090  const IndustrySpec *ind = GetIndustrySpec(i->type);
1091  this->editable = ind->UsesSmoothEconomy() ? EA_RATE : EA_MULTIPLIER;
1092  } else {
1093  this->editable = EA_NONE;
1094  }
1095  }
1096 
1097  virtual bool IsNewGRFInspectable() const
1098  {
1099  return ::IsNewGRFInspectable(GSF_INDUSTRIES, this->window_number);
1100  }
1101 
1102  virtual void ShowNewGRFInspectWindow() const
1103  {
1104  ::ShowNewGRFInspectWindow(GSF_INDUSTRIES, this->window_number);
1105  }
1106 };
1107 
1108 static void UpdateIndustryProduction(Industry *i)
1109 {
1110  const IndustrySpec *indspec = GetIndustrySpec(i->type);
1111  if (!indspec->UsesSmoothEconomy()) i->RecomputeProductionMultipliers();
1112 
1113  for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
1114  if (i->produced_cargo[j] != CT_INVALID) {
1115  i->last_month_production[j] = 8 * i->production_rate[j];
1116  }
1117  }
1118 }
1119 
1123  NWidget(WWT_CLOSEBOX, COLOUR_CREAM),
1124  NWidget(WWT_CAPTION, COLOUR_CREAM, WID_IV_CAPTION), SetDataTip(STR_INDUSTRY_VIEW_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1125  NWidget(WWT_DEBUGBOX, COLOUR_CREAM),
1126  NWidget(WWT_SHADEBOX, COLOUR_CREAM),
1127  NWidget(WWT_DEFSIZEBOX, COLOUR_CREAM),
1128  NWidget(WWT_STICKYBOX, COLOUR_CREAM),
1129  EndContainer(),
1130  NWidget(WWT_PANEL, COLOUR_CREAM),
1131  NWidget(WWT_INSET, COLOUR_CREAM), SetPadding(2, 2, 2, 2),
1132  NWidget(NWID_VIEWPORT, INVALID_COLOUR, WID_IV_VIEWPORT), SetMinimalSize(254, 86), SetFill(1, 0), SetPadding(1, 1, 1, 1), SetResize(1, 1),
1133  EndContainer(),
1134  EndContainer(),
1135  NWidget(WWT_PANEL, COLOUR_CREAM, WID_IV_INFO), SetMinimalSize(260, 2), SetResize(1, 0),
1136  EndContainer(),
1138  NWidget(WWT_PUSHTXTBTN, COLOUR_CREAM, WID_IV_GOTO), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_BUTTON_LOCATION, STR_INDUSTRY_VIEW_LOCATION_TOOLTIP),
1139  NWidget(WWT_PUSHTXTBTN, COLOUR_CREAM, WID_IV_DISPLAY), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_INDUSTRY_DISPLAY_CHAIN, STR_INDUSTRY_DISPLAY_CHAIN_TOOLTIP),
1140  NWidget(WWT_RESIZEBOX, COLOUR_CREAM),
1141  EndContainer(),
1142 };
1143 
1146  WDP_AUTO, "view_industry", 260, 120,
1148  0,
1149  _nested_industry_view_widgets, lengthof(_nested_industry_view_widgets)
1150 );
1151 
1152 void ShowIndustryViewWindow(int industry)
1153 {
1154  AllocateWindowDescFront<IndustryViewWindow>(&_industry_view_desc, industry);
1155 }
1156 
1160  NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
1161  NWidget(WWT_CAPTION, COLOUR_BROWN), SetDataTip(STR_INDUSTRY_DIRECTORY_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1162  NWidget(WWT_SHADEBOX, COLOUR_BROWN),
1163  NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
1164  NWidget(WWT_STICKYBOX, COLOUR_BROWN),
1165  EndContainer(),
1169  NWidget(WWT_TEXTBTN, COLOUR_BROWN, WID_ID_DROPDOWN_ORDER), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER),
1170  NWidget(WWT_DROPDOWN, COLOUR_BROWN, WID_ID_DROPDOWN_CRITERIA), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_SORT_CRITERIA),
1171  NWidget(WWT_PANEL, COLOUR_BROWN), SetResize(1, 0), EndContainer(),
1172  EndContainer(),
1173  NWidget(WWT_PANEL, COLOUR_BROWN, WID_ID_INDUSTRY_LIST), SetDataTip(0x0, STR_INDUSTRY_DIRECTORY_LIST_CAPTION), SetResize(1, 1), SetScrollbar(WID_ID_SCROLLBAR), EndContainer(),
1174  EndContainer(),
1176  NWidget(NWID_VSCROLLBAR, COLOUR_BROWN, WID_ID_SCROLLBAR),
1177  NWidget(WWT_RESIZEBOX, COLOUR_BROWN),
1178  EndContainer(),
1179  EndContainer(),
1180 };
1181 
1183 
1184 
1189 protected:
1190  /* Runtime saved values */
1191  static Listing last_sorting;
1192  static const Industry *last_industry;
1193 
1194  /* Constants for sorting stations */
1195  static const StringID sorter_names[];
1196  static GUIIndustryList::SortFunction * const sorter_funcs[];
1197 
1198  GUIIndustryList industries;
1199  Scrollbar *vscroll;
1200 
1203  {
1204  if (this->industries.NeedRebuild()) {
1205  this->industries.Clear();
1206 
1207  const Industry *i;
1208  FOR_ALL_INDUSTRIES(i) {
1209  *this->industries.Append() = i;
1210  }
1211 
1212  this->industries.Compact();
1213  this->industries.RebuildDone();
1214  this->vscroll->SetCount(this->industries.Length()); // Update scrollbar as well.
1215  }
1216 
1217  if (!this->industries.Sort()) return;
1218  IndustryDirectoryWindow::last_industry = NULL; // Reset name sorter sort cache
1219  this->SetWidgetDirty(WID_ID_INDUSTRY_LIST); // Set the modified widget dirty
1220  }
1221 
1229  static inline int GetCargoTransportedPercentsIfValid(const Industry *i, uint id)
1230  {
1231  assert(id < lengthof(i->produced_cargo));
1232 
1233  if (i->produced_cargo[id] == CT_INVALID) return 101;
1234  return ToPercent8(i->last_month_pct_transported[id]);
1235  }
1236 
1245  {
1246  int p1 = GetCargoTransportedPercentsIfValid(i, 0);
1247  int p2 = GetCargoTransportedPercentsIfValid(i, 1);
1248 
1249  if (p1 > p2) Swap(p1, p2); // lower value has higher priority
1250 
1251  return (p1 << 8) + p2;
1252  }
1253 
1255  static int CDECL IndustryNameSorter(const Industry * const *a, const Industry * const *b)
1256  {
1257  static char buf_cache[96];
1258  static char buf[96];
1259 
1260  SetDParam(0, (*a)->index);
1261  GetString(buf, STR_INDUSTRY_NAME, lastof(buf));
1262 
1263  if (*b != last_industry) {
1264  last_industry = *b;
1265  SetDParam(0, (*b)->index);
1266  GetString(buf_cache, STR_INDUSTRY_NAME, lastof(buf_cache));
1267  }
1268 
1269  return strnatcmp(buf, buf_cache); // Sort by name (natural sorting).
1270  }
1271 
1273  static int CDECL IndustryTypeSorter(const Industry * const *a, const Industry * const *b)
1274  {
1275  int it_a = 0;
1276  while (it_a != NUM_INDUSTRYTYPES && (*a)->type != _sorted_industry_types[it_a]) it_a++;
1277  int it_b = 0;
1278  while (it_b != NUM_INDUSTRYTYPES && (*b)->type != _sorted_industry_types[it_b]) it_b++;
1279  int r = it_a - it_b;
1280  return (r == 0) ? IndustryNameSorter(a, b) : r;
1281  }
1282 
1284  static int CDECL IndustryProductionSorter(const Industry * const *a, const Industry * const *b)
1285  {
1286  uint prod_a = 0, prod_b = 0;
1287  for (uint i = 0; i < lengthof((*a)->produced_cargo); i++) {
1288  if ((*a)->produced_cargo[i] != CT_INVALID) prod_a += (*a)->last_month_production[i];
1289  if ((*b)->produced_cargo[i] != CT_INVALID) prod_b += (*b)->last_month_production[i];
1290  }
1291  int r = prod_a - prod_b;
1292 
1293  return (r == 0) ? IndustryTypeSorter(a, b) : r;
1294  }
1295 
1297  static int CDECL IndustryTransportedCargoSorter(const Industry * const *a, const Industry * const *b)
1298  {
1299  int r = GetCargoTransportedSortValue(*a) - GetCargoTransportedSortValue(*b);
1300  return (r == 0) ? IndustryNameSorter(a, b) : r;
1301  }
1302 
1309  {
1310  const IndustrySpec *indsp = GetIndustrySpec(i->type);
1311  byte p = 0;
1312 
1313  /* Industry name */
1314  SetDParam(p++, i->index);
1315 
1316  static CargoSuffix cargo_suffix[lengthof(i->produced_cargo)];
1317  GetAllCargoSuffixes(CARGOSUFFIX_OUT, CST_DIR, i, i->type, indsp, i->produced_cargo, cargo_suffix);
1318 
1319  /* Industry productions */
1320  for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
1321  if (i->produced_cargo[j] == CT_INVALID) continue;
1322  SetDParam(p++, i->produced_cargo[j]);
1323  SetDParam(p++, i->last_month_production[j]);
1324  SetDParamStr(p++, cargo_suffix[j].text);
1325  }
1326 
1327  /* Transported productions */
1328  for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
1329  if (i->produced_cargo[j] == CT_INVALID) continue;
1331  }
1332 
1333  /* Drawing the right string */
1334  switch (p) {
1335  case 1: return STR_INDUSTRY_DIRECTORY_ITEM_NOPROD;
1336  case 5: return STR_INDUSTRY_DIRECTORY_ITEM;
1337  default: return STR_INDUSTRY_DIRECTORY_ITEM_TWO;
1338  }
1339  }
1340 
1341 public:
1342  IndustryDirectoryWindow(WindowDesc *desc, WindowNumber number) : Window(desc)
1343  {
1344  this->CreateNestedTree();
1345  this->vscroll = this->GetScrollbar(WID_ID_SCROLLBAR);
1346 
1347  this->industries.SetListing(this->last_sorting);
1348  this->industries.SetSortFuncs(IndustryDirectoryWindow::sorter_funcs);
1349  this->industries.ForceRebuild();
1350  this->BuildSortIndustriesList();
1351 
1352  this->FinishInitNested(0);
1353  }
1354 
1356  {
1357  this->last_sorting = this->industries.GetListing();
1358  }
1359 
1360  virtual void SetStringParameters(int widget) const
1361  {
1362  if (widget == WID_ID_DROPDOWN_CRITERIA) SetDParam(0, IndustryDirectoryWindow::sorter_names[this->industries.SortType()]);
1363  }
1364 
1365  virtual void DrawWidget(const Rect &r, int widget) const
1366  {
1367  switch (widget) {
1368  case WID_ID_DROPDOWN_ORDER:
1369  this->DrawSortButtonState(widget, this->industries.IsDescSortOrder() ? SBS_DOWN : SBS_UP);
1370  break;
1371 
1372  case WID_ID_INDUSTRY_LIST: {
1373  int n = 0;
1374  int y = r.top + WD_FRAMERECT_TOP;
1375  if (this->industries.Length() == 0) {
1376  DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_INDUSTRY_DIRECTORY_NONE);
1377  break;
1378  }
1379  for (uint i = this->vscroll->GetPosition(); i < this->industries.Length(); i++) {
1380  DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, this->GetIndustryString(this->industries[i]));
1381 
1382  y += this->resize.step_height;
1383  if (++n == this->vscroll->GetCapacity()) break; // max number of industries in 1 window
1384  }
1385  break;
1386  }
1387  }
1388  }
1389 
1390  virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
1391  {
1392  switch (widget) {
1393  case WID_ID_DROPDOWN_ORDER: {
1394  Dimension d = GetStringBoundingBox(this->GetWidget<NWidgetCore>(widget)->widget_data);
1395  d.width += padding.width + Window::SortButtonWidth() * 2; // Doubled since the string is centred and it also looks better.
1396  d.height += padding.height;
1397  *size = maxdim(*size, d);
1398  break;
1399  }
1400 
1401  case WID_ID_DROPDOWN_CRITERIA: {
1402  Dimension d = {0, 0};
1403  for (uint i = 0; IndustryDirectoryWindow::sorter_names[i] != INVALID_STRING_ID; i++) {
1404  d = maxdim(d, GetStringBoundingBox(IndustryDirectoryWindow::sorter_names[i]));
1405  }
1406  d.width += padding.width;
1407  d.height += padding.height;
1408  *size = maxdim(*size, d);
1409  break;
1410  }
1411 
1412  case WID_ID_INDUSTRY_LIST: {
1413  Dimension d = GetStringBoundingBox(STR_INDUSTRY_DIRECTORY_NONE);
1414  for (uint i = 0; i < this->industries.Length(); i++) {
1415  d = maxdim(d, GetStringBoundingBox(this->GetIndustryString(this->industries[i])));
1416  }
1417  resize->height = d.height;
1418  d.height *= 5;
1419  d.width += padding.width + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
1420  d.height += padding.height + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
1421  *size = maxdim(*size, d);
1422  break;
1423  }
1424  }
1425  }
1426 
1427 
1428  virtual void OnClick(Point pt, int widget, int click_count)
1429  {
1430  switch (widget) {
1431  case WID_ID_DROPDOWN_ORDER:
1432  this->industries.ToggleSortOrder();
1433  this->SetDirty();
1434  break;
1435 
1437  ShowDropDownMenu(this, IndustryDirectoryWindow::sorter_names, this->industries.SortType(), WID_ID_DROPDOWN_CRITERIA, 0, 0);
1438  break;
1439 
1440  case WID_ID_INDUSTRY_LIST: {
1441  uint p = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_ID_INDUSTRY_LIST, WD_FRAMERECT_TOP);
1442  if (p < this->industries.Length()) {
1443  if (_ctrl_pressed) {
1444  ShowExtraViewPortWindow(this->industries[p]->location.tile);
1445  } else {
1446  ScrollMainWindowToTile(this->industries[p]->location.tile);
1447  }
1448  }
1449  break;
1450  }
1451  }
1452  }
1453 
1454  virtual void OnDropdownSelect(int widget, int index)
1455  {
1456  if (this->industries.SortType() != index) {
1457  this->industries.SetSortType(index);
1458  this->BuildSortIndustriesList();
1459  }
1460  }
1461 
1462  virtual void OnResize()
1463  {
1464  this->vscroll->SetCapacityFromWidget(this, WID_ID_INDUSTRY_LIST);
1465  }
1466 
1467  virtual void OnPaint()
1468  {
1469  if (this->industries.NeedRebuild()) this->BuildSortIndustriesList();
1470  this->DrawWidgets();
1471  }
1472 
1473  virtual void OnHundredthTick()
1474  {
1475  this->industries.ForceResort();
1476  this->BuildSortIndustriesList();
1477  }
1478 
1484  virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
1485  {
1486  if (data == 0) {
1487  /* This needs to be done in command-scope to enforce rebuilding before resorting invalid data */
1488  this->industries.ForceRebuild();
1489  } else {
1490  this->industries.ForceResort();
1491  }
1492  }
1493 };
1494 
1495 Listing IndustryDirectoryWindow::last_sorting = {false, 0};
1496 const Industry *IndustryDirectoryWindow::last_industry = NULL;
1497 
1498 /* Available station sorting functions. */
1499 GUIIndustryList::SortFunction * const IndustryDirectoryWindow::sorter_funcs[] = {
1500  &IndustryNameSorter,
1501  &IndustryTypeSorter,
1502  &IndustryProductionSorter,
1503  &IndustryTransportedCargoSorter
1504 };
1505 
1506 /* Names of the sorting functions */
1507 const StringID IndustryDirectoryWindow::sorter_names[] = {
1508  STR_SORT_BY_NAME,
1509  STR_SORT_BY_TYPE,
1510  STR_SORT_BY_PRODUCTION,
1511  STR_SORT_BY_TRANSPORTED,
1513 };
1514 
1515 
1518  WDP_AUTO, "list_industries", 428, 190,
1520  0,
1521  _nested_industry_directory_widgets, lengthof(_nested_industry_directory_widgets)
1522 );
1523 
1524 void ShowIndustryDirectory()
1525 {
1526  AllocateWindowDescFront<IndustryDirectoryWindow>(&_industry_directory_desc, 0);
1527 }
1528 
1532  NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
1533  NWidget(WWT_CAPTION, COLOUR_BROWN, WID_IC_CAPTION), SetDataTip(STR_INDUSTRY_CARGOES_INDUSTRY_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1534  NWidget(WWT_SHADEBOX, COLOUR_BROWN),
1535  NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
1536  NWidget(WWT_STICKYBOX, COLOUR_BROWN),
1537  EndContainer(),
1542  NWidget(WWT_TEXTBTN, COLOUR_BROWN, WID_IC_NOTIFY),
1543  SetDataTip(STR_INDUSTRY_CARGOES_NOTIFY_SMALLMAP, STR_INDUSTRY_CARGOES_NOTIFY_SMALLMAP_TOOLTIP),
1544  NWidget(WWT_PANEL, COLOUR_BROWN), SetFill(1, 0), SetResize(0, 0), EndContainer(),
1545  NWidget(WWT_DROPDOWN, COLOUR_BROWN, WID_IC_IND_DROPDOWN), SetFill(0, 0), SetResize(0, 0),
1546  SetDataTip(STR_INDUSTRY_CARGOES_SELECT_INDUSTRY, STR_INDUSTRY_CARGOES_SELECT_INDUSTRY_TOOLTIP),
1547  NWidget(WWT_DROPDOWN, COLOUR_BROWN, WID_IC_CARGO_DROPDOWN), SetFill(0, 0), SetResize(0, 0),
1548  SetDataTip(STR_INDUSTRY_CARGOES_SELECT_CARGO, STR_INDUSTRY_CARGOES_SELECT_CARGO_TOOLTIP),
1549  EndContainer(),
1550  EndContainer(),
1552  NWidget(NWID_VSCROLLBAR, COLOUR_BROWN, WID_IC_SCROLLBAR),
1553  NWidget(WWT_RESIZEBOX, COLOUR_BROWN),
1554  EndContainer(),
1555  EndContainer(),
1556 };
1557 
1560  WDP_AUTO, "industry_cargoes", 300, 210,
1562  0,
1563  _nested_industry_cargoes_widgets, lengthof(_nested_industry_cargoes_widgets)
1564 );
1565 
1574 };
1575 
1576 static const uint MAX_CARGOES = 16;
1577 
1580  static const int VERT_INTER_INDUSTRY_SPACE;
1581  static const int HOR_CARGO_BORDER_SPACE;
1582  static const int CARGO_STUB_WIDTH;
1583  static const int HOR_CARGO_WIDTH, HOR_CARGO_SPACE;
1584  static const int VERT_CARGO_SPACE, VERT_CARGO_EDGE;
1585  static const int BLOB_DISTANCE, BLOB_WIDTH, BLOB_HEIGHT;
1586 
1587  static const int INDUSTRY_LINE_COLOUR;
1588  static const int CARGO_LINE_COLOUR;
1589 
1590  static int small_height, normal_height;
1591  static int cargo_field_width;
1592  static int industry_width;
1593  static uint max_cargoes;
1594 
1596  union {
1597  struct {
1598  IndustryType ind_type;
1599  CargoID other_produced[MAX_CARGOES];
1600  CargoID other_accepted[MAX_CARGOES];
1601  } industry;
1602  struct {
1603  CargoID vertical_cargoes[MAX_CARGOES];
1605  CargoID supp_cargoes[MAX_CARGOES];
1606  byte top_end;
1607  CargoID cust_cargoes[MAX_CARGOES];
1608  byte bottom_end;
1609  } cargo;
1610  struct {
1612  bool left_align;
1613  } cargo_label;
1615  } u; // Data for each type.
1616 
1622  {
1623  this->type = type;
1624  }
1625 
1631  void MakeIndustry(IndustryType ind_type)
1632  {
1633  this->type = CFT_INDUSTRY;
1634  this->u.industry.ind_type = ind_type;
1635  MemSetT(this->u.industry.other_accepted, INVALID_CARGO, MAX_CARGOES);
1636  MemSetT(this->u.industry.other_produced, INVALID_CARGO, MAX_CARGOES);
1637  }
1638 
1645  int ConnectCargo(CargoID cargo, bool producer)
1646  {
1647  assert(this->type == CFT_CARGO);
1648  if (cargo == INVALID_CARGO) return -1;
1649 
1650  /* Find the vertical cargo column carrying the cargo. */
1651  int column = -1;
1652  for (int i = 0; i < this->u.cargo.num_cargoes; i++) {
1653  if (cargo == this->u.cargo.vertical_cargoes[i]) {
1654  column = i;
1655  break;
1656  }
1657  }
1658  if (column < 0) return -1;
1659 
1660  if (producer) {
1661  assert(this->u.cargo.supp_cargoes[column] == INVALID_CARGO);
1662  this->u.cargo.supp_cargoes[column] = column;
1663  } else {
1664  assert(this->u.cargo.cust_cargoes[column] == INVALID_CARGO);
1665  this->u.cargo.cust_cargoes[column] = column;
1666  }
1667  return column;
1668  }
1669 
1675  {
1676  assert(this->type == CFT_CARGO);
1677 
1678  for (uint i = 0; i < MAX_CARGOES; i++) {
1679  if (this->u.cargo.supp_cargoes[i] != INVALID_CARGO) return true;
1680  if (this->u.cargo.cust_cargoes[i] != INVALID_CARGO) return true;
1681  }
1682  return false;
1683  }
1684 
1694  void MakeCargo(const CargoID *cargoes, uint length, int count = -1, bool top_end = false, bool bottom_end = false)
1695  {
1696  this->type = CFT_CARGO;
1697  uint i;
1698  uint num = 0;
1699  for (i = 0; i < MAX_CARGOES && i < length; i++) {
1700  if (cargoes[i] != INVALID_CARGO) {
1701  this->u.cargo.vertical_cargoes[num] = cargoes[i];
1702  num++;
1703  }
1704  }
1705  this->u.cargo.num_cargoes = (count < 0) ? num : count;
1706  for (; num < MAX_CARGOES; num++) this->u.cargo.vertical_cargoes[num] = INVALID_CARGO;
1707  this->u.cargo.top_end = top_end;
1708  this->u.cargo.bottom_end = bottom_end;
1709  MemSetT(this->u.cargo.supp_cargoes, INVALID_CARGO, MAX_CARGOES);
1710  MemSetT(this->u.cargo.cust_cargoes, INVALID_CARGO, MAX_CARGOES);
1711  }
1712 
1719  void MakeCargoLabel(const CargoID *cargoes, uint length, bool left_align)
1720  {
1721  this->type = CFT_CARGO_LABEL;
1722  uint i;
1723  for (i = 0; i < MAX_CARGOES && i < length; i++) this->u.cargo_label.cargoes[i] = cargoes[i];
1724  for (; i < MAX_CARGOES; i++) this->u.cargo_label.cargoes[i] = INVALID_CARGO;
1725  this->u.cargo_label.left_align = left_align;
1726  }
1727 
1732  void MakeHeader(StringID textid)
1733  {
1734  this->type = CFT_HEADER;
1735  this->u.header = textid;
1736  }
1737 
1743  int GetCargoBase(int xpos) const
1744  {
1745  assert(this->type == CFT_CARGO);
1746  int n = this->u.cargo.num_cargoes;
1747 
1748  if (n % 2 == 0) {
1749  return xpos + cargo_field_width / 2 - (HOR_CARGO_WIDTH + HOR_CARGO_SPACE / 2) * (n / 2);
1750  } else {
1751  return xpos + cargo_field_width / 2 - HOR_CARGO_WIDTH / 2 - (HOR_CARGO_WIDTH + HOR_CARGO_SPACE) * (n / 2);
1752  }
1753  }
1754 
1760  void Draw(int xpos, int ypos) const
1761  {
1762  switch (this->type) {
1763  case CFT_EMPTY:
1764  case CFT_SMALL_EMPTY:
1765  break;
1766 
1767  case CFT_HEADER:
1768  ypos += (small_height - FONT_HEIGHT_NORMAL) / 2;
1769  DrawString(xpos, xpos + industry_width, ypos, this->u.header, TC_WHITE, SA_HOR_CENTER);
1770  break;
1771 
1772  case CFT_INDUSTRY: {
1773  int ypos1 = ypos + VERT_INTER_INDUSTRY_SPACE / 2;
1774  int ypos2 = ypos + normal_height - 1 - VERT_INTER_INDUSTRY_SPACE / 2;
1775  int xpos2 = xpos + industry_width - 1;
1776  GfxDrawLine(xpos, ypos1, xpos2, ypos1, INDUSTRY_LINE_COLOUR);
1777  GfxDrawLine(xpos, ypos1, xpos, ypos2, INDUSTRY_LINE_COLOUR);
1778  GfxDrawLine(xpos, ypos2, xpos2, ypos2, INDUSTRY_LINE_COLOUR);
1779  GfxDrawLine(xpos2, ypos1, xpos2, ypos2, INDUSTRY_LINE_COLOUR);
1780  ypos += (normal_height - FONT_HEIGHT_NORMAL) / 2;
1781  if (this->u.industry.ind_type < NUM_INDUSTRYTYPES) {
1782  const IndustrySpec *indsp = GetIndustrySpec(this->u.industry.ind_type);
1783  DrawString(xpos, xpos2, ypos, indsp->name, TC_WHITE, SA_HOR_CENTER);
1784 
1785  /* Draw the industry legend. */
1786  int blob_left, blob_right;
1787  if (_current_text_dir == TD_RTL) {
1788  blob_right = xpos2 - BLOB_DISTANCE;
1789  blob_left = blob_right - BLOB_WIDTH;
1790  } else {
1791  blob_left = xpos + BLOB_DISTANCE;
1792  blob_right = blob_left + BLOB_WIDTH;
1793  }
1794  GfxFillRect(blob_left, ypos2 - BLOB_DISTANCE - BLOB_HEIGHT, blob_right, ypos2 - BLOB_DISTANCE, PC_BLACK); // Border
1795  GfxFillRect(blob_left + 1, ypos2 - BLOB_DISTANCE - BLOB_HEIGHT + 1, blob_right - 1, ypos2 - BLOB_DISTANCE - 1, indsp->map_colour);
1796  } else {
1797  DrawString(xpos, xpos2, ypos, STR_INDUSTRY_CARGOES_HOUSES, TC_FROMSTRING, SA_HOR_CENTER);
1798  }
1799 
1800  /* Draw the other_produced/other_accepted cargoes. */
1801  const CargoID *other_right, *other_left;
1802  if (_current_text_dir == TD_RTL) {
1803  other_right = this->u.industry.other_accepted;
1804  other_left = this->u.industry.other_produced;
1805  } else {
1806  other_right = this->u.industry.other_produced;
1807  other_left = this->u.industry.other_accepted;
1808  }
1809  ypos1 += VERT_CARGO_EDGE;
1810  for (uint i = 0; i < CargoesField::max_cargoes; i++) {
1811  if (other_right[i] != INVALID_CARGO) {
1812  const CargoSpec *csp = CargoSpec::Get(other_right[i]);
1813  int xp = xpos + industry_width + CARGO_STUB_WIDTH;
1814  DrawHorConnection(xpos + industry_width, xp - 1, ypos1, csp);
1815  GfxDrawLine(xp, ypos1, xp, ypos1 + FONT_HEIGHT_NORMAL - 1, CARGO_LINE_COLOUR);
1816  }
1817  if (other_left[i] != INVALID_CARGO) {
1818  const CargoSpec *csp = CargoSpec::Get(other_left[i]);
1819  int xp = xpos - CARGO_STUB_WIDTH;
1820  DrawHorConnection(xp + 1, xpos - 1, ypos1, csp);
1821  GfxDrawLine(xp, ypos1, xp, ypos1 + FONT_HEIGHT_NORMAL - 1, CARGO_LINE_COLOUR);
1822  }
1823  ypos1 += FONT_HEIGHT_NORMAL + VERT_CARGO_SPACE;
1824  }
1825  break;
1826  }
1827 
1828  case CFT_CARGO: {
1829  int cargo_base = this->GetCargoBase(xpos);
1830  int top = ypos + (this->u.cargo.top_end ? VERT_INTER_INDUSTRY_SPACE / 2 + 1 : 0);
1831  int bot = ypos - (this->u.cargo.bottom_end ? VERT_INTER_INDUSTRY_SPACE / 2 + 1 : 0) + normal_height - 1;
1832  int colpos = cargo_base;
1833  for (int i = 0; i < this->u.cargo.num_cargoes; i++) {
1834  if (this->u.cargo.top_end) GfxDrawLine(colpos, top - 1, colpos + HOR_CARGO_WIDTH - 1, top - 1, CARGO_LINE_COLOUR);
1835  if (this->u.cargo.bottom_end) GfxDrawLine(colpos, bot + 1, colpos + HOR_CARGO_WIDTH - 1, bot + 1, CARGO_LINE_COLOUR);
1836  GfxDrawLine(colpos, top, colpos, bot, CARGO_LINE_COLOUR);
1837  colpos++;
1838  const CargoSpec *csp = CargoSpec::Get(this->u.cargo.vertical_cargoes[i]);
1839  GfxFillRect(colpos, top, colpos + HOR_CARGO_WIDTH - 2, bot, csp->legend_colour, FILLRECT_OPAQUE);
1840  colpos += HOR_CARGO_WIDTH - 2;
1841  GfxDrawLine(colpos, top, colpos, bot, CARGO_LINE_COLOUR);
1842  colpos += 1 + HOR_CARGO_SPACE;
1843  }
1844 
1845  const CargoID *hor_left, *hor_right;
1846  if (_current_text_dir == TD_RTL) {
1847  hor_left = this->u.cargo.cust_cargoes;
1848  hor_right = this->u.cargo.supp_cargoes;
1849  } else {
1850  hor_left = this->u.cargo.supp_cargoes;
1851  hor_right = this->u.cargo.cust_cargoes;
1852  }
1853  ypos += VERT_CARGO_EDGE + VERT_INTER_INDUSTRY_SPACE / 2;
1854  for (uint i = 0; i < MAX_CARGOES; i++) {
1855  if (hor_left[i] != INVALID_CARGO) {
1856  int col = hor_left[i];
1857  int dx = 0;
1858  const CargoSpec *csp = CargoSpec::Get(this->u.cargo.vertical_cargoes[col]);
1859  for (; col > 0; col--) {
1860  int lf = cargo_base + col * HOR_CARGO_WIDTH + (col - 1) * HOR_CARGO_SPACE;
1861  DrawHorConnection(lf, lf + HOR_CARGO_SPACE - dx, ypos, csp);
1862  dx = 1;
1863  }
1864  DrawHorConnection(xpos, cargo_base - dx, ypos, csp);
1865  }
1866  if (hor_right[i] != INVALID_CARGO) {
1867  int col = hor_right[i];
1868  int dx = 0;
1869  const CargoSpec *csp = CargoSpec::Get(this->u.cargo.vertical_cargoes[col]);
1870  for (; col < this->u.cargo.num_cargoes - 1; col++) {
1871  int lf = cargo_base + (col + 1) * HOR_CARGO_WIDTH + col * HOR_CARGO_SPACE;
1872  DrawHorConnection(lf + dx - 1, lf + HOR_CARGO_SPACE - 1, ypos, csp);
1873  dx = 1;
1874  }
1875  DrawHorConnection(cargo_base + col * HOR_CARGO_SPACE + (col + 1) * HOR_CARGO_WIDTH - 1 + dx, xpos + CargoesField::cargo_field_width - 1, ypos, csp);
1876  }
1877  ypos += FONT_HEIGHT_NORMAL + VERT_CARGO_SPACE;
1878  }
1879  break;
1880  }
1881 
1882  case CFT_CARGO_LABEL:
1883  ypos += VERT_CARGO_EDGE + VERT_INTER_INDUSTRY_SPACE / 2;
1884  for (uint i = 0; i < MAX_CARGOES; i++) {
1885  if (this->u.cargo_label.cargoes[i] != INVALID_CARGO) {
1886  const CargoSpec *csp = CargoSpec::Get(this->u.cargo_label.cargoes[i]);
1887  DrawString(xpos + WD_FRAMERECT_LEFT, xpos + industry_width - 1 - WD_FRAMERECT_RIGHT, ypos, csp->name, TC_WHITE,
1888  (this->u.cargo_label.left_align) ? SA_LEFT : SA_RIGHT);
1889  }
1890  ypos += FONT_HEIGHT_NORMAL + VERT_CARGO_SPACE;
1891  }
1892  break;
1893 
1894  default:
1895  NOT_REACHED();
1896  }
1897  }
1898 
1906  CargoID CargoClickedAt(const CargoesField *left, const CargoesField *right, Point pt) const
1907  {
1908  assert(this->type == CFT_CARGO);
1909 
1910  /* Vertical matching. */
1911  int cpos = this->GetCargoBase(0);
1912  uint col;
1913  for (col = 0; col < this->u.cargo.num_cargoes; col++) {
1914  if (pt.x < cpos) break;
1915  if (pt.x < cpos + CargoesField::HOR_CARGO_WIDTH) return this->u.cargo.vertical_cargoes[col];
1917  }
1918  /* col = 0 -> left of first col, 1 -> left of 2nd col, ... this->u.cargo.num_cargoes right of last-col. */
1919 
1920  int vpos = VERT_INTER_INDUSTRY_SPACE / 2 + VERT_CARGO_EDGE;
1921  uint row;
1922  for (row = 0; row < MAX_CARGOES; row++) {
1923  if (pt.y < vpos) return INVALID_CARGO;
1924  if (pt.y < vpos + FONT_HEIGHT_NORMAL) break;
1925  vpos += FONT_HEIGHT_NORMAL + VERT_CARGO_SPACE;
1926  }
1927  if (row == MAX_CARGOES) return INVALID_CARGO;
1928 
1929  /* row = 0 -> at first horizontal row, row = 1 -> second horizontal row, 2 = 3rd horizontal row. */
1930  if (col == 0) {
1931  if (this->u.cargo.supp_cargoes[row] != INVALID_CARGO) return this->u.cargo.vertical_cargoes[this->u.cargo.supp_cargoes[row]];
1932  if (left != NULL) {
1933  if (left->type == CFT_INDUSTRY) return left->u.industry.other_produced[row];
1934  if (left->type == CFT_CARGO_LABEL && !left->u.cargo_label.left_align) return left->u.cargo_label.cargoes[row];
1935  }
1936  return INVALID_CARGO;
1937  }
1938  if (col == this->u.cargo.num_cargoes) {
1939  if (this->u.cargo.cust_cargoes[row] != INVALID_CARGO) return this->u.cargo.vertical_cargoes[this->u.cargo.cust_cargoes[row]];
1940  if (right != NULL) {
1941  if (right->type == CFT_INDUSTRY) return right->u.industry.other_accepted[row];
1942  if (right->type == CFT_CARGO_LABEL && right->u.cargo_label.left_align) return right->u.cargo_label.cargoes[row];
1943  }
1944  return INVALID_CARGO;
1945  }
1946  if (row >= col) {
1947  /* Clicked somewhere in-between vertical cargo connection.
1948  * Since the horizontal connection is made in the same order as the vertical list, the above condition
1949  * ensures we are left-below the main diagonal, thus at the supplying side.
1950  */
1951  return (this->u.cargo.supp_cargoes[row] != INVALID_CARGO) ? this->u.cargo.vertical_cargoes[this->u.cargo.supp_cargoes[row]] : INVALID_CARGO;
1952  } else {
1953  /* Clicked at a customer connection. */
1954  return (this->u.cargo.cust_cargoes[row] != INVALID_CARGO) ? this->u.cargo.vertical_cargoes[this->u.cargo.cust_cargoes[row]] : INVALID_CARGO;
1955  }
1956  }
1957 
1964  {
1965  assert(this->type == CFT_CARGO_LABEL);
1966 
1967  int vpos = VERT_INTER_INDUSTRY_SPACE / 2 + VERT_CARGO_EDGE;
1968  uint row;
1969  for (row = 0; row < MAX_CARGOES; row++) {
1970  if (pt.y < vpos) return INVALID_CARGO;
1971  if (pt.y < vpos + FONT_HEIGHT_NORMAL) break;
1972  vpos += FONT_HEIGHT_NORMAL + VERT_CARGO_SPACE;
1973  }
1974  if (row == MAX_CARGOES) return INVALID_CARGO;
1975  return this->u.cargo_label.cargoes[row];
1976  }
1977 
1978 private:
1986  static void DrawHorConnection(int left, int right, int top, const CargoSpec *csp)
1987  {
1988  GfxDrawLine(left, top, right, top, CARGO_LINE_COLOUR);
1989  GfxFillRect(left, top + 1, right, top + FONT_HEIGHT_NORMAL - 2, csp->legend_colour, FILLRECT_OPAQUE);
1990  GfxDrawLine(left, top + FONT_HEIGHT_NORMAL - 1, right, top + FONT_HEIGHT_NORMAL - 1, CARGO_LINE_COLOUR);
1991  }
1992 };
1993 
1994 assert_compile(MAX_CARGOES >= cpp_lengthof(IndustrySpec, produced_cargo));
1995 assert_compile(MAX_CARGOES >= cpp_lengthof(IndustrySpec, accepts_cargo));
1996 
2003 
2004 const int CargoesField::HOR_CARGO_BORDER_SPACE = 15;
2005 const int CargoesField::CARGO_STUB_WIDTH = 10;
2006 const int CargoesField::HOR_CARGO_WIDTH = 15;
2007 const int CargoesField::HOR_CARGO_SPACE = 5;
2008 const int CargoesField::VERT_CARGO_EDGE = 4;
2009 const int CargoesField::VERT_CARGO_SPACE = 4;
2010 
2011 const int CargoesField::BLOB_DISTANCE = 5;
2012 const int CargoesField::BLOB_WIDTH = 12;
2013 const int CargoesField::BLOB_HEIGHT = 9;
2014 
2017 
2019 struct CargoesRow {
2020  CargoesField columns[5];
2021 
2026  void ConnectIndustryProduced(int column)
2027  {
2028  CargoesField *ind_fld = this->columns + column;
2029  CargoesField *cargo_fld = this->columns + column + 1;
2030  assert(ind_fld->type == CFT_INDUSTRY && cargo_fld->type == CFT_CARGO);
2031 
2032  MemSetT(ind_fld->u.industry.other_produced, INVALID_CARGO, MAX_CARGOES);
2033 
2034  if (ind_fld->u.industry.ind_type < NUM_INDUSTRYTYPES) {
2035  CargoID others[MAX_CARGOES]; // Produced cargoes not carried in the cargo column.
2036  int other_count = 0;
2037 
2038  const IndustrySpec *indsp = GetIndustrySpec(ind_fld->u.industry.ind_type);
2039  assert(CargoesField::max_cargoes <= lengthof(indsp->produced_cargo));
2040  for (uint i = 0; i < CargoesField::max_cargoes; i++) {
2041  int col = cargo_fld->ConnectCargo(indsp->produced_cargo[i], true);
2042  if (col < 0) others[other_count++] = indsp->produced_cargo[i];
2043  }
2044 
2045  /* Allocate other cargoes in the empty holes of the horizontal cargo connections. */
2046  for (uint i = 0; i < CargoesField::max_cargoes && other_count > 0; i++) {
2047  if (cargo_fld->u.cargo.supp_cargoes[i] == INVALID_CARGO) ind_fld->u.industry.other_produced[i] = others[--other_count];
2048  }
2049  } else {
2050  /* Houses only display what is demanded. */
2051  for (uint i = 0; i < cargo_fld->u.cargo.num_cargoes; i++) {
2052  CargoID cid = cargo_fld->u.cargo.vertical_cargoes[i];
2053  if (cid == CT_PASSENGERS || cid == CT_MAIL) cargo_fld->ConnectCargo(cid, true);
2054  }
2055  }
2056  }
2057 
2063  void MakeCargoLabel(int column, bool accepting)
2064  {
2065  CargoID cargoes[MAX_CARGOES];
2066  MemSetT(cargoes, INVALID_CARGO, lengthof(cargoes));
2067 
2068  CargoesField *label_fld = this->columns + column;
2069  CargoesField *cargo_fld = this->columns + (accepting ? column - 1 : column + 1);
2070 
2071  assert(cargo_fld->type == CFT_CARGO && label_fld->type == CFT_EMPTY);
2072  for (uint i = 0; i < cargo_fld->u.cargo.num_cargoes; i++) {
2073  int col = cargo_fld->ConnectCargo(cargo_fld->u.cargo.vertical_cargoes[i], !accepting);
2074  if (col >= 0) cargoes[col] = cargo_fld->u.cargo.vertical_cargoes[i];
2075  }
2076  label_fld->MakeCargoLabel(cargoes, lengthof(cargoes), accepting);
2077  }
2078 
2079 
2084  void ConnectIndustryAccepted(int column)
2085  {
2086  CargoesField *ind_fld = this->columns + column;
2087  CargoesField *cargo_fld = this->columns + column - 1;
2088  assert(ind_fld->type == CFT_INDUSTRY && cargo_fld->type == CFT_CARGO);
2089 
2090  MemSetT(ind_fld->u.industry.other_accepted, INVALID_CARGO, MAX_CARGOES);
2091 
2092  if (ind_fld->u.industry.ind_type < NUM_INDUSTRYTYPES) {
2093  CargoID others[MAX_CARGOES]; // Accepted cargoes not carried in the cargo column.
2094  int other_count = 0;
2095 
2096  const IndustrySpec *indsp = GetIndustrySpec(ind_fld->u.industry.ind_type);
2098  for (uint i = 0; i < CargoesField::max_cargoes; i++) {
2099  int col = cargo_fld->ConnectCargo(indsp->accepts_cargo[i], false);
2100  if (col < 0) others[other_count++] = indsp->accepts_cargo[i];
2101  }
2102 
2103  /* Allocate other cargoes in the empty holes of the horizontal cargo connections. */
2104  for (uint i = 0; i < CargoesField::max_cargoes && other_count > 0; i++) {
2105  if (cargo_fld->u.cargo.cust_cargoes[i] == INVALID_CARGO) ind_fld->u.industry.other_accepted[i] = others[--other_count];
2106  }
2107  } else {
2108  /* Houses only display what is demanded. */
2109  for (uint i = 0; i < cargo_fld->u.cargo.num_cargoes; i++) {
2110  for (uint h = 0; h < NUM_HOUSES; h++) {
2111  HouseSpec *hs = HouseSpec::Get(h);
2112  if (!hs->enabled) continue;
2113 
2114  for (uint j = 0; j < lengthof(hs->accepts_cargo); j++) {
2115  if (hs->cargo_acceptance[j] > 0 && cargo_fld->u.cargo.vertical_cargoes[i] == hs->accepts_cargo[j]) {
2116  cargo_fld->ConnectCargo(cargo_fld->u.cargo.vertical_cargoes[i], false);
2117  goto next_cargo;
2118  }
2119  }
2120  }
2121 next_cargo: ;
2122  }
2123  }
2124  }
2125 };
2126 
2127 
2156  static const int HOR_TEXT_PADDING, VERT_TEXT_PADDING;
2157 
2159 
2160  Fields fields;
2161  uint ind_cargo;
2164  Scrollbar *vscroll;
2165 
2166  IndustryCargoesWindow(int id) : Window(&_industry_cargoes_desc)
2167  {
2168  this->OnInit();
2169  this->CreateNestedTree();
2170  this->vscroll = this->GetScrollbar(WID_IC_SCROLLBAR);
2171  this->FinishInitNested(0);
2172  this->OnInvalidateData(id);
2173  }
2174 
2175  virtual void OnInit()
2176  {
2177  /* Initialize static CargoesField size variables. */
2178  Dimension d = GetStringBoundingBox(STR_INDUSTRY_CARGOES_PRODUCERS);
2179  d = maxdim(d, GetStringBoundingBox(STR_INDUSTRY_CARGOES_CUSTOMERS));
2181  d.height += WD_FRAMETEXT_TOP + WD_FRAMETEXT_BOTTOM;
2182  CargoesField::small_height = d.height;
2183 
2184  /* Decide about the size of the box holding the text of an industry type. */
2185  this->ind_textsize.width = 0;
2186  this->ind_textsize.height = 0;
2188  for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
2189  const IndustrySpec *indsp = GetIndustrySpec(it);
2190  if (!indsp->enabled) continue;
2191  this->ind_textsize = maxdim(this->ind_textsize, GetStringBoundingBox(indsp->name));
2193  CargoesField::max_cargoes = max<uint>(CargoesField::max_cargoes, std::count_if(indsp->produced_cargo, endof(indsp->produced_cargo), IsCargoIDValid));
2194  }
2195  d.width = max(d.width, this->ind_textsize.width);
2196  d.height = this->ind_textsize.height;
2197  this->ind_textsize = maxdim(this->ind_textsize, GetStringBoundingBox(STR_INDUSTRY_CARGOES_SELECT_INDUSTRY));
2198 
2199  /* Compute max size of the cargo texts. */
2200  this->cargo_textsize.width = 0;
2201  this->cargo_textsize.height = 0;
2202  for (uint i = 0; i < NUM_CARGO; i++) {
2203  const CargoSpec *csp = CargoSpec::Get(i);
2204  if (!csp->IsValid()) continue;
2205  this->cargo_textsize = maxdim(this->cargo_textsize, GetStringBoundingBox(csp->name));
2206  }
2207  d = maxdim(d, this->cargo_textsize); // Box must also be wide enough to hold any cargo label.
2208  this->cargo_textsize = maxdim(this->cargo_textsize, GetStringBoundingBox(STR_INDUSTRY_CARGOES_SELECT_CARGO));
2209 
2210  d.width += 2 * HOR_TEXT_PADDING;
2211  /* Ensure the height is enough for the industry type text, for the horizontal connections, and for the cargo labels. */
2213  d.height = max(d.height + 2 * VERT_TEXT_PADDING, min_ind_height);
2214 
2215  CargoesField::industry_width = d.width;
2217 
2218  /* Width of a #CFT_CARGO field. */
2220  }
2221 
2222  virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
2223  {
2224  switch (widget) {
2225  case WID_IC_PANEL:
2227  break;
2228 
2229  case WID_IC_IND_DROPDOWN:
2230  size->width = max(size->width, this->ind_textsize.width + padding.width);
2231  break;
2232 
2233  case WID_IC_CARGO_DROPDOWN:
2234  size->width = max(size->width, this->cargo_textsize.width + padding.width);
2235  break;
2236  }
2237  }
2238 
2239 
2241  virtual void SetStringParameters (int widget) const
2242  {
2243  if (widget != WID_IC_CAPTION) return;
2244 
2245  if (this->ind_cargo < NUM_INDUSTRYTYPES) {
2246  const IndustrySpec *indsp = GetIndustrySpec(this->ind_cargo);
2247  SetDParam(0, indsp->name);
2248  } else {
2249  const CargoSpec *csp = CargoSpec::Get(this->ind_cargo - NUM_INDUSTRYTYPES);
2250  SetDParam(0, csp->name);
2251  }
2252  }
2253 
2262  static bool HasCommonValidCargo(const CargoID *cargoes1, uint length1, const CargoID *cargoes2, uint length2)
2263  {
2264  while (length1 > 0) {
2265  if (*cargoes1 != INVALID_CARGO) {
2266  for (uint i = 0; i < length2; i++) if (*cargoes1 == cargoes2[i]) return true;
2267  }
2268  cargoes1++;
2269  length1--;
2270  }
2271  return false;
2272  }
2273 
2280  static bool HousesCanSupply(const CargoID *cargoes, uint length)
2281  {
2282  for (uint i = 0; i < length; i++) {
2283  if (cargoes[i] == INVALID_CARGO) continue;
2284  if (cargoes[i] == CT_PASSENGERS || cargoes[i] == CT_MAIL) return true;
2285  }
2286  return false;
2287  }
2288 
2295  static bool HousesCanAccept(const CargoID *cargoes, uint length)
2296  {
2297  HouseZones climate_mask;
2299  case LT_TEMPERATE: climate_mask = HZ_TEMP; break;
2300  case LT_ARCTIC: climate_mask = HZ_SUBARTC_ABOVE | HZ_SUBARTC_BELOW; break;
2301  case LT_TROPIC: climate_mask = HZ_SUBTROPIC; break;
2302  case LT_TOYLAND: climate_mask = HZ_TOYLND; break;
2303  default: NOT_REACHED();
2304  }
2305  for (uint i = 0; i < length; i++) {
2306  if (cargoes[i] == INVALID_CARGO) continue;
2307 
2308  for (uint h = 0; h < NUM_HOUSES; h++) {
2309  HouseSpec *hs = HouseSpec::Get(h);
2310  if (!hs->enabled || !(hs->building_availability & climate_mask)) continue;
2311 
2312  for (uint j = 0; j < lengthof(hs->accepts_cargo); j++) {
2313  if (hs->cargo_acceptance[j] > 0 && cargoes[i] == hs->accepts_cargo[j]) return true;
2314  }
2315  }
2316  }
2317  return false;
2318  }
2319 
2326  static int CountMatchingAcceptingIndustries(const CargoID *cargoes, uint length)
2327  {
2328  int count = 0;
2329  for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
2330  const IndustrySpec *indsp = GetIndustrySpec(it);
2331  if (!indsp->enabled) continue;
2332 
2333  if (HasCommonValidCargo(cargoes, length, indsp->accepts_cargo, lengthof(indsp->accepts_cargo))) count++;
2334  }
2335  return count;
2336  }
2337 
2344  static int CountMatchingProducingIndustries(const CargoID *cargoes, uint length)
2345  {
2346  int count = 0;
2347  for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
2348  const IndustrySpec *indsp = GetIndustrySpec(it);
2349  if (!indsp->enabled) continue;
2350 
2351  if (HasCommonValidCargo(cargoes, length, indsp->produced_cargo, lengthof(indsp->produced_cargo))) count++;
2352  }
2353  return count;
2354  }
2355 
2362  void ShortenCargoColumn(int column, int top, int bottom)
2363  {
2364  while (top < bottom && !this->fields[top].columns[column].HasConnection()) {
2365  this->fields[top].columns[column].MakeEmpty(CFT_EMPTY);
2366  top++;
2367  }
2368  this->fields[top].columns[column].u.cargo.top_end = true;
2369 
2370  while (bottom > top && !this->fields[bottom].columns[column].HasConnection()) {
2371  this->fields[bottom].columns[column].MakeEmpty(CFT_EMPTY);
2372  bottom--;
2373  }
2374  this->fields[bottom].columns[column].u.cargo.bottom_end = true;
2375  }
2376 
2383  void PlaceIndustry(int row, int col, IndustryType it)
2384  {
2385  assert(this->fields[row].columns[col].type == CFT_EMPTY);
2386  this->fields[row].columns[col].MakeIndustry(it);
2387  if (col == 0) {
2388  this->fields[row].ConnectIndustryProduced(col);
2389  } else {
2390  this->fields[row].ConnectIndustryAccepted(col);
2391  }
2392  }
2393 
2398  {
2399  if (!this->IsWidgetLowered(WID_IC_NOTIFY)) return;
2400 
2401  /* Only notify the smallmap window if it exists. In particular, do not
2402  * bring it to the front to prevent messing up any nice layout of the user. */
2404  }
2405 
2410  void ComputeIndustryDisplay(IndustryType it)
2411  {
2412  this->GetWidget<NWidgetCore>(WID_IC_CAPTION)->widget_data = STR_INDUSTRY_CARGOES_INDUSTRY_CAPTION;
2413  this->ind_cargo = it;
2414  _displayed_industries.reset();
2415  _displayed_industries.set(it);
2416 
2417  this->fields.Clear();
2418  CargoesRow *row = this->fields.Append();
2419  row->columns[0].MakeHeader(STR_INDUSTRY_CARGOES_PRODUCERS);
2423  row->columns[4].MakeHeader(STR_INDUSTRY_CARGOES_CUSTOMERS);
2424 
2425  const IndustrySpec *central_sp = GetIndustrySpec(it);
2426  bool houses_supply = HousesCanSupply(central_sp->accepts_cargo, lengthof(central_sp->accepts_cargo));
2427  bool houses_accept = HousesCanAccept(central_sp->produced_cargo, lengthof(central_sp->produced_cargo));
2428  /* Make a field consisting of two cargo columns. */
2429  int num_supp = CountMatchingProducingIndustries(central_sp->accepts_cargo, lengthof(central_sp->accepts_cargo)) + houses_supply;
2430  int num_cust = CountMatchingAcceptingIndustries(central_sp->produced_cargo, lengthof(central_sp->produced_cargo)) + houses_accept;
2431  int num_indrows = max(3, max(num_supp, num_cust)); // One is needed for the 'it' industry, and 2 for the cargo labels.
2432  for (int i = 0; i < num_indrows; i++) {
2433  CargoesRow *row = this->fields.Append();
2434  row->columns[0].MakeEmpty(CFT_EMPTY);
2435  row->columns[1].MakeCargo(central_sp->accepts_cargo, lengthof(central_sp->accepts_cargo));
2436  row->columns[2].MakeEmpty(CFT_EMPTY);
2437  row->columns[3].MakeCargo(central_sp->produced_cargo, lengthof(central_sp->produced_cargo));
2438  row->columns[4].MakeEmpty(CFT_EMPTY);
2439  }
2440  /* Add central industry. */
2441  int central_row = 1 + num_indrows / 2;
2442  this->fields[central_row].columns[2].MakeIndustry(it);
2443  this->fields[central_row].ConnectIndustryProduced(2);
2444  this->fields[central_row].ConnectIndustryAccepted(2);
2445 
2446  /* Add cargo labels. */
2447  this->fields[central_row - 1].MakeCargoLabel(2, true);
2448  this->fields[central_row + 1].MakeCargoLabel(2, false);
2449 
2450  /* Add suppliers and customers of the 'it' industry. */
2451  int supp_count = 0;
2452  int cust_count = 0;
2453  for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
2454  const IndustrySpec *indsp = GetIndustrySpec(it);
2455  if (!indsp->enabled) continue;
2456 
2457  if (HasCommonValidCargo(central_sp->accepts_cargo, lengthof(central_sp->accepts_cargo), indsp->produced_cargo, lengthof(indsp->produced_cargo))) {
2458  this->PlaceIndustry(1 + supp_count * num_indrows / num_supp, 0, it);
2459  _displayed_industries.set(it);
2460  supp_count++;
2461  }
2462  if (HasCommonValidCargo(central_sp->produced_cargo, lengthof(central_sp->produced_cargo), indsp->accepts_cargo, lengthof(indsp->accepts_cargo))) {
2463  this->PlaceIndustry(1 + cust_count * num_indrows / num_cust, 4, it);
2464  _displayed_industries.set(it);
2465  cust_count++;
2466  }
2467  }
2468  if (houses_supply) {
2469  this->PlaceIndustry(1 + supp_count * num_indrows / num_supp, 0, NUM_INDUSTRYTYPES);
2470  supp_count++;
2471  }
2472  if (houses_accept) {
2473  this->PlaceIndustry(1 + cust_count * num_indrows / num_cust, 4, NUM_INDUSTRYTYPES);
2474  cust_count++;
2475  }
2476 
2477  this->ShortenCargoColumn(1, 1, num_indrows);
2478  this->ShortenCargoColumn(3, 1, num_indrows);
2479  const NWidgetBase *nwp = this->GetWidget<NWidgetBase>(WID_IC_PANEL);
2481  this->SetDirty();
2482  this->NotifySmallmap();
2483  }
2484 
2490  {
2491  this->GetWidget<NWidgetCore>(WID_IC_CAPTION)->widget_data = STR_INDUSTRY_CARGOES_CARGO_CAPTION;
2492  this->ind_cargo = cid + NUM_INDUSTRYTYPES;
2493  _displayed_industries.reset();
2494 
2495  this->fields.Clear();
2496  CargoesRow *row = this->fields.Append();
2497  row->columns[0].MakeHeader(STR_INDUSTRY_CARGOES_PRODUCERS);
2499  row->columns[2].MakeHeader(STR_INDUSTRY_CARGOES_CUSTOMERS);
2502 
2503  bool houses_supply = HousesCanSupply(&cid, 1);
2504  bool houses_accept = HousesCanAccept(&cid, 1);
2505  int num_supp = CountMatchingProducingIndustries(&cid, 1) + houses_supply + 1; // Ensure room for the cargo label.
2506  int num_cust = CountMatchingAcceptingIndustries(&cid, 1) + houses_accept;
2507  int num_indrows = max(num_supp, num_cust);
2508  for (int i = 0; i < num_indrows; i++) {
2509  CargoesRow *row = this->fields.Append();
2510  row->columns[0].MakeEmpty(CFT_EMPTY);
2511  row->columns[1].MakeCargo(&cid, 1);
2512  row->columns[2].MakeEmpty(CFT_EMPTY);
2513  row->columns[3].MakeEmpty(CFT_EMPTY);
2514  row->columns[4].MakeEmpty(CFT_EMPTY);
2515  }
2516 
2517  this->fields[num_indrows].MakeCargoLabel(0, false); // Add cargo labels at the left bottom.
2518 
2519  /* Add suppliers and customers of the cargo. */
2520  int supp_count = 0;
2521  int cust_count = 0;
2522  for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
2523  const IndustrySpec *indsp = GetIndustrySpec(it);
2524  if (!indsp->enabled) continue;
2525 
2526  if (HasCommonValidCargo(&cid, 1, indsp->produced_cargo, lengthof(indsp->produced_cargo))) {
2527  this->PlaceIndustry(1 + supp_count * num_indrows / num_supp, 0, it);
2528  _displayed_industries.set(it);
2529  supp_count++;
2530  }
2531  if (HasCommonValidCargo(&cid, 1, indsp->accepts_cargo, lengthof(indsp->accepts_cargo))) {
2532  this->PlaceIndustry(1 + cust_count * num_indrows / num_cust, 2, it);
2533  _displayed_industries.set(it);
2534  cust_count++;
2535  }
2536  }
2537  if (houses_supply) {
2538  this->PlaceIndustry(1 + supp_count * num_indrows / num_supp, 0, NUM_INDUSTRYTYPES);
2539  supp_count++;
2540  }
2541  if (houses_accept) {
2542  this->PlaceIndustry(1 + cust_count * num_indrows / num_cust, 2, NUM_INDUSTRYTYPES);
2543  cust_count++;
2544  }
2545 
2546  this->ShortenCargoColumn(1, 1, num_indrows);
2547  const NWidgetBase *nwp = this->GetWidget<NWidgetBase>(WID_IC_PANEL);
2549  this->SetDirty();
2550  this->NotifySmallmap();
2551  }
2552 
2560  virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
2561  {
2562  if (!gui_scope) return;
2563  if (data == NUM_INDUSTRYTYPES) {
2564  if (this->IsWidgetLowered(WID_IC_NOTIFY)) {
2565  this->RaiseWidget(WID_IC_NOTIFY);
2566  this->SetWidgetDirty(WID_IC_NOTIFY);
2567  }
2568  return;
2569  }
2570 
2571  assert(data >= 0 && data < NUM_INDUSTRYTYPES);
2572  this->ComputeIndustryDisplay(data);
2573  }
2574 
2575  virtual void DrawWidget(const Rect &r, int widget) const
2576  {
2577  if (widget != WID_IC_PANEL) return;
2578 
2579  DrawPixelInfo tmp_dpi, *old_dpi;
2580  int width = r.right - r.left + 1;
2581  int height = r.bottom - r.top + 1 - WD_FRAMERECT_TOP - WD_FRAMERECT_BOTTOM;
2582  if (!FillDrawPixelInfo(&tmp_dpi, r.left + WD_FRAMERECT_LEFT, r.top + WD_FRAMERECT_TOP, width, height)) return;
2583  old_dpi = _cur_dpi;
2584  _cur_dpi = &tmp_dpi;
2585 
2586  int left_pos = WD_FRAMERECT_LEFT;
2587  if (this->ind_cargo >= NUM_INDUSTRYTYPES) left_pos += (CargoesField::industry_width + CargoesField::cargo_field_width) / 2;
2588  int last_column = (this->ind_cargo < NUM_INDUSTRYTYPES) ? 4 : 2;
2589 
2590  const NWidgetBase *nwp = this->GetWidget<NWidgetBase>(WID_IC_PANEL);
2591  int vpos = -this->vscroll->GetPosition() * nwp->resize_y;
2592  for (uint i = 0; i < this->fields.Length(); i++) {
2593  int row_height = (i == 0) ? CargoesField::small_height : CargoesField::normal_height;
2594  if (vpos + row_height >= 0) {
2595  int xpos = left_pos;
2596  int col, dir;
2597  if (_current_text_dir == TD_RTL) {
2598  col = last_column;
2599  dir = -1;
2600  } else {
2601  col = 0;
2602  dir = 1;
2603  }
2604  while (col >= 0 && col <= last_column) {
2605  this->fields[i].columns[col].Draw(xpos, vpos);
2607  col += dir;
2608  }
2609  }
2610  vpos += row_height;
2611  if (vpos >= height) break;
2612  }
2613 
2614  _cur_dpi = old_dpi;
2615  }
2616 
2624  bool CalculatePositionInWidget(Point pt, Point *fieldxy, Point *xy)
2625  {
2626  const NWidgetBase *nw = this->GetWidget<NWidgetBase>(WID_IC_PANEL);
2627  pt.x -= nw->pos_x;
2628  pt.y -= nw->pos_y;
2629 
2630  int vpos = WD_FRAMERECT_TOP + CargoesField::small_height - this->vscroll->GetPosition() * nw->resize_y;
2631  if (pt.y < vpos) return false;
2632 
2633  int row = (pt.y - vpos) / CargoesField::normal_height; // row is relative to row 1.
2634  if (row + 1 >= (int)this->fields.Length()) return false;
2635  vpos = pt.y - vpos - row * CargoesField::normal_height; // Position in the row + 1 field
2636  row++; // rebase row to match index of this->fields.
2637 
2638  int xpos = 2 * WD_FRAMERECT_LEFT + ((this->ind_cargo < NUM_INDUSTRYTYPES) ? 0 : (CargoesField::industry_width + CargoesField::cargo_field_width) / 2);
2639  if (pt.x < xpos) return false;
2640  int column;
2641  for (column = 0; column <= 5; column++) {
2643  if (pt.x < xpos + width) break;
2644  xpos += width;
2645  }
2646  int num_columns = (this->ind_cargo < NUM_INDUSTRYTYPES) ? 4 : 2;
2647  if (column > num_columns) return false;
2648  xpos = pt.x - xpos;
2649 
2650  /* Return both positions, compensating for RTL languages (which works due to the equal symmetry in both displays). */
2651  fieldxy->y = row;
2652  xy->y = vpos;
2653  if (_current_text_dir == TD_RTL) {
2654  fieldxy->x = num_columns - column;
2655  xy->x = ((column & 1) ? CargoesField::cargo_field_width : CargoesField::industry_width) - xpos;
2656  } else {
2657  fieldxy->x = column;
2658  xy->x = xpos;
2659  }
2660  return true;
2661  }
2662 
2663  virtual void OnClick(Point pt, int widget, int click_count)
2664  {
2665  switch (widget) {
2666  case WID_IC_PANEL: {
2667  Point fieldxy, xy;
2668  if (!CalculatePositionInWidget(pt, &fieldxy, &xy)) return;
2669 
2670  const CargoesField *fld = this->fields[fieldxy.y].columns + fieldxy.x;
2671  switch (fld->type) {
2672  case CFT_INDUSTRY:
2673  if (fld->u.industry.ind_type < NUM_INDUSTRYTYPES) this->ComputeIndustryDisplay(fld->u.industry.ind_type);
2674  break;
2675 
2676  case CFT_CARGO: {
2677  CargoesField *lft = (fieldxy.x > 0) ? this->fields[fieldxy.y].columns + fieldxy.x - 1 : NULL;
2678  CargoesField *rgt = (fieldxy.x < 4) ? this->fields[fieldxy.y].columns + fieldxy.x + 1 : NULL;
2679  CargoID cid = fld->CargoClickedAt(lft, rgt, xy);
2680  if (cid != INVALID_CARGO) this->ComputeCargoDisplay(cid);
2681  break;
2682  }
2683 
2684  case CFT_CARGO_LABEL: {
2685  CargoID cid = fld->CargoLabelClickedAt(xy);
2686  if (cid != INVALID_CARGO) this->ComputeCargoDisplay(cid);
2687  break;
2688  }
2689 
2690  default:
2691  break;
2692  }
2693  break;
2694  }
2695 
2696  case WID_IC_NOTIFY:
2697  this->ToggleWidgetLoweredState(WID_IC_NOTIFY);
2698  this->SetWidgetDirty(WID_IC_NOTIFY);
2699  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
2700 
2701  if (this->IsWidgetLowered(WID_IC_NOTIFY)) {
2702  if (FindWindowByClass(WC_SMALLMAP) == NULL) ShowSmallMap();
2703  this->NotifySmallmap();
2704  }
2705  break;
2706 
2707  case WID_IC_CARGO_DROPDOWN: {
2708  DropDownList *lst = new DropDownList;
2709  const CargoSpec *cs;
2711  *lst->Append() = new DropDownListStringItem(cs->name, cs->Index(), false);
2712  }
2713  if (lst->Length() == 0) {
2714  delete lst;
2715  break;
2716  }
2717  int selected = (this->ind_cargo >= NUM_INDUSTRYTYPES) ? (int)(this->ind_cargo - NUM_INDUSTRYTYPES) : -1;
2718  ShowDropDownList(this, lst, selected, WID_IC_CARGO_DROPDOWN, 0, true);
2719  break;
2720  }
2721 
2722  case WID_IC_IND_DROPDOWN: {
2723  DropDownList *lst = new DropDownList;
2724  for (uint i = 0; i < NUM_INDUSTRYTYPES; i++) {
2725  IndustryType ind = _sorted_industry_types[i];
2726  const IndustrySpec *indsp = GetIndustrySpec(ind);
2727  if (!indsp->enabled) continue;
2728  *lst->Append() = new DropDownListStringItem(indsp->name, ind, false);
2729  }
2730  if (lst->Length() == 0) {
2731  delete lst;
2732  break;
2733  }
2734  int selected = (this->ind_cargo < NUM_INDUSTRYTYPES) ? (int)this->ind_cargo : -1;
2735  ShowDropDownList(this, lst, selected, WID_IC_IND_DROPDOWN, 0, true);
2736  break;
2737  }
2738  }
2739  }
2740 
2741  virtual void OnDropdownSelect(int widget, int index)
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  virtual void OnHover(Point pt, int widget)
2757  {
2758  if (widget != WID_IC_PANEL) return;
2759 
2760  Point fieldxy, xy;
2761  if (!CalculatePositionInWidget(pt, &fieldxy, &xy)) return;
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 : NULL;
2768  CargoesField *rgt = (fieldxy.x < 4) ? this->fields[fieldxy.y].columns + fieldxy.x + 1 : NULL;
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, NULL, TCC_HOVER);
2781  }
2782  return;
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, TCC_HOVER);
2792  }
2793  }
2794 
2795  virtual void OnResize()
2796  {
2797  this->vscroll->SetCapacityFromWidget(this, WID_IC_PANEL);
2798  }
2799 };
2800 
2803 
2808 static void ShowIndustryCargoesWindow(IndustryType id)
2809 {
2810  if (id >= NUM_INDUSTRYTYPES) {
2811  for (uint i = 0; i < NUM_INDUSTRYTYPES; i++) {
2813  if (indsp->enabled) {
2814  id = _sorted_industry_types[i];
2815  break;
2816  }
2817  }
2818  if (id >= NUM_INDUSTRYTYPES) return;
2819  }
2820 
2822  if (w != NULL) {
2823  w->InvalidateData(id);
2824  return;
2825  }
2826  new IndustryCargoesWindow(id);
2827 }
2828 
2831 {
2833 }
CargoID accepts_cargo[INDUSTRY_NUM_INPUTS]
16 accepted cargoes.
Definition: industrytype.h:118
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
virtual void OnHover(Point pt, int widget)
The mouse is hovering over a widget in the window, perform an action for it, like opening a custom to...
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...
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.
virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
Update size and resize step of a widget in the window.
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition: settings.cpp:77
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:56
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
virtual void OnDropdownSelect(int widget, int index)
A dropdown option associated to this window has been selected.
bool enabled
entity still available (by default true).newgrf can disable it, though
Definition: industrytype.h:137
Build (fund or prospect) a new industry,.
byte production_rate[INDUSTRY_NUM_OUTPUTS]
production rate for each cargo
Definition: industry.h:47
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:210
Dropdown for the criteria of the sort.
Horizontally center the text.
Definition: gfx_func.h:99
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:123
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:33
static Titem * Get(size_t index)
Returns Titem with given index.
Definition: pool_type.hpp:246
static bool IsInsideMM(const T x, const uint min, const uint max)
Checks if a value is in an interval.
Definition: math_func.hpp:266
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).
virtual void OnInvalidateData(int data=0, bool gui_scope=true)
Some data on this window has become invalid.
virtual void SetStringParameters(int widget) const
Initialize string parameters for a widget.
void PlaceIndustry(int row, int col, IndustryType it)
Place an industry in the fields.
virtual void DrawWidget(const Rect &r, int widget) const
Draw the contents of a nested widget.
Offset at top to draw the frame rectangular area.
Definition: window_gui.h:64
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.
virtual void OnPaint()
The window must be repainted.
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 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:1064
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 BuildSortIndustriesList()
(Re)Build industries list
Specification of a cargo type.
Definition: cargotype.h:56
void GuiShowTooltips(Window *parent, StringID str, uint paramcount, const uint64 params[], TooltipCloseCondition close_tooltip)
Shows a tooltip.
Definition: misc_gui.cpp:741
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:34
Row of buttons at the bottom.
static int small_height
Height of the header row.
IndustryType _sorted_industry_types[NUM_INDUSTRYTYPES]
Industry types sorted by name.
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:135
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 IsInsideBS(const T x, const uint base, const uint size)
Checks if a value is between a window started at some base point.
Definition: math_func.hpp:250
void Clear()
Remove all items from the list.
static bool HousesCanSupply(const CargoID *cargoes, uint length)
Can houses be used to supply one of the cargoes?
void Compact()
Compact the list down to the smallest block size boundary.
signal set to actually close the industry
Definition: industry.h:32
Defines the internal data of a functional industry.
Definition: industry.h:41
static const int BLOB_HEIGHT
Height of the industry legend colour, including border.
DifficultySettings difficulty
settings related to the difficulty
virtual void SetStringParameters(int widget) const
Initialize string parameters for a widget.
virtual void DrawWidget(const Rect &r, int widget) const
Draw the contents of a nested widget.
Industry-directory window.
Tindex index
Index of this pool item.
Definition: pool_type.hpp:147
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
virtual void OnPlaceObjectAbort()
The user cancelled a tile highlight mode that has been set.
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...
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
the industry is running at full speed
Definition: industry.h:35
Nested widget to display a viewport in a window.
Definition: widget_type.h:575
Common string list item.
Definition: dropdown_type.h:41
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:2206
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:71
Industry directory; Window numbers:
Definition: window_type.h:261
static const HouseID NUM_HOUSES
Total number of houses.
Definition: house.h:31
StringID name
Displayed name of the industry.
Definition: industrytype.h:124
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.
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:1480
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 uint ClampU(const uint a, const uint min, const uint max)
Clamp an unsigned integer between an interval.
Definition: math_func.hpp:184
virtual void OnResize()
Called after the window got resized.
T * Append(uint to_add=1)
Append an item and return it.
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
CompanyByte _local_company
Company controlled by the human player at this client. Can also be COMPANY_SPECTATOR.
Definition: company_cmd.cpp:46
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.
Data structure for an opened window.
Definition: window_gui.h:271
Invalid cargo type.
Definition: cargo_type.h:70
bool _ctrl_pressed
Is Ctrl pressed?
Definition: gfx.cpp:36
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
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
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:3297
virtual void OnInvalidateData(int data=0, bool gui_scope=true)
Some data on this window has become invalid.
void ShowErrorMessage(StringID summary_msg, StringID detailed_msg, WarningLevel wl, int x=0, int y=0, const GRFFile *textref_stack_grffile=NULL, uint textref_stack_size=0, const uint32 *textref_stack=NULL)
Display an error message in a window.
Definition: error_gui.cpp:378
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:279
virtual void OnClick(Point pt, int widget, int click_count)
A click with the left mouse button has been made on the window.
Header text.
Functions related to low-level strings.
Types related to cheating.
virtual void OnInit()
Notification that the nested widget tree gets initialized.
StringID header
Header text (for CFT_HEADER).
virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
Update size and resize step of a widget in the window.
Functions related to errors.
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
bool IsRawIndustry() const
Is an industry with the spec a raw industry?
uint Length() const
Get the number of items in the list.
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.
uint pos_y
Vertical position of top-left corner of the widget in the window.
Definition: widget_type.h:178
This window is used for construction; close it whenever changing company.
Definition: window_gui.h:210
static int CDECL IndustryProductionSorter(const Industry *const *a, const Industry *const *b)
Sort industries by production and name.
Fill rectangle with a single colour.
Definition: gfx_type.h:283
SoundSettings sound
sound effect settings
virtual void OnTimeout()
Called when this window&#39;s timeout has been reached.
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:180
static NWidgetPart SetDataTip(uint32 data, StringID tip)
Widget part function for setting the data and tooltip.
Definition: widget_type.h:1014
void CcBuildIndustry(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2)
Command callback.
Simple vector template class, with automatic delete.
CargoSuffixDisplay display
How to display the cargo and text.
ClientSettings _settings_client
The current settings for this game.
Definition: settings.cpp:76
static NWidgetPart SetMinimalSize(int16 x, int16 y)
Widget part function for setting the minimal size.
Definition: widget_type.h:947
virtual void OnResize()
Called after the window got resized.
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.
Definition of base types and functions in a cross-platform compatible way.
TileIndex GetCenterTile() const
Get the center tile.
Definition: tilearea_type.h:57
char text[512]
Cargo suffix text.
bool UsesSmoothEconomy() const
Determines whether this industrytype uses smooth economy or whether it uses standard/newgrf productio...
static int CDECL IndustryNameSorter(const Industry *const *a, const Industry *const *b)
Sort industries by name.
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:57
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
Default zoom level for the industry view.
Definition: zoom_type.h:37
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:42
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:53
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:44
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.
Defines the data structure for constructing industry.
Definition: industrytype.h:103
InfoLine clicked_line
The line of the button that has been clicked.
virtual void OnTimeout()
Called when this window&#39;s timeout has been reached.
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
The tile has no ownership.
Definition: company_type.h:27
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:49
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:531
Industry view; Window numbers:
Definition: window_type.h:358
uint8 cargo_map[NUM_CARGO]
Inverse cargo translation table (CargoID -> local ID)
Definition: newgrf.h:126
uint16 incoming_cargo_waiting[INDUSTRY_NUM_INPUTS]
incoming cargo waiting to be processed
Definition: industry.h:46
static const int CARGO_STUB_WIDTH
Width of a cargo not carried in the column (should be less than HOR_CARGO_BORDER_SPACE).
#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:122
byte prod_level
general production level
Definition: industry.h:48
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:138
Grid of rows and columns.
Definition: widget_type.h:59
uint pos_x
Horizontal position of top-left corner of the widget in the window.
Definition: widget_type.h:177
static int CDECL IndustryTypeSorter(const Industry *const *a, const Industry *const *b)
Sort industries by type and name.
#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:2122
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
IndustryType selected_type
industry corresponding to the above index
static const uint8 PC_BLACK
Black palette colour.
Definition: gfx_func.h:207
virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
Update size and resize step of a widget in the window.
#define SETTING_BUTTON_WIDTH
Width of setting buttons.
Definition: settings_gui.h:19
12 1000 can appear in temperate climate
Definition: house.h:82
virtual void OnDropdownSelect(int widget, int index)
A dropdown option associated to this window has been selected.
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.
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.
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:699
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.
virtual void OnPlaceObject(Point pt, TileIndex tile)
The user clicked some place on the map when a tile highlight mode has been set.
byte num_table
Number of elements in the table.
Definition: industrytype.h:105
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:220
virtual void OnInvalidateData(int data=0, bool gui_scope=true)
Some data on this window has become invalid.
Smallmap GUI functions.
Functions related to companies.
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:61
15 8000 can appear in toyland climate
Definition: house.h:85
Both numeric and alphabetic and spaces and stuff.
Definition: string_type.h:27
static const IndustryType INVALID_INDUSTRYTYPE
one above amount is considered invalid
Definition: industry_type.h:29
GUISettings gui
settings related to the GUI
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
virtual void ShowNewGRFInspectWindow() const
Show the NewGRF inspection window.
static CargoSpec * Get(size_t index)
Retrieve cargo details for the given cargo ID.
Definition: cargotype.h:118
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)
CompanyByte _current_company
Company currently doing an action.
Definition: company_cmd.cpp:47
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:104
#define cpp_lengthof(base, variable)
Gets the length of an array variable within a class.
Definition: stdafx.h:432
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:52
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:165
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
int CDECL SortFunction(const T *, const T *)
Signature of sort function.
Definition: sortlist_type.h:52
Scrollbar of the matrix.
Allow produced/accepted cargoes callbacks to supply more than 2 and 3 types.
Definition: industrytype.h:83
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.
virtual void OnResize()
Called after the window got resized.
virtual void OnClick(Point pt, int widget, int click_count)
A click with the left mouse button has been made on the window.
static const byte INVALID_CARGO
Constant representing invalid cargo.
Definition: cargotype.h:53
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:403
Window displaying the cargo connections around an industry (or cargo).
call production callback when cargo arrives at the industry
virtual void OnClick(Point pt, int widget, int click_count)
A click with the left mouse button has been made on the window.
virtual void OnHundredthTick()
Called once every 100 (game) ticks.
Window * FindWindowByClass(WindowClass cls)
Find any window by its class.
Definition: window.cpp:1127
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:2133
Scrollbar of the list.
Functions related to commands.
virtual void OnQueryTextFinished(char *str)
The query window opened from this window has closed.
Fund-industry window.
Coordinates of a point in 2D.
byte top_end
Stop at the top of the vertical cargoes.
CargoID Index() const
Determines index of this cargospec.
Definition: cargotype.h:89
static bool IsValidID(size_t index)
Tests whether given index can be used to get valid (non-NULL) Titem.
Definition: pool_type.hpp:235
Data structure describing how to show the list (what sort direction and criteria).
Definition: sortlist_type.h:34
Drop down list.
Definition: widget_type.h:70
static const int BLOB_WIDTH
Width of the industry legend colour, including border.
static int CDECL IndustryTransportedCargoSorter(const Industry *const *a, const Industry *const *b)
Sort industries by transported cargo and name.
virtual bool IsNewGRFInspectable() const
Is the data related to this window NewGRF inspectable?
uint16 GetCapacity() const
Gets the number of visible elements of the scrollbar.
Definition: widget_type.h:622
virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
Update size and resize step of a widget in the window.
Window does not do autoscroll,.
Definition: window_gui.h:241
ConstructionSettings construction
construction of things in-game
static int CDECL IndustryTypeNameSorter(const IndustryType *a, const IndustryType *b)
Sort industry types by their name.
static const StringID INVALID_STRING_ID
Constant representing an invalid string (16bit in case it is used in savegames)
Definition: strings_type.h:19
virtual void OnPaint()
The window must be repainted.
Base of all industries.
Called to determine text to display after cargo name.
virtual void OnGameTick()
Called once per (game) tick.
bool left_align
Align all cargo texts to the left (else align to the right).
virtual void OnInvalidateData(int data=0, bool gui_scope=true)
Some data on this window has become invalid.
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
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:99
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.
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:369
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:3075
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:100
Called to determine more text in the industry window.
Info of the industry.
Left align the text.
Definition: gfx_func.h:98
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:77
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:52
virtual void OnResize()
Called after the window got resized.
virtual void DrawWidget(const Rect &r, int widget) const
Draw the contents of a nested widget.
GUI functions that shouldn&#39;t be here.
CargoID CargoLabelClickedAt(Point pt) const
Decide what cargo the user clicked in the cargo label field.
void SetButtons()
Update status of the fund and display-chain widgets.
virtual void OnClick(Point pt, int widget, int click_count)
A click with the left mouse button has been made on the window.
call production callback every 256 ticks
struct CargoesField::@17::@18 industry
Industry data (for CFT_INDUSTRY).
static void QSortT(T *base, uint num, int(CDECL *comparator)(const T *, const T *), bool desc=false)
Type safe qsort()
Definition: sort_func.hpp:28
virtual void OnInit()
Notification that the nested widget tree gets initialized.
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
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
virtual void SetStringParameters(int widget) const
Initialize string parameters for a widget.
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:1240
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
virtual void SetStringParameters(int widget) const
Initialize string parameters for a widget.
Display chain button.
build a new industry
Definition: command_type.h:233
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:3220
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:97
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