OpenTTD
industry_gui.cpp
Go to the documentation of this file.
1 /* $Id: industry_gui.cpp 27952 2017-12-27 21:54:52Z frosch $ */
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 
89 static void GetCargoSuffix(uint cargo, CargoSuffixType cst, const Industry *ind, IndustryType ind_type, const IndustrySpec *indspec, CargoSuffix &suffix)
90 {
91  suffix.text[0] = '\0';
92  suffix.display = CSD_CARGO_AMOUNT;
93 
94  if (HasBit(indspec->callback_mask, CBM_IND_CARGO_SUFFIX)) {
95  TileIndex t = (cst != CST_FUND) ? ind->location.tile : INVALID_TILE;
96  uint16 callback = GetIndustryCallback(CBID_INDUSTRY_CARGO_SUFFIX, 0, (cst << 8) | cargo, const_cast<Industry *>(ind), ind_type, t);
97  if (callback == CALLBACK_FAILED) return;
98 
99  if (indspec->grf_prop.grffile->grf_version < 8) {
100  if (GB(callback, 0, 8) == 0xFF) return;
101  if (callback < 0x400) {
103  GetString(suffix.text, GetGRFStringID(indspec->grf_prop.grffile->grfid, 0xD000 + callback), lastof(suffix.text));
106  return;
107  }
109  return;
110 
111  } else { // GRF version 8 or higher.
112  if (callback == 0x400) return;
113  if (callback == 0x401) {
114  suffix.display = CSD_CARGO;
115  return;
116  }
117  if (callback < 0x400) {
119  GetString(suffix.text, GetGRFStringID(indspec->grf_prop.grffile->grfid, 0xD000 + callback), lastof(suffix.text));
122  return;
123  }
124  if (callback >= 0x800 && callback < 0xC00) {
126  GetString(suffix.text, GetGRFStringID(indspec->grf_prop.grffile->grfid, 0xD000 - 0x800 + callback), lastof(suffix.text));
128  suffix.display = CSD_CARGO_TEXT;
129  return;
130  }
132  return;
133  }
134  }
135 }
136 
147 template <typename TC, typename TS>
148 static inline void GetAllCargoSuffixes(uint cb_offset, 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  for (uint j = 0; j < lengthof(cargoes); j++) {
152  if (cargoes[j] != CT_INVALID) {
153  GetCargoSuffix(cb_offset + j, cst, ind, ind_type, indspec, suffixes[j]);
154  } else {
155  suffixes[j].text[0] = '\0';
156  }
157  }
158 }
159 
161 
163 static int CDECL IndustryTypeNameSorter(const IndustryType *a, const IndustryType *b)
164 {
165  static char industry_name[2][64];
166 
167  const IndustrySpec *indsp1 = GetIndustrySpec(*a);
168  GetString(industry_name[0], indsp1->name, lastof(industry_name[0]));
169 
170  const IndustrySpec *indsp2 = GetIndustrySpec(*b);
171  GetString(industry_name[1], indsp2->name, lastof(industry_name[1]));
172 
173  int r = strnatcmp(industry_name[0], industry_name[1]); // Sort by name (natural sorting).
174 
175  /* If the names are equal, sort by industry type. */
176  return (r != 0) ? r : (*a - *b);
177 }
178 
183 {
184  /* Add each industry type to the list. */
185  for (IndustryType i = 0; i < NUM_INDUSTRYTYPES; i++) {
186  _sorted_industry_types[i] = i;
187  }
188 
189  /* Sort industry types by name. */
191 }
192 
200 void CcBuildIndustry(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2)
201 {
202  if (result.Succeeded()) return;
203 
204  uint8 indtype = GB(p1, 0, 8);
205  if (indtype < NUM_INDUSTRYTYPES) {
206  const IndustrySpec *indsp = GetIndustrySpec(indtype);
207  if (indsp->enabled) {
208  SetDParam(0, indsp->name);
209  ShowErrorMessage(STR_ERROR_CAN_T_BUILD_HERE, result.GetErrorMessage(), WL_INFO, TileX(tile) * TILE_SIZE, TileY(tile) * TILE_SIZE);
210  }
211  }
212 }
213 
214 static const NWidgetPart _nested_build_industry_widgets[] = {
216  NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
217  NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetDataTip(STR_FUND_INDUSTRY_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
218  NWidget(WWT_SHADEBOX, COLOUR_DARK_GREEN),
219  NWidget(WWT_DEFSIZEBOX, COLOUR_DARK_GREEN),
220  NWidget(WWT_STICKYBOX, COLOUR_DARK_GREEN),
221  EndContainer(),
223  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),
224  NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, WID_DPI_SCROLLBAR),
225  EndContainer(),
226  NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_DPI_INFOPANEL), SetResize(1, 0),
227  EndContainer(),
229  NWidget(WWT_TEXTBTN, COLOUR_DARK_GREEN, WID_DPI_DISPLAY_WIDGET), SetFill(1, 0), SetResize(1, 0),
230  SetDataTip(STR_INDUSTRY_DISPLAY_CHAIN, STR_INDUSTRY_DISPLAY_CHAIN_TOOLTIP),
231  NWidget(WWT_TEXTBTN, COLOUR_DARK_GREEN, WID_DPI_FUND_WIDGET), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_JUST_STRING, STR_NULL),
232  NWidget(WWT_RESIZEBOX, COLOUR_DARK_GREEN),
233  EndContainer(),
234 };
235 
238  WDP_AUTO, "build_industry", 170, 212,
241  _nested_build_industry_widgets, lengthof(_nested_build_industry_widgets)
242 );
243 
245 class BuildIndustryWindow : public Window {
247  IndustryType selected_type;
248  uint16 callback_timer;
250  uint16 count;
251  IndustryType index[NUM_INDUSTRYTYPES + 1];
252  bool enabled[NUM_INDUSTRYTYPES + 1];
253  Scrollbar *vscroll;
254 
256  static const int MATRIX_TEXT_OFFSET = 17;
257 
258  void SetupArrays()
259  {
260  this->count = 0;
261 
262  for (uint i = 0; i < lengthof(this->index); i++) {
263  this->index[i] = INVALID_INDUSTRYTYPE;
264  this->enabled[i] = false;
265  }
266 
267  if (_game_mode == GM_EDITOR) { // give room for the Many Random "button"
268  this->index[this->count] = INVALID_INDUSTRYTYPE;
269  this->enabled[this->count] = true;
270  this->count++;
271  this->timer_enabled = false;
272  }
273  /* Fill the arrays with industries.
274  * The tests performed after the enabled allow to load the industries
275  * In the same way they are inserted by grf (if any)
276  */
277  for (uint i = 0; i < NUM_INDUSTRYTYPES; i++) {
278  IndustryType ind = _sorted_industry_types[i];
279  const IndustrySpec *indsp = GetIndustrySpec(ind);
280  if (indsp->enabled) {
281  /* Rule is that editor mode loads all industries.
282  * In game mode, all non raw industries are loaded too
283  * and raw ones are loaded only when setting allows it */
284  if (_game_mode != GM_EDITOR && indsp->IsRawIndustry() && _settings_game.construction.raw_industry_construction == 0) {
285  /* Unselect if the industry is no longer in the list */
286  if (this->selected_type == ind) this->selected_index = -1;
287  continue;
288  }
289  this->index[this->count] = ind;
290  this->enabled[this->count] = (_game_mode == GM_EDITOR) || GetIndustryProbabilityCallback(ind, IACT_USERCREATION, 1) > 0;
291  /* Keep the selection to the correct line */
292  if (this->selected_type == ind) this->selected_index = this->count;
293  this->count++;
294  }
295  }
296 
297  /* first industry type is selected if the current selection is invalid.
298  * I'll be damned if there are none available ;) */
299  if (this->selected_index == -1) {
300  this->selected_index = 0;
301  this->selected_type = this->index[0];
302  }
303 
304  this->vscroll->SetCount(this->count);
305  }
306 
308  void SetButtons()
309  {
310  this->SetWidgetDisabledState(WID_DPI_FUND_WIDGET, this->selected_type != INVALID_INDUSTRYTYPE && !this->enabled[this->selected_index]);
311  this->SetWidgetDisabledState(WID_DPI_DISPLAY_WIDGET, this->selected_type == INVALID_INDUSTRYTYPE && this->enabled[this->selected_index]);
312  }
313 
314 public:
315  BuildIndustryWindow() : Window(&_build_industry_desc)
316  {
317  this->timer_enabled = _loaded_newgrf_features.has_newindustries;
318 
319  this->selected_index = -1;
320  this->selected_type = INVALID_INDUSTRYTYPE;
321 
322  this->callback_timer = DAY_TICKS;
323 
324  this->CreateNestedTree();
325  this->vscroll = this->GetScrollbar(WID_DPI_SCROLLBAR);
326  this->FinishInitNested(0);
327 
328  this->SetButtons();
329  }
330 
331  virtual void OnInit()
332  {
333  this->SetupArrays();
334  }
335 
336  virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
337  {
338  switch (widget) {
339  case WID_DPI_MATRIX_WIDGET: {
340  Dimension d = GetStringBoundingBox(STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES);
341  for (byte i = 0; i < this->count; i++) {
342  if (this->index[i] == INVALID_INDUSTRYTYPE) continue;
343  d = maxdim(d, GetStringBoundingBox(GetIndustrySpec(this->index[i])->name));
344  }
346  d.width += MATRIX_TEXT_OFFSET + padding.width;
347  d.height = 5 * resize->height;
348  *size = maxdim(*size, d);
349  break;
350  }
351 
352  case WID_DPI_INFOPANEL: {
353  /* Extra line for cost outside of editor + extra lines for 'extra' information for NewGRFs. */
354  int height = 2 + (_game_mode == GM_EDITOR ? 0 : 1) + (_loaded_newgrf_features.has_newindustries ? 4 : 0);
355  Dimension d = {0, 0};
356  for (byte i = 0; i < this->count; i++) {
357  if (this->index[i] == INVALID_INDUSTRYTYPE) continue;
358 
359  const IndustrySpec *indsp = GetIndustrySpec(this->index[i]);
360 
361  CargoSuffix cargo_suffix[3];
362  GetAllCargoSuffixes(0, CST_FUND, NULL, this->index[i], indsp, indsp->accepts_cargo, cargo_suffix);
363  StringID str = STR_INDUSTRY_VIEW_REQUIRES_CARGO;
364  byte p = 0;
365  SetDParam(0, STR_JUST_NOTHING);
366  SetDParamStr(1, "");
367  for (byte j = 0; j < lengthof(indsp->accepts_cargo); j++) {
368  if (indsp->accepts_cargo[j] == CT_INVALID) continue;
369  if (p > 0) str++;
370  SetDParam(p++, CargoSpec::Get(indsp->accepts_cargo[j])->name);
371  SetDParamStr(p++, cargo_suffix[j].text);
372  }
373  d = maxdim(d, GetStringBoundingBox(str));
374 
375  /* Draw the produced cargoes, if any. Otherwise, will print "Nothing". */
376  GetAllCargoSuffixes(3, CST_FUND, NULL, this->index[i], indsp, indsp->produced_cargo, cargo_suffix);
377  str = STR_INDUSTRY_VIEW_PRODUCES_CARGO;
378  p = 0;
379  SetDParam(0, STR_JUST_NOTHING);
380  SetDParamStr(1, "");
381  for (byte j = 0; j < lengthof(indsp->produced_cargo); j++) {
382  if (indsp->produced_cargo[j] == CT_INVALID) continue;
383  if (p > 0) str++;
384  SetDParam(p++, CargoSpec::Get(indsp->produced_cargo[j])->name);
385  SetDParamStr(p++, cargo_suffix[j].text);
386  }
387  d = maxdim(d, GetStringBoundingBox(str));
388  }
389 
390  /* Set it to something more sane :) */
391  size->height = height * FONT_HEIGHT_NORMAL + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
392  size->width = d.width + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
393  break;
394  }
395 
396  case WID_DPI_FUND_WIDGET: {
397  Dimension d = GetStringBoundingBox(STR_FUND_INDUSTRY_BUILD_NEW_INDUSTRY);
398  d = maxdim(d, GetStringBoundingBox(STR_FUND_INDUSTRY_PROSPECT_NEW_INDUSTRY));
399  d = maxdim(d, GetStringBoundingBox(STR_FUND_INDUSTRY_FUND_NEW_INDUSTRY));
400  d.width += padding.width;
401  d.height += padding.height;
402  *size = maxdim(*size, d);
403  break;
404  }
405  }
406  }
407 
408  virtual void SetStringParameters(int widget) const
409  {
410  switch (widget) {
411  case WID_DPI_FUND_WIDGET:
412  /* Raw industries might be prospected. Show this fact by changing the string
413  * In Editor, you just build, while ingame, or you fund or you prospect */
414  if (_game_mode == GM_EDITOR) {
415  /* We've chosen many random industries but no industries have been specified */
416  SetDParam(0, STR_FUND_INDUSTRY_BUILD_NEW_INDUSTRY);
417  } else {
418  const IndustrySpec *indsp = GetIndustrySpec(this->index[this->selected_index]);
419  SetDParam(0, (_settings_game.construction.raw_industry_construction == 2 && indsp->IsRawIndustry()) ? STR_FUND_INDUSTRY_PROSPECT_NEW_INDUSTRY : STR_FUND_INDUSTRY_FUND_NEW_INDUSTRY);
420  }
421  break;
422  }
423  }
424 
425  virtual void DrawWidget(const Rect &r, int widget) const
426  {
427  switch (widget) {
428  case WID_DPI_MATRIX_WIDGET: {
429  uint text_left, text_right, icon_left, icon_right;
430  if (_current_text_dir == TD_RTL) {
431  icon_right = r.right - WD_MATRIX_RIGHT;
432  icon_left = icon_right - 10;
433  text_right = icon_right - BuildIndustryWindow::MATRIX_TEXT_OFFSET;
434  text_left = r.left + WD_MATRIX_LEFT;
435  } else {
436  icon_left = r.left + WD_MATRIX_LEFT;
437  icon_right = icon_left + 10;
438  text_left = icon_left + BuildIndustryWindow::MATRIX_TEXT_OFFSET;
439  text_right = r.right - WD_MATRIX_RIGHT;
440  }
441 
442  for (byte i = 0; i < this->vscroll->GetCapacity() && i + this->vscroll->GetPosition() < this->count; i++) {
443  int y = r.top + WD_MATRIX_TOP + i * this->resize.step_height;
444  bool selected = this->selected_index == i + this->vscroll->GetPosition();
445 
446  if (this->index[i + this->vscroll->GetPosition()] == INVALID_INDUSTRYTYPE) {
447  DrawString(text_left, text_right, y, STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES, selected ? TC_WHITE : TC_ORANGE);
448  continue;
449  }
450  const IndustrySpec *indsp = GetIndustrySpec(this->index[i + this->vscroll->GetPosition()]);
451 
452  /* Draw the name of the industry in white is selected, otherwise, in orange */
453  DrawString(text_left, text_right, y, indsp->name, selected ? TC_WHITE : TC_ORANGE);
454  GfxFillRect(icon_left, y + 1, icon_right, y + 7, selected ? PC_WHITE : PC_BLACK);
455  GfxFillRect(icon_left + 1, y + 2, icon_right - 1, y + 6, indsp->map_colour);
456  }
457  break;
458  }
459 
460  case WID_DPI_INFOPANEL: {
461  int y = r.top + WD_FRAMERECT_TOP;
462  int bottom = r.bottom - WD_FRAMERECT_BOTTOM;
463  int left = r.left + WD_FRAMERECT_LEFT;
464  int right = r.right - WD_FRAMERECT_RIGHT;
465 
466  if (this->selected_type == INVALID_INDUSTRYTYPE) {
467  DrawStringMultiLine(left, right, y, bottom, STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_TOOLTIP);
468  break;
469  }
470 
471  const IndustrySpec *indsp = GetIndustrySpec(this->selected_type);
472 
473  if (_game_mode != GM_EDITOR) {
474  SetDParam(0, indsp->GetConstructionCost());
475  DrawString(left, right, y, STR_FUND_INDUSTRY_INDUSTRY_BUILD_COST);
476  y += FONT_HEIGHT_NORMAL;
477  }
478 
479  /* Draw the accepted cargoes, if any. Otherwise, will print "Nothing". */
480  CargoSuffix cargo_suffix[3];
481  GetAllCargoSuffixes(0, CST_FUND, NULL, this->selected_type, indsp, indsp->accepts_cargo, cargo_suffix);
482  StringID str = STR_INDUSTRY_VIEW_REQUIRES_CARGO;
483  byte p = 0;
484  SetDParam(0, STR_JUST_NOTHING);
485  SetDParamStr(1, "");
486  for (byte j = 0; j < lengthof(indsp->accepts_cargo); j++) {
487  if (indsp->accepts_cargo[j] == CT_INVALID) continue;
488  if (p > 0) str++;
489  SetDParam(p++, CargoSpec::Get(indsp->accepts_cargo[j])->name);
490  SetDParamStr(p++, cargo_suffix[j].text);
491  }
492  DrawString(left, right, y, str);
493  y += FONT_HEIGHT_NORMAL;
494 
495  /* Draw the produced cargoes, if any. Otherwise, will print "Nothing". */
496  GetAllCargoSuffixes(3, CST_FUND, NULL, this->selected_type, indsp, indsp->produced_cargo, cargo_suffix);
497  str = STR_INDUSTRY_VIEW_PRODUCES_CARGO;
498  p = 0;
499  SetDParam(0, STR_JUST_NOTHING);
500  SetDParamStr(1, "");
501  for (byte j = 0; j < lengthof(indsp->produced_cargo); j++) {
502  if (indsp->produced_cargo[j] == CT_INVALID) continue;
503  if (p > 0) str++;
504  SetDParam(p++, CargoSpec::Get(indsp->produced_cargo[j])->name);
505  SetDParamStr(p++, cargo_suffix[j].text);
506  }
507  DrawString(left, right, y, str);
508  y += FONT_HEIGHT_NORMAL;
509 
510  /* Get the additional purchase info text, if it has not already been queried. */
511  str = STR_NULL;
513  uint16 callback_res = GetIndustryCallback(CBID_INDUSTRY_FUND_MORE_TEXT, 0, 0, NULL, this->selected_type, INVALID_TILE);
514  if (callback_res != CALLBACK_FAILED && callback_res != 0x400) {
515  if (callback_res > 0x400) {
517  } else {
518  str = GetGRFStringID(indsp->grf_prop.grffile->grfid, 0xD000 + callback_res); // No. here's the new string
519  if (str != STR_UNDEFINED) {
521  DrawStringMultiLine(left, right, y, bottom, str, TC_YELLOW);
523  }
524  }
525  }
526  }
527  break;
528  }
529  }
530  }
531 
532  virtual void OnClick(Point pt, int widget, int click_count)
533  {
534  switch (widget) {
535  case WID_DPI_MATRIX_WIDGET: {
536  int y = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_DPI_MATRIX_WIDGET);
537  if (y < this->count) { // Is it within the boundaries of available data?
538  this->selected_index = y;
539  this->selected_type = this->index[y];
540  const IndustrySpec *indsp = (this->selected_type == INVALID_INDUSTRYTYPE) ? NULL : GetIndustrySpec(this->selected_type);
541 
542  this->SetDirty();
543 
544  if (_thd.GetCallbackWnd() == this &&
545  ((_game_mode != GM_EDITOR && _settings_game.construction.raw_industry_construction == 2 && indsp != NULL && indsp->IsRawIndustry()) ||
546  this->selected_type == INVALID_INDUSTRYTYPE ||
547  !this->enabled[this->selected_index])) {
548  /* Reset the button state if going to prospecting or "build many industries" */
549  this->RaiseButtons();
551  }
552 
553  this->SetButtons();
554  if (this->enabled[this->selected_index] && click_count > 1) this->OnClick(pt, WID_DPI_FUND_WIDGET, 1);
555  }
556  break;
557  }
558 
560  if (this->selected_type != INVALID_INDUSTRYTYPE) ShowIndustryCargoesWindow(this->selected_type);
561  break;
562 
563  case WID_DPI_FUND_WIDGET: {
564  if (this->selected_type == INVALID_INDUSTRYTYPE) {
565  this->HandleButtonClick(WID_DPI_FUND_WIDGET);
566 
567  if (Town::GetNumItems() == 0) {
568  ShowErrorMessage(STR_ERROR_CAN_T_GENERATE_INDUSTRIES, STR_ERROR_MUST_FOUND_TOWN_FIRST, WL_INFO);
569  } else {
570  extern void GenerateIndustries();
571  _generating_world = true;
573  _generating_world = false;
574  }
575  } else if (_game_mode != GM_EDITOR && _settings_game.construction.raw_industry_construction == 2 && GetIndustrySpec(this->selected_type)->IsRawIndustry()) {
576  DoCommandP(0, this->selected_type, InteractiveRandom(), CMD_BUILD_INDUSTRY | CMD_MSG(STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY));
577  this->HandleButtonClick(WID_DPI_FUND_WIDGET);
578  } else {
579  HandlePlacePushButton(this, WID_DPI_FUND_WIDGET, SPR_CURSOR_INDUSTRY, HT_RECT);
580  }
581  break;
582  }
583  }
584  }
585 
586  virtual void OnResize()
587  {
588  /* Adjust the number of items in the matrix depending of the resize */
589  this->vscroll->SetCapacityFromWidget(this, WID_DPI_MATRIX_WIDGET);
590  }
591 
592  virtual void OnPlaceObject(Point pt, TileIndex tile)
593  {
594  bool success = true;
595  /* We do not need to protect ourselves against "Random Many Industries" in this mode */
596  const IndustrySpec *indsp = GetIndustrySpec(this->selected_type);
597  uint32 seed = InteractiveRandom();
598 
599  if (_game_mode == GM_EDITOR) {
600  /* Show error if no town exists at all */
601  if (Town::GetNumItems() == 0) {
602  SetDParam(0, indsp->name);
603  ShowErrorMessage(STR_ERROR_CAN_T_BUILD_HERE, STR_ERROR_MUST_FOUND_TOWN_FIRST, WL_INFO, pt.x, pt.y);
604  return;
605  }
606 
607  Backup<CompanyByte> cur_company(_current_company, OWNER_NONE, FILE_LINE);
608  _generating_world = true;
609  _ignore_restrictions = true;
610 
611  DoCommandP(tile, (InteractiveRandomRange(indsp->num_table) << 8) | this->selected_type, seed,
612  CMD_BUILD_INDUSTRY | CMD_MSG(STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY), &CcBuildIndustry);
613 
614  cur_company.Restore();
615  _ignore_restrictions = false;
616  _generating_world = false;
617  } else {
618  success = DoCommandP(tile, (InteractiveRandomRange(indsp->num_table) << 8) | this->selected_type, seed, CMD_BUILD_INDUSTRY | CMD_MSG(STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY));
619  }
620 
621  /* If an industry has been built, just reset the cursor and the system */
623  }
624 
625  virtual void OnTick()
626  {
627  if (_pause_mode != PM_UNPAUSED) return;
628  if (!this->timer_enabled) return;
629  if (--this->callback_timer == 0) {
630  /* We have just passed another day.
631  * See if we need to update availability of currently selected industry */
632  this->callback_timer = DAY_TICKS; // restart counter
633 
634  const IndustrySpec *indsp = GetIndustrySpec(this->selected_type);
635 
636  if (indsp->enabled) {
637  bool call_back_result = GetIndustryProbabilityCallback(this->selected_type, IACT_USERCREATION, 1) > 0;
638 
639  /* Only if result does match the previous state would it require a redraw. */
640  if (call_back_result != this->enabled[this->selected_index]) {
641  this->enabled[this->selected_index] = call_back_result;
642  this->SetButtons();
643  this->SetDirty();
644  }
645  }
646  }
647  }
648 
649  virtual void OnTimeout()
650  {
651  this->RaiseButtons();
652  }
653 
654  virtual void OnPlaceObjectAbort()
655  {
656  this->RaiseButtons();
657  }
658 
664  virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
665  {
666  if (!gui_scope) return;
667  this->SetupArrays();
668 
669  const IndustrySpec *indsp = (this->selected_type == INVALID_INDUSTRYTYPE) ? NULL : GetIndustrySpec(this->selected_type);
670  if (indsp == NULL) this->enabled[this->selected_index] = _settings_game.difficulty.industry_density != ID_FUND_ONLY;
671  this->SetButtons();
672  }
673 };
674 
675 void ShowBuildIndustryWindow()
676 {
677  if (_game_mode != GM_EDITOR && !Company::IsValidID(_local_company)) return;
679  new BuildIndustryWindow();
680 }
681 
682 static void UpdateIndustryProduction(Industry *i);
683 
684 static inline bool IsProductionAlterable(const Industry *i)
685 {
686  const IndustrySpec *is = GetIndustrySpec(i->type);
687  return ((_game_mode == GM_EDITOR || _cheats.setup_prod.value) &&
688  (is->production_rate[0] != 0 || is->production_rate[1] != 0 || is->IsRawIndustry()) &&
689  !_networking);
690 }
691 
693 {
695  enum Editability {
699  };
700 
702  enum InfoLine {
707  };
708 
715 
716 public:
717  IndustryViewWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc)
718  {
719  this->flags |= WF_DISABLE_VP_SCROLL;
720  this->editbox_line = IL_NONE;
721  this->clicked_line = IL_NONE;
722  this->clicked_button = 0;
723  this->info_height = WD_FRAMERECT_TOP + 2 * FONT_HEIGHT_NORMAL + WD_FRAMERECT_BOTTOM + 1; // Info panel has at least two lines text.
724 
725  this->InitNested(window_number);
726  NWidgetViewport *nvp = this->GetWidget<NWidgetViewport>(WID_IV_VIEWPORT);
727  nvp->InitializeViewport(this, Industry::Get(window_number)->location.GetCenterTile(), ZOOM_LVL_INDUSTRY);
728 
729  this->InvalidateData();
730  }
731 
732  virtual void OnPaint()
733  {
734  this->DrawWidgets();
735 
736  if (this->IsShaded()) return; // Don't draw anything when the window is shaded.
737 
738  NWidgetBase *nwi = this->GetWidget<NWidgetBase>(WID_IV_INFO);
739  uint expected = this->DrawInfo(nwi->pos_x, nwi->pos_x + nwi->current_x - 1, nwi->pos_y) - nwi->pos_y;
740  if (expected > nwi->current_y - 1) {
741  this->info_height = expected + 1;
742  this->ReInit();
743  return;
744  }
745  }
746 
754  int DrawInfo(uint left, uint right, uint top)
755  {
756  Industry *i = Industry::Get(this->window_number);
757  const IndustrySpec *ind = GetIndustrySpec(i->type);
758  int y = top + WD_FRAMERECT_TOP;
759  bool first = true;
760  bool has_accept = false;
761 
762  if (i->prod_level == PRODLEVEL_CLOSURE) {
763  DrawString(left + WD_FRAMERECT_LEFT, right - WD_FRAMERECT_RIGHT, y, STR_INDUSTRY_VIEW_INDUSTRY_ANNOUNCED_CLOSURE);
764  y += 2 * FONT_HEIGHT_NORMAL;
765  }
766 
767  CargoSuffix cargo_suffix[3];
768  GetAllCargoSuffixes(0, CST_VIEW, i, i->type, ind, i->accepts_cargo, cargo_suffix);
770 
771  uint left_side = left + WD_FRAMERECT_LEFT * 4; // Indent accepted cargoes.
772  for (byte j = 0; j < lengthof(i->accepts_cargo); j++) {
773  if (i->accepts_cargo[j] == CT_INVALID) continue;
774  has_accept = true;
775  if (first) {
776  DrawString(left + WD_FRAMERECT_LEFT, right - WD_FRAMERECT_RIGHT, y, STR_INDUSTRY_VIEW_REQUIRES);
777  y += FONT_HEIGHT_NORMAL;
778  first = false;
779  }
781  SetDParam(1, i->accepts_cargo[j]);
783  SetDParamStr(3, "");
784  StringID str = STR_NULL;
785  switch (cargo_suffix[j].display) {
787  SetDParamStr(3, cargo_suffix[j].text);
788  FALLTHROUGH;
789  case CSD_CARGO_AMOUNT:
790  str = stockpiling ? STR_INDUSTRY_VIEW_ACCEPT_CARGO_AMOUNT : STR_INDUSTRY_VIEW_ACCEPT_CARGO;
791  break;
792 
793  case CSD_CARGO_TEXT:
794  SetDParamStr(3, cargo_suffix[j].text);
795  FALLTHROUGH;
796  case CSD_CARGO:
797  str = STR_INDUSTRY_VIEW_ACCEPT_CARGO;
798  break;
799 
800  default:
801  NOT_REACHED();
802  }
803  DrawString(left_side, right - WD_FRAMERECT_RIGHT, y, str);
804  y += FONT_HEIGHT_NORMAL;
805  }
806 
807  GetAllCargoSuffixes(3, CST_VIEW, i, i->type, ind, i->produced_cargo, cargo_suffix);
808  first = true;
809  for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
810  if (i->produced_cargo[j] == CT_INVALID) continue;
811  if (first) {
812  if (has_accept) y += WD_PAR_VSEP_WIDE;
813  DrawString(left + WD_FRAMERECT_LEFT, right - WD_FRAMERECT_RIGHT, y, STR_INDUSTRY_VIEW_PRODUCTION_LAST_MONTH_TITLE);
814  y += FONT_HEIGHT_NORMAL;
815  if (this->editable == EA_RATE) this->production_offset_y = y;
816  first = false;
817  }
818 
819  SetDParam(0, i->produced_cargo[j]);
821  SetDParamStr(2, cargo_suffix[j].text);
823  uint x = left + WD_FRAMETEXT_LEFT + (this->editable == EA_RATE ? SETTING_BUTTON_WIDTH + 10 : 0);
824  DrawString(x, right - WD_FRAMERECT_RIGHT, y, STR_INDUSTRY_VIEW_TRANSPORTED);
825  /* Let's put out those buttons.. */
826  if (this->editable == EA_RATE) {
827  DrawArrowButtons(left + WD_FRAMETEXT_LEFT, y, COLOUR_YELLOW, (this->clicked_line == IL_RATE1 + j) ? this->clicked_button : 0,
828  i->production_rate[j] > 0, i->production_rate[j] < 255);
829  }
830  y += FONT_HEIGHT_NORMAL;
831  }
832 
833  /* Display production multiplier if editable */
834  if (this->editable == EA_MULTIPLIER) {
835  y += WD_PAR_VSEP_WIDE;
836  this->production_offset_y = y;
838  uint x = left + WD_FRAMETEXT_LEFT + SETTING_BUTTON_WIDTH + 10;
839  DrawString(x, right - WD_FRAMERECT_RIGHT, y, STR_INDUSTRY_VIEW_PRODUCTION_LEVEL);
840  DrawArrowButtons(left + WD_FRAMETEXT_LEFT, y, COLOUR_YELLOW, (this->clicked_line == IL_MULTIPLIER) ? this->clicked_button : 0,
842  y += FONT_HEIGHT_NORMAL;
843  }
844 
845  /* Get the extra message for the GUI */
847  uint16 callback_res = GetIndustryCallback(CBID_INDUSTRY_WINDOW_MORE_TEXT, 0, 0, i, i->type, i->location.tile);
848  if (callback_res != CALLBACK_FAILED && callback_res != 0x400) {
849  if (callback_res > 0x400) {
851  } else {
852  StringID message = GetGRFStringID(ind->grf_prop.grffile->grfid, 0xD000 + callback_res);
853  if (message != STR_NULL && message != STR_UNDEFINED) {
854  y += WD_PAR_VSEP_WIDE;
855 
857  /* Use all the available space left from where we stand up to the
858  * end of the window. We ALSO enlarge the window if needed, so we
859  * can 'go' wild with the bottom of the window. */
860  y = DrawStringMultiLine(left + WD_FRAMERECT_LEFT, right - WD_FRAMERECT_RIGHT, y, UINT16_MAX, message, TC_BLACK);
862  }
863  }
864  }
865  }
866  return y + WD_FRAMERECT_BOTTOM;
867  }
868 
869  virtual void SetStringParameters(int widget) const
870  {
871  if (widget == WID_IV_CAPTION) SetDParam(0, this->window_number);
872  }
873 
874  virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
875  {
876  if (widget == WID_IV_INFO) size->height = this->info_height;
877  }
878 
879  virtual void OnClick(Point pt, int widget, int click_count)
880  {
881  switch (widget) {
882  case WID_IV_INFO: {
883  Industry *i = Industry::Get(this->window_number);
884  InfoLine line = IL_NONE;
885 
886  switch (this->editable) {
887  case EA_NONE: break;
888 
889  case EA_MULTIPLIER:
890  if (IsInsideBS(pt.y, this->production_offset_y, FONT_HEIGHT_NORMAL)) line = IL_MULTIPLIER;
891  break;
892 
893  case EA_RATE:
894  if (pt.y >= this->production_offset_y) {
895  int row = (pt.y - this->production_offset_y) / FONT_HEIGHT_NORMAL;
896  for (uint j = 0; j < lengthof(i->produced_cargo); j++) {
897  if (i->produced_cargo[j] == CT_INVALID) continue;
898  row--;
899  if (row < 0) {
900  line = (InfoLine)(IL_RATE1 + j);
901  break;
902  }
903  }
904  }
905  break;
906  }
907  if (line == IL_NONE) return;
908 
909  NWidgetBase *nwi = this->GetWidget<NWidgetBase>(widget);
910  int left = nwi->pos_x + WD_FRAMETEXT_LEFT;
911  int right = nwi->pos_x + nwi->current_x - 1 - WD_FRAMERECT_RIGHT;
912  if (IsInsideMM(pt.x, left, left + SETTING_BUTTON_WIDTH)) {
913  /* Clicked buttons, decrease or increase production */
914  byte button = (pt.x < left + SETTING_BUTTON_WIDTH / 2) ? 1 : 2;
915  switch (this->editable) {
916  case EA_MULTIPLIER:
917  if (button == 1) {
918  if (i->prod_level <= PRODLEVEL_MINIMUM) return;
919  i->prod_level = max<uint>(i->prod_level / 2, PRODLEVEL_MINIMUM);
920  } else {
921  if (i->prod_level >= PRODLEVEL_MAXIMUM) return;
923  }
924  break;
925 
926  case EA_RATE:
927  if (button == 1) {
928  if (i->production_rate[line - IL_RATE1] <= 0) return;
929  i->production_rate[line - IL_RATE1] = max(i->production_rate[line - IL_RATE1] / 2, 0);
930  } else {
931  if (i->production_rate[line - IL_RATE1] >= 255) return;
932  /* a zero production industry is unlikely to give anything but zero, so push it a little bit */
933  int new_prod = i->production_rate[line - IL_RATE1] == 0 ? 1 : i->production_rate[line - IL_RATE1] * 2;
934  i->production_rate[line - IL_RATE1] = minu(new_prod, 255);
935  }
936  break;
937 
938  default: NOT_REACHED();
939  }
940 
941  UpdateIndustryProduction(i);
942  this->SetDirty();
943  this->SetTimeout();
944  this->clicked_line = line;
945  this->clicked_button = button;
946  } else if (IsInsideMM(pt.x, left + SETTING_BUTTON_WIDTH + 10, right)) {
947  /* clicked the text */
948  this->editbox_line = line;
949  switch (this->editable) {
950  case EA_MULTIPLIER:
952  ShowQueryString(STR_JUST_INT, STR_CONFIG_GAME_PRODUCTION_LEVEL, 10, this, CS_ALPHANUMERAL, QSF_NONE);
953  break;
954 
955  case EA_RATE:
956  SetDParam(0, i->production_rate[line - IL_RATE1] * 8);
957  ShowQueryString(STR_JUST_INT, STR_CONFIG_GAME_PRODUCTION, 10, this, CS_ALPHANUMERAL, QSF_NONE);
958  break;
959 
960  default: NOT_REACHED();
961  }
962  }
963  break;
964  }
965 
966  case WID_IV_GOTO: {
967  Industry *i = Industry::Get(this->window_number);
968  if (_ctrl_pressed) {
970  } else {
972  }
973  break;
974  }
975 
976  case WID_IV_DISPLAY: {
977  Industry *i = Industry::Get(this->window_number);
979  break;
980  }
981  }
982  }
983 
984  virtual void OnTimeout()
985  {
986  this->clicked_line = IL_NONE;
987  this->clicked_button = 0;
988  this->SetDirty();
989  }
990 
991  virtual void OnResize()
992  {
993  if (this->viewport != NULL) {
994  NWidgetViewport *nvp = this->GetWidget<NWidgetViewport>(WID_IV_VIEWPORT);
995  nvp->UpdateViewportCoordinates(this);
996 
997  ScrollWindowToTile(Industry::Get(this->window_number)->location.GetCenterTile(), this, true); // Re-center viewport.
998  }
999  }
1000 
1001  virtual void OnQueryTextFinished(char *str)
1002  {
1003  if (StrEmpty(str)) return;
1004 
1005  Industry *i = Industry::Get(this->window_number);
1006  uint value = atoi(str);
1007  switch (this->editbox_line) {
1008  case IL_NONE: NOT_REACHED();
1009 
1010  case IL_MULTIPLIER:
1012  break;
1013 
1014  default:
1015  i->production_rate[this->editbox_line - IL_RATE1] = ClampU(RoundDivSU(value, 8), 0, 255);
1016  break;
1017  }
1018  UpdateIndustryProduction(i);
1019  this->SetDirty();
1020  }
1021 
1027  virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
1028  {
1029  if (!gui_scope) return;
1030  const Industry *i = Industry::Get(this->window_number);
1031  if (IsProductionAlterable(i)) {
1032  const IndustrySpec *ind = GetIndustrySpec(i->type);
1033  this->editable = ind->UsesSmoothEconomy() ? EA_RATE : EA_MULTIPLIER;
1034  } else {
1035  this->editable = EA_NONE;
1036  }
1037  }
1038 
1039  virtual bool IsNewGRFInspectable() const
1040  {
1041  return ::IsNewGRFInspectable(GSF_INDUSTRIES, this->window_number);
1042  }
1043 
1044  virtual void ShowNewGRFInspectWindow() const
1045  {
1046  ::ShowNewGRFInspectWindow(GSF_INDUSTRIES, this->window_number);
1047  }
1048 };
1049 
1050 static void UpdateIndustryProduction(Industry *i)
1051 {
1052  const IndustrySpec *indspec = GetIndustrySpec(i->type);
1053  if (!indspec->UsesSmoothEconomy()) i->RecomputeProductionMultipliers();
1054 
1055  for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
1056  if (i->produced_cargo[j] != CT_INVALID) {
1057  i->last_month_production[j] = 8 * i->production_rate[j];
1058  }
1059  }
1060 }
1061 
1065  NWidget(WWT_CLOSEBOX, COLOUR_CREAM),
1066  NWidget(WWT_CAPTION, COLOUR_CREAM, WID_IV_CAPTION), SetDataTip(STR_INDUSTRY_VIEW_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1067  NWidget(WWT_DEBUGBOX, COLOUR_CREAM),
1068  NWidget(WWT_SHADEBOX, COLOUR_CREAM),
1069  NWidget(WWT_DEFSIZEBOX, COLOUR_CREAM),
1070  NWidget(WWT_STICKYBOX, COLOUR_CREAM),
1071  EndContainer(),
1072  NWidget(WWT_PANEL, COLOUR_CREAM),
1073  NWidget(WWT_INSET, COLOUR_CREAM), SetPadding(2, 2, 2, 2),
1074  NWidget(NWID_VIEWPORT, INVALID_COLOUR, WID_IV_VIEWPORT), SetMinimalSize(254, 86), SetFill(1, 0), SetPadding(1, 1, 1, 1), SetResize(1, 1),
1075  EndContainer(),
1076  EndContainer(),
1077  NWidget(WWT_PANEL, COLOUR_CREAM, WID_IV_INFO), SetMinimalSize(260, 2), SetResize(1, 0),
1078  EndContainer(),
1080  NWidget(WWT_PUSHTXTBTN, COLOUR_CREAM, WID_IV_GOTO), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_BUTTON_LOCATION, STR_INDUSTRY_VIEW_LOCATION_TOOLTIP),
1081  NWidget(WWT_PUSHTXTBTN, COLOUR_CREAM, WID_IV_DISPLAY), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_INDUSTRY_DISPLAY_CHAIN, STR_INDUSTRY_DISPLAY_CHAIN_TOOLTIP),
1082  NWidget(WWT_RESIZEBOX, COLOUR_CREAM),
1083  EndContainer(),
1084 };
1085 
1088  WDP_AUTO, "view_industry", 260, 120,
1090  0,
1091  _nested_industry_view_widgets, lengthof(_nested_industry_view_widgets)
1092 );
1093 
1094 void ShowIndustryViewWindow(int industry)
1095 {
1096  AllocateWindowDescFront<IndustryViewWindow>(&_industry_view_desc, industry);
1097 }
1098 
1102  NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
1103  NWidget(WWT_CAPTION, COLOUR_BROWN), SetDataTip(STR_INDUSTRY_DIRECTORY_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1104  NWidget(WWT_SHADEBOX, COLOUR_BROWN),
1105  NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
1106  NWidget(WWT_STICKYBOX, COLOUR_BROWN),
1107  EndContainer(),
1111  NWidget(WWT_TEXTBTN, COLOUR_BROWN, WID_ID_DROPDOWN_ORDER), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER),
1112  NWidget(WWT_DROPDOWN, COLOUR_BROWN, WID_ID_DROPDOWN_CRITERIA), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_SORT_CRITERIA),
1113  NWidget(WWT_PANEL, COLOUR_BROWN), SetResize(1, 0), EndContainer(),
1114  EndContainer(),
1115  NWidget(WWT_PANEL, COLOUR_BROWN, WID_ID_INDUSTRY_LIST), SetDataTip(0x0, STR_INDUSTRY_DIRECTORY_LIST_CAPTION), SetResize(1, 1), SetScrollbar(WID_ID_SCROLLBAR), EndContainer(),
1116  EndContainer(),
1118  NWidget(NWID_VSCROLLBAR, COLOUR_BROWN, WID_ID_SCROLLBAR),
1119  NWidget(WWT_RESIZEBOX, COLOUR_BROWN),
1120  EndContainer(),
1121  EndContainer(),
1122 };
1123 
1125 
1126 
1131 protected:
1132  /* Runtime saved values */
1133  static Listing last_sorting;
1134  static const Industry *last_industry;
1135 
1136  /* Constants for sorting stations */
1137  static const StringID sorter_names[];
1138  static GUIIndustryList::SortFunction * const sorter_funcs[];
1139 
1140  GUIIndustryList industries;
1141  Scrollbar *vscroll;
1142 
1145  {
1146  if (this->industries.NeedRebuild()) {
1147  this->industries.Clear();
1148 
1149  const Industry *i;
1150  FOR_ALL_INDUSTRIES(i) {
1151  *this->industries.Append() = i;
1152  }
1153 
1154  this->industries.Compact();
1155  this->industries.RebuildDone();
1156  this->vscroll->SetCount(this->industries.Length()); // Update scrollbar as well.
1157  }
1158 
1159  if (!this->industries.Sort()) return;
1160  IndustryDirectoryWindow::last_industry = NULL; // Reset name sorter sort cache
1161  this->SetWidgetDirty(WID_ID_INDUSTRY_LIST); // Set the modified widget dirty
1162  }
1163 
1171  static inline int GetCargoTransportedPercentsIfValid(const Industry *i, uint id)
1172  {
1173  assert(id < lengthof(i->produced_cargo));
1174 
1175  if (i->produced_cargo[id] == CT_INVALID) return 101;
1176  return ToPercent8(i->last_month_pct_transported[id]);
1177  }
1178 
1187  {
1188  int p1 = GetCargoTransportedPercentsIfValid(i, 0);
1189  int p2 = GetCargoTransportedPercentsIfValid(i, 1);
1190 
1191  if (p1 > p2) Swap(p1, p2); // lower value has higher priority
1192 
1193  return (p1 << 8) + p2;
1194  }
1195 
1197  static int CDECL IndustryNameSorter(const Industry * const *a, const Industry * const *b)
1198  {
1199  static char buf_cache[96];
1200  static char buf[96];
1201 
1202  SetDParam(0, (*a)->index);
1203  GetString(buf, STR_INDUSTRY_NAME, lastof(buf));
1204 
1205  if (*b != last_industry) {
1206  last_industry = *b;
1207  SetDParam(0, (*b)->index);
1208  GetString(buf_cache, STR_INDUSTRY_NAME, lastof(buf_cache));
1209  }
1210 
1211  return strnatcmp(buf, buf_cache); // Sort by name (natural sorting).
1212  }
1213 
1215  static int CDECL IndustryTypeSorter(const Industry * const *a, const Industry * const *b)
1216  {
1217  int it_a = 0;
1218  while (it_a != NUM_INDUSTRYTYPES && (*a)->type != _sorted_industry_types[it_a]) it_a++;
1219  int it_b = 0;
1220  while (it_b != NUM_INDUSTRYTYPES && (*b)->type != _sorted_industry_types[it_b]) it_b++;
1221  int r = it_a - it_b;
1222  return (r == 0) ? IndustryNameSorter(a, b) : r;
1223  }
1224 
1226  static int CDECL IndustryProductionSorter(const Industry * const *a, const Industry * const *b)
1227  {
1228  uint prod_a = 0, prod_b = 0;
1229  for (uint i = 0; i < lengthof((*a)->produced_cargo); i++) {
1230  if ((*a)->produced_cargo[i] != CT_INVALID) prod_a += (*a)->last_month_production[i];
1231  if ((*b)->produced_cargo[i] != CT_INVALID) prod_b += (*b)->last_month_production[i];
1232  }
1233  int r = prod_a - prod_b;
1234 
1235  return (r == 0) ? IndustryTypeSorter(a, b) : r;
1236  }
1237 
1239  static int CDECL IndustryTransportedCargoSorter(const Industry * const *a, const Industry * const *b)
1240  {
1241  int r = GetCargoTransportedSortValue(*a) - GetCargoTransportedSortValue(*b);
1242  return (r == 0) ? IndustryNameSorter(a, b) : r;
1243  }
1244 
1251  {
1252  const IndustrySpec *indsp = GetIndustrySpec(i->type);
1253  byte p = 0;
1254 
1255  /* Industry name */
1256  SetDParam(p++, i->index);
1257 
1258  static CargoSuffix cargo_suffix[lengthof(i->produced_cargo)];
1259  GetAllCargoSuffixes(3, CST_DIR, i, i->type, indsp, i->produced_cargo, cargo_suffix);
1260 
1261  /* Industry productions */
1262  for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
1263  if (i->produced_cargo[j] == CT_INVALID) continue;
1264  SetDParam(p++, i->produced_cargo[j]);
1265  SetDParam(p++, i->last_month_production[j]);
1266  SetDParamStr(p++, cargo_suffix[j].text);
1267  }
1268 
1269  /* Transported productions */
1270  for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
1271  if (i->produced_cargo[j] == CT_INVALID) continue;
1273  }
1274 
1275  /* Drawing the right string */
1276  switch (p) {
1277  case 1: return STR_INDUSTRY_DIRECTORY_ITEM_NOPROD;
1278  case 5: return STR_INDUSTRY_DIRECTORY_ITEM;
1279  default: return STR_INDUSTRY_DIRECTORY_ITEM_TWO;
1280  }
1281  }
1282 
1283 public:
1284  IndustryDirectoryWindow(WindowDesc *desc, WindowNumber number) : Window(desc)
1285  {
1286  this->CreateNestedTree();
1287  this->vscroll = this->GetScrollbar(WID_ID_SCROLLBAR);
1288 
1289  this->industries.SetListing(this->last_sorting);
1290  this->industries.SetSortFuncs(IndustryDirectoryWindow::sorter_funcs);
1291  this->industries.ForceRebuild();
1292  this->BuildSortIndustriesList();
1293 
1294  this->FinishInitNested(0);
1295  }
1296 
1298  {
1299  this->last_sorting = this->industries.GetListing();
1300  }
1301 
1302  virtual void SetStringParameters(int widget) const
1303  {
1304  if (widget == WID_ID_DROPDOWN_CRITERIA) SetDParam(0, IndustryDirectoryWindow::sorter_names[this->industries.SortType()]);
1305  }
1306 
1307  virtual void DrawWidget(const Rect &r, int widget) const
1308  {
1309  switch (widget) {
1310  case WID_ID_DROPDOWN_ORDER:
1311  this->DrawSortButtonState(widget, this->industries.IsDescSortOrder() ? SBS_DOWN : SBS_UP);
1312  break;
1313 
1314  case WID_ID_INDUSTRY_LIST: {
1315  int n = 0;
1316  int y = r.top + WD_FRAMERECT_TOP;
1317  if (this->industries.Length() == 0) {
1318  DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_INDUSTRY_DIRECTORY_NONE);
1319  break;
1320  }
1321  for (uint i = this->vscroll->GetPosition(); i < this->industries.Length(); i++) {
1322  DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, this->GetIndustryString(this->industries[i]));
1323 
1324  y += this->resize.step_height;
1325  if (++n == this->vscroll->GetCapacity()) break; // max number of industries in 1 window
1326  }
1327  break;
1328  }
1329  }
1330  }
1331 
1332  virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
1333  {
1334  switch (widget) {
1335  case WID_ID_DROPDOWN_ORDER: {
1336  Dimension d = GetStringBoundingBox(this->GetWidget<NWidgetCore>(widget)->widget_data);
1337  d.width += padding.width + Window::SortButtonWidth() * 2; // Doubled since the string is centred and it also looks better.
1338  d.height += padding.height;
1339  *size = maxdim(*size, d);
1340  break;
1341  }
1342 
1343  case WID_ID_DROPDOWN_CRITERIA: {
1344  Dimension d = {0, 0};
1345  for (uint i = 0; IndustryDirectoryWindow::sorter_names[i] != INVALID_STRING_ID; i++) {
1346  d = maxdim(d, GetStringBoundingBox(IndustryDirectoryWindow::sorter_names[i]));
1347  }
1348  d.width += padding.width;
1349  d.height += padding.height;
1350  *size = maxdim(*size, d);
1351  break;
1352  }
1353 
1354  case WID_ID_INDUSTRY_LIST: {
1355  Dimension d = GetStringBoundingBox(STR_INDUSTRY_DIRECTORY_NONE);
1356  for (uint i = 0; i < this->industries.Length(); i++) {
1357  d = maxdim(d, GetStringBoundingBox(this->GetIndustryString(this->industries[i])));
1358  }
1359  resize->height = d.height;
1360  d.height *= 5;
1361  d.width += padding.width + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
1362  d.height += padding.height + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
1363  *size = maxdim(*size, d);
1364  break;
1365  }
1366  }
1367  }
1368 
1369 
1370  virtual void OnClick(Point pt, int widget, int click_count)
1371  {
1372  switch (widget) {
1373  case WID_ID_DROPDOWN_ORDER:
1374  this->industries.ToggleSortOrder();
1375  this->SetDirty();
1376  break;
1377 
1379  ShowDropDownMenu(this, IndustryDirectoryWindow::sorter_names, this->industries.SortType(), WID_ID_DROPDOWN_CRITERIA, 0, 0);
1380  break;
1381 
1382  case WID_ID_INDUSTRY_LIST: {
1383  uint p = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_ID_INDUSTRY_LIST, WD_FRAMERECT_TOP);
1384  if (p < this->industries.Length()) {
1385  if (_ctrl_pressed) {
1386  ShowExtraViewPortWindow(this->industries[p]->location.tile);
1387  } else {
1388  ScrollMainWindowToTile(this->industries[p]->location.tile);
1389  }
1390  }
1391  break;
1392  }
1393  }
1394  }
1395 
1396  virtual void OnDropdownSelect(int widget, int index)
1397  {
1398  if (this->industries.SortType() != index) {
1399  this->industries.SetSortType(index);
1400  this->BuildSortIndustriesList();
1401  }
1402  }
1403 
1404  virtual void OnResize()
1405  {
1406  this->vscroll->SetCapacityFromWidget(this, WID_ID_INDUSTRY_LIST);
1407  }
1408 
1409  virtual void OnPaint()
1410  {
1411  if (this->industries.NeedRebuild()) this->BuildSortIndustriesList();
1412  this->DrawWidgets();
1413  }
1414 
1415  virtual void OnHundredthTick()
1416  {
1417  this->industries.ForceResort();
1418  this->BuildSortIndustriesList();
1419  }
1420 
1426  virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
1427  {
1428  if (data == 0) {
1429  /* This needs to be done in command-scope to enforce rebuilding before resorting invalid data */
1430  this->industries.ForceRebuild();
1431  } else {
1432  this->industries.ForceResort();
1433  }
1434  }
1435 };
1436 
1437 Listing IndustryDirectoryWindow::last_sorting = {false, 0};
1438 const Industry *IndustryDirectoryWindow::last_industry = NULL;
1439 
1440 /* Available station sorting functions. */
1441 GUIIndustryList::SortFunction * const IndustryDirectoryWindow::sorter_funcs[] = {
1442  &IndustryNameSorter,
1443  &IndustryTypeSorter,
1444  &IndustryProductionSorter,
1445  &IndustryTransportedCargoSorter
1446 };
1447 
1448 /* Names of the sorting functions */
1449 const StringID IndustryDirectoryWindow::sorter_names[] = {
1450  STR_SORT_BY_NAME,
1451  STR_SORT_BY_TYPE,
1452  STR_SORT_BY_PRODUCTION,
1453  STR_SORT_BY_TRANSPORTED,
1455 };
1456 
1457 
1460  WDP_AUTO, "list_industries", 428, 190,
1462  0,
1463  _nested_industry_directory_widgets, lengthof(_nested_industry_directory_widgets)
1464 );
1465 
1466 void ShowIndustryDirectory()
1467 {
1468  AllocateWindowDescFront<IndustryDirectoryWindow>(&_industry_directory_desc, 0);
1469 }
1470 
1474  NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
1475  NWidget(WWT_CAPTION, COLOUR_BROWN, WID_IC_CAPTION), SetDataTip(STR_INDUSTRY_CARGOES_INDUSTRY_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1476  NWidget(WWT_SHADEBOX, COLOUR_BROWN),
1477  NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
1478  NWidget(WWT_STICKYBOX, COLOUR_BROWN),
1479  EndContainer(),
1484  NWidget(WWT_TEXTBTN, COLOUR_BROWN, WID_IC_NOTIFY),
1485  SetDataTip(STR_INDUSTRY_CARGOES_NOTIFY_SMALLMAP, STR_INDUSTRY_CARGOES_NOTIFY_SMALLMAP_TOOLTIP),
1486  NWidget(WWT_PANEL, COLOUR_BROWN), SetFill(1, 0), SetResize(0, 0), EndContainer(),
1487  NWidget(WWT_DROPDOWN, COLOUR_BROWN, WID_IC_IND_DROPDOWN), SetFill(0, 0), SetResize(0, 0),
1488  SetDataTip(STR_INDUSTRY_CARGOES_SELECT_INDUSTRY, STR_INDUSTRY_CARGOES_SELECT_INDUSTRY_TOOLTIP),
1489  NWidget(WWT_DROPDOWN, COLOUR_BROWN, WID_IC_CARGO_DROPDOWN), SetFill(0, 0), SetResize(0, 0),
1490  SetDataTip(STR_INDUSTRY_CARGOES_SELECT_CARGO, STR_INDUSTRY_CARGOES_SELECT_CARGO_TOOLTIP),
1491  EndContainer(),
1492  EndContainer(),
1494  NWidget(NWID_VSCROLLBAR, COLOUR_BROWN, WID_IC_SCROLLBAR),
1495  NWidget(WWT_RESIZEBOX, COLOUR_BROWN),
1496  EndContainer(),
1497  EndContainer(),
1498 };
1499 
1502  WDP_AUTO, "industry_cargoes", 300, 210,
1504  0,
1505  _nested_industry_cargoes_widgets, lengthof(_nested_industry_cargoes_widgets)
1506 );
1507 
1516 };
1517 
1518 static const uint MAX_CARGOES = 3;
1519 
1522  static const int VERT_INTER_INDUSTRY_SPACE;
1523  static const int HOR_CARGO_BORDER_SPACE;
1524  static const int CARGO_STUB_WIDTH;
1525  static const int HOR_CARGO_WIDTH, HOR_CARGO_SPACE;
1526  static const int CARGO_FIELD_WIDTH;
1527  static const int VERT_CARGO_SPACE, VERT_CARGO_EDGE;
1528  static const int BLOB_DISTANCE, BLOB_WIDTH, BLOB_HEIGHT;
1529 
1530  static const int INDUSTRY_LINE_COLOUR;
1531  static const int CARGO_LINE_COLOUR;
1532 
1533  static int small_height, normal_height;
1534  static int industry_width;
1535 
1537  union {
1538  struct {
1539  IndustryType ind_type;
1540  CargoID other_produced[MAX_CARGOES];
1541  CargoID other_accepted[MAX_CARGOES];
1542  } industry;
1543  struct {
1544  CargoID vertical_cargoes[MAX_CARGOES];
1546  CargoID supp_cargoes[MAX_CARGOES];
1547  byte top_end;
1548  CargoID cust_cargoes[MAX_CARGOES];
1549  byte bottom_end;
1550  } cargo;
1551  struct {
1553  bool left_align;
1554  } cargo_label;
1556  } u; // Data for each type.
1557 
1563  {
1564  this->type = type;
1565  }
1566 
1572  void MakeIndustry(IndustryType ind_type)
1573  {
1574  this->type = CFT_INDUSTRY;
1575  this->u.industry.ind_type = ind_type;
1576  MemSetT(this->u.industry.other_accepted, INVALID_CARGO, MAX_CARGOES);
1577  MemSetT(this->u.industry.other_produced, INVALID_CARGO, MAX_CARGOES);
1578  }
1579 
1586  int ConnectCargo(CargoID cargo, bool producer)
1587  {
1588  assert(this->type == CFT_CARGO);
1589  if (cargo == INVALID_CARGO) return -1;
1590 
1591  /* Find the vertical cargo column carrying the cargo. */
1592  int column = -1;
1593  for (int i = 0; i < this->u.cargo.num_cargoes; i++) {
1594  if (cargo == this->u.cargo.vertical_cargoes[i]) {
1595  column = i;
1596  break;
1597  }
1598  }
1599  if (column < 0) return -1;
1600 
1601  if (producer) {
1602  assert(this->u.cargo.supp_cargoes[column] == INVALID_CARGO);
1603  this->u.cargo.supp_cargoes[column] = column;
1604  } else {
1605  assert(this->u.cargo.cust_cargoes[column] == INVALID_CARGO);
1606  this->u.cargo.cust_cargoes[column] = column;
1607  }
1608  return column;
1609  }
1610 
1616  {
1617  assert(this->type == CFT_CARGO);
1618 
1619  for (uint i = 0; i < MAX_CARGOES; i++) {
1620  if (this->u.cargo.supp_cargoes[i] != INVALID_CARGO) return true;
1621  if (this->u.cargo.cust_cargoes[i] != INVALID_CARGO) return true;
1622  }
1623  return false;
1624  }
1625 
1635  void MakeCargo(const CargoID *cargoes, uint length, int count = -1, bool top_end = false, bool bottom_end = false)
1636  {
1637  this->type = CFT_CARGO;
1638  uint i;
1639  uint num = 0;
1640  for (i = 0; i < MAX_CARGOES && i < length; i++) {
1641  if (cargoes[i] != INVALID_CARGO) {
1642  this->u.cargo.vertical_cargoes[num] = cargoes[i];
1643  num++;
1644  }
1645  }
1646  this->u.cargo.num_cargoes = (count < 0) ? num : count;
1647  for (; num < MAX_CARGOES; num++) this->u.cargo.vertical_cargoes[num] = INVALID_CARGO;
1648  this->u.cargo.top_end = top_end;
1649  this->u.cargo.bottom_end = bottom_end;
1650  MemSetT(this->u.cargo.supp_cargoes, INVALID_CARGO, MAX_CARGOES);
1651  MemSetT(this->u.cargo.cust_cargoes, INVALID_CARGO, MAX_CARGOES);
1652  }
1653 
1660  void MakeCargoLabel(const CargoID *cargoes, uint length, bool left_align)
1661  {
1662  this->type = CFT_CARGO_LABEL;
1663  uint i;
1664  for (i = 0; i < MAX_CARGOES && i < length; i++) this->u.cargo_label.cargoes[i] = cargoes[i];
1665  for (; i < MAX_CARGOES; i++) this->u.cargo_label.cargoes[i] = INVALID_CARGO;
1666  this->u.cargo_label.left_align = left_align;
1667  }
1668 
1673  void MakeHeader(StringID textid)
1674  {
1675  this->type = CFT_HEADER;
1676  this->u.header = textid;
1677  }
1678 
1684  int GetCargoBase(int xpos) const
1685  {
1686  assert(this->type == CFT_CARGO);
1687 
1688  switch (this->u.cargo.num_cargoes) {
1689  case 0: return xpos + CARGO_FIELD_WIDTH / 2;
1690  case 1: return xpos + CARGO_FIELD_WIDTH / 2 - HOR_CARGO_WIDTH / 2;
1691  case 2: return xpos + CARGO_FIELD_WIDTH / 2 - HOR_CARGO_WIDTH - HOR_CARGO_SPACE / 2;
1692  case 3: return xpos + CARGO_FIELD_WIDTH / 2 - HOR_CARGO_WIDTH - HOR_CARGO_SPACE - HOR_CARGO_WIDTH / 2;
1693  default: NOT_REACHED();
1694  }
1695  }
1696 
1702  void Draw(int xpos, int ypos) const
1703  {
1704  switch (this->type) {
1705  case CFT_EMPTY:
1706  case CFT_SMALL_EMPTY:
1707  break;
1708 
1709  case CFT_HEADER:
1710  ypos += (small_height - FONT_HEIGHT_NORMAL) / 2;
1711  DrawString(xpos, xpos + industry_width, ypos, this->u.header, TC_WHITE, SA_HOR_CENTER);
1712  break;
1713 
1714  case CFT_INDUSTRY: {
1715  int ypos1 = ypos + VERT_INTER_INDUSTRY_SPACE / 2;
1716  int ypos2 = ypos + normal_height - 1 - VERT_INTER_INDUSTRY_SPACE / 2;
1717  int xpos2 = xpos + industry_width - 1;
1718  GfxDrawLine(xpos, ypos1, xpos2, ypos1, INDUSTRY_LINE_COLOUR);
1719  GfxDrawLine(xpos, ypos1, xpos, ypos2, INDUSTRY_LINE_COLOUR);
1720  GfxDrawLine(xpos, ypos2, xpos2, ypos2, INDUSTRY_LINE_COLOUR);
1721  GfxDrawLine(xpos2, ypos1, xpos2, ypos2, INDUSTRY_LINE_COLOUR);
1722  ypos += (normal_height - FONT_HEIGHT_NORMAL) / 2;
1723  if (this->u.industry.ind_type < NUM_INDUSTRYTYPES) {
1724  const IndustrySpec *indsp = GetIndustrySpec(this->u.industry.ind_type);
1725  DrawString(xpos, xpos2, ypos, indsp->name, TC_WHITE, SA_HOR_CENTER);
1726 
1727  /* Draw the industry legend. */
1728  int blob_left, blob_right;
1729  if (_current_text_dir == TD_RTL) {
1730  blob_right = xpos2 - BLOB_DISTANCE;
1731  blob_left = blob_right - BLOB_WIDTH;
1732  } else {
1733  blob_left = xpos + BLOB_DISTANCE;
1734  blob_right = blob_left + BLOB_WIDTH;
1735  }
1736  GfxFillRect(blob_left, ypos2 - BLOB_DISTANCE - BLOB_HEIGHT, blob_right, ypos2 - BLOB_DISTANCE, PC_BLACK); // Border
1737  GfxFillRect(blob_left + 1, ypos2 - BLOB_DISTANCE - BLOB_HEIGHT + 1, blob_right - 1, ypos2 - BLOB_DISTANCE - 1, indsp->map_colour);
1738  } else {
1739  DrawString(xpos, xpos2, ypos, STR_INDUSTRY_CARGOES_HOUSES, TC_FROMSTRING, SA_HOR_CENTER);
1740  }
1741 
1742  /* Draw the other_produced/other_accepted cargoes. */
1743  const CargoID *other_right, *other_left;
1744  if (_current_text_dir == TD_RTL) {
1745  other_right = this->u.industry.other_accepted;
1746  other_left = this->u.industry.other_produced;
1747  } else {
1748  other_right = this->u.industry.other_produced;
1749  other_left = this->u.industry.other_accepted;
1750  }
1751  ypos1 += VERT_CARGO_EDGE;
1752  for (uint i = 0; i < MAX_CARGOES; i++) {
1753  if (other_right[i] != INVALID_CARGO) {
1754  const CargoSpec *csp = CargoSpec::Get(other_right[i]);
1755  int xp = xpos + industry_width + CARGO_STUB_WIDTH;
1756  DrawHorConnection(xpos + industry_width, xp - 1, ypos1, csp);
1757  GfxDrawLine(xp, ypos1, xp, ypos1 + FONT_HEIGHT_NORMAL - 1, CARGO_LINE_COLOUR);
1758  }
1759  if (other_left[i] != INVALID_CARGO) {
1760  const CargoSpec *csp = CargoSpec::Get(other_left[i]);
1761  int xp = xpos - CARGO_STUB_WIDTH;
1762  DrawHorConnection(xp + 1, xpos - 1, ypos1, csp);
1763  GfxDrawLine(xp, ypos1, xp, ypos1 + FONT_HEIGHT_NORMAL - 1, CARGO_LINE_COLOUR);
1764  }
1765  ypos1 += FONT_HEIGHT_NORMAL + VERT_CARGO_SPACE;
1766  }
1767  break;
1768  }
1769 
1770  case CFT_CARGO: {
1771  int cargo_base = this->GetCargoBase(xpos);
1772  int top = ypos + (this->u.cargo.top_end ? VERT_INTER_INDUSTRY_SPACE / 2 + 1 : 0);
1773  int bot = ypos - (this->u.cargo.bottom_end ? VERT_INTER_INDUSTRY_SPACE / 2 + 1 : 0) + normal_height - 1;
1774  int colpos = cargo_base;
1775  for (int i = 0; i < this->u.cargo.num_cargoes; i++) {
1776  if (this->u.cargo.top_end) GfxDrawLine(colpos, top - 1, colpos + HOR_CARGO_WIDTH - 1, top - 1, CARGO_LINE_COLOUR);
1777  if (this->u.cargo.bottom_end) GfxDrawLine(colpos, bot + 1, colpos + HOR_CARGO_WIDTH - 1, bot + 1, CARGO_LINE_COLOUR);
1778  GfxDrawLine(colpos, top, colpos, bot, CARGO_LINE_COLOUR);
1779  colpos++;
1780  const CargoSpec *csp = CargoSpec::Get(this->u.cargo.vertical_cargoes[i]);
1781  GfxFillRect(colpos, top, colpos + HOR_CARGO_WIDTH - 2, bot, csp->legend_colour, FILLRECT_OPAQUE);
1782  colpos += HOR_CARGO_WIDTH - 2;
1783  GfxDrawLine(colpos, top, colpos, bot, CARGO_LINE_COLOUR);
1784  colpos += 1 + HOR_CARGO_SPACE;
1785  }
1786 
1787  const CargoID *hor_left, *hor_right;
1788  if (_current_text_dir == TD_RTL) {
1789  hor_left = this->u.cargo.cust_cargoes;
1790  hor_right = this->u.cargo.supp_cargoes;
1791  } else {
1792  hor_left = this->u.cargo.supp_cargoes;
1793  hor_right = this->u.cargo.cust_cargoes;
1794  }
1795  ypos += VERT_CARGO_EDGE + VERT_INTER_INDUSTRY_SPACE / 2;
1796  for (uint i = 0; i < MAX_CARGOES; i++) {
1797  if (hor_left[i] != INVALID_CARGO) {
1798  int col = hor_left[i];
1799  int dx = 0;
1800  const CargoSpec *csp = CargoSpec::Get(this->u.cargo.vertical_cargoes[col]);
1801  for (; col > 0; col--) {
1802  int lf = cargo_base + col * HOR_CARGO_WIDTH + (col - 1) * HOR_CARGO_SPACE;
1803  DrawHorConnection(lf, lf + HOR_CARGO_SPACE - dx, ypos, csp);
1804  dx = 1;
1805  }
1806  DrawHorConnection(xpos, cargo_base - dx, ypos, csp);
1807  }
1808  if (hor_right[i] != INVALID_CARGO) {
1809  int col = hor_right[i];
1810  int dx = 0;
1811  const CargoSpec *csp = CargoSpec::Get(this->u.cargo.vertical_cargoes[col]);
1812  for (; col < this->u.cargo.num_cargoes - 1; col++) {
1813  int lf = cargo_base + (col + 1) * HOR_CARGO_WIDTH + col * HOR_CARGO_SPACE;
1814  DrawHorConnection(lf + dx - 1, lf + HOR_CARGO_SPACE - 1, ypos, csp);
1815  dx = 1;
1816  }
1817  DrawHorConnection(cargo_base + col * HOR_CARGO_SPACE + (col + 1) * HOR_CARGO_WIDTH - 1 + dx, xpos + CARGO_FIELD_WIDTH - 1, ypos, csp);
1818  }
1819  ypos += FONT_HEIGHT_NORMAL + VERT_CARGO_SPACE;
1820  }
1821  break;
1822  }
1823 
1824  case CFT_CARGO_LABEL:
1825  ypos += VERT_CARGO_EDGE + VERT_INTER_INDUSTRY_SPACE / 2;
1826  for (uint i = 0; i < MAX_CARGOES; i++) {
1827  if (this->u.cargo_label.cargoes[i] != INVALID_CARGO) {
1828  const CargoSpec *csp = CargoSpec::Get(this->u.cargo_label.cargoes[i]);
1829  DrawString(xpos + WD_FRAMERECT_LEFT, xpos + industry_width - 1 - WD_FRAMERECT_RIGHT, ypos, csp->name, TC_WHITE,
1830  (this->u.cargo_label.left_align) ? SA_LEFT : SA_RIGHT);
1831  }
1832  ypos += FONT_HEIGHT_NORMAL + VERT_CARGO_SPACE;
1833  }
1834  break;
1835 
1836  default:
1837  NOT_REACHED();
1838  }
1839  }
1840 
1848  CargoID CargoClickedAt(const CargoesField *left, const CargoesField *right, Point pt) const
1849  {
1850  assert(this->type == CFT_CARGO);
1851 
1852  /* Vertical matching. */
1853  int cpos = this->GetCargoBase(0);
1854  uint col;
1855  for (col = 0; col < this->u.cargo.num_cargoes; col++) {
1856  if (pt.x < cpos) break;
1857  if (pt.x < cpos + CargoesField::HOR_CARGO_WIDTH) return this->u.cargo.vertical_cargoes[col];
1859  }
1860  /* col = 0 -> left of first col, 1 -> left of 2nd col, ... this->u.cargo.num_cargoes right of last-col. */
1861 
1862  int vpos = VERT_INTER_INDUSTRY_SPACE / 2 + VERT_CARGO_EDGE;
1863  uint row;
1864  for (row = 0; row < MAX_CARGOES; row++) {
1865  if (pt.y < vpos) return INVALID_CARGO;
1866  if (pt.y < vpos + FONT_HEIGHT_NORMAL) break;
1867  vpos += FONT_HEIGHT_NORMAL + VERT_CARGO_SPACE;
1868  }
1869  if (row == MAX_CARGOES) return INVALID_CARGO;
1870 
1871  /* row = 0 -> at first horizontal row, row = 1 -> second horizontal row, 2 = 3rd horizontal row. */
1872  if (col == 0) {
1873  if (this->u.cargo.supp_cargoes[row] != INVALID_CARGO) return this->u.cargo.vertical_cargoes[this->u.cargo.supp_cargoes[row]];
1874  if (left != NULL) {
1875  if (left->type == CFT_INDUSTRY) return left->u.industry.other_produced[row];
1876  if (left->type == CFT_CARGO_LABEL && !left->u.cargo_label.left_align) return left->u.cargo_label.cargoes[row];
1877  }
1878  return INVALID_CARGO;
1879  }
1880  if (col == this->u.cargo.num_cargoes) {
1881  if (this->u.cargo.cust_cargoes[row] != INVALID_CARGO) return this->u.cargo.vertical_cargoes[this->u.cargo.cust_cargoes[row]];
1882  if (right != NULL) {
1883  if (right->type == CFT_INDUSTRY) return right->u.industry.other_accepted[row];
1884  if (right->type == CFT_CARGO_LABEL && right->u.cargo_label.left_align) return right->u.cargo_label.cargoes[row];
1885  }
1886  return INVALID_CARGO;
1887  }
1888  if (row >= col) {
1889  /* Clicked somewhere in-between vertical cargo connection.
1890  * Since the horizontal connection is made in the same order as the vertical list, the above condition
1891  * ensures we are left-below the main diagonal, thus at the supplying side.
1892  */
1893  return (this->u.cargo.supp_cargoes[row] != INVALID_CARGO) ? this->u.cargo.vertical_cargoes[this->u.cargo.supp_cargoes[row]] : INVALID_CARGO;
1894  } else {
1895  /* Clicked at a customer connection. */
1896  return (this->u.cargo.cust_cargoes[row] != INVALID_CARGO) ? this->u.cargo.vertical_cargoes[this->u.cargo.cust_cargoes[row]] : INVALID_CARGO;
1897  }
1898  }
1899 
1906  {
1907  assert(this->type == CFT_CARGO_LABEL);
1908 
1909  int vpos = VERT_INTER_INDUSTRY_SPACE / 2 + VERT_CARGO_EDGE;
1910  uint row;
1911  for (row = 0; row < MAX_CARGOES; row++) {
1912  if (pt.y < vpos) return INVALID_CARGO;
1913  if (pt.y < vpos + FONT_HEIGHT_NORMAL) break;
1914  vpos += FONT_HEIGHT_NORMAL + VERT_CARGO_SPACE;
1915  }
1916  if (row == MAX_CARGOES) return INVALID_CARGO;
1917  return this->u.cargo_label.cargoes[row];
1918  }
1919 
1920 private:
1928  static void DrawHorConnection(int left, int right, int top, const CargoSpec *csp)
1929  {
1930  GfxDrawLine(left, top, right, top, CARGO_LINE_COLOUR);
1931  GfxFillRect(left, top + 1, right, top + FONT_HEIGHT_NORMAL - 2, csp->legend_colour, FILLRECT_OPAQUE);
1932  GfxDrawLine(left, top + FONT_HEIGHT_NORMAL - 1, right, top + FONT_HEIGHT_NORMAL - 1, CARGO_LINE_COLOUR);
1933  }
1934 };
1935 
1936 assert_compile(MAX_CARGOES >= cpp_lengthof(IndustrySpec, produced_cargo));
1937 assert_compile(MAX_CARGOES >= cpp_lengthof(IndustrySpec, accepts_cargo));
1938 
1943 
1944 const int CargoesField::HOR_CARGO_BORDER_SPACE = 15;
1945 const int CargoesField::CARGO_STUB_WIDTH = 10;
1946 const int CargoesField::HOR_CARGO_WIDTH = 15;
1947 const int CargoesField::HOR_CARGO_SPACE = 5;
1948 const int CargoesField::VERT_CARGO_EDGE = 4;
1949 const int CargoesField::VERT_CARGO_SPACE = 4;
1950 
1951 const int CargoesField::BLOB_DISTANCE = 5;
1952 const int CargoesField::BLOB_WIDTH = 12;
1953 const int CargoesField::BLOB_HEIGHT = 9;
1954 
1956 const int CargoesField::CARGO_FIELD_WIDTH = HOR_CARGO_BORDER_SPACE * 2 + HOR_CARGO_WIDTH * MAX_CARGOES + HOR_CARGO_SPACE * (MAX_CARGOES - 1);
1957 
1960 
1962 struct CargoesRow {
1963  CargoesField columns[5];
1964 
1969  void ConnectIndustryProduced(int column)
1970  {
1971  CargoesField *ind_fld = this->columns + column;
1972  CargoesField *cargo_fld = this->columns + column + 1;
1973  assert(ind_fld->type == CFT_INDUSTRY && cargo_fld->type == CFT_CARGO);
1974 
1975  MemSetT(ind_fld->u.industry.other_produced, INVALID_CARGO, MAX_CARGOES);
1976 
1977  if (ind_fld->u.industry.ind_type < NUM_INDUSTRYTYPES) {
1978  CargoID others[MAX_CARGOES]; // Produced cargoes not carried in the cargo column.
1979  int other_count = 0;
1980 
1981  const IndustrySpec *indsp = GetIndustrySpec(ind_fld->u.industry.ind_type);
1982  for (uint i = 0; i < lengthof(indsp->produced_cargo); i++) {
1983  int col = cargo_fld->ConnectCargo(indsp->produced_cargo[i], true);
1984  if (col < 0) others[other_count++] = indsp->produced_cargo[i];
1985  }
1986 
1987  /* Allocate other cargoes in the empty holes of the horizontal cargo connections. */
1988  for (uint i = 0; i < MAX_CARGOES && other_count > 0; i++) {
1989  if (cargo_fld->u.cargo.supp_cargoes[i] == INVALID_CARGO) ind_fld->u.industry.other_produced[i] = others[--other_count];
1990  }
1991  } else {
1992  /* Houses only display what is demanded. */
1993  for (uint i = 0; i < cargo_fld->u.cargo.num_cargoes; i++) {
1994  CargoID cid = cargo_fld->u.cargo.vertical_cargoes[i];
1995  if (cid == CT_PASSENGERS || cid == CT_MAIL) cargo_fld->ConnectCargo(cid, true);
1996  }
1997  }
1998  }
1999 
2005  void MakeCargoLabel(int column, bool accepting)
2006  {
2007  CargoID cargoes[MAX_CARGOES];
2008  MemSetT(cargoes, INVALID_CARGO, lengthof(cargoes));
2009 
2010  CargoesField *label_fld = this->columns + column;
2011  CargoesField *cargo_fld = this->columns + (accepting ? column - 1 : column + 1);
2012 
2013  assert(cargo_fld->type == CFT_CARGO && label_fld->type == CFT_EMPTY);
2014  for (uint i = 0; i < cargo_fld->u.cargo.num_cargoes; i++) {
2015  int col = cargo_fld->ConnectCargo(cargo_fld->u.cargo.vertical_cargoes[i], !accepting);
2016  if (col >= 0) cargoes[col] = cargo_fld->u.cargo.vertical_cargoes[i];
2017  }
2018  label_fld->MakeCargoLabel(cargoes, lengthof(cargoes), accepting);
2019  }
2020 
2021 
2026  void ConnectIndustryAccepted(int column)
2027  {
2028  CargoesField *ind_fld = this->columns + column;
2029  CargoesField *cargo_fld = this->columns + column - 1;
2030  assert(ind_fld->type == CFT_INDUSTRY && cargo_fld->type == CFT_CARGO);
2031 
2032  MemSetT(ind_fld->u.industry.other_accepted, INVALID_CARGO, MAX_CARGOES);
2033 
2034  if (ind_fld->u.industry.ind_type < NUM_INDUSTRYTYPES) {
2035  CargoID others[MAX_CARGOES]; // Accepted cargoes not carried in the cargo column.
2036  int other_count = 0;
2037 
2038  const IndustrySpec *indsp = GetIndustrySpec(ind_fld->u.industry.ind_type);
2039  for (uint i = 0; i < lengthof(indsp->accepts_cargo); i++) {
2040  int col = cargo_fld->ConnectCargo(indsp->accepts_cargo[i], false);
2041  if (col < 0) others[other_count++] = indsp->accepts_cargo[i];
2042  }
2043 
2044  /* Allocate other cargoes in the empty holes of the horizontal cargo connections. */
2045  for (uint i = 0; i < MAX_CARGOES && other_count > 0; i++) {
2046  if (cargo_fld->u.cargo.cust_cargoes[i] == INVALID_CARGO) ind_fld->u.industry.other_accepted[i] = others[--other_count];
2047  }
2048  } else {
2049  /* Houses only display what is demanded. */
2050  for (uint i = 0; i < cargo_fld->u.cargo.num_cargoes; i++) {
2051  for (uint h = 0; h < NUM_HOUSES; h++) {
2052  HouseSpec *hs = HouseSpec::Get(h);
2053  if (!hs->enabled) continue;
2054 
2055  for (uint j = 0; j < lengthof(hs->accepts_cargo); j++) {
2056  if (hs->cargo_acceptance[j] > 0 && cargo_fld->u.cargo.vertical_cargoes[i] == hs->accepts_cargo[j]) {
2057  cargo_fld->ConnectCargo(cargo_fld->u.cargo.vertical_cargoes[i], false);
2058  goto next_cargo;
2059  }
2060  }
2061  }
2062 next_cargo: ;
2063  }
2064  }
2065  }
2066 };
2067 
2068 
2097  static const int HOR_TEXT_PADDING, VERT_TEXT_PADDING;
2098 
2100 
2101  Fields fields;
2102  uint ind_cargo;
2105  Scrollbar *vscroll;
2106 
2107  IndustryCargoesWindow(int id) : Window(&_industry_cargoes_desc)
2108  {
2109  this->OnInit();
2110  this->CreateNestedTree();
2111  this->vscroll = this->GetScrollbar(WID_IC_SCROLLBAR);
2112  this->FinishInitNested(0);
2113  this->OnInvalidateData(id);
2114  }
2115 
2116  virtual void OnInit()
2117  {
2118  /* Initialize static CargoesField size variables. */
2119  Dimension d = GetStringBoundingBox(STR_INDUSTRY_CARGOES_PRODUCERS);
2120  d = maxdim(d, GetStringBoundingBox(STR_INDUSTRY_CARGOES_CUSTOMERS));
2122  d.height += WD_FRAMETEXT_TOP + WD_FRAMETEXT_BOTTOM;
2123  CargoesField::small_height = d.height;
2124 
2125  /* Decide about the size of the box holding the text of an industry type. */
2126  this->ind_textsize.width = 0;
2127  this->ind_textsize.height = 0;
2128  for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
2129  const IndustrySpec *indsp = GetIndustrySpec(it);
2130  if (!indsp->enabled) continue;
2131  this->ind_textsize = maxdim(this->ind_textsize, GetStringBoundingBox(indsp->name));
2132  }
2133  d.width = max(d.width, this->ind_textsize.width);
2134  d.height = this->ind_textsize.height;
2135  this->ind_textsize = maxdim(this->ind_textsize, GetStringBoundingBox(STR_INDUSTRY_CARGOES_SELECT_INDUSTRY));
2136 
2137  /* Compute max size of the cargo texts. */
2138  this->cargo_textsize.width = 0;
2139  this->cargo_textsize.height = 0;
2140  for (uint i = 0; i < NUM_CARGO; i++) {
2141  const CargoSpec *csp = CargoSpec::Get(i);
2142  if (!csp->IsValid()) continue;
2143  this->cargo_textsize = maxdim(this->cargo_textsize, GetStringBoundingBox(csp->name));
2144  }
2145  d = maxdim(d, this->cargo_textsize); // Box must also be wide enough to hold any cargo label.
2146  this->cargo_textsize = maxdim(this->cargo_textsize, GetStringBoundingBox(STR_INDUSTRY_CARGOES_SELECT_CARGO));
2147 
2148  d.width += 2 * HOR_TEXT_PADDING;
2149  /* Ensure the height is enough for the industry type text, for the horizontal connections, and for the cargo labels. */
2151  d.height = max(d.height + 2 * VERT_TEXT_PADDING, min_ind_height);
2152 
2153  CargoesField::industry_width = d.width;
2155  }
2156 
2157  virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
2158  {
2159  switch (widget) {
2160  case WID_IC_PANEL:
2162  break;
2163 
2164  case WID_IC_IND_DROPDOWN:
2165  size->width = max(size->width, this->ind_textsize.width + padding.width);
2166  break;
2167 
2168  case WID_IC_CARGO_DROPDOWN:
2169  size->width = max(size->width, this->cargo_textsize.width + padding.width);
2170  break;
2171  }
2172  }
2173 
2174 
2176  virtual void SetStringParameters (int widget) const
2177  {
2178  if (widget != WID_IC_CAPTION) return;
2179 
2180  if (this->ind_cargo < NUM_INDUSTRYTYPES) {
2181  const IndustrySpec *indsp = GetIndustrySpec(this->ind_cargo);
2182  SetDParam(0, indsp->name);
2183  } else {
2184  const CargoSpec *csp = CargoSpec::Get(this->ind_cargo - NUM_INDUSTRYTYPES);
2185  SetDParam(0, csp->name);
2186  }
2187  }
2188 
2197  static bool HasCommonValidCargo(const CargoID *cargoes1, uint length1, const CargoID *cargoes2, uint length2)
2198  {
2199  while (length1 > 0) {
2200  if (*cargoes1 != INVALID_CARGO) {
2201  for (uint i = 0; i < length2; i++) if (*cargoes1 == cargoes2[i]) return true;
2202  }
2203  cargoes1++;
2204  length1--;
2205  }
2206  return false;
2207  }
2208 
2215  static bool HousesCanSupply(const CargoID *cargoes, uint length)
2216  {
2217  for (uint i = 0; i < length; i++) {
2218  if (cargoes[i] == INVALID_CARGO) continue;
2219  if (cargoes[i] == CT_PASSENGERS || cargoes[i] == CT_MAIL) return true;
2220  }
2221  return false;
2222  }
2223 
2230  static bool HousesCanAccept(const CargoID *cargoes, uint length)
2231  {
2232  HouseZones climate_mask;
2234  case LT_TEMPERATE: climate_mask = HZ_TEMP; break;
2235  case LT_ARCTIC: climate_mask = HZ_SUBARTC_ABOVE | HZ_SUBARTC_BELOW; break;
2236  case LT_TROPIC: climate_mask = HZ_SUBTROPIC; break;
2237  case LT_TOYLAND: climate_mask = HZ_TOYLND; break;
2238  default: NOT_REACHED();
2239  }
2240  for (uint i = 0; i < length; i++) {
2241  if (cargoes[i] == INVALID_CARGO) continue;
2242 
2243  for (uint h = 0; h < NUM_HOUSES; h++) {
2244  HouseSpec *hs = HouseSpec::Get(h);
2245  if (!hs->enabled || !(hs->building_availability & climate_mask)) continue;
2246 
2247  for (uint j = 0; j < lengthof(hs->accepts_cargo); j++) {
2248  if (hs->cargo_acceptance[j] > 0 && cargoes[i] == hs->accepts_cargo[j]) return true;
2249  }
2250  }
2251  }
2252  return false;
2253  }
2254 
2261  static int CountMatchingAcceptingIndustries(const CargoID *cargoes, uint length)
2262  {
2263  int count = 0;
2264  for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
2265  const IndustrySpec *indsp = GetIndustrySpec(it);
2266  if (!indsp->enabled) continue;
2267 
2268  if (HasCommonValidCargo(cargoes, length, indsp->accepts_cargo, lengthof(indsp->accepts_cargo))) count++;
2269  }
2270  return count;
2271  }
2272 
2279  static int CountMatchingProducingIndustries(const CargoID *cargoes, uint length)
2280  {
2281  int count = 0;
2282  for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
2283  const IndustrySpec *indsp = GetIndustrySpec(it);
2284  if (!indsp->enabled) continue;
2285 
2286  if (HasCommonValidCargo(cargoes, length, indsp->produced_cargo, lengthof(indsp->produced_cargo))) count++;
2287  }
2288  return count;
2289  }
2290 
2297  void ShortenCargoColumn(int column, int top, int bottom)
2298  {
2299  while (top < bottom && !this->fields[top].columns[column].HasConnection()) {
2300  this->fields[top].columns[column].MakeEmpty(CFT_EMPTY);
2301  top++;
2302  }
2303  this->fields[top].columns[column].u.cargo.top_end = true;
2304 
2305  while (bottom > top && !this->fields[bottom].columns[column].HasConnection()) {
2306  this->fields[bottom].columns[column].MakeEmpty(CFT_EMPTY);
2307  bottom--;
2308  }
2309  this->fields[bottom].columns[column].u.cargo.bottom_end = true;
2310  }
2311 
2318  void PlaceIndustry(int row, int col, IndustryType it)
2319  {
2320  assert(this->fields[row].columns[col].type == CFT_EMPTY);
2321  this->fields[row].columns[col].MakeIndustry(it);
2322  if (col == 0) {
2323  this->fields[row].ConnectIndustryProduced(col);
2324  } else {
2325  this->fields[row].ConnectIndustryAccepted(col);
2326  }
2327  }
2328 
2333  {
2334  if (!this->IsWidgetLowered(WID_IC_NOTIFY)) return;
2335 
2336  /* Only notify the smallmap window if it exists. In particular, do not
2337  * bring it to the front to prevent messing up any nice layout of the user. */
2339  }
2340 
2345  void ComputeIndustryDisplay(IndustryType it)
2346  {
2347  this->GetWidget<NWidgetCore>(WID_IC_CAPTION)->widget_data = STR_INDUSTRY_CARGOES_INDUSTRY_CAPTION;
2348  this->ind_cargo = it;
2349  _displayed_industries.reset();
2350  _displayed_industries.set(it);
2351 
2352  this->fields.Clear();
2353  CargoesRow *row = this->fields.Append();
2354  row->columns[0].MakeHeader(STR_INDUSTRY_CARGOES_PRODUCERS);
2358  row->columns[4].MakeHeader(STR_INDUSTRY_CARGOES_CUSTOMERS);
2359 
2360  const IndustrySpec *central_sp = GetIndustrySpec(it);
2361  bool houses_supply = HousesCanSupply(central_sp->accepts_cargo, lengthof(central_sp->accepts_cargo));
2362  bool houses_accept = HousesCanAccept(central_sp->produced_cargo, lengthof(central_sp->produced_cargo));
2363  /* Make a field consisting of two cargo columns. */
2364  int num_supp = CountMatchingProducingIndustries(central_sp->accepts_cargo, lengthof(central_sp->accepts_cargo)) + houses_supply;
2365  int num_cust = CountMatchingAcceptingIndustries(central_sp->produced_cargo, lengthof(central_sp->produced_cargo)) + houses_accept;
2366  int num_indrows = max(3, max(num_supp, num_cust)); // One is needed for the 'it' industry, and 2 for the cargo labels.
2367  for (int i = 0; i < num_indrows; i++) {
2368  CargoesRow *row = this->fields.Append();
2369  row->columns[0].MakeEmpty(CFT_EMPTY);
2370  row->columns[1].MakeCargo(central_sp->accepts_cargo, lengthof(central_sp->accepts_cargo));
2371  row->columns[2].MakeEmpty(CFT_EMPTY);
2372  row->columns[3].MakeCargo(central_sp->produced_cargo, lengthof(central_sp->produced_cargo));
2373  row->columns[4].MakeEmpty(CFT_EMPTY);
2374  }
2375  /* Add central industry. */
2376  int central_row = 1 + num_indrows / 2;
2377  this->fields[central_row].columns[2].MakeIndustry(it);
2378  this->fields[central_row].ConnectIndustryProduced(2);
2379  this->fields[central_row].ConnectIndustryAccepted(2);
2380 
2381  /* Add cargo labels. */
2382  this->fields[central_row - 1].MakeCargoLabel(2, true);
2383  this->fields[central_row + 1].MakeCargoLabel(2, false);
2384 
2385  /* Add suppliers and customers of the 'it' industry. */
2386  int supp_count = 0;
2387  int cust_count = 0;
2388  for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
2389  const IndustrySpec *indsp = GetIndustrySpec(it);
2390  if (!indsp->enabled) continue;
2391 
2392  if (HasCommonValidCargo(central_sp->accepts_cargo, lengthof(central_sp->accepts_cargo), indsp->produced_cargo, lengthof(indsp->produced_cargo))) {
2393  this->PlaceIndustry(1 + supp_count * num_indrows / num_supp, 0, it);
2394  _displayed_industries.set(it);
2395  supp_count++;
2396  }
2397  if (HasCommonValidCargo(central_sp->produced_cargo, lengthof(central_sp->produced_cargo), indsp->accepts_cargo, lengthof(indsp->accepts_cargo))) {
2398  this->PlaceIndustry(1 + cust_count * num_indrows / num_cust, 4, it);
2399  _displayed_industries.set(it);
2400  cust_count++;
2401  }
2402  }
2403  if (houses_supply) {
2404  this->PlaceIndustry(1 + supp_count * num_indrows / num_supp, 0, NUM_INDUSTRYTYPES);
2405  supp_count++;
2406  }
2407  if (houses_accept) {
2408  this->PlaceIndustry(1 + cust_count * num_indrows / num_cust, 4, NUM_INDUSTRYTYPES);
2409  cust_count++;
2410  }
2411 
2412  this->ShortenCargoColumn(1, 1, num_indrows);
2413  this->ShortenCargoColumn(3, 1, num_indrows);
2414  const NWidgetBase *nwp = this->GetWidget<NWidgetBase>(WID_IC_PANEL);
2416  this->SetDirty();
2417  this->NotifySmallmap();
2418  }
2419 
2425  {
2426  this->GetWidget<NWidgetCore>(WID_IC_CAPTION)->widget_data = STR_INDUSTRY_CARGOES_CARGO_CAPTION;
2427  this->ind_cargo = cid + NUM_INDUSTRYTYPES;
2428  _displayed_industries.reset();
2429 
2430  this->fields.Clear();
2431  CargoesRow *row = this->fields.Append();
2432  row->columns[0].MakeHeader(STR_INDUSTRY_CARGOES_PRODUCERS);
2434  row->columns[2].MakeHeader(STR_INDUSTRY_CARGOES_CUSTOMERS);
2437 
2438  bool houses_supply = HousesCanSupply(&cid, 1);
2439  bool houses_accept = HousesCanAccept(&cid, 1);
2440  int num_supp = CountMatchingProducingIndustries(&cid, 1) + houses_supply + 1; // Ensure room for the cargo label.
2441  int num_cust = CountMatchingAcceptingIndustries(&cid, 1) + houses_accept;
2442  int num_indrows = max(num_supp, num_cust);
2443  for (int i = 0; i < num_indrows; i++) {
2444  CargoesRow *row = this->fields.Append();
2445  row->columns[0].MakeEmpty(CFT_EMPTY);
2446  row->columns[1].MakeCargo(&cid, 1);
2447  row->columns[2].MakeEmpty(CFT_EMPTY);
2448  row->columns[3].MakeEmpty(CFT_EMPTY);
2449  row->columns[4].MakeEmpty(CFT_EMPTY);
2450  }
2451 
2452  this->fields[num_indrows].MakeCargoLabel(0, false); // Add cargo labels at the left bottom.
2453 
2454  /* Add suppliers and customers of the cargo. */
2455  int supp_count = 0;
2456  int cust_count = 0;
2457  for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
2458  const IndustrySpec *indsp = GetIndustrySpec(it);
2459  if (!indsp->enabled) continue;
2460 
2461  if (HasCommonValidCargo(&cid, 1, indsp->produced_cargo, lengthof(indsp->produced_cargo))) {
2462  this->PlaceIndustry(1 + supp_count * num_indrows / num_supp, 0, it);
2463  _displayed_industries.set(it);
2464  supp_count++;
2465  }
2466  if (HasCommonValidCargo(&cid, 1, indsp->accepts_cargo, lengthof(indsp->accepts_cargo))) {
2467  this->PlaceIndustry(1 + cust_count * num_indrows / num_cust, 2, it);
2468  _displayed_industries.set(it);
2469  cust_count++;
2470  }
2471  }
2472  if (houses_supply) {
2473  this->PlaceIndustry(1 + supp_count * num_indrows / num_supp, 0, NUM_INDUSTRYTYPES);
2474  supp_count++;
2475  }
2476  if (houses_accept) {
2477  this->PlaceIndustry(1 + cust_count * num_indrows / num_cust, 2, NUM_INDUSTRYTYPES);
2478  cust_count++;
2479  }
2480 
2481  this->ShortenCargoColumn(1, 1, num_indrows);
2482  const NWidgetBase *nwp = this->GetWidget<NWidgetBase>(WID_IC_PANEL);
2484  this->SetDirty();
2485  this->NotifySmallmap();
2486  }
2487 
2495  virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
2496  {
2497  if (!gui_scope) return;
2498  if (data == NUM_INDUSTRYTYPES) {
2499  if (this->IsWidgetLowered(WID_IC_NOTIFY)) {
2500  this->RaiseWidget(WID_IC_NOTIFY);
2501  this->SetWidgetDirty(WID_IC_NOTIFY);
2502  }
2503  return;
2504  }
2505 
2506  assert(data >= 0 && data < NUM_INDUSTRYTYPES);
2507  this->ComputeIndustryDisplay(data);
2508  }
2509 
2510  virtual void DrawWidget(const Rect &r, int widget) const
2511  {
2512  if (widget != WID_IC_PANEL) return;
2513 
2514  DrawPixelInfo tmp_dpi, *old_dpi;
2515  int width = r.right - r.left + 1;
2516  int height = r.bottom - r.top + 1 - WD_FRAMERECT_TOP - WD_FRAMERECT_BOTTOM;
2517  if (!FillDrawPixelInfo(&tmp_dpi, r.left + WD_FRAMERECT_LEFT, r.top + WD_FRAMERECT_TOP, width, height)) return;
2518  old_dpi = _cur_dpi;
2519  _cur_dpi = &tmp_dpi;
2520 
2521  int left_pos = WD_FRAMERECT_LEFT;
2522  if (this->ind_cargo >= NUM_INDUSTRYTYPES) left_pos += (CargoesField::industry_width + CargoesField::CARGO_FIELD_WIDTH) / 2;
2523  int last_column = (this->ind_cargo < NUM_INDUSTRYTYPES) ? 4 : 2;
2524 
2525  const NWidgetBase *nwp = this->GetWidget<NWidgetBase>(WID_IC_PANEL);
2526  int vpos = -this->vscroll->GetPosition() * nwp->resize_y;
2527  for (uint i = 0; i < this->fields.Length(); i++) {
2528  int row_height = (i == 0) ? CargoesField::small_height : CargoesField::normal_height;
2529  if (vpos + row_height >= 0) {
2530  int xpos = left_pos;
2531  int col, dir;
2532  if (_current_text_dir == TD_RTL) {
2533  col = last_column;
2534  dir = -1;
2535  } else {
2536  col = 0;
2537  dir = 1;
2538  }
2539  while (col >= 0 && col <= last_column) {
2540  this->fields[i].columns[col].Draw(xpos, vpos);
2542  col += dir;
2543  }
2544  }
2545  vpos += row_height;
2546  if (vpos >= height) break;
2547  }
2548 
2549  _cur_dpi = old_dpi;
2550  }
2551 
2559  bool CalculatePositionInWidget(Point pt, Point *fieldxy, Point *xy)
2560  {
2561  const NWidgetBase *nw = this->GetWidget<NWidgetBase>(WID_IC_PANEL);
2562  pt.x -= nw->pos_x;
2563  pt.y -= nw->pos_y;
2564 
2565  int vpos = WD_FRAMERECT_TOP + CargoesField::small_height - this->vscroll->GetPosition() * nw->resize_y;
2566  if (pt.y < vpos) return false;
2567 
2568  int row = (pt.y - vpos) / CargoesField::normal_height; // row is relative to row 1.
2569  if (row + 1 >= (int)this->fields.Length()) return false;
2570  vpos = pt.y - vpos - row * CargoesField::normal_height; // Position in the row + 1 field
2571  row++; // rebase row to match index of this->fields.
2572 
2573  int xpos = 2 * WD_FRAMERECT_LEFT + ((this->ind_cargo < NUM_INDUSTRYTYPES) ? 0 : (CargoesField::industry_width + CargoesField::CARGO_FIELD_WIDTH) / 2);
2574  if (pt.x < xpos) return false;
2575  int column;
2576  for (column = 0; column <= 5; column++) {
2578  if (pt.x < xpos + width) break;
2579  xpos += width;
2580  }
2581  int num_columns = (this->ind_cargo < NUM_INDUSTRYTYPES) ? 4 : 2;
2582  if (column > num_columns) return false;
2583  xpos = pt.x - xpos;
2584 
2585  /* Return both positions, compensating for RTL languages (which works due to the equal symmetry in both displays). */
2586  fieldxy->y = row;
2587  xy->y = vpos;
2588  if (_current_text_dir == TD_RTL) {
2589  fieldxy->x = num_columns - column;
2590  xy->x = ((column & 1) ? CargoesField::CARGO_FIELD_WIDTH : CargoesField::industry_width) - xpos;
2591  } else {
2592  fieldxy->x = column;
2593  xy->x = xpos;
2594  }
2595  return true;
2596  }
2597 
2598  virtual void OnClick(Point pt, int widget, int click_count)
2599  {
2600  switch (widget) {
2601  case WID_IC_PANEL: {
2602  Point fieldxy, xy;
2603  if (!CalculatePositionInWidget(pt, &fieldxy, &xy)) return;
2604 
2605  const CargoesField *fld = this->fields[fieldxy.y].columns + fieldxy.x;
2606  switch (fld->type) {
2607  case CFT_INDUSTRY:
2608  if (fld->u.industry.ind_type < NUM_INDUSTRYTYPES) this->ComputeIndustryDisplay(fld->u.industry.ind_type);
2609  break;
2610 
2611  case CFT_CARGO: {
2612  CargoesField *lft = (fieldxy.x > 0) ? this->fields[fieldxy.y].columns + fieldxy.x - 1 : NULL;
2613  CargoesField *rgt = (fieldxy.x < 4) ? this->fields[fieldxy.y].columns + fieldxy.x + 1 : NULL;
2614  CargoID cid = fld->CargoClickedAt(lft, rgt, xy);
2615  if (cid != INVALID_CARGO) this->ComputeCargoDisplay(cid);
2616  break;
2617  }
2618 
2619  case CFT_CARGO_LABEL: {
2620  CargoID cid = fld->CargoLabelClickedAt(xy);
2621  if (cid != INVALID_CARGO) this->ComputeCargoDisplay(cid);
2622  break;
2623  }
2624 
2625  default:
2626  break;
2627  }
2628  break;
2629  }
2630 
2631  case WID_IC_NOTIFY:
2632  this->ToggleWidgetLoweredState(WID_IC_NOTIFY);
2633  this->SetWidgetDirty(WID_IC_NOTIFY);
2634  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
2635 
2636  if (this->IsWidgetLowered(WID_IC_NOTIFY)) {
2637  if (FindWindowByClass(WC_SMALLMAP) == NULL) ShowSmallMap();
2638  this->NotifySmallmap();
2639  }
2640  break;
2641 
2642  case WID_IC_CARGO_DROPDOWN: {
2643  DropDownList *lst = new DropDownList;
2644  const CargoSpec *cs;
2646  *lst->Append() = new DropDownListStringItem(cs->name, cs->Index(), false);
2647  }
2648  if (lst->Length() == 0) {
2649  delete lst;
2650  break;
2651  }
2652  int selected = (this->ind_cargo >= NUM_INDUSTRYTYPES) ? (int)(this->ind_cargo - NUM_INDUSTRYTYPES) : -1;
2653  ShowDropDownList(this, lst, selected, WID_IC_CARGO_DROPDOWN, 0, true);
2654  break;
2655  }
2656 
2657  case WID_IC_IND_DROPDOWN: {
2658  DropDownList *lst = new DropDownList;
2659  for (uint i = 0; i < NUM_INDUSTRYTYPES; i++) {
2660  IndustryType ind = _sorted_industry_types[i];
2661  const IndustrySpec *indsp = GetIndustrySpec(ind);
2662  if (!indsp->enabled) continue;
2663  *lst->Append() = new DropDownListStringItem(indsp->name, ind, false);
2664  }
2665  if (lst->Length() == 0) {
2666  delete lst;
2667  break;
2668  }
2669  int selected = (this->ind_cargo < NUM_INDUSTRYTYPES) ? (int)this->ind_cargo : -1;
2670  ShowDropDownList(this, lst, selected, WID_IC_IND_DROPDOWN, 0, true);
2671  break;
2672  }
2673  }
2674  }
2675 
2676  virtual void OnDropdownSelect(int widget, int index)
2677  {
2678  if (index < 0) return;
2679 
2680  switch (widget) {
2681  case WID_IC_CARGO_DROPDOWN:
2682  this->ComputeCargoDisplay(index);
2683  break;
2684 
2685  case WID_IC_IND_DROPDOWN:
2686  this->ComputeIndustryDisplay(index);
2687  break;
2688  }
2689  }
2690 
2691  virtual void OnHover(Point pt, int widget)
2692  {
2693  if (widget != WID_IC_PANEL) return;
2694 
2695  Point fieldxy, xy;
2696  if (!CalculatePositionInWidget(pt, &fieldxy, &xy)) return;
2697 
2698  const CargoesField *fld = this->fields[fieldxy.y].columns + fieldxy.x;
2699  CargoID cid = INVALID_CARGO;
2700  switch (fld->type) {
2701  case CFT_CARGO: {
2702  CargoesField *lft = (fieldxy.x > 0) ? this->fields[fieldxy.y].columns + fieldxy.x - 1 : NULL;
2703  CargoesField *rgt = (fieldxy.x < 4) ? this->fields[fieldxy.y].columns + fieldxy.x + 1 : NULL;
2704  cid = fld->CargoClickedAt(lft, rgt, xy);
2705  break;
2706  }
2707 
2708  case CFT_CARGO_LABEL: {
2709  cid = fld->CargoLabelClickedAt(xy);
2710  break;
2711  }
2712 
2713  case CFT_INDUSTRY:
2714  if (fld->u.industry.ind_type < NUM_INDUSTRYTYPES && (this->ind_cargo >= NUM_INDUSTRYTYPES || fieldxy.x != 2)) {
2715  GuiShowTooltips(this, STR_INDUSTRY_CARGOES_INDUSTRY_TOOLTIP, 0, NULL, TCC_HOVER);
2716  }
2717  return;
2718 
2719  default:
2720  break;
2721  }
2722  if (cid != INVALID_CARGO && (this->ind_cargo < NUM_INDUSTRYTYPES || cid != this->ind_cargo - NUM_INDUSTRYTYPES)) {
2723  const CargoSpec *csp = CargoSpec::Get(cid);
2724  uint64 params[5];
2725  params[0] = csp->name;
2726  GuiShowTooltips(this, STR_INDUSTRY_CARGOES_CARGO_TOOLTIP, 1, params, TCC_HOVER);
2727  }
2728  }
2729 
2730  virtual void OnResize()
2731  {
2732  this->vscroll->SetCapacityFromWidget(this, WID_IC_PANEL);
2733  }
2734 };
2735 
2738 
2743 static void ShowIndustryCargoesWindow(IndustryType id)
2744 {
2745  if (id >= NUM_INDUSTRYTYPES) {
2746  for (uint i = 0; i < NUM_INDUSTRYTYPES; i++) {
2748  if (indsp->enabled) {
2749  id = _sorted_industry_types[i];
2750  break;
2751  }
2752  }
2753  if (id >= NUM_INDUSTRYTYPES) return;
2754  }
2755 
2757  if (w != NULL) {
2758  w->InvalidateData(id);
2759  return;
2760  }
2761  new IndustryCargoesWindow(id);
2762 }
2763 
2766 {
2768 }
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:111
virtual void OnHover(Point pt, int widget)
The mouse is hovering over a widget in the window, perform an action for it, like opening a custom to...
Functions related to OTTD&#39;s strings.
void NotifySmallmap()
Notify smallmap that new displayed industries have been selected (in _displayed_industries).
void GenerateIndustries()
This function will create random industries during game creation.
static const int HOR_CARGO_BORDER_SPACE
Amount of space between the left/right edge of a CFT_CARGO field, and the left/right most vertical ca...
Functions/types related to NewGRF debugging.
static void Swap(T &a, T &b)
Type safe swap operation.
Definition: math_func.hpp:277
Transfer storage of cargo suffix information.
Base types for having sorted lists in GUIs.
void RebuildDone()
Notify the sortlist that the rebuild is done.
Matrix of the industries.
static const uint MAX_CARGOES
Maximum number of cargoes carried in a CFT_CARGO field in CargoesField.
virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
Update size and resize step of a widget in the window.
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition: settings.cpp:77
Definition of stuff that is very close to a company, like the company struct itself.
bool _networking
are we in networking mode?
Definition: network.cpp:56
static int GetCargoTransportedPercentsIfValid(const Industry *i, uint id)
Returns percents of cargo transported if industry produces this cargo, else -1.
static uint minu(const uint a, const uint b)
Returns the minimum of two unsigned integers.
Definition: math_func.hpp:70
virtual void OnDropdownSelect(int widget, int index)
A dropdown option associated to this window has been selected.
bool enabled
entity still available (by default true).newgrf can disable it, though
Definition: industrytype.h:135
Build (fund or prospect) a new industry,.
void SortIndustryTypes()
Initialize the list of sorted industry types.
static const uint CALLBACK_FAILED
Different values for Callback result evaluations.
Data about how and where to blit pixels.
Definition: gfx_type.h:156
static const uint8 PC_WHITE
White palette colour.
Definition: gfx_func.h:210
Dropdown for the criteria of the sort.
Horizontally center the text.
Definition: gfx_func.h:99
static int CountMatchingAcceptingIndustries(const CargoID *cargoes, uint length)
Count how many industries have accepted cargoes in common with one of the supplied set...
static NWidgetPart SetResize(int16 dx, int16 dy)
Widget part function for setting the resize step.
Definition: widget_type.h:930
uint8 raw_industry_construction
type of (raw) industry construction (none, "normal", prospecting)
void GfxFillRect(int left, int top, int right, int bottom, int colour, FillRectMode mode)
Applies a certain FillRectMode-operation to a rectangle [left, right] x [top, bottom] on the screen...
Definition: gfx.cpp:112
Offset at right of a matrix cell.
Definition: window_gui.h:79
A normal unpaused game.
Definition: openttd.h:58
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:121
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:31
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.
uint16 last_month_production[2]
total units produced per cargo in the last full month
Definition: industry.h:51
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.
static void GetAllCargoSuffixes(uint cb_offset, 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)
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:1065
uint32 GetIndustryProbabilityCallback(IndustryType type, IndustryAvailabilityCallType creation_type, uint32 default_prob)
Check with callback CBID_INDUSTRY_PROBABILITY whether the industry can be built.
CargoSuffixDisplay
Ways of displaying the cargo.
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:742
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:32
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.
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:133
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.
byte cargo_acceptance[3]
acceptance level for the cargo slots
Definition: house.h:107
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:30
Defines the internal data of a functional industry.
Definition: industry.h:39
static const int BLOB_HEIGHT
Height of the industry legend colour, including border.
DifficultySettings difficulty
settings related to the difficulty
virtual void SetStringParameters(int widget) const
Initialize string parameters for a widget.
virtual void DrawWidget(const Rect &r, int widget) const
Draw the contents of a nested widget.
Industry-directory window.
Tindex index
Index of this pool item.
Definition: pool_type.hpp:147
static const int DAY_TICKS
1 day is 74 ticks; _date_fract used to be uint16 and incremented by 885.
Definition: date_type.h:30
Close box (at top-left of a window)
Definition: widget_type.h:69
Offset at top of a matrix cell.
Definition: window_gui.h:80
InfoLine
Specific lines in the info panel.
View-industry window.
Display cargo labels.
#define lastof(x)
Get the last element of an fixed size array.
Definition: depend.cpp:50
virtual void OnPlaceObjectAbort()
The user cancelled a tile highlight mode that has been set.
StringID GetGRFStringID(uint32 grfid, StringID stringid)
Returns the index for this stringid associated with its grfID.
Functions related to world/map generation.
static void ShowIndustryCargoesWindow(IndustryType id)
Open the industry and cargoes window.
Stuff related to the text buffer GUI.
static const int MATRIX_TEXT_OFFSET
The offset for the text in the matrix.
void InitializeViewport(Window *w, uint32 follow_flags, ZoomLevel zoom)
Initialize the viewport of the window.
Definition: widget.cpp:1927
static int CountMatchingProducingIndustries(const CargoID *cargoes, uint length)
Count how many industries have produced cargoes in common with one of the supplied set...
bool persistent_buildingtools
keep the building tools active after usage
void ComputeCargoDisplay(CargoID cid)
Compute what and where to display for cargo id cid.
Common return value for all commands.
Definition: command_type.h:25
Dimension ind_textsize
Size to hold any industry type text, as well as STR_INDUSTRY_CARGOES_SELECT_INDUSTRY.
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:33
uint16 incoming_cargo_waiting[3]
incoming cargo waiting to be processed
Definition: industry.h:44
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:2341
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:122
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:71
bool NeedRebuild() const
Check if a rebuild is needed.
bool FillDrawPixelInfo(DrawPixelInfo *n, int left, int top, int width, int height)
Set up a clipping area for only drawing into a certain area.
Definition: gfx.cpp:1480
Called to determine more text in the fund industry window.
Display the cargo and amount (if useful), but no sub-type (cb37 result 400 or fail).
static uint ClampU(const uint a, const uint min, const uint max)
Clamp an unsigned integer between an interval.
Definition: math_func.hpp:184
virtual void OnResize()
Called after the window got resized.
T * Append(uint to_add=1)
Append an item and return it.
StringID GetIndustryString(const Industry *i) const
Get the StringID to draw and set the appropriate DParams.
The list of industries.
HouseZones building_availability
where can it be built (climates, zones)
Definition: house.h:110
Class to backup a specific variable and restore it later.
Definition: backup_type.hpp:23
Info panel about the industry.
void SetCount(int num)
Sets the number of elements in the list.
Definition: widget_type.h:670
CompanyByte _local_company
Company controlled by the human player at this client. Can also be COMPANY_SPECTATOR.
Definition: company_cmd.cpp:46
Partial widget specification to allow NWidgets to be written nested.
Definition: widget_type.h:910
Functions related to (drawing on) viewports.
Pseudo random number generator.
void ForceRebuild()
Force that a rebuild is needed.
Data structure for an opened window.
Definition: window_gui.h:271
bool _ctrl_pressed
Is Ctrl pressed?
Definition: gfx.cpp:36
static const int CARGO_FIELD_WIDTH
Width of a CFT_CARGO field.
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:3238
virtual void OnInvalidateData(int data=0, bool gui_scope=true)
Some data on this window has become invalid.
void ShowErrorMessage(StringID summary_msg, StringID detailed_msg, WarningLevel wl, int x=0, int y=0, const GRFFile *textref_stack_grffile=NULL, uint textref_stack_size=0, const uint32 *textref_stack=NULL)
Display an error message in a window.
Definition: error_gui.cpp:378
Bottom offset of the text of the frame.
Definition: window_gui.h:75
Header of Action 04 "universal holder" structure and functions.
void SetDParamStr(uint n, const char *str)
This function is used to "bind" a C string to a OpenTTD dparam slot.
Definition: strings.cpp:279
virtual void OnClick(Point pt, int widget, int click_count)
A click with the left mouse button has been made on the window.
Header text.
Functions related to low-level strings.
Types related to cheating.
virtual void OnInit()
Notification that the nested widget tree gets initialized.
StringID header
Header text (for CFT_HEADER).
virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
Update size and resize step of a widget in the window.
Functions related to errors.
Offset at bottom of a matrix cell.
Definition: window_gui.h:81
Default window size box (at top-right of a window, between WWT_SHADEBOX and WWT_STICKYBOX) ...
Definition: widget_type.h:65
bool IsRawIndustry() const
Is an industry with the spec a raw industry?
uint Length() const
Get the number of items in the list.
int GetScrolledRowFromWidget(int clickpos, const Window *const w, int widget, int padding=0, int line_height=-1) const
Compute the row of a scrolled widget that a user clicked in.
Definition: widget.cpp:1959
Caption of the window.
uint pos_y
Vertical position of top-left corner of the widget in the window.
Definition: widget_type.h:178
This window is used for construction; close it whenever changing company.
Definition: window_gui.h:210
static int CDECL IndustryProductionSorter(const Industry *const *a, const Industry *const *b)
Sort industries by production and name.
Fill rectangle with a single colour.
Definition: gfx_type.h:283
SoundSettings sound
sound effect settings
virtual void OnTimeout()
Called when this window&#39;s timeout has been reached.
Listing GetListing() const
Export current sort conditions.
static const NWidgetPart _nested_industry_cargoes_widgets[]
Widgets of the industry cargoes window.
uint current_y
Current vertical size (after resizing).
Definition: widget_type.h:175
Sort descending.
Definition: window_gui.h:227
void MakeCargoLabel(int column, bool accepting)
Construct a CFT_CARGO_LABEL field.
StringID GetErrorMessage() const
Returns the error message of a command.
Definition: command_type.h:142
Caption of the window.
Small map; Window numbers:
Definition: window_type.h:99
#define FONT_HEIGHT_NORMAL
Height of characters in the normal (FS_NORMAL) font.
Definition: gfx_func.h:180
static NWidgetPart SetDataTip(uint32 data, StringID tip)
Widget part function for setting the data and tooltip.
Definition: widget_type.h:1014
void CcBuildIndustry(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2)
Command callback.
Simple vector template class, with automatic delete.
CargoSuffixDisplay display
How to display the cargo and text.
ClientSettings _settings_client
The current settings for this game.
Definition: settings.cpp:76
static NWidgetPart SetMinimalSize(int16 x, int16 y)
Widget part function for setting the minimal size.
Definition: widget_type.h:947
virtual void OnResize()
Called after the window got resized.
bool Succeeded() const
Did this command succeed?
Definition: command_type.h:152
const IndustrySpec * GetIndustrySpec(IndustryType thistype)
Accessor for array _industry_specs.
Data about a single field in the IndustryCargoesWindow panel.
Definition of base types and functions in a cross-platform compatible way.
TileIndex GetCenterTile() const
Get the center tile.
Definition: tilearea_type.h:57
char text[512]
Cargo suffix text.
bool UsesSmoothEconomy() const
Determines whether this industrytype uses smooth economy or whether it uses standard/newgrf productio...
static int CDECL IndustryNameSorter(const Industry *const *a, const Industry *const *b)
Sort industries by name.
A number of safeguards to prevent using unsafe methods.
CargoesFieldType
Available types of field.
bool value
tells if the bool cheat is active or not
Definition: cheat_type.h:20
static void DrawHorConnection(int left, int right, int top, const CargoSpec *csp)
Draw a horizontal cargo connection.
IndustryType type
type of industry.
Definition: industry.h:55
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
CargoID accepts_cargo[3]
3 input cargo slots
Definition: industry.h:47
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.
CargoID accepts_cargo[3]
3 input cargo slots
Definition: house.h:108
TileArea location
Location of the industry.
Definition: industry.h:40
additional text in industry window
static const int BLOB_DISTANCE
Distance of the industry legend colour from the edge of the industry box.
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.
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:101
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
CargoID produced_cargo[2]
2 production cargo slots
Definition: industry.h:42
Baseclass for nested widgets.
Definition: widget_type.h:126
Money GetConstructionCost() const
Get the cost for constructing this industry.
int DrawString(int left, int right, int top, const char *str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly truncated to make it fit in its allocated space.
Definition: gfx.cpp:499
additional text in fund window
Basic functions/variables used all over the place.
static bool HasCommonValidCargo(const CargoID *cargoes1, uint length1, const CargoID *cargoes2, uint length2)
Do the two sets of cargoes have a valid cargo in common?
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:527
Industry view; Window numbers:
Definition: window_type.h:358
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
PauseModeByte _pause_mode
The current pause mode.
Definition: gfx.cpp:48
Invalid cargo type.
Definition: cargo_type.h:70
byte prod_level
general production level
Definition: industry.h:46
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:136
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:2257
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:18
12 1000 can appear in temperate climate
Definition: house.h:80
byte last_month_pct_transported[2]
percentage transported per cargo in the last full month
Definition: industry.h:50
virtual void OnDropdownSelect(int widget, int index)
A dropdown option associated to this window has been selected.
IndustryType ind_type
Industry type (NUM_INDUSTRYTYPES means &#39;houses&#39;).
Empty field.
static const int HOR_CARGO_SPACE
Amount of horizontal space between two vertical cargoes.
Display then cargo, amount, and string (cb37 result 000-3FF).
int GetCargoBase(int xpos) const
For a CFT_CARGO, compute the left position of the left-most vertical cargo connection.
static int normal_height
Height of the non-header rows.
Build industry; Window numbers:
Definition: window_type.h:430
void ShowExtraViewPortWindow(TileIndex tile=INVALID_TILE)
Show a new Extra Viewport window.
Allow changing the production multiplier.
Dimension GetStringBoundingBox(const char *str, FontSize start_fontsize)
Return the string dimension in pixels.
Definition: gfx.cpp:699
Maximal number of cargo types in a game.
Definition: cargo_type.h:66
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:82
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:81
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:103
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:83
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:569
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:103
#define cpp_lengthof(base, variable)
Gets the length of an array variable within a class.
Definition: stdafx.h:456
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.
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.
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:1121
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:2268
byte production_rate[2]
production rate for each cargo
Definition: industry.h:45
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.
CargoID accepts_cargo[3]
3 accepted cargoes.
Definition: industrytype.h:116
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.
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
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:366
int32 WindowNumber
Number to differentiate different windows of the same class.
Definition: window_type.h:695
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:3211
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.
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:1234
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:3161
struct CargoesField::@17::@20 cargo_label
Label data (for CFT_CARGO_LABEL).
void StopTextRefStackUsage()
Stop using the TTDP compatible string code parsing.
virtual void OnTick()
Called once per (game) tick.
int DrawStringMultiLine(int left, int right, int top, int bottom, const char *str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly over multiple lines.
Definition: gfx.cpp:620
(Toggle) Button with text
Definition: widget_type.h:55
static const int VERT_CARGO_EDGE
Amount of vertical space between top/bottom and the top/bottom connected cargo at an industry...
static void MemSetT(T *ptr, byte value, size_t num=1)
Type-safe version of memset().
Definition: mem_func.hpp:51
Cheats _cheats
All the cheats.
Definition: cheat.cpp:18
uint16 GetIndustryCallback(CallbackID callback, uint32 param1, uint32 param2, Industry *industry, IndustryType type, TileIndex tile)
Perform an industry callback.
int selected_index
index of the element in the matrix
11 800 can appear in sub-arctic climate above the snow line
Definition: house.h:79
uint16 GetPosition() const
Gets the position of the first visible element in the list.
Definition: widget_type.h:631
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