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 
227 void CcBuildIndustry(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2, uint32 cmd)
228 {
229  if (result.Succeeded()) return;
230 
231  uint8 indtype = GB(p1, 0, 8);
232  if (indtype < NUM_INDUSTRYTYPES) {
233  const IndustrySpec *indsp = GetIndustrySpec(indtype);
234  if (indsp->enabled) {
235  SetDParam(0, indsp->name);
236  ShowErrorMessage(STR_ERROR_CAN_T_BUILD_HERE, result.GetErrorMessage(), WL_INFO, TileX(tile) * TILE_SIZE, TileY(tile) * TILE_SIZE);
237  }
238  }
239 }
240 
241 static const NWidgetPart _nested_build_industry_widgets[] = {
243  NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
244  NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetDataTip(STR_FUND_INDUSTRY_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
245  NWidget(WWT_SHADEBOX, COLOUR_DARK_GREEN),
246  NWidget(WWT_DEFSIZEBOX, COLOUR_DARK_GREEN),
247  NWidget(WWT_STICKYBOX, COLOUR_DARK_GREEN),
248  EndContainer(),
250  NWidget(WWT_MATRIX, COLOUR_DARK_GREEN, WID_DPI_MATRIX_WIDGET), SetMatrixDataTip(1, 0, STR_FUND_INDUSTRY_SELECTION_TOOLTIP), SetFill(1, 0), SetResize(1, 1), SetScrollbar(WID_DPI_SCROLLBAR),
251  NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, WID_DPI_SCROLLBAR),
252  EndContainer(),
253  NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_DPI_INFOPANEL), SetResize(1, 0),
254  EndContainer(),
256  NWidget(WWT_TEXTBTN, COLOUR_DARK_GREEN, WID_DPI_DISPLAY_WIDGET), SetFill(1, 0), SetResize(1, 0),
257  SetDataTip(STR_INDUSTRY_DISPLAY_CHAIN, STR_INDUSTRY_DISPLAY_CHAIN_TOOLTIP),
258  NWidget(WWT_TEXTBTN, COLOUR_DARK_GREEN, WID_DPI_FUND_WIDGET), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_JUST_STRING, STR_NULL),
259  NWidget(WWT_RESIZEBOX, COLOUR_DARK_GREEN),
260  EndContainer(),
261 };
262 
265  WDP_AUTO, "build_industry", 170, 212,
268  _nested_build_industry_widgets, lengthof(_nested_build_industry_widgets)
269 );
270 
272 class BuildIndustryWindow : public Window {
274  IndustryType selected_type;
275  uint16 callback_timer;
277  uint16 count;
278  IndustryType index[NUM_INDUSTRYTYPES + 1];
279  bool enabled[NUM_INDUSTRYTYPES + 1];
280  Scrollbar *vscroll;
281 
283  static const int MATRIX_TEXT_OFFSET = 17;
285  static const int MAX_MINWIDTH_LINEHEIGHTS = 20;
286 
287  void SetupArrays()
288  {
289  this->count = 0;
290 
291  for (uint i = 0; i < lengthof(this->index); i++) {
292  this->index[i] = INVALID_INDUSTRYTYPE;
293  this->enabled[i] = false;
294  }
295 
296  if (_game_mode == GM_EDITOR) { // give room for the Many Random "button"
297  this->index[this->count] = INVALID_INDUSTRYTYPE;
298  this->enabled[this->count] = true;
299  this->count++;
300  this->timer_enabled = false;
301  }
302  /* Fill the arrays with industries.
303  * The tests performed after the enabled allow to load the industries
304  * In the same way they are inserted by grf (if any)
305  */
306  for (uint i = 0; i < NUM_INDUSTRYTYPES; i++) {
307  IndustryType ind = _sorted_industry_types[i];
308  const IndustrySpec *indsp = GetIndustrySpec(ind);
309  if (indsp->enabled) {
310  /* Rule is that editor mode loads all industries.
311  * In game mode, all non raw industries are loaded too
312  * and raw ones are loaded only when setting allows it */
313  if (_game_mode != GM_EDITOR && indsp->IsRawIndustry() && _settings_game.construction.raw_industry_construction == 0) {
314  /* Unselect if the industry is no longer in the list */
315  if (this->selected_type == ind) this->selected_index = -1;
316  continue;
317  }
318  this->index[this->count] = ind;
319  this->enabled[this->count] = (_game_mode == GM_EDITOR) || GetIndustryProbabilityCallback(ind, IACT_USERCREATION, 1) > 0;
320  /* Keep the selection to the correct line */
321  if (this->selected_type == ind) this->selected_index = this->count;
322  this->count++;
323  }
324  }
325 
326  /* first industry type is selected if the current selection is invalid.
327  * I'll be damned if there are none available ;) */
328  if (this->selected_index == -1) {
329  this->selected_index = 0;
330  this->selected_type = this->index[0];
331  }
332 
333  this->vscroll->SetCount(this->count);
334  }
335 
337  void SetButtons()
338  {
339  this->SetWidgetDisabledState(WID_DPI_FUND_WIDGET, this->selected_type != INVALID_INDUSTRYTYPE && !this->enabled[this->selected_index]);
340  this->SetWidgetDisabledState(WID_DPI_DISPLAY_WIDGET, this->selected_type == INVALID_INDUSTRYTYPE && this->enabled[this->selected_index]);
341  }
342 
355  std::string MakeCargoListString(const CargoID *cargolist, const CargoSuffix *cargo_suffix, int cargolistlen, StringID prefixstr) const
356  {
357  std::string cargostring;
358  char buf[1024];
359  int numcargo = 0;
360  int firstcargo = -1;
361 
362  for (byte j = 0; j < cargolistlen; j++) {
363  if (cargolist[j] == CT_INVALID) continue;
364  numcargo++;
365  if (firstcargo < 0) {
366  firstcargo = j;
367  continue;
368  }
369  SetDParam(0, CargoSpec::Get(cargolist[j])->name);
370  SetDParamStr(1, cargo_suffix[j].text);
371  GetString(buf, STR_INDUSTRY_VIEW_CARGO_LIST_EXTENSION, lastof(buf));
372  cargostring += buf;
373  }
374 
375  if (numcargo > 0) {
376  SetDParam(0, CargoSpec::Get(cargolist[firstcargo])->name);
377  SetDParamStr(1, cargo_suffix[firstcargo].text);
378  GetString(buf, prefixstr, lastof(buf));
379  cargostring = std::string(buf) + cargostring;
380  } else {
381  SetDParam(0, STR_JUST_NOTHING);
382  SetDParamStr(1, "");
383  GetString(buf, prefixstr, lastof(buf));
384  cargostring = std::string(buf);
385  }
386 
387  return cargostring;
388  }
389 
390 public:
391  BuildIndustryWindow() : Window(&_build_industry_desc)
392  {
393  this->timer_enabled = _loaded_newgrf_features.has_newindustries;
394 
395  this->selected_index = -1;
396  this->selected_type = INVALID_INDUSTRYTYPE;
397 
398  this->callback_timer = DAY_TICKS;
399 
400  this->CreateNestedTree();
401  this->vscroll = this->GetScrollbar(WID_DPI_SCROLLBAR);
402  this->FinishInitNested(0);
403 
404  this->SetButtons();
405  }
406 
407  virtual void OnInit()
408  {
409  this->SetupArrays();
410  }
411 
412  virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
413  {
414  switch (widget) {
415  case WID_DPI_MATRIX_WIDGET: {
416  Dimension d = GetStringBoundingBox(STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES);
417  for (byte i = 0; i < this->count; i++) {
418  if (this->index[i] == INVALID_INDUSTRYTYPE) continue;
419  d = maxdim(d, GetStringBoundingBox(GetIndustrySpec(this->index[i])->name));
420  }
422  d.width += MATRIX_TEXT_OFFSET + padding.width;
423  d.height = 5 * resize->height;
424  *size = maxdim(*size, d);
425  break;
426  }
427 
428  case WID_DPI_INFOPANEL: {
429  /* Extra line for cost outside of editor + extra lines for 'extra' information for NewGRFs. */
430  int height = 2 + (_game_mode == GM_EDITOR ? 0 : 1) + (_loaded_newgrf_features.has_newindustries ? 4 : 0);
431  uint extra_lines_req = 0;
432  uint extra_lines_prd = 0;
433  uint max_minwidth = FONT_HEIGHT_NORMAL * MAX_MINWIDTH_LINEHEIGHTS;
434  Dimension d = {0, 0};
435  for (byte i = 0; i < this->count; i++) {
436  if (this->index[i] == INVALID_INDUSTRYTYPE) continue;
437 
438  const IndustrySpec *indsp = GetIndustrySpec(this->index[i]);
439  CargoSuffix cargo_suffix[lengthof(indsp->accepts_cargo)];
440 
441  /* Measure the accepted cargoes, if any. */
442  GetAllCargoSuffixes(CARGOSUFFIX_IN, CST_FUND, NULL, this->index[i], indsp, indsp->accepts_cargo, cargo_suffix);
443  std::string cargostring = this->MakeCargoListString(indsp->accepts_cargo, cargo_suffix, lengthof(indsp->accepts_cargo), STR_INDUSTRY_VIEW_REQUIRES_N_CARGO);
444  Dimension strdim = GetStringBoundingBox(cargostring.c_str());
445  if (strdim.width > max_minwidth) {
446  extra_lines_req = max(extra_lines_req, strdim.width / max_minwidth + 1);
447  strdim.width = max_minwidth;
448  }
449  d = maxdim(d, strdim);
450 
451  /* Measure the produced cargoes, if any. */
452  GetAllCargoSuffixes(CARGOSUFFIX_OUT, CST_FUND, NULL, this->index[i], indsp, indsp->produced_cargo, cargo_suffix);
453  cargostring = this->MakeCargoListString(indsp->produced_cargo, cargo_suffix, lengthof(indsp->produced_cargo), STR_INDUSTRY_VIEW_PRODUCES_N_CARGO);
454  strdim = GetStringBoundingBox(cargostring.c_str());
455  if (strdim.width > max_minwidth) {
456  extra_lines_prd = max(extra_lines_prd, strdim.width / max_minwidth + 1);
457  strdim.width = max_minwidth;
458  }
459  d = maxdim(d, strdim);
460  }
461 
462  /* Set it to something more sane :) */
463  height += extra_lines_prd + extra_lines_req;
464  size->height = height * FONT_HEIGHT_NORMAL + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
465  size->width = d.width + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
466  break;
467  }
468 
469  case WID_DPI_FUND_WIDGET: {
470  Dimension d = GetStringBoundingBox(STR_FUND_INDUSTRY_BUILD_NEW_INDUSTRY);
471  d = maxdim(d, GetStringBoundingBox(STR_FUND_INDUSTRY_PROSPECT_NEW_INDUSTRY));
472  d = maxdim(d, GetStringBoundingBox(STR_FUND_INDUSTRY_FUND_NEW_INDUSTRY));
473  d.width += padding.width;
474  d.height += padding.height;
475  *size = maxdim(*size, d);
476  break;
477  }
478  }
479  }
480 
481  virtual void SetStringParameters(int widget) const
482  {
483  switch (widget) {
484  case WID_DPI_FUND_WIDGET:
485  /* Raw industries might be prospected. Show this fact by changing the string
486  * In Editor, you just build, while ingame, or you fund or you prospect */
487  if (_game_mode == GM_EDITOR) {
488  /* We've chosen many random industries but no industries have been specified */
489  SetDParam(0, STR_FUND_INDUSTRY_BUILD_NEW_INDUSTRY);
490  } else {
491  const IndustrySpec *indsp = GetIndustrySpec(this->index[this->selected_index]);
492  SetDParam(0, (_settings_game.construction.raw_industry_construction == 2 && indsp->IsRawIndustry()) ? STR_FUND_INDUSTRY_PROSPECT_NEW_INDUSTRY : STR_FUND_INDUSTRY_FUND_NEW_INDUSTRY);
493  }
494  break;
495  }
496  }
497 
498  virtual void DrawWidget(const Rect &r, int widget) const
499  {
500  switch (widget) {
501  case WID_DPI_MATRIX_WIDGET: {
502  uint text_left, text_right, icon_left, icon_right;
503  if (_current_text_dir == TD_RTL) {
504  icon_right = r.right - WD_MATRIX_RIGHT;
505  icon_left = icon_right - 10;
506  text_right = icon_right - BuildIndustryWindow::MATRIX_TEXT_OFFSET;
507  text_left = r.left + WD_MATRIX_LEFT;
508  } else {
509  icon_left = r.left + WD_MATRIX_LEFT;
510  icon_right = icon_left + 10;
511  text_left = icon_left + BuildIndustryWindow::MATRIX_TEXT_OFFSET;
512  text_right = r.right - WD_MATRIX_RIGHT;
513  }
514 
515  for (byte i = 0; i < this->vscroll->GetCapacity() && i + this->vscroll->GetPosition() < this->count; i++) {
516  int y = r.top + WD_MATRIX_TOP + i * this->resize.step_height;
517  bool selected = this->selected_index == i + this->vscroll->GetPosition();
518 
519  if (this->index[i + this->vscroll->GetPosition()] == INVALID_INDUSTRYTYPE) {
520  DrawString(text_left, text_right, y, STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES, selected ? TC_WHITE : TC_ORANGE);
521  continue;
522  }
523  const IndustrySpec *indsp = GetIndustrySpec(this->index[i + this->vscroll->GetPosition()]);
524 
525  /* Draw the name of the industry in white is selected, otherwise, in orange */
526  DrawString(text_left, text_right, y, indsp->name, selected ? TC_WHITE : TC_ORANGE);
527  GfxFillRect(icon_left, y + 1, icon_right, y + 7, selected ? PC_WHITE : PC_BLACK);
528  GfxFillRect(icon_left + 1, y + 2, icon_right - 1, y + 6, indsp->map_colour);
529  }
530  break;
531  }
532 
533  case WID_DPI_INFOPANEL: {
534  int y = r.top + WD_FRAMERECT_TOP;
535  int bottom = r.bottom - WD_FRAMERECT_BOTTOM;
536  int left = r.left + WD_FRAMERECT_LEFT;
537  int right = r.right - WD_FRAMERECT_RIGHT;
538 
539  if (this->selected_type == INVALID_INDUSTRYTYPE) {
540  DrawStringMultiLine(left, right, y, bottom, STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_TOOLTIP);
541  break;
542  }
543 
544  const IndustrySpec *indsp = GetIndustrySpec(this->selected_type);
545 
546  if (_game_mode != GM_EDITOR) {
547  SetDParam(0, indsp->GetConstructionCost());
548  DrawString(left, right, y, STR_FUND_INDUSTRY_INDUSTRY_BUILD_COST);
549  y += FONT_HEIGHT_NORMAL;
550  }
551 
552  CargoSuffix cargo_suffix[lengthof(indsp->accepts_cargo)];
553 
554  /* Draw the accepted cargoes, if any. Otherwise, will print "Nothing". */
555  GetAllCargoSuffixes(CARGOSUFFIX_IN, CST_FUND, NULL, this->selected_type, indsp, indsp->accepts_cargo, cargo_suffix);
556  std::string cargostring = this->MakeCargoListString(indsp->accepts_cargo, cargo_suffix, lengthof(indsp->accepts_cargo), STR_INDUSTRY_VIEW_REQUIRES_N_CARGO);
557  y = DrawStringMultiLine(left, right, y, bottom, cargostring.c_str());
558 
559  /* Draw the produced cargoes, if any. Otherwise, will print "Nothing". */
560  GetAllCargoSuffixes(CARGOSUFFIX_OUT, CST_FUND, NULL, this->selected_type, indsp, indsp->produced_cargo, cargo_suffix);
561  cargostring = this->MakeCargoListString(indsp->produced_cargo, cargo_suffix, lengthof(indsp->produced_cargo), STR_INDUSTRY_VIEW_PRODUCES_N_CARGO);
562  y = DrawStringMultiLine(left, right, y, bottom, cargostring.c_str());
563 
564  /* Get the additional purchase info text, if it has not already been queried. */
566  uint16 callback_res = GetIndustryCallback(CBID_INDUSTRY_FUND_MORE_TEXT, 0, 0, NULL, this->selected_type, INVALID_TILE);
567  if (callback_res != CALLBACK_FAILED && callback_res != 0x400) {
568  if (callback_res > 0x400) {
570  } else {
571  StringID str = GetGRFStringID(indsp->grf_prop.grffile->grfid, 0xD000 + callback_res); // No. here's the new string
572  if (str != STR_UNDEFINED) {
574  DrawStringMultiLine(left, right, y, bottom, str, TC_YELLOW);
576  }
577  }
578  }
579  }
580  break;
581  }
582  }
583  }
584 
585  virtual void OnClick(Point pt, int widget, int click_count)
586  {
587  switch (widget) {
588  case WID_DPI_MATRIX_WIDGET: {
589  int y = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_DPI_MATRIX_WIDGET);
590  if (y < this->count) { // Is it within the boundaries of available data?
591  this->selected_index = y;
592  this->selected_type = this->index[y];
593  const IndustrySpec *indsp = (this->selected_type == INVALID_INDUSTRYTYPE) ? NULL : GetIndustrySpec(this->selected_type);
594 
595  this->SetDirty();
596 
597  if (_thd.GetCallbackWnd() == this &&
598  ((_game_mode != GM_EDITOR && _settings_game.construction.raw_industry_construction == 2 && indsp != NULL && indsp->IsRawIndustry()) ||
599  this->selected_type == INVALID_INDUSTRYTYPE ||
600  !this->enabled[this->selected_index])) {
601  /* Reset the button state if going to prospecting or "build many industries" */
602  this->RaiseButtons();
604  }
605 
606  this->SetButtons();
607  if (this->enabled[this->selected_index] && click_count > 1) this->OnClick(pt, WID_DPI_FUND_WIDGET, 1);
608  }
609  break;
610  }
611 
613  if (this->selected_type != INVALID_INDUSTRYTYPE) ShowIndustryCargoesWindow(this->selected_type);
614  break;
615 
616  case WID_DPI_FUND_WIDGET: {
617  if (this->selected_type == INVALID_INDUSTRYTYPE) {
618  this->HandleButtonClick(WID_DPI_FUND_WIDGET);
619 
620  if (Town::GetNumItems() == 0) {
621  ShowErrorMessage(STR_ERROR_CAN_T_GENERATE_INDUSTRIES, STR_ERROR_MUST_FOUND_TOWN_FIRST, WL_INFO);
622  } else {
623  extern void GenerateIndustries();
624  _generating_world = true;
626  _generating_world = false;
627  }
628  } else if (_game_mode != GM_EDITOR && _settings_game.construction.raw_industry_construction == 2 && GetIndustrySpec(this->selected_type)->IsRawIndustry()) {
629  DoCommandP(0, this->selected_type, InteractiveRandom(), CMD_BUILD_INDUSTRY | CMD_MSG(STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY));
630  this->HandleButtonClick(WID_DPI_FUND_WIDGET);
631  } else {
632  HandlePlacePushButton(this, WID_DPI_FUND_WIDGET, SPR_CURSOR_INDUSTRY, HT_RECT);
633  }
634  break;
635  }
636  }
637  }
638 
639  virtual void OnResize()
640  {
641  /* Adjust the number of items in the matrix depending of the resize */
642  this->vscroll->SetCapacityFromWidget(this, WID_DPI_MATRIX_WIDGET);
643  }
644 
645  virtual void OnPlaceObject(Point pt, TileIndex tile)
646  {
647  bool success = true;
648  /* We do not need to protect ourselves against "Random Many Industries" in this mode */
649  const IndustrySpec *indsp = GetIndustrySpec(this->selected_type);
650  uint32 seed = InteractiveRandom();
651 
652  if (_game_mode == GM_EDITOR) {
653  /* Show error if no town exists at all */
654  if (Town::GetNumItems() == 0) {
655  SetDParam(0, indsp->name);
656  ShowErrorMessage(STR_ERROR_CAN_T_BUILD_HERE, STR_ERROR_MUST_FOUND_TOWN_FIRST, WL_INFO, pt.x, pt.y);
657  return;
658  }
659 
660  Backup<CompanyByte> cur_company(_current_company, OWNER_NONE, FILE_LINE);
661  _generating_world = true;
662  _ignore_restrictions = true;
663 
664  DoCommandP(tile, (InteractiveRandomRange(indsp->num_table) << 8) | this->selected_type, seed,
665  CMD_BUILD_INDUSTRY | CMD_MSG(STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY), &CcBuildIndustry);
666 
667  cur_company.Restore();
668  _ignore_restrictions = false;
669  _generating_world = false;
670  } else {
671  success = DoCommandP(tile, (InteractiveRandomRange(indsp->num_table) << 8) | this->selected_type, seed, CMD_BUILD_INDUSTRY | CMD_MSG(STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY));
672  }
673 
674  /* If an industry has been built, just reset the cursor and the system */
676  }
677 
678  virtual void OnGameTick()
679  {
680  if (!this->timer_enabled) return;
681  if (--this->callback_timer == 0) {
682  /* We have just passed another day.
683  * See if we need to update availability of currently selected industry */
684  this->callback_timer = DAY_TICKS; // restart counter
685 
686  const IndustrySpec *indsp = GetIndustrySpec(this->selected_type);
687 
688  if (indsp->enabled) {
689  bool call_back_result = GetIndustryProbabilityCallback(this->selected_type, IACT_USERCREATION, 1) > 0;
690 
691  /* Only if result does match the previous state would it require a redraw. */
692  if (call_back_result != this->enabled[this->selected_index]) {
693  this->enabled[this->selected_index] = call_back_result;
694  this->SetButtons();
695  this->SetDirty();
696  }
697  }
698  }
699  }
700 
701  virtual void OnTimeout()
702  {
703  this->RaiseButtons();
704  }
705 
706  virtual void OnPlaceObjectAbort()
707  {
708  this->RaiseButtons();
709  }
710 
716  virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
717  {
718  if (!gui_scope) return;
719  this->SetupArrays();
720 
721  const IndustrySpec *indsp = (this->selected_type == INVALID_INDUSTRYTYPE) ? NULL : GetIndustrySpec(this->selected_type);
722  if (indsp == NULL) this->enabled[this->selected_index] = _settings_game.difficulty.industry_density != ID_FUND_ONLY;
723  this->SetButtons();
724  }
725 };
726 
727 void ShowBuildIndustryWindow()
728 {
729  if (_game_mode != GM_EDITOR && !Company::IsValidID(_local_company)) return;
731  new BuildIndustryWindow();
732 }
733 
734 static void UpdateIndustryProduction(Industry *i);
735 
736 static inline bool IsProductionAlterable(const Industry *i)
737 {
738  const IndustrySpec *is = GetIndustrySpec(i->type);
739  bool has_prod = false;
740  for (size_t j = 0; j < lengthof(is->production_rate); j++) {
741  if (is->production_rate[j] != 0) {
742  has_prod = true;
743  break;
744  }
745  }
746  return ((_game_mode == GM_EDITOR || _cheats.setup_prod.value) &&
747  (has_prod || is->IsRawIndustry()) &&
748  !_networking);
749 }
750 
752 {
754  enum Editability {
758  };
759 
761  enum InfoLine {
766  };
767 
774 
775 public:
776  IndustryViewWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc)
777  {
778  this->flags |= WF_DISABLE_VP_SCROLL;
779  this->editbox_line = IL_NONE;
780  this->clicked_line = IL_NONE;
781  this->clicked_button = 0;
782  this->info_height = WD_FRAMERECT_TOP + 2 * FONT_HEIGHT_NORMAL + WD_FRAMERECT_BOTTOM + 1; // Info panel has at least two lines text.
783 
784  this->InitNested(window_number);
785  NWidgetViewport *nvp = this->GetWidget<NWidgetViewport>(WID_IV_VIEWPORT);
786  nvp->InitializeViewport(this, Industry::Get(window_number)->location.GetCenterTile(), ZOOM_LVL_INDUSTRY);
787 
788  this->InvalidateData();
789  }
790 
791  virtual void OnPaint()
792  {
793  this->DrawWidgets();
794 
795  if (this->IsShaded()) return; // Don't draw anything when the window is shaded.
796 
797  NWidgetBase *nwi = this->GetWidget<NWidgetBase>(WID_IV_INFO);
798  uint expected = this->DrawInfo(nwi->pos_x, nwi->pos_x + nwi->current_x - 1, nwi->pos_y) - nwi->pos_y;
799  if (expected > nwi->current_y - 1) {
800  this->info_height = expected + 1;
801  this->ReInit();
802  return;
803  }
804  }
805 
813  int DrawInfo(uint left, uint right, uint top)
814  {
815  Industry *i = Industry::Get(this->window_number);
816  const IndustrySpec *ind = GetIndustrySpec(i->type);
817  int y = top + WD_FRAMERECT_TOP;
818  bool first = true;
819  bool has_accept = false;
820 
821  if (i->prod_level == PRODLEVEL_CLOSURE) {
822  DrawString(left + WD_FRAMERECT_LEFT, right - WD_FRAMERECT_RIGHT, y, STR_INDUSTRY_VIEW_INDUSTRY_ANNOUNCED_CLOSURE);
823  y += 2 * FONT_HEIGHT_NORMAL;
824  }
825 
826  CargoSuffix cargo_suffix[lengthof(i->accepts_cargo)];
827  GetAllCargoSuffixes(CARGOSUFFIX_IN, CST_VIEW, i, i->type, ind, i->accepts_cargo, cargo_suffix);
829 
830  uint left_side = left + WD_FRAMERECT_LEFT * 4; // Indent accepted cargoes.
831  for (byte j = 0; j < lengthof(i->accepts_cargo); j++) {
832  if (i->accepts_cargo[j] == CT_INVALID) continue;
833  has_accept = true;
834  if (first) {
835  DrawString(left + WD_FRAMERECT_LEFT, right - WD_FRAMERECT_RIGHT, y, STR_INDUSTRY_VIEW_REQUIRES);
836  y += FONT_HEIGHT_NORMAL;
837  first = false;
838  }
840  SetDParam(1, i->accepts_cargo[j]);
842  SetDParamStr(3, "");
843  StringID str = STR_NULL;
844  switch (cargo_suffix[j].display) {
846  SetDParamStr(3, cargo_suffix[j].text);
847  FALLTHROUGH;
848  case CSD_CARGO_AMOUNT:
849  str = stockpiling ? STR_INDUSTRY_VIEW_ACCEPT_CARGO_AMOUNT : STR_INDUSTRY_VIEW_ACCEPT_CARGO;
850  break;
851 
852  case CSD_CARGO_TEXT:
853  SetDParamStr(3, cargo_suffix[j].text);
854  FALLTHROUGH;
855  case CSD_CARGO:
856  str = STR_INDUSTRY_VIEW_ACCEPT_CARGO;
857  break;
858 
859  default:
860  NOT_REACHED();
861  }
862  DrawString(left_side, right - WD_FRAMERECT_RIGHT, y, str);
863  y += FONT_HEIGHT_NORMAL;
864  }
865 
866  GetAllCargoSuffixes(CARGOSUFFIX_OUT, CST_VIEW, i, i->type, ind, i->produced_cargo, cargo_suffix);
867  first = true;
868  for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
869  if (i->produced_cargo[j] == CT_INVALID) continue;
870  if (first) {
871  if (has_accept) y += WD_PAR_VSEP_WIDE;
872  DrawString(left + WD_FRAMERECT_LEFT, right - WD_FRAMERECT_RIGHT, y, STR_INDUSTRY_VIEW_PRODUCTION_LAST_MONTH_TITLE);
873  y += FONT_HEIGHT_NORMAL;
874  if (this->editable == EA_RATE) this->production_offset_y = y;
875  first = false;
876  }
877 
878  SetDParam(0, i->produced_cargo[j]);
880  SetDParamStr(2, cargo_suffix[j].text);
882  uint x = left + WD_FRAMETEXT_LEFT + (this->editable == EA_RATE ? SETTING_BUTTON_WIDTH + 10 : 0);
883  DrawString(x, right - WD_FRAMERECT_RIGHT, y, STR_INDUSTRY_VIEW_TRANSPORTED);
884  /* Let's put out those buttons.. */
885  if (this->editable == EA_RATE) {
886  DrawArrowButtons(left + WD_FRAMETEXT_LEFT, y, COLOUR_YELLOW, (this->clicked_line == IL_RATE1 + j) ? this->clicked_button : 0,
887  i->production_rate[j] > 0, i->production_rate[j] < 255);
888  }
889  y += FONT_HEIGHT_NORMAL;
890  }
891 
892  /* Display production multiplier if editable */
893  if (this->editable == EA_MULTIPLIER) {
894  y += WD_PAR_VSEP_WIDE;
895  this->production_offset_y = y;
897  uint x = left + WD_FRAMETEXT_LEFT + SETTING_BUTTON_WIDTH + 10;
898  DrawString(x, right - WD_FRAMERECT_RIGHT, y, STR_INDUSTRY_VIEW_PRODUCTION_LEVEL);
899  DrawArrowButtons(left + WD_FRAMETEXT_LEFT, y, COLOUR_YELLOW, (this->clicked_line == IL_MULTIPLIER) ? this->clicked_button : 0,
901  y += FONT_HEIGHT_NORMAL;
902  }
903 
904  /* Get the extra message for the GUI */
906  uint16 callback_res = GetIndustryCallback(CBID_INDUSTRY_WINDOW_MORE_TEXT, 0, 0, i, i->type, i->location.tile);
907  if (callback_res != CALLBACK_FAILED && callback_res != 0x400) {
908  if (callback_res > 0x400) {
910  } else {
911  StringID message = GetGRFStringID(ind->grf_prop.grffile->grfid, 0xD000 + callback_res);
912  if (message != STR_NULL && message != STR_UNDEFINED) {
913  y += WD_PAR_VSEP_WIDE;
914 
916  /* Use all the available space left from where we stand up to the
917  * end of the window. We ALSO enlarge the window if needed, so we
918  * can 'go' wild with the bottom of the window. */
919  y = DrawStringMultiLine(left + WD_FRAMERECT_LEFT, right - WD_FRAMERECT_RIGHT, y, UINT16_MAX, message, TC_BLACK);
921  }
922  }
923  }
924  }
925  return y + WD_FRAMERECT_BOTTOM;
926  }
927 
928  virtual void SetStringParameters(int widget) const
929  {
930  if (widget == WID_IV_CAPTION) SetDParam(0, this->window_number);
931  }
932 
933  virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
934  {
935  if (widget == WID_IV_INFO) size->height = this->info_height;
936  }
937 
938  virtual void OnClick(Point pt, int widget, int click_count)
939  {
940  switch (widget) {
941  case WID_IV_INFO: {
942  Industry *i = Industry::Get(this->window_number);
943  InfoLine line = IL_NONE;
944 
945  switch (this->editable) {
946  case EA_NONE: break;
947 
948  case EA_MULTIPLIER:
949  if (IsInsideBS(pt.y, this->production_offset_y, FONT_HEIGHT_NORMAL)) line = IL_MULTIPLIER;
950  break;
951 
952  case EA_RATE:
953  if (pt.y >= this->production_offset_y) {
954  int row = (pt.y - this->production_offset_y) / FONT_HEIGHT_NORMAL;
955  for (uint j = 0; j < lengthof(i->produced_cargo); j++) {
956  if (i->produced_cargo[j] == CT_INVALID) continue;
957  row--;
958  if (row < 0) {
959  line = (InfoLine)(IL_RATE1 + j);
960  break;
961  }
962  }
963  }
964  break;
965  }
966  if (line == IL_NONE) return;
967 
968  NWidgetBase *nwi = this->GetWidget<NWidgetBase>(widget);
969  int left = nwi->pos_x + WD_FRAMETEXT_LEFT;
970  int right = nwi->pos_x + nwi->current_x - 1 - WD_FRAMERECT_RIGHT;
971  if (IsInsideMM(pt.x, left, left + SETTING_BUTTON_WIDTH)) {
972  /* Clicked buttons, decrease or increase production */
973  byte button = (pt.x < left + SETTING_BUTTON_WIDTH / 2) ? 1 : 2;
974  switch (this->editable) {
975  case EA_MULTIPLIER:
976  if (button == 1) {
977  if (i->prod_level <= PRODLEVEL_MINIMUM) return;
978  i->prod_level = max<uint>(i->prod_level / 2, PRODLEVEL_MINIMUM);
979  } else {
980  if (i->prod_level >= PRODLEVEL_MAXIMUM) return;
982  }
983  break;
984 
985  case EA_RATE:
986  if (button == 1) {
987  if (i->production_rate[line - IL_RATE1] <= 0) return;
988  i->production_rate[line - IL_RATE1] = max(i->production_rate[line - IL_RATE1] / 2, 0);
989  } else {
990  if (i->production_rate[line - IL_RATE1] >= 255) return;
991  /* a zero production industry is unlikely to give anything but zero, so push it a little bit */
992  int new_prod = i->production_rate[line - IL_RATE1] == 0 ? 1 : i->production_rate[line - IL_RATE1] * 2;
993  i->production_rate[line - IL_RATE1] = minu(new_prod, 255);
994  }
995  break;
996 
997  default: NOT_REACHED();
998  }
999 
1000  UpdateIndustryProduction(i);
1001  this->SetDirty();
1002  this->SetTimeout();
1003  this->clicked_line = line;
1004  this->clicked_button = button;
1005  } else if (IsInsideMM(pt.x, left + SETTING_BUTTON_WIDTH + 10, right)) {
1006  /* clicked the text */
1007  this->editbox_line = line;
1008  switch (this->editable) {
1009  case EA_MULTIPLIER:
1011  ShowQueryString(STR_JUST_INT, STR_CONFIG_GAME_PRODUCTION_LEVEL, 10, this, CS_ALPHANUMERAL, QSF_NONE);
1012  break;
1013 
1014  case EA_RATE:
1015  SetDParam(0, i->production_rate[line - IL_RATE1] * 8);
1016  ShowQueryString(STR_JUST_INT, STR_CONFIG_GAME_PRODUCTION, 10, this, CS_ALPHANUMERAL, QSF_NONE);
1017  break;
1018 
1019  default: NOT_REACHED();
1020  }
1021  }
1022  break;
1023  }
1024 
1025  case WID_IV_GOTO: {
1026  Industry *i = Industry::Get(this->window_number);
1027  if (_ctrl_pressed) {
1029  } else {
1031  }
1032  break;
1033  }
1034 
1035  case WID_IV_DISPLAY: {
1036  Industry *i = Industry::Get(this->window_number);
1038  break;
1039  }
1040  }
1041  }
1042 
1043  virtual void OnTimeout()
1044  {
1045  this->clicked_line = IL_NONE;
1046  this->clicked_button = 0;
1047  this->SetDirty();
1048  }
1049 
1050  virtual void OnResize()
1051  {
1052  if (this->viewport != NULL) {
1053  NWidgetViewport *nvp = this->GetWidget<NWidgetViewport>(WID_IV_VIEWPORT);
1054  nvp->UpdateViewportCoordinates(this);
1055 
1056  ScrollWindowToTile(Industry::Get(this->window_number)->location.GetCenterTile(), this, true); // Re-center viewport.
1057  }
1058  }
1059 
1060  virtual void OnQueryTextFinished(char *str)
1061  {
1062  if (StrEmpty(str)) return;
1063 
1064  Industry *i = Industry::Get(this->window_number);
1065  uint value = atoi(str);
1066  switch (this->editbox_line) {
1067  case IL_NONE: NOT_REACHED();
1068 
1069  case IL_MULTIPLIER:
1071  break;
1072 
1073  default:
1074  i->production_rate[this->editbox_line - IL_RATE1] = ClampU(RoundDivSU(value, 8), 0, 255);
1075  break;
1076  }
1077  UpdateIndustryProduction(i);
1078  this->SetDirty();
1079  }
1080 
1086  virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
1087  {
1088  if (!gui_scope) return;
1089  const Industry *i = Industry::Get(this->window_number);
1090  if (IsProductionAlterable(i)) {
1091  const IndustrySpec *ind = GetIndustrySpec(i->type);
1092  this->editable = ind->UsesSmoothEconomy() ? EA_RATE : EA_MULTIPLIER;
1093  } else {
1094  this->editable = EA_NONE;
1095  }
1096  }
1097 
1098  virtual bool IsNewGRFInspectable() const
1099  {
1100  return ::IsNewGRFInspectable(GSF_INDUSTRIES, this->window_number);
1101  }
1102 
1103  virtual void ShowNewGRFInspectWindow() const
1104  {
1105  ::ShowNewGRFInspectWindow(GSF_INDUSTRIES, this->window_number);
1106  }
1107 };
1108 
1109 static void UpdateIndustryProduction(Industry *i)
1110 {
1111  const IndustrySpec *indspec = GetIndustrySpec(i->type);
1112  if (!indspec->UsesSmoothEconomy()) i->RecomputeProductionMultipliers();
1113 
1114  for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
1115  if (i->produced_cargo[j] != CT_INVALID) {
1116  i->last_month_production[j] = 8 * i->production_rate[j];
1117  }
1118  }
1119 }
1120 
1124  NWidget(WWT_CLOSEBOX, COLOUR_CREAM),
1125  NWidget(WWT_CAPTION, COLOUR_CREAM, WID_IV_CAPTION), SetDataTip(STR_INDUSTRY_VIEW_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1126  NWidget(WWT_DEBUGBOX, COLOUR_CREAM),
1127  NWidget(WWT_SHADEBOX, COLOUR_CREAM),
1128  NWidget(WWT_DEFSIZEBOX, COLOUR_CREAM),
1129  NWidget(WWT_STICKYBOX, COLOUR_CREAM),
1130  EndContainer(),
1131  NWidget(WWT_PANEL, COLOUR_CREAM),
1132  NWidget(WWT_INSET, COLOUR_CREAM), SetPadding(2, 2, 2, 2),
1133  NWidget(NWID_VIEWPORT, INVALID_COLOUR, WID_IV_VIEWPORT), SetMinimalSize(254, 86), SetFill(1, 0), SetPadding(1, 1, 1, 1), SetResize(1, 1),
1134  EndContainer(),
1135  EndContainer(),
1136  NWidget(WWT_PANEL, COLOUR_CREAM, WID_IV_INFO), SetMinimalSize(260, 2), SetResize(1, 0),
1137  EndContainer(),
1139  NWidget(WWT_PUSHTXTBTN, COLOUR_CREAM, WID_IV_GOTO), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_BUTTON_LOCATION, STR_INDUSTRY_VIEW_LOCATION_TOOLTIP),
1140  NWidget(WWT_PUSHTXTBTN, COLOUR_CREAM, WID_IV_DISPLAY), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_INDUSTRY_DISPLAY_CHAIN, STR_INDUSTRY_DISPLAY_CHAIN_TOOLTIP),
1141  NWidget(WWT_RESIZEBOX, COLOUR_CREAM),
1142  EndContainer(),
1143 };
1144 
1147  WDP_AUTO, "view_industry", 260, 120,
1149  0,
1150  _nested_industry_view_widgets, lengthof(_nested_industry_view_widgets)
1151 );
1152 
1153 void ShowIndustryViewWindow(int industry)
1154 {
1155  AllocateWindowDescFront<IndustryViewWindow>(&_industry_view_desc, industry);
1156 }
1157 
1161  NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
1162  NWidget(WWT_CAPTION, COLOUR_BROWN), SetDataTip(STR_INDUSTRY_DIRECTORY_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1163  NWidget(WWT_SHADEBOX, COLOUR_BROWN),
1164  NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
1165  NWidget(WWT_STICKYBOX, COLOUR_BROWN),
1166  EndContainer(),
1170  NWidget(WWT_TEXTBTN, COLOUR_BROWN, WID_ID_DROPDOWN_ORDER), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER),
1171  NWidget(WWT_DROPDOWN, COLOUR_BROWN, WID_ID_DROPDOWN_CRITERIA), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_SORT_CRITERIA),
1172  NWidget(WWT_PANEL, COLOUR_BROWN), SetResize(1, 0), EndContainer(),
1173  EndContainer(),
1174  NWidget(WWT_PANEL, COLOUR_BROWN, WID_ID_INDUSTRY_LIST), SetDataTip(0x0, STR_INDUSTRY_DIRECTORY_LIST_CAPTION), SetResize(1, 1), SetScrollbar(WID_ID_SCROLLBAR), EndContainer(),
1175  EndContainer(),
1177  NWidget(NWID_VSCROLLBAR, COLOUR_BROWN, WID_ID_SCROLLBAR),
1178  NWidget(WWT_RESIZEBOX, COLOUR_BROWN),
1179  EndContainer(),
1180  EndContainer(),
1181 };
1182 
1184 
1185 
1190 protected:
1191  /* Runtime saved values */
1192  static Listing last_sorting;
1193  static const Industry *last_industry;
1194 
1195  /* Constants for sorting stations */
1196  static const StringID sorter_names[];
1197  static GUIIndustryList::SortFunction * const sorter_funcs[];
1198 
1199  GUIIndustryList industries;
1200  Scrollbar *vscroll;
1201 
1204  {
1205  if (this->industries.NeedRebuild()) {
1206  this->industries.Clear();
1207 
1208  const Industry *i;
1209  FOR_ALL_INDUSTRIES(i) {
1210  *this->industries.Append() = i;
1211  }
1212 
1213  this->industries.Compact();
1214  this->industries.RebuildDone();
1215  this->vscroll->SetCount(this->industries.Length()); // Update scrollbar as well.
1216  }
1217 
1218  if (!this->industries.Sort()) return;
1219  IndustryDirectoryWindow::last_industry = NULL; // Reset name sorter sort cache
1220  this->SetWidgetDirty(WID_ID_INDUSTRY_LIST); // Set the modified widget dirty
1221  }
1222 
1230  static inline int GetCargoTransportedPercentsIfValid(const Industry *i, uint id)
1231  {
1232  assert(id < lengthof(i->produced_cargo));
1233 
1234  if (i->produced_cargo[id] == CT_INVALID) return 101;
1235  return ToPercent8(i->last_month_pct_transported[id]);
1236  }
1237 
1246  {
1247  int p1 = GetCargoTransportedPercentsIfValid(i, 0);
1248  int p2 = GetCargoTransportedPercentsIfValid(i, 1);
1249 
1250  if (p1 > p2) Swap(p1, p2); // lower value has higher priority
1251 
1252  return (p1 << 8) + p2;
1253  }
1254 
1256  static int CDECL IndustryNameSorter(const Industry * const *a, const Industry * const *b)
1257  {
1258  static char buf_cache[96];
1259  static char buf[96];
1260 
1261  SetDParam(0, (*a)->index);
1262  GetString(buf, STR_INDUSTRY_NAME, lastof(buf));
1263 
1264  if (*b != last_industry) {
1265  last_industry = *b;
1266  SetDParam(0, (*b)->index);
1267  GetString(buf_cache, STR_INDUSTRY_NAME, lastof(buf_cache));
1268  }
1269 
1270  return strnatcmp(buf, buf_cache); // Sort by name (natural sorting).
1271  }
1272 
1274  static int CDECL IndustryTypeSorter(const Industry * const *a, const Industry * const *b)
1275  {
1276  int it_a = 0;
1277  while (it_a != NUM_INDUSTRYTYPES && (*a)->type != _sorted_industry_types[it_a]) it_a++;
1278  int it_b = 0;
1279  while (it_b != NUM_INDUSTRYTYPES && (*b)->type != _sorted_industry_types[it_b]) it_b++;
1280  int r = it_a - it_b;
1281  return (r == 0) ? IndustryNameSorter(a, b) : r;
1282  }
1283 
1285  static int CDECL IndustryProductionSorter(const Industry * const *a, const Industry * const *b)
1286  {
1287  uint prod_a = 0, prod_b = 0;
1288  for (uint i = 0; i < lengthof((*a)->produced_cargo); i++) {
1289  if ((*a)->produced_cargo[i] != CT_INVALID) prod_a += (*a)->last_month_production[i];
1290  if ((*b)->produced_cargo[i] != CT_INVALID) prod_b += (*b)->last_month_production[i];
1291  }
1292  int r = prod_a - prod_b;
1293 
1294  return (r == 0) ? IndustryTypeSorter(a, b) : r;
1295  }
1296 
1298  static int CDECL IndustryTransportedCargoSorter(const Industry * const *a, const Industry * const *b)
1299  {
1300  int r = GetCargoTransportedSortValue(*a) - GetCargoTransportedSortValue(*b);
1301  return (r == 0) ? IndustryNameSorter(a, b) : r;
1302  }
1303 
1310  {
1311  const IndustrySpec *indsp = GetIndustrySpec(i->type);
1312  byte p = 0;
1313 
1314  /* Industry name */
1315  SetDParam(p++, i->index);
1316 
1317  static CargoSuffix cargo_suffix[lengthof(i->produced_cargo)];
1318  GetAllCargoSuffixes(CARGOSUFFIX_OUT, CST_DIR, i, i->type, indsp, i->produced_cargo, cargo_suffix);
1319 
1320  /* Industry productions */
1321  for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
1322  if (i->produced_cargo[j] == CT_INVALID) continue;
1323  SetDParam(p++, i->produced_cargo[j]);
1324  SetDParam(p++, i->last_month_production[j]);
1325  SetDParamStr(p++, cargo_suffix[j].text);
1326  }
1327 
1328  /* Transported productions */
1329  for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
1330  if (i->produced_cargo[j] == CT_INVALID) continue;
1332  }
1333 
1334  /* Drawing the right string */
1335  switch (p) {
1336  case 1: return STR_INDUSTRY_DIRECTORY_ITEM_NOPROD;
1337  case 5: return STR_INDUSTRY_DIRECTORY_ITEM;
1338  default: return STR_INDUSTRY_DIRECTORY_ITEM_TWO;
1339  }
1340  }
1341 
1342 public:
1343  IndustryDirectoryWindow(WindowDesc *desc, WindowNumber number) : Window(desc)
1344  {
1345  this->CreateNestedTree();
1346  this->vscroll = this->GetScrollbar(WID_ID_SCROLLBAR);
1347 
1348  this->industries.SetListing(this->last_sorting);
1349  this->industries.SetSortFuncs(IndustryDirectoryWindow::sorter_funcs);
1350  this->industries.ForceRebuild();
1351  this->BuildSortIndustriesList();
1352 
1353  this->FinishInitNested(0);
1354  }
1355 
1357  {
1358  this->last_sorting = this->industries.GetListing();
1359  }
1360 
1361  virtual void SetStringParameters(int widget) const
1362  {
1363  if (widget == WID_ID_DROPDOWN_CRITERIA) SetDParam(0, IndustryDirectoryWindow::sorter_names[this->industries.SortType()]);
1364  }
1365 
1366  virtual void DrawWidget(const Rect &r, int widget) const
1367  {
1368  switch (widget) {
1369  case WID_ID_DROPDOWN_ORDER:
1370  this->DrawSortButtonState(widget, this->industries.IsDescSortOrder() ? SBS_DOWN : SBS_UP);
1371  break;
1372 
1373  case WID_ID_INDUSTRY_LIST: {
1374  int n = 0;
1375  int y = r.top + WD_FRAMERECT_TOP;
1376  if (this->industries.Length() == 0) {
1377  DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_INDUSTRY_DIRECTORY_NONE);
1378  break;
1379  }
1380  for (uint i = this->vscroll->GetPosition(); i < this->industries.Length(); i++) {
1381  DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, this->GetIndustryString(this->industries[i]));
1382 
1383  y += this->resize.step_height;
1384  if (++n == this->vscroll->GetCapacity()) break; // max number of industries in 1 window
1385  }
1386  break;
1387  }
1388  }
1389  }
1390 
1391  virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
1392  {
1393  switch (widget) {
1394  case WID_ID_DROPDOWN_ORDER: {
1395  Dimension d = GetStringBoundingBox(this->GetWidget<NWidgetCore>(widget)->widget_data);
1396  d.width += padding.width + Window::SortButtonWidth() * 2; // Doubled since the string is centred and it also looks better.
1397  d.height += padding.height;
1398  *size = maxdim(*size, d);
1399  break;
1400  }
1401 
1402  case WID_ID_DROPDOWN_CRITERIA: {
1403  Dimension d = {0, 0};
1404  for (uint i = 0; IndustryDirectoryWindow::sorter_names[i] != INVALID_STRING_ID; i++) {
1405  d = maxdim(d, GetStringBoundingBox(IndustryDirectoryWindow::sorter_names[i]));
1406  }
1407  d.width += padding.width;
1408  d.height += padding.height;
1409  *size = maxdim(*size, d);
1410  break;
1411  }
1412 
1413  case WID_ID_INDUSTRY_LIST: {
1414  Dimension d = GetStringBoundingBox(STR_INDUSTRY_DIRECTORY_NONE);
1415  for (uint i = 0; i < this->industries.Length(); i++) {
1416  d = maxdim(d, GetStringBoundingBox(this->GetIndustryString(this->industries[i])));
1417  }
1418  resize->height = d.height;
1419  d.height *= 5;
1420  d.width += padding.width + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
1421  d.height += padding.height + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
1422  *size = maxdim(*size, d);
1423  break;
1424  }
1425  }
1426  }
1427 
1428 
1429  virtual void OnClick(Point pt, int widget, int click_count)
1430  {
1431  switch (widget) {
1432  case WID_ID_DROPDOWN_ORDER:
1433  this->industries.ToggleSortOrder();
1434  this->SetDirty();
1435  break;
1436 
1438  ShowDropDownMenu(this, IndustryDirectoryWindow::sorter_names, this->industries.SortType(), WID_ID_DROPDOWN_CRITERIA, 0, 0);
1439  break;
1440 
1441  case WID_ID_INDUSTRY_LIST: {
1442  uint p = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_ID_INDUSTRY_LIST, WD_FRAMERECT_TOP);
1443  if (p < this->industries.Length()) {
1444  if (_ctrl_pressed) {
1445  ShowExtraViewPortWindow(this->industries[p]->location.tile);
1446  } else {
1447  ScrollMainWindowToTile(this->industries[p]->location.tile);
1448  }
1449  }
1450  break;
1451  }
1452  }
1453  }
1454 
1455  virtual void OnDropdownSelect(int widget, int index)
1456  {
1457  if (this->industries.SortType() != index) {
1458  this->industries.SetSortType(index);
1459  this->BuildSortIndustriesList();
1460  }
1461  }
1462 
1463  virtual void OnResize()
1464  {
1465  this->vscroll->SetCapacityFromWidget(this, WID_ID_INDUSTRY_LIST);
1466  }
1467 
1468  virtual void OnPaint()
1469  {
1470  if (this->industries.NeedRebuild()) this->BuildSortIndustriesList();
1471  this->DrawWidgets();
1472  }
1473 
1474  virtual void OnHundredthTick()
1475  {
1476  this->industries.ForceResort();
1477  this->BuildSortIndustriesList();
1478  }
1479 
1485  virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
1486  {
1487  if (data == 0) {
1488  /* This needs to be done in command-scope to enforce rebuilding before resorting invalid data */
1489  this->industries.ForceRebuild();
1490  } else {
1491  this->industries.ForceResort();
1492  }
1493  }
1494 };
1495 
1496 Listing IndustryDirectoryWindow::last_sorting = {false, 0};
1497 const Industry *IndustryDirectoryWindow::last_industry = NULL;
1498 
1499 /* Available station sorting functions. */
1500 GUIIndustryList::SortFunction * const IndustryDirectoryWindow::sorter_funcs[] = {
1501  &IndustryNameSorter,
1502  &IndustryTypeSorter,
1503  &IndustryProductionSorter,
1504  &IndustryTransportedCargoSorter
1505 };
1506 
1507 /* Names of the sorting functions */
1508 const StringID IndustryDirectoryWindow::sorter_names[] = {
1509  STR_SORT_BY_NAME,
1510  STR_SORT_BY_TYPE,
1511  STR_SORT_BY_PRODUCTION,
1512  STR_SORT_BY_TRANSPORTED,
1514 };
1515 
1516 
1519  WDP_AUTO, "list_industries", 428, 190,
1521  0,
1522  _nested_industry_directory_widgets, lengthof(_nested_industry_directory_widgets)
1523 );
1524 
1525 void ShowIndustryDirectory()
1526 {
1527  AllocateWindowDescFront<IndustryDirectoryWindow>(&_industry_directory_desc, 0);
1528 }
1529 
1533  NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
1534  NWidget(WWT_CAPTION, COLOUR_BROWN, WID_IC_CAPTION), SetDataTip(STR_INDUSTRY_CARGOES_INDUSTRY_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1535  NWidget(WWT_SHADEBOX, COLOUR_BROWN),
1536  NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
1537  NWidget(WWT_STICKYBOX, COLOUR_BROWN),
1538  EndContainer(),
1543  NWidget(WWT_TEXTBTN, COLOUR_BROWN, WID_IC_NOTIFY),
1544  SetDataTip(STR_INDUSTRY_CARGOES_NOTIFY_SMALLMAP, STR_INDUSTRY_CARGOES_NOTIFY_SMALLMAP_TOOLTIP),
1545  NWidget(WWT_PANEL, COLOUR_BROWN), SetFill(1, 0), SetResize(0, 0), EndContainer(),
1546  NWidget(WWT_DROPDOWN, COLOUR_BROWN, WID_IC_IND_DROPDOWN), SetFill(0, 0), SetResize(0, 0),
1547  SetDataTip(STR_INDUSTRY_CARGOES_SELECT_INDUSTRY, STR_INDUSTRY_CARGOES_SELECT_INDUSTRY_TOOLTIP),
1548  NWidget(WWT_DROPDOWN, COLOUR_BROWN, WID_IC_CARGO_DROPDOWN), SetFill(0, 0), SetResize(0, 0),
1549  SetDataTip(STR_INDUSTRY_CARGOES_SELECT_CARGO, STR_INDUSTRY_CARGOES_SELECT_CARGO_TOOLTIP),
1550  EndContainer(),
1551  EndContainer(),
1553  NWidget(NWID_VSCROLLBAR, COLOUR_BROWN, WID_IC_SCROLLBAR),
1554  NWidget(WWT_RESIZEBOX, COLOUR_BROWN),
1555  EndContainer(),
1556  EndContainer(),
1557 };
1558 
1561  WDP_AUTO, "industry_cargoes", 300, 210,
1563  0,
1564  _nested_industry_cargoes_widgets, lengthof(_nested_industry_cargoes_widgets)
1565 );
1566 
1575 };
1576 
1577 static const uint MAX_CARGOES = 16;
1578 
1581  static const int VERT_INTER_INDUSTRY_SPACE;
1582  static const int HOR_CARGO_BORDER_SPACE;
1583  static const int CARGO_STUB_WIDTH;
1584  static const int HOR_CARGO_WIDTH, HOR_CARGO_SPACE;
1585  static const int VERT_CARGO_SPACE, VERT_CARGO_EDGE;
1586  static const int BLOB_DISTANCE, BLOB_WIDTH, BLOB_HEIGHT;
1587 
1588  static const int INDUSTRY_LINE_COLOUR;
1589  static const int CARGO_LINE_COLOUR;
1590 
1591  static int small_height, normal_height;
1592  static int cargo_field_width;
1593  static int industry_width;
1594  static uint max_cargoes;
1595 
1597  union {
1598  struct {
1599  IndustryType ind_type;
1600  CargoID other_produced[MAX_CARGOES];
1601  CargoID other_accepted[MAX_CARGOES];
1602  } industry;
1603  struct {
1604  CargoID vertical_cargoes[MAX_CARGOES];
1606  CargoID supp_cargoes[MAX_CARGOES];
1607  byte top_end;
1608  CargoID cust_cargoes[MAX_CARGOES];
1609  byte bottom_end;
1610  } cargo;
1611  struct {
1613  bool left_align;
1614  } cargo_label;
1616  } u; // Data for each type.
1617 
1623  {
1624  this->type = type;
1625  }
1626 
1632  void MakeIndustry(IndustryType ind_type)
1633  {
1634  this->type = CFT_INDUSTRY;
1635  this->u.industry.ind_type = ind_type;
1636  MemSetT(this->u.industry.other_accepted, INVALID_CARGO, MAX_CARGOES);
1637  MemSetT(this->u.industry.other_produced, INVALID_CARGO, MAX_CARGOES);
1638  }
1639 
1646  int ConnectCargo(CargoID cargo, bool producer)
1647  {
1648  assert(this->type == CFT_CARGO);
1649  if (cargo == INVALID_CARGO) return -1;
1650 
1651  /* Find the vertical cargo column carrying the cargo. */
1652  int column = -1;
1653  for (int i = 0; i < this->u.cargo.num_cargoes; i++) {
1654  if (cargo == this->u.cargo.vertical_cargoes[i]) {
1655  column = i;
1656  break;
1657  }
1658  }
1659  if (column < 0) return -1;
1660 
1661  if (producer) {
1662  assert(this->u.cargo.supp_cargoes[column] == INVALID_CARGO);
1663  this->u.cargo.supp_cargoes[column] = column;
1664  } else {
1665  assert(this->u.cargo.cust_cargoes[column] == INVALID_CARGO);
1666  this->u.cargo.cust_cargoes[column] = column;
1667  }
1668  return column;
1669  }
1670 
1676  {
1677  assert(this->type == CFT_CARGO);
1678 
1679  for (uint i = 0; i < MAX_CARGOES; i++) {
1680  if (this->u.cargo.supp_cargoes[i] != INVALID_CARGO) return true;
1681  if (this->u.cargo.cust_cargoes[i] != INVALID_CARGO) return true;
1682  }
1683  return false;
1684  }
1685 
1695  void MakeCargo(const CargoID *cargoes, uint length, int count = -1, bool top_end = false, bool bottom_end = false)
1696  {
1697  this->type = CFT_CARGO;
1698  uint i;
1699  uint num = 0;
1700  for (i = 0; i < MAX_CARGOES && i < length; i++) {
1701  if (cargoes[i] != INVALID_CARGO) {
1702  this->u.cargo.vertical_cargoes[num] = cargoes[i];
1703  num++;
1704  }
1705  }
1706  this->u.cargo.num_cargoes = (count < 0) ? num : count;
1707  for (; num < MAX_CARGOES; num++) this->u.cargo.vertical_cargoes[num] = INVALID_CARGO;
1708  this->u.cargo.top_end = top_end;
1709  this->u.cargo.bottom_end = bottom_end;
1710  MemSetT(this->u.cargo.supp_cargoes, INVALID_CARGO, MAX_CARGOES);
1711  MemSetT(this->u.cargo.cust_cargoes, INVALID_CARGO, MAX_CARGOES);
1712  }
1713 
1720  void MakeCargoLabel(const CargoID *cargoes, uint length, bool left_align)
1721  {
1722  this->type = CFT_CARGO_LABEL;
1723  uint i;
1724  for (i = 0; i < MAX_CARGOES && i < length; i++) this->u.cargo_label.cargoes[i] = cargoes[i];
1725  for (; i < MAX_CARGOES; i++) this->u.cargo_label.cargoes[i] = INVALID_CARGO;
1726  this->u.cargo_label.left_align = left_align;
1727  }
1728 
1733  void MakeHeader(StringID textid)
1734  {
1735  this->type = CFT_HEADER;
1736  this->u.header = textid;
1737  }
1738 
1744  int GetCargoBase(int xpos) const
1745  {
1746  assert(this->type == CFT_CARGO);
1747  int n = this->u.cargo.num_cargoes;
1748 
1749  if (n % 2 == 0) {
1750  return xpos + cargo_field_width / 2 - (HOR_CARGO_WIDTH + HOR_CARGO_SPACE / 2) * (n / 2);
1751  } else {
1752  return xpos + cargo_field_width / 2 - HOR_CARGO_WIDTH / 2 - (HOR_CARGO_WIDTH + HOR_CARGO_SPACE) * (n / 2);
1753  }
1754  }
1755 
1761  void Draw(int xpos, int ypos) const
1762  {
1763  switch (this->type) {
1764  case CFT_EMPTY:
1765  case CFT_SMALL_EMPTY:
1766  break;
1767 
1768  case CFT_HEADER:
1769  ypos += (small_height - FONT_HEIGHT_NORMAL) / 2;
1770  DrawString(xpos, xpos + industry_width, ypos, this->u.header, TC_WHITE, SA_HOR_CENTER);
1771  break;
1772 
1773  case CFT_INDUSTRY: {
1774  int ypos1 = ypos + VERT_INTER_INDUSTRY_SPACE / 2;
1775  int ypos2 = ypos + normal_height - 1 - VERT_INTER_INDUSTRY_SPACE / 2;
1776  int xpos2 = xpos + industry_width - 1;
1777  GfxDrawLine(xpos, ypos1, xpos2, ypos1, INDUSTRY_LINE_COLOUR);
1778  GfxDrawLine(xpos, ypos1, xpos, ypos2, INDUSTRY_LINE_COLOUR);
1779  GfxDrawLine(xpos, ypos2, xpos2, ypos2, INDUSTRY_LINE_COLOUR);
1780  GfxDrawLine(xpos2, ypos1, xpos2, ypos2, INDUSTRY_LINE_COLOUR);
1781  ypos += (normal_height - FONT_HEIGHT_NORMAL) / 2;
1782  if (this->u.industry.ind_type < NUM_INDUSTRYTYPES) {
1783  const IndustrySpec *indsp = GetIndustrySpec(this->u.industry.ind_type);
1784  DrawString(xpos, xpos2, ypos, indsp->name, TC_WHITE, SA_HOR_CENTER);
1785 
1786  /* Draw the industry legend. */
1787  int blob_left, blob_right;
1788  if (_current_text_dir == TD_RTL) {
1789  blob_right = xpos2 - BLOB_DISTANCE;
1790  blob_left = blob_right - BLOB_WIDTH;
1791  } else {
1792  blob_left = xpos + BLOB_DISTANCE;
1793  blob_right = blob_left + BLOB_WIDTH;
1794  }
1795  GfxFillRect(blob_left, ypos2 - BLOB_DISTANCE - BLOB_HEIGHT, blob_right, ypos2 - BLOB_DISTANCE, PC_BLACK); // Border
1796  GfxFillRect(blob_left + 1, ypos2 - BLOB_DISTANCE - BLOB_HEIGHT + 1, blob_right - 1, ypos2 - BLOB_DISTANCE - 1, indsp->map_colour);
1797  } else {
1798  DrawString(xpos, xpos2, ypos, STR_INDUSTRY_CARGOES_HOUSES, TC_FROMSTRING, SA_HOR_CENTER);
1799  }
1800 
1801  /* Draw the other_produced/other_accepted cargoes. */
1802  const CargoID *other_right, *other_left;
1803  if (_current_text_dir == TD_RTL) {
1804  other_right = this->u.industry.other_accepted;
1805  other_left = this->u.industry.other_produced;
1806  } else {
1807  other_right = this->u.industry.other_produced;
1808  other_left = this->u.industry.other_accepted;
1809  }
1810  ypos1 += VERT_CARGO_EDGE;
1811  for (uint i = 0; i < CargoesField::max_cargoes; i++) {
1812  if (other_right[i] != INVALID_CARGO) {
1813  const CargoSpec *csp = CargoSpec::Get(other_right[i]);
1814  int xp = xpos + industry_width + CARGO_STUB_WIDTH;
1815  DrawHorConnection(xpos + industry_width, xp - 1, ypos1, csp);
1816  GfxDrawLine(xp, ypos1, xp, ypos1 + FONT_HEIGHT_NORMAL - 1, CARGO_LINE_COLOUR);
1817  }
1818  if (other_left[i] != INVALID_CARGO) {
1819  const CargoSpec *csp = CargoSpec::Get(other_left[i]);
1820  int xp = xpos - CARGO_STUB_WIDTH;
1821  DrawHorConnection(xp + 1, xpos - 1, ypos1, csp);
1822  GfxDrawLine(xp, ypos1, xp, ypos1 + FONT_HEIGHT_NORMAL - 1, CARGO_LINE_COLOUR);
1823  }
1824  ypos1 += FONT_HEIGHT_NORMAL + VERT_CARGO_SPACE;
1825  }
1826  break;
1827  }
1828 
1829  case CFT_CARGO: {
1830  int cargo_base = this->GetCargoBase(xpos);
1831  int top = ypos + (this->u.cargo.top_end ? VERT_INTER_INDUSTRY_SPACE / 2 + 1 : 0);
1832  int bot = ypos - (this->u.cargo.bottom_end ? VERT_INTER_INDUSTRY_SPACE / 2 + 1 : 0) + normal_height - 1;
1833  int colpos = cargo_base;
1834  for (int i = 0; i < this->u.cargo.num_cargoes; i++) {
1835  if (this->u.cargo.top_end) GfxDrawLine(colpos, top - 1, colpos + HOR_CARGO_WIDTH - 1, top - 1, CARGO_LINE_COLOUR);
1836  if (this->u.cargo.bottom_end) GfxDrawLine(colpos, bot + 1, colpos + HOR_CARGO_WIDTH - 1, bot + 1, CARGO_LINE_COLOUR);
1837  GfxDrawLine(colpos, top, colpos, bot, CARGO_LINE_COLOUR);
1838  colpos++;
1839  const CargoSpec *csp = CargoSpec::Get(this->u.cargo.vertical_cargoes[i]);
1840  GfxFillRect(colpos, top, colpos + HOR_CARGO_WIDTH - 2, bot, csp->legend_colour, FILLRECT_OPAQUE);
1841  colpos += HOR_CARGO_WIDTH - 2;
1842  GfxDrawLine(colpos, top, colpos, bot, CARGO_LINE_COLOUR);
1843  colpos += 1 + HOR_CARGO_SPACE;
1844  }
1845 
1846  const CargoID *hor_left, *hor_right;
1847  if (_current_text_dir == TD_RTL) {
1848  hor_left = this->u.cargo.cust_cargoes;
1849  hor_right = this->u.cargo.supp_cargoes;
1850  } else {
1851  hor_left = this->u.cargo.supp_cargoes;
1852  hor_right = this->u.cargo.cust_cargoes;
1853  }
1854  ypos += VERT_CARGO_EDGE + VERT_INTER_INDUSTRY_SPACE / 2;
1855  for (uint i = 0; i < MAX_CARGOES; i++) {
1856  if (hor_left[i] != INVALID_CARGO) {
1857  int col = hor_left[i];
1858  int dx = 0;
1859  const CargoSpec *csp = CargoSpec::Get(this->u.cargo.vertical_cargoes[col]);
1860  for (; col > 0; col--) {
1861  int lf = cargo_base + col * HOR_CARGO_WIDTH + (col - 1) * HOR_CARGO_SPACE;
1862  DrawHorConnection(lf, lf + HOR_CARGO_SPACE - dx, ypos, csp);
1863  dx = 1;
1864  }
1865  DrawHorConnection(xpos, cargo_base - dx, ypos, csp);
1866  }
1867  if (hor_right[i] != INVALID_CARGO) {
1868  int col = hor_right[i];
1869  int dx = 0;
1870  const CargoSpec *csp = CargoSpec::Get(this->u.cargo.vertical_cargoes[col]);
1871  for (; col < this->u.cargo.num_cargoes - 1; col++) {
1872  int lf = cargo_base + (col + 1) * HOR_CARGO_WIDTH + col * HOR_CARGO_SPACE;
1873  DrawHorConnection(lf + dx - 1, lf + HOR_CARGO_SPACE - 1, ypos, csp);
1874  dx = 1;
1875  }
1876  DrawHorConnection(cargo_base + col * HOR_CARGO_SPACE + (col + 1) * HOR_CARGO_WIDTH - 1 + dx, xpos + CargoesField::cargo_field_width - 1, ypos, csp);
1877  }
1878  ypos += FONT_HEIGHT_NORMAL + VERT_CARGO_SPACE;
1879  }
1880  break;
1881  }
1882 
1883  case CFT_CARGO_LABEL:
1884  ypos += VERT_CARGO_EDGE + VERT_INTER_INDUSTRY_SPACE / 2;
1885  for (uint i = 0; i < MAX_CARGOES; i++) {
1886  if (this->u.cargo_label.cargoes[i] != INVALID_CARGO) {
1887  const CargoSpec *csp = CargoSpec::Get(this->u.cargo_label.cargoes[i]);
1888  DrawString(xpos + WD_FRAMERECT_LEFT, xpos + industry_width - 1 - WD_FRAMERECT_RIGHT, ypos, csp->name, TC_WHITE,
1889  (this->u.cargo_label.left_align) ? SA_LEFT : SA_RIGHT);
1890  }
1891  ypos += FONT_HEIGHT_NORMAL + VERT_CARGO_SPACE;
1892  }
1893  break;
1894 
1895  default:
1896  NOT_REACHED();
1897  }
1898  }
1899 
1907  CargoID CargoClickedAt(const CargoesField *left, const CargoesField *right, Point pt) const
1908  {
1909  assert(this->type == CFT_CARGO);
1910 
1911  /* Vertical matching. */
1912  int cpos = this->GetCargoBase(0);
1913  uint col;
1914  for (col = 0; col < this->u.cargo.num_cargoes; col++) {
1915  if (pt.x < cpos) break;
1916  if (pt.x < cpos + CargoesField::HOR_CARGO_WIDTH) return this->u.cargo.vertical_cargoes[col];
1918  }
1919  /* col = 0 -> left of first col, 1 -> left of 2nd col, ... this->u.cargo.num_cargoes right of last-col. */
1920 
1921  int vpos = VERT_INTER_INDUSTRY_SPACE / 2 + VERT_CARGO_EDGE;
1922  uint row;
1923  for (row = 0; row < MAX_CARGOES; row++) {
1924  if (pt.y < vpos) return INVALID_CARGO;
1925  if (pt.y < vpos + FONT_HEIGHT_NORMAL) break;
1926  vpos += FONT_HEIGHT_NORMAL + VERT_CARGO_SPACE;
1927  }
1928  if (row == MAX_CARGOES) return INVALID_CARGO;
1929 
1930  /* row = 0 -> at first horizontal row, row = 1 -> second horizontal row, 2 = 3rd horizontal row. */
1931  if (col == 0) {
1932  if (this->u.cargo.supp_cargoes[row] != INVALID_CARGO) return this->u.cargo.vertical_cargoes[this->u.cargo.supp_cargoes[row]];
1933  if (left != NULL) {
1934  if (left->type == CFT_INDUSTRY) return left->u.industry.other_produced[row];
1935  if (left->type == CFT_CARGO_LABEL && !left->u.cargo_label.left_align) return left->u.cargo_label.cargoes[row];
1936  }
1937  return INVALID_CARGO;
1938  }
1939  if (col == this->u.cargo.num_cargoes) {
1940  if (this->u.cargo.cust_cargoes[row] != INVALID_CARGO) return this->u.cargo.vertical_cargoes[this->u.cargo.cust_cargoes[row]];
1941  if (right != NULL) {
1942  if (right->type == CFT_INDUSTRY) return right->u.industry.other_accepted[row];
1943  if (right->type == CFT_CARGO_LABEL && right->u.cargo_label.left_align) return right->u.cargo_label.cargoes[row];
1944  }
1945  return INVALID_CARGO;
1946  }
1947  if (row >= col) {
1948  /* Clicked somewhere in-between vertical cargo connection.
1949  * Since the horizontal connection is made in the same order as the vertical list, the above condition
1950  * ensures we are left-below the main diagonal, thus at the supplying side.
1951  */
1952  return (this->u.cargo.supp_cargoes[row] != INVALID_CARGO) ? this->u.cargo.vertical_cargoes[this->u.cargo.supp_cargoes[row]] : INVALID_CARGO;
1953  } else {
1954  /* Clicked at a customer connection. */
1955  return (this->u.cargo.cust_cargoes[row] != INVALID_CARGO) ? this->u.cargo.vertical_cargoes[this->u.cargo.cust_cargoes[row]] : INVALID_CARGO;
1956  }
1957  }
1958 
1965  {
1966  assert(this->type == CFT_CARGO_LABEL);
1967 
1968  int vpos = VERT_INTER_INDUSTRY_SPACE / 2 + VERT_CARGO_EDGE;
1969  uint row;
1970  for (row = 0; row < MAX_CARGOES; row++) {
1971  if (pt.y < vpos) return INVALID_CARGO;
1972  if (pt.y < vpos + FONT_HEIGHT_NORMAL) break;
1973  vpos += FONT_HEIGHT_NORMAL + VERT_CARGO_SPACE;
1974  }
1975  if (row == MAX_CARGOES) return INVALID_CARGO;
1976  return this->u.cargo_label.cargoes[row];
1977  }
1978 
1979 private:
1987  static void DrawHorConnection(int left, int right, int top, const CargoSpec *csp)
1988  {
1989  GfxDrawLine(left, top, right, top, CARGO_LINE_COLOUR);
1990  GfxFillRect(left, top + 1, right, top + FONT_HEIGHT_NORMAL - 2, csp->legend_colour, FILLRECT_OPAQUE);
1991  GfxDrawLine(left, top + FONT_HEIGHT_NORMAL - 1, right, top + FONT_HEIGHT_NORMAL - 1, CARGO_LINE_COLOUR);
1992  }
1993 };
1994 
1995 assert_compile(MAX_CARGOES >= cpp_lengthof(IndustrySpec, produced_cargo));
1996 assert_compile(MAX_CARGOES >= cpp_lengthof(IndustrySpec, accepts_cargo));
1997 
2004 
2005 const int CargoesField::HOR_CARGO_BORDER_SPACE = 15;
2006 const int CargoesField::CARGO_STUB_WIDTH = 10;
2007 const int CargoesField::HOR_CARGO_WIDTH = 15;
2008 const int CargoesField::HOR_CARGO_SPACE = 5;
2009 const int CargoesField::VERT_CARGO_EDGE = 4;
2010 const int CargoesField::VERT_CARGO_SPACE = 4;
2011 
2012 const int CargoesField::BLOB_DISTANCE = 5;
2013 const int CargoesField::BLOB_WIDTH = 12;
2014 const int CargoesField::BLOB_HEIGHT = 9;
2015 
2018 
2020 struct CargoesRow {
2021  CargoesField columns[5];
2022 
2027  void ConnectIndustryProduced(int column)
2028  {
2029  CargoesField *ind_fld = this->columns + column;
2030  CargoesField *cargo_fld = this->columns + column + 1;
2031  assert(ind_fld->type == CFT_INDUSTRY && cargo_fld->type == CFT_CARGO);
2032 
2033  MemSetT(ind_fld->u.industry.other_produced, INVALID_CARGO, MAX_CARGOES);
2034 
2035  if (ind_fld->u.industry.ind_type < NUM_INDUSTRYTYPES) {
2036  CargoID others[MAX_CARGOES]; // Produced cargoes not carried in the cargo column.
2037  int other_count = 0;
2038 
2039  const IndustrySpec *indsp = GetIndustrySpec(ind_fld->u.industry.ind_type);
2040  assert(CargoesField::max_cargoes <= lengthof(indsp->produced_cargo));
2041  for (uint i = 0; i < CargoesField::max_cargoes; i++) {
2042  int col = cargo_fld->ConnectCargo(indsp->produced_cargo[i], true);
2043  if (col < 0) others[other_count++] = indsp->produced_cargo[i];
2044  }
2045 
2046  /* Allocate other cargoes in the empty holes of the horizontal cargo connections. */
2047  for (uint i = 0; i < CargoesField::max_cargoes && other_count > 0; i++) {
2048  if (cargo_fld->u.cargo.supp_cargoes[i] == INVALID_CARGO) ind_fld->u.industry.other_produced[i] = others[--other_count];
2049  }
2050  } else {
2051  /* Houses only display what is demanded. */
2052  for (uint i = 0; i < cargo_fld->u.cargo.num_cargoes; i++) {
2053  CargoID cid = cargo_fld->u.cargo.vertical_cargoes[i];
2054  if (cid == CT_PASSENGERS || cid == CT_MAIL) cargo_fld->ConnectCargo(cid, true);
2055  }
2056  }
2057  }
2058 
2064  void MakeCargoLabel(int column, bool accepting)
2065  {
2066  CargoID cargoes[MAX_CARGOES];
2067  MemSetT(cargoes, INVALID_CARGO, lengthof(cargoes));
2068 
2069  CargoesField *label_fld = this->columns + column;
2070  CargoesField *cargo_fld = this->columns + (accepting ? column - 1 : column + 1);
2071 
2072  assert(cargo_fld->type == CFT_CARGO && label_fld->type == CFT_EMPTY);
2073  for (uint i = 0; i < cargo_fld->u.cargo.num_cargoes; i++) {
2074  int col = cargo_fld->ConnectCargo(cargo_fld->u.cargo.vertical_cargoes[i], !accepting);
2075  if (col >= 0) cargoes[col] = cargo_fld->u.cargo.vertical_cargoes[i];
2076  }
2077  label_fld->MakeCargoLabel(cargoes, lengthof(cargoes), accepting);
2078  }
2079 
2080 
2085  void ConnectIndustryAccepted(int column)
2086  {
2087  CargoesField *ind_fld = this->columns + column;
2088  CargoesField *cargo_fld = this->columns + column - 1;
2089  assert(ind_fld->type == CFT_INDUSTRY && cargo_fld->type == CFT_CARGO);
2090 
2091  MemSetT(ind_fld->u.industry.other_accepted, INVALID_CARGO, MAX_CARGOES);
2092 
2093  if (ind_fld->u.industry.ind_type < NUM_INDUSTRYTYPES) {
2094  CargoID others[MAX_CARGOES]; // Accepted cargoes not carried in the cargo column.
2095  int other_count = 0;
2096 
2097  const IndustrySpec *indsp = GetIndustrySpec(ind_fld->u.industry.ind_type);
2099  for (uint i = 0; i < CargoesField::max_cargoes; i++) {
2100  int col = cargo_fld->ConnectCargo(indsp->accepts_cargo[i], false);
2101  if (col < 0) others[other_count++] = indsp->accepts_cargo[i];
2102  }
2103 
2104  /* Allocate other cargoes in the empty holes of the horizontal cargo connections. */
2105  for (uint i = 0; i < CargoesField::max_cargoes && other_count > 0; i++) {
2106  if (cargo_fld->u.cargo.cust_cargoes[i] == INVALID_CARGO) ind_fld->u.industry.other_accepted[i] = others[--other_count];
2107  }
2108  } else {
2109  /* Houses only display what is demanded. */
2110  for (uint i = 0; i < cargo_fld->u.cargo.num_cargoes; i++) {
2111  for (uint h = 0; h < NUM_HOUSES; h++) {
2112  HouseSpec *hs = HouseSpec::Get(h);
2113  if (!hs->enabled) continue;
2114 
2115  for (uint j = 0; j < lengthof(hs->accepts_cargo); j++) {
2116  if (hs->cargo_acceptance[j] > 0 && cargo_fld->u.cargo.vertical_cargoes[i] == hs->accepts_cargo[j]) {
2117  cargo_fld->ConnectCargo(cargo_fld->u.cargo.vertical_cargoes[i], false);
2118  goto next_cargo;
2119  }
2120  }
2121  }
2122 next_cargo: ;
2123  }
2124  }
2125  }
2126 };
2127 
2128 
2157  static const int HOR_TEXT_PADDING, VERT_TEXT_PADDING;
2158 
2160 
2161  Fields fields;
2162  uint ind_cargo;
2165  Scrollbar *vscroll;
2166 
2167  IndustryCargoesWindow(int id) : Window(&_industry_cargoes_desc)
2168  {
2169  this->OnInit();
2170  this->CreateNestedTree();
2171  this->vscroll = this->GetScrollbar(WID_IC_SCROLLBAR);
2172  this->FinishInitNested(0);
2173  this->OnInvalidateData(id);
2174  }
2175 
2176  virtual void OnInit()
2177  {
2178  /* Initialize static CargoesField size variables. */
2179  Dimension d = GetStringBoundingBox(STR_INDUSTRY_CARGOES_PRODUCERS);
2180  d = maxdim(d, GetStringBoundingBox(STR_INDUSTRY_CARGOES_CUSTOMERS));
2182  d.height += WD_FRAMETEXT_TOP + WD_FRAMETEXT_BOTTOM;
2183  CargoesField::small_height = d.height;
2184 
2185  /* Decide about the size of the box holding the text of an industry type. */
2186  this->ind_textsize.width = 0;
2187  this->ind_textsize.height = 0;
2189  for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
2190  const IndustrySpec *indsp = GetIndustrySpec(it);
2191  if (!indsp->enabled) continue;
2192  this->ind_textsize = maxdim(this->ind_textsize, GetStringBoundingBox(indsp->name));
2194  CargoesField::max_cargoes = max<uint>(CargoesField::max_cargoes, std::count_if(indsp->produced_cargo, endof(indsp->produced_cargo), IsCargoIDValid));
2195  }
2196  d.width = max(d.width, this->ind_textsize.width);
2197  d.height = this->ind_textsize.height;
2198  this->ind_textsize = maxdim(this->ind_textsize, GetStringBoundingBox(STR_INDUSTRY_CARGOES_SELECT_INDUSTRY));
2199 
2200  /* Compute max size of the cargo texts. */
2201  this->cargo_textsize.width = 0;
2202  this->cargo_textsize.height = 0;
2203  for (uint i = 0; i < NUM_CARGO; i++) {
2204  const CargoSpec *csp = CargoSpec::Get(i);
2205  if (!csp->IsValid()) continue;
2206  this->cargo_textsize = maxdim(this->cargo_textsize, GetStringBoundingBox(csp->name));
2207  }
2208  d = maxdim(d, this->cargo_textsize); // Box must also be wide enough to hold any cargo label.
2209  this->cargo_textsize = maxdim(this->cargo_textsize, GetStringBoundingBox(STR_INDUSTRY_CARGOES_SELECT_CARGO));
2210 
2211  d.width += 2 * HOR_TEXT_PADDING;
2212  /* Ensure the height is enough for the industry type text, for the horizontal connections, and for the cargo labels. */
2214  d.height = max(d.height + 2 * VERT_TEXT_PADDING, min_ind_height);
2215 
2216  CargoesField::industry_width = d.width;
2218 
2219  /* Width of a #CFT_CARGO field. */
2221  }
2222 
2223  virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
2224  {
2225  switch (widget) {
2226  case WID_IC_PANEL:
2228  break;
2229 
2230  case WID_IC_IND_DROPDOWN:
2231  size->width = max(size->width, this->ind_textsize.width + padding.width);
2232  break;
2233 
2234  case WID_IC_CARGO_DROPDOWN:
2235  size->width = max(size->width, this->cargo_textsize.width + padding.width);
2236  break;
2237  }
2238  }
2239 
2240 
2242  virtual void SetStringParameters (int widget) const
2243  {
2244  if (widget != WID_IC_CAPTION) return;
2245 
2246  if (this->ind_cargo < NUM_INDUSTRYTYPES) {
2247  const IndustrySpec *indsp = GetIndustrySpec(this->ind_cargo);
2248  SetDParam(0, indsp->name);
2249  } else {
2250  const CargoSpec *csp = CargoSpec::Get(this->ind_cargo - NUM_INDUSTRYTYPES);
2251  SetDParam(0, csp->name);
2252  }
2253  }
2254 
2263  static bool HasCommonValidCargo(const CargoID *cargoes1, uint length1, const CargoID *cargoes2, uint length2)
2264  {
2265  while (length1 > 0) {
2266  if (*cargoes1 != INVALID_CARGO) {
2267  for (uint i = 0; i < length2; i++) if (*cargoes1 == cargoes2[i]) return true;
2268  }
2269  cargoes1++;
2270  length1--;
2271  }
2272  return false;
2273  }
2274 
2281  static bool HousesCanSupply(const CargoID *cargoes, uint length)
2282  {
2283  for (uint i = 0; i < length; i++) {
2284  if (cargoes[i] == INVALID_CARGO) continue;
2285  if (cargoes[i] == CT_PASSENGERS || cargoes[i] == CT_MAIL) return true;
2286  }
2287  return false;
2288  }
2289 
2296  static bool HousesCanAccept(const CargoID *cargoes, uint length)
2297  {
2298  HouseZones climate_mask;
2300  case LT_TEMPERATE: climate_mask = HZ_TEMP; break;
2301  case LT_ARCTIC: climate_mask = HZ_SUBARTC_ABOVE | HZ_SUBARTC_BELOW; break;
2302  case LT_TROPIC: climate_mask = HZ_SUBTROPIC; break;
2303  case LT_TOYLAND: climate_mask = HZ_TOYLND; break;
2304  default: NOT_REACHED();
2305  }
2306  for (uint i = 0; i < length; i++) {
2307  if (cargoes[i] == INVALID_CARGO) continue;
2308 
2309  for (uint h = 0; h < NUM_HOUSES; h++) {
2310  HouseSpec *hs = HouseSpec::Get(h);
2311  if (!hs->enabled || !(hs->building_availability & climate_mask)) continue;
2312 
2313  for (uint j = 0; j < lengthof(hs->accepts_cargo); j++) {
2314  if (hs->cargo_acceptance[j] > 0 && cargoes[i] == hs->accepts_cargo[j]) return true;
2315  }
2316  }
2317  }
2318  return false;
2319  }
2320 
2327  static int CountMatchingAcceptingIndustries(const CargoID *cargoes, uint length)
2328  {
2329  int count = 0;
2330  for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
2331  const IndustrySpec *indsp = GetIndustrySpec(it);
2332  if (!indsp->enabled) continue;
2333 
2334  if (HasCommonValidCargo(cargoes, length, indsp->accepts_cargo, lengthof(indsp->accepts_cargo))) count++;
2335  }
2336  return count;
2337  }
2338 
2345  static int CountMatchingProducingIndustries(const CargoID *cargoes, uint length)
2346  {
2347  int count = 0;
2348  for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
2349  const IndustrySpec *indsp = GetIndustrySpec(it);
2350  if (!indsp->enabled) continue;
2351 
2352  if (HasCommonValidCargo(cargoes, length, indsp->produced_cargo, lengthof(indsp->produced_cargo))) count++;
2353  }
2354  return count;
2355  }
2356 
2363  void ShortenCargoColumn(int column, int top, int bottom)
2364  {
2365  while (top < bottom && !this->fields[top].columns[column].HasConnection()) {
2366  this->fields[top].columns[column].MakeEmpty(CFT_EMPTY);
2367  top++;
2368  }
2369  this->fields[top].columns[column].u.cargo.top_end = true;
2370 
2371  while (bottom > top && !this->fields[bottom].columns[column].HasConnection()) {
2372  this->fields[bottom].columns[column].MakeEmpty(CFT_EMPTY);
2373  bottom--;
2374  }
2375  this->fields[bottom].columns[column].u.cargo.bottom_end = true;
2376  }
2377 
2384  void PlaceIndustry(int row, int col, IndustryType it)
2385  {
2386  assert(this->fields[row].columns[col].type == CFT_EMPTY);
2387  this->fields[row].columns[col].MakeIndustry(it);
2388  if (col == 0) {
2389  this->fields[row].ConnectIndustryProduced(col);
2390  } else {
2391  this->fields[row].ConnectIndustryAccepted(col);
2392  }
2393  }
2394 
2399  {
2400  if (!this->IsWidgetLowered(WID_IC_NOTIFY)) return;
2401 
2402  /* Only notify the smallmap window if it exists. In particular, do not
2403  * bring it to the front to prevent messing up any nice layout of the user. */
2405  }
2406 
2411  void ComputeIndustryDisplay(IndustryType it)
2412  {
2413  this->GetWidget<NWidgetCore>(WID_IC_CAPTION)->widget_data = STR_INDUSTRY_CARGOES_INDUSTRY_CAPTION;
2414  this->ind_cargo = it;
2415  _displayed_industries.reset();
2416  _displayed_industries.set(it);
2417 
2418  this->fields.Clear();
2419  CargoesRow *row = this->fields.Append();
2420  row->columns[0].MakeHeader(STR_INDUSTRY_CARGOES_PRODUCERS);
2424  row->columns[4].MakeHeader(STR_INDUSTRY_CARGOES_CUSTOMERS);
2425 
2426  const IndustrySpec *central_sp = GetIndustrySpec(it);
2427  bool houses_supply = HousesCanSupply(central_sp->accepts_cargo, lengthof(central_sp->accepts_cargo));
2428  bool houses_accept = HousesCanAccept(central_sp->produced_cargo, lengthof(central_sp->produced_cargo));
2429  /* Make a field consisting of two cargo columns. */
2430  int num_supp = CountMatchingProducingIndustries(central_sp->accepts_cargo, lengthof(central_sp->accepts_cargo)) + houses_supply;
2431  int num_cust = CountMatchingAcceptingIndustries(central_sp->produced_cargo, lengthof(central_sp->produced_cargo)) + houses_accept;
2432  int num_indrows = max(3, max(num_supp, num_cust)); // One is needed for the 'it' industry, and 2 for the cargo labels.
2433  for (int i = 0; i < num_indrows; i++) {
2434  CargoesRow *row = this->fields.Append();
2435  row->columns[0].MakeEmpty(CFT_EMPTY);
2436  row->columns[1].MakeCargo(central_sp->accepts_cargo, lengthof(central_sp->accepts_cargo));
2437  row->columns[2].MakeEmpty(CFT_EMPTY);
2438  row->columns[3].MakeCargo(central_sp->produced_cargo, lengthof(central_sp->produced_cargo));
2439  row->columns[4].MakeEmpty(CFT_EMPTY);
2440  }
2441  /* Add central industry. */
2442  int central_row = 1 + num_indrows / 2;
2443  this->fields[central_row].columns[2].MakeIndustry(it);
2444  this->fields[central_row].ConnectIndustryProduced(2);
2445  this->fields[central_row].ConnectIndustryAccepted(2);
2446 
2447  /* Add cargo labels. */
2448  this->fields[central_row - 1].MakeCargoLabel(2, true);
2449  this->fields[central_row + 1].MakeCargoLabel(2, false);
2450 
2451  /* Add suppliers and customers of the 'it' industry. */
2452  int supp_count = 0;
2453  int cust_count = 0;
2454  for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
2455  const IndustrySpec *indsp = GetIndustrySpec(it);
2456  if (!indsp->enabled) continue;
2457 
2458  if (HasCommonValidCargo(central_sp->accepts_cargo, lengthof(central_sp->accepts_cargo), indsp->produced_cargo, lengthof(indsp->produced_cargo))) {
2459  this->PlaceIndustry(1 + supp_count * num_indrows / num_supp, 0, it);
2460  _displayed_industries.set(it);
2461  supp_count++;
2462  }
2463  if (HasCommonValidCargo(central_sp->produced_cargo, lengthof(central_sp->produced_cargo), indsp->accepts_cargo, lengthof(indsp->accepts_cargo))) {
2464  this->PlaceIndustry(1 + cust_count * num_indrows / num_cust, 4, it);
2465  _displayed_industries.set(it);
2466  cust_count++;
2467  }
2468  }
2469  if (houses_supply) {
2470  this->PlaceIndustry(1 + supp_count * num_indrows / num_supp, 0, NUM_INDUSTRYTYPES);
2471  supp_count++;
2472  }
2473  if (houses_accept) {
2474  this->PlaceIndustry(1 + cust_count * num_indrows / num_cust, 4, NUM_INDUSTRYTYPES);
2475  cust_count++;
2476  }
2477 
2478  this->ShortenCargoColumn(1, 1, num_indrows);
2479  this->ShortenCargoColumn(3, 1, num_indrows);
2480  const NWidgetBase *nwp = this->GetWidget<NWidgetBase>(WID_IC_PANEL);
2482  this->SetDirty();
2483  this->NotifySmallmap();
2484  }
2485 
2491  {
2492  this->GetWidget<NWidgetCore>(WID_IC_CAPTION)->widget_data = STR_INDUSTRY_CARGOES_CARGO_CAPTION;
2493  this->ind_cargo = cid + NUM_INDUSTRYTYPES;
2494  _displayed_industries.reset();
2495 
2496  this->fields.Clear();
2497  CargoesRow *row = this->fields.Append();
2498  row->columns[0].MakeHeader(STR_INDUSTRY_CARGOES_PRODUCERS);
2500  row->columns[2].MakeHeader(STR_INDUSTRY_CARGOES_CUSTOMERS);
2503 
2504  bool houses_supply = HousesCanSupply(&cid, 1);
2505  bool houses_accept = HousesCanAccept(&cid, 1);
2506  int num_supp = CountMatchingProducingIndustries(&cid, 1) + houses_supply + 1; // Ensure room for the cargo label.
2507  int num_cust = CountMatchingAcceptingIndustries(&cid, 1) + houses_accept;
2508  int num_indrows = max(num_supp, num_cust);
2509  for (int i = 0; i < num_indrows; i++) {
2510  CargoesRow *row = this->fields.Append();
2511  row->columns[0].MakeEmpty(CFT_EMPTY);
2512  row->columns[1].MakeCargo(&cid, 1);
2513  row->columns[2].MakeEmpty(CFT_EMPTY);
2514  row->columns[3].MakeEmpty(CFT_EMPTY);
2515  row->columns[4].MakeEmpty(CFT_EMPTY);
2516  }
2517 
2518  this->fields[num_indrows].MakeCargoLabel(0, false); // Add cargo labels at the left bottom.
2519 
2520  /* Add suppliers and customers of the cargo. */
2521  int supp_count = 0;
2522  int cust_count = 0;
2523  for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
2524  const IndustrySpec *indsp = GetIndustrySpec(it);
2525  if (!indsp->enabled) continue;
2526 
2527  if (HasCommonValidCargo(&cid, 1, indsp->produced_cargo, lengthof(indsp->produced_cargo))) {
2528  this->PlaceIndustry(1 + supp_count * num_indrows / num_supp, 0, it);
2529  _displayed_industries.set(it);
2530  supp_count++;
2531  }
2532  if (HasCommonValidCargo(&cid, 1, indsp->accepts_cargo, lengthof(indsp->accepts_cargo))) {
2533  this->PlaceIndustry(1 + cust_count * num_indrows / num_cust, 2, it);
2534  _displayed_industries.set(it);
2535  cust_count++;
2536  }
2537  }
2538  if (houses_supply) {
2539  this->PlaceIndustry(1 + supp_count * num_indrows / num_supp, 0, NUM_INDUSTRYTYPES);
2540  supp_count++;
2541  }
2542  if (houses_accept) {
2543  this->PlaceIndustry(1 + cust_count * num_indrows / num_cust, 2, NUM_INDUSTRYTYPES);
2544  cust_count++;
2545  }
2546 
2547  this->ShortenCargoColumn(1, 1, num_indrows);
2548  const NWidgetBase *nwp = this->GetWidget<NWidgetBase>(WID_IC_PANEL);
2550  this->SetDirty();
2551  this->NotifySmallmap();
2552  }
2553 
2561  virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
2562  {
2563  if (!gui_scope) return;
2564  if (data == NUM_INDUSTRYTYPES) {
2565  if (this->IsWidgetLowered(WID_IC_NOTIFY)) {
2566  this->RaiseWidget(WID_IC_NOTIFY);
2567  this->SetWidgetDirty(WID_IC_NOTIFY);
2568  }
2569  return;
2570  }
2571 
2572  assert(data >= 0 && data < NUM_INDUSTRYTYPES);
2573  this->ComputeIndustryDisplay(data);
2574  }
2575 
2576  virtual void DrawWidget(const Rect &r, int widget) const
2577  {
2578  if (widget != WID_IC_PANEL) return;
2579 
2580  DrawPixelInfo tmp_dpi, *old_dpi;
2581  int width = r.right - r.left + 1;
2582  int height = r.bottom - r.top + 1 - WD_FRAMERECT_TOP - WD_FRAMERECT_BOTTOM;
2583  if (!FillDrawPixelInfo(&tmp_dpi, r.left + WD_FRAMERECT_LEFT, r.top + WD_FRAMERECT_TOP, width, height)) return;
2584  old_dpi = _cur_dpi;
2585  _cur_dpi = &tmp_dpi;
2586 
2587  int left_pos = WD_FRAMERECT_LEFT;
2588  if (this->ind_cargo >= NUM_INDUSTRYTYPES) left_pos += (CargoesField::industry_width + CargoesField::cargo_field_width) / 2;
2589  int last_column = (this->ind_cargo < NUM_INDUSTRYTYPES) ? 4 : 2;
2590 
2591  const NWidgetBase *nwp = this->GetWidget<NWidgetBase>(WID_IC_PANEL);
2592  int vpos = -this->vscroll->GetPosition() * nwp->resize_y;
2593  for (uint i = 0; i < this->fields.Length(); i++) {
2594  int row_height = (i == 0) ? CargoesField::small_height : CargoesField::normal_height;
2595  if (vpos + row_height >= 0) {
2596  int xpos = left_pos;
2597  int col, dir;
2598  if (_current_text_dir == TD_RTL) {
2599  col = last_column;
2600  dir = -1;
2601  } else {
2602  col = 0;
2603  dir = 1;
2604  }
2605  while (col >= 0 && col <= last_column) {
2606  this->fields[i].columns[col].Draw(xpos, vpos);
2608  col += dir;
2609  }
2610  }
2611  vpos += row_height;
2612  if (vpos >= height) break;
2613  }
2614 
2615  _cur_dpi = old_dpi;
2616  }
2617 
2625  bool CalculatePositionInWidget(Point pt, Point *fieldxy, Point *xy)
2626  {
2627  const NWidgetBase *nw = this->GetWidget<NWidgetBase>(WID_IC_PANEL);
2628  pt.x -= nw->pos_x;
2629  pt.y -= nw->pos_y;
2630 
2631  int vpos = WD_FRAMERECT_TOP + CargoesField::small_height - this->vscroll->GetPosition() * nw->resize_y;
2632  if (pt.y < vpos) return false;
2633 
2634  int row = (pt.y - vpos) / CargoesField::normal_height; // row is relative to row 1.
2635  if (row + 1 >= (int)this->fields.Length()) return false;
2636  vpos = pt.y - vpos - row * CargoesField::normal_height; // Position in the row + 1 field
2637  row++; // rebase row to match index of this->fields.
2638 
2639  int xpos = 2 * WD_FRAMERECT_LEFT + ((this->ind_cargo < NUM_INDUSTRYTYPES) ? 0 : (CargoesField::industry_width + CargoesField::cargo_field_width) / 2);
2640  if (pt.x < xpos) return false;
2641  int column;
2642  for (column = 0; column <= 5; column++) {
2644  if (pt.x < xpos + width) break;
2645  xpos += width;
2646  }
2647  int num_columns = (this->ind_cargo < NUM_INDUSTRYTYPES) ? 4 : 2;
2648  if (column > num_columns) return false;
2649  xpos = pt.x - xpos;
2650 
2651  /* Return both positions, compensating for RTL languages (which works due to the equal symmetry in both displays). */
2652  fieldxy->y = row;
2653  xy->y = vpos;
2654  if (_current_text_dir == TD_RTL) {
2655  fieldxy->x = num_columns - column;
2656  xy->x = ((column & 1) ? CargoesField::cargo_field_width : CargoesField::industry_width) - xpos;
2657  } else {
2658  fieldxy->x = column;
2659  xy->x = xpos;
2660  }
2661  return true;
2662  }
2663 
2664  virtual void OnClick(Point pt, int widget, int click_count)
2665  {
2666  switch (widget) {
2667  case WID_IC_PANEL: {
2668  Point fieldxy, xy;
2669  if (!CalculatePositionInWidget(pt, &fieldxy, &xy)) return;
2670 
2671  const CargoesField *fld = this->fields[fieldxy.y].columns + fieldxy.x;
2672  switch (fld->type) {
2673  case CFT_INDUSTRY:
2674  if (fld->u.industry.ind_type < NUM_INDUSTRYTYPES) this->ComputeIndustryDisplay(fld->u.industry.ind_type);
2675  break;
2676 
2677  case CFT_CARGO: {
2678  CargoesField *lft = (fieldxy.x > 0) ? this->fields[fieldxy.y].columns + fieldxy.x - 1 : NULL;
2679  CargoesField *rgt = (fieldxy.x < 4) ? this->fields[fieldxy.y].columns + fieldxy.x + 1 : NULL;
2680  CargoID cid = fld->CargoClickedAt(lft, rgt, xy);
2681  if (cid != INVALID_CARGO) this->ComputeCargoDisplay(cid);
2682  break;
2683  }
2684 
2685  case CFT_CARGO_LABEL: {
2686  CargoID cid = fld->CargoLabelClickedAt(xy);
2687  if (cid != INVALID_CARGO) this->ComputeCargoDisplay(cid);
2688  break;
2689  }
2690 
2691  default:
2692  break;
2693  }
2694  break;
2695  }
2696 
2697  case WID_IC_NOTIFY:
2698  this->ToggleWidgetLoweredState(WID_IC_NOTIFY);
2699  this->SetWidgetDirty(WID_IC_NOTIFY);
2700  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
2701 
2702  if (this->IsWidgetLowered(WID_IC_NOTIFY)) {
2703  if (FindWindowByClass(WC_SMALLMAP) == NULL) ShowSmallMap();
2704  this->NotifySmallmap();
2705  }
2706  break;
2707 
2708  case WID_IC_CARGO_DROPDOWN: {
2709  DropDownList *lst = new DropDownList;
2710  const CargoSpec *cs;
2712  *lst->Append() = new DropDownListStringItem(cs->name, cs->Index(), false);
2713  }
2714  if (lst->Length() == 0) {
2715  delete lst;
2716  break;
2717  }
2718  int selected = (this->ind_cargo >= NUM_INDUSTRYTYPES) ? (int)(this->ind_cargo - NUM_INDUSTRYTYPES) : -1;
2719  ShowDropDownList(this, lst, selected, WID_IC_CARGO_DROPDOWN, 0, true);
2720  break;
2721  }
2722 
2723  case WID_IC_IND_DROPDOWN: {
2724  DropDownList *lst = new DropDownList;
2725  for (uint i = 0; i < NUM_INDUSTRYTYPES; i++) {
2726  IndustryType ind = _sorted_industry_types[i];
2727  const IndustrySpec *indsp = GetIndustrySpec(ind);
2728  if (!indsp->enabled) continue;
2729  *lst->Append() = new DropDownListStringItem(indsp->name, ind, false);
2730  }
2731  if (lst->Length() == 0) {
2732  delete lst;
2733  break;
2734  }
2735  int selected = (this->ind_cargo < NUM_INDUSTRYTYPES) ? (int)this->ind_cargo : -1;
2736  ShowDropDownList(this, lst, selected, WID_IC_IND_DROPDOWN, 0, true);
2737  break;
2738  }
2739  }
2740  }
2741 
2742  virtual void OnDropdownSelect(int widget, int index)
2743  {
2744  if (index < 0) return;
2745 
2746  switch (widget) {
2747  case WID_IC_CARGO_DROPDOWN:
2748  this->ComputeCargoDisplay(index);
2749  break;
2750 
2751  case WID_IC_IND_DROPDOWN:
2752  this->ComputeIndustryDisplay(index);
2753  break;
2754  }
2755  }
2756 
2757  bool OnTooltip(Point pt, int widget, TooltipCloseCondition close_cond)
2758  {
2759  if (widget != WID_IC_PANEL) return false;
2760 
2761  Point fieldxy, xy;
2762  if (!CalculatePositionInWidget(pt, &fieldxy, &xy)) return false;
2763 
2764  const CargoesField *fld = this->fields[fieldxy.y].columns + fieldxy.x;
2765  CargoID cid = INVALID_CARGO;
2766  switch (fld->type) {
2767  case CFT_CARGO: {
2768  CargoesField *lft = (fieldxy.x > 0) ? this->fields[fieldxy.y].columns + fieldxy.x - 1 : NULL;
2769  CargoesField *rgt = (fieldxy.x < 4) ? this->fields[fieldxy.y].columns + fieldxy.x + 1 : NULL;
2770  cid = fld->CargoClickedAt(lft, rgt, xy);
2771  break;
2772  }
2773 
2774  case CFT_CARGO_LABEL: {
2775  cid = fld->CargoLabelClickedAt(xy);
2776  break;
2777  }
2778 
2779  case CFT_INDUSTRY:
2780  if (fld->u.industry.ind_type < NUM_INDUSTRYTYPES && (this->ind_cargo >= NUM_INDUSTRYTYPES || fieldxy.x != 2)) {
2781  GuiShowTooltips(this, STR_INDUSTRY_CARGOES_INDUSTRY_TOOLTIP, 0, NULL, close_cond);
2782  }
2783  return true;
2784 
2785  default:
2786  break;
2787  }
2788  if (cid != INVALID_CARGO && (this->ind_cargo < NUM_INDUSTRYTYPES || cid != this->ind_cargo - NUM_INDUSTRYTYPES)) {
2789  const CargoSpec *csp = CargoSpec::Get(cid);
2790  uint64 params[5];
2791  params[0] = csp->name;
2792  GuiShowTooltips(this, STR_INDUSTRY_CARGOES_CARGO_TOOLTIP, 1, params, close_cond);
2793  return true;
2794  }
2795 
2796  return false;
2797  }
2798 
2799  virtual void OnResize()
2800  {
2801  this->vscroll->SetCapacityFromWidget(this, WID_IC_PANEL);
2802  }
2803 };
2804 
2807 
2812 static void ShowIndustryCargoesWindow(IndustryType id)
2813 {
2814  if (id >= NUM_INDUSTRYTYPES) {
2815  for (uint i = 0; i < NUM_INDUSTRYTYPES; i++) {
2817  if (indsp->enabled) {
2818  id = _sorted_industry_types[i];
2819  break;
2820  }
2821  }
2822  if (id >= NUM_INDUSTRYTYPES) return;
2823  }
2824 
2826  if (w != NULL) {
2827  w->InvalidateData(id);
2828  return;
2829  }
2830  new IndustryCargoesWindow(id);
2831 }
2832 
2835 {
2837 }
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
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:113
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.
void CcBuildIndustry(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2, uint32 cmd)
Command callback.
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:1482
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.
bool OnTooltip(Point pt, int widget, TooltipCloseCondition close_cond)
Event to display a custom tooltip.
Data structure for an opened window.
Definition: window_gui.h:278
Invalid cargo type.
Definition: cargo_type.h:70
bool _ctrl_pressed
Is Ctrl pressed?
Definition: gfx.cpp: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:3319
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:282
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
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:500
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:700
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:441
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:412
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:1130
cargo sub-type display
Panel that shows the chain.
CargoSuffixType
Cargo suffix type (for which window is it requested)
bool ScrollMainWindowToTile(TileIndex tile, bool instant)
Scrolls the viewport of the main window to a given location.
Definition: viewport.cpp: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:3088
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:1243
static const int HOR_TEXT_PADDING
Horizontal padding around the industry type text.
Shade box (at top-right of a window, between WWT_DEBUGBOX and WWT_DEFSIZEBOX)
Definition: widget_type.h:64
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:3242
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:621
(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