OpenTTD Source  20241120-master-g6d3adc6169
graph_gui.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of OpenTTD.
3  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6  */
7 
10 #include "stdafx.h"
11 #include "graph_gui.h"
12 #include "window_gui.h"
13 #include "company_base.h"
14 #include "company_gui.h"
15 #include "economy_func.h"
16 #include "cargotype.h"
17 #include "strings_func.h"
18 #include "window_func.h"
19 #include "gfx_func.h"
20 #include "core/geometry_func.hpp"
21 #include "currency.h"
22 #include "timer/timer.h"
23 #include "timer/timer_window.h"
24 #include "timer/timer_game_tick.h"
26 #include "zoom_func.h"
27 #include "industry.h"
28 
29 #include "widgets/graph_widget.h"
30 
31 #include "table/strings.h"
32 #include "table/sprites.h"
33 
34 #include "safeguards.h"
35 
36 /* Bitmasks of company and cargo indices that shouldn't be drawn. */
37 static CompanyMask _legend_excluded_companies;
38 static CargoTypes _legend_excluded_cargo_payment_rates;
39 static CargoTypes _legend_excluded_cargo_production_history;
40 
41 /* Apparently these don't play well with enums. */
42 static const OverflowSafeInt64 INVALID_DATAPOINT(INT64_MAX); // Value used for a datapoint that shouldn't be drawn.
43 static const uint INVALID_DATAPOINT_POS = UINT_MAX; // Used to determine if the previous point was drawn.
44 
45 constexpr double INT64_MAX_IN_DOUBLE = static_cast<double>(INT64_MAX - 512);
46 static_assert(static_cast<int64_t>(INT64_MAX_IN_DOUBLE) < INT64_MAX);
47 
48 /****************/
49 /* GRAPH LEGEND */
50 /****************/
51 
54  {
55  this->InitNested(window_number);
56 
57  for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
58  if (!HasBit(_legend_excluded_companies, c)) this->LowerWidget(WID_GL_FIRST_COMPANY + c);
59 
60  this->OnInvalidateData(c);
61  }
62  }
63 
64  void DrawWidget(const Rect &r, WidgetID widget) const override
65  {
67 
68  CompanyID cid = (CompanyID)(widget - WID_GL_FIRST_COMPANY);
69 
70  if (!Company::IsValidID(cid)) return;
71 
72  bool rtl = _current_text_dir == TD_RTL;
73 
74  const Rect ir = r.Shrink(WidgetDimensions::scaled.framerect);
75  Dimension d = GetSpriteSize(SPR_COMPANY_ICON);
76  DrawCompanyIcon(cid, rtl ? ir.right - d.width : ir.left, CenterBounds(ir.top, ir.bottom, d.height));
77 
78  const Rect tr = ir.Indent(d.width + WidgetDimensions::scaled.hsep_normal, rtl);
79  SetDParam(0, cid);
80  SetDParam(1, cid);
81  DrawString(tr.left, tr.right, CenterBounds(tr.top, tr.bottom, GetCharacterHeight(FS_NORMAL)), STR_COMPANY_NAME_COMPANY_NUM, HasBit(_legend_excluded_companies, cid) ? TC_BLACK : TC_WHITE);
82  }
83 
84  void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
85  {
87 
88  ToggleBit(_legend_excluded_companies, widget - WID_GL_FIRST_COMPANY);
89  this->ToggleWidgetLoweredState(widget);
90  this->SetDirty();
96  }
97 
103  void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
104  {
105  if (!gui_scope) return;
106  if (Company::IsValidID(data)) return;
107 
108  SetBit(_legend_excluded_companies, data);
109  this->RaiseWidget(data + WID_GL_FIRST_COMPANY);
110  }
111 };
112 
117 static std::unique_ptr<NWidgetBase> MakeNWidgetCompanyLines()
118 {
119  auto vert = std::make_unique<NWidgetVertical>(NC_EQUALSIZE);
120  vert->SetPadding(2, 2, 2, 2);
121  uint sprite_height = GetSpriteSize(SPR_COMPANY_ICON, nullptr, ZOOM_LVL_NORMAL).height;
122 
123  for (WidgetID widnum = WID_GL_FIRST_COMPANY; widnum <= WID_GL_LAST_COMPANY; widnum++) {
124  auto panel = std::make_unique<NWidgetBackground>(WWT_PANEL, COLOUR_BROWN, widnum);
125  panel->SetMinimalSize(246, sprite_height + WidgetDimensions::unscaled.framerect.Vertical());
126  panel->SetMinimalTextLines(1, WidgetDimensions::unscaled.framerect.Vertical(), FS_NORMAL);
127  panel->SetFill(1, 1);
128  panel->SetDataTip(0x0, STR_GRAPH_KEY_COMPANY_SELECTION_TOOLTIP);
129  vert->Add(std::move(panel));
130  }
131  return vert;
132 }
133 
134 static constexpr NWidgetPart _nested_graph_legend_widgets[] = {
136  NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
137  NWidget(WWT_CAPTION, COLOUR_BROWN), SetDataTip(STR_GRAPH_KEY_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
138  NWidget(WWT_SHADEBOX, COLOUR_BROWN),
139  NWidget(WWT_STICKYBOX, COLOUR_BROWN),
140  EndContainer(),
141  NWidget(WWT_PANEL, COLOUR_BROWN, WID_GL_BACKGROUND),
143  EndContainer(),
144 };
145 
146 static WindowDesc _graph_legend_desc(
147  WDP_AUTO, "graph_legend", 0, 0,
149  0,
150  _nested_graph_legend_widgets
151 );
152 
153 static void ShowGraphLegend()
154 {
155  AllocateWindowDescFront<GraphLegendWindow>(_graph_legend_desc, 0);
156 }
157 
162 };
163 
164 /******************/
165 /* BASE OF GRAPHS */
166 /*****************/
167 
169 protected:
170  static const int GRAPH_MAX_DATASETS = 64;
171  static const int GRAPH_BASE_COLOUR = GREY_SCALE(2);
172  static const int GRAPH_GRID_COLOUR = GREY_SCALE(3);
173  static const int GRAPH_AXIS_LINE_COLOUR = GREY_SCALE(1);
174  static const int GRAPH_ZERO_LINE_COLOUR = GREY_SCALE(8);
175  static const int GRAPH_YEAR_LINE_COLOUR = GREY_SCALE(5);
176  static const int GRAPH_NUM_MONTHS = 24;
177  static const int PAYMENT_GRAPH_X_STEP_DAYS = 10;
178  static const int PAYMENT_GRAPH_X_STEP_SECONDS = 20;
179  static const int ECONOMY_QUARTER_MINUTES = 3;
180  static const int ECONOMY_MONTH_MINUTES = 1;
181 
182  static const TextColour GRAPH_AXIS_LABEL_COLOUR = TC_BLACK;
183 
184  static const int MIN_GRAPH_NUM_LINES_Y = 9;
185  static const int MIN_GRID_PIXEL_SIZE = 20;
186 
187  uint64_t excluded_data;
188  uint8_t num_dataset;
189  uint8_t num_on_x_axis;
190  uint8_t num_vert_lines;
191 
192  /* The starting month and year that values are plotted against. */
195  uint8_t month_increment;
196 
197  bool draw_dates = true;
198 
199  /* These values are used if the graph is being plotted against values
200  * rather than the dates specified by month and year. */
201  uint16_t x_values_start;
202  uint16_t x_values_increment;
203 
204  StringID format_str_y_axis;
205  uint8_t colours[GRAPH_MAX_DATASETS];
206  OverflowSafeInt64 cost[GRAPH_MAX_DATASETS][GRAPH_NUM_MONTHS];
207 
214  ValuesInterval GetValuesInterval(int num_hori_lines) const
215  {
216  assert(num_hori_lines > 0);
217 
218  ValuesInterval current_interval;
219  current_interval.highest = INT64_MIN;
220  current_interval.lowest = INT64_MAX;
221 
222  for (int i = 0; i < this->num_dataset; i++) {
223  if (HasBit(this->excluded_data, i)) continue;
224  for (int j = 0; j < this->num_on_x_axis; j++) {
225  OverflowSafeInt64 datapoint = this->cost[i][j];
226 
227  if (datapoint != INVALID_DATAPOINT) {
228  current_interval.highest = std::max(current_interval.highest, datapoint);
229  current_interval.lowest = std::min(current_interval.lowest, datapoint);
230  }
231  }
232  }
233 
234  /* Always include zero in the shown range. */
235  double abs_lower = (current_interval.lowest > 0) ? 0 : (double)abs(current_interval.lowest);
236  double abs_higher = (current_interval.highest < 0) ? 0 : (double)current_interval.highest;
237 
238  /* Prevent showing values too close to the graph limits. */
239  abs_higher = (11.0 * abs_higher) / 10.0;
240  abs_lower = (11.0 * abs_lower) / 10.0;
241 
242  int num_pos_grids;
243  OverflowSafeInt64 grid_size;
244 
245  if (abs_lower != 0 || abs_higher != 0) {
246  /* The number of grids to reserve for the positive part is: */
247  num_pos_grids = (int)floor(0.5 + num_hori_lines * abs_higher / (abs_higher + abs_lower));
248 
249  /* If there are any positive or negative values, force that they have at least one grid. */
250  if (num_pos_grids == 0 && abs_higher != 0) num_pos_grids++;
251  if (num_pos_grids == num_hori_lines && abs_lower != 0) num_pos_grids--;
252 
253  /* Get the required grid size for each side and use the maximum one. */
254 
255  OverflowSafeInt64 grid_size_higher = 0;
256  if (abs_higher > 0) {
257  grid_size_higher = abs_higher > INT64_MAX_IN_DOUBLE ? INT64_MAX : static_cast<int64_t>(abs_higher);
258  grid_size_higher = (grid_size_higher + num_pos_grids - 1) / num_pos_grids;
259  }
260 
261  OverflowSafeInt64 grid_size_lower = 0;
262  if (abs_lower > 0) {
263  grid_size_lower = abs_lower > INT64_MAX_IN_DOUBLE ? INT64_MAX : static_cast<int64_t>(abs_lower);
264  grid_size_lower = (grid_size_lower + num_hori_lines - num_pos_grids - 1) / (num_hori_lines - num_pos_grids);
265  }
266 
267  grid_size = std::max(grid_size_higher, grid_size_lower);
268  } else {
269  /* If both values are zero, show an empty graph. */
270  num_pos_grids = num_hori_lines / 2;
271  grid_size = 1;
272  }
273 
274  current_interval.highest = num_pos_grids * grid_size;
275  current_interval.lowest = -(num_hori_lines - num_pos_grids) * grid_size;
276  return current_interval;
277  }
278 
284  uint GetYLabelWidth(ValuesInterval current_interval, int num_hori_lines) const
285  {
286  /* draw text strings on the y axis */
287  int64_t y_label = current_interval.highest;
288  int64_t y_label_separation = (current_interval.highest - current_interval.lowest) / num_hori_lines;
289 
290  uint max_width = 0;
291 
292  for (int i = 0; i < (num_hori_lines + 1); i++) {
293  SetDParam(0, this->format_str_y_axis);
294  SetDParam(1, y_label);
295  Dimension d = GetStringBoundingBox(STR_GRAPH_Y_LABEL);
296  if (d.width > max_width) max_width = d.width;
297 
298  y_label -= y_label_separation;
299  }
300 
301  return max_width;
302  }
303 
308  void DrawGraph(Rect r) const
309  {
310  uint x, y;
311  ValuesInterval interval;
312  int x_axis_offset;
313 
314  /* the colours and cost array of GraphDrawer must accommodate
315  * both values for cargo and companies. So if any are higher, quit */
316  static_assert(GRAPH_MAX_DATASETS >= (int)NUM_CARGO && GRAPH_MAX_DATASETS >= (int)MAX_COMPANIES);
317  assert(this->num_vert_lines > 0);
318 
319  /* Rect r will be adjusted to contain just the graph, with labels being
320  * placed outside the area. */
321  r.top += ScaleGUITrad(5) + GetCharacterHeight(FS_SMALL) / 2;
322  r.bottom -= (this->draw_dates ? 2 : 1) * GetCharacterHeight(FS_SMALL) + ScaleGUITrad(4);
323  r.left += ScaleGUITrad(9);
324  r.right -= ScaleGUITrad(5);
325 
326  /* Initial number of horizontal lines. */
327  int num_hori_lines = 160 / ScaleGUITrad(MIN_GRID_PIXEL_SIZE);
328  /* For the rest of the height, the number of horizontal lines will increase more slowly. */
329  int resize = (r.bottom - r.top - 160) / (2 * ScaleGUITrad(MIN_GRID_PIXEL_SIZE));
330  if (resize > 0) num_hori_lines += resize;
331 
332  interval = GetValuesInterval(num_hori_lines);
333 
334  int label_width = GetYLabelWidth(interval, num_hori_lines);
335 
336  r.left += label_width;
337 
338  int x_sep = (r.right - r.left) / this->num_vert_lines;
339  int y_sep = (r.bottom - r.top) / num_hori_lines;
340 
341  /* Redetermine right and bottom edge of graph to fit with the integer
342  * separation values. */
343  r.right = r.left + x_sep * this->num_vert_lines;
344  r.bottom = r.top + y_sep * num_hori_lines;
345 
346  OverflowSafeInt64 interval_size = interval.highest + abs(interval.lowest);
347  /* Where to draw the X axis. Use floating point to avoid overflowing and results of zero. */
348  x_axis_offset = (int)((r.bottom - r.top) * (double)interval.highest / (double)interval_size);
349 
350  /* Draw the background of the graph itself. */
351  GfxFillRect(r.left, r.top, r.right, r.bottom, GRAPH_BASE_COLOUR);
352 
353  /* Draw the vertical grid lines. */
354 
355  /* Don't draw the first line, as that's where the axis will be. */
356  x = r.left + x_sep;
357 
358  int grid_colour = GRAPH_GRID_COLOUR;
359  for (int i = 1; i < this->num_vert_lines + 1; i++) {
360  /* If using wallclock units, we separate periods with a lighter line. */
362  grid_colour = (i % 4 == 0) ? GRAPH_YEAR_LINE_COLOUR : GRAPH_GRID_COLOUR;
363  }
364  GfxFillRect(x, r.top, x, r.bottom, grid_colour);
365  x += x_sep;
366  }
367 
368  /* Draw the horizontal grid lines. */
369  y = r.bottom;
370 
371  for (int i = 0; i < (num_hori_lines + 1); i++) {
372  GfxFillRect(r.left - ScaleGUITrad(3), y, r.left - 1, y, GRAPH_AXIS_LINE_COLOUR);
373  GfxFillRect(r.left, y, r.right, y, GRAPH_GRID_COLOUR);
374  y -= y_sep;
375  }
376 
377  /* Draw the y axis. */
378  GfxFillRect(r.left, r.top, r.left, r.bottom, GRAPH_AXIS_LINE_COLOUR);
379 
380  /* Draw the x axis. */
381  y = x_axis_offset + r.top;
382  GfxFillRect(r.left, y, r.right, y, GRAPH_ZERO_LINE_COLOUR);
383 
384  /* Find the largest value that will be drawn. */
385  if (this->num_on_x_axis == 0) return;
386 
387  assert(this->num_on_x_axis > 0);
388 
389  /* draw text strings on the y axis */
390  int64_t y_label = interval.highest;
391  int64_t y_label_separation = abs(interval.highest - interval.lowest) / num_hori_lines;
392 
393  y = r.top - GetCharacterHeight(FS_SMALL) / 2;
394 
395  for (int i = 0; i < (num_hori_lines + 1); i++) {
396  SetDParam(0, this->format_str_y_axis);
397  SetDParam(1, y_label);
398  DrawString(r.left - label_width - ScaleGUITrad(4), r.left - ScaleGUITrad(4), y, STR_GRAPH_Y_LABEL, GRAPH_AXIS_LABEL_COLOUR, SA_RIGHT);
399 
400  y_label -= y_label_separation;
401  y += y_sep;
402  }
403 
404  /* Draw x-axis labels and markings for graphs based on financial quarters and years. */
405  if (this->draw_dates) {
406  x = r.left;
407  y = r.bottom + ScaleGUITrad(2);
408  TimerGameEconomy::Month month = this->month;
409  TimerGameEconomy::Year year = this->year;
410  for (int i = 0; i < this->num_on_x_axis; i++) {
411  SetDParam(0, month + STR_MONTH_ABBREV_JAN);
412  SetDParam(1, year);
413  DrawStringMultiLine(x, x + x_sep, y, this->height, month == 0 ? STR_GRAPH_X_LABEL_MONTH_YEAR : STR_GRAPH_X_LABEL_MONTH, GRAPH_AXIS_LABEL_COLOUR, SA_LEFT);
414 
415  month += this->month_increment;
416  if (month >= 12) {
417  month = 0;
418  year++;
419 
420  /* Draw a lighter grid line between years. Top and bottom adjustments ensure we don't draw over top and bottom horizontal grid lines. */
421  GfxFillRect(x + x_sep, r.top + 1, x + x_sep, r.bottom - 1, GRAPH_YEAR_LINE_COLOUR);
422  }
423  x += x_sep;
424  }
425  } else {
426  /* Draw x-axis labels for graphs not based on quarterly performance (cargo payment rates, and all graphs when using wallclock units). */
427  x = r.left;
428  y = r.bottom + ScaleGUITrad(2);
429  uint16_t label = this->x_values_start;
430 
431  for (int i = 0; i < this->num_on_x_axis; i++) {
432  SetDParam(0, label);
433  DrawString(x + 1, x + x_sep - 1, y, STR_GRAPH_Y_LABEL_NUMBER, GRAPH_AXIS_LABEL_COLOUR, SA_HOR_CENTER);
434 
435  label += this->x_values_increment;
436  x += x_sep;
437  }
438  }
439 
440  /* draw lines and dots */
441  uint linewidth = _settings_client.gui.graph_line_thickness;
442  uint pointoffs1 = (linewidth + 1) / 2;
443  uint pointoffs2 = linewidth + 1 - pointoffs1;
444  for (int i = 0; i < this->num_dataset; i++) {
445  if (!HasBit(this->excluded_data, i)) {
446  /* Centre the dot between the grid lines. */
447  x = r.left + (x_sep / 2);
448 
449  uint8_t colour = this->colours[i];
450  uint prev_x = INVALID_DATAPOINT_POS;
451  uint prev_y = INVALID_DATAPOINT_POS;
452 
453  for (int j = 0; j < this->num_on_x_axis; j++) {
454  OverflowSafeInt64 datapoint = this->cost[i][j];
455 
456  if (datapoint != INVALID_DATAPOINT) {
457  /*
458  * Check whether we need to reduce the 'accuracy' of the
459  * datapoint value and the highest value to split overflows.
460  * And when 'drawing' 'one million' or 'one million and one'
461  * there is no significant difference, so the least
462  * significant bits can just be removed.
463  *
464  * If there are more bits needed than would fit in a 32 bits
465  * integer, so at about 31 bits because of the sign bit, the
466  * least significant bits are removed.
467  */
468  int mult_range = FindLastBit<uint32_t>(x_axis_offset) + FindLastBit<uint64_t>(abs(datapoint));
469  int reduce_range = std::max(mult_range - 31, 0);
470 
471  /* Handle negative values differently (don't shift sign) */
472  if (datapoint < 0) {
473  datapoint = -(abs(datapoint) >> reduce_range);
474  } else {
475  datapoint >>= reduce_range;
476  }
477  y = r.top + x_axis_offset - ((r.bottom - r.top) * datapoint) / (interval_size >> reduce_range);
478 
479  /* Draw the point. */
480  GfxFillRect(x - pointoffs1, y - pointoffs1, x + pointoffs2, y + pointoffs2, colour);
481 
482  /* Draw the line connected to the previous point. */
483  if (prev_x != INVALID_DATAPOINT_POS) GfxDrawLine(prev_x, prev_y, x, y, colour, linewidth);
484 
485  prev_x = x;
486  prev_y = y;
487  } else {
488  prev_x = INVALID_DATAPOINT_POS;
489  prev_y = INVALID_DATAPOINT_POS;
490  }
491 
492  x += x_sep;
493  }
494  }
495  }
496  }
497 
498 
499  BaseGraphWindow(WindowDesc &desc, StringID format_str_y_axis) :
500  Window(desc),
501  format_str_y_axis(format_str_y_axis)
502  {
504  this->num_vert_lines = GRAPH_NUM_MONTHS;
505  this->month_increment = 3;
506  }
507 
508  void InitializeWindow(WindowNumber number)
509  {
510  /* Initialise the dataset */
511  this->UpdateStatistics(true);
512 
513  this->CreateNestedTree();
514 
515  auto *wid = this->GetWidget<NWidgetCore>(WID_GRAPH_FOOTER);
516  if (wid != nullptr && TimerGameEconomy::UsingWallclockUnits()) {
517  wid->SetDataTip(STR_GRAPH_LAST_72_MINUTES_TIME_LABEL, STR_NULL);
518  }
519 
520  this->FinishInitNested(number);
521  }
522 
523 public:
524  void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
525  {
526  if (widget != WID_GRAPH_GRAPH) return;
527 
528  uint x_label_width = 0;
529 
530  /* Draw x-axis labels and markings for graphs based on financial quarters and years. */
531  if (this->draw_dates) {
532  TimerGameEconomy::Month month = this->month;
533  TimerGameEconomy::Year year = this->year;
534  for (int i = 0; i < this->num_on_x_axis; i++) {
535  SetDParam(0, month + STR_MONTH_ABBREV_JAN);
536  SetDParam(1, year);
537  x_label_width = std::max(x_label_width, GetStringBoundingBox(month == 0 ? STR_GRAPH_X_LABEL_MONTH_YEAR : STR_GRAPH_X_LABEL_MONTH).width);
538 
539  month += this->month_increment;
540  if (month >= 12) {
541  month = 0;
542  year++;
543  }
544  }
545  } else {
546  /* Draw x-axis labels for graphs not based on quarterly performance (cargo payment rates). */
547  SetDParamMaxValue(0, this->x_values_start + this->num_on_x_axis * this->x_values_increment, 0, FS_SMALL);
548  x_label_width = GetStringBoundingBox(STR_GRAPH_Y_LABEL_NUMBER).width;
549  }
550 
551  SetDParam(0, this->format_str_y_axis);
552  SetDParam(1, INT64_MAX);
553  uint y_label_width = GetStringBoundingBox(STR_GRAPH_Y_LABEL).width;
554 
555  size.width = std::max<uint>(size.width, ScaleGUITrad(5) + y_label_width + this->num_vert_lines * (x_label_width + ScaleGUITrad(5)) + ScaleGUITrad(9));
556  size.height = std::max<uint>(size.height, ScaleGUITrad(5) + (1 + MIN_GRAPH_NUM_LINES_Y * 2 + (this->draw_dates ? 3 : 1)) * GetCharacterHeight(FS_SMALL) + ScaleGUITrad(4));
557  size.height = std::max<uint>(size.height, size.width / 3);
558  }
559 
560  void DrawWidget(const Rect &r, WidgetID widget) const override
561  {
562  if (widget != WID_GRAPH_GRAPH) return;
563 
564  DrawGraph(r);
565  }
566 
567  virtual OverflowSafeInt64 GetGraphData(const Company *, int)
568  {
569  return INVALID_DATAPOINT;
570  }
571 
572  void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
573  {
574  /* Clicked on legend? */
575  if (widget == WID_GRAPH_KEY_BUTTON) ShowGraphLegend();
576  }
577 
578  void OnGameTick() override
579  {
580  this->UpdateStatistics(false);
581  }
582 
588  void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
589  {
590  if (!gui_scope) return;
591  this->UpdateStatistics(true);
592  }
593 
598  virtual void UpdateStatistics(bool initialize)
599  {
600  CompanyMask excluded_companies = _legend_excluded_companies;
601 
602  /* Exclude the companies which aren't valid */
603  for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
604  if (!Company::IsValidID(c)) SetBit(excluded_companies, c);
605  }
606 
607  uint8_t nums = 0;
608  for (const Company *c : Company::Iterate()) {
609  nums = std::min(this->num_vert_lines, std::max(nums, c->num_valid_stat_ent));
610  }
611 
612  int mo = (TimerGameEconomy::month / this->month_increment - nums) * this->month_increment;
613  auto yr = TimerGameEconomy::year;
614  while (mo < 0) {
615  yr--;
616  mo += 12;
617  }
618 
619  if (!initialize && this->excluded_data == excluded_companies && this->num_on_x_axis == nums &&
620  this->year == yr && this->month == mo) {
621  /* There's no reason to get new stats */
622  return;
623  }
624 
625  this->excluded_data = excluded_companies;
626  this->num_on_x_axis = nums;
627  this->year = yr;
628  this->month = mo;
629 
630  int numd = 0;
631  for (CompanyID k = COMPANY_FIRST; k < MAX_COMPANIES; k++) {
632  const Company *c = Company::GetIfValid(k);
633  if (c != nullptr) {
634  this->colours[numd] = GetColourGradient(c->colour, SHADE_LIGHTER);
635  for (int j = this->num_on_x_axis, i = 0; --j >= 0;) {
636  if (j >= c->num_valid_stat_ent) {
637  this->cost[numd][i] = INVALID_DATAPOINT;
638  } else {
639  /* Ensure we never assign INVALID_DATAPOINT, as that has another meaning.
640  * Instead, use the value just under it. Hopefully nobody will notice. */
641  this->cost[numd][i] = std::min(GetGraphData(c, j), INVALID_DATAPOINT - 1);
642  }
643  i++;
644  }
645  }
646  numd++;
647  }
648 
649  this->num_dataset = numd;
650  }
651 };
652 
653 
654 /********************/
655 /* OPERATING PROFIT */
656 /********************/
657 
660  BaseGraphWindow(desc, STR_JUST_CURRENCY_SHORT)
661  {
662  this->num_on_x_axis = GRAPH_NUM_MONTHS;
663  this->num_vert_lines = GRAPH_NUM_MONTHS;
664  this->x_values_start = ECONOMY_QUARTER_MINUTES;
665  this->x_values_increment = ECONOMY_QUARTER_MINUTES;
666  this->draw_dates = !TimerGameEconomy::UsingWallclockUnits();
667 
668  this->InitializeWindow(window_number);
669  }
670 
671  OverflowSafeInt64 GetGraphData(const Company *c, int j) override
672  {
673  return c->old_economy[j].income + c->old_economy[j].expenses;
674  }
675 };
676 
677 static constexpr NWidgetPart _nested_operating_profit_widgets[] = {
679  NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
680  NWidget(WWT_CAPTION, COLOUR_BROWN), SetDataTip(STR_GRAPH_OPERATING_PROFIT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
681  NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_KEY_BUTTON), SetMinimalSize(50, 0), SetDataTip(STR_GRAPH_KEY_BUTTON, STR_GRAPH_KEY_TOOLTIP),
682  NWidget(WWT_SHADEBOX, COLOUR_BROWN),
683  NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
684  NWidget(WWT_STICKYBOX, COLOUR_BROWN),
685  EndContainer(),
686  NWidget(WWT_PANEL, COLOUR_BROWN, WID_GRAPH_BACKGROUND),
688  NWidget(WWT_EMPTY, COLOUR_BROWN, WID_GRAPH_GRAPH), SetMinimalSize(576, 160), SetFill(1, 1), SetResize(1, 1),
690  NWidget(NWID_SPACER), SetMinimalSize(12, 0), SetFill(1, 0), SetResize(1, 0),
691  NWidget(WWT_TEXT, COLOUR_BROWN, WID_GRAPH_FOOTER), SetMinimalSize(0, 6), SetPadding(2, 0, 2, 0), SetDataTip(STR_EMPTY, STR_NULL),
692  NWidget(NWID_SPACER), SetFill(1, 0), SetResize(1, 0),
693  NWidget(WWT_RESIZEBOX, COLOUR_BROWN, WID_GRAPH_RESIZE), SetDataTip(RWV_HIDE_BEVEL, STR_TOOLTIP_RESIZE),
694  EndContainer(),
695  EndContainer(),
696  EndContainer(),
697 };
698 
699 static WindowDesc _operating_profit_desc(
700  WDP_AUTO, "graph_operating_profit", 0, 0,
702  0,
703  _nested_operating_profit_widgets
704 );
705 
706 
707 void ShowOperatingProfitGraph()
708 {
709  AllocateWindowDescFront<OperatingProfitGraphWindow>(_operating_profit_desc, 0);
710 }
711 
712 
713 /****************/
714 /* INCOME GRAPH */
715 /****************/
716 
719  BaseGraphWindow(desc, STR_JUST_CURRENCY_SHORT)
720  {
721  this->num_on_x_axis = GRAPH_NUM_MONTHS;
722  this->num_vert_lines = GRAPH_NUM_MONTHS;
723  this->x_values_start = ECONOMY_QUARTER_MINUTES;
724  this->x_values_increment = ECONOMY_QUARTER_MINUTES;
725  this->draw_dates = !TimerGameEconomy::UsingWallclockUnits();
726 
727  this->InitializeWindow(window_number);
728  }
729 
730  OverflowSafeInt64 GetGraphData(const Company *c, int j) override
731  {
732  return c->old_economy[j].income;
733  }
734 };
735 
736 static constexpr NWidgetPart _nested_income_graph_widgets[] = {
738  NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
739  NWidget(WWT_CAPTION, COLOUR_BROWN), SetDataTip(STR_GRAPH_INCOME_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
740  NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_KEY_BUTTON), SetMinimalSize(50, 0), SetDataTip(STR_GRAPH_KEY_BUTTON, STR_GRAPH_KEY_TOOLTIP),
741  NWidget(WWT_SHADEBOX, COLOUR_BROWN),
742  NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
743  NWidget(WWT_STICKYBOX, COLOUR_BROWN),
744  EndContainer(),
745  NWidget(WWT_PANEL, COLOUR_BROWN, WID_GRAPH_BACKGROUND),
747  NWidget(WWT_EMPTY, COLOUR_BROWN, WID_GRAPH_GRAPH), SetMinimalSize(576, 128), SetFill(1, 1), SetResize(1, 1),
749  NWidget(NWID_SPACER), SetMinimalSize(12, 0), SetFill(1, 0), SetResize(1, 0),
750  NWidget(WWT_TEXT, COLOUR_BROWN, WID_GRAPH_FOOTER), SetMinimalSize(0, 6), SetPadding(2, 0, 2, 0), SetDataTip(STR_EMPTY, STR_NULL),
751  NWidget(NWID_SPACER), SetFill(1, 0), SetResize(1, 0),
752  NWidget(WWT_RESIZEBOX, COLOUR_BROWN, WID_GRAPH_RESIZE), SetDataTip(RWV_HIDE_BEVEL, STR_TOOLTIP_RESIZE),
753  EndContainer(),
754  EndContainer(),
755  EndContainer(),
756 };
757 
758 static WindowDesc _income_graph_desc(
759  WDP_AUTO, "graph_income", 0, 0,
761  0,
762  _nested_income_graph_widgets
763 );
764 
765 void ShowIncomeGraph()
766 {
767  AllocateWindowDescFront<IncomeGraphWindow>(_income_graph_desc, 0);
768 }
769 
770 /*******************/
771 /* DELIVERED CARGO */
772 /*******************/
773 
776  BaseGraphWindow(desc, STR_JUST_COMMA)
777  {
778  this->num_on_x_axis = GRAPH_NUM_MONTHS;
779  this->num_vert_lines = GRAPH_NUM_MONTHS;
780  this->x_values_start = ECONOMY_QUARTER_MINUTES;
781  this->x_values_increment = ECONOMY_QUARTER_MINUTES;
782  this->draw_dates = !TimerGameEconomy::UsingWallclockUnits();
783 
784  this->InitializeWindow(window_number);
785  }
786 
787  OverflowSafeInt64 GetGraphData(const Company *c, int j) override
788  {
790  }
791 };
792 
793 static constexpr NWidgetPart _nested_delivered_cargo_graph_widgets[] = {
795  NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
796  NWidget(WWT_CAPTION, COLOUR_BROWN), SetDataTip(STR_GRAPH_CARGO_DELIVERED_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
797  NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_KEY_BUTTON), SetMinimalSize(50, 0), SetDataTip(STR_GRAPH_KEY_BUTTON, STR_GRAPH_KEY_TOOLTIP),
798  NWidget(WWT_SHADEBOX, COLOUR_BROWN),
799  NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
800  NWidget(WWT_STICKYBOX, COLOUR_BROWN),
801  EndContainer(),
802  NWidget(WWT_PANEL, COLOUR_BROWN, WID_GRAPH_BACKGROUND),
804  NWidget(WWT_EMPTY, COLOUR_BROWN, WID_GRAPH_GRAPH), SetMinimalSize(576, 128), SetFill(1, 1), SetResize(1, 1),
806  NWidget(NWID_SPACER), SetMinimalSize(12, 0), SetFill(1, 0), SetResize(1, 0),
807  NWidget(WWT_TEXT, COLOUR_BROWN, WID_GRAPH_FOOTER), SetMinimalSize(0, 6), SetPadding(2, 0, 2, 0), SetDataTip(STR_EMPTY, STR_NULL),
808  NWidget(NWID_SPACER), SetFill(1, 0), SetResize(1, 0),
809  NWidget(WWT_RESIZEBOX, COLOUR_BROWN, WID_GRAPH_RESIZE), SetDataTip(RWV_HIDE_BEVEL, STR_TOOLTIP_RESIZE),
810  EndContainer(),
811  EndContainer(),
812  EndContainer(),
813 };
814 
815 static WindowDesc _delivered_cargo_graph_desc(
816  WDP_AUTO, "graph_delivered_cargo", 0, 0,
818  0,
819  _nested_delivered_cargo_graph_widgets
820 );
821 
822 void ShowDeliveredCargoGraph()
823 {
824  AllocateWindowDescFront<DeliveredCargoGraphWindow>(_delivered_cargo_graph_desc, 0);
825 }
826 
827 /***********************/
828 /* PERFORMANCE HISTORY */
829 /***********************/
830 
833  BaseGraphWindow(desc, STR_JUST_COMMA)
834  {
835  this->num_on_x_axis = GRAPH_NUM_MONTHS;
836  this->num_vert_lines = GRAPH_NUM_MONTHS;
837  this->x_values_start = ECONOMY_QUARTER_MINUTES;
838  this->x_values_increment = ECONOMY_QUARTER_MINUTES;
839  this->draw_dates = !TimerGameEconomy::UsingWallclockUnits();
840 
841  this->InitializeWindow(window_number);
842  }
843 
844  OverflowSafeInt64 GetGraphData(const Company *c, int j) override
845  {
846  return c->old_economy[j].performance_history;
847  }
848 
849  void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
850  {
851  if (widget == WID_PHG_DETAILED_PERFORMANCE) ShowPerformanceRatingDetail();
852  this->BaseGraphWindow::OnClick(pt, widget, click_count);
853  }
854 };
855 
856 static constexpr NWidgetPart _nested_performance_history_widgets[] = {
858  NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
859  NWidget(WWT_CAPTION, COLOUR_BROWN), SetDataTip(STR_GRAPH_COMPANY_PERFORMANCE_RATINGS_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
860  NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_PHG_DETAILED_PERFORMANCE), SetMinimalSize(50, 0), SetDataTip(STR_PERFORMANCE_DETAIL_KEY, STR_GRAPH_PERFORMANCE_DETAIL_TOOLTIP),
861  NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_KEY_BUTTON), SetMinimalSize(50, 0), SetDataTip(STR_GRAPH_KEY_BUTTON, STR_GRAPH_KEY_TOOLTIP),
862  NWidget(WWT_SHADEBOX, COLOUR_BROWN),
863  NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
864  NWidget(WWT_STICKYBOX, COLOUR_BROWN),
865  EndContainer(),
866  NWidget(WWT_PANEL, COLOUR_BROWN, WID_GRAPH_BACKGROUND),
868  NWidget(WWT_EMPTY, COLOUR_BROWN, WID_GRAPH_GRAPH), SetMinimalSize(576, 224), SetFill(1, 1), SetResize(1, 1),
870  NWidget(NWID_SPACER), SetMinimalSize(12, 0), SetFill(1, 0), SetResize(1, 0),
871  NWidget(WWT_TEXT, COLOUR_BROWN, WID_GRAPH_FOOTER), SetMinimalSize(0, 6), SetPadding(2, 0, 2, 0), SetDataTip(STR_EMPTY, STR_NULL),
872  NWidget(NWID_SPACER), SetFill(1, 0), SetResize(1, 0),
873  NWidget(WWT_RESIZEBOX, COLOUR_BROWN, WID_GRAPH_RESIZE), SetDataTip(RWV_HIDE_BEVEL, STR_TOOLTIP_RESIZE),
874  EndContainer(),
875  EndContainer(),
876  EndContainer(),
877 };
878 
879 static WindowDesc _performance_history_desc(
880  WDP_AUTO, "graph_performance", 0, 0,
882  0,
883  _nested_performance_history_widgets
884 );
885 
886 void ShowPerformanceHistoryGraph()
887 {
888  AllocateWindowDescFront<PerformanceHistoryGraphWindow>(_performance_history_desc, 0);
889 }
890 
891 /*****************/
892 /* COMPANY VALUE */
893 /*****************/
894 
897  BaseGraphWindow(desc, STR_JUST_CURRENCY_SHORT)
898  {
899  this->num_on_x_axis = GRAPH_NUM_MONTHS;
900  this->num_vert_lines = GRAPH_NUM_MONTHS;
901  this->x_values_start = ECONOMY_QUARTER_MINUTES;
902  this->x_values_increment = ECONOMY_QUARTER_MINUTES;
903  this->draw_dates = !TimerGameEconomy::UsingWallclockUnits();
904 
905  this->InitializeWindow(window_number);
906  }
907 
908  OverflowSafeInt64 GetGraphData(const Company *c, int j) override
909  {
910  return c->old_economy[j].company_value;
911  }
912 };
913 
914 static constexpr NWidgetPart _nested_company_value_graph_widgets[] = {
916  NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
917  NWidget(WWT_CAPTION, COLOUR_BROWN), SetDataTip(STR_GRAPH_COMPANY_VALUES_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
918  NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_KEY_BUTTON), SetMinimalSize(50, 0), SetDataTip(STR_GRAPH_KEY_BUTTON, STR_GRAPH_KEY_TOOLTIP),
919  NWidget(WWT_SHADEBOX, COLOUR_BROWN),
920  NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
921  NWidget(WWT_STICKYBOX, COLOUR_BROWN),
922  EndContainer(),
923  NWidget(WWT_PANEL, COLOUR_BROWN, WID_GRAPH_BACKGROUND),
925  NWidget(WWT_EMPTY, COLOUR_BROWN, WID_GRAPH_GRAPH), SetMinimalSize(576, 224), SetFill(1, 1), SetResize(1, 1),
927  NWidget(NWID_SPACER), SetMinimalSize(12, 0), SetFill(1, 0), SetResize(1, 0),
928  NWidget(WWT_TEXT, COLOUR_BROWN, WID_GRAPH_FOOTER), SetMinimalSize(0, 6), SetPadding(2, 0, 2, 0), SetDataTip(STR_EMPTY, STR_NULL),
929  NWidget(NWID_SPACER), SetFill(1, 0), SetResize(1, 0),
930  NWidget(WWT_RESIZEBOX, COLOUR_BROWN, WID_GRAPH_RESIZE), SetDataTip(RWV_HIDE_BEVEL, STR_TOOLTIP_RESIZE),
931  EndContainer(),
932  EndContainer(),
933  EndContainer(),
934 };
935 
936 static WindowDesc _company_value_graph_desc(
937  WDP_AUTO, "graph_company_value", 0, 0,
939  0,
940  _nested_company_value_graph_widgets
941 );
942 
943 void ShowCompanyValueGraph()
944 {
945  AllocateWindowDescFront<CompanyValueGraphWindow>(_company_value_graph_desc, 0);
946 }
947 
948 /*****************/
949 /* PAYMENT RATES */
950 /*****************/
951 
953  uint line_height;
956 
958  BaseGraphWindow(desc, STR_JUST_CURRENCY_SHORT)
959  {
960  this->num_on_x_axis = 20;
961  this->num_vert_lines = 20;
962  this->draw_dates = false;
963  /* The x-axis is labeled in either seconds or days. A day is two seconds, so we adjust the label if needed. */
966 
967  this->CreateNestedTree();
968  this->vscroll = this->GetScrollbar(WID_GRAPH_MATRIX_SCROLLBAR);
969  this->vscroll->SetCount(_sorted_standard_cargo_specs.size());
970 
971  auto *wid = this->GetWidget<NWidgetCore>(WID_GRAPH_FOOTER);
972  wid->SetDataTip(TimerGameEconomy::UsingWallclockUnits() ? STR_GRAPH_CARGO_PAYMENT_RATES_SECONDS: STR_GRAPH_CARGO_PAYMENT_RATES_DAYS, STR_NULL);
973 
974  /* Initialise the dataset */
975  this->UpdatePaymentRates();
976 
977  this->FinishInitNested(window_number);
978  }
979 
980  void OnInit() override
981  {
982  /* Width of the legend blob. */
983  this->legend_width = GetCharacterHeight(FS_SMALL) * 9 / 6;
984  }
985 
986  void UpdateExcludedData()
987  {
988  this->excluded_data = 0;
989 
990  int i = 0;
991  for (const CargoSpec *cs : _sorted_standard_cargo_specs) {
992  if (HasBit(_legend_excluded_cargo_payment_rates, cs->Index())) SetBit(this->excluded_data, i);
993  i++;
994  }
995  }
996 
997  void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
998  {
999  if (widget != WID_GRAPH_MATRIX) {
1000  BaseGraphWindow::UpdateWidgetSize(widget, size, padding, fill, resize);
1001  return;
1002  }
1003 
1005 
1006  for (const CargoSpec *cs : _sorted_standard_cargo_specs) {
1007  SetDParam(0, cs->name);
1008  Dimension d = GetStringBoundingBox(STR_GRAPH_CARGO_PAYMENT_CARGO);
1009  d.width += this->legend_width + WidgetDimensions::scaled.hsep_normal; // colour field
1012  size = maxdim(d, size);
1013  }
1014 
1015  this->line_height = size.height;
1016  size.height = this->line_height * 11; /* Default number of cargo types in most climates. */
1017  resize.width = 0;
1018  resize.height = this->line_height;
1019  }
1020 
1021  void DrawWidget(const Rect &r, WidgetID widget) const override
1022  {
1023  if (widget != WID_GRAPH_MATRIX) {
1024  BaseGraphWindow::DrawWidget(r, widget);
1025  return;
1026  }
1027 
1028  bool rtl = _current_text_dir == TD_RTL;
1029 
1030  auto [first, last] = this->vscroll->GetVisibleRangeIterators(_sorted_standard_cargo_specs);
1031 
1032  Rect line = r.WithHeight(this->line_height);
1033  for (auto it = first; it != last; ++it) {
1034  const CargoSpec *cs = *it;
1035 
1036  bool lowered = !HasBit(_legend_excluded_cargo_payment_rates, cs->Index());
1037 
1038  /* Redraw frame if lowered */
1039  if (lowered) DrawFrameRect(line, COLOUR_BROWN, FR_LOWERED);
1040 
1041  const Rect text = line.Shrink(WidgetDimensions::scaled.framerect);
1042 
1043  /* Cargo-colour box with outline */
1044  const Rect cargo = text.WithWidth(this->legend_width, rtl);
1045  GfxFillRect(cargo, PC_BLACK);
1046  GfxFillRect(cargo.Shrink(WidgetDimensions::scaled.bevel), cs->legend_colour);
1047 
1048  /* Cargo name */
1049  SetDParam(0, cs->name);
1050  DrawString(text.Indent(this->legend_width + WidgetDimensions::scaled.hsep_normal, rtl), STR_GRAPH_CARGO_PAYMENT_CARGO);
1051 
1052  line = line.Translate(0, this->line_height);
1053  }
1054  }
1055 
1056  void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
1057  {
1058  switch (widget) {
1060  /* Remove all cargoes from the excluded lists. */
1061  _legend_excluded_cargo_payment_rates = 0;
1062  this->excluded_data = 0;
1063  this->SetDirty();
1064  break;
1065 
1067  /* Add all cargoes to the excluded lists. */
1068  int i = 0;
1069  for (const CargoSpec *cs : _sorted_standard_cargo_specs) {
1070  SetBit(_legend_excluded_cargo_payment_rates, cs->Index());
1071  SetBit(this->excluded_data, i);
1072  i++;
1073  }
1074  this->SetDirty();
1075  break;
1076  }
1077 
1078  case WID_GRAPH_MATRIX: {
1079  auto it = this->vscroll->GetScrolledItemFromWidget(_sorted_standard_cargo_specs, pt.y, this, WID_GRAPH_MATRIX);
1080  if (it != _sorted_standard_cargo_specs.end()) {
1081  ToggleBit(_legend_excluded_cargo_payment_rates, (*it)->Index());
1082  this->UpdateExcludedData();
1083  this->SetDirty();
1084  }
1085  break;
1086  }
1087  }
1088  }
1089 
1090  void OnResize() override
1091  {
1092  this->vscroll->SetCapacityFromWidget(this, WID_GRAPH_MATRIX);
1093  }
1094 
1095  void OnGameTick() override
1096  {
1097  /* Override default OnGameTick */
1098  }
1099 
1105  void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
1106  {
1107  if (!gui_scope) return;
1108  this->UpdatePaymentRates();
1109  }
1110 
1112  IntervalTimer<TimerWindow> update_payment_interval = {std::chrono::seconds(3), [this](auto) {
1113  this->UpdatePaymentRates();
1114  }};
1115 
1120  {
1121  this->UpdateExcludedData();
1122 
1123  int i = 0;
1124  for (const CargoSpec *cs : _sorted_standard_cargo_specs) {
1125  this->colours[i] = cs->legend_colour;
1126  for (uint j = 0; j != this->num_on_x_axis; j++) {
1127  this->cost[i][j] = GetTransportedGoodsIncome(10, 20, j * 4 + 4, cs->Index());
1128  }
1129  i++;
1130  }
1131  this->num_dataset = i;
1132  }
1133 };
1134 
1135 static constexpr NWidgetPart _nested_cargo_payment_rates_widgets[] = {
1137  NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
1138  NWidget(WWT_CAPTION, COLOUR_BROWN), SetDataTip(STR_GRAPH_CARGO_PAYMENT_RATES_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1139  NWidget(WWT_SHADEBOX, COLOUR_BROWN),
1140  NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
1141  NWidget(WWT_STICKYBOX, COLOUR_BROWN),
1142  EndContainer(),
1143  NWidget(WWT_PANEL, COLOUR_BROWN, WID_GRAPH_BACKGROUND), SetMinimalSize(568, 128),
1145  NWidget(NWID_SPACER), SetFill(1, 0), SetResize(1, 0),
1146  NWidget(WWT_TEXT, COLOUR_BROWN, WID_GRAPH_HEADER), SetMinimalSize(0, 6), SetPadding(2, 0, 2, 0), SetDataTip(STR_GRAPH_CARGO_PAYMENT_RATES_TITLE, STR_NULL),
1147  NWidget(NWID_SPACER), SetFill(1, 0), SetResize(1, 0),
1148  EndContainer(),
1150  NWidget(WWT_EMPTY, COLOUR_BROWN, WID_GRAPH_GRAPH), SetMinimalSize(495, 0), SetFill(1, 1), SetResize(1, 1),
1152  NWidget(NWID_SPACER), SetMinimalSize(0, 24), SetFill(0, 1),
1153  NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_ENABLE_CARGOES), SetDataTip(STR_GRAPH_CARGO_ENABLE_ALL, STR_GRAPH_CARGO_TOOLTIP_ENABLE_ALL), SetFill(1, 0),
1154  NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_DISABLE_CARGOES), SetDataTip(STR_GRAPH_CARGO_DISABLE_ALL, STR_GRAPH_CARGO_TOOLTIP_DISABLE_ALL), SetFill(1, 0),
1157  NWidget(WWT_MATRIX, COLOUR_BROWN, WID_GRAPH_MATRIX), SetFill(1, 0), SetResize(0, 2), SetMatrixDataTip(1, 0, STR_GRAPH_CARGO_PAYMENT_TOGGLE_CARGO), SetScrollbar(WID_GRAPH_MATRIX_SCROLLBAR),
1159  EndContainer(),
1160  NWidget(NWID_SPACER), SetMinimalSize(0, 24), SetFill(0, 1),
1161  EndContainer(),
1162  NWidget(NWID_SPACER), SetMinimalSize(5, 0), SetFill(0, 1), SetResize(0, 1),
1163  EndContainer(),
1165  NWidget(NWID_SPACER), SetMinimalSize(12, 0), SetFill(1, 0), SetResize(1, 0),
1166  NWidget(WWT_TEXT, COLOUR_BROWN, WID_GRAPH_FOOTER), SetMinimalSize(0, 6), SetPadding(2, 0, 2, 0), SetDataTip(STR_NULL, STR_NULL),
1167  NWidget(NWID_SPACER), SetFill(1, 0), SetResize(1, 0),
1168  NWidget(WWT_RESIZEBOX, COLOUR_BROWN, WID_GRAPH_RESIZE), SetDataTip(RWV_HIDE_BEVEL, STR_TOOLTIP_RESIZE),
1169  EndContainer(),
1170  EndContainer(),
1171 };
1172 
1173 static WindowDesc _cargo_payment_rates_desc(
1174  WDP_AUTO, "graph_cargo_payment_rates", 0, 0,
1176  0,
1177  _nested_cargo_payment_rates_widgets
1178 );
1179 
1180 
1181 void ShowCargoPaymentRates()
1182 {
1183  AllocateWindowDescFront<PaymentRatesGraphWindow>(_cargo_payment_rates_desc, 0);
1184 }
1185 
1186 /*****************************/
1187 /* PERFORMANCE RATING DETAIL */
1188 /*****************************/
1189 
1191  static CompanyID company;
1192  int timeout;
1193 
1195  {
1196  this->UpdateCompanyStats();
1197 
1198  this->InitNested(window_number);
1200  }
1201 
1202  void UpdateCompanyStats()
1203  {
1204  /* Update all company stats with the current data
1205  * (this is because _score_info is not saved to a savegame) */
1206  for (Company *c : Company::Iterate()) {
1207  UpdateCompanyRatingAndValue(c, false);
1208  }
1209 
1210  this->timeout = Ticks::DAY_TICKS * 5;
1211  }
1212 
1213  uint score_info_left;
1214  uint score_info_right;
1215  uint bar_left;
1216  uint bar_right;
1217  uint bar_width;
1218  uint bar_height;
1219  uint score_detail_left;
1220  uint score_detail_right;
1221 
1222  void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
1223  {
1224  switch (widget) {
1225  case WID_PRD_SCORE_FIRST:
1227  size.height = this->bar_height + WidgetDimensions::scaled.matrix.Vertical();
1228 
1229  uint score_info_width = 0;
1230  for (uint i = SCORE_BEGIN; i < SCORE_END; i++) {
1231  score_info_width = std::max(score_info_width, GetStringBoundingBox(STR_PERFORMANCE_DETAIL_VEHICLES + i).width);
1232  }
1233  SetDParamMaxValue(0, 1000);
1234  score_info_width += GetStringBoundingBox(STR_JUST_COMMA).width + WidgetDimensions::scaled.hsep_wide;
1235 
1236  SetDParamMaxValue(0, 100);
1237  this->bar_width = GetStringBoundingBox(STR_PERFORMANCE_DETAIL_PERCENT).width + WidgetDimensions::scaled.hsep_indent * 2; // Wide bars!
1238 
1239  /* At this number we are roughly at the max; it can become wider,
1240  * but then you need at 1000 times more money. At that time you're
1241  * not that interested anymore in the last few digits anyway.
1242  * The 500 is because 999 999 500 to 999 999 999 are rounded to
1243  * 1 000 M, and not 999 999 k. Use negative numbers to account for
1244  * the negative income/amount of money etc. as well. */
1245  int max = -(999999999 - 500);
1246 
1247  /* Scale max for the display currency. Prior to rendering the value
1248  * is converted into the display currency, which may cause it to
1249  * raise significantly. We need to compensate for that since {{CURRCOMPACT}}
1250  * is used, which can produce quite short renderings of very large
1251  * values. Otherwise the calculated width could be too narrow.
1252  * Note that it doesn't work if there was a currency with an exchange
1253  * rate greater than max.
1254  * When the currency rate is more than 1000, the 999 999 k becomes at
1255  * least 999 999 M which roughly is equally long. Furthermore if the
1256  * exchange rate is that high, 999 999 k is usually not enough anymore
1257  * to show the different currency numbers. */
1258  if (GetCurrency().rate < 1000) max /= GetCurrency().rate;
1259  SetDParam(0, max);
1260  SetDParam(1, max);
1261  uint score_detail_width = GetStringBoundingBox(STR_PERFORMANCE_DETAIL_AMOUNT_CURRENCY).width;
1262 
1263  size.width = WidgetDimensions::scaled.frametext.Horizontal() + score_info_width + WidgetDimensions::scaled.hsep_wide + this->bar_width + WidgetDimensions::scaled.hsep_wide + score_detail_width;
1265  uint right = size.width - WidgetDimensions::scaled.frametext.right;
1266 
1267  bool rtl = _current_text_dir == TD_RTL;
1268  this->score_info_left = rtl ? right - score_info_width : left;
1269  this->score_info_right = rtl ? right : left + score_info_width;
1270 
1271  this->score_detail_left = rtl ? left : right - score_detail_width;
1272  this->score_detail_right = rtl ? left + score_detail_width : right;
1273 
1274  this->bar_left = left + (rtl ? score_detail_width : score_info_width) + WidgetDimensions::scaled.hsep_wide;
1275  this->bar_right = this->bar_left + this->bar_width - 1;
1276  break;
1277  }
1278  }
1279 
1280  void DrawWidget(const Rect &r, WidgetID widget) const override
1281  {
1282  /* No need to draw when there's nothing to draw */
1283  if (this->company == INVALID_COMPANY) return;
1284 
1286  if (this->IsWidgetDisabled(widget)) return;
1287  CompanyID cid = (CompanyID)(widget - WID_PRD_COMPANY_FIRST);
1288  Dimension sprite_size = GetSpriteSize(SPR_COMPANY_ICON);
1289  DrawCompanyIcon(cid, CenterBounds(r.left, r.right, sprite_size.width), CenterBounds(r.top, r.bottom, sprite_size.height));
1290  return;
1291  }
1292 
1293  if (!IsInsideMM(widget, WID_PRD_SCORE_FIRST, WID_PRD_SCORE_LAST + 1)) return;
1294 
1295  ScoreID score_type = (ScoreID)(widget - WID_PRD_SCORE_FIRST);
1296 
1297  /* The colours used to show how the progress is going */
1298  int colour_done = GetColourGradient(COLOUR_GREEN, SHADE_NORMAL);
1299  int colour_notdone = GetColourGradient(COLOUR_RED, SHADE_NORMAL);
1300 
1301  /* Draw all the score parts */
1302  int64_t val = _score_part[company][score_type];
1303  int64_t needed = _score_info[score_type].needed;
1304  int score = _score_info[score_type].score;
1305 
1306  /* SCORE_TOTAL has its own rules ;) */
1307  if (score_type == SCORE_TOTAL) {
1308  for (ScoreID i = SCORE_BEGIN; i < SCORE_END; i++) score += _score_info[i].score;
1309  needed = SCORE_MAX;
1310  }
1311 
1312  uint bar_top = CenterBounds(r.top, r.bottom, this->bar_height);
1313  uint text_top = CenterBounds(r.top, r.bottom, GetCharacterHeight(FS_NORMAL));
1314 
1315  DrawString(this->score_info_left, this->score_info_right, text_top, STR_PERFORMANCE_DETAIL_VEHICLES + score_type);
1316 
1317  /* Draw the score */
1318  SetDParam(0, score);
1319  DrawString(this->score_info_left, this->score_info_right, text_top, STR_JUST_COMMA, TC_BLACK, SA_RIGHT);
1320 
1321  /* Calculate the %-bar */
1322  uint x = Clamp<int64_t>(val, 0, needed) * this->bar_width / needed;
1323  bool rtl = _current_text_dir == TD_RTL;
1324  if (rtl) {
1325  x = this->bar_right - x;
1326  } else {
1327  x = this->bar_left + x;
1328  }
1329 
1330  /* Draw the bar */
1331  if (x != this->bar_left) GfxFillRect(this->bar_left, bar_top, x, bar_top + this->bar_height - 1, rtl ? colour_notdone : colour_done);
1332  if (x != this->bar_right) GfxFillRect(x, bar_top, this->bar_right, bar_top + this->bar_height - 1, rtl ? colour_done : colour_notdone);
1333 
1334  /* Draw it */
1335  SetDParam(0, Clamp<int64_t>(val, 0, needed) * 100 / needed);
1336  DrawString(this->bar_left, this->bar_right, text_top, STR_PERFORMANCE_DETAIL_PERCENT, TC_FROMSTRING, SA_HOR_CENTER);
1337 
1338  /* SCORE_LOAN is inversed */
1339  if (score_type == SCORE_LOAN) val = needed - val;
1340 
1341  /* Draw the amount we have against what is needed
1342  * For some of them it is in currency format */
1343  SetDParam(0, val);
1344  SetDParam(1, needed);
1345  switch (score_type) {
1346  case SCORE_MIN_PROFIT:
1347  case SCORE_MIN_INCOME:
1348  case SCORE_MAX_INCOME:
1349  case SCORE_MONEY:
1350  case SCORE_LOAN:
1351  DrawString(this->score_detail_left, this->score_detail_right, text_top, STR_PERFORMANCE_DETAIL_AMOUNT_CURRENCY);
1352  break;
1353  default:
1354  DrawString(this->score_detail_left, this->score_detail_right, text_top, STR_PERFORMANCE_DETAIL_AMOUNT_INT);
1355  }
1356  }
1357 
1358  void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
1359  {
1360  /* Check which button is clicked */
1362  /* Is it no on disable? */
1363  if (!this->IsWidgetDisabled(widget)) {
1364  this->RaiseWidget(WID_PRD_COMPANY_FIRST + this->company);
1365  this->company = (CompanyID)(widget - WID_PRD_COMPANY_FIRST);
1366  this->LowerWidget(WID_PRD_COMPANY_FIRST + this->company);
1367  this->SetDirty();
1368  }
1369  }
1370  }
1371 
1372  void OnGameTick() override
1373  {
1374  /* Update the company score every 5 days */
1375  if (--this->timeout == 0) {
1376  this->UpdateCompanyStats();
1377  this->SetDirty();
1378  }
1379  }
1380 
1386  void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
1387  {
1388  if (!gui_scope) return;
1389  /* Disable the companies who are not active */
1390  for (CompanyID i = COMPANY_FIRST; i < MAX_COMPANIES; i++) {
1392  }
1393 
1394  /* Check if the currently selected company is still active. */
1395  if (this->company != INVALID_COMPANY && !Company::IsValidID(this->company)) {
1396  /* Raise the widget for the previous selection. */
1397  this->RaiseWidget(WID_PRD_COMPANY_FIRST + this->company);
1398  this->company = INVALID_COMPANY;
1399  }
1400 
1401  if (this->company == INVALID_COMPANY) {
1402  for (const Company *c : Company::Iterate()) {
1403  this->company = c->index;
1404  break;
1405  }
1406  }
1407 
1408  /* Make sure the widget is lowered */
1409  if (this->company != INVALID_COMPANY) {
1410  this->LowerWidget(WID_PRD_COMPANY_FIRST + this->company);
1411  }
1412  }
1413 };
1414 
1415 CompanyID PerformanceRatingDetailWindow::company = INVALID_COMPANY;
1416 
1417 /*******************************/
1418 /* INDUSTRY PRODUCTION HISTORY */
1419 /*******************************/
1420 
1425 
1427  BaseGraphWindow(desc, STR_JUST_COMMA)
1428  {
1429  this->num_on_x_axis = GRAPH_NUM_MONTHS;
1430  this->num_vert_lines = GRAPH_NUM_MONTHS;
1431  this->month_increment = 1;
1432  this->x_values_start = ECONOMY_MONTH_MINUTES;
1433  this->x_values_increment = ECONOMY_MONTH_MINUTES;
1434  this->draw_dates = !TimerGameEconomy::UsingWallclockUnits();
1435 
1436  this->CreateNestedTree();
1437  this->vscroll = this->GetScrollbar(WID_GRAPH_MATRIX_SCROLLBAR);
1438 
1439  int count = 0;
1440  const Industry *i = Industry::Get(window_number);
1441  for (const auto &p : i->produced) {
1442  if (!IsValidCargoID(p.cargo)) continue;
1443  count++;
1444  }
1445  this->vscroll->SetCount(count);
1446 
1447  auto *wid = this->GetWidget<NWidgetCore>(WID_GRAPH_FOOTER);
1448  wid->SetDataTip(TimerGameEconomy::UsingWallclockUnits() ? STR_GRAPH_LAST_24_MINUTES_TIME_LABEL : STR_EMPTY, STR_NULL);
1449 
1450  this->FinishInitNested(window_number);
1451 
1452  /* Initialise the dataset */
1453  this->UpdateStatistics(true);
1454  }
1455 
1456  void OnInit() override
1457  {
1458  /* Width of the legend blob. */
1459  this->legend_width = GetCharacterHeight(FS_SMALL) * 9 / 6;
1460  }
1461 
1462  void UpdateExcludedData()
1463  {
1464  this->excluded_data = 0;
1465 
1466  int index = 0;
1467  const Industry *i = Industry::Get(this->window_number);
1468  for (const auto &p : i->produced) {
1469  if (!IsValidCargoID(p.cargo)) continue;
1470  if (HasBit(_legend_excluded_cargo_production_history, p.cargo)) SetBit(this->excluded_data, index);
1471  index++;
1472  }
1473  }
1474 
1475  void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
1476  {
1477  if (widget != WID_GRAPH_MATRIX) {
1478  BaseGraphWindow::UpdateWidgetSize(widget, size, padding, fill, resize);
1479  return;
1480  }
1481 
1482  const Industry *i = Industry::Get(this->window_number);
1483  const CargoSpec *cs;
1484  for (const auto &p : i->produced) {
1485  if (!IsValidCargoID(p.cargo)) continue;
1486 
1487  cs = CargoSpec::Get(p.cargo);
1488  SetDParam(0, cs->name);
1489  Dimension d = GetStringBoundingBox(STR_GRAPH_CARGO_PAYMENT_CARGO);
1490  d.width += this->legend_width + WidgetDimensions::scaled.hsep_normal; // colour field
1493  size = maxdim(d, size);
1494  }
1495 
1496  this->line_height = size.height;
1497  size.height = this->line_height * 11; /* Default number of cargo types in most climates. */
1498  resize.width = 0;
1499  resize.height = this->line_height;
1500  }
1501 
1502  void DrawWidget(const Rect &r, WidgetID widget) const override
1503  {
1504  if (widget != WID_GRAPH_MATRIX) {
1505  BaseGraphWindow::DrawWidget(r, widget);
1506  return;
1507  }
1508 
1509  bool rtl = _current_text_dir == TD_RTL;
1510 
1511  int pos = this->vscroll->GetPosition();
1512  int max = pos + this->vscroll->GetCapacity();
1513 
1514  Rect line = r.WithHeight(this->line_height);
1515  const Industry *i = Industry::Get(this->window_number);
1516  const CargoSpec *cs;
1517 
1518  for (const auto &p : i->produced) {
1519  if (!IsValidCargoID(p.cargo)) continue;
1520 
1521  if (pos-- > 0) continue;
1522  if (--max < 0) break;
1523 
1524  cs = CargoSpec::Get(p.cargo);
1525 
1526  bool lowered = !HasBit(_legend_excluded_cargo_production_history, p.cargo);
1527 
1528  /* Redraw frame if lowered */
1529  if (lowered) DrawFrameRect(line, COLOUR_BROWN, FR_LOWERED);
1530 
1531  const Rect text = line.Shrink(WidgetDimensions::scaled.framerect);
1532 
1533  /* Cargo-colour box with outline */
1534  const Rect cargo = text.WithWidth(this->legend_width, rtl);
1535  GfxFillRect(cargo, PC_BLACK);
1536  GfxFillRect(cargo.Shrink(WidgetDimensions::scaled.bevel), cs->legend_colour);
1537 
1538  /* Cargo name */
1539  SetDParam(0, cs->name);
1540  DrawString(text.Indent(this->legend_width + WidgetDimensions::scaled.hsep_normal, rtl), STR_GRAPH_CARGO_PAYMENT_CARGO);
1541 
1542  line = line.Translate(0, this->line_height);
1543  }
1544  }
1545 
1546  void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
1547  {
1548  switch (widget) {
1550  /* Remove all cargoes from the excluded lists. */
1551  _legend_excluded_cargo_production_history = 0;
1552  this->excluded_data = 0;
1553  this->SetDirty();
1554  break;
1555 
1557  /* Add all cargoes to the excluded lists. */
1558  int index = 0;
1559  const Industry *i = Industry::Get(this->window_number);
1560  for (const auto &p : i->produced) {
1561  if (!IsValidCargoID(p.cargo)) continue;
1562 
1563  SetBit(_legend_excluded_cargo_production_history, p.cargo);
1564  SetBit(this->excluded_data, index);
1565  index++;
1566  }
1567  this->SetDirty();
1568  break;
1569  }
1570 
1571  case WID_GRAPH_MATRIX: {
1572  int row = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_GRAPH_MATRIX);
1573  if (row >= this->vscroll->GetCount()) return;
1574 
1575  const Industry *i = Industry::Get(this->window_number);
1576  for (const auto &p : i->produced) {
1577  if (!IsValidCargoID(p.cargo)) continue;
1578  if (row-- > 0) continue;
1579 
1580  ToggleBit(_legend_excluded_cargo_production_history, p.cargo);
1581  this->UpdateExcludedData();
1582  this->SetDirty();
1583  break;
1584  }
1585  break;
1586  }
1587  }
1588  }
1589 
1590  void SetStringParameters(WidgetID widget) const override
1591  {
1592  if (widget == WID_GRAPH_CAPTION) SetDParam(0, this->window_number);
1593  }
1594 
1595  void OnResize() override
1596  {
1597  this->vscroll->SetCapacityFromWidget(this, WID_GRAPH_MATRIX);
1598  }
1599 
1600  void UpdateStatistics(bool initialize) override
1601  {
1602  CargoTypes excluded_cargo = this->excluded_data;
1603  this->UpdateExcludedData();
1604 
1605  int mo = TimerGameEconomy::month - this->num_vert_lines;
1606  auto yr = TimerGameEconomy::year;
1607  while (mo < 0) {
1608  yr--;
1609  mo += 12;
1610  }
1611 
1612  if (!initialize && this->excluded_data == excluded_cargo && this->num_on_x_axis == this->num_vert_lines && this->year == yr && this->month == mo) {
1613  /* There's no reason to get new stats */
1614  return;
1615  }
1616 
1617  this->year = yr;
1618  this->month = mo;
1619 
1620  int index = 0;
1621  const Industry *i = Industry::Get(this->window_number);
1622  for (const auto &p : i->produced) {
1623  if (!IsValidCargoID(p.cargo)) continue;
1624 
1625  const CargoSpec *cs = CargoSpec::Get(p.cargo);
1626 
1627  this->colours[index] = cs->legend_colour;
1628  for (uint j = 0; j < GRAPH_NUM_MONTHS; j++) {
1629  this->cost[index][j] = p.history[GRAPH_NUM_MONTHS - j].production;
1630  }
1631  index++;
1632  }
1633 
1634  this->num_dataset = index;
1635  this->vscroll->SetCount(index);
1636 
1637  this->SetDirty();
1638  }
1639 };
1640 
1641 static constexpr NWidgetPart _nested_industry_production_widgets[] = {
1643  NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
1644  NWidget(WWT_CAPTION, COLOUR_BROWN, WID_GRAPH_CAPTION), SetDataTip(STR_GRAPH_INDUSTRY_PRODUCTION_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1645  NWidget(WWT_SHADEBOX, COLOUR_BROWN),
1646  NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
1647  NWidget(WWT_STICKYBOX, COLOUR_BROWN),
1648  EndContainer(),
1649  NWidget(WWT_PANEL, COLOUR_BROWN, WID_GRAPH_BACKGROUND), SetMinimalSize(568, 128),
1651  NWidget(WWT_EMPTY, COLOUR_BROWN, WID_GRAPH_GRAPH), SetMinimalSize(495, 0), SetFill(1, 1), SetResize(1, 1),
1653  NWidget(NWID_SPACER), SetMinimalSize(0, 24), SetFill(0, 1),
1654  NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_ENABLE_CARGOES), SetDataTip(STR_GRAPH_CARGO_ENABLE_ALL, STR_GRAPH_CARGO_TOOLTIP_ENABLE_ALL), SetFill(1, 0),
1655  NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_DISABLE_CARGOES), SetDataTip(STR_GRAPH_CARGO_DISABLE_ALL, STR_GRAPH_CARGO_TOOLTIP_DISABLE_ALL), SetFill(1, 0),
1658  NWidget(WWT_MATRIX, COLOUR_BROWN, WID_GRAPH_MATRIX), SetFill(1, 0), SetResize(0, 2), SetMatrixDataTip(1, 0, STR_GRAPH_CARGO_PAYMENT_TOGGLE_CARGO), SetScrollbar(WID_GRAPH_MATRIX_SCROLLBAR),
1660  EndContainer(),
1661  NWidget(NWID_SPACER), SetMinimalSize(0, 24), SetFill(0, 1),
1662  EndContainer(),
1663  NWidget(NWID_SPACER), SetMinimalSize(5, 0), SetFill(0, 1), SetResize(0, 1),
1664  EndContainer(),
1666  NWidget(NWID_SPACER), SetMinimalSize(12, 0), SetFill(1, 0), SetResize(1, 0),
1667  NWidget(WWT_TEXT, COLOUR_BROWN, WID_GRAPH_FOOTER), SetMinimalSize(0, 6), SetPadding(2, 0, 2, 0), SetDataTip(STR_EMPTY, STR_NULL),
1668  NWidget(NWID_SPACER), SetFill(1, 0), SetResize(1, 0),
1669  NWidget(WWT_RESIZEBOX, COLOUR_BROWN, WID_GRAPH_RESIZE), SetDataTip(RWV_HIDE_BEVEL, STR_TOOLTIP_RESIZE),
1670  EndContainer(),
1671  EndContainer(),
1672 };
1673 
1674 static WindowDesc _industry_production_desc(
1675  WDP_AUTO, "graph_industry_production", 0, 0,
1677  0,
1678  _nested_industry_production_widgets
1679 );
1680 
1681 void ShowIndustryProductionGraph(WindowNumber window_number)
1682 {
1683  AllocateWindowDescFront<IndustryProductionGraphWindow>(_industry_production_desc, window_number);
1684 }
1685 
1690 static std::unique_ptr<NWidgetBase> MakePerformanceDetailPanels()
1691 {
1692  auto realtime = TimerGameEconomy::UsingWallclockUnits();
1693  const StringID performance_tips[] = {
1694  realtime ? STR_PERFORMANCE_DETAIL_VEHICLES_TOOLTIP_PERIODS : STR_PERFORMANCE_DETAIL_VEHICLES_TOOLTIP_YEARS,
1695  STR_PERFORMANCE_DETAIL_STATIONS_TOOLTIP,
1696  realtime ? STR_PERFORMANCE_DETAIL_MIN_PROFIT_TOOLTIP_PERIODS : STR_PERFORMANCE_DETAIL_MIN_PROFIT_TOOLTIP_YEARS,
1697  STR_PERFORMANCE_DETAIL_MIN_INCOME_TOOLTIP,
1698  STR_PERFORMANCE_DETAIL_MAX_INCOME_TOOLTIP,
1699  STR_PERFORMANCE_DETAIL_DELIVERED_TOOLTIP,
1700  STR_PERFORMANCE_DETAIL_CARGO_TOOLTIP,
1701  STR_PERFORMANCE_DETAIL_MONEY_TOOLTIP,
1702  STR_PERFORMANCE_DETAIL_LOAN_TOOLTIP,
1703  STR_PERFORMANCE_DETAIL_TOTAL_TOOLTIP,
1704  };
1705 
1706  static_assert(lengthof(performance_tips) == SCORE_END - SCORE_BEGIN);
1707 
1708  auto vert = std::make_unique<NWidgetVertical>(NC_EQUALSIZE);
1709  for (WidgetID widnum = WID_PRD_SCORE_FIRST; widnum <= WID_PRD_SCORE_LAST; widnum++) {
1710  auto panel = std::make_unique<NWidgetBackground>(WWT_PANEL, COLOUR_BROWN, widnum);
1711  panel->SetFill(1, 1);
1712  panel->SetDataTip(0x0, performance_tips[widnum - WID_PRD_SCORE_FIRST]);
1713  vert->Add(std::move(panel));
1714  }
1715  return vert;
1716 }
1717 
1719 std::unique_ptr<NWidgetBase> MakeCompanyButtonRowsGraphGUI()
1720 {
1721  return MakeCompanyButtonRows(WID_PRD_COMPANY_FIRST, WID_PRD_COMPANY_LAST, COLOUR_BROWN, 8, STR_PERFORMANCE_DETAIL_SELECT_COMPANY_TOOLTIP);
1722 }
1723 
1724 static constexpr NWidgetPart _nested_performance_rating_detail_widgets[] = {
1726  NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
1727  NWidget(WWT_CAPTION, COLOUR_BROWN), SetDataTip(STR_PERFORMANCE_DETAIL, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1728  NWidget(WWT_SHADEBOX, COLOUR_BROWN),
1729  NWidget(WWT_STICKYBOX, COLOUR_BROWN),
1730  EndContainer(),
1731  NWidget(WWT_PANEL, COLOUR_BROWN),
1733  EndContainer(),
1735 };
1736 
1737 static WindowDesc _performance_rating_detail_desc(
1738  WDP_AUTO, "league_details", 0, 0,
1740  0,
1741  _nested_performance_rating_detail_widgets
1742 );
1743 
1744 void ShowPerformanceRatingDetail()
1745 {
1746  AllocateWindowDescFront<PerformanceRatingDetailWindow>(_performance_rating_detail_desc, 0);
1747 }
1748 
1749 void InitializeGraphGui()
1750 {
1751  _legend_excluded_companies = 0;
1752  _legend_excluded_cargo_payment_rates = 0;
1753  _legend_excluded_cargo_production_history = 0;
1754 }
constexpr debug_inline bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
constexpr T ToggleBit(T &x, const uint8_t y)
Toggles a bit in a variable.
bool IsValidCargoID(CargoID t)
Test whether cargo type is not INVALID_CARGO.
Definition: cargo_type.h:107
static const CargoID NUM_CARGO
Maximum number of cargo types in a game.
Definition: cargo_type.h:74
std::span< const CargoSpec * > _sorted_standard_cargo_specs
Standard cargo specifications sorted alphabetically by name.
Definition: cargotype.cpp:184
Types/functions related to cargoes.
Scrollbar data structure.
Definition: widget_type.h:694
size_type GetCapacity() const
Gets the number of visible elements of the scrollbar.
Definition: widget_type.h:731
void SetCount(size_t num)
Sets the number of elements in the list.
Definition: widget_type.h:780
auto GetScrolledItemFromWidget(Tcontainer &container, int clickpos, const Window *const w, WidgetID widget, int padding=0, int line_height=-1) const
Return an iterator pointing to the element of a scrolled widget that a user clicked in.
Definition: widget_type.h:879
size_type GetScrolledRowFromWidget(int clickpos, const Window *const w, WidgetID widget, int padding=0, int line_height=-1) const
Compute the row of a scrolled widget that a user clicked in.
Definition: widget.cpp:2320
void SetCapacityFromWidget(Window *w, WidgetID widget, int padding=0)
Set capacity of visible elements from the size and resize properties of a widget.
Definition: widget.cpp:2394
size_type GetCount() const
Gets the number of elements in the list.
Definition: widget_type.h:722
auto GetVisibleRangeIterators(Tcontainer &container) const
Get a pair of iterators for the range of visible elements in a container.
Definition: widget_type.h:860
size_type GetPosition() const
Gets the position of the first visible element in the list.
Definition: widget_type.h:740
static constexpr TimerGameTick::Ticks DAY_TICKS
1 day is 74 ticks; TimerGameCalendar::date_fract used to be uint16_t and incremented by 885.
static Year year
Current year, starting at 0.
static Month month
Current month (0..11).
static bool UsingWallclockUnits(bool newgame=false)
Check if we are using wallclock units.
uint8_t Month
Type for the month, note: 0 based, i.e.
RectPadding framerect
Standard padding inside many panels.
Definition: window_gui.h:42
RectPadding frametext
Padding inside frame with text.
Definition: window_gui.h:43
static WidgetDimensions scaled
Widget dimensions scaled for current zoom level.
Definition: window_gui.h:68
int hsep_wide
Wide horizontal spacing.
Definition: window_gui.h:64
RectPadding fullbevel
Always-scaled bevel thickness.
Definition: window_gui.h:41
static const WidgetDimensions unscaled
Unscaled widget dimensions.
Definition: window_gui.h:67
RectPadding matrix
Padding of WWT_MATRIX items.
Definition: window_gui.h:44
int hsep_normal
Normal horizontal spacing.
Definition: window_gui.h:63
RectPadding bevel
Bevel thickness, affected by "scaled bevels" game option.
Definition: window_gui.h:40
int hsep_indent
Width of identation for tree layouts.
Definition: window_gui.h:65
Definition of stuff that is very close to a company, like the company struct itself.
void DrawCompanyIcon(CompanyID c, int x, int y)
Draw the icon of a company.
GUI Functions related to companies.
Owner
Enum for all companies/owners.
Definition: company_type.h:18
@ INVALID_COMPANY
An invalid company.
Definition: company_type.h:30
@ COMPANY_FIRST
First company, same as owner.
Definition: company_type.h:22
@ MAX_COMPANIES
Maximum number of companies.
Definition: company_type.h:23
Functions to handle different currencies.
const CurrencySpec & GetCurrency()
Get the currently selected currency.
Definition: currency.h:117
int UpdateCompanyRatingAndValue(Company *c, bool update)
if update is set to true, the economy is updated with this score (also the house is updated,...
Definition: economy.cpp:201
const ScoreInfo _score_info[]
Score info, values used for computing the detailed performance rating.
Definition: economy.cpp:90
Functions related to the economy.
ScoreID
Score categories in the detailed performance rating.
Definition: economy_type.h:60
@ SCORE_END
How many scores are there..
Definition: economy_type.h:72
@ SCORE_MAX
The max score that can be in the performance history.
Definition: economy_type.h:74
@ SCORE_TOTAL
This must always be the last entry.
Definition: economy_type.h:71
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
Definition: fontcache.cpp:77
Dimension maxdim(const Dimension &d1, const Dimension &d2)
Compute bounding box of both dimensions.
Geometry functions.
Dimension GetSpriteSize(SpriteID sprid, Point *offset, ZoomLevel zoom)
Get the size of a sprite.
Definition: gfx.cpp:922
Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
Return the string dimension in pixels.
Definition: gfx.cpp:851
int DrawString(int left, int right, int top, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly truncated to make it fit in its allocated space.
Definition: gfx.cpp:657
void GfxFillRect(int left, int top, int right, int bottom, int colour, FillRectMode mode)
Applies a certain FillRectMode-operation to a rectangle [left, right] x [top, bottom] on the screen.
Definition: gfx.cpp:114
int DrawStringMultiLine(int left, int right, int top, int bottom, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly over multiple lines.
Definition: gfx.cpp:774
Functions related to the gfx engine.
int CenterBounds(int min, int max, int size)
Determine where to draw a centred object inside a widget.
Definition: gfx_func.h:166
@ SA_LEFT
Left align the text.
Definition: gfx_type.h:343
@ SA_RIGHT
Right align the text (must be a single bit).
Definition: gfx_type.h:345
@ SA_HOR_CENTER
Horizontally center the text.
Definition: gfx_type.h:344
@ FS_SMALL
Index of the small font in the font tables.
Definition: gfx_type.h:210
@ FS_NORMAL
Index of the normal font in the font tables.
Definition: gfx_type.h:209
TextColour
Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palett...
Definition: gfx_type.h:260
static std::unique_ptr< NWidgetBase > MakeNWidgetCompanyLines()
Construct a vertical list of buttons, one for each company.
Definition: graph_gui.cpp:117
constexpr double INT64_MAX_IN_DOUBLE
The biggest double that when cast to int64_t still fits in a int64_t.
Definition: graph_gui.cpp:45
static std::unique_ptr< NWidgetBase > MakePerformanceDetailPanels()
Make a vertical list of panels for outputting score details.
Definition: graph_gui.cpp:1690
std::unique_ptr< NWidgetBase > MakeCompanyButtonRowsGraphGUI()
Make a number of rows with buttons for each company for the performance rating detail window.
Definition: graph_gui.cpp:1719
Graph GUI functions.
Types related to the graph widgets.
@ WID_GRAPH_FOOTER
Footer.
Definition: graph_widget.h:32
@ WID_GRAPH_RESIZE
Resize button.
Definition: graph_widget.h:30
@ WID_GRAPH_BACKGROUND
Background of the window.
Definition: graph_widget.h:27
@ WID_GRAPH_GRAPH
Graph itself.
Definition: graph_widget.h:29
@ WID_PHG_DETAILED_PERFORMANCE
Detailed performance.
Definition: graph_widget.h:39
@ WID_GRAPH_HEADER
Header.
Definition: graph_widget.h:31
@ WID_GRAPH_MATRIX_SCROLLBAR
Cargo list scrollbar.
Definition: graph_widget.h:37
@ WID_GRAPH_DISABLE_CARGOES
Disable cargoes button.
Definition: graph_widget.h:35
@ WID_GRAPH_CAPTION
Caption.
Definition: graph_widget.h:28
@ WID_GRAPH_KEY_BUTTON
Key button.
Definition: graph_widget.h:26
@ WID_GRAPH_MATRIX
Cargo list.
Definition: graph_widget.h:36
@ WID_GRAPH_ENABLE_CARGOES
Enable cargoes button.
Definition: graph_widget.h:34
@ WID_GL_FIRST_COMPANY
First company in the legend.
Definition: graph_widget.h:20
@ WID_GL_LAST_COMPANY
Last company in the legend.
Definition: graph_widget.h:21
@ WID_GL_BACKGROUND
Background of the window.
Definition: graph_widget.h:18
@ WID_PRD_COMPANY_FIRST
First company.
Definition: graph_widget.h:47
@ WID_PRD_SCORE_FIRST
First entry in the score list.
Definition: graph_widget.h:44
@ WID_PRD_SCORE_LAST
Last entry in the score list.
Definition: graph_widget.h:45
@ WID_PRD_COMPANY_LAST
Last company.
Definition: graph_widget.h:48
constexpr NWidgetPart NWidgetFunction(NWidgetFunctionType *func_ptr)
Obtain a nested widget (sub)tree from an external source.
Definition: widget_type.h:1330
constexpr NWidgetPart SetFill(uint16_t fill_x, uint16_t fill_y)
Widget part function for setting filling.
Definition: widget_type.h:1181
constexpr NWidgetPart SetScrollbar(WidgetID index)
Attach a scrollbar to a widget.
Definition: widget_type.h:1284
constexpr NWidgetPart SetPadding(uint8_t top, uint8_t right, uint8_t bottom, uint8_t left)
Widget part function for setting additional space around a widget.
Definition: widget_type.h:1228
constexpr NWidgetPart SetDataTip(uint32_t data, StringID tip)
Widget part function for setting the data and tooltip.
Definition: widget_type.h:1202
constexpr NWidgetPart SetMinimalSize(int16_t x, int16_t y)
Widget part function for setting the minimal size.
Definition: widget_type.h:1137
constexpr NWidgetPart NWidget(WidgetType tp, Colours col, WidgetID idx=-1)
Widget part function for starting a new 'real' widget.
Definition: widget_type.h:1309
constexpr NWidgetPart SetMatrixDataTip(uint8_t cols, uint8_t rows, StringID tip)
Widget part function for setting the data and tooltip of WWT_MATRIX widgets.
Definition: widget_type.h:1214
constexpr NWidgetPart EndContainer()
Widget part function for denoting the end of a container (horizontal, vertical, WWT_FRAME,...
Definition: widget_type.h:1191
constexpr NWidgetPart SetResize(int16_t dx, int16_t dy)
Widget part function for setting the resize step.
Definition: widget_type.h:1126
void SetDirty() const
Mark entire window as dirty (in need of re-paint)
Definition: window.cpp:940
Base of all industries.
constexpr T abs(const T a)
Returns the absolute value of (scalar) variable.
Definition: math_func.hpp:23
constexpr bool IsInsideMM(const T x, const size_t min, const size_t max) noexcept
Checks if a value is in an interval.
Definition: math_func.hpp:268
uint8_t GetColourGradient(Colours colour, ColourShade shade)
Get colour gradient palette index.
Definition: palette.cpp:314
static const uint8_t PC_BLACK
Black palette colour.
Definition: palette_func.h:67
#define GREY_SCALE(level)
Return the colour for a particular greyscale level.
Definition: palette_func.h:65
A number of safeguards to prevent using unsafe methods.
ClientSettings _settings_client
The current settings for this game.
Definition: settings.cpp:56
This file contains all sprite-related enums and defines.
Definition of base types and functions in a cross-platform compatible way.
#define lengthof(array)
Return the length of an fixed size array.
Definition: stdafx.h:280
void SetDParamMaxValue(size_t n, uint64_t max_value, uint min_count, FontSize size)
Set DParam n to some number that is suitable for string size computations.
Definition: strings.cpp:127
void SetDParam(size_t n, uint64_t v)
Set a string parameter v at index n in the global string parameter array.
Definition: strings.cpp:104
TextDirection _current_text_dir
Text direction of the currently selected language.
Definition: strings.cpp:56
Functions related to OTTD's strings.
@ TD_RTL
Text is written right-to-left by default.
Definition: strings_type.h:24
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
Definition: strings_type.h:16
uint GetYLabelWidth(ValuesInterval current_interval, int num_hori_lines) const
Get width for Y labels.
Definition: graph_gui.cpp:284
void OnGameTick() override
Called once per (game) tick.
Definition: graph_gui.cpp:578
static const int MIN_GRID_PIXEL_SIZE
Minimum distance between graph lines.
Definition: graph_gui.cpp:185
static const int GRAPH_NUM_MONTHS
Number of months displayed in the graph.
Definition: graph_gui.cpp:176
static const int ECONOMY_QUARTER_MINUTES
Minutes per economic quarter.
Definition: graph_gui.cpp:179
static const int ECONOMY_MONTH_MINUTES
Minutes per economic month.
Definition: graph_gui.cpp:180
uint64_t excluded_data
bitmask of the datasets that shouldn't be displayed.
Definition: graph_gui.cpp:187
bool draw_dates
Should we draw months and years on the time axis?
Definition: graph_gui.cpp:197
static const int MIN_GRAPH_NUM_LINES_Y
Minimal number of horizontal lines to draw.
Definition: graph_gui.cpp:184
uint8_t month_increment
month increment between vertical lines. must be divisor of 12.
Definition: graph_gui.cpp:195
static const int PAYMENT_GRAPH_X_STEP_DAYS
X-axis step label for cargo payment rates "Days in transit".
Definition: graph_gui.cpp:177
ValuesInterval GetValuesInterval(int num_hori_lines) const
Get the interval that contains the graph's data.
Definition: graph_gui.cpp:214
OverflowSafeInt64 cost[GRAPH_MAX_DATASETS][GRAPH_NUM_MONTHS]
Stored costs for the last GRAPH_NUM_MONTHS months.
Definition: graph_gui.cpp:206
virtual void UpdateStatistics(bool initialize)
Update the statistics.
Definition: graph_gui.cpp:598
static const TextColour GRAPH_AXIS_LABEL_COLOUR
colour of the graph axis label.
Definition: graph_gui.cpp:182
void OnInvalidateData([[maybe_unused]] int data=0, [[maybe_unused]] bool gui_scope=true) override
Some data on this window has become invalid.
Definition: graph_gui.cpp:588
void DrawGraph(Rect r) const
Actually draw the graph.
Definition: graph_gui.cpp:308
static const int PAYMENT_GRAPH_X_STEP_SECONDS
X-axis step label for cargo payment rates "Seconds in transit".
Definition: graph_gui.cpp:178
const T GetSum() const
Get the sum of all cargo amounts.
Definition: cargo_type.h:120
Specification of a cargo type.
Definition: cargotype.h:71
CargoID Index() const
Determines index of this cargospec.
Definition: cargotype.h:105
static CargoSpec * Get(size_t index)
Retrieve cargo details for the given cargo ID.
Definition: cargotype.h:134
StringID name
Name of this type of cargo.
Definition: cargotype.h:88
GUISettings gui
settings related to the GUI
Money income
The amount of income.
Definition: company_base.h:25
Money expenses
The amount of expenses.
Definition: company_base.h:26
Money company_value
The value of the company.
Definition: company_base.h:29
CargoArray delivered_cargo
The amount of delivered cargo.
Definition: company_base.h:27
int32_t performance_history
Company score (scale 0-1000)
Definition: company_base.h:28
CompanyEconomyEntry old_economy[MAX_HISTORY_QUARTERS]
Economic data of the company of the last MAX_HISTORY_QUARTERS quarters.
Definition: company_base.h:116
Colours colour
Company colour.
Definition: company_base.h:87
uint8_t num_valid_stat_ent
Number of valid statistical entries in old_economy.
Definition: company_base.h:117
uint16_t rate
The conversion rate compared to the base currency.
Definition: currency.h:76
Dimensions (a width and height) of a rectangle in 2D.
uint8_t graph_line_thickness
the thickness of the lines in the various graph guis
void OnInvalidateData([[maybe_unused]] int data=0, [[maybe_unused]] bool gui_scope=true) override
Some data on this window has become invalid.
Definition: graph_gui.cpp:103
void UpdateStatistics(bool initialize) override
Update the statistics.
Definition: graph_gui.cpp:1600
void OnResize() override
Called after the window got resized.
Definition: graph_gui.cpp:1595
uint line_height
Pixel height of each cargo type row.
Definition: graph_gui.cpp:1422
uint legend_width
Width of legend 'blob'.
Definition: graph_gui.cpp:1424
void OnInit() override
Notification that the nested widget tree gets initialized.
Definition: graph_gui.cpp:1456
Scrollbar * vscroll
Cargo list scrollbar.
Definition: graph_gui.cpp:1423
Defines the internal data of a functional industry.
Definition: industry.h:66
ProducedCargoes produced
produced cargo slots
Definition: industry.h:97
Partial widget specification to allow NWidgets to be written nested.
Definition: widget_type.h:1075
uint line_height
Pixel height of each cargo type row.
Definition: graph_gui.cpp:953
void OnResize() override
Called after the window got resized.
Definition: graph_gui.cpp:1090
void OnInvalidateData([[maybe_unused]] int data=0, [[maybe_unused]] bool gui_scope=true) override
Some data on this window has become invalid.
Definition: graph_gui.cpp:1105
void OnInit() override
Notification that the nested widget tree gets initialized.
Definition: graph_gui.cpp:980
void OnGameTick() override
Called once per (game) tick.
Definition: graph_gui.cpp:1095
void UpdatePaymentRates()
Update the payment rates according to the latest information.
Definition: graph_gui.cpp:1119
IntervalTimer< TimerWindow > update_payment_interval
Update the payment rates on a regular interval.
Definition: graph_gui.cpp:1112
uint legend_width
Width of legend 'blob'.
Definition: graph_gui.cpp:955
Scrollbar * vscroll
Cargo list scrollbar.
Definition: graph_gui.cpp:954
void OnGameTick() override
Called once per (game) tick.
Definition: graph_gui.cpp:1372
void OnInvalidateData([[maybe_unused]] int data=0, [[maybe_unused]] bool gui_scope=true) override
Some data on this window has become invalid.
Definition: graph_gui.cpp:1386
Coordinates of a point in 2D.
static Titem * Get(size_t index)
Returns Titem with given index.
Definition: pool_type.hpp:339
static bool IsValidID(size_t index)
Tests whether given index can be used to get valid (non-nullptr) Titem.
Definition: pool_type.hpp:328
static Titem * GetIfValid(size_t index)
Returns Titem with given index.
Definition: pool_type.hpp:350
static Pool::IterateWrapper< Titem > Iterate(size_t from=0)
Returns an iterable ensemble of all valid Titem.
Definition: pool_type.hpp:388
constexpr uint Horizontal() const
Get total horizontal padding of RectPadding.
constexpr uint Vertical() const
Get total vertical padding of RectPadding.
Specification of a rectangle with absolute coordinates of all edges.
Rect WithWidth(int width, bool end) const
Copy Rect and set its width.
Rect Shrink(int s) const
Copy and shrink Rect by s pixels.
Rect WithHeight(int height, bool end=false) const
Copy Rect and set its height.
Rect Indent(int indent, bool end) const
Copy Rect and indent it from its position.
Rect Translate(int x, int y) const
Copy and translate Rect by x,y pixels.
int needed
How much you need to get the perfect score.
Definition: economy_type.h:81
int score
How much score it will give.
Definition: economy_type.h:82
Templated helper to make a type-safe 'typedef' representing a single POD value.
Contains the interval of a graph's data.
Definition: graph_gui.cpp:159
OverflowSafeInt64 lowest
Lowest value of this interval. Must be zero or less.
Definition: graph_gui.cpp:161
OverflowSafeInt64 highest
Highest value of this interval. Must be zero or greater.
Definition: graph_gui.cpp:160
High level window description.
Definition: window_gui.h:159
Data structure for an opened window.
Definition: window_gui.h:273
void FinishInitNested(WindowNumber window_number=0)
Perform the second part of the initialization of a nested widget tree.
Definition: window.cpp:1733
void RaiseWidget(WidgetID widget_index)
Marks a widget as raised.
Definition: window_gui.h:475
ResizeInfo resize
Resize information.
Definition: window_gui.h:314
void CreateNestedTree()
Perform the first part of the initialization of a nested widget tree.
Definition: window.cpp:1723
bool IsWidgetDisabled(WidgetID widget_index) const
Gets the enabled/disabled status of a widget.
Definition: window_gui.h:416
int left
x position of left edge of the window
Definition: window_gui.h:309
Window(WindowDesc &desc)
Empty constructor, initialization has been moved to InitNested() called from the constructor of the d...
Definition: window.cpp:1756
void LowerWidget(WidgetID widget_index)
Marks a widget as lowered.
Definition: window_gui.h:466
void InitNested(WindowNumber number=0)
Perform complete initialization of the Window with nested widgets, to allow use.
Definition: window.cpp:1746
const Scrollbar * GetScrollbar(WidgetID widnum) const
Return the Scrollbar to a widget index.
Definition: window.cpp:314
void SetWidgetDisabledState(WidgetID widget_index, bool disab_stat)
Sets the enabled/disabled status of a widget.
Definition: window_gui.h:387
int height
Height of the window (number of pixels down in y direction)
Definition: window_gui.h:312
int width
width of the window (number of pixels to the right in x direction)
Definition: window_gui.h:311
void ToggleWidgetLoweredState(WidgetID widget_index)
Invert the lowered/raised status of a widget.
Definition: window_gui.h:456
WindowNumber window_number
Window number within the window class.
Definition: window_gui.h:302
Definition of Interval and OneShot timers.
Definition of the game-economy-timer.
Definition of the tick-based game-timer.
Definition of the Window system.
void DrawFrameRect(int left, int top, int right, int bottom, Colours colour, FrameFlags flags)
Draw frame rectangle.
Definition: widget.cpp:281
std::unique_ptr< NWidgetBase > MakeCompanyButtonRows(WidgetID widget_first, WidgetID widget_last, Colours button_colour, int max_length, StringID button_tooltip, bool resizable)
Make a number of rows with button-like graphics, for enabling/disabling each company.
Definition: widget.cpp:3300
@ NC_EQUALSIZE
Value of the NCB_EQUALSIZE flag.
Definition: widget_type.h:524
@ WWT_PUSHTXTBTN
Normal push-button (no toggle button) with text caption.
Definition: widget_type.h:112
@ NWID_SPACER
Invisible widget that takes some space.
Definition: widget_type.h:79
@ NWID_HORIZONTAL
Horizontal container.
Definition: widget_type.h:75
@ WWT_PANEL
Simple depressed panel.
Definition: widget_type.h:50
@ WWT_STICKYBOX
Sticky box (at top-right of a window, after WWT_DEFSIZEBOX)
Definition: widget_type.h:66
@ WWT_MATRIX
Grid of rows and columns.
Definition: widget_type.h:59
@ WWT_SHADEBOX
Shade box (at top-right of a window, between WWT_DEBUGBOX and WWT_DEFSIZEBOX)
Definition: widget_type.h:64
@ WWT_CAPTION
Window caption (window title between closebox and stickybox)
Definition: widget_type.h:61
@ NWID_VSCROLLBAR
Vertical scrollbar.
Definition: widget_type.h:84
@ NWID_VERTICAL
Vertical container.
Definition: widget_type.h:77
@ WWT_CLOSEBOX
Close box (at top-left of a window)
Definition: widget_type.h:69
@ WWT_EMPTY
Empty widget, place holder to reserve space in widget tree.
Definition: widget_type.h:48
@ WWT_RESIZEBOX
Resize box (normally at bottom-right of a window)
Definition: widget_type.h:68
@ WWT_DEFSIZEBOX
Default window size box (at top-right of a window, between WWT_SHADEBOX and WWT_STICKYBOX)
Definition: widget_type.h:65
@ WWT_TEXT
Pure simple text.
Definition: widget_type.h:58
@ RWV_HIDE_BEVEL
Bevel of resize box is hidden.
Definition: widget_type.h:40
void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
Mark window data of the window of a given class and specific window number as invalid (in need of re-...
Definition: window.cpp:3211
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting)
Definition: window.cpp:3093
Window functions not directly related to making/drawing windows.
Functions, definitions and such used only by the GUI.
@ FR_LOWERED
If set the frame is lowered and the background colour brighter (ie. buttons when pressed)
Definition: window_gui.h:28
@ WDP_AUTO
Find a place automatically.
Definition: window_gui.h:147
int WidgetID
Widget ID.
Definition: window_type.h:18
int32_t WindowNumber
Number to differentiate different windows of the same class.
Definition: window_type.h:737
@ WC_PERFORMANCE_HISTORY
Performance history graph; Window numbers:
Definition: window_type.h:551
@ WC_PERFORMANCE_DETAIL
Performance detail window; Window numbers:
Definition: window_type.h:575
@ WC_PAYMENT_RATES
Payment rates graph; Window numbers:
Definition: window_type.h:569
@ WC_GRAPH_LEGEND
Legend for graphs; Window numbers:
Definition: window_type.h:521
@ WC_NONE
No window, redirects to WC_MAIN_WINDOW.
Definition: window_type.h:45
@ WC_OPERATING_PROFIT
Operating profit graph; Window numbers:
Definition: window_type.h:539
@ WC_INDUSTRY_PRODUCTION
Industry production history graph; Window numbers:
Definition: window_type.h:581
@ WC_INDUSTRY_VIEW
Industry view; Window numbers:
Definition: window_type.h:363
@ WC_INCOME_GRAPH
Income graph; Window numbers:
Definition: window_type.h:533
@ WC_DELIVERED_CARGO
Delivered cargo graph; Window numbers:
Definition: window_type.h:545
@ WC_COMPANY_VALUE
Company value graph; Window numbers:
Definition: window_type.h:557
Functions related to zooming.
int ScaleGUITrad(int value)
Scale traditional pixel dimensions to GUI zoom level.
Definition: zoom_func.h:117
@ ZOOM_LVL_NORMAL
The normal zoom level.
Definition: zoom_type.h:21